/* DESCRIPTION: Main stylesheet for the CTT React SPA. */
/* DESCRIPTION: Landing page, experiment cards, and shared layout primitives. */

/* Heading / banner stack — use the display font when available, fall back cleanly.
   CR4 (May 7): expanded to cover EVERY title across narrated screens so every
   info-blurb title in Initial Experiments renders in the Hyperspace capsule
   font, per Cristina's feedback (image 4). The body copy continues to use the
   default theme font; only titles get the display font. */
/* Hyperspace capsule font — applied to every title slot via the shared
   .ctt-title class plus the legacy per-screen __title classes for
   backwards compatibility while screens migrate to the shared title slot. */
.ctt-title,
.ctt-heading,
.ctt-experiment-shell__banner,
.ctt-experiment-shell__sidebar-number,
.ctt-experiment-shell__sidebar-cue,
.ctt-landing__title,
.ctt-experiment-card__title,
.ctt-intro-screen__title,
.ctt-experiment-intro__title,
.ctt-result__heading,
.ctt-save-prompt__title,
.ctt-tour-instruction__title,
.ctt-quiz-instruction__title,
.ctt-gender-switch__title,
.ctt-transition-screen__title,
.ctt-tour__cue-name,
#ctt-root h1,
#ctt-root h2,
#ctt-root h3 {
    font-family: var(--ctt-font-hyperspace);
}
/* NOTE: .ctt-thank-you__title intentionally REMOVED from this list — that
   screen has no canonical title (L0.1 p24); both lines are body copy. */

/* ─── Reset / Base ─────────────────────────────────────────────────────────── */

/* Scoped reset: only apply to descendants of #ctt-root so we don't stomp on
   theme/Elementor styles elsewhere on the page. */
#ctt-root,
#ctt-root *,
#ctt-root *::before,
#ctt-root *::after {
    box-sizing: border-box;
}

/* Default app canvas variables.
   --ctt-chrome-offset is written by src/index.js from the live header + footer
   heights. The CSS fallback of 160px covers the brief window before JS runs or
   a site where the selectors miss. --ctt-min-screen is what every full-screen
   app view uses for its min-height, so header + #ctt-root + footer = 100vh. */
#ctt-root {
    --ctt-chrome-offset: 160px;
    --ctt-min-screen: calc(100vh - var(--ctt-chrome-offset));

    /* Button design tokens — single source of truth for every .ctt-btn variant.
       Per CTT button spec: three reusable categories (Primary / Secondary / Chip)
       cover all four button functions across Initial Experiments and Prep-Lab. */
    --ctt-brown-darkest: rgb(82, 62, 46);
    --ctt-brown-dark:    rgb(120, 88, 65);
    --ctt-brown-mid:     rgb(166, 123, 91);
    --ctt-brown-pale:    rgb(238, 217, 196);
    --ctt-text-on-brown: #ffffff;
    --ctt-text-on-pale:  rgb(51, 51, 51);
    --ctt-disabled-alpha: 0.5;

    /* ─── Layout tokens (Anchoring Architecture, Layer 1) ───────────────────
       Single source of truth for every spatial / typographic value used by
       experiment screens. Per-screen CSS reads these via var(...) so changes
       propagate everywhere instead of needing per-screen hunts. */

    /* Textbox (rope-bordered info blurb) */
    --ctt-textbox-max-width: 720px;
    --ctt-textbox-border-width: 42px;
    --ctt-textbox-padding-y: 28px;
    --ctt-textbox-padding-x: 36px;

    /* Screen frame */
    --ctt-screen-padding-y: 24px;
    --ctt-screen-padding-x: 24px;
    --ctt-screen-bottom-padding: 32px;

    /* Stacked screen row (textbox + chip gutter on either side) */
    --ctt-row-max-width: 940px;

    /* Photo pair on Tour / Quiz screens */
    --ctt-photo-pair-max-width: 720px;
    --ctt-photo-gap: 12px;
    --ctt-photo-aspect: 1 / 1;

    /* HUD chip dimensions (rope-frame + palette + label baked into the PNG) */
    --ctt-chip-width: 90px;
    --ctt-chip-height: 110px;

    /* State B (single-cue intro screens) renders the chip enlarged per the
       L0.1-L0.5 storyboards: the solo-focus chip is drawn bigger than its
       grouped State A size. Uniform 1.5x across all experiments per Shai
       2026-05-14. Aspect kept at the asset's native 90:110. */
    --ctt-chip-width-large: 135px;
    --ctt-chip-height-large: 165px;

    /* Buttons */
    --ctt-button-padding-y: 12px;
    --ctt-button-padding-x: 32px;

    /* Narration icon */
    --ctt-narration-icon-size: 20px;

    /* Hourglass timer (CR7 — 2x size) */
    --ctt-hourglass-size: 48px;

    /* Footer divider (Quiz active / Tour active footer line if any) */
    --ctt-footer-divider: 0;          /* zero = no divider; canon does not show one */

    /* Vertical gaps */
    --ctt-stack-gap: 16px;
    --ctt-cta-margin-top: 24px;

    /* Typography
       Three admin-controlled role variables backed by the ctt_typography
       option (CTT_Typography::build_inline_css). The values below are static
       defaults that match the day-one configuration of the typography
       admin panel — if the inline emitter does not run for any reason the
       app still renders with the same fonts as before. The inline emitter
       targets #ctt-root with the same selector so its declarations replace
       these on real page loads. */
    --ctt-font-title:  'Hyperspace-Race-Cap-Variable', hyperspace-race-cap-variable, sans-serif;
    /* Body + button defaults to `inherit` so on day one (before the admin
       has picked a font) body text and button labels render in whatever
       font the WordPress theme (Astra) is already applying. This matches
       the pre-CR01 production behavior exactly. When the admin picks a
       font from the Typography settings page, CTT_Typography overrides
       these via the inline emitter on wp_head. */
    --ctt-font-body:   inherit;
    --ctt-font-button: inherit;

    /* Legacy alias: every existing selector that references
       --ctt-font-hyperspace continues to work and now follows whatever the
       title role resolves to. Removing the alias would require touching
       every selector in this file. */
    --ctt-font-hyperspace: var(--ctt-font-title);

    --ctt-color-text-body: #1a1a1a;
    --ctt-color-text-muted: #555555;
    --ctt-color-rope-fallback: #b3001b;
}

/* Body-font role is applied at the SPA root so every text element inside the
   React app inherits it by default. Title elements override with
   var(--ctt-font-title) via the .ctt-title etc. selectors at the top of
   this file. Buttons override with var(--ctt-font-button) on .ctt-btn. */
#ctt-root {
    font-family: var(--ctt-font-body);
}

#ctt-root p {
    color: #333333;
}

/* ─── Landing page ──────────────────────────────────────────────────────────── */

.ctt-landing {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: var(--ctt-min-screen);
    padding: 64px 24px 80px;
    max-width: 960px;
    margin: 0 auto;
}

/* Header */

.ctt-landing__header {
    text-align: center;
    margin-bottom: 56px;
    max-width: 600px;
}

.ctt-landing__title {
    font-size: clamp(1.75rem, 4vw, 2.75rem);
    font-weight: 700;
    color: #1a1a1a;
    margin-bottom: 12px;
    line-height: 1.15;
}

.ctt-landing__subtitle {
    font-size: 1rem;
    color: #333333;
    line-height: 1.7;
}

/* Grid */

.ctt-landing__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 20px;
    width: 100%;
}

/* ─── Experiment card ───────────────────────────────────────────────────────── */

.ctt-experiment-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border: 1px solid #dddddd;
    background: #ffffff;
    padding: 24px;
    border-radius: 5px;
    transition: border-color 0.15s ease;
}

.ctt-experiment-card:hover {
    border-color: #523e2e;
}

.ctt-experiment-card__number {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.ctt-experiment-card__title {
    font-size: 1.125rem;
    font-weight: 700;
    color: #1a1a1a;
    line-height: 1.4;
    flex: 1;
}

/* Layout-only overrides for the experiment card CTA. Color, hover, active,
   and disabled states come from .ctt-btn / .ctt-btn--primary. */
.ctt-experiment-card__start-btn {
    margin-top: 8px;
    padding: 10px 22px;
    font-size: 0.875rem;
    align-self: flex-start;
}

/* Locked card — sequential unlock per Cristina #2 / Bug 5. Card stays in the
   grid but greyed and the Start button is disabled. */
.ctt-experiment-card--locked {
    opacity: 0.55;
    background: #f5f5f5;
}

.ctt-experiment-card--locked:hover {
    border-color: #dddddd;
}

.ctt-experiment-card__locked-note {
    font-size: 0.8125rem;
    color: #666666;
    font-style: italic;
}

/* ─── Loading state ─────────────────────────────────────────────────────────── */

.ctt-landing__loading {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--ctt-min-screen);
    font-size: 0.95rem;
    color: #666666;
}

/* ─── Error state ───────────────────────────────────────────────────────────── */

.ctt-landing__error {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--ctt-min-screen);
    font-size: 0.95rem;
    color: #c0392b;
    padding: 24px;
    text-align: center;
}

/* ─── Responsive ────────────────────────────────────────────────────────────── */

@media (max-width: 480px) {
    .ctt-landing {
        padding: 40px 16px 60px;
    }

    .ctt-landing__header {
        margin-bottom: 36px;
    }

    .ctt-landing__grid {
        grid-template-columns: 1fr;
    }

    /* Mobile polish round 2 (Shai 2026-05-10): on very small phones below
       480 px, stack the photo pair to one column. Above 480 the 768 mobile
       block keeps 2 columns so each 3:4 photo wrapper stays ~half-viewport
       wide and the page does not overflow into multi-screen scroll. */
    .ctt-photo-pair {
        grid-template-columns: 1fr;
    }
}

/* ─── Shared experiment states ──────────────────────────────────────────────── */

.ctt-experiment__loading,
.ctt-experiment__error,
.ctt-experiment__empty {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--ctt-min-screen);
    font-size: 0.95rem;
    padding: 24px;
    text-align: center;
}

.ctt-experiment__loading { color: #666666; }
.ctt-experiment__error   { color: #c0392b; }
.ctt-experiment__empty   { color: #666666; }

/* ─── Experiment shell (persistent sidebar + subtitle banner) ───────────────── */

/* Shell layout: 2 cols, 3 rows.
   Row 1: sidebar header ("Experiment #N / Title") on the LEFT, sharing the
          row with the centered banner ("~ Initial Experiments ~") on the
          RIGHT. Both share the row's top/bottom borders so the experiment
          label appears at the same vertical level as the page title, per
          the L0.1 canon mockup.
   Row 2: empty sidebar-rest (vertical line only) | body (screen content).
   Row 3: full-width footer (caption + bell), aligned bottom. */
.ctt-experiment-shell {
    display: grid;
    grid-template-columns: 200px 1fr;
    grid-template-rows: auto 1fr auto;
    /* Claim the full viewport height for the experiment, pushing the site
       footer below the fold. The default --ctt-min-screen reserves space for
       both the WP header AND the site footer, which leaves the body too
       short (~605 on a 1440x900 viewport) to hold a 720x560 ribbon plus
       chips above and below. The experiment is a focused task, so the site
       footer doesn't need to be on-screen during it. */
    min-height: 100vh;
    background: #ffffff;
    /* Horizontal scroll on the shell itself. The responsive media query
       below floors the column track at minmax(1200px, 1fr); on viewports
       narrower than 200 + 1200 px (sidebar + min stage), the grid pushes
       the shell wider than the viewport. Without overflow-x:auto here the
       overflow is silently clipped by ancestor wrappers (WordPress themes
       commonly set overflow-x:hidden on body), leaving no scrollbar and
       the right side of the stage unreachable. The sticky sidebar (see
       __sidebar-header / __sidebar-rest below) stays anchored against the
       shell's left edge while the body scrolls horizontally.
       Note: per CSS spec, setting overflow on one axis forces the other to
       compute as auto. This is benign here because the shell has no
       max-height — the element grows with its content rather than scrolling
       vertically, so overflow-y:auto stays dormant in practice. */
    overflow-x: auto;
}

/* Responsive height floor (2026-05-21 v5 — correct mechanism). Below
   ~910 px viewport height the body row would naturally shrink and the
   rope ribbon would shrink with it, causing text to overflow the rope.
   Set the grid track containing the body cell to minmax(860px, 1fr) so
   the row is at least 860 tall but can grow to 1fr at taller viewports.
   The body cell gets a DEFINITE block size from the grid track, which
   keeps container-type:size happy (cqh resolves to the actual track
   height, not to 0 as it did when min-height was on the body cell
   directly in v4).

   Gated to viewports above the JS gate (800x640 in ExperimentShell.js)
   so the gate-message can still fit truly small viewports without
   being pushed off-screen by a 860 px row.

   Also widens column 2 to minmax(1200px, 1fr) so the stage has a
   horizontal floor too. The 200px sidebar column stays put. CSS Grid
   expands the shell horizontally past the viewport when needed; the
   sticky sidebar (see .ctt-experiment-shell__sidebar-* below) keeps
   the left rail visible during horizontal scroll. */
@media (min-width: 800px) and (min-height: 640px) {
    .ctt-experiment-shell {
        grid-template-columns: 200px minmax(1200px, 1fr);
        grid-template-rows: auto minmax(860px, 1fr) auto;
    }
}

.ctt-experiment-shell__sidebar-header {
    grid-column: 1;
    grid-row: 1;
    padding: 20px 20px;
    border-top: 1px solid #333333;
    border-right: 1px solid #333333;
    border-bottom: 1px solid #333333;
    display: flex;
    align-items: center;
    /* Stick to viewport left during horizontal scroll (when page > viewport
       wide because body cell hit its min-width floor). Without this the
       sidebar would scroll off-screen left when the user scrolls right.
       z-index keeps the sidebar painting above the body cell that slides
       under it; background prevents the body content from showing through.
       Above the floor (no horizontal scroll) sticky is dormant. */
    position: sticky;
    left: 0;
    z-index: 2;
    background: #ffffff;
}

/* Cue title (e.g. "Mouth Shape") sits at the top of the sidebar-rest
   column, just below the experiment-label row. */
.ctt-experiment-shell__sidebar-rest .ctt-experiment-shell__sidebar-cue {
    padding: 16px 20px 0;
}

.ctt-experiment-shell__banner {
    grid-column: 2;
    grid-row: 1;
    padding: 20px 24px;
    text-align: center;
    font-size: 1.25rem;
    color: #1a1a1a;
    border-top: 1px solid #333333;
    border-bottom: 1px solid #333333;
}

/* Sidebar-rest is the vertical column under the sidebar-header. Spans
   rows 2 and 3 so the vertical right-border runs continuously from below
   the experiment-label header all the way to the bottom of the footer. */
.ctt-experiment-shell__sidebar-rest {
    grid-column: 1;
    grid-row: 2 / span 2;
    border-right: 1px solid #333333;
    /* Sticks alongside sidebar-header so the whole left rail stays visible
       during horizontal scroll. See sidebar-header for rationale. */
    position: sticky;
    left: 0;
    z-index: 2;
    background: #ffffff;
}

/* Main-spacer is no longer needed; keep as a no-op for any stale refs. */
.ctt-experiment-shell__main-spacer {
    display: none;
}

.ctt-experiment-shell__sidebar-number {
    font-size: 1rem;
    color: #1a1a1a;
    font-weight: 500;
}

.ctt-experiment-shell__sidebar-cue {
    font-size: 0.95rem;
    color: #333333;
}

.ctt-experiment-shell__body {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    flex-direction: column;
    padding: 32px 24px;
    min-width: 0;
}

/* Loading/error/empty states inside the shell — override default min-height */
.ctt-experiment-shell__body .ctt-experiment__loading,
.ctt-experiment-shell__body .ctt-experiment__error,
.ctt-experiment-shell__body .ctt-experiment__empty {
    min-height: 400px;
}

/* When an experiment screen renders inside the shell, it must not stack a
   second viewport-height on top of the shell's own min-height:100vh. Without
   this override, the screen's own `min-height:100vh` pushes primary actions
   below the fold on standard desktop viewports.

   Two parents listed:
   - Legacy direct path (.ctt-experiment-shell__body > .X) for callers that
     bypass the gate wrapper.
   - New .ctt-experiment-shell__body-children > .X path. The gate wrapper
     renders as `display:contents` when the gate is hidden, so the screens
     are visually flush with the body but DOM-wise are one level deeper.
     Child combinators traverse the DOM (not the box tree), so we need
     this parallel selector for the wrapped case. */
.ctt-experiment-shell__body > .ctt-intro-screen,
.ctt-experiment-shell__body > .ctt-gender-select,
.ctt-experiment-shell__body > .ctt-gender-switch,
.ctt-experiment-shell__body > .ctt-tour-instruction,
.ctt-experiment-shell__body > .ctt-quiz-instruction,
.ctt-experiment-shell__body > .ctt-thank-you,
.ctt-experiment-shell__body > .ctt-transition-screen,
.ctt-experiment-shell__body > .ctt-quiz,
.ctt-experiment-shell__body > .ctt-result,
.ctt-experiment-shell__body-children > .ctt-intro-screen,
.ctt-experiment-shell__body-children > .ctt-gender-select,
.ctt-experiment-shell__body-children > .ctt-gender-switch,
.ctt-experiment-shell__body-children > .ctt-tour-instruction,
.ctt-experiment-shell__body-children > .ctt-quiz-instruction,
.ctt-experiment-shell__body-children > .ctt-thank-you,
.ctt-experiment-shell__body-children > .ctt-transition-screen,
.ctt-experiment-shell__body-children > .ctt-quiz,
.ctt-experiment-shell__body-children > .ctt-result {
    min-height: 0;
    flex: 1;
}

/* Responsive: collapse the 2-column grid into a single column on narrow viewports.
   The sidebar header becomes a horizontal strip with the experiment number + cue
   side-by-side, and the spacer / sidebar-rest are hidden since they have no content. */
@media ( max-width: 640px ) {
    .ctt-experiment-shell {
        grid-template-columns: 1fr;
        grid-template-rows: auto auto 1fr auto;
    }

    .ctt-experiment-shell__banner {
        grid-column: 1;
    }

    .ctt-experiment-shell__sidebar-header {
        grid-column: 1;
        grid-row: 2;
        border-right: none;
        flex-direction: row;
        align-items: baseline;
        gap: 12px;
        padding: 16px 20px;
    }

    .ctt-experiment-shell__main-spacer,
    .ctt-experiment-shell__sidebar-rest {
        display: none;
    }

    .ctt-experiment-shell__body {
        grid-column: 1;
        grid-row: 3;
    }
}

/* ─── Shared sample image ───────────────────────────────────────────────────── */

.ctt-sample-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
    display: block;
}

/* Mouth crop — lower-third of the face */
.ctt-sample-image--crop-mouth {
    object-position: center 80%;
}

/* Eye crop — upper portion of the face */
.ctt-sample-image--crop-eye {
    object-position: center 25%;
}

/* Fallback when a sample image fails to load. Fills the image wrap with a
   bordered grey box and subtle copy so missing assets are visible to QA
   instead of silently collapsing to blank space. */
.ctt-image-placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f5f5f5;
    border: 1px dashed #bbbbbb;
    box-sizing: border-box;
}

.ctt-image-placeholder__text {
    font-size: 0.875rem;
    color: #666666;
    letter-spacing: 0.02em;
    text-align: center;
    padding: 0 12px;
}

/* Spinner overlay shown by SampleImage while the underlying <img> loads.
   Positioned absolutely so it sits centered over the wrap regardless of
   whether the wrap uses aspect-ratio (tour) or grid sizing (quiz). */
.ctt-sample-image__spinner {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

/* ─── Experiment Intro ──────────────────────────────────────────────────────── */

.ctt-experiment-intro {
    display: flex;
    flex-direction: column;
    min-height: var(--ctt-min-screen);
    padding: 40px 24px 64px;
    max-width: 640px;
    margin: 0 auto;
}

.ctt-experiment-intro__body {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.ctt-experiment-intro__eyebrow {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #EED9C4;
}

.ctt-experiment-intro__title {
    font-size: clamp(1.75rem, 4vw, 2.5rem);
    font-weight: 700;
    color: #1a1a1a;
    line-height: 1.15;
}

.ctt-experiment-intro__description {
    font-size: 1rem;
    color: #333333;
    line-height: 1.7;
    max-width: 520px;
}

.ctt-experiment-intro__meta {
    list-style: none;
    display: flex;
    gap: 32px;
    flex-wrap: wrap;
    border-top: 1px solid #333333;
    border-bottom: 1px solid #333333;
    padding: 16px 0;
    margin: 8px 0;
}

.ctt-experiment-intro__meta li {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-experiment-intro__meta-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #666666;
}

.ctt-experiment-intro__meta-value {
    font-size: 1rem;
    font-weight: 600;
    color: #1a1a1a;
}

.ctt-experiment-intro__error {
    font-size: 0.875rem;
    color: #c0392b;
}

.ctt-experiment-intro__start-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 32px;
    background: #EED9C4;
    color: #ffffff;
    font-size: 0.9375rem;
    font-weight: 600;
    border: none;
    cursor: pointer;
    transition: background 0.15s ease;
    align-self: flex-start;
    border-radius: 5px;
}

.ctt-experiment-intro__start-btn:hover:not(:disabled) {
    background: #A67B5B;
}

.ctt-experiment-intro__start-btn:focus-visible {
    outline: 2px solid #EED9C4;
    outline-offset: 3px;
}

.ctt-experiment-intro__start-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* ─── Tour screen ───────────────────────────────────────────────────────────── */

/* The legacy `.ctt-tour` flex container (max-width:960px, margin:0 auto) and
   the per-experiment `.ctt-tour__stage--hud-*` positioning wrappers are gone.
   Tour screens now render through ExperimentStage on the HUD grid; chip
   placement comes from src/pages/experiment-hud-layout.js via grid cells.
   Only the cue-list / cue-line / cue-description BEM children below are kept
   because TourScreen still uses them to style the caption text. */

/* Shared info-textbox rope frame.
   CR2 + CR3 (May 7): added standardized max-width and horizontal centering
   so every info blurb across all Initial Experiments screens renders at the
   same size and is horizontally centered under the page title. The
   max-width is calibrated to the largest blurb in the program (L20 slide 27
   "Accepting Greatness"). Padding bumped slightly so the rope frame has
   breathing room around the standardized text size. Body text inside the
   textbox is centered to match the page title. */
.ctt-info-textbox {
    border-style: solid;
    border-width: var(--ctt-textbox-border-width);
    border-color: transparent;
    /* Cristina's rope braid PNG (2991×1970), 9-slice cut at 525px so the
       middle tile's left and right edges land on visually identical
       columns of the rope pattern (analysed: edge_match_diff ≈ 1212
       at slice=525 vs ≈ 70k at slice=300). With `round` repeat, copies
       of the middle slice now meet at matched pixels and the seams
       Cristina flagged become invisible. */
    border-image: url(../images/Textbox_Border.png) 525 / var(--ctt-textbox-border-width) / 0 round;
    background: transparent;
    padding: var(--ctt-textbox-padding-y) var(--ctt-textbox-padding-x);
    max-width: var(--ctt-textbox-max-width);
    width: 100%;
    margin: 0 auto;
    text-align: center;
    /* Restored: the program-wide ribbon lock from the client's brief. Every
       .ctt-info-textbox is the same fixed size on every screen — short blurbs
       render with empty space above/below, long blurbs cap at the lock and
       scroll. SavePrompt is the only carveout. 560 was the measured longest
       Initial Experiments blurb height at the current 720 max-width. */
    min-height: 560px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
    box-sizing: border-box;
}

/* Standardized size + centering for body copy and titles inside the
   textbox. Titles in the Hyperspace font, body copy in the theme default;
   both centered horizontally to match the page title above. */
.ctt-info-textbox > * {
    margin: 0 auto;
    max-width: 100%;
}

.ctt-info-textbox > * + * {
    margin-top: 18px;
}

/* Exp 5 Core-Cat cue list — three cues stacked on three centered lines.
   Used in both the description (IntroScreen) and tour (TourScreen) renderings. */
.ctt-intro-screen__cue-list,
.ctt-tour__cue-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 4px;
}

.ctt-intro-screen__cue-line,
.ctt-tour__cue-line {
    margin: 0;
}

/* Shared Prep-Lab page-title banner. */
.ctt-page-title-banner {
    display: block;
    background-image: url(../images/Title_Banner.png);
    background-repeat: repeat-x;
    background-position: center center;
    background-size: auto 100%;
    text-align: center;
    color: #ffffff !important;
    -webkit-text-stroke: 1.5px #000000;
    font-size: clamp(1.75rem, 4vw, 2.5rem);
    font-weight: 400;
    line-height: 1;
    padding: 18px 24px;
    margin: 0 0 24px;
    letter-spacing: 0.04em;
}

.ctt-tour__clock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    grid-column: 2;
    /* Photos are now page-centered (chip is absolute, not in flow), so the
       hourglass needs no horizontal offset. */
    --photo-center-offset: 0px;
}

