/* =============================================================
   Presale cohort landing pages (/coding-cohort, /entrepreneurship-cohort)
   Scoped under the .cl- prefix. Web-only (linked per-route from each page's
   <HeadContent>, not global). Ported from the approved Claude Design
   ("unQbd Coding Cohort") -- the .cl-shell wrapper maps the unQbd Design System
   tokens onto local --cl-* variables so dark mode is mostly a one-block remap, backed by
   the component overrides at the foot of this file (some fight app.css). Layout is
   intrinsically responsive (flex-wrap + clamp + auto-fit grids), so it reads on a
   ~390px phone and on desktop from one set of rules.
   ============================================================= */

.cl-shell {
    /* ----- unQbd Design System tokens mapped onto cl tokens ----- */
    --cl-blue: #00B4FF;
    --cl-blue-deep: #0174B1;
    --cl-blue-tint: #F6FBFF;
    --cl-orange: #FFAA00;
    --cl-orange-deep: #E89600;
    --cl-orange-text: #A15900;  /* AA-legible deep amber for orange TEXT on pale backgrounds */
    --cl-charcoal: #222222;

    --cl-fg: var(--cl-charcoal);
    --cl-fg-secondary: #475569;
    --cl-fg-muted: #64748B;
    --cl-success: #17B26A;

    --cl-white: #FFFFFF;
    --cl-gray-100: #F1F5F9;
    --cl-gray-300: #CBD3DC;
    --cl-gray-900: #0F1620;

    --cl-border: #E0E0E0;
    --cl-border-subtle: rgba(2, 6, 23, .06);
    --cl-border-strong: #CBD3DC;

    --cl-tint-blue: #E6F7FF;   /* pale-blue pill / icon wash */
    --cl-tint-orange: #FFF4DA; /* pale-orange pill wash */

    --cl-r-md: 10px;
    --cl-r-lg: 14px;
    --cl-r-xl: 20px;
    --cl-pill: 999px;

    --cl-shadow-sm: 0 2px 8px rgba(2, 6, 23, .06);
    --cl-shadow-md: 0 8px 24px rgba(2, 6, 23, .08);
    --cl-shadow-lg: 0 18px 45px rgba(2, 6, 23, .10);
    --cl-shadow-xl: 0 28px 70px rgba(2, 6, 23, .14);
    --cl-shadow-blue: 0 10px 28px rgba(0, 180, 255, .28);
    --cl-shadow-orange: 0 10px 28px rgba(255, 170, 0, .28);

    --cl-ease: cubic-bezier(.22, 1, .36, 1);
    --cl-maxw: 1120px;
    /* Width of the price card and the parent-call line under it. The desktop media query
       in the ENROLLMENT block widens this and switches the card to two columns.
       .cl-scholarship deliberately does NOT track this and stays 480px: it is a short note,
       and at the card's desktop width a single element would leave a dead gutter. See that
       rule's own comment. */
    --cl-enroll-w: 480px;
    --cl-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

    color: var(--cl-fg);
    font-family: var(--cl-font);
    line-height: 1.5;
    background: var(--cl-blue-tint);
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;
    min-height: 100vh;
}

.cl-shell *,
.cl-shell *::before,
.cl-shell *::after { box-sizing: border-box; }

/* Heading reset. :where() is load-bearing, not decoration: as a plain
   `.cl-shell h1, .cl-shell h2, .cl-shell h3` this scored (0,1,1) and silently beat the
   single-class rules that own the real spacing -- .cl-hero-h1 (0,1,0) and .cl-h2 (0,1,0)
   -- so the hero H1's bottom margin and every section H2's vertical margins collapsed to
   zero and the subheads sat flush against the display type. (Deliberately no px values
   here: those rules are tuned below and would date this note.) :where() adds no
   specificity, so
   this drops to (0,1,0) and any component rule later in the file wins on source order,
   while unstyled headings still get the reset. Keep the :where(). */
.cl-shell :where(h1, h2, h3) { margin: 0; }
.cl-shell h1,
.cl-shell h2,
.cl-shell h3 { color: var(--cl-fg); }

/* Bootstrap Icons, bundled and linked globally by App.razor. The rules below only size
   and align them; the glyph itself comes from the .bi-* class. The old Material Symbols
   font-variation-settings axes are gone with the font -- Bootstrap Icons is a static
   font, so those axes did nothing but sit there. Every .bi rule here stays scoped under
   a .cl- class so nothing leaks onto the rest of the site, which shares this icon set. */
.cl-shell .bi {
    line-height: 1;
    flex-shrink: 0;
}

/* =============================================================
   Focused campaign chrome. These are ad landing pages, so they drop the full site
   nav / footer for a minimal, conversion-focused header + a small legal footer. The
   global top-nav, footer, and mobile app-download bar are hidden via body:has(.cl-shell);
   because this stylesheet is only ever linked on the two cohort routes, these rules can
   never touch any other page. (Same page-scoping pattern the reader uses to dock the nav.)
   ============================================================= */
body:has(.cl-shell) .unqbd-topnav-sticky,
body:has(.cl-shell) .footer-modern,
body:has(.cl-shell) .adb-bar,
body:has(.cl-shell) .bottom-nav {
    display: none !important;
}

/* No in-page control jumps to these any more (the final band's "Read The Questions"
   button was replaced by the parent call), but these ids stay deep-linkable from an ad or
   a shared URL -- /coding-cohort#cl-enroll drops a visitor straight on the price card.
   Without this the sticky topbar covers the section heading on arrival.

   Strictly speaking this is belt-and-braces: `html:has(.cl-shell)` sets scroll-padding-top
   on the SCROLL CONTAINER further down this file, which reserves the same 72px for every
   scroll-into-view in the page rather than per target, so an id absent from this list is
   still clear of the bar. It is kept, and the guarantee anchors added to it, because
   #cl-guarantee-terms is the destination of an EXTERNAL link -- the Stripe payment link's
   terms-of-service URL -- and that is the one anchor whose landing position cannot be
   checked by browsing the site. Two selectors is a cheap hedge on a link carrying refund
   terms. */
#cl-faq,
#cl-enroll,
#cl-guarantee,
#cl-guarantee-terms {
    scroll-margin-top: 72px;
}

.cl-topbar {
    position: sticky;
    top: 0;
    z-index: 50;
    background: rgba(255, 255, 255, .9);
    -webkit-backdrop-filter: saturate(180%) blur(10px);
    backdrop-filter: saturate(180%) blur(10px);
    border-bottom: 1px solid var(--cl-border-subtle);
}
.cl-topbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 60px;
    padding-top: 8px;
    padding-bottom: 8px;
}
.cl-topbar-brand { display: inline-flex; align-items: center; flex-shrink: 0; }
/* 34px against the bar's 61px on desktop (56%, up from 30px / 49%) and 32px against 55px on
   mobile (58%, up from 26px / 47%). The old sizes came from a header that also had to hold the
   deposit button; with that gone below 900px the logo was the smallest thing in a mostly empty
   bar.

   The ceiling is 44px, not the ~36px an earlier version of this note claimed, and the reason is
   different too: the header's height comes from .cl-topbar-inner's own min-height (60px desktop,
   54px mobile), NOT from the 42px CTA, which at 42 + 16px of padding is 58px and never reaches
   it. Under border-box that min-height minus 8px + 8px padding leaves a 44px content box, so a
   logo taller than 44px is what would start driving the header taller. 34px is a deliberate stop
   well short of that, to grow the wordmark without growing the chrome.

   Aspect is 2.283:1, so 34px renders ~78px wide. */
.cl-topbar-brand img { display: block; height: 34px; width: auto; }
.cl-topbar-actions { display: flex; align-items: center; gap: 8px; min-width: 0; }

/* The one escape hatch to the rest of the site (opens in a new tab, so the landing
   page is never lost). */
.cl-topbar-explore {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 14px;
    font-weight: 600;
    color: var(--cl-fg-secondary);
    text-decoration: none;
    white-space: nowrap;
    padding: 8px 6px;
    border-radius: 8px;
}
.cl-topbar-explore .bi { font-size: 17px; }
.cl-topbar-explore:hover { color: var(--cl-blue-deep); }
.cl-topbar-explore:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; }

/* Compact the deposit CTA inside the slim bar, in every state (live link, placeholder,
   or the sold-out soft button). */
.cl-topbar-actions .cl-btn {
    min-height: 42px;
    padding: 10px 18px;
    font-size: 14.5px;
    box-shadow: none;
}

/* The open house secondary in the sticky bar.

   The selector is deliberately as specific as the .cl-topbar-actions .cl-btn rule above,
   plus one class. A bare .cl-topbar-openhouse loses to it on specificity (0,2,0 against
   0,1,0) and every declaration here is silently dropped -- which is exactly what happened
   first time round, and a diff cannot show it. Verified against the rendered page's
   computed styles, not by reading this file.

   Hierarchy here is carried by outline-against-fill and by DOM order, NOT by size, and it
   has to be: the two labels are within one character of each other ("Free Open House .
   Aug 15" against "Reserve With $100 Deposit") and only this one has an icon, so at equal
   type it rendered 236px against the deposit's 213px. The shrink takes it to 224px, which
   is near-parity rather than under -- getting it genuinely narrower would mean dropping
   the icon or shortening the label, and the label is fixed copy. Measured on the rendered
   page; do not restate this as "smaller than the primary", because it is not.

   Below 900px it goes entirely. Measured, not estimated: the four items (logo 68.5,
   Explore 129, deposit 213, this button at 224, plus 28px of gaps) need 662.7px of content
   width, and .cl-wrap's clamp(20px, 5vw, 40px) padding makes the narrowest viewport that
   fits 737px. 900px is the breakpoint rather than 737 because 737 leaves single-digit
   slack -- tablet portrait at 768px would clear it by ~20px, so any label edit would start
   overflowing with nothing to warn us. .cl-btn is nowrap, so overflow does not wrap: it
   squeezes the deposit, which is the one outcome the primary-first rule exists to prevent.

   Below the breakpoint the deposit has the bar to itself and the hero's own open house
   button, full-width ~200px down the page, is the surface -- see CohortLanding.razor. */
.cl-topbar-actions .cl-btn.cl-topbar-openhouse { font-size: 14px; padding: 10px 15px; }

@media (max-width: 900px) {
    .cl-topbar-actions .cl-btn.cl-topbar-openhouse { display: none; }

    /* The DEPOSIT also leaves the header on mobile -- but only when the bottom bar is there to
       catch it. Below 900px that bar already pins the same deposit button in the thumb zone, so
       keeping this one too put "Reserve With $100 Deposit" on screen twice at once and pushed
       fixed chrome to 172px: 55px of header plus a 117px stacked bar, which is 26% of an iPhone
       SE. The header goes back to what it was designed as -- brand plus the one escape hatch --
       and the primary CTA lives where a thumb actually reaches.

       Conditional on the bar EXISTING, not merely on the width, and that is the whole risk in
       this rule. The bar renders only while the open house is on, so an unconditional version
       would leave mobile with no persistent CTA at all the day after the event. Same :has()
       scoping as the page padding and the mini-player offset further down.

       Hidden rather than not rendered because the decision is width-dependent and Razor cannot
       see the viewport. The button stays in the DOM, so the DOM-order guarantee this page is
       built on -- deposit before open house inside .cl-topbar-actions -- is untouched, and the
       header still carries the deposit at every width where the bottom bar is absent. */
    body:has(.cl-shell .cl-mobile-cta) .cl-topbar-actions .cl-btn-primary,
    body:has(.cl-shell .cl-mobile-cta) .cl-topbar-actions .cl-btn-soft { display: none; }
}

