/* ===================================
   VSVSV - Main Stylesheet
   =================================== */
/* Montserrat is SELF-HOSTED — see css/fonts.css, loaded before this file.
   An @import lived here and survived the migration that removed every Google
   <link> tag, so the site still fetched the font from Google on every page.
   An @import is the worst shape for it: the browser cannot even discover the
   URL until this stylesheet has been downloaded and parsed, so it is a
   serialised second round trip that blocks rendering behind the first.
   Do not reintroduce one; add @font-face to fonts.css instead. */

/* ===== CSS Variables ===== */
:root {
    /* Brand palette — VSVSV (see docs/brand-guidelines.md) */
    --brand-navy: #002D62;        /* primary */
    --brand-navy-dark: #001B3D;   /* deeper navy for gradients/footer */
    --brand-crimson: #A6192E;     /* energy / accent */
    --brand-crimson-dark: #7d1322;
    --brand-sky: #6AAAD7;         /* light accent on light backgrounds */
    --brand-sky-light: #A9CFE8;   /* accent on dark backgrounds (better contrast) */

    /* Semantic aliases (kept so existing rules don't need renaming) */
    --ncc-green: var(--brand-navy);       /* legacy name → now navy */
    --military-olive: var(--brand-crimson); /* legacy name → now crimson */
    --gold-accent: var(--brand-sky-light);  /* legacy name → now light sky */

    /* Secondary Colors */
    --white: #ffffff;
    --light-gray: #f5f5f5;
    --dark-gray: #1f2937;
    --text-gray: #4a5a6e;   /* was var(--surface-ink-muted): 3.15:1 on white, below the 4.5:1 AA floor */

    /* Status Colors */
    --success: #1e8e5a;
    --warning: #c98a00;
    --danger: var(--brand-crimson);
    --info: var(--brand-sky);

    /* Typography
       Montserrat carries NO DEVANAGARI. Until 2026-08-12 it was the only face
       loaded, so every Hindi string on the site rendered in whatever the OS
       happened to substitute — a different face, different weight and different
       vertical metrics from the Latin around it, on a platform whose whole pitch
       is studying in your own language.

       Font fallback is PER GLYPH, not per element: Latin characters still take
       Montserrat, and only the characters Montserrat cannot draw fall through to
       Noto Sans Devanagari. So this needs no per-language font switching and no
       change to any component — it fixes Hindi everywhere at once, including
       strings that mix scripts ("A प्रमाणपत्र").

       Noto Sans Devanagari is requested in the SAME Google Fonts URL as
       Montserrat, so it costs no extra connection. */
    /* UPDATE 2026-08-24: naming the Devanagari family here put it in the inherited
       chain of every element on every page, including English ones. The language
       switcher shows "हिन्दी"/"मराठी" in its <option>s, and those ~10 glyphs were
       enough to make the browser fetch the whole 118 KB Devanagari face on an English
       page — measured on the live homepage. Scoping the family to pages that are
       ACTUALLY in a Devanagari language (below) keeps every word of the reasoning
       above true where it matters, and stops English visitors paying for it.
       Switcher labels fall back to the system Indic font, which renders them fine. */
    --font-heading: 'Montserrat', system-ui, sans-serif;
    --font-body: 'Montserrat', system-ui, sans-serif;
    
    /* Spacing */
    --spacing-xs: 0.5rem;
    --spacing-sm: 1rem;
    --spacing-md: 2rem;
    --spacing-lg: 3rem;
    --spacing-xl: 4rem;
    
    /* Border Radius */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    
    /* Shadows */
    --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
    --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.15);
    --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.2);
    
    /* Transitions */
    --transition: all 0.3s ease;
}

/* Devanagari languages get the webfont; everyone else does not download it.
   `lang` is set on <html> by i18n.js when a language is chosen, and by the
   server-rendered shells. Per-glyph fallback still applies WITHIN these pages,
   so mixed-script strings ("A प्रमाणपत्र") still render correctly. */
html[lang="hi"], html[lang="mr"] {
    --font-heading: 'Montserrat', 'Noto Sans Devanagari', system-ui, sans-serif;
    --font-body: 'Montserrat', 'Noto Sans Devanagari', system-ui, sans-serif;
}

/* Tri-lingual blocks put Hindi/Marathi BESIDE English on otherwise-English pages
   (tri-lingual.js injects .tri-block / .tri-text / .tri-notice). Those are real
   Devanagari CONTENT — as opposed to the switcher's two labels — so they keep the
   webfont even when the document language is English. This is the reason the rule
   above is scoped by document language rather than applied to the whole page: it
   has to stay possible for one page to be English and still render Hindi properly. */
.tri-block, .tri-text, .tri-notice {
    font-family: 'Montserrat', 'Noto Sans Devanagari', system-ui, sans-serif;
}


/* ===== Devanagari typography corrections =====================================
   Applied by script: i18n.js sets <html lang="…"> on every language switch, so
   :lang() is live rather than aspirational.

   Two Latin typographic habits actively damage Devanagari:

   1. text-transform: uppercase is a NO-OP for Devanagari — the script has no
      letter case. So an uppercased label silently stops being a style signal in
      Hindi while still costing the layout the extra tracking below.
   2. letter-spacing separates a Devanagari cluster from its own matras and
      conjuncts. Latin letters are discrete; Devanagari glyphs combine, and
      forcing space between them is closer to breaking a word than to tracking
      it. Our eyebrows and chips sit at .1–.16em, which is well past the point
      where that shows.

   Kept narrow on purpose: only the two properties that misbehave, only for the
   scripts that need it. Nothing here changes English rendering.
   =========================================================================== */
:lang(hi), :lang(mr), :lang(sa), :lang(ne), :lang(mai), :lang(brx), :lang(doi), :lang(kok) {
    letter-spacing: normal;
}
:lang(hi) [class*="eyebrow"], :lang(hi) [class*="chip"], :lang(hi) [class*="tag"],
:lang(hi) [class*="label"], :lang(hi) [class*="kicker"], :lang(hi) .track-band-count,
:lang(mr) [class*="eyebrow"], :lang(mr) [class*="chip"], :lang(mr) [class*="tag"],
:lang(mr) [class*="label"], :lang(mr) [class*="kicker"], :lang(mr) .track-band-count {
    text-transform: none;
    letter-spacing: normal;
}

/* ===== Reset & Base Styles ===== */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* THE CANVAS IS WHAT THE BROWSER PAINTS DURING THE CROSS-PAGE GAP, before the
       new document has any content to show. Whatever colour this is, the user
       sees it full-screen on every single navigation.

       This was a flat `var(--brand-navy)` (652a7bb1d, "kill the white-flash
       burst") — which fixed a thin white strip where the navbar lands, but paid
       for it with a FULL-SCREEN NAVY FLASH between every pair of pages. Measured
       on production with a CDP screencast: the whole viewport was solid navy at
       147ms and still navy-washed at 210ms, on pages whose body is white.

       Every page has the same structure — a 105px navy navbar over a white body —
       so the canvas is painted to match it. During the gap the user now sees
       something shaped like the page instead of a dark rectangle.

       MEASURED, and it is why the strip approach was wrong: sampling the real
       rendered background of 14 pages down the left gutter, EVERY page is navy at
       the top of the viewport, and most are navy for the whole of it — the heroes
       are full-viewport navy gradients (the homepage hero alone is 789px of an
       800px viewport). A canvas that is 87% white is therefore wrong for nearly
       the entire site, and it showed: navigating between two dark pages put a
       near-white screen between them, which reads as the whole site changing, not
       the page. Measured about -> index on production, mean brightness:

         white-strip canvas   43 (old page) -> 228 (WHITE FLASH) -> ramp down to 49
         solid navy canvas    43 -> 43, no flash and no ramp at all

       The ramp was the navy hero compositing over a white canvas as it rasterised;
       with navy underneath there is nothing to ramp from. So the canvas is solid
       brand-navy — which is what 652a7bb1d originally chose, and it was right about
       this. Its mistake was the OTHER half of that commit, the cross-fade, which is
       removed below. Navy canvas + no cross-fade is the combination that had never
       been tried. */
    background-color: var(--brand-navy);
}

body {
    font-family: var(--font-body);
    font-size: 16px;
    line-height: 1.6;
    color: var(--dark-gray);
    background-color: var(--white);
    overflow-x: hidden;
}

/* ─────────────────────────────────────────────────────────────────────────
   Cross-page navigation: NO view transition. THIS IS DELIBERATE.

   652a7bb1d ("kill the white-flash burst") opted the whole site into the
   cross-document View Transitions API, on the reasoning that a cross-fade would
   be softer than a hard page swap. In practice it IS the flash the owner kept
   reporting, and it survived the earlier fix to the html background because it
   happens BEFORE the blank-canvas moment that fix addressed.

   Measured on production, homepage -> courses, real click, throttled, by mean
   brightness of the middle of the viewport across compositor frames:

     with @view-transition   49 -> 54 -> 65 -> 80 -> 99 -> 120 -> 139 -> 155 ...
     without it              49 -> 236, in a single frame

   The cross-fade washes the OLD page out through pale grey-blue before the new
   one arrives. On a site whose pages are navy-heavy that reads as a blink, and it
   is ~400ms long on a slow connection. A hard swap is not a compromise here: the
   html canvas is already painted as the page skeleton (navy navbar strip over
   white, above), so there is nothing jarring left to soften.

   Do not re-enable this without capturing frames and looking at them. Neither CLS
   (0.0000) nor FCP shows it — it is invisible to every timing metric.
   ───────────────────────────────────────────────────────────────────────── */

/* NO PAGE-LOAD ENTRANCE ANIMATIONS. THIS IS DELIBERATE.

   Cards used `nccFadeUp` and the hero used `fadeInUp` (1s, staggered to 0.55s, so
   up to 1.55s), both starting at opacity 0. That meant every page visibly
   assembled itself on arrival: 19 animations were running on the homepage alone,
   measured with document.getAnimations(). Combined with the cross-page gap it read
   as the whole site changing rather than a page changing, which is what the owner
   kept reporting.

   Content is now painted in its final state. If an entrance effect is ever wanted
   back, it must not start from opacity 0 — a viewer should never see the page
   without its content — and it must sit inside a `prefers-reduced-motion:
   no-preference` guard, which the hero animations never did. */

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading);
    font-weight: 700;
    line-height: 1.2;
    margin-bottom: 1rem;
    color: var(--ncc-green);
}