/*
 * Hourglass timer (Cristina May 5 2026 evening bullet 2). The SVG is the
 * default visible content; the formatted readout is hidden by default and
 * revealed on hover so the practitioner can check the exact remaining time
 * without staring at a countdown. The `title` attribute also surfaces the
 * formatted time as a native browser tooltip for trackpad users. The
 * sand-spill animation is the second half of this request and waits on
 * Cristina's Q2 (animation source).
 *
 * --photo-center-offset: empirical horizontal shift to align the timer
 * with the center of the two photos above it (rather than the geometric
 * footer center, which is misaligned because the HUD chip on the left
 * pushes the photo block right of page center). Default 50px = approx
 * half the HUD chip width (84px) + half the gap (16px). Per L0.1 p16 / p12
 * storyboard the clock sits centered under the photo gap. Override to 0 for
 * layouts without a left-side HUD (e.g. Exp 5 right-column quiz).
 */
.ctt-hourglass-timer {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #1a1a1a;
    /* Position relative so the absolute-positioned readout (see __readout
       below) is anchored to the icon, not to the page. Wrapper width =
       icon width = 48px so the icon centers exactly at the photo gap. */
    position: relative;
    cursor: help;
    line-height: 1;
    transform: translateX(var(--photo-center-offset, 0px));
}

.ctt-hourglass-timer__svg {
    /* Hourglass artwork uses a 96x96 viewBox. Sized to ~48px square per
       the L0.1 canon proportion (roughly twice the previous render size).
       The readout still hides behind it until hover. */
    width: 48px;
    height: 48px;
    display: block;
    flex: 0 0 auto;
}

/* Hourglass animation video. Replaces the SVG (.ctt-hourglass-timer__svg
   above) per Cristina's hand-animated source. VP9 webm with real alpha
   channel composites cleanly on any background — no mix-blend-mode trick.
   Source is 128x202 (tight-cropped to body, portrait aspect 0.634).
   Height locked to 53px so the icon matches the Back/Next button height
   in the footer row (per Shai QA 2026-05-13 — 48px was visibly smaller
   next to the 53px buttons). Width auto so the video keeps Cristina's
   portrait aspect (renders at 34 wide × 53 tall). */
.ctt-hourglass-timer__video {
    width: auto;
    height: 53px;
    display: block;
    flex: 0 0 auto;
    object-fit: contain;
    pointer-events: none;
}

.ctt-hourglass-timer__readout {
    /* Position absolutely so the readout does NOT take up space in the
       flex flow. Without this, the wrapper width grows to icon+text and
       the visible icon ends up offset to the LEFT of the photo gap (the
       wrapper centers, not the icon). With this, the wrapper width = icon
       width = 48px, and the icon centers exactly at the photo-gap line.
       Per Shai 2026-05-08 round-4 visual QA: hourglass must be centered
       between the two photos. */
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 6px;
    font-size: 0.875rem;
    font-variant-numeric: tabular-nums;
    color: inherit;
    opacity: 0;
    pointer-events: none;
    /* No transition: React re-renders the timer span every second when
       secondsLeft drops, which interfered with an opacity transition and
       left the readout stuck mid-fade. Instant snap is robust. */
    min-width: 3.5em;
    text-align: left;
}

.ctt-hourglass-timer:hover .ctt-hourglass-timer__readout,
.ctt-hourglass-timer:focus-visible .ctt-hourglass-timer__readout {
    opacity: 1;
}

.ctt-hourglass-timer--warning {
    color: #b00020;
}

.ctt-hourglass-timer--warning .ctt-hourglass-timer__readout {
    /* Cristina 2026-05-09 round-2 feedback (point 7): hourglass time readout
       must be hover-only on every screen, including when the timer is in
       warning state. The earlier behavior of forcing the readout visible
       during warning broke the hover-only contract on Quiz Active because
       Quiz enters warning at 30 seconds left and the user is typically
       still on the screen at that point.
       Now: warning state only changes the icon colour (the red tint stays
       on .ctt-hourglass-timer--warning above). The readout itself stays
       opacity 0 by default and only appears on :hover or :focus-visible
       per the rule at .ctt-hourglass-timer:hover above. The font-weight
       bump is kept so that IF a user hovers during warning state, the
       readout reads as bolder than the calm-state hover readout. */
    font-weight: 600;
}

.ctt-tour__counter {
    font-size: 0.875rem;
    color: #666666;
    font-variant-numeric: tabular-nums;
    text-align: right;
    grid-column: 3;
    /* Photo-pair-anchored: counter row sits below the photo footer, its
       right edge aligned with the photo-pair right edge. Without these,
       the counter shrinks to text width and lands in the middle of the
       body column. Verified via measurement audit 2026-05-08. */
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    margin: 4px auto 0;
}

.ctt-tour__nav {
    display: flex;
    gap: 12px;
    grid-column: 3;
    justify-content: flex-end;
}

/* Footer layout: clock center, nav right. Counter sits below the nav row on its own line. */.ctt-tour__clock   { grid-area: clock; }
.ctt-tour__nav     { grid-area: nav; }
.ctt-tour__counter { grid-area: counter; }

/* Layout-only overrides for tour back/next. Color, hover, active, focus,
   and disabled states come from .ctt-btn / .ctt-btn--secondary / --primary.
   white-space: nowrap + min-width: max-content keeps the long-label
   variants (e.g. "Continue to Quiz", "Next Cue") within their own border.
   The canon cell was sized for the short "Next" label; longer labels grow
   the button rightward rather than wrapping or overflowing the border. */
.ctt-tour__nav-btn,
.ctt-quiz__nav-btn {
    padding: 10px 24px;
    font-size: 0.875rem;
    white-space: nowrap;
    min-width: max-content;
}

/* ─── Quiz screen ───────────────────────────────────────────────────────────── */

/* The legacy `.ctt-quiz` flex container (max-width:960px, margin:0 auto) is
   gone. Quiz screens now render through ExperimentStage on the HUD grid;
   chip and photo placement come from src/pages/experiment-hud-layout.js via
   grid cells. The `.ctt-quiz__*` BEM children below are kept where QuizScreen
   still uses them. */

/* Bottom toolbar matches L0.1 Walkthru.pdf p16 storyboard — timer (stopwatch
   style) on the left, counter "N of M" on the right. Same pattern as
   .ctt-tour__footer for consistency across Tour active and Quiz active. */.ctt-quiz__timer {
    grid-column: 2;
    justify-self: center;
    /* Photos are now page-centered for every layout (chips are absolute,
       not in flow), so the hourglass needs no horizontal offset. */
    --photo-center-offset: 0px;
}

/* Exp 5 quiz has chips on the RIGHT of photos serving as answer buttons
   (per L0.5 p18 / p29 storyboard). Photos are no longer pushed right by a
   left-side HUD, so the timer at footer center already aligns. Zero the
   offset to avoid double-shifting. */
.ctt-quiz__sample-row--right-column ~ .ctt-quiz__footer .ctt-quiz__timer,
.ctt-quiz:has(.ctt-quiz__sample-row--right-column) .ctt-quiz__timer {
    --photo-center-offset: 0px;
}

.ctt-quiz__footer .ctt-quiz__counter {
    grid-column: 3;
    justify-self: end;
}

/* Legacy: kept in case other screens reference it. The Quiz top header was
   removed in favor of the bottom footer per N6 (reversed) so counter+timer
   stay visually consistent with Tour active. */
.ctt-quiz__counter {
    font-size: 0.875rem;
    color: #666666;
    font-variant-numeric: tabular-nums;
    text-align: right;
    /* Photo-pair-anchored counter row, same as .ctt-tour__counter. */
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    margin: 4px auto 0;
}

.ctt-quiz__timer {
    font-size: 1rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #666666;
    letter-spacing: 0.04em;
    min-width: 3.5ch;
    text-align: right;
}

.ctt-quiz__feedback-verdict {
    display: block;
    font-weight: 700;
    margin-bottom: 4px;
}

.ctt-quiz__prompt-palette {
    display: block;
    color: var(--ctt-color-text-body);
    font-weight: 500;
}

.ctt-quiz__feedback-palette {
    color: #666666;
    font-weight: 500;
    font-size: 0.8125rem;
}

/* Solid-color feedback flash so the verdict reads at a glance. The original
   white-background variant was too subtle and led to feedback #7 ("users
   cannot tell if they got an answer right or wrong"). */.ctt-quiz__feedback--correct .ctt-quiz__feedback-palette,
.ctt-quiz__feedback--incorrect .ctt-quiz__feedback-palette {
    color: rgba( 255, 255, 255, 0.85 );
}

/* Prompt label — centered text above the answer buttons telling the user what they are sorting by. */

.ctt-quiz__prompt-label {
    margin: 0 0 12px 0;
    text-align: center;
    font-size: 1rem;
    font-weight: 600;
    color: #333333;
}

.ctt-quiz__error,
.ctt-quiz__saving {
    font-size: 0.875rem;
    text-align: center;
}

.ctt-quiz__error  { color: #c0392b; }
.ctt-quiz__saving { color: #666666; }

/* ─── Result screen ─────────────────────────────────────────────────────────── */
/* Layout/styles for the result screen now live in the L0.1 walkthrough alignment
   block at the bottom of this file (tracker #21). The legacy table-based result
   layout was replaced with a single bordered textbox containing heading, score
   lines, combined score, and pass/fail body messages. */

/* ─── Save Prompt ───────────────────────────────────────────────────────────── */

.ctt-save-prompt {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: var(--ctt-min-screen);
    padding: 24px 24px 32px;
    gap: 20px;
    text-align: center;
    max-width: 560px;
    margin: 0 auto;
}

/* Override when .ctt-save-prompt is on the .ctt-experiment-stage wrapper
   (post-Stage refactor IntroScreen applies the class there). The old
   max-width 560px / margin auto came from the standalone-page rendering;
   on the Stage it makes the entire stage shrink to 559px wide. Restore
   the Stage's full canon dimensions. */
.ctt-experiment-stage.ctt-save-prompt {
    max-width: none;
    margin: 0;
    padding: 0;
    min-height: 0;
    gap: 0;
    display: grid;
    grid-template-columns: repeat(24, 1fr);
    grid-template-rows:    repeat(18, 1fr);
}

/* L0.1 p27 canon: title, choice rows, and sign-in form all live inside
   ONE rope-bordered box. Layout/spacing only here — border comes from
   .ctt-info-textbox.
   SavePrompt is the carveout from the program-wide L20 textbox lock
   (Cristina round 2 point 3). It is functionally a form, not a
   narrative blurb. The fixed 380px min-height from the base rule would
   waste vertical space on the SavePrompt heading-plus-paragraph case
   and would also fight the intrinsic height needed when the sign-in
   switch link and inline form expand the contents. min-height: 0 lets
   the box size to whatever the form contents require, while every
   other screen still inherits the L20 lock. */
.ctt-save-prompt__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 14px;
    padding: 28px 36px;
    width: 100%;
    min-height: 0;
}

/* Scoped under #ctt-root because Astra theme styles h1 with its accent color
   (currently rendering green on save prompt). #ctt-root + class beats Astra's
   .entry-content h1 specificity. Canon L0.1 p27 is black-and-white — no green. */
#ctt-root .ctt-save-prompt__title {
    font-size: clamp(1.5rem, 3.5vw, 2rem);
    font-weight: 700;
    color: #1a1a1a;
    line-height: 1.2;
}

.ctt-save-prompt__body {
    font-size: 1rem;
    color: #333333;
    line-height: 1.7;
    max-width: 380px;
}

/* Layout-only override for save-prompt CTAs. Color and states come from
   .ctt-btn / .ctt-btn--primary or --secondary applied in JSX. */
.ctt-save-prompt__btn {
    padding: 12px 20px;
}

.ctt-save-prompt__form {
    display: flex;
    flex-direction: column;
    gap: 14px;
    width: 100%;
    max-width: 300px;
    /* Pop-out panel treatment: when this form is shown it usually overflows
       the canon-fixed ribbon. White background + soft drop shadow + padding
       makes the overflowing portion read as a separate floating card rather
       than a layout break (Shai 2026-05-17: "have it like a pop-up almost
       separated"). The auth-heading above it sits inside the ribbon; the
       form below visually pops out beneath. */
    background: #ffffff;
    padding: 18px 22px 22px;
    border-radius: 8px;
    box-shadow: 0 12px 28px -8px rgba(0, 0, 0, 0.22),
                0 4px 12px -4px rgba(0, 0, 0, 0.12);
}

.ctt-save-prompt__field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    text-align: left;
}

.ctt-save-prompt__label {
    font-size: 0.875rem;
    font-weight: 600;
    color: #333333;
}

.ctt-save-prompt__input {
    padding: 10px 14px;
    border: 1px solid #999999;
    background: #ffffff;
    font-size: 0.9375rem;
    width: 100%;
    box-sizing: border-box;
}

.ctt-save-prompt__input:focus {
    outline: 2px solid #EED9C4;
    outline-offset: 0;
    border-color: #EED9C4;
}

.ctt-save-prompt__input--error {
    border-color: #c0392b;
}

.ctt-save-prompt__field-error {
    font-size: 0.8125rem;
    color: #c0392b;
}

.ctt-save-prompt__submit-error {
    font-size: 0.875rem;
    color: #c0392b;
    margin: 0;
    text-align: center;
}

/* Checkbox option rows (Exp 1–4 optional sign-in progressive disclosure).
   Per L0.1 p27 canon the whole prompt sits inside a single rope-bordered
   box, so the choice rows themselves do NOT carry their own border. */
.ctt-save-prompt__option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 12px;
    cursor: pointer;
    text-align: left;
    width: 100%;
    max-width: 420px;
    box-sizing: border-box;
    font-size: 0.9375rem;
    line-height: 1.5;
}

.ctt-save-prompt__option-checkbox {
    margin-top: 3px;
    cursor: pointer;
}

.ctt-save-prompt__option-label {
    color: #1a1a1a;
}

/* Heading above the email/password fields ("Create your account below:" or
 * "Sign in to your existing account:" depending on which tab is active). */
.ctt-save-prompt__signin-heading {
    margin: 0;
    font-size: 0.9375rem;
    font-weight: 600;
    color: #333333;
    text-align: left;
}

/* Standard auth heading — sits above the email + password fields. Replaces
 * the old "Sign-in below:" plain paragraph with a proper h2 since this is
 * a section header, not a sentence. */
/* Body style — was title style (Hyperspace via --ctt-font-title) which
   Shai 2026-05-17 felt looked "ugly" as a sub-heading. Body family + a
   medium-weight rendering keeps the section header visually distinct
   without competing with the main "Would you like to save your progress?"
   title above it. Still routed through an admin Typography variable so
   the choice flows from the admin panel. Scoped under #ctt-root so the
   selector beats Astra's `.entry-content h2` heading size + family. */
#ctt-root .ctt-save-prompt__auth-heading {
    margin: 12px 0 16px;
    font-family: var(--ctt-font-body) !important;
    font-size: 1.05rem !important;
    font-weight: 600 !important;
    color: var(--ctt-color-text-body, #1a1a1a) !important;
    text-align: center;
    line-height: 1.3 !important;
}

/* "Already have an account? Sign in" / "Don't have an account? Create one"
 * switch link at the bottom of each auth form. Standard pattern across the
 * web — keeps create-account and sign-in paths in the same surface. */
.ctt-save-prompt__switch {
    margin: 16px 0 0;
    font-size: 0.875rem;
    color: #666666;
    text-align: center;
}

.ctt-save-prompt__switch-link,
.ctt-save-prompt__switch-link:hover,
.ctt-save-prompt__switch-link:focus,
.ctt-save-prompt__switch-link:active {
    /* Astra theme applies brown bg on button:hover/focus by default. Override
     * all interactive states with !important so the link stays text-only. */
    background: transparent !important;
    background-color: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    padding: 0 !important;
    font-family: inherit;
    font-size: inherit;
    font-weight: 600;
    cursor: pointer;
    text-decoration: underline;
}

.ctt-save-prompt__switch-link {
    color: #a67b5b;
}

.ctt-save-prompt__switch-link:hover,
.ctt-save-prompt__switch-link:focus {
    color: #8c6646;
}

/* ─── Responsive (experiment screens) ──────────────────────────────────────── */

@media (max-width: 600px) {.ctt-tour,
    .ctt-quiz {
        padding: 16px 16px 32px;
    }
}

/* Layout-only overrides for the six narrated-screen primary CTAs.
   Color, hover, focus, active, and disabled states come from
   .ctt-btn / .ctt-btn--primary applied in JSX. Centred horizontally
   below the textbox per L0.1 Walkthru.pdf p11/p15/p25 — Start/Next/
   Proceed/Continue all sit in the centre column below the centred
   instruction box, not flush-left. */
.ctt-intro-screen__next-btn,
.ctt-gender-switch__continue-btn,
.ctt-tour-instruction__start-btn,
.ctt-quiz-instruction__start-btn,
.ctt-thank-you__results-btn,
.ctt-transition-screen__continue-btn {
    padding: 12px 32px;
    align-self: center;
    margin-top: 24px;
}


/* ─── Gender Select ─────────────────────────────────────────────────────────── */

.ctt-gender-select__buttons {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 24px;
    justify-content: center;
}

/* Layout-only override for gender-select chips. Color and states come from
   .ctt-btn / .ctt-btn--choice applied in JSX. */
.ctt-gender-select__btn {
    padding: 14px 36px;
}