/* ---------------- mobile sticky CTA bar ----------------
   Mobile counterpart to the sticky header. Below 900px the header cannot hold a fourth control
   so it drops the open house button, which left the $100 deposit as the only persistent CTA on
   a ~10,500px page. This bar puts the free step back within thumb reach for the whole scroll.

   Hidden by default and revealed only under 900px, which is the SAME breakpoint that hides
   .cl-topbar-openhouse. The two are complementary halves of one decision: exactly one of them is
   on at any width, so the open house always has precisely one persistent slot and never two.

   Below 900px this bar is now the ONLY persistent CTA surface: the header drops its deposit
   button too whenever this bar is present (see the rule above), because carrying it in both
   put the same button on screen twice and cost a quarter of a small phone in fixed chrome. So
   the deposit likewise has exactly one persistent slot at any width -- the header above 900px,
   this bar below it.

   Renders only when the open house is on (gated in CohortLanding.razor), so nothing here needs
   a "no open house" layout -- and note the header rule depends on that, since with this bar
   absent the header must keep its deposit. */
/* ONE source of truth for the bar's height. The page padding, the scroll padding and the
   mini-player offset all derive from it, so the stacked variant below only has to restate this
   number once instead of in four places. On :root rather than .cl-shell because scroll-padding
   is set on <html>, which is outside the shell. This file is route-scoped, so it leaks nowhere.
   65px, measured: 1px border-top + 10px + the 44px control + 10px. */
:root { --cl-mcta-h: 65px; }

.cl-mobile-cta { display: none; }

.cl-mobile-cta-inner {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    max-width: var(--cl-maxw);
    margin: 0 auto;
    padding: 10px clamp(12px, 4vw, 20px);
    /* env() with an EXPLICIT 0px fallback, matching book-reader.css and bottom-nav.css. Without
       the fallback a UA that does not know env() drops the whole calc() and the bar loses its
       bottom padding.
       Honest about what this buys TODAY: nothing. App.razor's viewport meta has no
       `viewport-fit=cover`, so every safe-area inset resolves to 0 on iOS, and these two routes
       are web-only. It is here so the bar is already correct if that meta ever gains
       viewport-fit, not because it is currently holding buttons off a home indicator. */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
}

@media (max-width: 900px) {
    .cl-mobile-cta {
        display: block;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        /* Above .cl-topbar's 50. They cannot actually collide (one is pinned top, one bottom),
           but the page's own sticky chrome should outrank ordinary content as one band. */
        z-index: 60;
        background: rgba(255, 255, 255, .9);
        -webkit-backdrop-filter: saturate(180%) blur(10px);
        backdrop-filter: saturate(180%) blur(10px);
        border-top: 1px solid var(--cl-border-subtle);
    }

    /* Compact the two controls to bar height. 44px is the WCAG 2.5.5 / iOS HIG tap-target
       floor and is deliberately NOT reduced further to fit copy. */
    .cl-mobile-cta .cl-btn {
        min-height: 44px;
        padding: 10px 12px;
        font-size: 14px;
        box-shadow: none;
    }
    .cl-mobile-cta .cl-btn .bi { font-size: 17px; }

    /* The deposit is wider as well as first: at 390px the two labels are close enough in
       length that equal flex would read as two equal options. Matched on BOTH deposit states --
       cl-btn-soft is what a sold-out cohort renders, and it must keep the same footprint
       rather than collapsing and letting the secondary become the widest thing in the bar. */
    .cl-mobile-cta .cl-btn-primary,
    .cl-mobile-cta .cl-btn-soft { flex: 1.6; }
    .cl-mobile-cta .cl-btn-ghost { flex: 1; }

    /* Narrow phones STACK, and the flex ratios above are exactly why they have to.
       A flex item's default `min-width: auto` floors it at min-content, and .cl-btn is
       `white-space: nowrap`, so neither button can shrink below its full label no matter what
       ratio it is given -- the ratios only divide SURPLUS space, never a deficit. Measured: the
       pair needs 363.9px (195.3 + 160.6 + 8px gap) while this bar's own
       clamp(12px, 4vw, 20px) side padding leaves ~0.92 of the viewport across the widths that
       matter, so a single row needs ~396px just to stop eating its own padding and ~364px
       before the second button is pushed off-screen outright. At 360px -- a very common Android
       width -- "Free Open House" was being clipped by .cl-shell's overflow-x: clip.

       420px is the threshold rather than the bare 396px minimum so the row keeps real slack
       against a font swap or a longer deposit label. Below it both buttons go full width, which
       is also what the hero already does with the same two CTAs at 560px, so the stacked shape
       is not new to the page. Deposit stays first: column direction preserves DOM order. */
    @media (max-width: 420px) {
        /* 117px, measured: 1px border-top + 10px + 44px + 8px gap + 44px + 10px. The border is
           easy to drop from that sum and doing so leaves the mini player overlapping this bar's
           top edge by exactly 1px, which is how the first version of this number was wrong. */
        :root { --cl-mcta-h: 117px; }

        .cl-mobile-cta-inner { flex-direction: column; align-items: stretch; }
        .cl-mobile-cta .cl-btn-primary,
        .cl-mobile-cta .cl-btn-soft,
        .cl-mobile-cta .cl-btn-ghost { flex: none; width: 100%; }

        /* Nothing here re-shows the date: it is simply never hidden at these widths. See the
           min-width rule below, which is deliberately the ONLY rule touching .cl-oh-date in this
           bar, so the two layouts never fight over it. */
    }

    /* Drop the date from the secondary's label ONLY across the widths where it genuinely will not
       fit, which is a narrow slice rather than the whole row.

       Measured with the date forced on: the row needs 432.4px (deposit 209.5 + gap 8 + secondary
       214.9) and first clears this bar's clamp(12px, 4vw, 20px) side padding at 471px. So the
       date cannot fit between 421 and 470px -- about 10% of the 421-900px row band -- and fits
       everywhere above it. Hiding it across the entire row would deny it to every width from
       471px up for no reason.

       STACKED (420px and below) it is FREE and always kept: the button is already full width, so
       the longer label costs 0 extra pixels -- measured at 390px it just fills space the button
       already occupies. That matters because this bar is the CTA a parent meets after scrolling
       ~10,000px, and it was the one open house control on the page not telling them when.

       KNOWN GAP, stated rather than hidden: the iPhone Pro Max line (428, 430, 440 CSS px) sits
       in the row band below 471px, so those phones show the bar without the date. Closing it
       means either stacking them -- a 117px bar on a large phone -- or shortening the deposit
       label. Both are product calls, not layout fixes.

       NESTED `not all and (max-width: 420px)` rather than `min-width: 421px`, and that is
       load-bearing. min-width: 421px leaves widths in (420, 421) matching NEITHER this rule nor
       the stacked block above -- a row layout showing the date, i.e. the overflow this rule
       exists to prevent. That band is reachable: a 526px pane at 125% Windows scaling is 420.8
       CSS px, and browser zoom lands there routinely. `not all and (max-width: 420px)` is the
       exact complement of the stacked breakpoint, so the two meet with no gap at any fractional
       width. The inner query then bounds the top end; `not` cannot be folded into a single
       conjunction because it would negate the whole thing.

       An earlier version instead put `display: inline` inside the stacked block and left a
       `display: none` after it at equal specificity, so the hide won at every width and the date
       never appeared at all. Two rules racing on source order was that bug; one rule over a
       bounded, gapless range is the fix. */
    @media not all and (max-width: 420px) {
        @media (max-width: 470px) {
            .cl-mobile-cta .cl-oh-date { display: none; }
        }
    }

    /* Keep the bar off the last of the page. Applied via :has so it is tied to the bar actually
       rendering -- with the open house over, the bar is absent and the page keeps its own
       spacing.
       Derived from --cl-mcta-h (65px in a row, 117px stacked) plus 7px of deliberate slack, so
       the stacked variant cannot leave the footer under the bar. Over-padding shows a gap;
       under-padding hides content. */
    .cl-shell:has(.cl-mobile-cta) {
        padding-bottom: calc(var(--cl-mcta-h) + 7px + env(safe-area-inset-bottom, 0px));
    }

    /* The audiobook mini player is the one piece of site chrome these routes do NOT hide, and
       it would otherwise win outright: MainLayout mounts it on every page, it is
       `position: fixed; bottom: 0; z-index: 1001` at 64px tall, and this bar is z-index 60. With
       audio loaded it covered all but this bar's 1px top border between 769 and 900px, and at
       768px and below its own rule offsets it by the bottom-nav height -- a nav these routes
       hide -- so it floated over the top of the bar instead.

       Stacking it ABOVE the bar rather than hiding it: silently removing the controls for audio
       that is still playing is worse than losing a nav link, which is why the player was never
       in the hide list up top.

       The selector must test for THE BAR, not merely for .cl-shell. /cohort-reserved also
       renders .cl-shell and loads this stylesheet but has no bar at all, so a shell-only
       selector pushes the player up over 65px of nothing on a page that is live today -- and it
       would do the same on both landing pages once the open house is over. Matching the two
       rules above, which scope the same way for the same reason.

       max() with --android-nav-inset-bottom, not a bare env(): the Android host injects that var
       because env() reports 0 in an embedded WebView, and audio-player-v2.css's own offset uses
       exactly this pair. Diverging here would move the player onto the system nav bar in the app
       the moment either inset became non-zero. */
    body:has(.cl-shell .cl-mobile-cta) .mini-player.visible {
        bottom: calc(var(--cl-mcta-h)
            + max(env(safe-area-inset-bottom, 0px), var(--android-nav-inset-bottom, 0px)));
    }

    /* The bottom-bar counterpart to scroll-padding-top below. Without it, tabbing FORWARD to a
       control below the fold scrolls it to the block-end of the scrollport -- directly under
       65px of fixed chrome -- which is the same WCAG 2.2 2.4.11 (Focus Not Obscured) failure the
       top offset exists to prevent, just at the other edge. The page padding above only clears
       the END of the document; this covers every focus stop in between.

       Scoped to the BAR, not merely to .cl-shell, so it matches the padding rule above: with
       the open house over the bar is not rendered, and a bare html:has(.cl-shell) would keep
       reserving space against chrome that is not there. */
    html:has(.cl-shell .cl-mobile-cta) {
        scroll-padding-bottom: calc(var(--cl-mcta-h) + 7px);
    }
}

/* Print. This bar is interactive-only chrome, and without an explicit override it would follow
   the page into paged media: the Letter and A4 page boxes are both narrower than 900px, so the
   screen media query above matches, and a `position: fixed` element in paged media repeats at
   the foot of EVERY printed page, over the program copy. The page padding it reserves would
   also print as a blank strip. Neither belongs in a PDF a parent saves or hands to a partner.
   Stated as its own block rather than by adding `screen` to the query above, so the reason is
   visible at the point of the fix. */
@media print {
    .cl-mobile-cta { display: none !important; }
    .cl-shell:has(.cl-mobile-cta) { padding-bottom: 0; }

    /* The mini-player offset has to be undone explicitly, because hiding the bar does NOT undo
       it: :has() tests for the element's PRESENCE, and display:none leaves it in the DOM, so the
       screen rule above would still hold the player 65px up on every printed page. Only the
       offset this file added is reset -- the player's own paged-media behaviour is
       audio-player-v2.css's business, not this stylesheet's. */
    body:has(.cl-shell .cl-mobile-cta) .mini-player.visible { bottom: 0; }
}

html.dark-mode .cl-mobile-cta,
body.dark-mode .cl-mobile-cta {
    background: rgba(17, 32, 42, .9);
    border-top-color: #2a3b47;
}

/* Offset for the sticky header so an element scrolled to the top -- an anchor jump OR a
   keyboard-focused control (WCAG 2.2 2.4.11 Focus Not Obscured) -- lands clear of the
   ~61px .cl-topbar, not behind it. Scoped to these routes via :has(.cl-shell). */
html:has(.cl-shell) { scroll-padding-top: 72px; }

.cl-wrap {
    width: 100%;
    max-width: var(--cl-maxw);
    margin: 0 auto;
    padding: 0 clamp(20px, 5vw, 40px);
}