h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
h5 { font-size: 1.1rem; }
h6 { font-size: 1rem; }

p {
    margin-bottom: 1rem;
    color: var(--text-gray);
}

a {
    text-decoration: none;
    color: inherit;
    transition: var(--transition);
}

img {
    max-width: 100%;
    height: auto;
    display: block;
}

/* ===== Accessibility Utilities ===== */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}

ul {
    list-style: none;
}

/* ===== Container ===== */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 2rem;
}

/* ===== Buttons ===== */
.btn {
    display: inline-block;
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    font-weight: 600;
    text-align: center;
    border-radius: var(--radius-md);
    cursor: pointer;
    border: 2px solid transparent;
    transition: var(--transition);
    white-space: nowrap;
}

.btn i {
    margin-right: 0.5rem;
}

.btn-primary {
    background-color: var(--ncc-green);
    color: var(--white);
    border-color: var(--ncc-green);
}

.btn-primary:hover {
    background-color: var(--military-olive);
    border-color: var(--military-olive);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.btn-secondary {
    background-color: var(--military-olive);
    color: var(--white);
    border-color: var(--military-olive);
}

.btn-secondary:hover {
    background-color: var(--ncc-green);
    border-color: var(--ncc-green);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.btn-outline {
    background-color: transparent;
    color: var(--white);
    border-color: var(--white);
}

.btn-outline:hover {
    background-color: var(--white);
    color: var(--ncc-green);
}

/* .btn-outline-on-light — same outline shape as .btn-outline but with navy
   ink + navy border so it stays visible when placed on a white/light card.
   Use this instead of .btn-outline inside .course-card / .spec-card. */
.btn-outline-on-light,
.course-card .btn-outline,
.spec-card .btn-outline {
    background-color: transparent;
    color: var(--brand-navy);
    border-color: var(--brand-navy);
}

/* One-time course price shown on catalog cards (courses.html + index live grid). */
.course-price {
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--brand-navy);
    margin: 6px 0 12px;
}
.course-price .course-price-note {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--surface-ink-muted);
}
.course-price-free {
    color: #1a7f4b;
}

.btn-outline-on-light:hover,
.course-card .btn-outline:hover,
.spec-card .btn-outline:hover {
    background-color: var(--brand-navy);
    color: var(--white);
}

/* "Coming soon" course-card: visually clear it is not purchasable. */
.course-card-comingsoon {
    opacity: 0.78;
    border: 1px dashed rgba(0, 45, 98, 0.35);
}
.course-card-comingsoon .course-badge {
    background: var(--surface-ink-muted);
    color: #fff;
}
.course-card-empty {
    grid-column: 1 / -1;
    text-align: center;
    padding: 2rem;
}

.btn-light {
    background-color: var(--white);
    color: var(--ncc-green);
    border-color: var(--white);
}

.btn-light:hover {
    background-color: var(--brand-crimson);
    color: var(--white);
    border-color: var(--brand-crimson);
}

/* Secondary action on a dark band. `.btn-outline` is scoped to light grounds
   (it inherits a dark border and dark label), so it disappears on the CTA. */
.btn-outline-light {
    background-color: transparent;
    color: var(--white);
    border-color: rgba(255, 255, 255, 0.55);
}

.btn-outline-light:hover,
.btn-outline-light:focus-visible {
    background-color: rgba(255, 255, 255, 0.14);
    border-color: var(--white);
    color: var(--white);
}

/* Buttons on a centred band: wrap rather than overflow on a phone. */
.cta-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.9rem;
}

.btn-sm {
    padding: 0.5rem 1rem;
    font-size: 0.9rem;
}

.btn-lg {
    padding: 1rem 2rem;
    font-size: 1.1rem;
}

/* ===== Header & Navigation ===== */
.header {
    background-color: var(--ncc-green);
    box-shadow: var(--shadow-md);
    position: sticky;
    top: 0;
    z-index: 1000;
}

.navbar {
    padding: 1rem 0;
    /* Make every navbar variant lay out as a single horizontal row at
       desktop widths. Without this, pages using the
       <nav class="navbar"><div class="nav-brand">…</div><ul class="nav-menu">…</ul></nav>
       pattern (about/login/register/courses/forgot-pw/course-detail/404)
       stack the brand and the menu on separate lines because <nav> is
       block by default and there was no flex rule on .navbar itself —
       which is the "menu jumps below the logo when I navigate to About"
       bug. The nav-wrapper / nav-container pattern overrides this with
       its own flex rules; this just gives every page a sane baseline. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: nowrap;
}

/* The V2 navbar wraps its contents in .nav-container, so the
   <nav class="navbar"> itself shouldn't be flex (it would double up).
   Undo the baseline only for that case. (The legacy .nav-wrapper /
   .nav-brand / .brand-text V1 markup was retired on 2026-05-14 when
   all 13 public pages were unified — see commit history.) */
.navbar:has(.nav-container) {
    display: block;
}

/* ─────────────────────────────────────────────────────────────────────────
   V2 navbar branding (the only navbar variant in use as of 2026-05-14).
   The navbar uses the icon mark (ncc-icon.png — just the tri-colour
   swoosh) in a small SQUARE white badge so the navy stroke stays
   readable on the navbar's navy background; the "VSVSV" wordmark
   sits as a SEPARATE text element to the right of the icon.
   Pattern:  .nav-logo > .nav-logo-img + .logo-text > .logo-main
   ───────────────────────────────────────────────────────────────────────── */
.nav-logo .nav-logo-img {
    width: 46px;
    height: 46px;
    object-fit: contain;
    flex: 0 0 auto;
    display: block;
    background: #ffffff;
    border-radius: 8px;           /* SQUARE-ish, not circular */
    padding: 4px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
}

/* Brand name shown to the RIGHT of the icon in white text. */
.nav-logo .logo-main {
    display: inline-block;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--white);
    letter-spacing: 0.2px;
    line-height: 1;
    white-space: nowrap;
}

/* Hide the decorative subtitle on narrow viewports. */
.nav-logo .logo-sub {
    display: none;
}

/* Dashboard / admin vertical sidebar — square icon at the top */
.sidebar-logo {
    width: 56px;
    height: 56px;
    object-fit: contain;
}

/* ===== Canonical V2 navbar (used by every public page) =====
   Top-level <nav class="navbar">, .nav-container as the flex row,
   .nav-logo with .nav-logo-img + .logo-main + .logo-sub, and
   .nav-buttons for Login/Register. Unified across all 13 public
   pages on 2026-05-14. */
.navbar:has(.nav-container) {
    background-color: var(--brand-navy);
    position: sticky;
    top: 0;
    z-index: 1000;
    box-shadow: var(--shadow-md);
}

.nav-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0.85rem 2rem;
    gap: 1.5rem;
}

.nav-logo {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex: 0 0 auto;
}

/* (Per-class .nav-logo-img / .logo-main / .logo-sub rules live in the
   "V2 navbar branding" block earlier in this file.) */

.nav-container > .nav-menu {
    display: flex;
    align-items: center;
    gap: 1.75rem;
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    justify-content: center;
}

.nav-container .nav-link {
    color: var(--white);
    font-weight: 500;
    padding: 0.4rem 0;
    position: relative;
    text-decoration: none;
    transition: color 0.2s ease;
}

.nav-container .nav-link:hover,
.nav-container .nav-link.active {
    color: var(--brand-sky-light);
}

.nav-container .nav-link.active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -4px;
    height: 2px;
    background: var(--brand-crimson);
}

.nav-container .nav-link.active {
    text-decoration: none;
}

.nav-buttons {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    flex: 0 0 auto;
}

.nav-buttons .btn {
    padding: 0.55rem 1.15rem;
    font-size: 0.95rem;
}

/* ===== Nav dropdowns (Courses ▾ and the account menu) =====
   CSS-first: opens on hover and on keyboard focus (:focus-within); the
   .is-open class is toggled by JS for touch. Used by the slim navbar that
   main.js renders. */
.nav-dropdown { position: relative; display: inline-flex; align-items: center; }
.nav-dropdown-caret,
.nav-account-caret { font-size: 0.7rem; margin-left: 0.3rem; opacity: 0.85; }

.nav-dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    margin: 0.4rem 0 0;
    padding: 0.4rem 0;
    list-style: none;
    background: var(--white);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 34px rgba(0, 0, 0, 0.18);
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
    z-index: 1200;
}

.nav-dropdown:hover > .nav-dropdown-menu,
.nav-dropdown:focus-within > .nav-dropdown-menu,
.nav-dropdown.is-open > .nav-dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.nav-dropdown-menu li { margin: 0; }
/* .nav-container prefix raises specificity to (0,2,1) so these navy links beat
   the generic white `.nav-menu a` rule (0,1,1) that sits later in the file —
   without it the Courses dropdown rendered white-on-white (invisible). */
.nav-container .nav-dropdown-menu a,
.nav-container .nav-dropdown-menu button {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    text-align: left;
    padding: 0.6rem 1.15rem;
    color: var(--brand-navy);
    background: none;
    border: none;
    font: inherit;
    font-size: 0.92rem;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}
.nav-container .nav-dropdown-menu a:hover,
.nav-container .nav-dropdown-menu button:hover { background: var(--light-gray); color: var(--brand-navy); }
.nav-container .nav-dropdown-menu a::after { display: none; }   /* kill the .nav-menu a underline pseudo */
.nav-dropdown-menu i { width: 1.15em; text-align: center; color: var(--brand-crimson); }

/* Account menu hangs off the right edge (it sits in .nav-buttons, far right) */
.nav-account { position: relative; }
.nav-account .nav-dropdown-menu { left: auto; right: 0; }

@media (max-width: 820px) {
    .nav-container {
        flex-wrap: wrap;
        gap: 0.75rem;
        padding: 0.7rem 1rem;
    }
    .nav-container > .nav-menu {
        order: 3;
        flex-basis: 100%;
        justify-content: flex-start;
        gap: 1rem;
        flex-wrap: wrap;
        padding-top: 0.4rem;
        border-top: 1px solid rgba(255,255,255,0.12);
    }
    .nav-logo .logo-sub {
        display: none;
    }
}