/* ─── Narration Player ────────────────────────────────────────────────────── */

.ctt-narration {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
}

.ctt-narration__label {
    color: var(--ctt-brown-mid);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* Clickable narration play/stop icon. The SVG itself is the click target —
   no surrounding button. Color comes from currentColor on the parent. */
.ctt-narration__icon {
    display: inline-block;
    color: var(--ctt-brown-mid);
    cursor: pointer;
    outline: none;
}

.ctt-narration__icon:hover {
    color: var(--ctt-brown-dark);
}

.ctt-narration__icon:focus-visible {
    outline: 2px solid var(--ctt-brown-mid);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ─── Prep-Lab portal shell (M5.2) ──────────────────────────────────────────── */

/* Portal headings now inherit the title-role font via the global selector
   list at the top of this file. The previous hardcoded system-stack override
   was an artefact from the pre-Typography-admin world when Hyperspace was
   the only title font and broke portal-heavy text with missing glyphs. With
   the admin Typography page in place, that workaround would silently
   override admin choice on portal screens, which is wrong. If Hyperspace
   glyphs look broken on portal copy, admin picks a clean sans-serif for the
   title role and every screen including portal harmonises in one click. */

.ctt-portal {
    min-height: var(--ctt-min-screen);
    display: flex;
    flex-direction: column;
    background: #ffffff;
    color: #1a1a1a;
}

.ctt-portal__topbar {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 24px 0;
    border-bottom: 1px solid #e2e2e2;
    background: #ffffff;
}

/* Two-row topbar per L10 Walkthru.pdf p27 storyboard:
   row 1 = secondary nav (Core-Cat Gallery, Honor Log, Raw Read Challenge, Volunteer)
   row 2 = primary nav (Main Portal, CC Build, CC Train, CC Test, Info Index, Progress) */
.ctt-portal__topbar-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ctt-portal__topbar-row--secondary {
    padding-top: 2px;
}

.ctt-portal__topbar-row--primary {
    padding-top: 2px;
}

.ctt-portal__tab--secondary {
    padding: 6px 14px;
    font-size: 13px;
    border-bottom: none;
}

.ctt-portal__tab--placeholder {
    color: #b0b0b0;
    cursor: not-allowed;
}

/* All portal tab state rules scoped under #ctt-root so Astra theme's
   button:focus / button:hover rules (specificity 0,1,1) cannot override
   the tab text colour. Same pattern as .ctt-btn--secondary. Sapayth's
   M6+ work on CC Train / CC Test will use the same portal tab system
   and inherits this fix automatically through the rebase. */
#ctt-root .ctt-portal__tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 12px 18px;
    color: #1a1a1a;
    text-decoration: none;
    font-size: 15px;
    font-weight: 500;
    border-bottom: 3px solid transparent;
    margin-bottom: -1px;
}

#ctt-root .ctt-portal__tab:hover {
    color: #4a6fa5;
}

#ctt-root .ctt-portal__tab--active {
    font-weight: 700;
    border-bottom-color: #1a1a1a;
}

#ctt-root .ctt-portal__tab--locked {
    color: #b0b0b0;
    cursor: not-allowed;
}

#ctt-root .ctt-portal__tab--locked:hover {
    color: #b0b0b0;
}

.ctt-portal__tab-lock {
    font-size: 12px;
}

.ctt-portal__body {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
}

.ctt-portal__sidebar {
    width: 220px;
    flex: 0 0 220px;
    padding: 24px 16px;
    border-right: 1px solid #e2e2e2;
    background: #fafafa;
}

/* Username + Practitioner Lvl block at top of sidebar per L1 Walkthru.pdf p10 */
.ctt-portal__sidebar-identity {
    margin: 0 0 18px;
    padding-bottom: 12px;
    border-bottom: 1px solid #e2e2e2;
}

.ctt-portal__sidebar-username {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-portal__sidebar-level {
    margin: 4px 0 0;
    font-size: 14px;
    color: #555555;
}

/* L{n} Requirements heading + per-row label/asterisk styling */
.ctt-portal__sidebar-title {
    margin: 0 0 10px;
    font-size: 15px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-portal__sidebar-asterisk {
    display: inline-block;
    width: 12px;
    color: #1a1a1a;
    font-weight: 700;
}

.ctt-portal__sidebar-label {
    margin-left: 4px;
    font-size: 14px;
    color: #1a1a1a;
}

/* Bold the requirement label for the section the user is currently on,
   per L1 Walkthru.pdf p10 storyboard convention. */
.ctt-portal__sidebar-item--active .ctt-portal__sidebar-label {
    font-weight: 700;
}

/* Show More secondary items — placeholders for Core-Cat Gallery, Honor Log,
   Raw Read Challenge, Volunteer until M7+ ships their real screens. */
.ctt-portal__sidebar-label--placeholder {
    color: #b0b0b0;
    cursor: not-allowed;
}

.ctt-portal__sidebar-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-portal__sidebar-item {
    margin: 0;
}

.ctt-portal__sidebar-link {
    display: block;
    padding: 8px 10px;
    font-size: 14px;
    font-weight: 500;
    color: #1a1a1a;
    text-decoration: none;
    border-radius: 4px;
}

.ctt-portal__sidebar-link--placeholder {
    color: #b0b0b0;
    cursor: not-allowed;
}

.ctt-portal__sidebar-toggle {
    display: block;
    margin: 8px 0 0;
    padding: 6px 10px;
    background: none;
    border: none;
    color: #4a6fa5;
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}

.ctt-portal__sidebar-toggle:hover,
.ctt-portal__sidebar-toggle:focus {
    text-decoration: underline;
}

.ctt-portal__sidebar-list--secondary {
    margin-top: 4px;
}

.ctt-portal__main {
    flex: 1 1 auto;
    padding: 32px 40px;
    min-width: 0;
}

/* Responsive parity for the L20 Accepting Greatness preview only. Mirrors
   the experiment shell's responsive contract: a 1200x860 floor on the
   main content cell, overflow-x:auto on the portal so the page is usable
   on narrow viewports without the right edge being clipped by an ancestor
   with overflow-x:hidden, and a sticky sidebar that stays anchored to the
   left during horizontal scroll. Same 800x640 gate threshold as the
   experiment shell.

   SCOPED TO [data-l20-preview]: the real L1 Prep-Lab portal (rendered
   by PortalLayout) uses the same .ctt-portal chrome but has its own
   working sizing today, and the user did not flag it for change. The
   L20AcceptingGreatnessPreview component sets data-l20-preview="true"
   on its outer .ctt-portal div, so this selector targets only the
   preview rendering and leaves the live portal untouched. */
@media (min-width: 800px) and (min-height: 640px) {
    .ctt-portal[data-l20-preview] {
        overflow-x: auto;
    }
    .ctt-portal[data-l20-preview] .ctt-portal__main {
        min-width: 1200px;
        min-height: 860px;
    }
    .ctt-portal[data-l20-preview] .ctt-portal__sidebar {
        position: sticky;
        left: 0;
        z-index: 2;
    }
}

.ctt-portal__error {
    max-width: 720px;
    margin: 0 auto 16px;
    padding: 12px 16px;
    border: 1px solid #c0392b;
    background: #fdecea;
    color: #c0392b;
    border-radius: 4px;
    font-size: 14px;
}

/* ─── Main Portal page (M5.2.1) ─────────────────────────────────────────────── */

.ctt-main-portal {
    display: flex;
    flex-direction: column;
    max-width: 1100px;
    margin: 0 auto;
}

.ctt-main-portal__row {
    display: flex;
    gap: 32px;
    align-items: flex-start;
}

.ctt-main-portal__requirements {
    flex: 0 0 220px;
    padding: 16px;
    border: 1px solid #e2e2e2;
    background: #ffffff;
}

.ctt-main-portal__requirements-title {
    font-size: 16px;
    font-weight: 700;
    margin: 0 0 12px;
    color: #1a1a1a;
}

.ctt-main-portal__requirements-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ctt-main-portal__requirements-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
}

.ctt-main-portal__asterisk {
    display: inline-flex;
    width: 12px;
    justify-content: center;
    color: #1a1a1a;
    font-size: 16px;
    line-height: 1;
}

.ctt-main-portal__requirements-label {
    color: #1a1a1a;
}

.ctt-main-portal__content {
    flex: 1 1 auto;
    min-width: 0;
}

.ctt-main-portal__welcome {
    margin-bottom: 24px;
}

.ctt-main-portal__heading {
    font-size: 22px;
    font-weight: 700;
    margin: 0 0 8px;
    color: #1a1a1a;
}

.ctt-main-portal__greeting {
    margin: 0;
    color: #555555;
    font-size: 14px;
}

.ctt-main-portal__itinerary {
    border: 1px solid #e2e2e2;
    padding: 24px;
    background: #ffffff;
}

.ctt-main-portal__itinerary-title {
    font-size: 18px;
    font-weight: 700;
    margin: 0 0 12px;
    color: #1a1a1a;
}

.ctt-main-portal__itinerary p {
    margin: 0 0 12px;
    line-height: 1.55;
    font-size: 15px;
    color: #1a1a1a;
}

.ctt-main-portal__itinerary-footer {
    margin-top: 16px !important;
    padding-top: 12px;
    border-top: 1px solid #e2e2e2;
    font-style: italic;
    color: #555555;
}

/* ─── Tab stub pages (placeholders until real UI ships) ─────────────────────── */

.ctt-portal-stub {
    max-width: 640px;
    margin: 48px auto;
    padding: 32px;
    border: 1px solid #e2e2e2;
    background: #ffffff;
    text-align: center;
}

.ctt-portal-stub__title {
    font-size: 22px;
    font-weight: 700;
    margin: 0 0 12px;
    color: #1a1a1a;
}

.ctt-portal-stub__body {
    margin: 0;
    color: #555555;
    font-size: 15px;
    line-height: 1.55;
}

/* ─── CC Build (M5.3) ────────────────────────────────────────────────────────
 * The Hub view is a table-style list of gen-cats with a Tour button + the
 * Adjustments panel to the right. The Tour view is a centered Celeb Surfer.
 * After lock, the Hub goes greyscale via .ctt-cc-build__hub--locked.
 */

.ctt-cc-build__loading,
.ctt-cc-build__empty,
.ctt-cc-build__hub-heading,
.ctt-cc-build__hub-col-title {
    color: #1a1a1a;
}

.ctt-cc-build__loading {
    padding: 48px 0;
    text-align: center;
    color: #666666;
}

.ctt-cc-build__hub {
    padding: 24px 0 48px;
}

.ctt-cc-build__hub-header {
    border: 1px solid #999999;
    padding: 12px 16px;
    margin-bottom: 16px;
}

.ctt-cc-build__hub-heading {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
}

.ctt-cc-build__hub-grid {
    display: block;
}

.ctt-cc-build__hub-col-titles {
    display: grid;
    grid-template-columns: 110px 70px 28px 60px 1fr;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid #dddddd;
    font-weight: 700;
    font-size: 14px;
}

.ctt-cc-build__hub-col-title--adj {
    text-align: center;
    border: 1px solid #999999;
    padding: 6px 0;
}

.ctt-cc-build__hub-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ctt-cc-build__hub-row {
    display: grid;
    grid-template-columns: 110px 70px 28px 60px 1fr;
    align-items: center;
    gap: 12px;
    padding: 6px 0;
}

.ctt-cc-build__hub-name {
    border: 1px solid #999999;
    padding: 6px 10px;
    font-size: 14px;
    background: #ffffff;
    text-align: center;
}

/* Layout-only override for the per-row Tour CTA. Color and states come from
   .ctt-btn / .ctt-btn--choice applied in JSX. */
.ctt-cc-build__tour-btn {
    padding: 6px 14px;
    font-size: 14px;
}

.ctt-cc-build__hub-checkbox,
.ctt-cc-build__adj-checkbox {
    width: 18px;
    height: 18px;
    border: 1px solid #1a1a1a;
    background: #ffffff;
    display: inline-block;
}

.ctt-cc-build__hub-checkbox--filled,
.ctt-cc-build__adj-checkbox--filled {
    background: #1a3a5f;
    border-color: #1a3a5f;
}

.ctt-cc-build__hub-goal {
    border: 1px solid #999999;
    padding: 6px 10px;
    font-size: 14px;
    text-align: center;
    background: #ffffff;
}

/* Adjustments row */
.ctt-cc-build__adj-row {
    display: grid;
    grid-template-columns: 24px repeat(4, 1fr);
    align-items: center;
    gap: 8px;
    border: 1px solid #dddddd;
    background: #f5f5f5;
    padding: 6px 8px;
}

.ctt-cc-build__adj-cell {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 8px;
    border: 1px solid #cccccc;
    background: #ffffff;
    padding: 4px 8px;
    font-size: 13px;
}

.ctt-cc-build__adj-cell-label {
    color: #333333;
    font-weight: 600;
}

.ctt-cc-build__adj-cell-value {
    font-variant-numeric: tabular-nums;
    color: #333333;
}

.ctt-cc-build__adj-cell-value--under {
    color: #c0392b;
    font-weight: 700;
}

.ctt-cc-build__adj-cell-value--over {
    color: #4a8c3f;
    font-weight: 700;
}

.ctt-cc-build__adj-cell-value--met {
    color: #333333;
    font-weight: 700;
}

/* Lock-in banner */
.ctt-cc-build__banner {
    margin-top: 24px;
    padding: 14px 18px;
    border: 1px solid #999999;
    background: #ffffff;
    font-size: 14px;
    color: #333333;
}

.ctt-cc-build__banner--prompt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.ctt-cc-build__banner--locked {
    background: #f5f5f5;
}

.ctt-cc-build__banner-confirm {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    font-weight: 600;
}

.ctt-cc-build__banner-error {
    color: #c0392b;
    margin-left: 12px;
    font-size: 13px;
}

/* Locked overall styling — apply opacity wash to non-banner content. */
.ctt-cc-build__hub--locked .ctt-cc-build__hub-header,
.ctt-cc-build__hub--locked .ctt-cc-build__hub-grid {
    opacity: 0.45;
    filter: grayscale(1);
    pointer-events: none;
}

/* ─── Tour view ──────────────────────────────────────────────────────────── */

.ctt-cc-build__tour {
    padding: 16px 0 48px;
    position: relative;
}

.ctt-cc-build__tour-bar {
    margin-bottom: 12px;
}

/* Layout-only override for the CC Build "Back to Hub" link. Color and states
   come from .ctt-btn / .ctt-btn--secondary applied in JSX. */
.ctt-cc-build__back-link {
    padding: 4px 12px;
    min-height: 32px;
    font-size: 14px;
    font-weight: 500;
}

.ctt-cc-build__surfer {
    position: relative;
    margin: 0 auto;
    max-width: 720px;
}

.ctt-cc-build__photos {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.ctt-cc-build__photo {
    margin: 0;
    position: relative;
    background: #f5f5f5;
    border: 1px solid #dddddd;
    aspect-ratio: 3 / 4;
    overflow: hidden;
}

.ctt-cc-build__photo-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
    display: block;
}

.ctt-cc-build__gender {
    position: absolute;
    top: 8px;
    left: 8px;
    padding: 2px 8px;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #999999;
    font-size: 12px;
    font-weight: 700;
    z-index: 2;
}

/* HUD overlays — only render at L2+ when cheek_palette_sig is set. */
.ctt-cc-build__hud-cell {
    position: absolute;
    border: 1px solid #1a1a1a;
    background: #ffffff;
    padding: 6px 12px;
    font-weight: 700;
    font-size: 14px;
    z-index: 3;
}

/* HUD cell positions per L1 Walkthru.pdf p10/p15 storyboard:
   eye_field on top-center, eye_palette on left-mid, mouth on left-bottom.
   Right and bottom positions reserved for L9+ cheek field/palette. */
.ctt-cc-build__hud-cell--top         { top: -28px; left: 50%; transform: translateX(-50%); }
.ctt-cc-build__hud-cell--left        { left: -56px; top: 25%; }
.ctt-cc-build__hud-cell--left-bottom { left: -56px; bottom: 0; }
.ctt-cc-build__hud-cell--right       { right: -56px; top: 40%; }
.ctt-cc-build__hud-cell--bottom      { bottom: -28px; left: 50%; transform: translateX(-50%); }

.ctt-cc-build__meta {
    margin-top: 12px;
    padding: 12px 16px;
    border: 1px solid #999999;
    background: #ffffff;
    font-size: 14px;
    line-height: 1.55;
}

.ctt-cc-build__meta-row {
    display: flex;
    gap: 8px;
    padding: 2px 0;
}

.ctt-cc-build__meta-label {
    color: #666666;
    font-weight: 600;
    min-width: 90px;
}

.ctt-cc-build__meta-value {
    color: #1a1a1a;
}

.ctt-cc-build__career-bubble {
    display: inline-block;
    padding: 2px 10px;
    border: 1px solid #cccccc;
    border-radius: 999px;
    background: #f5f5f5;
    font-size: 13px;
}

/* Tour controls */
.ctt-cc-build__tour-controls {
    margin: 24px auto 0;
    max-width: 720px;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 16px;
    align-items: start;
}

.ctt-cc-build__tags {
    border: 1px solid #999999;
    padding: 8px 12px;
    margin: 0;
}

.ctt-cc-build__tags-legend {
    font-weight: 700;
    font-size: 13px;
    padding: 0 6px;
}

.ctt-cc-build__tag-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    font-size: 14px;
    cursor: pointer;
}

.ctt-cc-build__sound {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-cc-build__sound-label {
    font-size: 13px;
    color: #666666;
    font-weight: 600;
}

.ctt-cc-build__sound-select {
    border: 1px solid #999999;
    padding: 8px 10px;
    font-size: 14px;
    background: #ffffff;
}

.ctt-cc-build__sound-error {
    font-size: 12px;
    color: #c0392b;
}

.ctt-cc-build__nav {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

/* Layout-only overrides for CC Build tour controls. Color and states come
   from .ctt-btn / .ctt-btn--primary or --secondary applied in JSX. */
.ctt-cc-build__nav-btn {
    padding: 8px 18px;
    font-size: 14px;
}

.ctt-cc-build__nav-btn--end {
    font-weight: 700;
}

.ctt-cc-build__nav-btn--primary {
    font-weight: 700;
}

.ctt-cc-build__counter {
    margin-top: 16px;
    text-align: right;
    font-size: 13px;
    color: #666666;
}

.ctt-cc-build__counter-subline {
    margin-right: 12px;
}

.ctt-cc-build__counter-count {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Profile screens (L5 / L9) */
.ctt-cc-build__profile {
    max-width: 720px;
    margin: 24px auto;
    border: 1px solid #999999;
    padding: 32px;
    background: #ffffff;
}

.ctt-cc-build__profile-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    border-bottom: 1px solid #dddddd;
    padding-bottom: 12px;
    margin-bottom: 16px;
}

.ctt-cc-build__profile-title {
    font-size: 20px;
    font-weight: 700;
    margin: 0;
}

.ctt-cc-build__profile-progress {
    color: #666666;
    font-size: 13px;
}

.ctt-cc-build__profile-body {
    font-size: 15px;
    line-height: 1.7;
    color: #333333;
}

.ctt-cc-build__profile-nav {
    margin-top: 24px;
    display: flex;
    justify-content: space-between;
    gap: 12px;
}

/* ─── Shared button system ──────────────────────────────────────────────────
   Three reusable button categories cover every action across Initial
   Experiments and Prep-Lab L1–L10:

     .ctt-btn--primary    — advance forward (Start, Next, Continue, Enter)
     .ctt-btn--secondary  — go back / skip (Back, Skip)
     .ctt-btn--chip       — selectable cue / palette / answer chip
                            (quiz answers, gender select, gen-cat tour)
     :disabled / [aria-disabled] — applied via attribute on any of the above

   .ctt-btn--answer and .ctt-btn--choice are aliases of --chip kept for
   continuity with existing JSX. .ctt-btn--skip is a low-emphasis variant
   of --secondary used for narration overlays.

   Color is sourced exclusively from the --ctt-brown-* tokens declared on
   #ctt-root. Any new button in M5+ should @apply (or duplicate) one of the
   three base modifiers — never introduce a new bg / hover color rule. */

.ctt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 28px;
    min-height: 44px;
    border: 1px solid transparent;
    border-radius: 6px;
    /* Button-font role: every .ctt-btn variant resolves through the same
       --ctt-font-button variable, which the admin Typography page controls. */
    font-family: var(--ctt-font-button);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0.02em;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
    -webkit-appearance: none;
    appearance: none;
}

.ctt-btn:focus {
    outline: none;
}

.ctt-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(166, 123, 91, 0.45);
}

.ctt-btn:disabled,
.ctt-btn[aria-disabled="true"] {
    opacity: var(--ctt-disabled-alpha);
    cursor: not-allowed;
    transform: none;
}

.ctt-btn:disabled:hover {
    color: #fff !important;
}

/* ─── Primary: filled, advance-forward action ───────────────────────────── */
/* default: brown-mid · hover: brown-dark · active: brown-darkest · disabled: opacity */
.ctt-btn--primary {
    background: var(--ctt-brown-mid);
    color: var(--ctt-text-on-brown) !important;
    border-color: var(--ctt-brown-mid);
}