.cl-wrap-narrow { max-width: 760px; }
.cl-wrap-mid { max-width: 820px; }

/* The heading gets a wider measure than its paragraph on purpose. At 620px the section
   H2s overflowed by a few pixels -- "What your child walks away with." needs 629px, "From
   first line of code to launch day." 688px -- and text-wrap: balance turned that near-miss
   into two evenly-split lines ("What your child / walks away with."), which reads as a
   centering mistake rather than a deliberate break. 700px lets every section H2 sit on one
   line; the paragraph stays capped at 620px so body copy keeps its ~70-character measure. */
.cl-center {
    max-width: 700px;
    margin: 0 auto clamp(32px, 4vw, 52px);
    text-align: center;
}

.cl-section { padding: clamp(52px, 8vw, 96px) 0; }
.cl-sec-white { background: var(--cl-white); }
.cl-sec-tint { background: var(--cl-blue-tint); }

.cl-eyebrow {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--cl-blue-deep);
}

.cl-h2 {
    font-size: clamp(26px, 4.2vw, 40px);
    font-weight: 800;
    line-height: 1.14;
    letter-spacing: -.025em;
    margin: 14px 0 14px;
    text-wrap: balance;
}

.cl-center p {
    font-size: clamp(16px, 2.2vw, 18px);
    line-height: 1.55;
    color: var(--cl-fg-secondary);
    max-width: 620px;
    margin: 0 auto;
    text-wrap: pretty;
}

/* ---------------- pills ---------------- */
.cl-pill {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    padding: 7px 13px;
    border-radius: var(--cl-pill);
    white-space: nowrap;
}

.cl-pill .bi { font-size: 15px; }
.cl-pill-blue { color: var(--cl-blue-deep); background: var(--cl-tint-blue); }
.cl-pill-orange { color: var(--cl-orange-text); background: var(--cl-tint-orange); }

/* ---------------- buttons ---------------- */
.cl-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-family: inherit;
    font-weight: 600;
    font-size: 16px;
    line-height: 1.1;
    padding: 16px 26px;
    border-radius: 12px;
    border: 1.5px solid transparent;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    min-height: 52px;
    transition: transform .2s var(--cl-ease), background .2s var(--cl-ease), box-shadow .2s var(--cl-ease);
}

.cl-btn .bi { font-size: 20px; }

/* WCAG AA: white on --cl-blue (#00B4FF) is 2.3:1; the deep brand blue is 5.1:1. */
.cl-btn-primary { background: var(--cl-blue-deep); color: #fff; box-shadow: var(--cl-shadow-blue); }
.cl-btn-primary:hover { background: #015E93; color: #fff; transform: translateY(-1px); }

/* The shadow is load-bearing, not polish. This is a WHITE fill on a hero gradient whose
   lower half is #F6FBFF to #FFFFFF, so at the CTA row the fill matches the page and a
   1.5px border was the only thing saying "control" -- which reads as a bordered callout
   just as easily as a button. The lift is what makes it unambiguous next to the solid
   primary beside it. Hover moves to the blue tint rather than deepening the shadow, so
   the affordance is legible without hover too (it has to be: this is the secondary CTA
   on a page that mostly gets touch traffic from paid social, where there is no hover). */
.cl-btn-ghost {
    background: var(--cl-white);
    color: var(--cl-blue-deep);
    border-color: var(--cl-blue);
    box-shadow: var(--cl-shadow-sm);
}
.cl-btn-ghost:hover { background: var(--cl-tint-blue); color: var(--cl-blue-deep); }

.cl-btn-light { background: #fff; color: var(--cl-blue-deep); box-shadow: var(--cl-shadow-lg); }
.cl-btn-light:hover { color: var(--cl-blue-deep); transform: translateY(-1px); }

/* Darken (not lighten) behind white text so the label clears AA on the blue band. */
.cl-btn-outline-light { background: rgba(0, 0, 0, .22); color: #fff; border-color: rgba(255, 255, 255, .55); }
.cl-btn-outline-light:hover { background: rgba(255, 255, 255, .2); color: #fff; }

/* Soft deposit placeholder: a real disabled <button>, dashed + muted. */
.cl-btn-soft {
    background: var(--cl-white);
    color: var(--cl-fg-muted);
    border: 1.5px dashed var(--cl-border-strong);
    cursor: not-allowed;
    box-shadow: none;
}

.cl-btn:focus-visible {
    outline: 3px solid var(--cl-blue-deep);
    outline-offset: 3px;
}
.cl-btn-light:focus-visible, .cl-btn-outline-light:focus-visible { outline-color: #fff; }

.cl-deposit-wrap { display: flex; flex-direction: column; gap: 5px; }

/* ==================== HERO ==================== */
.cl-hero {
    position: relative;
    overflow: hidden;
    background: radial-gradient(115% 80% at 50% -10%, #E4F5FF 0%, #F6FBFF 46%, #FFFFFF 100%);
    border-bottom: 1px solid var(--cl-border-subtle);
    /* The focused header (sticky, in normal flow) sits above, so the hero just needs its
       own top breathing room now, not a fixed-nav offset. */
    padding: clamp(36px, 6vw, 68px) 0 clamp(44px, 6vw, 76px);
}

.cl-hero-grid {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(28px, 4vw, 56px);
}

.cl-hero-copy { flex: 1 1 360px; min-width: min(100%, 340px); }
.cl-hero-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }

.cl-hero-h1 {
    font-size: clamp(30px, 6vw, 52px);
    font-weight: 800;
    line-height: 1.06;
    letter-spacing: -.025em;
    /* 20px, not the original 16px: at a 52px display size the subhead read as glued to the
       H1. Still tighter than the 26px below the subhead, so the hierarchy holds. */
    margin: 0 0 20px;
    text-wrap: balance;
}

/* One accent word in the hero H1. The deeper brand blue keeps large text above the
   3:1 contrast threshold on the near-white hero gradient. */
.cl-blue-word { color: var(--cl-blue-deep); }

.cl-hero-sub {
    font-size: clamp(16px, 2.3vw, 19px);
    line-height: 1.5;
    color: var(--cl-fg-secondary);
    margin: 0 0 26px;
    max-width: 30em;
    text-wrap: pretty;
}

.cl-hero-cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 20px;
}

/* Funding line under the primary CTA. Plain icon + text, NOT a box.
   It used to be a tinted, bordered, rounded rectangle, on the reasoning that a fill reads
   as "a fact attached to the button" rather than as something clickable. It does not: 12px
   of padding, a blue border and a 10px radius are the same recipe as .cl-btn-ghost right
   above it, so at a glance the hero stacked three button-shaped things and only two of them
   did anything. Chrome is what made it ambiguous, so the chrome is gone and the hierarchy
   is carried by weight instead: button > this line > .cl-hero-trust chips. */
.cl-cta-note {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 18px;
    max-width: 520px;
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1.4;
    color: var(--cl-fg);
}

.cl-cta-note .bi { font-size: 20px; color: var(--cl-blue); margin-top: 1px; flex: none; }

/* The parent call, the hero's third path. A link, not a button, on purpose: three 52px
   controls pushed the deposit below the fold at 390px. Sits between the funding line and
   the trust chips, matching .cl-enroll-call's treatment further down the page so the same
   action looks the same in both places. */
.cl-hero-call {
    margin: 0 0 18px;
    font-size: 14px;
    color: var(--cl-fg-secondary);
}
.cl-hero-call a {
    margin-left: 6px;
    font-weight: 700;
    color: var(--cl-blue-deep);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.cl-hero-call a:hover { color: var(--cl-blue-deep); }
.cl-hero-call a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; border-radius: 4px; }

.cl-hero-trust { display: flex; flex-wrap: wrap; gap: 10px 18px; padding-top: 4px; }
.cl-trust-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--cl-fg-secondary);
}
.cl-trust-item .bi { font-size: 18px; color: var(--cl-blue); }

/* ----- hero capstone card (illustrative mockup, not a real testimonial) ----- */
.cl-hero-media { flex: 1 1 300px; min-width: min(100%, 288px); display: flex; justify-content: center; }
.cl-capstone { position: relative; width: 100%; max-width: 380px; }

.cl-capstone-tag {
    position: absolute;
    z-index: 2;
    top: -12px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    /* WCAG AA: white on the orange is 1.9:1; near-black reads at 9:1. */
    color: #1A1A2E;
    background: var(--cl-orange);
    padding: 8px 13px;
    border-radius: var(--cl-pill);
    box-shadow: var(--cl-shadow-orange);
}
.cl-capstone-tag .bi { font-size: 15px; }

.cl-capstone-card {
    background: #fff;
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-xl);
    box-shadow: var(--cl-shadow-xl);
    padding: 18px;
    transform: rotate(-1deg);
}

.cl-capstone-head { display: flex; gap: 14px; align-items: center; }
.cl-capstone-icon {
    width: 58px;
    height: 78px;
    border-radius: 8px;
    background: linear-gradient(155deg, #00B4FF, #0174B1);
    box-shadow: var(--cl-shadow-md);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.cl-capstone-icon .bi { font-size: 30px; color: rgba(255, 255, 255, .95); }
.cl-capstone-title { font-size: 16px; font-weight: 700; color: var(--cl-charcoal); letter-spacing: -.01em; }
.cl-capstone-sub { font-size: 13px; color: var(--cl-fg-muted); margin-top: 2px; }
.cl-capstone-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 8px;
    font-size: 11px;
    font-weight: 600;
    color: var(--cl-blue-deep);
    background: var(--cl-tint-blue);
    padding: 4px 9px;
    border-radius: var(--cl-pill);
}
.cl-capstone-chip .bi { font-size: 13px; }
.cl-capstone-rule { height: 1px; background: var(--cl-border); margin: 16px 0; }
.cl-capstone-skel { display: flex; flex-direction: column; gap: 8px; }
.cl-capstone-skel span { height: 9px; border-radius: var(--cl-pill); background: var(--cl-gray-100); display: block; }
.cl-capstone-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; }
.cl-capstone-stamp {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--cl-orange-text);
}
.cl-capstone-example {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--cl-fg-muted);
    background: var(--cl-gray-100);
    padding: 4px 9px;
    border-radius: var(--cl-pill);
}

/* ==================== PROOF STRIP ==================== */
.cl-proof {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
    gap: 14px;
    padding: clamp(24px, 3.5vw, 36px) 0;
}
.cl-proof-item { text-align: center; padding: 8px 6px; }
/* Divider only when the four stats sit in one row (desktop); the adjacent-sibling
   border would otherwise strand on the left edge of a wrapped 2x2 / stacked layout. */
@media (min-width: 760px) {
    .cl-proof-item + .cl-proof-item { border-left: 1px solid var(--cl-border); }
}
.cl-proof-num { font-size: clamp(26px, 4vw, 34px); font-weight: 800; letter-spacing: -.03em; color: var(--cl-blue-deep); }
.cl-proof-num a { color: inherit; text-decoration: none; border-bottom: 2px solid rgba(1, 116, 177, .35); }
.cl-proof-num a:hover { border-bottom-color: var(--cl-blue-deep); }
.cl-proof-num a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; }
.cl-proof-label { font-size: 13px; font-weight: 500; color: var(--cl-fg-secondary); margin-top: 2px; }

/* ==================== INSTRUCTOR ==================== */
.cl-instructor { display: flex; flex-wrap: wrap; gap: clamp(28px, 4vw, 56px); align-items: center; }
.cl-instructor-media { flex: 1 1 300px; min-width: min(100%, 280px); order: 2; }
.cl-instructor-body { flex: 1 1 360px; min-width: min(100%, 320px); order: 1; }
/* When no headshot is present the copy takes the full width. */
.cl-instructor--noimg .cl-instructor-body { flex-basis: 100%; }