.nav-menu {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    flex-wrap: nowrap;             /* don't bump the menu onto a second row */
    white-space: nowrap;
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav-menu a {
    color: var(--white);
    font-weight: 500;
    padding: 0.5rem 0;
    position: relative;
}

.nav-menu a::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 0;
    height: 2px;
    background-color: var(--gold-accent);
    transition: var(--transition);
}

.nav-menu a:hover::after,
.nav-menu a.active::after {
    width: 100%;
}

/* ─────────────────────────────────────────────────────────────────────────
   Canonical Login / Register buttons used in every public-page navbar.
   The audit on 2026-05-14 found five different colour pairs across 13
   pages (.btn-login/.btn-register on index, .btn-secondary/.btn-primary
   on contact/resources/privacy/terms, .btn-primary + inline-crimson on
   the rest). User-chosen unification: Login = outline-on-navy, Register
   = solid crimson. Used as <a class="btn btn-login"> / <a class="btn
   btn-register"> inside .nav-buttons.
   ───────────────────────────────────────────────────────────────────────── */
.btn-login {
    background-color: transparent;
    color: var(--white);
    border: 2px solid var(--white);
    padding: 0.5rem 1rem;
    border-radius: var(--radius-md);
}

.btn-login:hover {
    background-color: var(--white);
    color: var(--brand-navy);
    border-color: var(--white);
}

.btn-register {
    background-color: var(--brand-crimson);
    color: var(--white);
    border: 2px solid var(--brand-crimson);
    padding: 0.5rem 1rem;
    border-radius: var(--radius-md);
}

.btn-register:hover {
    /* darker crimson on hover so the button stays clearly a CTA — the
       previous rule flipped to white-bg + navy ink, which read as if the
       Register button had been "swapped" with Login. */
    background-color: #a8002e;
    border-color: #a8002e;
    color: var(--white);
}

/* .hamburger is a <button>, not a <div> (2026-08-10). It opens the mobile
   nav, so it has to carry a role and an accessible name and be reachable by
   keyboard — as a <div> it was none of those: no name, no focus, and main.js
   bound click only, so the mobile menu could not be opened without a mouse.
   These four lines strip the UA button chrome so the three bars render
   exactly as they did before. Do NOT reset `outline` — the focus ring is the
   only thing telling a keyboard user where they are. */
.hamburger {
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    cursor: pointer;
    background: none;
    border: 0;
    /* The bars are 25x19, so the button was a 25x19 tap target — under the 24x24
       minimum (WCAG 2.5.8) for the PRIMARY navigation control on a phone. Padding
       rather than width/height so the bars keep their size and position. */
    padding: 6px 4px;
    min-width: 44px;
    min-height: 44px;
    font: inherit;
}

.hamburger span {
    width: 25px;
    height: 3px;
    background-color: var(--white);
    transition: var(--transition);
    /* The bars are decoration; the button's aria-label is the name. */
    display: block;
}

/* ===== Hero Section ===== */
.hero {
    position: relative;
    min-height: 600px;
    display: flex;
    align-items: center;
    background: linear-gradient(135deg, var(--brand-navy) 0%, var(--brand-navy-dark) 100%);
    color: var(--white);
    overflow: hidden;
}

/* Make sure the slider wrappers fill .hero so the inner .container is
   actually centered on the viewport (without these, .hero-slider is a
   shrink-to-fit flex child and the content sits flush-left). */
.hero > .hero-slider,
.hero .hero-slide {
    width: 100%;
    flex: 1 1 auto;
}

.hero::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url('../images/hero-pattern.png') repeat;
    opacity: 0.1;
}

.hero-content {
    position: relative;
    z-index: 2;
    text-align: center;
    /* Asymmetric on purpose. This was `4rem 0`, and .hero already carries
       padding-bottom: 80px, so 64 + 80 left 144px of empty navy under the identity
       router — two paddings that knew nothing about each other, the same doubling
       that produced the 196px hole on /courses.html. The repo's whitespace gate
       allowed it (a band seam gets 2x the 88px limit), which is why no check caught
       a void that is plainly visible on screen. 24px here lands the total near
       104px: the tiles stop floating without cramping the hero. */
    padding: 2.75rem 0 0.9rem;
    max-width: 1160px;
    margin: 0 auto;
}

/* ===== Split hero (copy left, photo right) =====
   The UX review: a text-only hero reads "software demo"; a split hero with a
   real photo reads "institution" and shortens the scroll to the course cards. */
.hero-split {
    display: grid;
    grid-template-columns: 1.05fr 0.95fr;
    gap: 2.75rem;
    align-items: center;
    text-align: left;
}
.hero-split .hero-buttons { justify-content: flex-start; }

/* ===== Hero without a photograph (owner decision, 2026-08-14) =====
   The stock photo was replaced by nothing rather than by another image: it was
   anonymous Western stock that said nothing about the subject, and every
   alternative either cost a licence or carried impersonation risk. With no
   picture the burden shifts to the typography and to the router, so the router
   stops being a footnote under the buttons and becomes the thing the hero is
   FOR — the visitor's first question is "which of these am I?", and that is
   exactly what the tiles answer. */
.hero-split-solo {
    grid-template-columns: 1fr;
    text-align: center;
    gap: 0;
    max-width: 820px;
    margin: 0 auto;
}

.hero-split-solo .hero-buttons { justify-content: center; }

.hero-split-solo .hero-title {
    /* Larger than the split version: it is now the only thing carrying the top
       of the page. Capped in ch so the line count stays stable as it scales.
       24ch, not 20ch: at 20 the headline broke to FOUR lines and stood 242px
       tall on its own, which pushed the instructor band below the fold on a
       1440x900 laptop. Three lines keeps the band on the first screen, which is
       the entire point of a band. */
    font-size: clamp(2rem, 4.2vw, 2.9rem);
    max-width: 24ch;
    margin-left: auto;
    margin-right: auto;
}

.hero-split-solo .hero-subtitle {
    max-width: 60ch;
    margin-left: auto;
    margin-right: auto;
}
.hero-media { display: flex; justify-content: center; }
.hero-photo {
    width: 100%;
    max-width: 560px;
    border-radius: 16px;
    box-shadow: 0 22px 55px rgba(0, 0, 0, 0.4);
}

@media (max-width: 860px) {
    .hero-split {
        grid-template-columns: 1fr;
        text-align: center;
        gap: 1.75rem;
    }
    .hero-split .hero-buttons { justify-content: center; }
    .hero-media { order: -1; }              /* photo above the copy on narrow screens */
    .hero-photo { max-width: 460px; }
}

.hero-title {
    font-size: 3rem;
    color: var(--white);
    margin-bottom: 1.5rem;
}

.hero-subtitle {
    font-size: 1.25rem;
    color: var(--light-gray);
    margin-bottom: 2rem;
}

.hero-buttons {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
}

/* ===== Identity router =====
   Four tiles under the hero CTAs that route each audience (junior cadet,
   senior cadet, ANO/teacher, school) straight to the right destination —
   the single highest-leverage conversion device (exam-picker pattern). */
/* Flex-wrap, not a fixed column count: the router has one tile per audience and
   that number changes (it went 4 → 5 when SSB got its own entry point). A fixed
   `repeat(4, 1fr)` strands the 5th tile hard-left on its own row; wrapping with a
   centred last row absorbs any count. */
.identity-router {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.9rem;
    max-width: 1040px;
    margin: 2.25rem auto 0;
}

/* With no photograph the router IS the hero's picture, so it gets room to be
   one: a wider field, taller tiles, and each audience carrying the accent it
   already has in the course catalogue. The colour is doing work, not decoration
   — a returning visitor learns that amber is the C certificate and teal is SSB,
   and the same coding greets them on /courses. */
.hero-split-solo ~ .identity-router,
.hero-content .identity-router {
    max-width: 1120px;
    margin-top: 1.9rem;
    gap: 1rem;
}

.identity-tile {
    flex: 1 1 190px;
    max-width: 260px;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 1.1rem 0.9rem;
    text-align: center;
    color: var(--white);
    text-decoration: none;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: var(--radius-md);
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.identity-tile i {
    font-size: 1.5rem;
    color: var(--brand-sky-light);
}

.identity-tile-label {
    font-size: 0.92rem;
    font-weight: 600;
    line-height: 1.3;
}

.identity-tile:hover,
.identity-tile:focus-visible {
    background: rgba(255, 255, 255, 0.14);
    border-color: var(--white);
    transform: translateY(-3px);
    outline: none;
}

@media (max-width: 720px) {
    .identity-router {
        gap: 0.7rem;
    }
    /* Two per row on phones. The basis must be small enough that two tiles plus the
       gap fit the narrowest supported viewport (320px); the max-width must match it,
       or an odd-numbered last tile grows to the desktop 260px cap and sits visibly
       wider than the column above it. */
    .identity-tile {
        flex: 1 1 min(130px, calc(50% - 0.35rem));
        max-width: calc(50% - 0.35rem);
    }
}

/* ===== Coming-soon strip =====
   One slim row for tracks that aren't buyable yet — honest, but without
   giving unpurchasable items the same shelf space as live courses. */
.coming-soon-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.5rem 1rem;
    margin: 2rem auto 0;
    max-width: 820px;
    padding: 0.85rem 1.25rem;
    background: var(--light-gray);
    border: 1px dashed rgba(0, 45, 98, 0.3);
    border-radius: var(--radius-md);
    font-size: 0.95rem;
}

.coming-soon-label {
    font-weight: 700;
    color: var(--brand-navy);
    white-space: nowrap;
}

.coming-soon-label i { margin-right: 0.35rem; color: var(--surface-ink-muted); }

.coming-soon-items { color: var(--text-gray); }

.coming-soon-link {
    margin-left: auto;
    color: var(--brand-crimson);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.coming-soon-link:hover { text-decoration: underline; }

@media (max-width: 560px) {
    .coming-soon-link { margin-left: 0; }
}

/* ===== Section Styles ===== */
/* Page-level band rhythm. `section` is used here as a PAGE BAND, so it carries
   the band spacing. */
section {
    padding: var(--rhythm-band, 5rem) 0;
}

/* A section nested inside another section is a sub-block, not a second page
   band — it must not re-apply the band rhythm.

   WHY: this blunt element selector was the largest single source of dead space
   on the site. The visual audit of 2026-08-12 found 18 empty bands over 110px
   across five pages; the biggest were simply two `section` paddings meeting
   (80px + 80px), plus whatever margin the inner block also carried. On
   /courses.html the DB-rendered `<section class="track">` sits inside
   `<section class="courses-section">`, so every track paid the band tax twice.
   Separation between sub-blocks comes from the container's own margin
   (`.track` uses --rhythm-section), never from re-padding. */
section section {
    padding-block: 0;
}

.section-header {
    text-align: center;
    margin-bottom: 3rem;
}

.section-header h2 {
    font-size: 2.5rem;
    margin-bottom: 1rem;
}

.section-header p {
    font-size: 1.1rem;
    color: var(--text-gray);
}

/* ===== Features Section ===== */
.features {
    background-color: var(--light-gray);
}

.features-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 2rem;
    max-width: 960px;
    margin: 0 auto;
}