.ctt-btn--primary:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--ctt-brown-dark);
    border-color: var(--ctt-brown-dark);
    color: var(--ctt-text-on-brown) !important;
}

.ctt-btn--primary:active:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--primary.is-active {
    background: var(--ctt-brown-darkest);
    border-color: var(--ctt-brown-darkest);
    color: var(--ctt-text-on-brown) !important;
    transform: translateY(1px);
}

/* ─── Secondary: outline, go-back / skip action ─────────────────────────── */
/* default: transparent + brown-mid border · hover: brown-pale · active: brown-mid filled */
/* All four state rules are scoped under #ctt-root (specificity +1 in the
   ID column) so Astra theme's `button:focus` / `button:hover` rules
   (specificity 0,1,1) cannot override our brown text colour. Without the
   scope, a Back button becomes focused after click, Astra's button:focus
   white text colour wins, and the text reads as invisible on the
   transparent background. Scoping fixes every secondary button across
   the React app in one place. Primary buttons already use `color
   !important` for the same reason, so they did not need the scope. */
#ctt-root .ctt-btn--secondary {
    background: transparent;
    color: var(--ctt-brown-mid);
    border-color: var(--ctt-brown-mid);
}

#ctt-root .ctt-btn--secondary:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--ctt-brown-pale);
    color: var(--ctt-brown-dark);
    border-color: var(--ctt-brown-mid);
}

#ctt-root .ctt-btn--secondary:active:not(:disabled):not([aria-disabled="true"]),
#ctt-root .ctt-btn--secondary.is-active {
    background: var(--ctt-brown-mid);
    color: var(--ctt-text-on-brown);
    border-color: var(--ctt-brown-mid);
    transform: translateY(1px);
}

/* ─── Chip: selectable palette / cue / answer chip ──────────────────────── */
/* default: white + dark text · hover: brown-pale · active|selected: brown-mid filled */
.ctt-btn--chip,
.ctt-btn--answer,
.ctt-btn--choice {
    background: #ffffff;
    color: var(--ctt-text-on-pale) !important;
    border-color: var(--ctt-text-on-pale);
}

.ctt-btn--chip:hover:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--answer:hover:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--choice:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--ctt-brown-pale);
    color: var(--ctt-text-on-pale) !important;
    border-color: var(--ctt-brown-mid);
}

.ctt-btn--chip:active:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--chip.is-selected,
.ctt-btn--chip[aria-pressed="true"],
.ctt-btn--answer:active:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--answer.is-selected,
.ctt-btn--answer[aria-pressed="true"],
.ctt-btn--choice:active:not(:disabled):not([aria-disabled="true"]),
.ctt-btn--choice.is-selected,
.ctt-btn--choice[aria-pressed="true"] {
    background: var(--ctt-brown-mid);
    color: var(--ctt-text-on-brown) !important;
    border-color: var(--ctt-brown-darkest);
    transform: translateY(1px);
}

/* Answer keeps its own size scale — the chip role provides the colors. */
.ctt-btn--answer {
    min-width: 80px;
    padding: 16px 24px;
    font-size: 18px;
}

/* ─── L0.1 walkthrough alignment: instruction screens, mouth crop, cue desc ─── */

/*
 * Tracker #11 / Cristina May 5 2026 evening bullet 5:
 * Tour and Quiz screens for the Mouth Shape experiment crop the photo to
 * the lower 60% of the face. Cristina updated the spec from 50% to 60% on
 * May 5, 2026, citing the actual pixel measurement she used in her HUD
 * mockup (`SPEC/HUD_Mockups_2026-05-05/Full HUD with Buttons L0.1.pptx`).
 * Wrap aspect-ratio is 5:3 (height = 60% of width on a square source) when
 * the inner image carries crop-mouth, and object-fit:cover with
 * anchor-bottom keeps the lower 60% visible. The same rule applies to the
 * quiz wrap so the crop is consistent between tour and quiz.
 */
/* CR6 (May 7): the cropped mouth-shape frame previously used a 5:3 aspect ratio
   with the source image pinned to the bottom edge (object-position 50% 100%).
   Result: the mouth sat right at the bottom border of the frame with no
   breathing room. Per Cristina's feedback the mouth should occupy the same
   vertical position it does on an uncropped face shot in the L0.1 canon HUD
   — meaning visible empty space above the mouth, with the mouth more
   centered in the frame. Aspect bumped from 5:3 -> 4:3 (taller frame), and
   object-position shifted from 50% 100% -> 50% 75% so the lower face zone
   sits in the middle of the frame instead of the bottom edge. */
/* Legacy wrap selectors removed 2026-05-08. New architecture uses
   .ctt-photo-pair as the photo container; the canonical mouth/eye crop
   rules live near line ~2700 (Crop overrides — must come AFTER the default
   .ctt-photo-pair > img rule). Don't duplicate them here.
   See SCREEN_POSITION_MATRIX.md for the canonical aspect + object-position
   per crop class. */

/*
 * Tracker #15: Bottom cue description box on the Tour screen uses a simple light
 * brown border, NOT the braided rope frame inherited from .ctt-info-textbox.
 * The selector overrides border-image to none so the solid border-color shows.
 */
.ctt-tour__cue-description.ctt-info-textbox {
    border-style: solid;
    border-width: 1px;
    border-color: var(--ctt-brown-mid);
    border-image: none;
    background: #ffffff;
    padding: 12px 16px;
    text-align: center;
}

/*
 * Tracker #9: Slide 10 (GenderSelect) info-blurb textbox stays page-centered
 * regardless of left- or right-stacked cues. Switching the row to a 3-column
 * grid pins the copy box to the middle column so the cues sit in the side
 * columns without pushing the textbox off center.
 *
 * align-items: end (May 5, 2026) — Shai called out the chips were rendering
 * vertically centered against the textbox while L0.1 Walkthru.pdf page 10
 * shows them at the BOTTOM-left of the textbox with chip top at textbox
 * bottom. Switched from `center` to `end` so the chip column bottom-aligns
 * with the textbox bottom.
 *//*
 * Tracker #10/#13/#14/#20: Instruction and transition screens (TourInstruction,
 * QuizInstruction, GenderSwitch, ThankYou) show their body copy in a simple
 * thin-bordered box per the L0.1 walkthrough (slides 11, 15, 17, 24). Centered
 * text, white background, single border. Distinct from .ctt-info-textbox (rope
 * frame for educational info-blurbs on intro slides).
 */
/* These body wrappers ALSO carry .ctt-info-textbox (added in the polish pass)
   which provides the rope border, standardized max-width, and centered margin.
   We keep the inner-layout properties (text-align, line-height, flex sizing)
   but drop border / padding / max-width / width so the rope frame renders.
   align-self stays so the box stays centered inside its flex/grid parent. */
.ctt-tour-instruction__body,
.ctt-quiz-instruction__body,
.ctt-gender-switch__body,
.ctt-thank-you__body {
    align-self: center;
    text-align: center;
    line-height: 1.6;
    flex: 0 0 auto;
}

/*
 * Body paragraphs on the instruction/transition screens render centered with no
 * inline list or rule-row styling. Override the previous default left-aligned
 * paragraphs so the spec layout (three centered lines on slides 11/15/17/24)
 * renders cleanly.
 *//*
 * Tracker #21: ResultScreen body wrapped in the same standard textbox style as
 * the other instruction/transition screens (slides 25 and 26 in L0.1 walkthrough).
 * Heading "Results" inside, score lines + body messages inside, Next button outside.
 *
 * Result/ThankYou/Transition now render on the Stage grid (Tier 4 step 16);
 * the legacy `.ctt-result { display: flex; min-height: var(--ctt-min-screen); }`
 * outer-frame rule was redundant once the Stage owns the page region and has
 * been removed.
 */

/* The element also carries .ctt-info-textbox so the rope braid border
   styling comes from that shared rule. Per CRISTINA_MINDSET.md line 84
   ("Results screen uses the standard textbox style for both score and
   message"). Layout/spacing only here — no border, no background. */
.ctt-result__body {
    padding: 32px 40px;
    /* max-width aligned to the standard textbox token (720px) per Cristina
       feedback 2026-05-12. Earlier hardcoded 560px made the result-screen
       textbox render visibly more square than other textboxes. */
    max-width: var(--ctt-photo-pair-max-width);
    width: 100%;
    text-align: center;
    line-height: 1.6;
    display: flex;
    flex-direction: column;
    gap: 14px;
    flex: 0 0 auto;
}

.ctt-result__heading {
    font-weight: 700;
    text-decoration: underline;
    margin: 0 0 6px;
    font-size: 1.125rem;
    color: #1a1a1a;
}

.ctt-result__score-line {
    margin: 0;
    color: #1a1a1a;
}

.ctt-result__combined {
    margin: 8px 0 4px;
    font-weight: 600;
}

.ctt-result__combined--pass {
    color: #2e7d32; /* spec green */
}

.ctt-result__combined--fail {
    color: #c62828; /* spec red */
}

.ctt-result__message {
    margin: 0;
    color: #1a1a1a;
}

.ctt-result__action-btn {
    align-self: center;
    padding: 12px 32px;
    margin-top: 4px;
}

/*
 * Tracker #3 #4 #5: Info-blurb frame on intro/info-blurb screens.
 *
 * #3 Centered frame, sized to fit the largest blurb in the program (L20 Slide 27,
 *    Accepting Greatness). Per CTT_Client_Profile.md: "all textboxes sized to
 *    Level 20 Slide 27. CSS constraint from day one."
 * #4 Color braided rope border via the existing .ctt-info-textbox class
 *    (border-image Textbox_Border.png 9-slice).
 * #5 Title in Hyperspace Race Capsule font, slightly larger than body, centered
 *    above the body text. Font family is already loaded globally.
 *
 * The L20 Slide 27 sizing is approximated by min-height that comfortably fits the
 * longest known program blurb and prevents layout jitter as the user steps through
 * intro slides with shorter blurbs.
 */
.ctt-intro-screen__body.ctt-info-textbox {
    align-self: center;
    /* max-width aligned to the standard textbox token (720px) per Cristina
       feedback 2026-05-12 (caught the result-screen drift, this is the
       same drift on the intro-screen body). Was 640px hardcoded. */
    max-width: var(--ctt-photo-pair-max-width);
    width: 100%;
    /* min-height is now inherited from the base .ctt-info-textbox rule
       (380px, locked to L20 size) per Cristina round 2 feedback point 3.
       The earlier 320px floor here was the original approximation and
       has been superseded by the program-wide lock on the base class. */
    padding: 36px 44px;
    text-align: center;
    line-height: 1.6;
    color: #1a1a1a;
    /* display/flex-direction/justify-content/align-items now inherited
       from base .ctt-info-textbox rule so the centering math is shared
       across all narrative screens. Only intro-specific gap and padding
       overrides remain here. */
    gap: 18px;
    flex: 0 0 auto;
}

.ctt-intro-screen__body .ctt-intro-screen__cue-list {
    margin: 0;
    align-items: center;
}

/* ─── Layer 4 — Layout primitives (Anchoring Architecture) ───────────────── */

/*
 * StackedScreen — outer frame for any narrated screen. One source of truth
 * for screen padding, vertical column flow, body centering, and CTA placement.
 */
.ctt-stacked-screen {
    display: flex;
    flex-direction: column;
    min-height: var(--ctt-min-screen);
    padding: var(--ctt-screen-padding-y) var(--ctt-screen-padding-x) var(--ctt-screen-bottom-padding);
}

.ctt-stacked-screen__body {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ctt-stack-gap);
    width: 100%;
    max-width: var(--ctt-row-max-width);
    margin: 0 auto;
    position: relative;          /* relative anchor for [data-anchor=...] children */
}

.ctt-stacked-screen__cta {
    display: flex;
    justify-content: center;
    margin-top: var(--ctt-cta-margin-top);
}

/*
 * PhotoPair — two photo slots in a side-by-side grid + relative anchor for
 * any chip positioned with [data-anchor=...]. Locks photo geometry to the
 * --ctt-photo-pair-max-width token so all anchored siblings (caption,
 * footer columns) can mirror the same column structure.
 */
.ctt-photo-pair {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--ctt-photo-gap);
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    margin: 0 auto;
}

.ctt-photo-pair > img,
.ctt-photo-pair > .ctt-sample-image,
.ctt-photo-pair > .ctt-sample-image-frame,
.ctt-photo-pair > picture,
.ctt-photo-pair__col > img,
.ctt-photo-pair__col > .ctt-sample-image,
.ctt-photo-pair__col > .ctt-sample-image-frame,
.ctt-photo-pair__col > picture {
    aspect-ratio: var(--ctt-photo-aspect);
    width: 100%;
    object-fit: cover;
    object-position: 50% 50%;
    /* Two-stop drop shadow per Shai 2026-05-17: photos read as lifted off
       the page rather than floating. Earlier single-shadow 2px/2px/5px/0.10
       was visually too flat; earlier 6px/6px/12px/0.18 single-shadow got
       clipped by the column's overflow:hidden. Now safe to use a larger
       softer shadow because the column was switched to overflow:visible
       in task #35.
         - inner contact halo: 0 3px 8px @ 0.10 — tight ground-shadow
         - outer ambient drop: 0 8px 20px @ 0.16 — soft depth lift
       Combined visible footprint ~20 px past the frame on bottom and
       sides; renders cleanly because the caption box renders later in
       DOM order and sits on top of any bottom overflow. */
    box-shadow:
        0 3px 8px  rgba(0, 0, 0, 0.10),
        0 8px 20px rgba(0, 0, 0, 0.16);
}

/* Frame wrapper for SampleImage (Cristina round 2 point 6 fix).
   The frame is the white-background box that holds the IMG. The IMG
   inside is absolutely positioned to fill the frame; clip-path on the
   IMG masks bands that should render as empty white, and the frame's
   background shows through where the IMG is clipped. Putting
   background:white directly on the IMG (the previous attempt) does not
   work because clip-path removes the entire IMG painted output
   including its own background, leaving a transparent band with the
   page bleeding through.
   overflow:hidden clips any object-fit overflow to the frame geometry
   so neighbours are not displaced. */
.ctt-photo-pair > .ctt-sample-image-frame,
.ctt-photo-pair__col > .ctt-sample-image-frame {
    position: relative;
    display: block;
    background: #ffffff;
    overflow: hidden;
}

/* IMG inside a frame: fills the frame absolutely so clip-path can mask
   in frame-relative coordinates without affecting the frame's own white
   background. object-fit cover with object-position 50% 50% means the
   source image always covers the full frame at the source's natural
   crop centre, so face features land at their reference y-position
   regardless of source dimensions.
   The __col-scoped selector is critical: PhotoPairInline wraps each frame
   in a .ctt-photo-pair__col flex column, so the legacy direct-child
   selector .ctt-photo-pair > .ctt-sample-image-frame no longer matches.
   Without this __col variant the IMG renders at its NATURAL source
   dimensions (because position:absolute + width/height 100% don't apply),
   which is why one photo can render visibly larger than the other on the
   same screen — each source image has its own intrinsic size. */
.ctt-photo-pair > .ctt-sample-image-frame > img,
.ctt-photo-pair__col > .ctt-sample-image-frame > img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
}

/* Crop overrides — must come AFTER the default .ctt-photo-pair > img/.ctt-sample-image
   rule above. Both selectors have specificity (0,2,0); source order breaks the tie.
   Without these rules in source order AFTER the default, the default's
   aspect-ratio: var(--ctt-photo-aspect) (1/1) wins and mouth crop renders as a
   full-face square. Verified via measurement audit 2026-05-08.

   Crop algorithm logic (per Shai 2026-05-08, asymmetric crop):
   - MOUTH crop: trim heavily from TOP, keep BOTTOM (chin + lips visible).
     Container made much wider than tall (3:1) so only the lower band of the
     face fits inside. object-position: 50% 100% pins the BOTTOM of the source
     image to the bottom of the container, so chin sits at the visible bottom
     and the upper-face area gets clipped above.
   - EYE crop: trim heavily from BOTTOM, keep TOP (eyes + brow visible).
     Container made wider than tall (3:1.4) so the upper band shows. Anchored
     at top so eye region sits in frame and lower face gets clipped below. */
/* Mouth crop: aspect 2:1 (354w × 177h on a 720-wide pair, each photo half).
   With object-position 50% 100% pinning image bottom to container bottom,
   the visible band shows the LOWER HALF of the source image — eyes-down
   to chin, with the MOUTH (upper + lower lip + corners) in the middle of
   the visible band. The 3:1 ratio used previously was too aggressive: it
   only showed the bottom 33% of the image, which on typical headshots
   landed just below the lips, cutting the mouth off entirely. 2:1 keeps
   the focus on the mouth while ensuring both lips remain in frame. */
.ctt-photo-pair > .ctt-sample-image--crop-mouth,
.ctt-photo-pair__col > .ctt-sample-image--crop-mouth {
    /* The crop class now lives on the FRAME wrapper, not the IMG. The
       wrapper is the visible white box at 3:4 aspect. clip-path is
       applied to the IMG INSIDE the frame (rule below), not on the
       wrapper, so the wrapper's white background can show through the
       clipped band.
       The __col variant matches the PhotoPairInline structure where the
       frame is nested inside .ctt-photo-pair__col rather than being a
       direct child of .ctt-photo-pair. */
    aspect-ratio: 521 / 656;
}
.ctt-photo-pair > .ctt-sample-image-frame.ctt-sample-image--crop-mouth > img,
.ctt-photo-pair__col > .ctt-sample-image-frame.ctt-sample-image--crop-mouth > img {
    /* Mouth lands at ~70% down a 3:4 portrait. Show only the lower 45%
       of the IMG (clip top 55%). Frame's white shows through the
       clipped top band. */
    object-position: 50% 50%;
    clip-path: inset(55% 0 0 0);
}

/* Full-face wrapper for Exp 2 (Eye Field) + Exp 5 (Core-Cat Configuration).
   Source images probed live 2026-05-09 are consistent ~3:4 portrait aspect
   (range 0.70-0.80 width/height across 8+ samples). Default 1:1 wrapper
   was auto-cropping ~25-30% of vertical face content (hair top + chin)
   inconsistently across samples. Bumping wrapper aspect-ratio to 3/4
   (matching source aspect ~0.75) lets photos render at near-source
   proportions with minimal auto-crop. Photo dimensions become 354×472.
   Scoped to [data-exp="2"] + [data-exp="5"] only — Exp 1/3/4 keep their
   crop-mouth / crop-eye class overrides which set their own aspect-ratio
   at higher specificity. Shai 2026-05-09: "anywhere we see full face
   images, we need a much larger wrapper." */
[data-exp="2"] .ctt-photo-pair > img,
[data-exp="2"] .ctt-photo-pair > .ctt-sample-image,
[data-exp="2"] .ctt-photo-pair > .ctt-sample-image-frame,
[data-exp="2"] .ctt-photo-pair__col > img,
[data-exp="2"] .ctt-photo-pair__col > .ctt-sample-image,
[data-exp="2"] .ctt-photo-pair__col > .ctt-sample-image-frame,
[data-exp="5"] .ctt-photo-pair > img,
[data-exp="5"] .ctt-photo-pair > .ctt-sample-image,
[data-exp="5"] .ctt-photo-pair > .ctt-sample-image-frame,
[data-exp="5"] .ctt-photo-pair__col > img,
[data-exp="5"] .ctt-photo-pair__col > .ctt-sample-image,
[data-exp="5"] .ctt-photo-pair__col > .ctt-sample-image-frame {
    aspect-ratio: 521 / 656;
}

/* Eye crop: aspect 1 / 1 with object-position 50% 0%. Iteration history:
   2 / 1 + 50% 0% (initial): pinned top, showed hair/forehead, eyes hidden.
   2 / 1 + 50% 25% (round 1): eyes visible but band cut off bottom of eyes.
   2 / 1 + 50% 35% (round 2 — wrong approach): re-anchored to push the
     same-sized band downward. Bottom of eyes still clipped on samples
     where eyes sit lower than the assumed 35-45% in source.
   3 / 2 + 50% 0% (round 3): size-based fix at top, 52.8% visible. Eyes
     still at bottom edge with no breathing room.
   4 / 3 + 50% 0% (round 4): 56.3% visible. Eyes at very bottom edge
     on Exp 3 Nai samples — still cut on samples where head sits low
     in source frame. Shai 2026-05-09 verification: "eye crop is not
     okay" with screenshot showing eyes barely peeking at the bottom.
   1 / 1 + 50% 0% (current — robust): visible source-y range = 0%
     to 75% of source on every image. Eye band (regardless of where
     head sits in source frame: 35%, 45%, 55%, 60%) lands well
     inside the visible window with significant breathing room
     below. Wrap dimensions: 354x354 (was 354x266 with 4/3). The
     trade-off is the visible region extends slightly past the
     nose tip on samples where eyes are at typical 35-45% of
     source, but the alternative (smaller wrap) cut eyes on samples
     where head sits low. Robust > tight.
   Affects Exp 3 + Exp 4 (both use .ctt-sample-image--crop-eye via
   CROP_CLASS lookup). */