.cl-instructor-photo {
    position: relative;
    width: 100%;
    max-width: 420px;
    margin: 0 auto;
}
.cl-instructor-photo img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: 24px;
    box-shadow: var(--cl-shadow-xl);
    border: 1px solid var(--cl-border);
}
.cl-instructor-badge {
    position: absolute;
    bottom: -16px;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    max-width: 92%;
    display: flex;
    align-items: center;
    gap: 10px;
    background: #fff;
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-pill);
    padding: 9px 16px;
    box-shadow: var(--cl-shadow-lg);
}
.cl-instructor-badge .bi { font-size: 20px; color: var(--cl-blue); }
.cl-instructor-badge span:last-child { font-size: 13.5px; font-weight: 600; color: var(--cl-charcoal); white-space: nowrap; }

.cl-role { font-size: 15px; font-weight: 600; color: var(--cl-blue-deep); margin: 0 0 18px; }
.cl-prose { font-size: clamp(15px, 2vw, 17px); line-height: 1.62; color: var(--cl-fg-secondary); margin: 0 0 16px; }
.cl-prose:last-of-type { margin-bottom: 24px; }
.cl-prose strong { color: var(--cl-fg); }

.cl-cred-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 12px; }
.cl-cred {
    display: flex;
    align-items: center;
    gap: 11px;
    background: #fff;
    border: 1px solid var(--cl-border);
    border-radius: 12px;
    padding: 13px 15px;
}
.cl-cred .bi { font-size: 22px; color: var(--cl-blue-deep); }
.cl-cred span:last-child { font-size: 13.5px; font-weight: 600; color: var(--cl-charcoal); }
a.cl-cred { text-decoration: none; transition: border-color .2s var(--cl-ease), box-shadow .2s var(--cl-ease); }
a.cl-cred:hover { border-color: var(--cl-blue); box-shadow: var(--cl-shadow-sm); }
a.cl-cred:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; }

/* ==================== CARD GRID (outcomes) ==================== */
/* Flex (not grid) so an odd count -- five outcome cards -- centers its last row
   instead of leaving a hanging card. */
.cl-cards { display: flex; flex-wrap: wrap; justify-content: center; gap: clamp(16px, 2vw, 20px); }
.cl-card {
    flex: 1 1 300px;
    min-width: min(100%, 300px); /* floors width so five cards settle into a centered 3 + 2 */
    max-width: 380px;
    background: var(--cl-white);
    border: 1px solid var(--cl-border);
    border-radius: 16px;
    padding: 24px;
    box-shadow: var(--cl-shadow-sm);
    transition: transform .2s var(--cl-ease), box-shadow .2s var(--cl-ease);
}
.cl-card:hover { transform: translateY(-2px); box-shadow: var(--cl-shadow-md); }
.cl-card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 12px;
    background: var(--cl-tint-blue);
    color: var(--cl-blue-deep);
    margin-bottom: 16px;
}
.cl-card-icon .bi { font-size: 24px; }
.cl-card h3 { font-size: 18px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 8px; }
.cl-card p { font-size: 14.5px; line-height: 1.55; color: var(--cl-fg-secondary); margin: 0; }

/* ==================== FORMAT ==================== */
.cl-format { display: flex; flex-wrap: wrap; gap: clamp(28px, 4vw, 48px); align-items: stretch; }
.cl-format-body { flex: 1 1 340px; min-width: min(100%, 320px); }
.cl-format-body p { font-size: clamp(15px, 2vw, 17px); line-height: 1.6; color: var(--cl-fg-secondary); margin: 12px 0 24px; }
.cl-format-media { flex: 1 1 280px; min-width: min(100%, 260px); display: flex; }
.cl-format-media img {
    display: block;
    width: 100%;
    min-height: 260px;
    object-fit: cover;
    border-radius: var(--cl-r-xl);
    box-shadow: var(--cl-shadow-lg);
    border: 1px solid var(--cl-border);
}

/* No .cl-img-placeholder rule: when the classroom photo is missing the markup omits the
   whole .cl-format-media column rather than drawing a "coming soon" box, so the copy
   column simply takes the full width of the wrapping flex row. */

.cl-info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 12px; }
.cl-info {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    background: #fff;
    border: 1px solid var(--cl-border);
    border-radius: 12px;
    padding: 15px;
}
.cl-info .bi { font-size: 22px; color: var(--cl-blue); margin-top: 1px; }
.cl-info-title { font-size: 14.5px; font-weight: 700; color: var(--cl-charcoal); }
.cl-info-sub { font-size: 13px; color: var(--cl-fg-muted); margin-top: 1px; }

/* ==================== CURRICULUM ==================== */
.cl-phase-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: clamp(16px, 2vw, 20px); }
.cl-phase {
    background: var(--cl-white);
    border: 1px solid var(--cl-border);
    border-radius: 18px;
    padding: 26px;
    box-shadow: var(--cl-shadow-sm);
}
.cl-phase--hi { background: linear-gradient(160deg, #F1FAFF, #FFFFFF); border-color: var(--cl-blue); box-shadow: var(--cl-shadow-md); }
.cl-phase-tag {
    display: inline-flex;
    align-items: center;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--cl-blue-deep);
    background: var(--cl-tint-blue);
    padding: 6px 12px;
    border-radius: var(--cl-pill);
}
.cl-phase-tag--hi { color: #fff; background: var(--cl-blue-deep); }
.cl-phase h3 { font-size: 21px; font-weight: 700; letter-spacing: -.01em; margin: 16px 0 12px; }

.cl-checklist { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.cl-checklist li { display: flex; gap: 9px; font-size: 14.5px; line-height: 1.45; color: var(--cl-fg-secondary); }
.cl-checklist .bi { font-size: 18px; color: var(--cl-blue); margin-top: 1px; }

/* ==================== TEXTBOOK ==================== */
/* Reuses the Format section's two-column flex row (.cl-format / .cl-format-body), so the
   copy takes the full width by itself whenever the page has no cover render to show.
   Only the MEDIA column is its own, and that is the whole point of these rules.

   .cl-format-media is built for a photograph: it applies a 1px border, a radius, a large
   shadow, object-fit: cover and a 260px min-height. The asset here is a transparent PNG of
   a printed book at an angle with its shadow already baked into the file, so all five are
   wrong for it -- the border and radius would frame a rectangle of empty transparent pixels
   rather than the book, the second shadow would double the first, and `cover` against a
   925x1400 portrait in a short box crops the spine straight off. So: no chrome at all, and
   height: auto so the book keeps its own aspect and is never cropped.

   flex-basis is sized for the PAIR of editions the coding cohort prints (two 150px books
   plus a 16px gap), and each book is capped rather than set to width: 100%. A book render
   is a tall portrait, and at the parent column's width it would tower over the copy.

   margin-inline:auto is what centers the book once the row WRAPS. Without it the column
   keeps its 260px basis on a line of its own and stays anchored left, so on a phone the
   book sits off to one side under centred-looking copy. flex-grow stays 0 on purpose --
   growing the column instead would also widen it beside the copy on desktop, which is the
   proportion the note above is deliberately protecting. The two do not fight: flexible
   lengths resolve before main-axis alignment, so on a shared line .cl-format-body's
   flex-grow:1 has already consumed the free space and these margins get nothing; alone on
   a wrapped line there is free space left and they take it. Verified in the browser at
   390px, not reasoned from the spec. */
/* The chips reuse .cl-cred-grid, but this section's copy column is far wider than the
   instructor's -- the book only takes a 260px media column where the headshot takes 280px
   plus a wider gutter, so the body measures ~732px here against ~524px there. Through the
   shared repeat(auto-fit, minmax(min(100%, 200px), 1fr)) that is three columns, and with
   exactly four chips the fourth is left stranded alone at the left of an otherwise empty
   second row. The instructor block gets 2x2 from the same rule purely because its column is
   narrower. Capping the grid restores that balance without special-casing the chip count:
   at 560px the shared minmax resolves to two columns, so four chips read as 2x2 and a fifth
   would simply start a third row instead of dangling. Below ~572px the min(100%, 200px)
   floor collapses it to one column, exactly as before. */
.cl-textbook .cl-cred-grid { max-width: 560px; }

.cl-textbook-media {
    flex: 0 1 330px;
    min-width: min(100%, 200px);
    margin-inline: auto;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* The editions, side by side. align-items:flex-end sits them on a shared baseline so the
   pair reads as two books on a shelf rather than two floating images; the captions then
   line up under them. Wraps to a stack on a narrow phone rather than shrinking the books
   to thumbnails. */
.cl-textbook-covers {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: flex-end;
    justify-content: center;
    width: 100%;
}
.cl-textbook-cover { flex: 0 1 150px; max-width: 150px; margin: 0; }

/* A single edition takes the room the pair would have, so a page with one book is not
   punished with a thumbnail by a rule written for two. :only-child rather than a count
   hack, so this follows the content instead of being maintained alongside it. */
.cl-textbook-cover:only-child { flex-basis: 260px; max-width: 260px; }

.cl-textbook-cover figcaption {
    margin-top: 10px;
    text-align: center;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--cl-fg-muted);
}
.cl-textbook-media img {
    display: block;
    width: 100%;
    max-width: 300px;
    height: auto;

    /* Reserves the box BEFORE the lazy image arrives, and it is load-bearing twice over.
       With height:auto and no intrinsic size the element is 260x0 until the bytes land: a
       zero-AREA lazy image is the classic way to make loading="lazy" fire late or not at
       all, and on the one section whose job is proving the book exists, "the book never
       appeared" is far worse than the ~1MB the lazy attribute saves. It also removes the
       content jump the lazy attribute would otherwise introduce, since the column is only
       stretched to the copy's height while the two share a row -- alone on a wrapped phone
       line its height IS the image, so 0 to 393px would shove the price card down.

       Paired with object-fit:contain rather than left bare. The ratio is the coding book's
       (925x1400) and the entrepreneurship render does not exist yet, so a future asset with
       a different shape would otherwise be stretched to fit this box. contain letterboxes
       instead, which on a transparent PNG is invisible. It is emphatically NOT cover, which
       is the classroom photo's rule and would crop the spine off. */
    aspect-ratio: 925 / 1400;
    object-fit: contain;
}

/* ==================== ENROLLMENT ==================== */
/* One founding-price card, centered. The free taster was removed once the deposit
   became fully refundable -- the first three sessions are the risk-free trial, so a
   separate free taster was redundant. */
.cl-enroll { display: flex; justify-content: center; }
.cl-price-card {
    flex: 1 1 auto;
    width: 100%;
    max-width: var(--cl-enroll-w);
    background: #fff;
    border: 2px solid var(--cl-blue);
    border-radius: var(--cl-r-xl);
    padding: clamp(24px, 3vw, 34px);
    box-shadow: var(--cl-shadow-lg);
    position: relative;
    display: flex;
    flex-direction: column;
}

/* Low-commitment "talk it through first" line beneath the card. */
/* The open house offramp INSIDE the price card, directly under the deposit button. Text, not a
   button, and deliberately quieter than .cl-enroll-call below the card: this one sits inches
   from the primary CTA, so it has to read as a footnote to that button rather than as a rival
   to it. Same colour vocabulary, one step down in size and weight. */
.cl-price-openhouse {
    margin: 12px 0 0;
    text-align: center;
    font-size: 13.5px;
    line-height: 1.45;
    color: var(--cl-fg-secondary);
}
.cl-price-openhouse a {
    font-weight: 600;
    color: var(--cl-blue-deep);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.cl-price-openhouse a:hover { color: var(--cl-blue-deep); }
.cl-price-openhouse a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; border-radius: 4px; }

.cl-enroll-call {
    margin: clamp(20px, 3vw, 28px) auto 0;
    max-width: var(--cl-enroll-w);
    text-align: center;
    font-size: 14.5px;
    color: var(--cl-fg-secondary);
}
.cl-enroll-call a {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-weight: 600;
    color: var(--cl-blue-deep);
    text-decoration: none;
    white-space: nowrap;
}
.cl-enroll-call a .bi { font-size: 17px; }
.cl-enroll-call a:hover { text-decoration: underline; }
.cl-enroll-call a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; border-radius: 4px; }

.cl-price-badge {
    position: absolute;
    top: -13px;
    left: clamp(24px, 3vw, 34px);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--cl-orange);
    /* WCAG AA: white on the orange is 1.9:1; near-black reads at 9:1. */
    color: #1A1A2E;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: 6px 12px;
    border-radius: var(--cl-pill);
    box-shadow: var(--cl-shadow-orange);
}
.cl-price-badge .bi { font-size: 14px; }
.cl-price-row { display: flex; align-items: baseline; gap: 10px; margin-top: 6px; }
.cl-price-amount { font-size: clamp(38px, 5vw, 48px); font-weight: 800; letter-spacing: -.03em; color: var(--cl-charcoal); }
.cl-price-strike { font-size: 20px; font-weight: 600; color: var(--cl-fg-muted); text-decoration: line-through; }
.cl-price-sub { font-size: 13.5px; color: var(--cl-fg-muted); margin-top: 2px; }
.cl-enrollment-deadline {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 10px;
    color: var(--cl-charcoal);
    font-size: 13.5px;
    font-weight: 700;
}
.cl-enrollment-deadline .bi { color: var(--cl-blue-deep); font-size: 16px; }
.cl-seats-pill {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    align-self: flex-start;
    margin: 16px 0 18px;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--cl-orange-text);
    background: var(--cl-tint-orange);
    padding: 7px 13px;
    border-radius: var(--cl-pill);
}
.cl-seats-pill .bi { font-size: 15px; }
.cl-price-cta { margin: 4px 0 20px; }
.cl-price-cta .cl-btn { width: 100%; }
.cl-price-list { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 11px; }
.cl-price-list li { display: flex; gap: 10px; font-size: 14.5px; line-height: 1.45; color: var(--cl-fg-secondary); }
.cl-price-list .bi { font-size: 19px; color: var(--cl-blue); }
.cl-deposit-note {
    background: var(--cl-blue-tint);
    border: 1px solid var(--cl-border-subtle);
    border-radius: 12px;
    padding: 14px 16px;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--cl-fg-secondary);
}
.cl-deposit-note strong { font-weight: 700; color: var(--cl-charcoal); }