@media (max-width: 720px) {
    .features-grid {
        grid-template-columns: 1fr;
    }
}

.feature-card {
    background-color: var(--white);
    padding: 2rem;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
    text-align: center;
}

.feature-card:hover {
    transform: translateY(-10px);
    box-shadow: var(--shadow-lg);
}

.feature-icon {
    width: 80px;
    height: 80px;
    margin: 0 auto 1.5rem;
    background: linear-gradient(135deg, var(--ncc-green), var(--military-olive));
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white);
    font-size: 2rem;
}

.feature-card h3 {
    margin-bottom: 1rem;
    font-size: 1.5rem;
}

.feature-card p {
    margin-bottom: 1.5rem;
}

.feature-list {
    text-align: left;
}

.feature-list li {
    padding: 0.5rem 0;
    color: var(--text-gray);
}

.feature-list i {
    color: var(--brand-crimson);
    margin-right: 0.5rem;
}

/* ===== Statistics Section ===== */
.statistics {
    background: linear-gradient(135deg, var(--brand-navy) 0%, var(--brand-navy-dark) 100%);
    color: var(--white);
}

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
    gap: 2rem;
}

.stat-card {
    background-color: rgba(255, 255, 255, 0.1);
    padding: 2rem;
    border-radius: var(--radius-lg);
    text-align: center;
    backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.2);
    transition: var(--transition);
}

.stat-card:hover {
    background-color: rgba(255, 255, 255, 0.15);
    transform: translateY(-5px);
}

.stat-icon {
    font-size: 3rem;
    margin-bottom: 1rem;
    color: var(--gold-accent);
}

.stat-number {
    font-size: 3rem;
    font-weight: 700;
    color: var(--white);
    margin-bottom: 0.5rem;
}

.statistics .stat-card h3 {
    color: var(--white);
    font-size: 1.2rem;
    margin-bottom: 0.5rem;
}

.stat-card p {
    color: var(--light-gray);
    font-size: 1.05rem;
}

/* ===== Courses Preview Section =====
   NOTE on `minmax(min(Npx, 100%), 1fr)` — used by every auto-fit grid in this
   file and in about/contact/for-schools/dashboard. A bare `minmax(Npx, 1fr)`
   CANNOT shrink below Npx, so on a viewport narrower than the floor the track
   overhangs its container; `body { overflow-x: hidden }` then CLIPS the card
   rather than scrolling to it, so the right-hand edge of the content is just
   gone. Wrapping the floor in `min(Npx, 100%)` lets the track collapse to the
   container width on very narrow screens (320px phones, the Galaxy Fold's
   280px outer display) and behaves identically everywhere else. Found on
   about.html and courses.html at 320px. */
.courses-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: 2rem;
}

/* Homepage track teaser only. It carries FOUR tiles since 2026-08-17 (instructor
   courses became their own track), and the shared 320px minimum fits only three in
   the 1136px container — stranding the fourth alone on its own row. That is the same
   marooned-card defect that left SSB's single card at 271px in 1136px on the
   programme pages. Scoped by id so every other .courses-grid is untouched.

   auto-fit cannot be used here: it packs as many columns as fit, which is THREE in
   the tablet range and strands the fourth again. Four tiles need an EVEN column
   count, so the counts are explicit — 4 wide, then 2x2, then a single stack. */
#liveCoursesGrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
}
@media (min-width: 1100px) {
    #liveCoursesGrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 619px) {
    #liveCoursesGrid { grid-template-columns: 1fr; }
}

.course-card {
    background-color: var(--white);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
}

.course-card:hover {
    transform: translateY(-10px);
    box-shadow: var(--shadow-lg);
}

.course-image {
    position: relative;
    height: 200px;
    background: linear-gradient(135deg, var(--brand-navy) 0%, var(--brand-navy-dark) 100%);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.course-card-wing {
    border-top: 5px solid var(--brand-navy);
}

.course-card-army {
    border-top-color: #355e3b;
}

.course-card-navy {
    border-top-color: #005f73;
}

.course-card-air {
    border-top-color: #2f80ed;
}

.course-card-rvc {
    border-top-color: #7a3b2e;
}

.course-image-army {
    background:
        linear-gradient(135deg, rgba(53,94,59,.96), rgba(0,45,98,.96)),
        radial-gradient(circle at 18% 20%, rgba(255,255,255,.12), transparent 28%);
}

.course-image-rvc {
    background:
        linear-gradient(135deg, rgba(122,59,46,.96), rgba(0,45,98,.96)),
        radial-gradient(circle at 18% 20%, rgba(255,255,255,.12), transparent 28%);
}

.course-image-navy {
    background:
        linear-gradient(135deg, rgba(0,45,98,.96), rgba(0,95,115,.96)),
        radial-gradient(circle at 82% 20%, rgba(106,170,215,.2), transparent 30%);
}

.course-image-air {
    background:
        linear-gradient(135deg, rgba(0,45,98,.96), rgba(47,128,237,.88)),
        radial-gradient(circle at 74% 22%, rgba(255,255,255,.2), transparent 32%);
}

/* Homepage three-track teaser.
   The army/navy/air gradients above encode the NCC WINGS. The homepage teaser is
   three PRODUCT TRACKS (NCC / SSB / Agniveer), so borrowing one wing colour per
   track rendered three unrelated gradients side by side — a visual mismatch with
   no meaning behind it. All three tiles share this one treatment instead. */
.course-image-track {
    background:
        linear-gradient(135deg, rgba(0,45,98,.96), rgba(0,95,115,.96)),
        radial-gradient(circle at 82% 20%, rgba(106,170,215,.2), transparent 30%);
}

/* Equal-height teaser cards with the CTA on a shared baseline. Without this the
   Agniveer tile (no price line, because nothing is purchasable yet) was shorter,
   so its button floated above the other two. Scoped to #liveCoursesGrid so the
   catalogue cards on courses.html are untouched. */
#liveCoursesGrid .course-card { display: flex; flex-direction: column; }
#liveCoursesGrid .course-content { display: flex; flex-direction: column; flex: 1 1 auto; }
#liveCoursesGrid .course-actions { margin-top: auto; }

/* Count/status badge sits ON the dark thumbnail, but .course-badge sets no colour
   at all — so it inherited near-black body text over a navy gradient (~1.5:1
   contrast, effectively unreadable). Light pill, identical for the count badges
   and the "In development" one, so the three tiles read as one system. */
#liveCoursesGrid .course-badge {
    background: rgba(255, 255, 255, .18);
    border: 1px solid rgba(255, 255, 255, .30);
    color: #fff;
    font-size: .72rem;
    letter-spacing: .04em;
}

.wing-chip {
    position: absolute;
    left: 1rem;
    top: 1rem;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(255,255,255,.92);
    color: var(--brand-navy);
    font-size: .78rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .02em;
}

.course-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.course-image .course-thumb-icon {
    position: relative;
    z-index: 1;
    font-size: 4rem;
    color: var(--brand-sky-light);
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25));
}

.course-image::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        radial-gradient(circle at 25% 25%, rgba(255,255,255,.08) 0%, transparent 50%),
        radial-gradient(circle at 75% 75%, rgba(166,25,46,.18) 0%, transparent 60%);
    pointer-events: none;
}

.course-badge {
    position: absolute;
    top: 1rem;
    right: 1rem;
    padding: 0.25rem 0.75rem;
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
    font-weight: 600;
    text-transform: uppercase;
}

.course-badge.free {
    /* --success is the FILL green at 4.14:1 against white text, and this badge is
       0.85rem/600 — not "large text", so the 3:1 allowance does not apply. Uses the
       ink green instead: 5.40:1. Pre-existing since the initial commit; found by
       re-auditing the palette work on 2026-08-14. */
    background-color: var(--success-ink);
    color: var(--white);
}

.course-badge.popular {
    background-color: var(--brand-crimson);
    color: var(--white);
}

.course-badge.advanced {
    background-color: var(--danger);
    color: var(--white);
}

.course-content {
    padding: 1.5rem;
}

.course-content h3 {
    margin-bottom: 0.75rem;
}

.course-meta {
    display: flex;
    gap: 1rem;
    margin-bottom: 1rem;
    font-size: 0.9rem;
    color: var(--text-gray);
}

.course-meta i {
    margin-right: 0.25rem;
}

.course-stats {
    display: flex;
    justify-content: space-between;
    padding: 1rem 0;
    border-top: 1px solid var(--light-gray);
    border-bottom: 1px solid var(--light-gray);
    margin: 1rem 0;
    font-size: 0.9rem;
    color: var(--text-gray);
}

.course-stats i {
    margin-right: 0.25rem;
    color: var(--ncc-green);
}

.course-actions {
    display: flex;
    gap: 0.75rem;
}

.course-actions .btn {
    flex: 1;
}

/* ===== Updates Section ===== */
.updates {
    background-color: var(--light-gray);
}

.updates-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: 2rem;
}

.update-card {
    background-color: var(--white);
    padding: 2rem;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
}

.update-card:hover {
    transform: translateY(-5px);
    box-shadow: var(--shadow-md);
}

.update-icon {
    width: 60px;
    height: 60px;
    background-color: var(--ncc-green);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white);
    font-size: 1.5rem;
    margin-bottom: 1rem;
}

.update-date {
    font-size: 0.85rem;
    color: var(--text-gray);
    margin-bottom: 1rem;
}

.update-card h3 {
    margin-bottom: 1rem;
    font-size: 1.25rem;
}