.ctt-photo-pair > .ctt-sample-image--crop-eye,
.ctt-photo-pair__col > .ctt-sample-image--crop-eye {
    /* The crop class now lives on the FRAME wrapper. clip-path is
       applied to the IMG INSIDE the frame (rule below). The wrapper at
       3:4 + the frame's white background gives us the empty-white
       above/below the visible eye band. */
    aspect-ratio: 521 / 656;
}
.ctt-photo-pair > .ctt-sample-image-frame.ctt-sample-image--crop-eye > img,
.ctt-photo-pair__col > .ctt-sample-image-frame.ctt-sample-image--crop-eye > img {
    /* Eye band lands at ~35% down a 3:4 portrait. Visible band runs
       from y=18% to y=65% of the frame (47% tall band). Eye lands at
       roughly the upper third of the band, with the lower portion
       extending down toward the nose so the band reads as an
       eye-and-bridge crop rather than a pure eye-strip.
       Shai 2026-05-10 round 2: previous bottom inset of 50% cut too
       aggressively; lower line raised down to 35% inset (visible to
       y=65%) per direct walkthrough. The frame's white still shows
       through above (y=0..18%) and below (y=65..100%). */
    object-position: 50% 50%;
    clip-path: inset(18% 0 35% 0);
}

/*
 * Caption — small plain-bordered text box rendered below a PhotoPair.
 * Per L0.x storyboards this is a simple text container, NOT a rope-bordered
 * info-textbox. Width matches the photo pair so it sits visually anchored
 * underneath. Used for cue descriptions on Tour and verdict feedback on Quiz.
 */
.ctt-caption {
    border: 1px solid #1a1a1a;
    background: #ffffff;
    padding: 14px 20px;
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    margin: 0 auto;
    text-align: center;
    font-size: 0.95rem;
    color: var(--ctt-color-text-body);
    line-height: 1.5;
}
/*
 * PhotoPairFooter — three-column footer that anchors its center column
 * to the gap between the two photos. Left/right columns flank.
 *
 * Critical: max-width matches PhotoPair so the center column lands at
 * the photo-gap center regardless of viewport. Eliminates the
 * `--photo-center-offset` empirical fudge.
 */
.ctt-photo-footer {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 16px;
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    margin: 0 auto;
    padding-top: 12px;
}

.ctt-photo-footer__left   { justify-self: start; }
.ctt-photo-footer__center { justify-self: center; display: inline-flex; align-items: center; gap: 12px; }
.ctt-photo-footer__right  { justify-self: end; display: inline-flex; align-items: center; gap: 12px; }

/* ─── Layer 3 — NarratedScreen (Anchoring Architecture) ─────────────────── */

.ctt-narrated-screen__title {
    font-size: clamp(1.5rem, 3.5vw, 2rem);
    font-weight: 700;
    color: var(--ctt-color-text-body);
    line-height: 1.2;
    margin: 0 0 12px;
    text-align: center;
}

.ctt-narrated-screen__body {
    /* .ctt-info-textbox already provides border, max-width, padding, centering. */
    /* Children render in normal body font — title slot is the only Hyperspace exception. */
}

.ctt-narrated-screen__body > p {
    margin: 0;
    text-align: center;
}

.ctt-narrated-screen__body > p + p {
    margin-top: 12px;
}

/* ─── Layer 4 — Prep-Lab primitives (Anchoring Architecture, M5 backport phase A) ── */

/*
 * HubPage — outer frame for Prep-Lab landing/hub pages.
 * Title banner on top, body in the middle, optional footer below.
 */
.ctt-hub-page {
    display: flex;
    flex-direction: column;
    min-height: var(--ctt-min-screen);
    padding: var(--ctt-screen-padding-y) var(--ctt-screen-padding-x) var(--ctt-screen-bottom-padding);
    gap: var(--ctt-stack-gap);
    width: 100%;
    max-width: var(--ctt-row-max-width);
    margin: 0 auto;
}

.ctt-hub-page__title {
    font-size: clamp(1.5rem, 3.5vw, 2rem);
    font-weight: 700;
    color: var(--ctt-color-text-body);
    line-height: 1.2;
    margin: 0;
    text-align: center;
}

.ctt-hub-page__body {
    flex: 1;
}

.ctt-hub-page__footer {
    margin-top: var(--ctt-cta-margin-top);
}

/*
 * HubTable — list of rows with name + primary action + indicator + extras.
 * Used by CCBuild gen-cat list, future CCTrain/CCTest hub lists.
 */
.ctt-hub-table {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ctt-hub-table__columns {
    display: grid;
    grid-template-columns: 1fr auto auto auto auto;
    gap: 16px;
    padding: 8px 12px;
    font-weight: 700;
    color: var(--ctt-color-text-body);
    border-bottom: 1px solid #1a1a1a;
}

.ctt-hub-table__rows {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-hub-table__row {
    display: grid;
    grid-template-columns: 1fr auto auto auto auto;
    gap: 16px;
    align-items: center;
    padding: 8px 12px;
}

.ctt-hub-table__cell--name {
    font-weight: 600;
    color: var(--ctt-color-text-body);
}

.ctt-hub-table--locked .ctt-hub-table__cell--action button {
    opacity: var(--ctt-disabled-alpha);
    pointer-events: none;
}

/*
 * RunScreen — frame for timed-activity screens (CCBuild Tour, future
 * CCTrain Run, CCTest Run). Uses position:relative on the photo anchor
 * so any HUD chip with data-anchor="around-*" resolves against the photo
 * block, not the page.
 */
.ctt-run-screen {
    display: flex;
    flex-direction: column;
    min-height: var(--ctt-min-screen);
    padding: var(--ctt-screen-padding-y) var(--ctt-screen-padding-x) var(--ctt-screen-bottom-padding);
    gap: var(--ctt-stack-gap);
}

.ctt-run-screen__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.ctt-run-screen__photo-anchor {
    position: relative;
    margin: 0 auto;
    width: 100%;
    max-width: var(--ctt-photo-pair-max-width);
    display: flex;
    justify-content: center;
}

.ctt-run-screen__controls {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 16px;
}

.ctt-run-screen__footer-slot {
    margin-top: auto;
}

/*
 * Extended position vocabulary — around-the-photo HUD positions used by
 * CCBuild HudOverlay (and any future Prep-Lab feature with cells positioned
 * outside the photo block edges). Same selector vocabulary as the original
 * 6 positions; just added 5 around-* values for outside-the-edge placement.
 */
[data-anchor="around-top"] {
    top: -12px;
    left: 50%;
    transform: translate(-50%, -100%);
    position: absolute;
}

[data-anchor="around-left"] {
    top: 50%;
    left: -12px;
    transform: translate(-100%, -50%);
    position: absolute;
}

[data-anchor="around-left-bottom"] {
    bottom: 0;
    left: -12px;
    transform: translateX(-100%);
    position: absolute;
}

[data-anchor="around-right"] {
    top: 50%;
    right: -12px;
    transform: translate(100%, -50%);
    position: absolute;
}

[data-anchor="around-bottom"] {
    bottom: -12px;
    left: 50%;
    transform: translate(-50%, 100%);
    position: absolute;
}

/* GenderSelect dual-CTA row inside NarratedScreen cta slot. */
.ctt-gender-select__buttons {
    display: flex;
    gap: 16px;
    justify-content: center;
}

/* ─── Mobile responsiveness — basic tier ──────────────────────────────────
 *
 * Per Shai 2026-05-09 #19: "even if it's bad, it should be mobile responsive.
 * Should not affect desktop and tablet." Breakpoint at 768px covers most
 * phones in portrait and small tablets. Pattern is stack-everything-vertically:
 * photos stack, chip columns drop inline below photos, top-center chips stay
 * above. Desktop layout above 768px is completely untouched — every rule
 * below is inside a single @media query.
 *
 * Trade-offs accepted:
 *   - Photos stack vertically — the side-by-side comparison is lost on
 *     narrow viewports but each photo renders at full available width.
 *   - Chip columns (left / middle-left / right) drop INLINE below the photo
 *     stack, centered. They no longer sit in the gutter beside photos
 *     because narrow viewports have no gutter.
 *   - Hourglass + Back/Next + Counter wrap into a stack.
 *   - Font sizes scale ~10-15% smaller for body copy.
 *   - Page padding tightens to 12px.
 *
 * What this DOES NOT do:
 *   - Optimize chip / hourglass tap target size for small screens
 *   - Optimize landscape mobile layout
 *   - Refactor JSX (no React-side responsive logic — pure CSS)
 *
 * If a screen breaks badly on mobile, the fix is to add a more specific
 * @media rule INSIDE this block, not to change desktop CSS above. */

/* Portal sidebar trigger — hidden on desktop, surfaced below 768px so the
   sidebar can collapse into an off-canvas drawer (M8.0 sidebar collapse). */
.ctt-portal__sidebar-trigger {
    display: none;
}

@media (max-width: 768px) {
    /* Sidebar trigger: top-right floating button, always visible while
       the practitioner is on a portal route. ☰ when closed, ✕ when open. */
    .ctt-portal__sidebar-trigger {
        display: inline-flex;
        position: fixed;
        top: 8px;
        right: 8px;
        z-index: 100;
        width: 40px;
        height: 40px;
        align-items: center;
        justify-content: center;
        background: #ffffff;
        border: 1px solid #1a1a1a;
        border-radius: 4px;
        font-size: 18px;
        color: #1a1a1a;
        cursor: pointer;
    }

    /* Sidebar slides off-screen by default on mobile. The trigger toggles
       the .ctt-portal__body--sidebar-open class on the body wrapper, which
       slides the sidebar back into view. The main content stays full-width
       in both states so the Stage (and its 24x16 grid) gets the full
       380px viewport width per the mobile rule in docs/instructions.md §10. */
    .ctt-portal__body {
        position: relative;
    }

    .ctt-portal__sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: 260px;
        z-index: 90;
        background: #ffffff;
        border-right: 1px solid #cccccc;
        box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
        transform: translateX(-100%);
        transition: transform 200ms ease-in-out;
        overflow-y: auto;
        padding-top: 56px;
    }

    .ctt-portal__body--sidebar-open .ctt-portal__sidebar {
        transform: translateX(0);
    }

    /* Main content full-width on mobile; the off-canvas sidebar overlays
       rather than pushing. */
    .ctt-portal__main {
        padding: 16px;
    }

    /* Photo pair on mobile: keep 2 columns at standard mobile widths so
       each photo wrapper at the new 3:4 portrait aspect (Cristina round 2
       point 6 fix) does not eat the entire viewport vertically. With the
       previous 1-column stack the wrapper rendered ~680 px tall per photo
       at vw≈600, pushing the hourglass and Next CTA over 1500 px below
       the fold. 2 columns gives each photo ~half the viewport width,
       which at 3:4 stays around 320 px tall — comfortable on phones.
       Stacking to 1 column only kicks in below 480 px (very small phones,
       the 480 sub-breakpoint below). */
    .ctt-photo-pair {
        gap: 12px;
    }

    /* Photos: full available width, aspect-ratio still applies per crop.
       Frame wrapper (Cristina round 2 point 6 fix) included so it stacks
       at the same width as the legacy direct-img pattern. */
    .ctt-photo-pair > img,
    .ctt-photo-pair > .ctt-sample-image,
    .ctt-photo-pair > .ctt-sample-image-frame,
    .ctt-photo-pair > picture {
        width: 100%;
    }

    /* Chip columns anchored beside photos drop INLINE below the photo stack.
     * Override absolute positioning + negative offsets that worked on desktop. */
    .ctt-photo-pair > [data-anchor="bottom-left"],
    .ctt-photo-pair > [data-anchor="top-left"],
    .ctt-photo-pair > [data-anchor="middle-left"],
    .ctt-photo-pair > [data-anchor="bottom-right"],
    .ctt-photo-pair > [data-anchor="top-right"],
    .ctt-photo-pair > [data-anchor="middle-right"],
    .ctt-photo-pair > [data-anchor="right"],
    .ctt-photo-pair [data-anchor="bottom-left"],
    .ctt-photo-pair [data-anchor="top-left"],
    .ctt-photo-pair [data-anchor="middle-left"],
    .ctt-photo-pair [data-anchor="bottom-right"],
    .ctt-photo-pair [data-anchor="right"] {
        position: static;
        left: auto;
        right: auto;
        top: auto;
        bottom: auto;
        transform: none;
        margin: 12px auto 0;
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        gap: 12px;
        justify-content: center;
        width: 100%;
        max-width: 100%;
    }

    /* Top-center chips (Exp 2): already in flow above photos — keep, just
     * ensure they wrap horizontally if there are many chips on later levels. */
    .ctt-cue-hud[data-anchor="top-center"] {
        flex-wrap: wrap;
    }

    /* Photo footer (hourglass + Back/Next + counter): stack into rows
     * instead of single 3-column grid which clips on narrow viewports. */
    .ctt-photo-footer {
        grid-template-columns: 1fr;
        gap: 8px;
        text-align: center;
    }

    /* Textbox padding tightens — rope border at 42px is too much on mobile.
     * Border-image still renders but content has more breathing room. */
    .ctt-info-textbox {
        padding: 24px 16px;
    }

    /* Body text scales down ~10-15% for readable line-length on narrow screens. */
    .ctt-info-textbox p,
    .ctt-narrated-screen__body p,
    .ctt-caption {
        font-size: 0.92em;
        line-height: 1.5;
    }

    /* Experiment shell padding tightens. */
    .ctt-experiment-shell {
        padding: 12px;
    }

    /* 5-card selection screen: stack cards vertically. */
    .ctt-card-selector {
        grid-template-columns: 1fr;
        gap: 12px;
    }

    /* Save Prompt fields: full-width on mobile. */
    .ctt-save-prompt__form {
        width: 100%;
    }

    .ctt-save-prompt__input {
        width: 100%;
        box-sizing: border-box;
    }
}

/* ─── Mobile responsiveness — extension pass 2 (2026-05-09) ─────────────────
 *
 * Static audit findings against tag v-exp1-cristina-feedback-fixed.
 * Each rule has a comment explaining the issue it fixes (severity in brackets).
 * Desktop and tablet styles above 768px are NOT modified. Every rule below
 * is inside this single @media block.
 *
 * Reference: Output/MOBILE_AUDIT_2026-05-09.md.
 */
@media (max-width: 768px) {

    /* B1: 5-card landing grid forced to 1 column up to the full mobile breakpoint.
       The existing rule at line 250 only kicks in below 480px, so phones 481-767px
       see a 2/3-col auto-fill grid that overflows on narrow widths. */
    .ctt-landing__grid {
        grid-template-columns: 1fr;
        gap: 14px;
    }

    /* P1: Tighten landing padding for the 481-767px band the legacy 480px rule
       does not reach. */
    .ctt-landing {
        padding: 32px 16px 56px;
    }
    .ctt-landing__header {
        margin-bottom: 28px;
    }

    /* B3 + B4: Login widget submit button (also used by the landing Begin button).
       Fixed 282px width prevents the button from shrinking inside narrow viewports
       and combined with the form max-width creates horizontal scroll on 320-360px
       phones. Switch to fluid width capped at 282px so it shrinks gracefully. */
    .ctt-login-form .ctt-login-submit {
        width: 100%;
        min-width: 0;
        max-width: 282px;
    }
    .ctt-login-form {
        max-width: 100%;
    }

    /* B12 + M2 + M14: Rope textbox border at 42px eats roughly 25% of viewport
       width per side on phones. Cap to 24px so content area is usable. The
       border-image still renders the rope braid pattern, just sliced into a
       thinner band.
       Cristina round 2 point 3 follow-up: min-height 380px from base rule
       wastes scarce vertical space on phones. Drop it on mobile so the
       textbox sizes to its content. The L20 visual-consistency lock is a
       desktop-only requirement; phones reset to flex-to-content. */
    .ctt-info-textbox {
        --ctt-textbox-border-width: 24px;
        border-width: 24px;
        padding: 18px 14px;
        min-height: 0;
    }

    /* B5: IntroScreen forced min-height of 320px wastes vertical real estate
       on phones and pushes Next below the fold. Let the box size to content. */
    .ctt-intro-screen__body.ctt-info-textbox {
        min-height: 0;
        padding: 22px 16px;
    }

    /* B6: Hyperspace title sizes can blow past viewport on long titles
       ("PALETTE OF AD EYES" etc). Cap below the desktop clamp lower bound. */
    .ctt-narrated-screen__title,
    .ctt-experiment-intro__title,
    #ctt-root .ctt-save-prompt__title,
    .ctt-result__heading,
    .ctt-page-title-banner {
        font-size: 1.25rem;
        line-height: 1.2;
    }
    .ctt-landing__title {
        font-size: 1.5rem;
    }

    /* B7 + B8: The instruction screens (Tour Instr / Quiz Instr / GenderSelect /
       GenderSwitch) wrap content in .ctt-photo-anchor (ghost) NOT .ctt-photo-pair.
       The existing mobile rule at line 3208 only resets data-anchor children of
       .ctt-photo-pair. Add equivalent reset for .ctt-photo-anchor children so
       the chip lands inline below the body content instead of off-screen at
       left: -114px. */
    .ctt-photo-anchor > [data-anchor="bottom-left"],
    .ctt-photo-anchor > [data-anchor="top-left"],
    .ctt-photo-anchor > [data-anchor="middle-left"],
    .ctt-photo-anchor > [data-anchor="bottom-right"],
    .ctt-photo-anchor > [data-anchor="right"],
    .ctt-photo-anchor [data-anchor="bottom-left"],
    .ctt-photo-anchor [data-anchor="top-left"],
    .ctt-photo-anchor [data-anchor="middle-left"],
    .ctt-photo-anchor [data-anchor="bottom-right"],
    .ctt-photo-anchor [data-anchor="right"] {
        position: static;
        left: auto;
        right: auto;
        top: auto;
        bottom: auto;
        transform: none;
        margin: 12px auto 0;
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        gap: 12px;
        justify-content: center;
        width: 100%;
        max-width: 100%;
    }

    /* B9: Exp 5 Quiz Active right-column chip block has a higher-specificity
       desktop rule (.ctt-photo-pair.ctt-quiz__sample-row--right-column >
       .ctt-cue-hud[data-anchor="right"]) that wins over the generic mobile
       reset and pins the chip column at right: -112px (off-screen). Override
       with matching specificity inside the mobile block. */
    .ctt-photo-pair.ctt-quiz__sample-row--right-column > .ctt-quiz__hud,
    .ctt-photo-pair.ctt-quiz__sample-row--right-column > .ctt-cue-hud[data-anchor="right"] {
        position: static;
        right: auto;
        top: auto;
        transform: none;
        margin: 12px auto 0;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: 12px;
        width: 100%;
        max-width: 100%;
    }

    /* M3: GenderSelect Female/Male pair stacks vertically below 360px to give
       full-width tap targets. Above 360 they remain side-by-side per the
       existing flex-wrap rule. */
    .ctt-gender-select__buttons {
        flex-wrap: wrap;
        gap: 12px;
    }
    .ctt-gender-select__btn {
        flex: 1 1 140px;
        padding: 14px 20px;
    }

    /* M7 + JSX-1: Hourglass readout depends on :hover, which never fires on
       touch. Force the readout visible on mobile so practitioners can read the
       remaining time without a hover affordance. */
    .ctt-hourglass-timer__readout {
        opacity: 1;
        position: static;
        margin-left: 6px;
        transform: none;
    }
    .ctt-hourglass-timer {
        cursor: default;
    }

    /* M8 + M9: Tour/Quiz footer counter + nav restack centered when the photo
       footer collapses to 1 column. Avoids the "right-aligned in a now-centered
       column" misalignment. */
    .ctt-tour__counter,
    .ctt-quiz__counter {
        text-align: center;
        margin-top: 8px;
    }
    .ctt-tour__nav,
    .ctt-quiz__nav {
        justify-content: center;
        flex-wrap: wrap;
        gap: 8px;
    }
    .ctt-tour__nav-btn,
    .ctt-quiz__nav-btn {
        flex: 0 1 auto;
        min-width: 120px;
    }

    /* M11 + M13: Result/Save body padding crushes content on narrow viewports.
       Reduce horizontal padding so the rope-border + reduced inner padding
       still leaves a usable content area. */
    .ctt-result,
    .ctt-save-prompt {
        padding: 16px 12px 24px;
    }
    .ctt-result__body {
        padding: 18px 14px;
        max-width: 100%;
    }
    .ctt-save-prompt__box {
        padding: 18px 14px;
    }

    /* P5 + general: Experiment shell padding tightens below 768. The existing
       rule at line 3265 sets padding: 12px on .ctt-experiment-shell, which
       targets the OUTER grid container — the body inside still has 32px 24px
       (line 358). Tighten the body too. */
    .ctt-experiment-shell__body {
        padding: 16px 12px;
    }

    /* General: Caption width on mobile has nothing to constrain it because the
       photos collapsed to 1 column already. Keep readable font and tighten
       padding so it doesn't dwarf the photo above. */
    .ctt-caption {
        padding: 10px 12px;
        font-size: 0.92rem;
    }

    /* General: ExperimentIntro container max-width was 640px desktop. On
       mobile let it span the viewport with smaller padding. */
    .ctt-experiment-intro {
        padding: 24px 16px 40px;
        max-width: 100%;
    }

    /* General: Save-prompt option rows widen to fill available area instead of
       capping at 420px (which left them visually narrow inside the rope box). */
    .ctt-save-prompt__option {
        max-width: 100%;
    }

}

/* ─── End Tour Confirmation Modal (CO02) ────────────────────────────────────
   Shown when a practitioner clicks Next on the last sample of a cue tour.
   Sits on top of the tour screen at fixed position covering the viewport.
   Only the dialog itself is interactive; the backdrop is click-to-cancel
   to match the Escape key and Cancel button behavior. */

.ctt-end-tour-confirm {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

.ctt-end-tour-confirm__backdrop {
    position: absolute;
    inset: 0;
    /* Opacity raised 0.55 to 0.78 on 2026-05-27 after L0.1 playthrough
       feedback flagged the underlying photos as still visible at 0.55.
       Same dark-brown tint, just stronger mute on the background so the
       eye locks onto the panel without the Stage photos competing. */
    background: rgba(28, 20, 14, 0.78);
    cursor: pointer;
}

.ctt-end-tour-confirm__panel {
    position: relative;
    z-index: 1;
    background: #fdfaf4;
    border: 2px solid var(--ctt-brown-dark, #785841);
    border-radius: 10px;
    max-width: 460px;
    width: 100%;
    padding: 28px 32px 24px;
    box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.35);
    color: var(--ctt-text-on-pale, #333);
}

/* Scoped under #ctt-root so this rule beats Astra's element-level h2
   sizing without leaking !important or affecting any heading outside the
   CTT React app. Astra's selector for h2 is element-only (0,0,1
   specificity); adding #ctt-root raises ours to (1,1,0) and wins the
   cascade. Same pattern applies to any other modal/dialog title inside
   the React app that needs to override theme defaults. */
#ctt-root .ctt-end-tour-confirm__title {
    font-family: var(--ctt-font-title, var(--ctt-font-hyperspace));
    /* 1.1rem keeps the title proportional inside a 460px modal. Hyperspace
       capsule glyphs are visually taller than typical sans, so 1.5rem
       read as oversized. */
    font-size: 1.1rem;
    line-height: 1.25;
    margin: 0 0 10px;
    color: var(--ctt-brown-darkest, #523e2e);
    letter-spacing: 0.02em;
}

.ctt-end-tour-confirm__body {
    font-size: 1rem;
    line-height: 1.5;
    /* 40px = one full body line of breathing room between the question
       and the Cancel/Continue action row. */
    margin: 0 0 40px;
    color: var(--ctt-text-on-pale, #333);
}

.ctt-end-tour-confirm__actions {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    flex-wrap: wrap;
}

/* The buttons inherit base .ctt-btn styles. These overrides give the modal
   actions tighter padding than the screen-level CTAs so the dialog reads
   as a focused, secondary surface and not a full screen action bar. */
.ctt-end-tour-confirm__btn {
    min-width: 110px;
    padding: 10px 22px;
    font-size: 0.95rem;
}

/* Mobile: stack the buttons full-width so they are easier to tap. Keeps
   primary Continue at the bottom (visual emphasis on the explicit action),
   secondary Cancel above it. The flex container in __actions reverses on
   narrow screens. */
@media (max-width: 480px) {
    .ctt-end-tour-confirm__panel {
        padding: 22px 20px 20px;
    }

    .ctt-end-tour-confirm__actions {
        flex-direction: column-reverse;
    }

    .ctt-end-tour-confirm__btn {
        width: 100%;
    }
}

/* ==========================================================================
   CC Train — Hub + Run Session
   ========================================================================== */

.ctt-cc-train__loading {
    padding: 32px;
    text-align: center;
    font-size: 1rem;
    color: #555;
}

/* Hub
   -------------------------------------------------------------------------- */

.ctt-cc-train__hub {
    display: flex;
    flex-direction: column;
    gap: 24px;
    padding: 16px 24px 32px;
}

.ctt-cc-train__hub-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    border-bottom: 1px solid #1a1a1a;
    padding-bottom: 12px;
}

.ctt-cc-train__hub-heading {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-cc-train__hub-sub {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: #1a1a1a;
}

.ctt-cc-train__run-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 640px;
}

.ctt-cc-train__run {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) 80px 24px 96px;
    align-items: center;
    gap: 12px;
}

.ctt-cc-train__run--locked {
    opacity: 0.45;
}

.ctt-cc-train__run-name {
    padding: 6px 10px;
    border: 1px solid #1a1a1a;
    border-radius: 4px;
    font-size: 0.95rem;
    background: #fff;
}

.ctt-cc-train__run--complete .ctt-cc-train__run-name,
.ctt-cc-train__run--locked .ctt-cc-train__run-name {
    background: #f3f3f3;
}

.ctt-cc-train__run-btn {
    width: 100%;
}

.ctt-cc-train__run-checkbox {
    width: 18px;
    height: 18px;
    border: 1px solid #1a1a1a;
    border-radius: 3px;
    background: #fff;
    display: inline-block;
}

.ctt-cc-train__run-checkbox--filled {
    background: #1a1a1a;
}

.ctt-cc-train__run-repeat {
    padding: 4px 10px;
    font-size: 0.85rem;
}

.ctt-cc-train__test-cta {
    margin: 8px 0 0;
    padding: 12px 16px;
    border: 1px solid #1a1a1a;
    border-radius: 4px;
    background: #f8f8f8;
    text-align: center;
    max-width: 640px;
}

/* Run session
   -------------------------------------------------------------------------- */

.ctt-cc-train__session {
    display: flex;
    flex-direction: column;
    gap: 24px;
    padding: 16px 24px 24px;
    min-height: 100%;
}

.ctt-cc-train__session-body {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24px;
    flex: 1 1 auto;
    flex-wrap: wrap;
}

.ctt-cc-train__answers {
    display: flex;
    flex-direction: column;
    gap: 16px;
    align-self: center;
}

.ctt-cc-train__answer {
    min-width: 80px;
    padding: 12px 18px;
    font-size: 1rem;
    font-weight: 600;
    background: #fff;
    border: 1px solid #1a1a1a;
    border-radius: 6px;
    cursor: pointer;
}

.ctt-cc-train__answer:hover:not(:disabled) {
    background: #f0f0f0;
}

.ctt-cc-train__answer:disabled {
    cursor: default;
    opacity: 0.6;
}

.ctt-cc-train__answer--chosen {
    background: #1a1a1a;
    color: #fff;
}

.ctt-cc-train__stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    flex: 0 1 720px;
}

.ctt-cc-train__photos {
    display: flex;
    gap: 12px;
    justify-content: center;
}

.ctt-cc-train__photo {
    width: 320px;
    height: 320px;
    object-fit: cover;
    border-radius: 6px;
    background: #f3d6c4;
    display: block;
}

/* Per CTT_API_Contract.md:193 — Mouth runs crop to mouth, Eye Palette runs crop
 * to eyes, all other run types use the full face. object-position shifts the
 * cropped focal area; object-fit: cover already enlarges to fill the box. */
.ctt-cc-train__photo--crop-none  { object-position: center center; }
.ctt-cc-train__photo--crop-mouth { object-position: center 85%; transform: scale(1.4); transform-origin: center 85%; }
.ctt-cc-train__photo--crop-eyes  { object-position: center 28%; transform: scale(1.4); transform-origin: center 28%; }

.ctt-cc-train__feedback {
    width: 100%;
    max-width: 660px;
    min-height: 64px;
    padding: 10px 14px;
    border: 1px solid #1a1a1a;
    border-radius: 6px;
    background: #fff;
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
}

.ctt-cc-train__feedback--correct {
    border-color: #1f7a3a;
    background: #eaf6ee;
}

.ctt-cc-train__feedback--incorrect {
    border-color: #b13a3a;
    background: #fbeaea;
}

.ctt-cc-train__feedback-status {
    font-weight: 700;
    font-size: 1rem;
}

.ctt-cc-train__feedback-label {
    font-size: 0.95rem;
}

.ctt-cc-train__session-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding-top: 16px;
    border-top: 1px solid #ddd;
}