/* Desktop: the card goes two-up instead of one tall column. The old 480px cap was a
   leftover from when this row held TWO cards: the free taster sat beside the price card
   in a two-up flex row, and when the taster was dropped (see the note above) the row
   became a single centered card but kept a width that only made sense with a sibling
   next to it. Alone, that ran ~660px tall inside a 1040px content area -- a narrow ribbon
   with ~280px of dead space either side, and the inclusion list pushed the price and the
   Reserve button apart.
   Side by side it is ~400px tall, so the price, the CTA and all seven inclusions land in
   one eyeful, which is the whole job of this section. Below 900px it stays the single
   column: two 370px columns are narrower than the copy wants.
   The list is explicitly placed in column 2 with `grid-row: 1 / span 5`, so the deposit note
   lands on its own full-width row underneath both. That span is a FIXED 5 and is deliberately
   not tied to the number of items in column 1, which now holds six (the open house offramp
   joined them): the sixth simply takes row 6, below where the list ends, and the note follows
   on row 7. Adding a seventh would do the same. The span only needs to be tall enough that the
   list clears the note, not to match the left column. */
@media (min-width: 900px) {
    .cl-shell { --cl-enroll-w: 780px; }

    .cl-price-card {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
        column-gap: clamp(28px, 3vw, 40px);
        align-content: start;
    }
    /* .cl-price-openhouse is in this list for CONSISTENCY, not as a bug fix. A review claimed
       that without it the offramp would auto-place after .cl-deposit-note (which is
       `grid-column: 1 / -1`) and so render below the note instead of under the Reserve button.
       That is not how auto-placement works and it was measured on both pages at 900px and
       1440px before this line was added: only items with a DEFINITE ROW are placed in an
       earlier step, and the note's row is auto, so both are placed in document order, where the
       offramp comes first. It rendered correctly either way. What was true is that it was the
       only column-1 child not declaring its column, which is what made the placement look
       uncertain to a reader. Declaring it removes the question. */
    .cl-price-card > .cl-price-row,
    .cl-price-card > .cl-price-sub,
    .cl-price-card > .cl-enrollment-deadline,
    .cl-price-card > .cl-price-openhouse,
    .cl-price-card > .cl-seats-pill { grid-column: 1; }
    .cl-price-card > .cl-price-cta { grid-column: 1; margin-bottom: 0; }
    .cl-price-card > .cl-price-list { grid-column: 2; grid-row: 1 / span 5; margin: 0; align-self: start; }
    .cl-price-card > .cl-deposit-note { grid-column: 1 / -1; margin-top: 24px; }
}

/* ==================== GUARANTEE ==================== */
.cl-guarantee {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(20px, 3vw, 32px);
    background: var(--cl-blue-tint);
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-xl);
    padding: clamp(24px, 3.5vw, 40px);
}
.cl-guarantee-icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border-radius: 18px;
    background: #fff;
    color: var(--cl-blue-deep);
    box-shadow: var(--cl-shadow-sm);
    border: 1px solid var(--cl-border);
}
.cl-guarantee-icon .bi { font-size: 34px; }
.cl-guarantee-body { flex: 1 1 260px; min-width: min(100%, 240px); }
.cl-guarantee-body h3 { font-size: clamp(20px, 2.8vw, 26px); font-weight: 800; letter-spacing: -.02em; margin: 0 0 8px; }
.cl-guarantee-body p { font-size: 15.5px; line-height: 1.6; color: var(--cl-fg-secondary); margin: 0; }

/* Two guarantees side by side on desktop, stacked below 720px. The breakpoint is where a
   half of .cl-wrap-mid (820px) stops being wide enough for a readable measure, not a device
   size. */
.cl-guarantee-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(16px, 2.5vw, 22px);
}
@media (min-width: 720px) {
    .cl-guarantee-grid { grid-template-columns: 1fr 1fr; }
}

/* Inside the grid each card becomes a COLUMN. This is deliberate rather than left to
   .cl-guarantee's own flex-wrap: at ~390px of card width, a 64px icon beside a body whose
   min-width is 240px cannot share a line, so the row would wrap anyway -- but the parent
   rule sets align-items:center, which centres the wrapped icon over the text and reads as a
   layout bug. Stacking explicitly puts the icon flush left above the heading, and resets the
   body's flex basis so it fills the card instead of holding a 260px width it no longer has.
   Verified against the rendered computed styles at 375px, 800px and 1280px, not the diff. */
.cl-guarantee-grid > .cl-guarantee {
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(14px, 2vw, 18px);
    padding: clamp(22px, 3vw, 30px);
}
.cl-guarantee-grid > .cl-guarantee > .cl-guarantee-body { flex: 1 1 auto; min-width: 0; }
/* One step down from the single-card size: two headings at 26px in a 820px row read as two
   competing section titles rather than as two cards. */
.cl-guarantee-grid .cl-guarantee-body h3 { font-size: clamp(18px, 2.2vw, 21px); }

/* ==================== GUARANTEE TERMS ==================== */
/* The full refund terms, collapsed under the two cards. Same native <details> pattern as the
   FAQ (no circuit, no JavaScript), one size down, because this is the small print that
   qualifies the cards rather than a third promise competing with them.

   KEEP the background on var(--cl-white), the SURFACE token, which the dark-mode block at the
   bottom of this file remaps. .cl-faq-item hardcodes #fff and pays for it with an extra
   dark-mode rule; there is no reason to repeat that here. Every other colour used below
   (--cl-border, --cl-charcoal, --cl-fg-secondary) is remapped too, and the one that is not,
   --cl-blue-deep on the check glyph, is the brand accent held constant across themes on
   purpose -- the same treatment .cl-guarantee-icon and .cl-scholarship-icon get. So this
   block needs no dark-mode entry of its own. */
.cl-terms {
    margin-top: clamp(18px, 2.6vw, 26px);
    background: var(--cl-white);
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-lg);
    overflow: hidden;
}
.cl-terms > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 15px 20px;
    font-size: 15px;
    font-weight: 700;
    color: var(--cl-charcoal);
    line-height: 1.4;
    cursor: pointer;
    list-style: none;
}
.cl-terms > summary::-webkit-details-marker { display: none; }
.cl-terms > summary:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: -3px; }
.cl-terms[open] .cl-faq-mark { transform: rotate(180deg); }

.cl-terms-block { padding: 0 20px 18px; }
.cl-terms-block + .cl-terms-block { border-top: 1px solid var(--cl-border); padding-top: 16px; }
.cl-terms-block h3 {
    font-size: 14.5px;
    font-weight: 800;
    letter-spacing: -.01em;
    margin: 0 0 8px;
    color: var(--cl-charcoal);
}
.cl-terms-block p {
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--cl-fg-secondary);
    margin: 0 0 8px;
    text-wrap: pretty;
}
.cl-terms-block p:last-child { margin-bottom: 0; }
.cl-terms-list {
    list-style: none;
    margin: 0 0 10px;
    padding: 0;
    display: grid;
    gap: 6px;
}
.cl-terms-list li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--cl-fg-secondary);
}
.cl-terms-list .bi { flex: none; color: var(--cl-blue-deep); font-size: 15px; line-height: 1.45; }

/* ==================== STEP UP SCHOLARSHIP NOTE ==================== */
/* A subordinate sibling of .cl-guarantee: same box, one size down, so it reads as a
   note under the price card rather than a second promise.
   Needs its own max-width because the enroll section uses the plain 1120px .cl-wrap; the
   cap is a literal 480px and no longer tracks .cl-price-card, which widens on desktop.
   Its own top margin is required regardless of what precedes it: Task 9 reordered this
   block to follow .cl-enroll-call (the parent-call line) instead of precede it, so the
   scholarship note reads last in the section, and margin-top does not care which sibling
   happens to sit above.
   The icon chip deliberately does NOT copy .cl-guarantee-icon's recipe: that one hardcodes
   #fff with a shadow and border, which is exactly why it needs its own entry in the
   dark-mode overrides. This one fills with var(--cl-tint-blue) instead.
   No dark-mode entry is needed here, but not because every token remaps: the fill, border,
   and text colors (--cl-white, --cl-border, --cl-charcoal, --cl-fg-secondary, --cl-tint-blue)
   are all remapped by the html.dark-mode .cl-shell block below, and the ONE that is not, the
   icon glyph's --cl-blue-deep, is the brand accent kept constant across themes on purpose
   (same as .cl-guarantee-icon and the .cl-capstone icon). Either way the rendered result is
   correct in both themes. Same lesson as .cl-price-card and .cl-faq-item, which hardcode #fff
   and pay for it with extra dark rules: use the token, not the literal. (Selectors, not line
   numbers, so this comment does not rot when rules move.) */
.cl-scholarship {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 16px;
    /* Matches the price card's mobile and tablet width exactly, at every breakpoint. The
       card widens to var(--cl-enroll-w) -- 780px, two columns -- on desktop; this note does
       NOT follow, because a single column of prose at that width runs past 110 characters a
       line, well beyond a readable measure. Widening it to line up would cost more than the
       misalignment does. */
    max-width: 480px;
    margin: clamp(20px, 3vw, 28px) auto 0;
    padding: 20px 22px;
    /* A NOTE, not a card, and styled to say so: the shadow is gone. It used to be a white
       shadowed box exactly like the price card above it, which made a 480px element read as a
       mis-sized SIBLING of a 780px one rather than as a footnote to it. Without the shadow it
       sits flat against the section while the price card lifts off it, and the difference in
       width stops looking like a mistake. The 2px blue border on that card against this one's
       1px grey already separates them; the shadow was the last thing making them look alike.

       KEEP background on var(--cl-white). It is the SURFACE token -- #FFFFFF in light,
       #11202a in dark -- and this element has no dark-mode rule of its own, so the token is
       the only thing that adapts it. Do NOT reach for var(--cl-blue-tint) because the name
       sounds right: that is the SECTION background token (#F6FBFF light, #0b151b dark) and
       this section is .cl-sec-tint, so it would paint the note the exact colour of the
       surface behind it and leave nothing but the 1px border, in BOTH modes. Note also that
       --cl-blue-tint and --cl-tint-blue are different tokens; the icon wash below uses the
       other one. */
    background: var(--cl-white);
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-lg);
}