.update-link {
    color: var(--ncc-green);
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.update-link:hover {
    color: var(--military-olive);
}

.update-link i {
    transition: var(--transition);
}

.update-link:hover i {
    transform: translateX(5px);
}

/* ===== CTA Section ===== */
.cta {
    background: linear-gradient(120deg, var(--brand-navy) 0%, var(--brand-navy-dark) 55%, var(--brand-crimson) 135%);
    color: var(--white);
    text-align: center;
    /* 2.25rem, was 4rem. Measured on the NCC landing page: a visitor had to
       scroll 2.8 SCREENS before the first price appeared, and this band was
       256px of navy carrying two lines of text. It is a signpost to the
       catalogue below, not a destination -- it does not need hero height. */
    padding: 2.25rem 0;
}

.cta-content h2 {
    color: var(--white);
    font-size: 2.5rem;
    margin-bottom: 1rem;
}

.cta-content p {
    color: var(--light-gray);
    font-size: 1.25rem;
    margin-bottom: 2rem;
}

.cta .cta-content h2 {
    color: #fff !important;
}

.cta .cta-content p {
    color: rgba(255,255,255,.9) !important;
}

/* Compliance/disclaimer line inside a CTA. Must stay legible — it is the
   non-affiliation notice, not fine print to be hidden — but should not compete
   with the offer above it. */
.cta .cta-content p.cta-note {
    font-size: .95rem;
    line-height: 1.55;
    max-width: 46rem;
    margin: 0 auto 1.75rem;
    color: rgba(255,255,255,.82) !important;
}

/* ===== Footer ===== */
.footer {
    background-color: var(--brand-navy-dark);
    color: var(--white);
    padding: 3rem 0 1rem;
}

.footer-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
    gap: 2rem;
    margin-bottom: 2rem;
}

/* Every footer h3 must contrast against the dark-navy footer. Without
   this baseline rule, the global h1-h6 selector colours h3 as navy
   (--ncc-green → --brand-navy) and on pages using `.footer-section`
   instead of `.footer-column` (about.html / courses.html / login.html /
   register.html etc), the h3 came out navy-on-navy and invisible. */
.footer h3,
.footer-col h3,
.footer-row h3,
.footer-content h3,
.footer-section h3 {
    color: var(--brand-sky-light);
    margin-bottom: 1rem;
    font-size: 1.25rem;
    font-weight: 700;
}

.footer-column h3 {
    color: var(--gold-accent);
    margin-bottom: 1rem;
    font-size: 1.25rem;
}

.footer-column p {
    color: var(--light-gray);
    margin-bottom: 1rem;
}

.footer-column ul li {
    margin-bottom: 0.75rem;
}

.footer-column ul li a {
    color: var(--light-gray);
    transition: var(--transition);
}

.footer-column ul li a:hover {
    color: var(--gold-accent);
    padding-left: 5px;
}

.social-links {
    display: flex;
    gap: 1rem;
    margin-top: 1rem;
}

.social-links a {
    width: 40px;
    height: 40px;
    background-color: rgba(255, 255, 255, 0.1);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white);
    transition: var(--transition);
}

.social-links a:hover {
    background-color: var(--brand-crimson);
    color: var(--white);
    transform: translateY(-3px);
}

.contact-info li {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    color: var(--light-gray);
    margin-bottom: 0.75rem;
}

.contact-info i {
    color: var(--gold-accent);
    width: 20px;
}

.footer-bottom {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    padding-top: 2rem;
}

.footer-disclaimer {
    color: var(--light-gray);
    font-size: 0.8rem;
    line-height: 1.55;
    margin: 0 0 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    opacity: 0.85;
}

.footer-bottom-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
}

/* `.footer-bottom > p` too: For Schools puts the copyright line straight into
   .footer-bottom with no .footer-bottom-content wrapper, so this rule missed it
   and the global `p { color: var(--text-gray) }` won — 2.43:1 on the navy footer,
   against 15.73:1 everywhere else. Same statement, same footer, unreadable on one
   page because of a wrapper div. */
.footer-bottom-content p,
.footer-bottom > p {
    color: var(--light-gray);
    margin: 0;
}

.footer-bottom-links {
    display: flex;
    gap: 2rem;
}

.footer-bottom-links a {
    color: var(--light-gray);
    font-size: 0.9rem;
}

.footer-bottom-links a:hover {
    color: var(--gold-accent);
}

/* ===== Compliance / disclaimer blocks ===== */
/* Languages note. Same shape as .compliance-note so the two read as a pair of
   facts about the catalogue, but a distinct accent: this one is a capability, not
   a disclaimer, and styling it identically would make a selling point look like
   small print. Sits ABOVE the compliance note and above every track — see the
   comment at its markup in courses.html for why it is stated exactly once. */
.language-note {
    background: var(--surface-panel);
    border-left: 4px solid var(--accent-ssb, #0a7370);
    padding: 0.85rem 1.25rem;
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    color: var(--text-gray);
    margin: 0 auto 1rem;
    max-width: 920px;
}

.compliance-note {
    background: var(--surface-panel);
    border-left: 4px solid var(--brand-navy);
    padding: 0.85rem 1.25rem;
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    color: var(--text-gray);
    margin: 0 auto 2rem;
    max-width: 920px;
}

.disclaimer-block {
    background: var(--surface-panel);
    border: 1px solid var(--surface-line);
    border-left: 4px solid var(--brand-navy);
    padding: 1rem 1.25rem;
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    color: var(--dark-gray);
    margin: 1.25rem auto;
    max-width: 920px;
    line-height: 1.6;
}

.disclaimer-block strong {
    color: var(--brand-navy);
}

/* A disclaimer that OPENS a section already sits below that section's own
   padding, so its top margin is pure dead space. On contact.html the section
   contributes 80px and this margin another 20px, leaving a 100px hole above the
   first words on the page — reported by site-ux-audit as `hole-in-block`.
   Scoped to :first-child because elsewhere (about.html) the margin is doing
   real separation work between the disclaimer and the paragraph above it. */
.disclaimer-block:first-child {
    margin-top: 0;
}

/* ===== Utility Classes ===== */
.text-center {
    text-align: center;
}

.text-left {
    text-align: left;
}

.text-right {
    text-align: right;
}

.mt-1 { margin-top: 1rem; }
.mt-2 { margin-top: 2rem; }
.mt-3 { margin-top: 3rem; }

.mb-1 { margin-bottom: 1rem; }
.mb-2 { margin-bottom: 2rem; }
.mb-3 { margin-bottom: 3rem; }

.pt-1 { padding-top: 1rem; }
.pt-2 { padding-top: 2rem; }
.pt-3 { padding-top: 3rem; }

.pb-1 { padding-bottom: 1rem; }
.pb-2 { padding-bottom: 2rem; }
.pb-3 { padding-bottom: 3rem; }

/* ===== i18n language switcher (injected into the navbar by js/i18n.js) =====
 * The switcher lives inside .nav-container, which renders on a navy navbar
 * (--brand-navy #002D62) across the public site. The chrome therefore reads
 * white-on-navy; option text inside the native dropdown stays dark so it
 * remains readable on the OS-rendered white menu. */
.lang-switcher {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    margin-right: .75rem;
    color: #ffffff;
}
.lang-switcher .fa-language { font-size: 1.05rem; opacity: .9; }
.lang-switcher select {
    /* The switcher shows each language in its own script — a Hindi speaker sees "हिन्दी",
       not "Hindi" (see i18n.js). On an English page those ~10 Devanagari glyphs were the
       ONLY Devanagari in the DOM, and inheriting the body stack made them pull the whole
       118 KB Noto Sans Devanagari webfont on every single page load. System Indic fonts
       render language names perfectly well, so the switcher opts out of the webfont.
       Hindi CONTENT pages are unaffected — their body text still uses Noto. */
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif;
    font-size: .9rem;
    font-weight: 600;
    color: #ffffff;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.45);
    border-radius: 6px;
    padding: .35rem 1.6rem .35rem .55rem;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23ffffff' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .5rem center;
}
.lang-switcher select option { color: var(--brand-navy); background: #ffffff; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif; }
.lang-switcher select:hover { border-color: #ffffff; }
.lang-switcher select:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 1px; }
@media (max-width: 768px) {
    .lang-switcher { margin: 0 .5rem 0 auto; }
    .lang-switcher .fa-language { display: none; }
}
/* Footer variant — scalable home for the picker in the footer bottom bar. */
.lang-switcher-footer { margin: 0; }
.lang-switcher-footer .fa-language { display: inline-block; }
@media (max-width: 768px) {
    .lang-switcher-footer { margin: .75rem auto 0; }
}

/* ===== First-visit language picker (onboarding) ===== */
.lang-picker-overlay {
    position: fixed;
    inset: 0;
    z-index: 4000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 27, 61, .55);
    backdrop-filter: blur(3px);
    padding: 1.5rem;
}
.lang-picker {
    position: relative;
    background: var(--white);
    border-radius: 16px;
    max-width: 420px;
    width: 100%;
    padding: 2.25rem 2rem;
    text-align: center;
    box-shadow: 0 20px 60px rgba(0, 27, 61, .35);
}
.lang-picker-close {
    position: absolute;
    top: .6rem;
    right: .8rem;
    background: transparent;
    border: none;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--text-gray);
    cursor: pointer;
    padding: .2rem .4rem;
}
.lang-picker-close:hover { color: var(--brand-navy); }
.lang-picker-close:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 2px; }
.lang-picker-icon {
    font-size: 2.4rem;
    color: var(--brand-navy);
    margin-bottom: .75rem;
}
.lang-picker h2 {
    margin: 0 0 .4rem;
    color: var(--brand-navy);
    font-size: 1.4rem;
}
.lang-picker p {
    margin: 0 0 1.5rem;
    color: var(--text-gray);
    font-size: .92rem;
}
.lang-picker-options {
    display: flex;
    flex-direction: column;
    gap: .6rem;
}
.lang-picker-options button {
    font-family: inherit;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--brand-navy);
    background: var(--white);
    border: 1.5px solid var(--surface-line);
    border-radius: 10px;
    padding: .8rem 1rem;
    cursor: pointer;
    transition: border-color .15s, background .15s, color .15s;
}
.lang-picker-options button:hover {
    border-color: var(--brand-navy);
    background: var(--brand-navy);
    color: var(--white);
}


/* Nav overflow guard.
   Adding bn/mr/te/ta to the switcher made the <select> size to its widest native
   option name; with the authenticated nav (account menu + "Signed in as …") the
   row exceeded the viewport and the whole page scrolled sideways by 69px. Let the
   nav wrap rather than overflow, and stop the select growing without bound. */