.ctt-cc-train__exit-btn {
    align-self: flex-start;
}

.ctt-cc-train__sound {
    flex: 0 1 auto;
}

.ctt-cc-train__counter {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    font-size: 0.9rem;
}

.ctt-cc-train__counter-label {
    color: #555;
}

.ctt-cc-train__counter-pos {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Completion screen
   -------------------------------------------------------------------------- */

.ctt-cc-train__complete {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 48px 24px;
    text-align: center;
}

.ctt-cc-train__complete-heading {
    margin: 0;
    font-size: 1.5rem;
}

.ctt-cc-train__complete-body {
    margin: 0;
    max-width: 540px;
}

.ctt-cc-train__complete-actions {
    display: flex;
    gap: 12px;
}

/* ==========================================================================
   CC Test — Hub + Live Test + Score Summary + Review
   ========================================================================== */

.ctt-cc-test__loading {
    padding: 32px;
    text-align: center;
    font-size: 1rem;
    color: #555;
}

.ctt-cc-test__gate-locked {
    padding: 32px 24px;
    max-width: 640px;
    margin: 16px auto;
    border: 1px solid #1a1a1a;
    border-radius: 6px;
    background: #f8f8f8;
    text-align: center;
}

/* Hub
   -------------------------------------------------------------------------- */

.ctt-cc-test__hub {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 16px 24px 32px;
}

.ctt-cc-test__hub-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    border-bottom: 1px solid #1a1a1a;
    padding-bottom: 12px;
}

.ctt-cc-test__hub-heading {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-cc-test__hub-sub {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: #1a1a1a;
}

.ctt-cc-test__hub-col-titles {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) 80px 24px 80px 96px;
    gap: 12px;
    padding-bottom: 4px;
    max-width: 720px;
}

.ctt-cc-test__hub-col-title {
    font-weight: 700;
    font-size: 0.85rem;
    text-decoration: underline;
}

.ctt-cc-test__run-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 720px;
}

.ctt-cc-test__run {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) 80px 24px 80px 96px;
    align-items: center;
    gap: 12px;
}

.ctt-cc-test__run--locked {
    opacity: 0.45;
}

.ctt-cc-test__run-name {
    padding: 6px 10px;
    border: 1px solid #1a1a1a;
    border-radius: 4px;
    font-size: 0.95rem;
    background: #fff;
}

.ctt-cc-test__run--passed .ctt-cc-test__run-name,
.ctt-cc-test__run--locked .ctt-cc-test__run-name {
    background: #f3f3f3;
}

.ctt-cc-test__run-btn {
    width: 100%;
}

.ctt-cc-test__run-checkbox {
    width: 18px;
    height: 18px;
    border: 1px solid #1a1a1a;
    border-radius: 3px;
    background: #fff;
    display: inline-block;
}

.ctt-cc-test__run-checkbox--filled {
    background: #1a1a1a;
}

.ctt-cc-test__run-score {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: center;
}

.ctt-cc-test__run-optional {
    display: flex;
    justify-content: flex-start;
}

.ctt-cc-test__run-retake {
    padding: 4px 10px;
    font-size: 0.85rem;
}

.ctt-cc-test__cycle-complete {
    margin: 16px 0 0;
    padding: 12px 16px;
    border: 1px solid #1a1a1a;
    border-radius: 4px;
    background: #f8f8f8;
    text-align: center;
    max-width: 720px;
}

/* Live test + Review (shared session layout)
   -------------------------------------------------------------------------- */

.ctt-cc-test__session {
    display: flex;
    flex-direction: column;
    gap: 24px;
    padding: 16px 24px 24px;
    min-height: 100%;
}

.ctt-cc-test__session-body {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24px;
    flex: 1 1 auto;
    flex-wrap: wrap;
}

.ctt-cc-test__stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    flex: 0 1 720px;
}

.ctt-cc-test__photos {
    display: flex;
    gap: 12px;
    justify-content: center;
}

.ctt-cc-test__photo {
    width: 320px;
    height: 320px;
    object-fit: cover;
    border-radius: 6px;
    background: #f3d6c4;
    display: block;
}

/* Same crop rules as CC Train (mouth/eyes/none). Mirrored here to keep
 * CC Test independent of CC Train's class names if they ever diverge. */
.ctt-cc-test__photo--crop-none  { object-position: center center; }
.ctt-cc-test__photo--crop-mouth { object-position: center 85%; transform: scale(1.4); transform-origin: center 85%; }
.ctt-cc-test__photo--crop-eyes  { object-position: center 28%; transform: scale(1.4); transform-origin: center 28%; }

.ctt-cc-test__feedback {
    width: 100%;
    max-width: 660px;
    min-height: 64px;
    padding: 10px 14px;
    border: 1px solid #1a1a1a;
    border-radius: 6px;
    background: #fff;
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
}

.ctt-cc-test__feedback--correct  { border-color: #1f7a3a; background: #eaf6ee; }
.ctt-cc-test__feedback--partial  { border-color: #b1833a; background: #fbf3e0; }
.ctt-cc-test__feedback--incorrect{ border-color: #b13a3a; background: #fbeaea; }

.ctt-cc-test__feedback-status {
    font-weight: 700;
    font-size: 1rem;
}

.ctt-cc-test__feedback-label {
    font-size: 0.95rem;
}

/* HUD overlay (Core-Cat full HUD)
   -------------------------------------------------------------------------- */

.ctt-cc-test__hud {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
    padding: 6px 8px;
    border: 1px solid #1a1a1a;
    border-radius: 999px;
    background: #fff;
    max-width: 720px;
}

.ctt-cc-test__hud-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1;
    padding: 4px 10px;
    min-width: 64px;
    border-radius: 999px;
    background: #f3f3f3;
}

.ctt-cc-test__hud-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    color: #555;
}

.ctt-cc-test__hud-value {
    font-size: 0.95rem;
    font-weight: 700;
    color: #1a1a1a;
}

/* Footer (timer, sound, nav)
   -------------------------------------------------------------------------- */

.ctt-cc-test__session-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding-top: 16px;
    border-top: 1px solid #ddd;
}