.cl-scholarship-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: var(--cl-r-md);
    background: var(--cl-tint-blue);
    color: var(--cl-blue-deep);
}
.cl-scholarship-icon .bi { font-size: 26px; }
/* 200px floor (against .cl-guarantee-body's 240px) keeps the chip inline on a ~350px
   phone instead of wrapping the icon onto its own line. */
.cl-scholarship-body { flex: 1 1 200px; min-width: min(100%, 200px); }
.cl-scholarship-body h3 {
    font-size: 16px;
    font-weight: 800;
    letter-spacing: -.01em;
    margin: 0 0 6px;
    color: var(--cl-charcoal);
}
.cl-scholarship-body p {
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--cl-fg-secondary);
    margin: 0;
    text-wrap: pretty;
}
.cl-scholarship-body p + p { margin-top: 10px; }

/* ==================== OPEN HOUSE SECTION ==================== */
/* Eyebrow / h2 / when-line center, matching every other section intro on this page
   (Outcomes, Format, Curriculum, and Enrollment all wrap theirs in .cl-center). The prose,
   the form, and its post-submit success message reset back to left: multi-sentence copy
   reads better ragged-right only, a centered .cl-openhouse-field would float its <label>
   over a left-aligned input, and .cl-openhouse-success replaces the form in the DOM (see
   CohortOpenHouseForm.razor's @if (_submitted)) rather than nesting inside it, so it needs
   its own selector here -- it does not inherit the form rule above. No dark-mode entry
   needed: --cl-charcoal and --cl-blue-deep are both remapped by the html.dark-mode
   .cl-shell block, and the icon glyph keeps the constant brand accent already used by
   .cl-guarantee-icon and the .cl-capstone icon. */
.cl-open-house { text-align: center; }
.cl-open-house .cl-prose,
.cl-open-house .cl-openhouse-form,
.cl-open-house .cl-openhouse-success {
    text-align: left;
}
.cl-open-house-when {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 6px 0 24px;
    font-size: 15.5px;
    font-weight: 700;
    color: var(--cl-charcoal);
}
.cl-open-house-when .bi { color: var(--cl-blue-deep); font-size: 18px; }

.cl-openhouse-form {
    display: grid;
    gap: 11px;
    margin: 14px 0;
    padding: 14px;
    border: 1px solid var(--cl-border);
    border-radius: 12px;
    background: var(--cl-blue-tint);
}
.cl-openhouse-field {
    display: grid;
    gap: 4px;
}
.cl-openhouse-field label {
    color: var(--cl-charcoal);
    font-size: 12.5px;
    font-weight: 700;
}
.cl-openhouse-field input,
.cl-openhouse-field select {
    width: 100%;
    min-height: 40px;
    padding: 8px 10px;
    border: 1px solid var(--cl-border);
    border-radius: 8px;
    background: var(--cl-white);
    color: var(--cl-fg);
    font: inherit;
    font-size: 14px;
}
.cl-openhouse-field input:focus,
.cl-openhouse-field select:focus {
    border-color: var(--cl-blue-deep);
    outline: 2px solid rgba(1, 116, 177, .2);
    outline-offset: 1px;
}
.cl-openhouse-field .validation-message,
.cl-openhouse-error {
    color: #a32020;
    font-size: 12px;
}
.cl-openhouse-error { margin: 0; }
.cl-openhouse-honeypot {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
}
.cl-openhouse-submit {
    justify-self: start;
    min-height: 38px;
    padding: 8px 15px;
    border: 1px solid var(--cl-blue-deep);
    border-radius: 8px;
    background: transparent;
    color: var(--cl-blue-deep);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.cl-openhouse-submit:hover { background: rgba(1, 116, 177, .08); }
.cl-openhouse-submit:focus-visible {
    outline: 3px solid var(--cl-blue-deep);
    outline-offset: 3px;
    border-radius: 4px;
}
.cl-openhouse-success {
    margin: 14px 0 10px !important;
    padding: 12px 14px;
    border: 1px solid rgba(25, 158, 117, .35);
    border-radius: 10px;
    background: rgba(25, 158, 117, .1);
    color: var(--cl-charcoal) !important;
    font-weight: 700;
}

/* ==================== FAQ ==================== */
.cl-faq { display: flex; flex-direction: column; gap: 10px; }
.cl-faq-item { background: #fff; border: 1px solid var(--cl-border); border-radius: var(--cl-r-lg); overflow: hidden; }
.cl-faq-item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 18px 20px;
    font-size: clamp(15px, 2vw, 16.5px);
    font-weight: 600;
    color: var(--cl-charcoal);
    line-height: 1.4;
    cursor: pointer;
    list-style: none;
}
.cl-faq-item summary::-webkit-details-marker { display: none; }
.cl-faq-item summary:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: -3px; }
.cl-faq-mark { color: var(--cl-fg-muted); transition: transform .2s var(--cl-ease); font-size: 22px; }
.cl-faq-item[open] .cl-faq-mark { transform: rotate(180deg); }
.cl-faq-item > p { margin: 0; padding: 0 20px 20px; font-size: 14.5px; line-height: 1.62; color: var(--cl-fg-secondary); }

/* ==================== POST-DEPOSIT CONFIRMATION ==================== */
/* /cohort-reserved (CohortReserved.razor), the Stripe payment-link redirect target.
   Built entirely on the .cl- tokens above, so the dark-mode block at the bottom of this
   file -- which remaps those tokens on .cl-shell -- covers this section with no
   dark-mode rules of its own. */
.cl-confirm-head { text-align: center; max-width: 620px; margin: 0 auto clamp(32px, 5vw, 48px); }
.cl-confirm-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 76px;
    height: 76px;
    margin-bottom: 22px;
    border-radius: 50%;
    background: var(--cl-tint-blue);
    color: var(--cl-blue-deep);
}
.cl-confirm-mark .bi { font-size: 44px; }
.cl-confirm-h1 {
    font-size: clamp(30px, 5vw, 44px);
    font-weight: 800;
    letter-spacing: -.03em;
    line-height: 1.1;
    margin: 0 0 14px;
}
.cl-confirm-sub { font-size: 16.5px; line-height: 1.62; color: var(--cl-fg-secondary); margin: 0; }

.cl-confirm-card {
    background: var(--cl-white);
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-xl);
    padding: clamp(24px, 3.5vw, 36px);
    box-shadow: var(--cl-shadow-sm);
    margin-bottom: clamp(20px, 3vw, 28px);
}
.cl-confirm-h2 {
    font-size: clamp(18px, 2.4vw, 22px);
    font-weight: 800;
    letter-spacing: -.02em;
    margin: 0 0 18px;
}
/* The shared .cl-price-list carries a 20px bottom margin for the price card's stacked
   deposit note. Nothing follows it inside this card, so drop it. */
.cl-confirm-card .cl-price-list { margin-bottom: 0; }
.cl-confirm-card .cl-price-list li { font-size: 15px; }

.cl-confirm-guarantee { margin-bottom: clamp(20px, 3vw, 28px); }

.cl-confirm-help { text-align: center; font-size: 14.5px; color: var(--cl-fg-muted); }
.cl-confirm-help a { color: var(--cl-blue-deep); font-weight: 600; text-decoration: none; }
.cl-confirm-help a:hover { text-decoration: underline; }
.cl-confirm-help a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; border-radius: 4px; }

/* ==================== FINAL CTA ==================== */
.cl-final {
    position: relative;
    overflow: hidden;
    padding: clamp(52px, 8vw, 92px) 0;
    background: linear-gradient(160deg, #0186CC 0%, #015E93 100%);
}
.cl-final::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(90% 120% at 80% -10%, rgba(0, 180, 255, .5), transparent 60%);
    pointer-events: none;
}
.cl-final-inner { position: relative; max-width: 720px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 40px); text-align: center; }
.cl-final-pill {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #fff;
    background: rgba(0, 0, 0, .22);
    padding: 7px 14px;
    border-radius: var(--cl-pill);
    margin-bottom: 18px;
}
.cl-final-pill .bi { font-size: 15px; }
.cl-final-inner h2 {
    font-size: clamp(28px, 4.6vw, 44px);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -.025em;
    margin: 0 0 14px;
    color: #fff;
    text-wrap: balance;
}
.cl-final-inner > p {
    font-size: clamp(16px, 2.3vw, 19px);
    line-height: 1.5;
    color: rgba(255, 255, 255, .9);
    margin: 0 0 28px;
    text-wrap: pretty;
}
.cl-final-cta { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }

/* The open house line under the final buttons. Selector is deliberately
   `.cl-final-inner > .cl-final-openhouse` and not a bare `.cl-final-openhouse`: the subhead
   rule above is `.cl-final-inner > p`, at (0,1,1), so a single class at (0,1,0) would LOSE
   and this line would inherit the 19px subhead size and its 28px bottom margin no matter
   where it sits in the file. Two classes take it to (0,2,0). */
.cl-final-inner > .cl-final-openhouse {
    margin: 20px 0 0;
    font-size: 15px;
    color: rgba(255, 255, 255, .85);
}
.cl-final-openhouse a {
    margin-left: 6px;
    font-weight: 700;
    color: #fff;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.cl-final-openhouse a:focus-visible { outline: 3px solid #fff; outline-offset: 3px; border-radius: 4px; }

/* ==================== FOOTER CROSS-LINK ==================== */
/* The one allowed link between the two campaign pages. */
.cl-crosslink { background: var(--cl-blue-tint); border-top: 1px solid var(--cl-border); padding: 26px 0; text-align: center; }
.cl-crosslink a { font-size: 15px; font-weight: 600; color: var(--cl-blue-deep); text-decoration: none; }
.cl-crosslink a:hover { text-decoration: underline; }
.cl-crosslink a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 3px; border-radius: 4px; }

/* ==================== MINIMAL LEGAL FOOTER ==================== */
/* Replaces the hidden global footer: just the legal links an ad landing page needs. */
.cl-mini-foot { background: var(--cl-white); border-top: 1px solid var(--cl-border); padding: 22px 0; }
.cl-mini-foot-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 20px;
}
.cl-mini-foot-copy { font-size: 13px; color: var(--cl-fg-muted); }
.cl-mini-foot-links { display: flex; flex-wrap: wrap; gap: 8px 20px; }
.cl-mini-foot-links a { font-size: 13px; font-weight: 500; color: var(--cl-fg-secondary); text-decoration: none; }
.cl-mini-foot-links a:hover { color: var(--cl-blue-deep); text-decoration: underline; }
.cl-mini-foot-links a:focus-visible { outline: 3px solid var(--cl-blue-deep); outline-offset: 2px; border-radius: 4px; }