.navbar .nav-container { flex-wrap: wrap; row-gap: 8px; }
.lang-switcher select { max-width: 7.5rem; }

/* Footer ink.
   .footer sits on --brand-navy-dark, but `a { color: inherit }` means any footer
   link inheriting a MUTED DARK token (--text-gray) renders dark-on-dark — the
   Privacy/Terms links measured 2.43:1. Darkening --text-gray for AA on white made
   this instance worse, which is the lesson: a colour token is only "accessible"
   relative to the background it lands on. Pin footer links to light ink. */
.footer a { color: var(--surface-line-soft); }
.footer a:hover { color: var(--brand-sky-light); }

/* ===== Journey band — the certificates as a progression =====
   Built 2026-08-13. Uses the family accent tokens so A/B/C read as the same
   families they do on the courses page; the accents are the AA-safe --accent-*
   values because the roundel carries a white letter. */
.journey-band {
    background: var(--surface-panel, #f5f7fa);
    border-block: 1px solid var(--surface-line, #dde4ee);
}

.journey-steps {
    list-style: none;
    margin: 0 0 var(--rhythm-block, 28px);
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
    gap: var(--rhythm-tight, 18px);
}

.journey-step {
    position: relative;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 20px;
    background: var(--surface-card, #fff);
    border: 1px solid var(--surface-line, #dde4ee);
    border-radius: 10px;
}

/* The arrow between steps. Decorative, so it is hidden from assistive tech by
   being CSS content — the <ol> already conveys order. Suppressed on the last
   step and whenever the grid wraps to one column, where a right-pointing arrow
   would point at nothing. */
.journey-step::after {
    content: "\f061"; /* fa-arrow-right */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    position: absolute;
    right: -14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--surface-line, #dde4ee);
    font-size: 1rem;
    z-index: 1;
}

.journey-step:last-child::after { content: none; }

.journey-step-mark {
    flex: 0 0 auto;
    width: 46px;
    height: 46px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    color: #fff;
    font-family: var(--font-heading, sans-serif);
    font-size: 1.25rem;
    font-weight: 800;
}

.journey-step-a .journey-step-mark { background: var(--accent-cert-a, #2f6fd0); }
.journey-step-b .journey-step-mark { background: var(--accent-cert-b, #157a53); }
.journey-step-c .journey-step-mark { background: var(--accent-cert-c, #9a5c08); }

.journey-step-body { display: flex; flex-direction: column; gap: 2px; }

.journey-step-name {
    font-weight: 700;
    color: var(--brand-navy, #002D62);
}

.journey-step-who {
    font-size: 0.88rem;
    line-height: 1.4;
    color: var(--surface-ink-muted, #5a6b7d);
}

/* SSB sits OUTSIDE the chain on purpose — see the comment in index.html. Its own
   block, its own accent, no arrow leading into it. */
.journey-aside {
    padding: 18px 20px;
    background: var(--surface-card, #fff);
    border: 1px solid var(--surface-line, #dde4ee);
    border-left: 4px solid var(--accent-ssb, #0a7370);
    border-radius: 10px;
}

.journey-aside-kicker {
    margin: 0 0 6px;
    font-size: 0.78rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--accent-ssb, #0a7370);
}

.journey-aside-body {
    margin: 0 0 10px;
    font-size: 0.94rem;
    line-height: 1.55;
    color: var(--surface-ink-muted, #5a6b7d);
}

.journey-aside-link {
    font-weight: 700;
    color: var(--accent-ssb, #0a7370);
    text-decoration: none;
}

.journey-aside-link:hover,
.journey-aside-link:focus-visible { text-decoration: underline; }

/* Devanagari separates from its own matras under tracking, and has no letter case. */
.journey-band:lang(hi) .journey-aside-kicker,
.journey-band:lang(mr) .journey-aside-kicker {
    letter-spacing: normal;
    text-transform: none;
}

@media (max-width: 780px) {
    /* One column: the arrow would point sideways into empty space. */
    .journey-step::after { content: none; }
}

/* ===== Tri-lingual body copy (EN + हिन्दी + reader's language) =====
   Owner decision 2026-08-14: on the static/legal pages the English is never
   replaced by a machine translation — the translations are added beneath it so
   a reader can compare a clause with the original on the same screen. See
   frontend/js/tri-lingual.js for the reasoning. */
.tri-notice {
    background: var(--notice-bg);
    border: 1px solid var(--notice-line);
    border-left: 4px solid var(--warning);
    border-radius: var(--radius-sm);
    padding: 1rem 1.25rem;
    margin: 0 auto 1.5rem;
    max-width: 920px;
    font-size: 0.93rem;
    line-height: 1.65;
    color: var(--notice-ink);
}

.tri-notice-head {
    font-weight: 700;
    color: var(--notice-ink-strong);
    margin: 0 0 0.4rem;
}

/* Same inheritance trap as the instructor band: `p { color: var(--text-gray) }`
   was overriding the notice ink this block inherits, so the body rendered at
   6.66:1 instead of the 9.76:1 documented for --notice-ink. Legible either way,
   but not the colour the token promises. */
.tri-notice p { margin: 0; color: var(--notice-ink); }

.tri-block {
    border-left: 3px solid var(--surface-line);
    padding: 0.35rem 0 0.35rem 0.85rem;
    margin: 0.5rem 0 0.9rem;
}

.tri-tag {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--surface-ink-muted);
    margin-bottom: 0.2rem;
}

.tri-text {
    color: var(--text-gray);
    line-height: 1.75;
}

.tri-block[dir="rtl"] {
    border-left: 0;
    border-right: 3px solid var(--surface-line);
    padding-left: 0;
    padding-right: 0.85rem;
    text-align: right;
}

.tri-notice-alt {
    margin-top: 0.75rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--notice-line);
}

.tri-notice-alt[dir="rtl"] { text-align: right; }

/* The free-mock line under the identity router. Deliberately quieter than a
   tile: it is a secondary path, and the sentence has to carry the "needs an
   account" caveat that would not fit on a tile. */
.identity-aside {
    margin: 1rem 0 0;
    font-size: 0.92rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.86);
}

.identity-aside a {
    color: #fff;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Two-offer aside: the no-account preview leads, the mock follows.
   Each line is a block so the caveat chip stays with the offer it describes —
   the same reason the mock's caveat was originally kept inside its sentence. */
.identity-aside-stacked .identity-aside-line {
    display: block;
    margin-top: 0.45rem;
}

.identity-aside-lead {
    font-weight: 600;
    color: var(--white);
}

/* The account requirement as a label rather than a clause, so it survives a skim.
   nowrap keeps "Account needed" intact; the longest translation is Bengali at 21
   characters, which fits — if a future locale is longer, let the chip fall to its
   own line rather than shrinking the type. */
.chip {
    display: inline-block;
    font-size: 0.64rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 1.5px 6px;
    border-radius: 3px;
    margin-left: 5px;
    vertical-align: 1.5px;
    white-space: nowrap;
}

.chip-free {
    background: var(--brand-sky-light);
    color: var(--brand-navy);
}

.chip-acct {
    background: rgba(255, 255, 255, 0.22);
    color: var(--white);
    border: 1px solid rgba(255, 255, 255, 0.25);
}

.identity-aside a:hover { color: var(--brand-sky-light); }

/* ===== Identity tiles: per-audience accent =====
   A 3px rail in the audience's own colour, matching the course catalogue, plus
   a slightly stronger icon. Without a photograph these five tiles are the only
   colour above the fold, so they carry it. The rail is decorative — the label
   still says which audience it is, so nothing depends on colour alone. */
.identity-tile::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--tile-accent, var(--brand-sky));
}

.identity-tile i {
    font-size: 1.35rem;
    color: var(--tile-accent, var(--brand-sky-light));
}

.identity-tile-a      { --tile-accent: var(--on-navy-cert-a); }   /* A certificate  */
.identity-tile-b      { --tile-accent: var(--on-navy-cert-b); }   /* B / C          */
.identity-tile-ssb    { --tile-accent: var(--on-navy-ssb); }   /* SSB            */
.identity-tile-inst   { --tile-accent: var(--on-navy-inst); }   /* ANO / GCI      */
.identity-tile-school { --tile-accent: var(--on-navy-school); }   /* schools        */
.identity-tile-agniveer { --tile-accent: var(--on-navy-agniveer); } /* Agniveer     */

.identity-tile:hover::before { height: 4px; }

/* One tile per line below this: five stacked full-width blocks read as a list,
   which is the right shape on a phone — the visitor scans for themselves. */
@media (max-width: 560px) {
    .identity-tile {
        flex: 1 1 100%;
        max-width: none;
        flex-direction: row;
        justify-content: flex-start;
        text-align: left;
        gap: 0.75rem;
        padding: 0.9rem 1rem;
    }
    .identity-tile i { font-size: 1.1rem; width: 1.5rem; text-align: center; }
}

/* ===== Photo-free hero on a phone =====
   The point of this hero is the router: the visitor's first question is "which
   of these am I?". At 390px the full-size headline plus the four-line subtitle
   pushed all five tiles below the fold and only ONE was visible — measured — so
   the hero was asking the question and hiding the answers. Tighten the type and
   the rhythm until the choices are on screen with the headline. */
@media (max-width: 560px) {
    .hero-split-solo .hero-title {
        font-size: 1.62rem;
        line-height: 1.2;
        max-width: 18ch;
        margin-bottom: 0.7rem;
    }

    .hero-split-solo .hero-subtitle {
        font-size: 0.94rem;
        line-height: 1.55;
        margin-bottom: 1.1rem;
    }

    .hero-content { padding-top: 2rem; }

    .hero-split-solo .hero-buttons { gap: 0.6rem; }

    .hero-content .identity-router {
        margin-top: 1.4rem;
        gap: 0.55rem;
    }
}

/* ===== Instructor band =====
   Sits directly under the hero. Cadets and instructors are different buyers at
   very different prices (₹199 vs ₹1,999); the hero speaks to cadets and this
   gives instructors a route that carries the price and the length, which a tile
   among five could not. Crimson because that is already the instructor family's
   colour in the course catalogue. */
.instructor-band {
    background: linear-gradient(120deg, var(--brand-crimson-dark), var(--brand-crimson));
    color: var(--white);
}

.instructor-band-inner {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding-top: 0.85rem;
    padding-bottom: 0.85rem;
    flex-wrap: wrap;
}

.instructor-band-mark {
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: rgba(255, 255, 255, 0.18);
    border: 1px solid rgba(255, 255, 255, 0.32);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}

.instructor-band-copy {
    /* Set explicitly, do NOT rely on inheriting from .instructor-band. The global
       `p { color: var(--text-gray) }` is a DIRECT rule on this element, and a direct
       rule beats an inherited one no matter how specific the ancestor's selector is.
       Without this line "Teachers & instructors:" rendered in #4a5a6e on crimson —
       1.50:1, effectively unreadable — while looking fine in the stylesheet. */
    color: var(--white);
    margin: 0;
    font-size: 0.93rem;
    line-height: 1.5;
    min-width: 0;
}

.instructor-band-copy strong { font-weight: 700; }

.instructor-band-copy span { color: var(--on-crimson-muted); }

.instructor-band-cta {
    margin-left: auto;
    background: var(--white);
    color: var(--brand-crimson-dark);
    font-size: 0.83rem;
    font-weight: 700;
    padding: 0.5rem 0.95rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    white-space: nowrap;
    flex: none;
}

.instructor-band-cta:hover { background: var(--brand-sky-light); color: var(--brand-navy-dark); }

/* On a phone the auto margin would strand the button; stack instead so the
   whole band stays one readable unit and the button is a full-width target. */
@media (max-width: 620px) {
    .instructor-band-inner { gap: 0.7rem; }
    .instructor-band-copy { flex: 1 1 100%; font-size: 0.88rem; }
    .instructor-band-cta { margin-left: 0; flex: 1 1 100%; text-align: center; }
}

/* Trust chips: quiet, factual, and sized so they never compete with the CTAs. */
.hero-chips {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    margin: 0.85rem 0 0;
    padding: 0;
}

.hero-chips li {
    font-size: 0.76rem;
    font-weight: 600;
    /* an existing token rather than a new near-white: 8.23:1 on the hero navy,
       comfortably above AA, and one less colour to keep in the palette. */
    color: var(--brand-sky-light);
    background: rgba(255, 255, 255, 0.09);
    border: 1px solid rgba(255, 255, 255, 0.18);
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
}

/* Reclaim the last of the vertical space so the instructor band clears the fold
   on a 1440x900 laptop — measured at 32px short before these trims. */
.hero-split-solo .hero-title { margin-bottom: 1rem; }
.hero-split-solo .hero-subtitle { margin-bottom: 1.35rem; }
.identity-aside { margin-top: 0.8rem; }


/* ── Programme catalogue ────────────────────────────────────────────────────
   The course list at the foot of each programme landing page (see
   frontend/js/programme-catalogue.js). Lives here rather than being injected
   from JS: a stylesheet injected at runtime is what produced the whole-page
   fadeIn that took three attempts to find. */
.pcat-section {
    /* Its own section, deliberately NOT inside .cta. That block carries
       `.cta .cta-content p { color: rgba(255,255,255,.9) !important }`, which
       forces every <p> inside it white — on a white card that is 1:1, and it
       shipped to production before this was caught. The audit found 15 of them.
       No amount of ordinary specificity beats !important, so the cards live
       outside the band rather than fighting it. */
    /* Top padding is NOT zero. The band above ("See what is available today")
       ends on a dark gradient and the first card began immediately against it,
       with no breathing room -- the owner's word was "awkward", and it was.
       The band owns no bottom space of its own, so this section owns the gap. */
    padding: 52px 0 60px;
    background: var(--white);
}
.pcat { margin-top: 0; }

.pcat-grid {
    /* auto-fit + a MAX track width, so the row adapts to how many courses a
       programme actually has. A fixed 4-column grid left the SSB page with one
       271px card marooned in 1136px of empty space; with this, one card is a
       centred 300px, thirteen wrap four-up. */
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 300px));
    justify-content: center;
    gap: 18px;
    text-align: left;
}

.pcat-card {
    background: var(--white);
    border: 1px solid var(--surface-line, #dde4ee);
    border-top: 3px solid var(--pcat-accent, var(--brand-navy));
    border-radius: 12px;
    padding: 18px;
    display: flex;
    flex-direction: column;
}
.pcat-a    { --pcat-accent: var(--accent-cert-a); }
.pcat-b    { --pcat-accent: var(--accent-cert-b); }
.pcat-c    { --pcat-accent: var(--accent-cert-c); }
.pcat-ssb  { --pcat-accent: var(--accent-ssb); }
.pcat-free { --pcat-accent: var(--accent-cert-c); }

.pcat-title {
    font-size: 1.02rem;
    line-height: 1.3;
    margin: 0 0 6px;
    /* Set explicitly. The global `h1..h6 { color: var(--ncc-green) }` is a DIRECT
       rule and would otherwise win over anything inherited from the card. */
    color: var(--brand-navy);
}
.pcat-desc {
    font-size: .86rem;
    color: var(--text-gray);
    margin: 0 0 14px;
    flex: 1;
}
.pcat-price {
    font-size: 1.12rem;
    font-weight: 800;
    margin: 0 0 12px;
    color: var(--brand-navy);
    font-variant-numeric: tabular-nums;
}
.pcat-price small { font-size: .72rem; font-weight: 500; color: var(--text-gray); }
.pcat-free-price { color: var(--success-ink); }

.pcat-cta {
    display: block;
    text-align: center;
    background: var(--brand-navy);
    color: var(--white);
    border-radius: 8px;
    padding: 11px 14px;
    font-weight: 600;
    font-size: .9rem;
    text-decoration: none;
}
.pcat-cta:hover, .pcat-cta:focus { background: var(--brand-navy-dark); color: var(--white); }
.pcat-det {
    display: block;
    text-align: center;
    margin-top: 10px;
    font-size: .84rem;
    color: var(--text-gray);
}
.pcat-empty {
    background: var(--white);
    border: 1px solid var(--surface-line, #dde4ee);
    border-radius: 12px;
    padding: 22px;
    margin: 0;
    color: var(--text-gray);
    font-size: .93rem;
}


/* ── Printing the course list ───────────────────────────────────────────────
   responsive.css hides .hero, .cta and .footer when printing. The cards used to
   live inside .cta, so printing a programme page silently dropped the entire
   catalogue — which is why only the first two pages ever came out. They now sit
   in .pcat-section, which prints; these rules make that deliberate rather than
   accidental. */
@media print {
    .pcat-section { padding: 0; background: none; }
    .pcat-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 10px;
    }
    .pcat-card {
        /* never split a course across a page boundary */
        break-inside: avoid;
        page-break-inside: avoid;
        border: 1px solid #999;
        padding: 10px;
    }
    .pcat-title { font-size: 11pt; }
    .pcat-desc  { font-size: 9pt; }
    .pcat-price { font-size: 11pt; }
    /* a button is meaningless on paper; the price and the name are what matter */
    .pcat-cta, .pcat-det { display: none; }
}


/* ---- Programme landing pages: the six-card explainer ---------------------
   At 2 columns those six cards ran 1355px -- more than a full screen of
   explainer standing between the visitor and the courses the page exists to
   sell. Three columns makes it two clean rows and roughly halves the height.

   A MODIFIER, not a change to .features-grid: the homepage uses the same grid
   for a FOUR-card group, and three columns there would strand the fourth on its
   own row -- the marooned-card problem this codebase has already hit twice.
   Applied only where the grid genuinely holds six. */
@media (min-width: 1000px) {
    .features-grid-compact { grid-template-columns: repeat(3, 1fr); max-width: 1140px; }
}
.features-grid-compact { gap: 1.25rem; }
.features-grid-compact .feature-card { padding: 1.5rem 1.25rem; }
.features-grid-compact .feature-card h3 { font-size: 1.05rem; }
.features-grid-compact .feature-card p { font-size: .92rem; }
/* The hover lift is 10px elsewhere; at this density it reads as jitter. */
.features-grid-compact .feature-card:hover { transform: translateY(-4px); }


/* ---- Programme landing pages: get to the courses sooner -----------------
   Measured, not guessed. On the NCC page a visitor had to scroll 2.8 SCREENS
   before the first price appeared. The section stack was hero 600 + features
   960 + band 200 before the catalogue even started, and every one of those
   carried 80px of padding top AND bottom.

   These pages exist to sell the courses at the bottom of them, so the explainer
   above is trimmed rather than the catalogue. Scoped to .programme-page so the
   homepage and For Schools keep their generous rhythm. */
.programme-page .features { padding-top: 48px; padding-bottom: 48px; }
.programme-page .hero {
    padding-top: 56px; padding-bottom: 56px;
    /* min-height was 600px while the content measured 350 -- 250px of empty navy
       above the fold on a page whose job is the catalogue underneath. Content
       decides the height here; the homepage hero keeps its 600. */
    min-height: 0;
}
.programme-page .section-header { margin-bottom: 1.75rem; }


/* The B2B cross-link strip on programme landing pages. Quiet by design: it is a
   signpost for a different buyer, not a competing call to action for the cadet
   the page is written for. */
.schools-crosslink { background: var(--surface-panel); padding: 18px 0; }
.schools-crosslink p { margin: 0; text-align: center; font-size: .93rem; color: var(--text-gray); }
.schools-crosslink i { color: var(--brand-navy); margin-right: 6px; }


/* ---- Homepage track tiles: aligned prices, one accent per track ----------
   PRICES WERE UNEVEN. Measured on the live page: the price sat at 372px on the
   NCC tile, 397 on SSB and 426 on Instructor -- a 54px stagger -- because the
   titles run 2 or 3 lines and the blurbs 2 or 3. The BUTTONS were already
   aligned (all at 473), which is why only the prices looked wrong.

   Fixed by reserving the tallest case for the title and blurb rather than by
   flex tricks: one tile has no price at all (Agniveer), and a `margin-top:auto`
   on the price would have left that tile's button floating on its own. */
#liveCoursesGrid .course-content h3 { min-height: calc(3 * 1.3em); margin-bottom: .35rem; }
#liveCoursesGrid .course-content > p { min-height: calc(3 * 1.6em); }
#liveCoursesGrid .course-price { margin-bottom: .25rem; }

/* One accent per track, matching the section each tile leads to. The values are
   the SAME tokens the catalogue uses for these four things, so this is
   consistency rather than decoration -- no new palette entries, nothing for the
   colour ratchet to flag. */
.track-tile-ncc        .course-image-track { background: linear-gradient(150deg, var(--accent-cert-c), var(--brand-navy)); }
.track-tile-ssb        .course-image-track { background: linear-gradient(150deg, var(--accent-ssb),    var(--brand-navy)); }
.track-tile-instructor .course-image-track { background: linear-gradient(150deg, var(--accent-inst),   var(--brand-navy)); }
/* Olive, not the "not yet built" grey. AGV-D-061 made field olive the Agniveer
   accent on 2026-09-06, but that commit touched only catalogue.css and tokens.css —
   these two rules were last edited 2026-08-19, three weeks BEFORE the decision, so
   the homepage and the courses page never got it. Measured live in a browser before
   the fix: this tile rendered rgb(91,107,133) = --accent-soon, which assessment.css
   calls "the palette's not-available grey" and catalogue.css:81 reserves for tracks
   with nothing to sell — on a card reading "9 COURSES · from ₹499".
   ⚠️ check:catalogue exists to catch a design applied to one surface and not another,
   and stayed green throughout: it checks markup OWNERSHIP, not accent tokens. */
.track-tile-agniveer   .course-image-track { background: linear-gradient(150deg, var(--accent-agniveer), var(--brand-navy)); }

/* A quiet top edge in the same accent, so the colour reads as belonging to the
   whole card and not just its thumbnail. */
.track-tile-ncc        { border-top: 4px solid var(--accent-cert-c); }
.track-tile-ssb        { border-top: 4px solid var(--accent-ssb); }
.track-tile-instructor { border-top: 4px solid var(--accent-inst); }
.track-tile-agniveer   { border-top: 4px solid var(--accent-agniveer); }

/* Skip-to-content link (a11y). Off-screen until focused, then pinned to the
   inline start. Not display:none — that would remove it from the tab order,
   which defeats it.

   🔴 THIS USED `left: -9999px` AND IT MADE THE ENTIRE SITE BLANK IN URDU FOR
   FIVE DAYS. In an RTL document a negative `left` does not move the element
   off-screen — it EXTENDS the initial containing block, so scrollWidth became
   11,439px and the visual viewport sat at x=0, about 10,000px of empty canvas
   away from the content. `body { overflow-x: hidden }` meant the reader could
   not scroll to it either: every page rendered as flat navy. The DOM was
   correct the whole time — right script, dir=rtl, zero console errors — so
   every gate stayed green. `ur` entered the picker in d73b2a1368 on
   2026-09-21 and nothing caught it until a rendered walk.

   The clip pattern below is direction-agnostic: it occupies a 1px box and is
   clipped away, so it cannot extend the containing block in either direction.
   If you change this, TEST IT IN AN RTL LANGUAGE, not just in English. */
.skip-to-content {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
    top: 0;
    inset-inline-start: 0;
    z-index: 10000;
    background: var(--brand-navy);
    color: #fff;
    font-weight: 600;
    text-decoration: none;
    border-radius: 0 0 4px 0;
}
.skip-to-content:focus {
    width: auto;
    height: auto;
    margin: 0;
    padding: 0.75rem 1.25rem;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
    outline: 3px solid var(--accent-cert-c-vivid);
    outline-offset: 2px;
}

/* ── Feedback prompt ────────────────────────────────────────────────────────
   One question, asked once, at a moment someone just finished something.
   Appended BELOW the content and never over it: this must never behave like a
   modal, and it must never cover a timed screen. Quiet enough to ignore, clear
   enough to answer. */
.fbp {
    margin: 28px 0 8px;
    padding: 16px 18px 14px;
    background: var(--surface-panel, #f5f7fa);
    border: 1px solid var(--surface-line, #dde4ee);
    border-left: 3px solid var(--brand-navy, #002D62);
    border-radius: 10px;
    max-width: 560px;
}
.fbp-q { margin: 0 0 12px; font-weight: 600; color: var(--brand-navy, #002D62); font-size: 1rem; }
.fbp-opts { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.fbp-opt {
    flex: 1 1 auto; min-width: 92px;
    padding: 9px 12px; font: inherit; font-weight: 600; font-size: .92rem;
    background: #fff; color: var(--brand-navy, #002D62);
    border: 1px solid var(--surface-line, #dde4ee); border-radius: 8px; cursor: pointer;
}
.fbp-opt:hover { border-color: var(--brand-navy, #002D62); }
.fbp-opt.is-on { background: var(--brand-navy, #002D62); color: #fff; border-color: var(--brand-navy, #002D62); }
.fbp-opt:focus-visible, .fbp-skip:focus-visible, .fbp-send:focus-visible,
.fbp-note:focus-visible { outline: 2px solid var(--brand-navy, #002D62); outline-offset: 2px; }
.fbp-note {
    width: 100%; padding: 9px 11px; font: inherit; font-size: .92rem;
    border: 1px solid var(--surface-line, #dde4ee); border-radius: 8px;
    /* Tokens, not near-miss literals. The value here was one shade off the palette,
       which is exactly the divergence check:brand-visual exists to stop — and the
       gate counts hex in COMMENTS too, so this note deliberately names none. */
    resize: vertical; background: var(--white, #fff); color: var(--text-gray, #5a6b7d);
}
.fbp-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; }
.fbp-skip { background: none; border: 0; font: inherit; font-size: .9rem; color: #5b6b7f; cursor: pointer; padding: 6px 2px; text-decoration: underline; }
.fbp-send { font: inherit; font-weight: 600; font-size: .92rem; background: var(--brand-navy, #002D62); color: #fff; border: 0; border-radius: 8px; padding: 9px 20px; cursor: pointer; }
.fbp-thanks { margin: 0; padding: 4px 0; color: #157a53; font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .fbp { transition: none; } }

/* ── Trial track selection ──────────────────────────────────────────────────
   A trial covers ONE course. These styles carry two states: choosable (before a
   pick) and chosen/other (after).

   is-trial-other is FADED, never disabled. Those courses remain fully
   purchasable — a trial member is exactly the person who might buy a second
   one, and hardening "not in your trial" into "you cannot buy this" would block
   the sale the pilot exists to earn. */
.trial-pick-banner {
    margin: 0 0 20px;
    padding: 14px 18px;
    border-radius: 10px;
    background: var(--surface-panel, #f5f7fa);
    border: 1px solid var(--surface-line, #dde4ee);
    border-left: 3px solid var(--brand-navy, #002D62);
    font-size: .96rem;
    line-height: 1.5;
}
.trial-pick-banner strong { color: var(--brand-navy, #002D62); }
.trial-pick-banner.is-chosen { border-left-color: #157a53; }

.course-card.is-trial-chosen {
    border: 2px solid #157a53;
    box-shadow: 0 2px 10px rgba(21, 122, 83, .12);
}
.course-card.is-trial-other { opacity: .62; }
.course-card.is-trial-other:hover,
.course-card.is-trial-other:focus-within { opacity: 1; }


/* Shown only when some blocks on a legal page have no translation in a language on
   screen. Set apart from the rest of the notice because it is the sentence that stops
   an untranslated clause reading as a broken page. */
.tri-notice-partial {
    margin-top: 8px;
    padding-left: 10px;
    border-left: 3px solid #c98a00;
    color: #6a4500;
}


/* The governing-language statement on a legal page that carries NO translation at all.
   No left rule: it is the whole notice here, not an aside within a larger one. */
.tri-notice-english-only {
    margin: 0;
}

/* ── English master panel (bilingual serve, owner-approved 2026-09-29) ───────────────
   Closed on ordinary lessons so the chosen language reads without interruption; open on
   safety-critical subjects, decided server-side by backend/lib/englishPanelDefault.js.
   Styled as a quiet aside, NOT as a second lesson: it is a reference the cadet consults,
   so it must not compete with the body it sits under. */
.english-master {
    margin: 2rem 0 0;
    border: 1px solid var(--surface-line);
    border-left: 3px solid var(--brand-navy);
    border-radius: 4px;
    background: var(--surface-panel);
    padding: 0.5rem 0.9rem;
}
.english-master-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    cursor: pointer;
    list-style: none;
    padding-block: 0.25rem;
}
.english-master-summary::-webkit-details-marker { display: none; }
.english-master-summary::marker { content: ''; }
.english-master-summary:focus-visible { outline: 2px solid var(--brand-navy); outline-offset: 2px; }
.english-master-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--brand-navy);
}
.english-master-cue {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--surface-ink-muted);
    white-space: nowrap;
}
.english-master-cue::after { content: 'Show +'; }
.english-master[open] .english-master-cue::after { content: 'Hide \2212'; }
.english-master[open] .english-master-summary {
    margin-bottom: 0.6rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--surface-line);
}
.english-master-note {
    margin: 0 0 1rem;
    font-size: 0.85rem;
    color: var(--surface-ink-muted);
}
/* 🔴 THE PANEL RENDERS THROUGH THE LESSON'S OWN RENDERER, SO WITHOUT THIS IT IS A SECOND
   LESSON, NOT A REFERENCE. Caught by walking the rendered page at 390px and 1280px on
   2026-09-29: the English came out at full body weight, with the same section cards and the
   same bold runs as the lesson above it, so the eye had no way to tell which one it was
   reading. Everything below steps the panel's interior down to reference weight. It is scoped
   to .english-master, so the lesson itself is untouched. */
.english-master :is(p, li, td, th) { font-size: 0.8125rem; line-height: 1.6; }
.english-master :is(h1, h2, h3, h4, h5) { font-size: 0.9375rem; margin: 1rem 0 0.35rem; }
.english-master strong { font-weight: 600; }
.english-master img { max-width: 100%; }
/* ⚠️ AN EARLIER VERSION OF THIS BLOCK ALSO STRIPPED THE SECTION CARDS' CHROME AND ZEROED THEIR
   HORIZONTAL PADDING. Walking the page showed why that was wrong: with the padding gone the text
   ran under the card's own left rule and the FIRST CHARACTER OF EVERY LINE WAS CLIPPED. The type
   step-down above already makes the panel read as a reference; the cards inside it are legible
   and subordinate at that size, so their chrome stays. Do not re-add a padding override here
   without looking at the rendered page at 390px and 1280px. */
@media (max-width: 480px) {
    .english-master { padding-inline: 0.7rem; }
}