.ctt-cc-test__session-footer-left,
.ctt-cc-test__session-footer-center,
.ctt-cc-test__session-footer-right {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.ctt-cc-test__session-footer-center {
    justify-content: center;
}

.ctt-cc-test__session-footer-right {
    justify-content: flex-end;
}

.ctt-cc-test__hourglass {
    font-size: 1.4rem;
}

.ctt-cc-test__timer {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 1.1rem;
}

.ctt-cc-test__answered {
    font-size: 0.85rem;
    color: #555;
}

.ctt-cc-test__counter {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    font-size: 0.9rem;
}

.ctt-cc-test__counter-pos {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.ctt-cc-test__counter-label {
    color: #555;
    font-size: 0.85rem;
}

.ctt-cc-test__sound {
    flex: 0 1 auto;
}

/* Score summary
   -------------------------------------------------------------------------- */

.ctt-cc-test__summary {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 24px;
    padding: 48px 24px;
    text-align: center;
}

.ctt-cc-test__summary-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-cc-test__summary-heading {
    margin: 0;
    font-size: 1.4rem;
}

.ctt-cc-test__summary-attempt {
    margin: 0;
    color: #555;
    font-size: 0.9rem;
}

.ctt-cc-test__summary-score {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.ctt-cc-test__summary-pct {
    font-size: 4rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.ctt-cc-test__summary-band {
    color: #555;
    font-size: 0.95rem;
}

.ctt-cc-test__summary-badge {
    margin-top: 4px;
    padding: 6px 14px;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.95rem;
    border: 1px solid #1a1a1a;
}

.ctt-cc-test__summary-badge--pass {
    background: #eaf6ee;
    border-color: #1f7a3a;
    color: #1f7a3a;
}

.ctt-cc-test__summary-badge--fail {
    background: #fbeaea;
    border-color: #b13a3a;
    color: #b13a3a;
}

.ctt-cc-test__summary-actions {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
}


/* ─── M7.3: Info Index / Progress / Gallery / Level Completion ──────────────
 * Visual standards from CTT_Design_Spec.pdf:
 *   - Muted academic palette: #fff bg, #1a1a1a titles, #333 body, #666
 *     counters/meta, #4a6fa5 links, #4a8c3f pass, #c0392b fail, #ddd borders.
 *   - Generous whitespace (24-40px section gaps, 16px between paragraphs).
 *   - Centered bordered content boxes, max-width 640px, padding 32px.
 *   - Titles via the existing .ctt-title family (Hyperspace font).
 * Mobile rules live in the @media block at the very end of this file. */

/* ─── Shared modal primitive (info-quiz result, level completion) ─────────── */

.ctt-modal {
    position: fixed;
    inset: 0;
    background: rgba(26, 26, 26, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 24px;
}

.ctt-modal__box {
    background: #ffffff;
    border: 1px solid #999999;
    max-width: 640px;
    width: 100%;
    padding: 32px;
    text-align: center;
    color: var(--ctt-color-text-body);
    font-size: 16px;
    line-height: 1.55;
    outline: none;
}

.ctt-modal__title {
    margin: 0 0 16px;
    font-weight: 700;
    font-size: 20px;
    color: #1a1a1a;
}

.ctt-modal__body p {
    margin: 0 0 12px;
    color: #333333;
}

.ctt-modal__body p:last-child {
    margin-bottom: 0;
}

.ctt-modal__cta {
    margin-top: 24px;
    display: flex;
    justify-content: center;
    gap: 16px;
}

/* ─── L20 Accepting Greatness preview — REMOVE-BEFORE-L1 (CD-10) ──────────
   Style overrides for the standalone L20 preview slide. The preview reuses
   the rope-bordered .ctt-info-textbox primitive from the L0.x design
   language, and pairs it with a bold + underlined title in the canon
   p-27 style. All rules are scoped to the preview's wrapper so they do
   NOT leak into the live L0.x or L1+ experiences. Delete this block when
   the preview is cleaned up per Output/CLEANUP_DEBT.md > CD-10. */
.ctt-l20-preview__ribbon {
    /* Match the experiment-screen ribbon dimensions (752x480 measured on
       intro / quiz_instr / result). The program-wide .ctt-info-textbox base
       rule earlier in this file uses 720x560 (the original client-brief lock),
       but the actual canonized size used on every shipped experiment screen
       is 752x480 — the Stage-cell coords in experiment-hud-layout.js override
       the base. The L20 preview is NOT inside a Stage, so we override the
       base dimensions explicitly here so the preview matches the look of the
       experiments instead of inheriting the older 720x560 lock. */
    margin: 0 auto;
    max-width: 752px;
    width: 100%;
    min-height: 0;
    height: 480px;
}
.ctt-l20-preview__title {
    margin: 0 0 16px;
    font-weight: 700;
    text-decoration: underline;
    text-align: center;
    color: #1a1a1a;
}
.ctt-l20-preview__body {
    margin: 0;
    line-height: 1.7;
    color: #333333;
    text-align: center;
}
.ctt-l20-preview__cta {
    margin-top: 24px;
    text-align: center;
}

/* ─── Info Index ─────────────────────────────────────────────────────────── */

.ctt-info-index {
    max-width: 960px;
    margin: 0 auto;
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.ctt-info-index__loading {
    padding: 48px 0;
    text-align: center;
    color: #666666;
}

.ctt-info-index__header,
.ctt-info-index__quiz-banner {
    border: 1px solid #999999;
    padding: 10px 16px;
}

.ctt-info-index__heading,
.ctt-info-index__quiz-banner-title {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-info-index__quiz-banner-title {
    text-align: center;
    text-decoration: underline;
}

/* Centered bordered card — the dominant primitive for every Info Index screen.
   Max-width 640px per Design Spec content-box rule; padding 32px. */
.ctt-info-index__card,
.ctt-info-index__quiz-card {
    background: #ffffff;
    border: 1px solid #999999;
    max-width: 640px;
    width: 100%;
    margin: 0 auto;
    padding: 32px;
    text-align: center;
    color: #333333;
    font-size: 16px;
    line-height: 1.7;
}

.ctt-info-index__card-title {
    margin: 0 0 16px;
    font-weight: 700;
    font-size: 18px;
    color: #1a1a1a;
    text-decoration: underline;
}

.ctt-info-index__card-body {
    margin: 0 0 16px;
}

.ctt-info-index__card-body:last-child {
    margin-bottom: 0;
}

/* Wrap that lets the narration button float to the top-right of the card area
   without breaking the card's clean centered layout. */
.ctt-info-index__card-wrap {
    position: relative;
}

.ctt-info-index__card-wrap .ctt-narration {
    position: absolute;
    top: 0;
    right: 16px;
    z-index: 1;
}

.ctt-info-index__counter {
    margin: 0;
    text-align: right;
    color: #666666;
    font-size: 14px;
}

.ctt-info-index__nav,
.ctt-info-index__cta {
    display: flex;
    justify-content: center;
    gap: 16px;
}

/* Review screen */
.ctt-info-index__quiz-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-left: 0;
}

.ctt-info-index__score {
    display: inline-flex;
    align-items: center;
    padding: 6px 14px;
    border: 1px solid #999999;
    background: #ffffff;
    font-size: 15px;
    font-weight: 700;
}

.ctt-info-index__score--pass { color: #4a8c3f; }
.ctt-info-index__score--fail { color: #c0392b; }

.ctt-info-index__quiz-asterisk {
    display: inline-block;
    width: 22px;
    height: 22px;
    border: 1px solid #1a1a1a;
    background: #ffffff;
}

.ctt-info-index__quiz-asterisk--filled {
    background: #1a1a1a;
}

.ctt-info-index__banner {
    margin: 0;
    padding: 10px 16px;
    border: 1px solid #999999;
    background: #f5f5f5;
    color: #333333;
    font-size: 15px;
}

.ctt-info-index__review {
    border: 1px solid #dddddd;
    background: #ffffff;
    padding: 24px;
}

.ctt-info-index__review-title {
    margin: 0 0 16px;
    font-weight: 700;
    font-size: 17px;
    color: #1a1a1a;
    text-decoration: underline;
    text-align: center;
}

.ctt-info-index__review-scroll {
    max-height: 480px;
    overflow-y: auto;
    padding-right: 8px;
}

.ctt-info-index__review-item + .ctt-info-index__review-item {
    margin-top: 24px;
    padding-top: 24px;
    border-top: 1px solid #eeeeee;
}

.ctt-info-index__review-item-title {
    margin: 0 0 8px;
    font-weight: 700;
    font-size: 15px;
    color: #1a1a1a;
    text-decoration: underline;
}

.ctt-info-index__review-item-body {
    color: #333333;
    font-size: 15px;
    line-height: 1.7;
}

.ctt-info-index__review-item-body p {
    margin: 0 0 12px;
}

.ctt-info-index__review-footer {
    margin: 16px 0 0;
    text-align: center;
    color: #666666;
    font-size: 14px;
}

/* Quiz question screen */
.ctt-info-index__quiz-legend {
    text-align: center;
    margin-bottom: 24px;
}

.ctt-info-index__quiz-theme {
    display: block;
    font-size: 14px;
    color: #4a6fa5;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: 700;
    margin-bottom: 4px;
}

.ctt-info-index__quiz-question {
    display: block;
    font-size: 17px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-info-index__quiz-options {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
    text-align: left;
}

.ctt-info-index__quiz-options--tf {
    align-items: center;
}

.ctt-info-index__quiz-option-label {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    cursor: pointer;
    color: #333333;
}

.ctt-info-index__quiz-radio {
    accent-color: #4a6fa5;
    margin-top: 3px;
}

.ctt-info-index__quiz-footer {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 16px;
}

.ctt-info-index__quiz-timer {
    justify-self: center;
}

.ctt-info-index__quiz-controls {
    display: flex;
    gap: 12px;
    justify-self: end;
    grid-column: 3;
}

.ctt-info-index__quiz-footer .ctt-info-index__counter {
    grid-column: 3;
    text-align: right;
}

/* ─── Progress ───────────────────────────────────────────────────────────── */

.ctt-progress {
    max-width: 960px;
    margin: 0 auto;
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.ctt-progress__loading {
    padding: 48px 0;
    text-align: center;
    color: #666666;
}

.ctt-progress__header {
    border: 1px solid #999999;
    padding: 10px 16px;
}

.ctt-progress__heading {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-progress__section {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ctt-progress__section-title {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: #1a1a1a;
    text-decoration: underline;
}

.ctt-progress__section-row {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

.ctt-progress__section-label {
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-progress__matrix {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ctt-progress__row {
    display: grid;
    grid-template-columns: 110px 1fr;
    align-items: center;
    gap: 8px;
}

.ctt-progress__row--header {
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-progress__row-label {
    font-size: 14px;
    color: #1a1a1a;
    font-weight: 600;
}

.ctt-progress__row-cells,
.ctt-progress__row-cells--inline {
    display: grid;
    grid-template-columns: repeat(var(--milestone-cols, 10), minmax(0, 1fr));
    align-items: center;
    gap: 6px;
}

.ctt-progress__cell {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px;
    background: #f5f5f5;
    font-size: 12px;
    color: #333333;
}

.ctt-progress__cell--empty {
    background: transparent;
}

.ctt-progress__cell--header,
.ctt-progress__cell--grade {
    justify-content: center;
    font-weight: 700;
    color: #1a1a1a;
    background: transparent;
}

.ctt-progress__cell--reached {
    color: #1a1a1a;
}

.ctt-progress__cell-number {
    font-size: 12px;
}

.ctt-progress__cell-box {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 1px solid #1a1a1a;
    background: #ffffff;
}

.ctt-progress__cell-box--filled {
    background: #1a1a1a;
}

.ctt-progress__quiz-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ctt-progress__quiz-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 4px 0;
    color: #333333;
    font-weight: 700;
    background: #f5f5f5;
    padding: 6px 12px;
}

.ctt-progress__quiz-item--passed {
    background: #ffffff;
}

.ctt-progress__quiz-box {
    display: inline-block;
    width: 18px;
    height: 18px;
    border: 1px solid #1a1a1a;
    background: #ffffff;
    flex-shrink: 0;
}

.ctt-progress__quiz-box--filled {
    background: #1a1a1a;
}

.ctt-progress__quiz-label {
    font-size: 15px;
}

.ctt-progress__paginator {
    display: flex;
    justify-content: flex-end;
}

.ctt-progress__page-toggle {
    min-width: 96px;
}

/* ─── Core-Cat Gallery ───────────────────────────────────────────────────── */

.ctt-gallery {
    max-width: 960px;
    margin: 0 auto;
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.ctt-gallery__loading {
    padding: 48px 0;
    text-align: center;
    color: #666666;
}

.ctt-gallery__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    border: 1px solid #999999;
    padding: 10px 16px;
    flex-wrap: wrap;
}

.ctt-gallery__heading {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-gallery__search {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ctt-gallery__search-input {
    border: 1px solid #999999;
    background: #ffffff;
    padding: 8px 12px;
    font-size: 14px;
    min-width: 220px;
}

.ctt-gallery__search-input:focus {
    outline: none;
    border-color: #4a6fa5;
}

.ctt-gallery__empty {
    border: 1px solid #999999;
    background: #ffffff;
    padding: 32px;
    text-align: center;
    color: #333333;
    max-width: 640px;
    margin: 0 auto;
}

.ctt-gallery__section {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ctt-gallery__section-title {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
    color: #1a1a1a;
}

.ctt-gallery__columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 32px;
    align-items: start;
}

.ctt-gallery__gen-cat-list,
.ctt-gallery__cue-list,
.ctt-gallery__grade-list,
.ctt-gallery__results {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ctt-gallery__gen-cat-row,
.ctt-gallery__cue-row,
.ctt-gallery__grade-row {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 6px 10px;
    border: 1px solid #dddddd;
    background: #ffffff;
}

.ctt-gallery__gen-cat-label,
.ctt-gallery__cue-label,
.ctt-gallery__grade-label {
    font-size: 14px;
    color: #1a1a1a;
    font-weight: 600;
}

.ctt-gallery__gen-cat-combined {
    grid-column: 3;
}

.ctt-gallery__results {
    gap: 8px;
}

.ctt-gallery__result {
    display: flex;
    flex-direction: column;
    padding: 10px 12px;
    border: 1px solid #dddddd;
    background: #ffffff;
}

.ctt-gallery__result-name {
    font-weight: 700;
    color: #1a1a1a;
    font-size: 15px;
}

.ctt-gallery__result-meta {
    color: #666666;
    font-size: 13px;
}

.ctt-gallery__result-career {
    color: #4a6fa5;
    font-size: 13px;
    margin-top: 2px;
}

.ctt-gallery__paginator {
    display: flex;
    justify-content: flex-end;
}

/* ─── Level Completion modal text ────────────────────────────────────────── */

.ctt-level-complete__title {
    margin: 0 0 16px;
    font-weight: 700;
    font-size: 22px;
    color: #1a1a1a;
    text-align: center;
}

.ctt-level-complete__error {
    margin-top: 12px;
    color: #c0392b;
    font-size: 14px;
}

.ctt-level-complete__cancel {
    margin-top: 12px;
}

/* ─── Level HUD System (M8.0) ─────────────────────────────────────────────── */
/*
 * Stage establishes a 24-column × 18-row CSS Grid over the content area.
 *
 * The base Stage is aspect-locked to 830/613 — the canon storyboard content
 * area ratio (~1.354). 24x18 at that ratio yields near-square cells. Parents
 * (e.g. .ctt-experiment-stage) may override width/height to flush the Stage
 * into a specific container, but the 830/613 ratio is the working shape.
 *
 * Per HUD spec §6.2 the Stage is positioned: relative so absolutely-placed
 * children (like the dev grid overlay) anchor to it, not the page.
 */
.ctt-stage {
    position: relative;
    display: grid;
    grid-template-columns: repeat(24, 1fr);
    grid-template-rows:    repeat(18, 1fr);
    width: 100%;
    /* The canon content rectangle is 830x613. The 24x18 grid divides exactly
       that ratio, so a cell read off the labelled storyboard lands at the
       same place live. */
    aspect-ratio: 1686 / 1220;
    min-height: 480px;
}

/* StageCell is a grid item; positioning is via inline style (grid-row/col-start
   + place-self) set by StageCell.js. The wrapper itself uses display:flex so a
   chip's intrinsic image dimensions scale within the spanned cell area. */
.ctt-stage-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    /* Ensure the wrapper itself doesn't get squeezed below its chip image. */
    min-width: 0;
    min-height: 0;
}

/* Dev grid overlay — toggled by Stage.js when ?ctt_grid=on. Renders as
   an absolutely-positioned 24x18 grid on top of the Stage with cell labels.
   pointer-events: none so chips beneath remain clickable while overlay is on. */
.ctt-stage-grid {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(24, 1fr);
    grid-template-rows:    repeat(18, 1fr);
    pointer-events: none;
    z-index: 9999;
}

.ctt-stage-grid__cell {
    border: 1px dashed rgba(180, 30, 30, 0.45);
    display: flex;
    align-items: flex-start;
    justify-content: flex-start;
    padding: 1px 3px;
}

.ctt-stage-grid__label {
    font-size: 9px;
    font-family: monospace;
    color: rgba(180, 30, 30, 0.7);
    line-height: 1;
}

/* LevelHud is a transparent layer of StageCells; it doesn't establish its own
   grid (each StageCell is a child of Stage's grid via portal-style direct
   placement). display: contents removes the wrapper from layout entirely so
   the StageCell children land in Stage's grid as if LevelHud weren't there. */
.ctt-level-hud {
    display: contents;
}

/* Chip body — sits inside StageCell. Active chips render the rope-frame PNG;
   outline placeholders render a dashed rectangle preserving the chip footprint. */
.ctt-level-hud__chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
}

.ctt-level-hud__chip-icon {
    /* Chip image fills its rectangle completely. Use object-fit: fill (not
       contain) so the chip lands at the exact canon-rect dimensions even
       when the source PNG's natural aspect ratio differs from the cell's
       aspect. Matches .ctt-quiz__chip-answer-icon (State C canon) per
       HUD_PLACEMENT_LOGIC.md and Shai 2026-05-18. Prior `contain` made the
       State A chips letterbox to ~88px wide inside a 106px cell, visibly
       narrower than the same chip rendered on State C (quiz_active). */
    width: 100%;
    height: 100%;
    object-fit: fill;
    display: block;
}

.ctt-level-hud__chip--outline {
    border: 1px dashed #999999;
    border-radius: 8px;
    background: transparent;
    color: #999999;
    font-size: 11px;
}

.ctt-level-hud__chip-placeholder-label {
    font-family: monospace;
    letter-spacing: 0.5px;
}

/* ─── Initial Experiment screens on the HUD Stage grid (M8.1 rebuild) ─────── */
/*
 * The 5 Initial Experiment screens (L0.1-L0.5) render EVERY content-area
 * element through the 24x18 HUD Stage via ExperimentStage.js +
 * experiment-hud-layout.js. The Stage IS the content area: the rectangle
 * right of the left gutter and below the "~ Initial Experiments ~" banner.
 *
 * Nothing renders in that rectangle outside the grid. The narration button,
 * info textbox, photo pair, cue chips, hourglass timer, Back/Next nav, and
 * the position counter are all StageCells inside the Stage. The previous
 * migration left the Stage nested inside StackedScreen / PhotoPairFooter
 * wrappers that squeezed and inset it — that is gone.
 *
 * The placement (which cell each element occupies) is set by inline grid
 * styles from StageCell.js — CSS here only controls how each element fills
 * the cell-span it has been given.
 */

/* The experiment-shell body, when it hosts a Stage screen. The base body is
   display:flex + padding:32px 24px (used by the plain screens — thank_you,
   result, transition, loading/error). For Stage screens it must instead host
   the Stage flush: a block, zero padding, container-type:size so the Stage's
   100cqw/100cqh sizing math resolves against this cell. */
.ctt-experiment-shell__body--stage {
    display: block;
    position: relative;
    container-type: size;
    padding: 0;
    overflow: hidden;
}

/* EMERGENCY REVERT 2026-05-21 — the @media block that lived here applied
   min-height: 860px on a `container-type: size` element, which has a CSS
   spec quirk: size containment uses the INTRINSIC block size (ignores
   min-height) to compute container query units. The result was 100cqh = 0,
   which collapsed the stage's `width: min(100cqw, 100cqh * 1686/1220)` to
   width:0, which collapsed every chip and photo on every experiment.
   Removed the @media block to restore production. Vertical responsive
   floor will be re-attempted via a different mechanism (e.g.
   `grid-template-rows: auto minmax(860px, 1fr) auto` on the shell's
   grid, so the body cell gets a definite block size from grid layout
   without putting min-height on the container itself). */

/* Viewport-minimum gate. ExperimentShell swaps the Stage children for this
   message when the viewport is below MIN_VIEWPORT_WIDTH × MIN_VIEWPORT_HEIGHT
   (1440 × 950 per ExperimentShell.js). The gate covers the full stage body
   area, centers the message both axes, and uses the theme body font (via
   --ctt-font-body which resolves to inherit, picking up the admin/theme
   typography). The banner, sidebar, and site chrome continue to render
   normally — only the stage region is replaced. */
.ctt-experiment-shell__viewport-gate {
    width: 100%;
    height: 100%;
    min-height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px;
    box-sizing: border-box;
}

.ctt-experiment-shell__viewport-gate-message {
    font-family: var(--ctt-font-body);
    font-size: 1rem;
    line-height: 1.5;
    color: #1a1a1a;
    text-align: center;
    max-width: 560px;
    margin: 0;
}

/* The experiment Stage — aspect-locked to the canon content ratio (830:613),
   centered horizontally in the body, anchored to the top. Body wider than the
   ratio (most desktop viewports): dead margins fall on the left AND right
   sides so the grid centre aligns with the centered banner above. Cells stay
   square so canon storyboard cell refs paste 1:1 onto the live grid. The 9px
   height difference between canon (614) and a typical live body (605) gets
   clipped at the bottom — acceptable per the project owner. */
.ctt-experiment-stage {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    aspect-ratio: 1686 / 1220;
    width: min(100cqw, calc(100cqh * 1686 / 1220));
    margin: 0;
}

/* The info textbox (ribbon) slot. Sized to the canon photo-outline rect
   via StageCell (cols 4.065..19.254, rows 3.000..13.025) — same area where
   the photo pair sits on State B/C. Inside the slot, the .ctt-info-textbox
   element fills 100% width and 100% height of the rect, overriding its
   global L20-lock max-width and min-height. SavePrompt's box is still the
   carveout (see .ctt-save-prompt__box). */
.ctt-experiment-stage__textbox {
    display: flex;
    align-items: stretch;
    justify-content: stretch;
    pointer-events: none;
}
.ctt-experiment-stage__textbox > * {
    pointer-events: auto;
    width: 100%;
    height: 100%;
    max-width: none;
    min-height: 0;
    margin: 0;
    box-sizing: border-box;
}
/* The actual rope-bordered ribbon inside the StageCell stretches to fill
   the cell rather than capping at the global L20 lock. Padding stays so
   text doesn't kiss the border. overflow stays at the default `visible`
   so content that exceeds the ribbon (currently only the SavePrompt
   create-account form) flows out below the rope border rather than being
   scrolled inside it (per Shai 2026-05-17 — a scrollbar inside the ribbon
   felt wrong; he wants overflowing content to read as a pop-out panel). */
.ctt-experiment-stage__textbox > .ctt-info-textbox,
.ctt-experiment-stage__textbox .ctt-info-textbox {
    max-width: none;
    width: 100%;
    height: 100%;
    min-height: 0;
    margin: 0;
}

/* A StageCell hosting the photo pair on Tour Active / Quiz Active. The pair
   fills the cell-span; its own grid keeps the two photos side by side. */
.ctt-experiment-stage__photos {
    align-items: stretch;
    justify-content: stretch;
}

.ctt-experiment-stage__photos > .ctt-photo-pair {
    width: 100%;
    height: 100%;
    max-width: none;
    margin: 0;
}

.ctt-experiment-stage__photos .ctt-photo-pair__col {
    height: 100%;
    min-height: 0;
    /* overflow: visible (default) so the photo frame's box-shadow renders on
       top / left / right. The frame's bottom overflows the column by ~46 px
       (canon-aspect math) — that bottom overflow is hidden by the caption
       box which renders later in DOM order and sits on top of it. So the
       shadow shows on three sides without coord changes or visible overlap. */
    overflow: visible;
}
.ctt-experiment-stage__photos .ctt-photo-pair__col > .ctt-sample-image-frame {
    height: auto;
    /* flex-shrink: 0 so the frame keeps its aspect-ratio-determined height
       instead of being squeezed by flex to fit available column space. The
       column is overflow: visible (see rule above) so the photo's drop
       shadow renders past the column edges; the caption box renders later
       in DOM order and visually covers any bottom overflow. */
    flex: 0 0 auto;
    min-height: 0;
}

/* Chip slots. Whether a chip is rendered by LevelHud (StageCell children via
   display:contents) or by the QuizScreen renderChip override (StageCell
   wrapping an interactive button), the chip image fills its slot and scales
   with the size token applied to the StageCell span. */
.ctt-experiment-stage__chip-slot {
    pointer-events: auto;
}

/* Narration button slot — the StageCell stretches; the .ctt-narration node
   inside aligns to the top-right of its cell-span. */
.ctt-experiment-stage__narration {
    align-items: flex-start;
    justify-content: flex-end;
}

.ctt-experiment-stage__narration > .ctt-narration {
    margin: 0;
}

/* Hourglass timer slot — fills its cell-span. The wrapper + inner video are
   stretched to 100%/100% (overriding the program-wide 53 px height) so the
   visible hourglass scales to its canon-sized StageCell rather than sitting
   inside it surrounded by transparent space. object-fit: contain keeps the
   video's aspect intact while filling the slot height. */
.ctt-experiment-stage__hourglass {
    display: flex;
    align-items: stretch;
    justify-content: center;
}

.ctt-experiment-stage__hourglass > .ctt-hourglass-timer {
    /* Strip the program-wide --photo-center-offset translate (the StageCell
       already lands on the canon-precise center). */
    transform: none;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-experiment-stage__hourglass .ctt-hourglass-timer__video {
    /* object-fit: contain shows the full hourglass at native aspect (portrait
       128x202, 0.634:1). Canon's slot is landscape (~1.4:1) so there is
       transparent side padding around the hourglass. This is the trade-off
       for showing the full hourglass body including the top and bottom
       wooden bars — using `cover` instead fills the slot but crops the
       top/bottom bars off the hourglass, which is worse (Shai 2026-05-17). */
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: 50% 50%;
    flex: 1 1 auto;
}

/* Back/Next nav slot — the button group fills the cell-span width and the
   buttons sit side by side at its bottom edge. */
.ctt-experiment-stage__nav {
    align-items: flex-end;
    justify-content: center;
}

.ctt-experiment-stage__nav > .ctt-experiment-stage-nav {
    display: flex;
    gap: 8px;
}

/* Canon-split Back / Next button slots (Quiz Active row 16, S16 + V16). The
   StageCell wrapper provides the cell; the inner button fills it. */
.ctt-experiment-stage__nav-back,
.ctt-experiment-stage__nav-next {
    align-items: center;
    justify-content: center;
}

.ctt-experiment-stage__nav-back > *,
.ctt-experiment-stage__nav-next > * {
    margin: 0;
}

/* Cue identifier slot (Quiz Active row 17, N17-S17). Centered text.
   Container-query so font scales with slot width — matches counter behaviour
   so both look balanced across viewport sizes. */
.ctt-experiment-stage__cue-id {
    display: flex !important;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    color: #1a1a1a;
    text-align: center;
    container-type: inline-size;
    line-height: 1.1;
}
.ctt-experiment-stage__cue-id > * {
    font-size: clamp(11px, 1.4cqw, 18px);
}

/* Position counter slot ("N of M"). Canon T17-V17, ~3.7 cols × 0.95 row.
   Body font / body weight / body color so the counter reads as part of the
   running text on the screen (matches the "Male" / "Female" photo labels and
   the "Mouth Shape Palette" caption label — all body style).
   Container-query so the font scales with the slot width and the canon
   proportions are preserved across viewports without overflowing. */
.ctt-experiment-stage__counter {
    display: flex !important;
    align-items: center;
    justify-content: center;
    color: var(--ctt-color-text-body);
    container-type: inline-size;
}
/* Counter centering inside the stage slot. Applies to BOTH the QuizScreen
   class (.ctt-quiz__counter) and the TourScreen class (.ctt-tour__counter)
   so state B and state C render the counter identically. Without the
   .ctt-tour__counter selector, the TourScreen counter inherits its legacy
   text-align:right + grid-column:3 (from the old tour layout) and renders
   stuck to the right side of the stage slot instead of centered. */
.ctt-experiment-stage__counter .ctt-quiz__counter,
.ctt-experiment-stage__counter .ctt-tour__counter {
    font-family: var(--ctt-font-body);
    font-size: clamp(13px, 7cqw, 22px);
    font-weight: 500;
    text-align: center;
    width: auto;
    max-width: none;
    margin: 0;
    line-height: 1.15;
    /* Strip the legacy tour grid-column positioning that would otherwise
       fight the stage's center alignment. */
    grid-column: auto;
    justify-self: center;
}

/* Corner-badge slot (X18). Holds the global narration-mute bell. Position is
   canon — PPT (9.521in, 7.043in) on every Full HUD slide (L0.1, L1-L8, L9,
   L10), mapped through canon-coordinates.js to cell X18 (cols 22.949-23.508,
   rows 17.113-17.687). Stretch the slot so the bell SVG fills its area. */
.ctt-experiment-stage__corner-badge {
    align-items: stretch;
    justify-content: stretch;
}

/* The bell itself. SVG is the L0.1 PowerPoint canon Icons_Bell_M outlined in
   currentColor, so it picks up the surrounding text colour. Click toggles the
   global narration mute (localStorage-backed). The .ctt-bell--muted variant
   adds a diagonal slash over the bell as the standard mute indicator. */
.ctt-bell {
    width: 100%;
    height: 100%;
    color: var(--ctt-brown-mid, #6e4b2c);
    cursor: pointer;
    transition: color 0.15s ease, opacity 0.15s ease;
    outline: none;
}

.ctt-bell:hover,
.ctt-bell:focus-visible {
    color: var(--ctt-brown-dark, #4a311c);
}

.ctt-bell:focus-visible {
    /* Keyboard focus ring — visible enough to find the bell without a tab
       stop indicator overlapping the SVG. */
    box-shadow: 0 0 0 2px rgba(110, 75, 44, 0.35);
    border-radius: 4px;
}

.ctt-bell__icon {
    fill: currentColor;
}

/* Slashed-bell muted state. Slightly dim the bell so the slash reads first as
   "off" rather than competing with the bell silhouette. */
.ctt-bell--muted .ctt-bell__icon {
    opacity: 0.65;
}

.ctt-bell__slash {
    /* Slash drawn in alert-red so the muted state reads instantly via colour
       semantics, not just shape. Overrides the inline stroke="currentColor"
       on the <line> element (CSS specificity beats SVG presentation
       attributes). Same red the existing .ctt-portal__error border uses
       (#c0392b) — keeps the alert palette consistent across the plugin.
       Line geometry (x1,y1,x2,y2 endpoints + strokeWidth=6) is set in the
       JSX; this rule only owns colour + interactivity. */
    stroke: #c0392b;
    pointer-events: none;
}


/* CTA slot — the primary Next / Start / gender-choice button group for the
   non-photo screens. Sits centered in its cell-span. */
.ctt-experiment-stage__cta {
    align-items: center;
    justify-content: center;
}

.ctt-experiment-stage__cta > * {
    margin: 0;
}

/* The single bordered text box rendered below the photos on Tour Active /
   Quiz Active (cue description on tour, verdict + palette label on quiz).
   It is a StageCell now, not a flow sibling. */
.ctt-experiment-stage__caption {
    align-items: stretch;
    justify-content: stretch;
}

.ctt-experiment-stage__caption > .ctt-caption {
    width: 100%;
    height: 100%;
    max-width: none;
    margin: 0;
}

/* Quiz Active chips double as answer buttons. Strip the default button chrome
   so only the rope-framed chip PNG shows, matching canon L0.x p16/p18. */
button.ctt-quiz__chip-answer-btn,
.ctt-btn.ctt-quiz__chip-answer-btn {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 100% !important;
    height: 100% !important;
    padding: 0 !important;
    min-height: 0 !important;
    border: none !important;
    background: transparent !important;
    background-color: transparent !important;
    box-shadow: none !important;
    cursor: pointer !important;
}

button.ctt-quiz__chip-answer-btn:disabled,
.ctt-btn.ctt-quiz__chip-answer-btn:disabled {
    cursor: default !important;
    opacity: 0.65 !important;
}

/* Selected (but not yet committed) answer chip. Per the two-step quiz flow:
   chip click → selected → Next click → committed verdict. The selected state
   gives the practitioner a visible "this is my answer" indicator before they
   hit Next.
   Visual: a soft brown glow underneath the chip via drop-shadow. drop-shadow
   respects the chip PNG's alpha so the glow follows the chip's silhouette
   rather than its bounding box. Two stacked drop-shadows give the glow some
   depth: a tight inner halo plus a wider soft halo. No outline, no scale —
   the glow alone reads as "selected" while the chip art stays at its canon
   position and size. */
.ctt-quiz__chip-answer-btn--selected {
    filter:
        drop-shadow(0 0 6px  rgba(166, 123, 91, 0.75))
        drop-shadow(0 6px 14px rgba(166, 123, 91, 0.45));
    transition: filter 0.18s ease-out;
}
.ctt-quiz__chip-answer-btn--selected:disabled {
    opacity: 1; /* Selected chip stays full opacity during feedback flash */
}

/* Outline (placeholder) chip — same PNG as the active version, faded heavily.
   Renders at the SAME pixel dimensions and silhouette as the active chip,
   just visually backgrounded. Heavier fade than the first version per Shai. */
.ctt-quiz__chip-outline {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.ctt-quiz__chip-outline-icon {
    /* Per HUD_PLACEMENT_LOGIC.md: chip fills its rectangle. object-fit: fill,
       not contain. Outline state adds the desaturate + fade. */
    width: 100%;
    height: 100%;
    object-fit: fill;
    display: block;
    filter: grayscale(100%) opacity(0.15);
}

/* Photo-pair gender label above each photo (canon L0.1 p16 "Male"/"Male"). */
.ctt-photo-pair__col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
}
.ctt-photo-pair__label {
    font-family: var(--ctt-font-body);
    font-size: 18px;
    font-weight: 500;
    color: #1a1a1a;
    line-height: 1.1;
}

/* Two-line caption: verdict on line 1, palette label on line 2.
   Block-level so they stack (canon L0.1 p16 caption box).
   CONSCIOUS DEPARTURE from canon: canon's caption holds chip-pair-name +
   photo description; we render verdict + palette label. See
   HUD_PLACEMENT_LOGIC.md "Conscious departures from canon". */
.ctt-experiment-stage__caption > .ctt-caption {
    /* Vertical stack with comfortable breathing room. The caption StageCell
       gives this box its canon-precise height (~2 grid rows).
       box-sizing: border-box is critical here — the .ctt-caption base style
       declares `border: 1px solid; padding: 14px 20px`, and width/height of
       100% on a content-box element would push the visible box BEYOND the
       StageCell by ~30 px vertically. border-box puts padding and border
       INSIDE the StageCell-sized rectangle so the visible box matches canon
       exactly. */
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 10px 16px;
}

.ctt-experiment-stage__caption .ctt-quiz__feedback-verdict {
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1.15;
    min-height: 1.15em; /* reserves the line even when verdict is empty */
}

.ctt-experiment-stage__caption .ctt-quiz__prompt-palette {
    display: block;
    margin: 0;
    font-size: 1rem;
    font-weight: 500;
    color: #2c2c2c;
    line-height: 1.2;
}

/* Original sibling-rule (kept so non-stage callers still see two-line stack). */
.ctt-quiz__prompt-palette {
    display: block;
    margin-top: 2px;
    font-weight: 500;
    color: #2c2c2c;
}

/* Palette divider — the canon's horizontal dashed separator on the left rail
   between the Mouth Palette (upper 4 chips) and the Eye Palette (lower 4
   chips). Placed pixel-precise from CANON_DECORATIONS.paletteDivider.
   Rendered as an inline SVG with stroke-dasharray="22 9" matching canon's
   measured dash pattern (22-unit dash, 9-unit gap, 4 full dashes across the
   line). preserveAspectRatio="none" stretches the SVG to its container
   width; the dashes scale proportionally with the stage. */
.ctt-experiment-stage__palette-divider {
    display: flex;
    align-items: center;
    justify-content: stretch;
    pointer-events: none;
}

svg.ctt-palette-divider {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.ctt-quiz__chip-answer-icon {
    /* Per HUD_PLACEMENT_LOGIC.md: the chip rectangle defines both position
       and size. The chip image fills the rectangle completely. No letterbox,
       no proportional reasoning. Use object-fit: fill (not contain) so the
       image stretches to the exact box dimensions. Chip PNGs are pre-trimmed
       of transparent margins so distortion is minimal. */
    width: 100%;
    height: 100%;
    object-fit: fill;
    display: block;
}

/* HudDevPage — DEV-only verification surface. Renders the four annotated
   reference layouts so QA can binary-check chip placements. */
.ctt-hud-dev {
    padding: 16px 24px;
}

.ctt-hud-dev__header {
    margin-bottom: 16px;
    border-bottom: 1px solid #e2e2e2;
    padding-bottom: 12px;
}

.ctt-hud-dev__title {
    font-size: 1.1rem;
    margin: 0 0 8px;
}

.ctt-hud-dev__nav {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.ctt-hud-dev__nav-link {
    font-size: 13px;
    padding: 4px 10px;
    border: 1px solid #cccccc;
    border-radius: 4px;
    text-decoration: none;
    color: #1a1a1a;
}

.ctt-hud-dev__nav-link--active {
    background: #1a1a1a;
    color: #ffffff;
    border-color: #1a1a1a;
}

.ctt-hud-dev__hint {
    font-size: 12px;
    color: #666666;
    margin: 0;
}

.ctt-hud-dev__hint code {
    background: #f4f4f4;
    padding: 1px 5px;
    border-radius: 3px;
    font-size: 11px;
}

.ctt-hud-dev__photo-stub {
    width: 100%;
    height: 100%;
    background: #fde2c4;
    border: 1px solid #c08850;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: sans-serif;
    color: #5a3a1a;
    font-size: 14px;
}

/* ─── Mobile — collapse to single columns / readable text ────────────────── */

@media (max-width: 768px) {
    .ctt-info-index,
    .ctt-progress,
    .ctt-gallery {
        padding: 12px;
        gap: 16px;
    }

    .ctt-info-index__card,
    .ctt-info-index__quiz-card {
        padding: 20px 16px;
    }

    .ctt-info-index__quiz-footer {
        grid-template-columns: 1fr;
        gap: 12px;
        text-align: center;
    }

    .ctt-info-index__quiz-controls,
    .ctt-info-index__quiz-footer .ctt-info-index__counter {
        justify-self: center;
        grid-column: 1;
    }

    .ctt-progress__row {
        grid-template-columns: 90px 1fr;
        gap: 6px;
    }

    .ctt-progress__row-cells,
    .ctt-progress__row-cells--inline {
        gap: 3px;
    }

    .ctt-progress__cell-number {
        font-size: 10px;
    }

    .ctt-progress__cell-box {
        width: 10px;
        height: 10px;
    }

    .ctt-gallery__header {
        flex-direction: column;
        align-items: stretch;
    }

    .ctt-gallery__search-input {
        min-width: 0;
        flex: 1;
    }

    .ctt-gallery__columns {
        grid-template-columns: 1fr;
        gap: 16px;
    }
}

/* ─── Full HUD Key Page (standalone L10 canon view) ──────────────────────── */
/*
 * Renders every HUD element at its PowerPoint-exact cell from
 * Output/HUD_COORDINATES.xlsx PLUS the page chrome that lives OUTSIDE the
 * 24×18 Stage (top portal-nav rows + left rail user gutter).
 *
 * Layout layers (outside-in):
 *   page → frame → [top-nav above] [body: left-gutter | Stage 24x18]
 */
.ctt-full-hud-key-page {
    max-width: 1280px;
    margin: 0 auto;
    padding: 24px 24px 80px;
    font-family: inherit;
}

.ctt-full-hud-key__frame {
    background: #ffffff;
    border: 1px solid #d4d4d4;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}

/* Top portal nav — two rows mirroring the canon header */
.ctt-full-hud-key__top-nav {
    border-bottom: 1px solid #2c2c2c;
}
.ctt-full-hud-key__top-nav-row {
    display: flex;
    gap: 28px;
    padding: 8px 16px;
    font-size: 14px;
    color: #2c2c2c;
}
.ctt-full-hud-key__top-nav-row--secondary {
    font-size: 12px;
    color: #555;
    padding-top: 4px;
    padding-bottom: 8px;
}
.ctt-full-hud-key__top-nav-active {
    font-weight: 700;
    text-decoration: underline;
}

/* Body row — left gutter + Stage side-by-side */
.ctt-full-hud-key__body {
    display: grid;
    grid-template-columns: 160px 1fr;
    align-items: stretch;
}

/* Left gutter — chrome OUTSIDE the Stage grid */
.ctt-full-hud-key__left-gutter {
    border-right: 1px solid #2c2c2c;
    padding: 12px 12px;
    font-size: 12px;
    color: #2c2c2c;
    background: #fafafa;
}
.ctt-full-hud-key__user-block {
    border-bottom: 1px solid #2c2c2c;
    padding-bottom: 8px;
    margin-bottom: 8px;
}
.ctt-full-hud-key__username {
    font-weight: 700;
    font-size: 13px;
}
.ctt-full-hud-key__user-level {
    color: #555;
    font-size: 11px;
    margin-top: 2px;
}
.ctt-full-hud-key__requirements {
    list-style: none;
    padding: 0;
    margin: 0 0 12px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.ctt-full-hud-key__req-heading {
    text-decoration: underline;
    font-weight: 600;
    margin-bottom: 2px;
}
.ctt-full-hud-key__req-done {
    color: #555;
}
.ctt-full-hud-key__req-active {
    font-weight: 700;
}
.ctt-full-hud-key__show-more {
    border: 1px solid #2c2c2c;
    background: #ffffff;
    padding: 6px 12px;
    font-family: var(--ctt-font-button);
    font-size: 12px;
    cursor: default;
}

.ctt-full-hud-key__heading {
    margin-bottom: 24px;
}

.ctt-full-hud-key__heading h1 {
    font-family: var(--ctt-font-title);
    font-size: 28px;
    line-height: 1.2;
    margin: 0 0 8px;
}

.ctt-full-hud-key__subhead {
    font-size: 14px;
    color: #555;
    max-width: 70ch;
    margin: 0;
}

.ctt-full-hud-key__subhead code {
    background: #f4f4f4;
    padding: 1px 5px;
    border-radius: 3px;
    font-size: 12px;
}

/* Stage wrapper — Stage.js's base .ctt-stage already enforces 830/613
   aspect-ratio and 100% width; this just gives it a background so the
   placement is visible against the gutter. */
.ctt-full-hud-key__stage {
    background: #ffffff;
}

/* Chip slots — same behavior as ExperimentStage chip slots */
.ctt-full-hud-key__chip {
    /* StageCell already places + sizes us; we just need the icon to fill */
}

.ctt-full-hud-key__chip-icon {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    display: block;
}

/* Photos */
.ctt-full-hud-key__photo-slot {
    overflow: hidden;
}

.ctt-full-hud-key__photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Caption ribbon */
.ctt-full-hud-key__caption-slot {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-full-hud-key__caption {
    width: 100%;
    height: 100%;
    border: 1px solid #2c2c2c;
    border-radius: 2px;
    background: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 4px 10px;
    font-size: 13px;
    line-height: 1.2;
    box-sizing: border-box;
}

.ctt-full-hud-key__caption-verdict {
    font-weight: 600;
    color: #0a6b1e;
    margin-right: 6px;
}

.ctt-full-hud-key__caption-answer {
    color: #c1761d;
    font-style: italic;
    font-weight: 600;
}

/* Hourglass */
.ctt-full-hud-key__hourglass {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-full-hud-key__hourglass-icon {
    font-size: 24px;
    color: #c1761d;
    line-height: 1;
}

/* Cue identifier */
.ctt-full-hud-key__cue-id-slot {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-full-hud-key__cue-id {
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    color: #2c2c2c;
    line-height: 1.2;
}

/* Counter */
.ctt-full-hud-key__counter-slot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

.ctt-full-hud-key__counter {
    font-size: 12px;
    color: #555;
    font-style: italic;
}

/* Back / Next nav buttons */
.ctt-full-hud-key__nav-slot {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-full-hud-key__nav-btn {
    border: 1px solid #2c2c2c;
    background: #ffffff;
    color: #2c2c2c;
    padding: 2px 12px;
    font-size: 13px;
    line-height: 1;
    cursor: default;
}

/* Quiz Active Back/Next nav buttons.
   Visual styling (background, border, color, hover, focus, disabled) comes
   from the canonical .ctt-btn--secondary (Back) and .ctt-btn--primary (Next)
   styles defined earlier in this file. The brown-* tokens and
   --ctt-font-button variable are populated from the CTT admin Settings /
   Typography page so the buttons stay in sync with the rest of the program.
   The rules below ONLY handle Stage-specific concerns: fill the fractional
   StageCell wrapper, scale the font with the slot. They do NOT redeclare
   color, background, or border — those flow through from the brown-button
   system. */
.ctt-btn.ctt-quiz__nav-btn {
    /* Fill the StageCell slot so the canon-precise rectangle defines the
       button's outer size. */
    width: 100%;
    height: 100%;
    /* Allow the button to shrink below the base .ctt-btn min-height
       (44px) because the canon nav row is shorter than that. The brown-
       button colour rules apply unchanged. */
    min-height: 0;
    /* Tight padding so the brown background fills the canon rectangle
       rather than the inner text floating in a tiny island. */
    padding: 0;
    /* Font size scales with the StageCell width via the wrapper's
       container query (see .ctt-experiment-stage__nav-* below). */
    font-size: clamp(11px, 1.4cqw, 18px);
    line-height: 1.1;
    white-space: nowrap;
}

/* Tour-final Next button (label "Continue to Quiz" / "Next Cue") is wider
   than the canon Next-button slot that's sized for the short "Next" label
   on the per-sample Tour and Quiz nav. The .ctt-btn.ctt-quiz__nav-btn rule
   above forces width:100% of the StageCell, which clips long labels. The
   Tour-side button carries both .ctt-tour__nav-btn AND .ctt-quiz__nav-btn
   classes (TourScreen.js Next button uses the shared quiz nav style); this
   selector is unique to it. Let the tour button keep width:100% as a
   minimum so single-word labels still fill the slot, but grow rightward
   past the slot edge for the long labels. Small horizontal padding gives
   the brown background breathing room around the text. */
.ctt-btn.ctt-tour__nav-btn.ctt-quiz__nav-btn {
    width: max-content;
    min-width: 100%;
    padding: 0 14px;
}

/* Nav slot wrappers — fill their fractional StageCell so the inner button
   stretches to canon dimensions. container-type lets the inner button's
   font scale with the slot. */
.ctt-experiment-stage__nav-back,
.ctt-experiment-stage__nav-next {
    display: flex !important;
    align-items: stretch !important;
    justify-content: stretch !important;
    container-type: inline-size;
}

/* Next-button wrapper. Sits inside the StageCell and (a) fills it so the
   button stretches to canon, (b) hosts the "Please select an answer first"
   tooltip when the button is locked because no answer is selected. Disabled
   buttons don't fire hover events, so the wrapper, not the button, owns the
   hover surface that triggers the tooltip. */
.ctt-quiz__nav-next-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: stretch;
    justify-content: stretch;
}

/* Tooltip body. Renders above the button only when the wrapper carries the
   --locked modifier AND the wrapper is hovered (or focused-within). Brown
   color matches the program palette; arrow drawn as a CSS triangle. */
.ctt-quiz__nav-next-wrap--locked::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    right: 0;
    background: var(--ctt-brown-darkest, #523e2e);
    color: #ffffff;
    padding: 6px 10px;
    font-family: var(--ctt-font-body);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.2;
    border-radius: 4px;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0s linear 0.15s;
    pointer-events: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
    z-index: 5;
}
.ctt-quiz__nav-next-wrap--locked::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    right: 14px;
    border: 6px solid transparent;
    border-top-color: var(--ctt-brown-darkest, #523e2e);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0s linear 0.15s;
    pointer-events: none;
    z-index: 5;
}
.ctt-quiz__nav-next-wrap--locked:hover::after,
.ctt-quiz__nav-next-wrap--locked:focus-within::after,
.ctt-quiz__nav-next-wrap--locked:hover::before,
.ctt-quiz__nav-next-wrap--locked:focus-within::before {
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}

/* Locked Next button gets the cursor cue. */
.ctt-quiz__nav-next-wrap--locked .ctt-quiz__nav-btn {
    cursor: not-allowed;
}

/* Corner badge (empty rectangle placeholder per canon) */
.ctt-full-hud-key__corner-badge-slot {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ctt-full-hud-key__corner-badge {
    width: 80%;
    height: 80%;
    border: 1px solid #2c2c2c;
    background: transparent;
}

@media (max-width: 768px) {
    .ctt-full-hud-key-page {
        padding: 16px 12px 60px;
    }
    .ctt-full-hud-key__heading h1 {
        font-size: 22px;
    }
}