/* ==================== responsive nudge ==================== */
@media (max-width: 560px) {
    .cl-hero-cta .cl-btn { width: 100%; }
    .cl-hero-cta .cl-deposit-wrap { width: 100%; }
    .cl-hero-cta .cl-btn-soft { width: 100%; }
    /* Same treatment on the final band: stacked, these two intrinsically size to 248px and
       243px, and near-but-not-quite-equal centered buttons read as a mistake. */
    .cl-final-cta .cl-btn { width: 100%; }
    /* Tighten the two hero badges so they stay side by side on phones. */
    .cl-hero-badges { gap: 6px; }
    .cl-hero-badges .cl-pill { font-size: 11px; padding: 6px 8px; gap: 5px; letter-spacing: .03em; }
    .cl-hero-badges .cl-pill .bi { font-size: 13px; }

    /* Slim the focused header so brand + escape hatch + Reserve stay on one line: the
       "Explore unQbd" text collapses to just its icon (the anchor keeps its aria-label),
       and the deposit CTA gets more compact. */
    .cl-topbar-inner { min-height: 54px; gap: 8px; }
    .cl-topbar-brand img { height: 32px; }

    /* The "Explore unQbd" text hides only when this header is genuinely crowded, which means only
       when it still carries the deposit button. Two conditions, and both are load-bearing:

       - the mobile CTA bar is ABSENT, i.e. the open house is over and the deposit has come back
         up here. With the bar present the deposit is hidden and there is nothing to crowd, so the
         label costs 95px of the ~243px standing empty at 390px and buys nothing by hiding.
       - .cl-topbar-actions actually CONTAINS a deposit control. /cohort-reserved renders this same
         header and stylesheet with no deposit at all, so without this half the label would be
         hidden there too, on a header with room to spare and nothing to make room for.

       :has() tests presence, not display, which is why the first condition cannot simply be
       folded into the second: on the landing pages the deposit is always in the DOM and merely
       hidden by CSS, so the second condition alone is always true there.

       A bare box-arrow icon does not say where it goes; the anchor's aria-label always did, which
       left screen reader users better informed than sighted ones.

       Written as :not(...) so the label keeps its default display when shown, rather than this
       rule having to guess the right display value for a flex child. */
    body:not(:has(.cl-shell .cl-mobile-cta))
        .cl-topbar-actions:has(.cl-btn-primary, .cl-btn-soft) .cl-topbar-explore-label {
        display: none;
    }

    .cl-topbar-explore { padding: 8px; }
    .cl-topbar-actions .cl-btn { min-height: 40px; padding: 9px 13px; font-size: 13px; }
    html:has(.cl-shell) { scroll-padding-top: 64px; } /* shorter mobile header */
}

/* The one genuinely crowded header left: no bottom bar (the open house is over, so the deposit
   is back up here) on a very narrow phone. Measured at 320px, that row needs 299px of the 280px
   available and the deposit's right edge is clipped by .cl-shell's overflow-x: clip.

   It was ALREADY over by 6px at the old 26px logo, so this is a pre-existing overflow that the
   34/32px logo widened to 19px rather than one the logo introduced. Both halves are fixed here:
   the logo goes back to 26px in this state only, and the deposit button compacts further than
   the 560px block already does, which is what buys back the original 6px.

   This block holds ONLY those two rules. The 560px block above keeps its own -- the explore
   padding, the base button compaction and the 64px scroll-padding all belong to every mobile
   width, and an earlier version of this change closed that block here by mistake and silently
   stole them from the 381-560px band. */
@media (max-width: 380px) {
    /* Both rules carry the same two-part condition as the label above, and for the same reason:
       they exist to make room for the deposit button, so they must not fire on a header that has
       no deposit in it. /cohort-reserved is exactly that header -- without the :has() half it
       shrank the wordmark there to buy space nothing was asking for. */
    body:not(:has(.cl-shell .cl-mobile-cta))
        .cl-topbar-inner:has(.cl-btn-primary, .cl-btn-soft) .cl-topbar-brand img {
        height: 26px;
    }

    body:not(:has(.cl-shell .cl-mobile-cta))
        .cl-topbar-actions:has(.cl-btn-primary, .cl-btn-soft) .cl-btn {
        padding: 9px;
        font-size: 12.5px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .cl-btn, .cl-card, .cl-faq-mark { transition: none; }
    .cl-btn-primary:hover, .cl-btn-light:hover, .cl-card:hover { transform: none; }
}

/* =============================================================
   Dark mode -- remap tokens via the body/html .dark-mode class
   (the site's single dark-mode signal; no OS/media or attribute theming).
   ============================================================= */
html.dark-mode .cl-shell,
body.dark-mode .cl-shell {
    --cl-blue-tint: #0b151b;
    --cl-charcoal: #eaf2f8;
    --cl-fg: #eaf2f8;
    --cl-fg-secondary: #b8c6d2;
    --cl-fg-muted: #8a9aa8;
    --cl-white: #11202a;
    --cl-border: #2a3b47;
    --cl-border-strong: #3a4d5a;
    --cl-gray-100: #1a2b36;
    --cl-tint-blue: rgba(0, 180, 255, .14);
    --cl-tint-orange: rgba(255, 170, 0, .16);
    --cl-orange-text: #FFC24D;  /* light amber so orange text stays legible on the dark tint */
    background: #0b151b;
}

/* the light hero gradient would strand near-white text on a light band */
html.dark-mode .cl-hero,
body.dark-mode .cl-hero {
    background: radial-gradient(115% 80% at 50% -10%, #12242f 0%, #0b151b 46%, #0b151b 100%);
}

html.dark-mode .cl-capstone-card,
body.dark-mode .cl-capstone-card,
html.dark-mode .cl-cred,
body.dark-mode .cl-cred,
html.dark-mode .cl-info,
body.dark-mode .cl-info,
html.dark-mode .cl-instructor-badge,
body.dark-mode .cl-instructor-badge,
html.dark-mode .cl-guarantee-icon,
body.dark-mode .cl-guarantee-icon,
html.dark-mode .cl-faq-item,
body.dark-mode .cl-faq-item {
    background: #11202a;
    border-color: #2a3b47;
}

/* .cl-btn-light is deliberately excluded: it is the primary CTA on the always-blue
   final band, so it must stay white (light) in dark mode, not darken into the band. */
html.dark-mode .cl-btn-ghost,
body.dark-mode .cl-btn-ghost,
html.dark-mode .cl-btn-soft,
body.dark-mode .cl-btn-soft {
    background: #11202a;
    color: #5BC8FF;
}
html.dark-mode .cl-btn-soft,
body.dark-mode .cl-btn-soft { color: var(--cl-fg-muted); border-color: #2a3b47; }

html.dark-mode .cl-price-card,
body.dark-mode .cl-price-card { background: linear-gradient(160deg, rgba(0, 180, 255, .1), #11202a 72%); }
html.dark-mode .cl-phase,
body.dark-mode .cl-phase { background: #11202a; border-color: #2a3b47; }
html.dark-mode .cl-phase--hi,
body.dark-mode .cl-phase--hi { background: linear-gradient(160deg, rgba(0, 180, 255, .14), #11202a); border-color: var(--cl-blue); }
html.dark-mode .cl-guarantee,
body.dark-mode .cl-guarantee { background: #11202a; border-color: #2a3b47; }
html.dark-mode .cl-eyebrow,
body.dark-mode .cl-eyebrow { color: #5BC8FF; }
html.dark-mode .cl-blue-word,
body.dark-mode .cl-blue-word { color: #5BC8FF; }
/* Both parent-call links. --cl-blue-deep (#0174B1) is not remapped for dark mode, and on
   the #0b151b ground it lands around 2.4:1, under AA. .cl-enroll-call had this gap before
   .cl-hero-call existed; they are fixed together because they are the same link twice. */
html.dark-mode .cl-hero-call a,
body.dark-mode .cl-hero-call a,
html.dark-mode .cl-enroll-call a,
body.dark-mode .cl-enroll-call a { color: #5BC8FF; }

/* The focus ring needs the same treatment, and for a stricter reason than the text: both
   links draw their outline in --cl-blue-deep, so overriding only `color` above left the
   keyboard indicator at that same ~2.4:1 on the dark ground. Text at least has AAA-sized
   weight working for it; a focus indicator is non-text and has a hard 3:1 floor under
   WCAG 2.2 (1.4.11), which #5BC8FF clears and --cl-blue-deep does not. Both links again,
   since they are the same link twice. */
/* .cl-price-openhouse is the third link of this shape and gets the identical treatment. Its
   ring on the price card's dark ground computes ~3.2:1, so unlike the two above it does scrape
   past 1.4.11 -- but "passes by 0.2" is not a reason for the one cohort link to look different
   from its siblings, and the margin disappears the moment the card's background is retuned.
   Its `color` is separately repainted by app.css's dark-mode anchor rule, which outranks the
   --cl-blue-deep here; that is the intended behaviour for a plain text link, so it is left
   alone rather than opted out. Only the non-text indicator is pinned. */
html.dark-mode .cl-hero-call a:focus-visible,
body.dark-mode .cl-hero-call a:focus-visible,
html.dark-mode .cl-enroll-call a:focus-visible,
body.dark-mode .cl-enroll-call a:focus-visible,
html.dark-mode .cl-price-openhouse a:focus-visible,
body.dark-mode .cl-price-openhouse a:focus-visible { outline-color: #5BC8FF; }
html.dark-mode .cl-openhouse-submit,
body.dark-mode .cl-openhouse-submit { color: #5BC8FF; border-color: #5BC8FF; }
html.dark-mode .cl-openhouse-field .validation-message,
body.dark-mode .cl-openhouse-field .validation-message,
html.dark-mode .cl-openhouse-error,
body.dark-mode .cl-openhouse-error { color: #FF8A8A; }

/* The focused header is a hardcoded translucent white in light mode; give it a matching
   translucent dark surface. (.cl-mini-foot uses tokens, so it remaps automatically.) */
html.dark-mode .cl-topbar,
body.dark-mode .cl-topbar {
    background: rgba(17, 32, 42, .9);
    border-bottom-color: #2a3b47;
}
html.dark-mode .cl-topbar-explore:hover,
body.dark-mode .cl-topbar-explore:hover { color: #5BC8FF; }

/* Re-assert this page's text palette over app.css's global dark-mode rules.
   `body.dark-mode h1, ...h6` and `body.dark-mode p` in app.css score (0,1,2) and outrank
   every colour rule in this file (`.cl-shell h1, h2, h3` and `.cl-prose`/`.cl-center p`/...
   are all (0,1,1)), so in dark mode EVERY heading and paragraph here rendered the same
   #e0e0e0: the primary/secondary split vanished, the brand-blue instructor role line went
   grey, and the near-white copy on the always-blue final band went grey on blue. The
   selectors below add .cl-shell to reach (0,2,2)+ so the landing palette wins back, and
   they stay page-scoped because this stylesheet only loads on the two cohort routes.
   A NEW heading or paragraph added above needs no entry here -- the two catch-alls cover
   it -- but a new *accent* colour on a p (like .cl-role) does. */
html.dark-mode .cl-shell :is(h1, h2, h3),
body.dark-mode .cl-shell :is(h1, h2, h3) { color: var(--cl-fg); }

html.dark-mode .cl-shell p,
body.dark-mode .cl-shell p { color: var(--cl-fg-secondary); }

/* Accent line under the instructor H2: matches the .cl-eyebrow / .cl-blue-word remap,
   because --cl-blue-deep (#0174B1) is far too dark to sit on the dark surface. */
html.dark-mode .cl-shell .cl-role,
body.dark-mode .cl-shell .cl-role { color: #5BC8FF; }

/* The final band is an always-blue gradient in both themes, so its copy stays near-white. */
html.dark-mode .cl-shell .cl-final-inner h2,
body.dark-mode .cl-shell .cl-final-inner h2 { color: #fff; }
html.dark-mode .cl-shell .cl-final-inner > p,
body.dark-mode .cl-shell .cl-final-inner > p { color: rgba(255, 255, 255, .9); }

/* The orange accents still carry their own contrast in both themes (--cl-orange-text
   remaps to a light amber for the dark tint), and the final band keeps its always-blue
   SURFACE in both themes with no override. Its TEXT is the exception, and that is exactly
   what the two rules directly above handle -- app.css was greying it out. */

/* ==================== COHORT INDEX (/cohorts) ==================== */
/* The program index. Same shell and the same tokens as the two landing pages, but a
   different job: it routes rather than sells, so it carries no price card, no deposit CTA
   and no guarantee, and the RSVP form is the hero instead of section eleven.

   Everything here is prefixed .cl-idx- so it cannot reach into either landing page. The
   shared pieces it DOES reuse unchanged are .cl-topbar, .cl-section, .cl-wrap, .cl-center,
   .cl-eyebrow, .cl-h2, .cl-prose, .cl-instructor*, .cl-mini-foot* and the whole
   .cl-openhouse-* form block.

   Appended AFTER the dark-mode block above on purpose: the overrides at the foot of this
   file are written to win on specificity, and a .cl-idx- rule placed before them that
   happened to tie would silently lose. Source order is load-bearing in this stylesheet. */

/* The one measurable requirement this page has is that the RSVP form clears the fold on a
   390px phone, so the hero opens tighter than .cl-section (clamp(52px, 8vw, 96px)). The
   BOTTOM padding is left alone: it separates the hero from the cards, where nothing is
   competing for vertical space.
   The 16px floor is spent deliberately: see the fold budget note on .cl-idx-detail. */
.cl-idx-openhouse { padding-top: clamp(16px, 4vw, 44px); }

.cl-idx-h1 {
    font-size: clamp(28px, 5.4vw, 44px);
    font-weight: 800;
    line-height: 1.12;
    letter-spacing: -.02em;
    /* Tight top margin against the eyebrow, which is a 12px label rather than a block of
       copy. Every pixel between the top of this section and the submit button is spent on
       the fold budget below. */
    margin: 4px 0 8px;
    text-wrap: balance;
}

/* When and where. Same recipe as .cl-open-house-when on the landing pages, deliberately
   not that class: this one is left-aligned in a left-aligned hero, while that one centers
   inside a centered section. */
.cl-idx-when {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 8px;
    font-size: clamp(15px, 2vw, 16.5px);
    font-weight: 700;
    line-height: 1.45;
    color: var(--cl-charcoal);
}
.cl-idx-when .bi {
    color: var(--cl-blue-deep);
    font-size: 18px;
    line-height: 1.35;
    flex: 0 0 auto;
}

/* .cl-prose ships with margin-bottom 16 and a :last-of-type rule widening it to 24. The
   RSVP prompt sits immediately above the form, so it takes neither. */
.cl-idx-tight { margin-bottom: 8px; }

/* The submit is the ONLY ask on this page, so it is a solid primary rather than the outline
   secondary the landing pages use, where it has to stay subordinate to the $100 deposit
   beside it. Same fill as .cl-btn-primary: white on --cl-blue-deep is 5.1:1, while white on
   --cl-blue is 2.3:1 and fails AA, so do not "brighten" this.
   Full width up to a readable cap. At 390px a start-aligned 13px button under two
   full-width inputs reads as a footnote rather than the thing to press. */
/* The form panel and its fields carry the rest of the fold budget. 44px inputs and a 48px
   submit are the floor here, not a starting point: those are the minimum comfortable touch
   targets, and this is a form whose traffic is almost entirely phones. The padding and gaps
   absorb the trimming instead. */
.cl-idx-openhouse .cl-openhouse-form {
    margin: 0 0 10px;
    padding: 13px;
    gap: 8px;
}
.cl-idx-openhouse .cl-openhouse-field {
    gap: 3px;
}
.cl-idx-openhouse .cl-openhouse-field input {
    min-height: 44px;
    font-size: 16px; /* iOS Safari zooms the page on focus for anything under 16px. */
}
.cl-idx-openhouse .cl-openhouse-submit {
    justify-self: stretch;
    width: 100%;
    max-width: 340px;
    min-height: 48px;
    border-color: var(--cl-blue-deep);
    background: var(--cl-blue-deep);
    color: #fff;
    font-size: 15.5px;
    box-shadow: var(--cl-shadow-blue);
}
.cl-idx-openhouse .cl-openhouse-submit:hover { background: #015E93; color: #fff; }

/* Reassurance directly under the form, and the last element in the block: the address plus
   "nothing to commit to". Small on purpose -- it answers the two things a hesitant parent
   wants to know at the moment they are deciding whether to press the button (where is it,
   does it cost anything) without competing with the button itself. */
.cl-idx-venue {
    margin: 0 0 22px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--cl-fg-muted);
}

/* The description of the event itself, sitting between the when-line and the RSVP prompt.
   Trims .cl-prose's 16px to 14px so the three stacked paragraphs read as one group rather
   than three separate notes.
   Specificity is why this works now and did not before: .cl-idx-detail is (0,1,0), the same
   as .cl-prose, so it wins on source order alone (this block is appended at the end of the
   file). While this paragraph was LAST in the block it was matched by .cl-prose:last-of-type
   at (0,2,0) instead, which outranks it -- so the margin-bottom: 0 this rule used to carry
   never applied at all. */
.cl-idx-detail {
    margin-bottom: 12px;
    /* THE FOLD BUDGET. This is the longest element above the form (six lines at 390px) and
       the only one whose size can move without touching the headline, the date line, or the
       44px/48px touch targets. Dropping the floor from .cl-prose's 15px to 14px is what puts
       the whole submit button inside iOS Safari's ~664px first-paint viewport with the
       requested copy order intact -- measured, not assumed.
       Only the FLOOR changes: the 2vw..17px range is identical to .cl-prose, so above a
       ~750px viewport this paragraph is byte-identical to every other .cl-prose on the page
       and the difference exists solely where the fold is actually contested.
       It also reads correctly: this is the explanation, and it should sit one step below the
       date line and the RSVP prompt it comes between. */
    font-size: clamp(14px, 2vw, 17px);
}

/* ---- cohort cards ---- */
/* auto-fit, NOT two hardcoded columns: adding a third cohort must be one entry in the
   Cohorts collection and no layout edit. minmax with min(100%, ...) so a single card at
   390px cannot force a horizontal scrollbar.

   320px, measured rather than guessed. At 340px the two cards needed 699px of a 692px
   content box at a 768px viewport and stacked, so an iPad in portrait -- squarely a
   "side by side" width -- got the phone layout by a 7px miss. 320 moves the flip to a
   ~731px viewport: phones and small tablets stack, iPad portrait and up go two-up, and the
   cards are 336px wide at the tightest two-column width, which still holds the headline. */
.cl-idx-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
    gap: clamp(16px, 2.5vw, 26px);
    align-items: stretch;
}

/* --cl-white, not --cl-blue-tint. The section behind these is .cl-sec-tint, which IS
   --cl-blue-tint, so the near-identically-named token would paint the card the same colour
   as its own background in BOTH themes. --cl-white is the surface token and dark mode
   remaps it to #11202a. */
.cl-idx-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: clamp(22px, 3vw, 30px);
    border: 1px solid var(--cl-border);
    border-radius: var(--cl-r-xl);
    background: var(--cl-white);
    box-shadow: var(--cl-shadow-sm);
    transition: transform .18s var(--cl-ease), box-shadow .18s var(--cl-ease);
}
.cl-idx-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--cl-shadow-md);
}
/* Keyboard users get the same affordance as the pointer overlay below, on the CARD rather
   than on the bare anchor, so the visible focus ring matches the actual click target. */
.cl-idx-card:focus-within {
    outline: 3px solid var(--cl-blue-deep);
    outline-offset: 3px;
}

.cl-idx-card-label {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--cl-blue-deep);
}
.cl-shell .cl-idx-card-h {
    font-size: clamp(20px, 2.6vw, 24px);
    font-weight: 800;
    line-height: 1.22;
    letter-spacing: -.01em;
    margin: 10px 0;
    text-wrap: balance;
}
.cl-idx-card-body {
    font-size: clamp(14.5px, 1.9vw, 16px);
    line-height: 1.58;
    color: var(--cl-fg-secondary);
    margin: 0 0 16px;
}
.cl-idx-card-sched {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    /* margin-top: auto pins the schedule and the link to the bottom of a stretched card, so
       two cards with different body lengths still line their links up. */
    margin: auto 0 16px;
    padding-top: 14px;
    border-top: 1px solid var(--cl-border-subtle);
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.45;
    color: var(--cl-fg-secondary);
}
.cl-idx-card-sched .bi { color: var(--cl-blue-deep); font-size: 16px; flex: 0 0 auto; }

.cl-idx-card-link {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    font-weight: 700;
    color: var(--cl-blue-deep);
    text-decoration: none;
}
/* Stretched hit area: the whole card is clickable for a pointer, while the accessible name
   stays just the link text instead of the entire card being read out as one link. The card
   is position: relative, so this resolves against it. */
.cl-idx-card-link::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
}
.cl-idx-card-link:hover { text-decoration: underline; }
/* The ring lives on .cl-idx-card:focus-within above, which covers the whole target. */
.cl-idx-card-link:focus-visible { outline: none; }
.cl-idx-card-link .bi { font-size: 16px; transition: transform .18s var(--cl-ease); }
.cl-idx-card:hover .cl-idx-card-link .bi { transform: translateX(3px); }

/* ---- instructor strip ---- */
/* Narrower than the landing pages instructor section: this is one short paragraph rather
   than the full biography, so the default 1120px measure leaves the copy stranded beside
   the photo. */
.cl-idx-instructor { max-width: 900px; }
.cl-idx-instructor .cl-instructor-photo { max-width: 320px; }
.cl-idx-instructor .cl-prose:last-of-type { margin-bottom: 0; }

/* ---- dark mode ---- */
/* Almost everything above is token-driven and needs nothing here (see the token block at
   the top of this file). These are the exceptions: --cl-blue-deep is NOT remapped for dark
   mode and lands around 2.4:1 on the dark ground, the same gap .cl-hero-call and
   .cl-enroll-call already carry overrides for. The submit button is exempt because it is a
   white-on-blue FILL, where the deep blue is the background and still passes. */
html.dark-mode .cl-idx-card-label,
body.dark-mode .cl-idx-card-label,
html.dark-mode .cl-idx-card-link,
body.dark-mode .cl-idx-card-link { color: #5BC8FF; }

/* The submit button, and this one is a real failure rather than a precaution. The existing
   `html.dark-mode .cl-openhouse-submit { color: #5BC8FF }` above was written for the OUTLINE
   button the landing pages use, where a light blue label sits on a TRANSPARENT background and
   reads fine. This page fills the same button with --cl-blue-deep, so that inherited rule put
   #5BC8FF on #0174B1: about 2.1:1, well under AA, on the only call to action the page has.
   Caught by reading the computed styles in dark mode, not from the diff -- the diff shows a
   `color: #fff` that simply never wins.
   Specificity (0,3,1) beats the (0,2,1) rule it has to override; source order alone would not
   be enough. */
html.dark-mode .cl-idx-openhouse .cl-openhouse-submit,
body.dark-mode .cl-idx-openhouse .cl-openhouse-submit {
    color: #fff;
    border-color: var(--cl-blue-deep);
}
html.dark-mode .cl-idx-when .bi,
body.dark-mode .cl-idx-when .bi,
html.dark-mode .cl-idx-card-sched .bi,
body.dark-mode .cl-idx-card-sched .bi { color: #5BC8FF; }

/* ---- reduced motion ---- */
/* The card lift and the arrow nudge, opted out for the same reason the buttons and .cl-card
   already are in the reduced-motion block earlier in this file.
   It needs its OWN block rather than an extra selector on that one: this section is appended
   AFTER it, and .cl-idx-card:hover is (0,2,0) -- exactly the same specificity as
   `.cl-card:hover` there -- so adding .cl-idx-card to that rule would lose on source order and
   silently do nothing at all. Same trap as the dark-mode submit override above. */
@media (prefers-reduced-motion: reduce) {
    .cl-idx-card,
    .cl-idx-card-link .bi { transition: none; }
    .cl-idx-card:hover { transform: none; }
    .cl-idx-card:hover .cl-idx-card-link .bi { transform: none; }
}
