/* ==========================================================================
   ClassTrack 2027 — marketing site
   Scope: every rule lives under .ct2027
   Order: tokens → base → chrome → components → sections → responsive
   ========================================================================== */

/* @tokens:start */
:root[data-ct-theme="dark"] {
  --ct-bg:            #020617;
  --ct-bg-alt:        #0a1628;
  --ct-surface:       rgba(255,255,255,.045);
  --ct-surface-2:     rgba(255,255,255,.07);
  --ct-line:          rgba(255,255,255,.09);
  --ct-line-strong:   rgba(255,255,255,.16);
  --ct-text:          #ffffff;
  --ct-text-mut:      #94a3b8;
  --ct-text-dim:      #7b8da5;
  --ct-cyan:          #22d3ee;
  --ct-blue:          #3b82f6;
  --ct-purple:        #a855f7;
  --ct-maple:         #e11d2a;
  --ct-maple-soft:    rgba(225,29,42,.38);
  --ct-warn:          #fbbf24;
  /* status-only red. NOT --ct-maple: maple is the rationed brand accent
     (hero eyebrow + final CTA). A status pill must never spend that budget. */
  --ct-danger:        #f87171;
  --ct-ok:            #34d399;
  --ct-on-accent:     #ffffff;
  --ct-shadow:        0 40px 80px -30px rgba(0,0,0,.9);
  --ct-shadow-cta:    0 14px 34px -10px rgba(6,182,212,.65);
  --ct-glow:          rgba(6,182,212,.09);
  --ct-wash-1:        rgba(6,182,212,.18);
  --ct-wash-2:        rgba(168,85,247,.16);
  --ct-wash-3:        rgba(225,29,42,.10);
}

:root[data-ct-theme="light"] {
  --ct-bg:            #f6f8fc;
  --ct-bg-alt:        #ffffff;
  --ct-surface:       #ffffff;
  --ct-surface-2:     #eef3fb;
  --ct-line:          rgba(15,23,42,.10);
  --ct-line-strong:   rgba(15,23,42,.18);
  --ct-text:          #0b1220;
  --ct-text-mut:      #475569;
  --ct-text-dim:      #5b6b80;
  --ct-cyan:          #0891b2;
  --ct-blue:          #2563eb;
  --ct-purple:        #7c3aed;
  --ct-maple:         #c81326;
  --ct-maple-soft:    rgba(200,19,38,.30);
  --ct-warn:          #b45309;
  --ct-danger:        #b91c1c;
  --ct-ok:            #047857;
  --ct-on-accent:     #ffffff;
  --ct-shadow:        0 30px 60px -30px rgba(15,23,42,.28);
  --ct-shadow-cta:    0 14px 34px -12px rgba(37,99,235,.45);
  --ct-glow:          rgba(8,145,178,.10);
  --ct-wash-1:        rgba(8,145,178,.10);
  --ct-wash-2:        rgba(124,58,237,.08);
  --ct-wash-3:        rgba(200,19,38,.06);
}

:root {
  --ct-r-sm: 9px;  --ct-r-md: 12px;  --ct-r-lg: 16px;  --ct-r-xl: 22px;
  --ct-blur: 14px;
  --ct-max: 1200px;
  --ct-grad: linear-gradient(120deg, var(--ct-cyan) 0%, var(--ct-blue) 45%, var(--ct-purple) 100%);
  --ct-grad-maple: linear-gradient(120deg, #f87171 0%, var(--ct-maple) 55%, #fb7185 100%);

  --ct-nav-h: 112px;

  --ct-dot-r: #ff5f57;
  --ct-dot-y: #febc2e;
  --ct-dot-g: #28c840;

  /* Video-letterbox background for the clip-modal iframe frame before the
     player paints. Theme-invariant like the dot colours above — chrome, not
     a brand accent — so it lives here rather than duplicated per-theme. */
  --ct-media-bg: #000;

  /* Frame colour for the 53 light-background gallery deck JPGs (Task 11,
     /gallery). Theme-invariant like --ct-media-bg above: these are
     screenshots of a real light-UI artifact, not a themed brand surface,
     so the frame stays white in both themes. The one permitted raw-hex
     exception to the no-raw-hex-outside-tokens rule, per this task's
     brief. */
  --ct-artifact-bg: #ffffff;
}
/* @tokens:end */

/* ----- base --------------------------------------------------------------- */
.ct2027 {
  background: var(--ct-bg);
  color: var(--ct-text);
  font-family: Inter, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
/* Ambient wash on a FIXED composited pseudo-element.
   Never background-attachment:fixed on the body — that repaints the whole
   viewport every scroll frame and was half the July 2026 jank. */
.ct2027::before {
  content: "";
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(ellipse at 78% 0%,   var(--ct-wash-1) 0%, transparent 55%),
    radial-gradient(ellipse at 4% 96%,   var(--ct-wash-3) 0%, transparent 52%),
    radial-gradient(ellipse at 0% 100%,  var(--ct-wash-2) 0%, transparent 55%);
  will-change: transform;
}
/* The fixed nav is taken out of flow, so #ct-main has to reserve its height.
   That height is now ONE token, --ct-nav-h, re-declared per breakpoint next to
   the nav rules that change it — not a magic number here and a second magic
   number in a media query. The pair used to drift: 108px/70px of main padding
   stacked on top of a 150px !important first-section padding produced ~220px
   of dead space above the first heading at 390px.

   The token carries a small deliberate surplus over the measured bar
   (112 vs 104.75 desktop, 72 vs 63 mobile). Reserving the exact height leaves
   a hairline: any sub-pixel shift in the font metrics or the action buttons
   and the first section tucks under the fixed bar. */
.ct2027 #ct-main { position: relative; z-index: 1; padding-top: var(--ct-nav-h); }
.ct2027 .ct-container { max-width: var(--ct-max); margin: 0 auto; padding: 0 24px; }

/* Headings: the legacy bundle ships a global
   `h1..h6 { font-family: Jost; text-transform: capitalize; color: #192a31 }`
   at (0,0,1). Every 2027 heading must out-specify it or it renders Title
   Cased, in the wrong face, in a colour that is invisible on the dark theme.
   Normalising once here means Tasks 5-12 inherit the fix. */
.ct2027 h1, .ct2027 h2, .ct2027 h3, .ct2027 h4, .ct2027 h5, .ct2027 h6 {
  font-family: inherit;
  text-transform: none;
  color: var(--ct-text);
  letter-spacing: -0.02em;
}
.ct2027 .ct-lede {
  font-size: 17.5px; line-height: 1.72; color: var(--ct-text-mut);
  max-width: 46ch; margin: 0 0 32px;
}

/* ----- chrome --------------------------------------------------------------- */
.ct2027 .ct-nav__theme {
  width: 38px; height: 38px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  color: var(--ct-text);
  cursor: pointer;
  transition: border-color .2s ease, background .2s ease;
}
.ct2027 .ct-nav__theme:hover { border-color: var(--ct-cyan); background: var(--ct-surface-2); }

/* nav shell — fixed, glass. `.is-scrolled` (toggled by initNav() past 40px)
   raises opacity + adds a lifted shadow so the bar stays legible once the
   ambient wash and hero content scroll underneath it. */
.ct2027 .ct-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  background: var(--ct-surface);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
  border-bottom: 1px solid var(--ct-line);
  transition: background .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-nav.is-scrolled {
  background: var(--ct-surface-2);
  border-bottom-color: var(--ct-line-strong);
  box-shadow: var(--ct-shadow);
}

.ct2027 .ct-nav__topbar {
  background: var(--ct-bg-alt);
  border-bottom: 1px solid var(--ct-line);
  text-align: center;
  padding: 8px 16px;
  font-size: 12.5px;
  color: var(--ct-text-mut);
}
.ct2027 .ct-nav__topbar a { color: var(--ct-text-mut); text-decoration: none; }
.ct2027 .ct-nav__topbar a:hover { color: var(--ct-text); }
.ct2027 .ct-nav__topbar strong { color: var(--ct-text); font-weight: 700; }
.ct2027 .ct-nav__topbar i { color: var(--ct-cyan); margin-right: 6px; }

.ct2027 .ct-nav__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding: 14px 24px;
}
.ct2027 .ct-nav__brand { display: inline-flex; align-items: center; line-height: 0; }
.ct2027 .ct-nav__brand img { display: block; height: 32px; width: auto; }

.ct2027 .ct-nav__menu { display: flex; align-items: center; gap: 28px; list-style: none; margin: 0; padding: 0; }
.ct2027 .ct-nav__menu a {
  color: var(--ct-text-mut); text-decoration: none; font-size: 14px; font-weight: 600;
  padding: 6px 1px; position: relative; display: inline-block;
  transition: color .2s ease;
}
.ct2027 .ct-nav__menu a:hover,
.ct2027 .ct-nav__menu a.is-active { color: var(--ct-text); }
.ct2027 .ct-nav__menu a.is-active::after,
.ct2027 .ct-nav__menu a:hover::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -7px; height: 2px;
  border-radius: 2px; background: var(--ct-grad);
}

.ct2027 .ct-nav__actions { display: flex; align-items: center; gap: 14px; }
.ct2027 .ct-nav__link {
  color: var(--ct-text-mut); text-decoration: none; font-size: 13.5px; font-weight: 600;
  white-space: nowrap; display: inline-flex; align-items: center; gap: 6px;
  transition: color .2s ease;
}
.ct2027 .ct-nav__link:hover { color: var(--ct-text); }

.ct2027 .ct-nav__toggle {
  display: none; width: 38px; height: 38px; border-radius: 50%; flex: none;
  align-items: center; justify-content: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line); color: var(--ct-text);
  cursor: pointer;
}

/* mobile drawer + backdrop — the scrim reuses the page's own bg token so it
   automatically reads as a dark scrim in dark mode and a soft glass wash in
   light mode, instead of a hardcoded black overlay. */
.ct2027 .ct-backdrop {
  position: fixed; inset: 0; z-index: 998;
  background: var(--ct-bg);
  opacity: 0; visibility: hidden;
  transition: opacity .3s ease, visibility 0s linear .3s;
}
.ct2027 .ct-backdrop.is-open {
  opacity: .72; visibility: visible;
  transition: opacity .3s ease, visibility 0s linear;
}

.ct2027 .ct-nav__drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 999;
  width: min(340px, 84vw);
  background: var(--ct-bg-alt);
  border-left: 1px solid var(--ct-line-strong);
  box-shadow: var(--ct-shadow);
  padding: 20px 22px 28px;
  display: flex; flex-direction: column; gap: 2px;
  transform: translateX(100%);
  transition: transform .35s ease;
  overflow-y: auto;
}
.ct2027 .ct-nav__drawer.is-open { transform: translateX(0); }
.ct2027 .ct-nav__drawer a {
  color: var(--ct-text); text-decoration: none; font-size: 16px; font-weight: 600;
  padding: 14px 4px; border-bottom: 1px solid var(--ct-line);
}
.ct2027 .ct-nav__drawer a.ct-btn { border-bottom: none; margin-top: 12px; justify-content: center; }
.ct2027 .ct-drawer-close {
  align-self: flex-end; width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line); color: var(--ct-text);
  cursor: pointer; margin-bottom: 8px;
}

/* footer */
.ct2027 .ct-footer {
  position: relative; z-index: 1;
  background: var(--ct-bg-alt);
  border-top: 1px solid var(--ct-line);
  padding: 64px 0 28px;
}
.ct2027 .ct-footer__grid {
  display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 40px;
  padding-bottom: 40px; border-bottom: 1px solid var(--ct-line);
}
.ct2027 .ct-footer__brand img { height: 28px; margin-bottom: 16px; }
.ct2027 .ct-footer__brand p { color: var(--ct-text-mut); font-size: 14px; line-height: 1.7; max-width: 320px; }
.ct2027 .ct-footer__social { display: flex; gap: 10px; margin-top: 18px; }
.ct2027 .ct-footer__social a {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line); color: var(--ct-text-mut);
  transition: color .2s ease, border-color .2s ease;
}
.ct2027 .ct-footer__social a:hover { color: var(--ct-cyan); border-color: var(--ct-cyan); }
.ct2027 .ct-footer h3 {
  color: var(--ct-text); font-size: 13px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; margin: 0 0 18px;
}
.ct2027 .ct-footer ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.ct2027 .ct-footer a { color: var(--ct-text-mut); text-decoration: none; font-size: 14px; transition: color .2s ease; }
.ct2027 .ct-footer a:hover { color: var(--ct-text); }
.ct2027 .ct-footer .new {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  background: var(--ct-surface-2); color: var(--ct-cyan); font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .02em;
}
.ct2027 .ct-footer__bottom {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; padding-top: 24px;
}
.ct2027 .ct-footer__bottom p { color: var(--ct-text-mut); font-size: 13px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ct2027 .ct-footer__bottom img { height: 20px; vertical-align: middle; border-radius: 4px; }

/* ----- carried-over legacy widgets -----------------------------------------
   `.proudly-canadian` ships from the shared styles.css bundle with
   `position:absolute; top:0; left:0` and no z-index — it renders stacked
   BEHIND our fixed `.ct-nav` (z-index:200), so only a corner smudge peeks
   out instead of a badge. The 2026 layout hit this identical bug and fixed
   it in 702354f630 by giving the badge its own fixed bottom-left placement,
   well clear of the nav, at the highest safe z-index; ported verbatim here
   so it can never again render invisibly under this or any future overlay.
   `top: auto` is NOT decorative: our rule never re-declares `top`, so the
   legacy `top:0` keeps cascading in per-property alongside our `bottom:14px`
   — with both set on a `position:fixed` box, the browser stretches its
   height to fill the gap between them instead of sizing to the image. */
.ct2027 .proudly-canadian { position: fixed; top: auto; left: 14px; bottom: 14px; z-index: 2147483647; width: 66px; }
.ct2027 .proudly-canadian img { width: 100%; }
@media (max-width: 1280px) { .ct2027 .proudly-canadian { display: none; } }

/* `#back2Top` is shown/hidden by the legacy jQuery in custom.js
   (`$('#back2Top').fadeIn()` past 100px of scroll), which writes an INLINE
   display value. Never declare `display` here — jQuery would overwrite it and
   the two would fight. `position: fixed` already blockifies the anchor, so
   width/height/line-height apply regardless. Only the skin is ours: the
   legacy rule paints a flat blue square that looks like a different website
   sitting on top of this one. Task 3 could never see this widget because the
   placeholder page was too short to cross the scroll threshold. */
.ct2027 #back2Top {
  width: 46px; height: 46px; line-height: 46px;
  right: 20px; bottom: 84px;
  border-radius: 50%;
  background: var(--ct-grad);
  color: var(--ct-on-accent);
  box-shadow: var(--ct-shadow-cta);
  font-size: 15px;
  z-index: 949;
}
.ct2027 #back2Top:hover { background: var(--ct-grad); color: var(--ct-on-accent); }

/* ----- buttons ------------------------------------------------------------
   SCOPING IS LOAD-BEARING — but not against the legacy bundle's plain
   `a { color:#30384e }` (landing2025/css/styles.css:40): at (0,0,1) that
   never actually contests a class-level button rule, scoped or not. The
   real threat is a broad rule under OUR OWN namespace, shaped like the one
   that actually caused the 2026 bug: `.ct2026 a { color: inherit }` is
   (0,1,1) and WOULD beat an unscoped `.ct-btn--primary { color: #fff }`
   (0,1,0) — that's what made every CTA dark-on-dark, including one that was
   invisible. check.js's `cascade traps` group injects a competitor of that
   same selector shape (`.ct2027 a { color: ... }`) and asserts the button
   still wins, in both the rest and hover states — with an explicit dark
   value rather than the historical rule's literal `inherit`, because on
   this page in dark theme the ambient/inherited colour is itself white, so
   `inherit` would be indistinguishable from a correct pass regardless of
   which rule actually won the cascade.
   Keep the `.ct2027` prefix AND the `.ct-btn--primary` class itself — never
   drop to a bare `a` or `.ct2027` alone — on ANY future button colour rule. */
.ct2027 .ct-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border-radius: var(--ct-r-md);
  font-weight: 700; font-size: 14px; line-height: 1;
  text-decoration: none; cursor: pointer; border: 1px solid transparent;
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.ct2027 .ct-btn--lg { padding: 15px 28px; font-size: 15px; }
.ct2027 .ct-btn--primary,
.ct2027 a.ct-btn--primary,
.ct2027 a.ct-btn--primary:hover {
  color: var(--ct-on-accent);
  background: var(--ct-grad);
  box-shadow: var(--ct-shadow-cta);
}
.ct2027 .ct-btn--primary:hover { transform: translateY(-2px); }
.ct2027 .ct-btn--ghost,
.ct2027 a.ct-btn--ghost,
.ct2027 a.ct-btn--ghost:hover {
  color: var(--ct-text);
  background: var(--ct-surface);
  border-color: var(--ct-line-strong);
}
.ct2027 .ct-btn--maple,
.ct2027 a.ct-btn--maple,
.ct2027 a.ct-btn--maple:hover {
  color: var(--ct-on-accent);
  background: var(--ct-grad-maple);
}

/* ----- responsive: chrome breakpoints -------------------------------------
   Desktop shows the horizontal menu; below the breakpoint the hamburger +
   off-canvas drawer take over. Kept separate from the legacy counter-block
   below, which must stay the very last thing in this file.

   `.ct2027 .ct-nav .ct-nav__inner` is deliberately (0,3,0). The nav bar is
   also a `.ct-container`, and the counter-block at the very bottom of this
   file sets `.ct2027 .ct-container { padding: 0 18px }` at this same
   breakpoint — equal specificity, later in the file, so it was silently
   winning and stripping the nav's 14px of VERTICAL padding. The bar
   collapsed to 39px on a phone. Order can't be changed (the counter block is
   mandated last), so specificity has to do the work.

   Task 13 (review round): the threshold was 993px (`max-width:992px`).
   Measured directly with a fine width sweep (not just the verification
   matrix's fixed widths): from 993px, where the hamburger disengages, up to
   1100px, `.ct-nav__menu`'s seven `<li>` flex items don't fit their natural
   single-line width in the space left by the brand mark and
   `.ct-nav__actions` — under the row's default `nowrap` + implicit
   flex-shrink, the two-word labels ("Why Us", "Contact Us") get squeezed
   narrow enough to break internally onto a second line while the one-word
   labels don't, so different links land at visibly different
   `getBoundingClientRect().top` values in the same bar (confirmed 2
   distinct row-tops at every width from 993px to 1100px, exactly 1 from
   1101px on). That's a real, visibly broken nav at ordinary tablet-landscape
   /small-laptop widths, not a corner case. Rather than hand-tune spacing to
   fit the same seven links back into a band this narrow (risking a new,
   different cramped state at the next content change), this raises the
   already-shipped, already-correct hamburger/drawer breakpoint to cover the
   whole measured-broken band with a small margin (1100px measured clear,
   1104px shipped). check.js's own nav-row-uniformity assertion (added the
   same round) is what would catch a future regression of this band. */
@media (max-width: 1104px) {
  .ct2027 .ct-nav__menu { display: none; }
  .ct2027 .ct-nav__toggle { display: inline-flex; }
}
@media (max-width: 767px) {
  .ct2027 .ct-nav__topbar { display: none; }
  .ct2027 { --ct-nav-h: 72px; }
  .ct2027 .ct-nav .ct-nav__inner { padding-top: 12px; padding-bottom: 12px; }
  .ct2027 .ct-footer__grid { grid-template-columns: 1fr 1fr; }
  .ct2027 .ct-footer__brand { grid-column: 1 / -1; }
}
@media (max-width: 480px) {
  .ct2027 .ct-nav__link.download-pdf { display: none; }
  .ct2027 .ct-footer__grid { grid-template-columns: 1fr; gap: 32px; }
}

/* ==========================================================================
   COMPONENTS — the shared vocabulary for every 2027 marketing page.

   Tasks 5-12 build their sections out of these and nothing else. Rules:
     - colour comes from tokens only, so both themes follow for free;
     - one accent per surface; premium reads as restraint, so hierarchy and
       spacing carry a section, not a second colour;
     - the maple accent is a rationed resource — at most ONE maple-tinted
       element in a viewport. check.js measures this in the hero.

   Tokens added by this layer (in the token block above):
     --ct-ok          verified / success text, contrast-safe on glass
     --ct-maple-soft  the only maple border tint; keeps the leaf legible
                      without spending the accent on a whole element
     --ct-dot-r/y/g   browser-chrome traffic lights. Deliberately NOT brand
                      colours: they are iconography, not accent, so using
                      --ct-maple here would burn the one maple per viewport
                      on a 10px dot.
     --ct-nav-h       fixed-nav clearance, see the base layer

   `--ct-text-dim` was retuned in BOTH themes by this task. It shipped as
   #64748b, which measures 4.24:1 on the dark background and 4.48:1 on the
   light one — under the 4.5:1 floor in both, not just light. Eight later
   pages inherit this token, so it is now genuinely safe as a third text tier
   (5.95:1 dark / 5.12:1 light) instead of a trap.
   ========================================================================== */

/* Glass: the base surface every raised block sits on. */
.ct2027 .ct-glass {
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-lg);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
}
.ct2027 .ct-glass--lift { border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }

/* Eyebrow vs chip — the one outstanding vocabulary ambiguity, closed here:
   an EYEBROW sits above a heading and announces the block that follows
   ("Module 01", "02 · One platform"); a CHIP is an inline tag attached to a
   piece of content wherever it appears — a filter, an audience marker, a
   step in a sequence ("For students", "Fall 2026", "Upload"). If it
   precedes an H2, reach for .ct-eyebrow; if it decorates content inline,
   reach for .ct-chip. Task 7's module spotlights are the first section to
   lean on both at once (eyebrow per block, chips for tags/steps), which is
   what surfaced the need to write the rule down.

   Eyebrow: the small uppercase label above a heading.
   The maple variant deliberately keeps --ct-text for the WORDS. --ct-maple
   measures 4.24:1 on the dark background, which fails at this size; the
   accent is carried by the leaf, the wash and the border instead. */
.ct2027 .ct-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 999px;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  color: var(--ct-text-mut);
  font-size: 11.5px; font-weight: 800; line-height: 1;
  letter-spacing: .10em; text-transform: uppercase;
}
.ct2027 .ct-eyebrow--maple {
  color: var(--ct-text);
  background: var(--ct-wash-3);
  border-color: var(--ct-maple-soft);
}

/* Chip: a neutral inline tag. --on is the selected / emphasised state. */
.ct2027 .ct-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border-radius: 999px;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  color: var(--ct-text-mut);
  font-size: 13px; font-weight: 600; line-height: 1.2;
}
.ct2027 .ct-chip--on {
  color: var(--ct-text);
  background: var(--ct-surface-2);
  border-color: var(--ct-line-strong);
}

/* Icon chip: the square tile that heads a feature card. */
.ct2027 .ct-ichip {
  width: 46px; height: 46px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--ct-r-md);
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  color: var(--ct-cyan);
  font-size: 18px; line-height: 1;
}
.ct2027 .ct-ichip--grad {
  background: var(--ct-grad);
  border-color: transparent;
  color: var(--ct-on-accent);
}

/* Gradient text. The fallback colour is not decorative: a browser without
   background-clip:text renders -webkit-text-fill-color:transparent as
   INVISIBLE words, so the flat colour is declared first and the gradient is
   layered on only where the clip is supported. */
.ct2027 .ct-grad-text {
  color: var(--ct-cyan);
}
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .ct2027 .ct-grad-text {
    background: var(--ct-grad);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}

/* Maple gradient text. DISPLAY SIZES ONLY (>= 24px, or >= 18.66px bold):
   the maple mid-stop measures 4.24:1 on the dark background, which clears
   the 3:1 large-text floor but not the 4.5:1 body-text floor. */
.ct2027 .ct-maple-text {
  color: var(--ct-maple);
  font-weight: 800;
}
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .ct2027 .ct-maple-text {
    background: var(--ct-grad-maple);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}

/* Stat: a number over a label. Solid, never gradient — the gradient belongs
   to headlines and the primary CTA, and a third gradient in one viewport is
   noise, not premium. */
.ct2027 .ct-stat { display: flex; flex-direction: column; gap: 5px; }
.ct2027 .ct-stat b {
  font-size: 30px; font-weight: 800; line-height: 1;
  letter-spacing: -0.03em; color: var(--ct-text);
}
.ct2027 .ct-stat span {
  font-size: 11.5px; font-weight: 600; line-height: 1;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ct-text-mut);
}
/* Large modifier: a scroll-counted dashboard KPI wants the exact same
   number-over-label treatment as the compact hero proof stats — same
   weight, tracking and colour — just bigger, because in a KPI tile the
   number IS the section's own headline moment rather than a footnote
   beside a CTA. This is the composition path for "same shape, bigger
   scale": reach for this modifier before reaching for a parallel
   component. */
.ct2027 .ct-stat--lg b { font-size: 34px; }

/* Browser-chrome frame: the mockup shell. */
.ct2027 .ct-frame {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--ct-r-xl);
  background: var(--ct-bg-alt);
  border: 1px solid var(--ct-line-strong);
  box-shadow: var(--ct-shadow);
}
.ct2027 .ct-frame:focus-visible { outline: 2px solid var(--ct-cyan); outline-offset: 3px; }
/* Final-review Fix 3: the hero panel's role="tabpanel"/tabindex="0" now live
   on .ct-hero__panel (a wrapper AROUND .ct-frame, not the same element —
   see that markup change's comment), so the focusable box that needs the
   ring is .ct-hero__panel there, not .ct-frame. Added, not swapped: .ct-frame
   alone is still directly focusable elsewhere (module spotlights). */
.ct2027 .ct-hero__panel:focus-visible { outline: 2px solid var(--ct-cyan); outline-offset: 3px; }
.ct2027 .ct-frame__bar {
  display: flex; align-items: center; gap: 7px;
  padding: 12px 16px;
  background: var(--ct-surface-2);
  border-bottom: 1px solid var(--ct-line);
}
.ct2027 .ct-dot { width: 10px; height: 10px; flex: none; border-radius: 50%; display: inline-block; }
.ct2027 .ct-dot--r { background: var(--ct-dot-r); }
.ct2027 .ct-dot--y { background: var(--ct-dot-y); }
.ct2027 .ct-dot--g { background: var(--ct-dot-g); }
.ct2027 .ct-frame__url {
  flex: 1 1 auto; min-width: 0; margin-left: 10px;
  padding: 5px 12px; border-radius: 999px;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  color: var(--ct-text-mut);
  font-size: 12.5px; font-weight: 600; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ct2027 .ct-frame__body { position: relative; padding: 6px 22px 14px; }

/* Row: one checked line inside a frame. */
.ct2027 .ct-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--ct-line);
  font-size: 14.5px; line-height: 1.45; color: var(--ct-text);
}
.ct2027 .ct-row:last-child { border-bottom: 0; }
.ct2027 .ct-row__ok,
.ct2027 .ct-row__flag {
  flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 999px;
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  font-size: 12px; font-weight: 700; line-height: 1.25;
  letter-spacing: .02em; white-space: nowrap;
}
.ct2027 .ct-row__ok { color: var(--ct-ok); }
.ct2027 .ct-row__flag { color: var(--ct-warn); }

/* Floating glass badge, pinned to a frame corner. */
.ct2027 .ct-badge {
  position: absolute; z-index: 3;
  display: inline-flex; align-items: center; gap: 11px;
  max-width: 78%;
  padding: 11px 15px; border-radius: var(--ct-r-md);
  background: var(--ct-surface);
  border: 1px solid var(--ct-line-strong);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
  box-shadow: var(--ct-shadow);
  color: var(--ct-text);
  font-size: 13px; font-weight: 700; line-height: 1.25;
}
.ct2027 .ct-badge i { font-style: normal; font-size: 16px; line-height: 1; }
.ct2027 .ct-badge s {
  display: block; margin-top: 3px;
  text-decoration: none;
  color: var(--ct-text-mut);
  font-size: 11.5px; font-weight: 600; letter-spacing: .01em;
}
.ct2027 .ct-badge--br { right: -12px; bottom: -20px; }
.ct2027 .ct-badge--tl { left: -12px; top: -20px; }

/* Scanline: the "we are checking this right now" cue over a frame body. */
.ct2027 .ct-scanline {
  position: absolute; left: 0; right: 0; height: 2px; top: 8%;
  background: linear-gradient(90deg, transparent, var(--ct-cyan), transparent);
  box-shadow: 0 0 14px 2px var(--ct-glow);
  animation: ctScan 3.4s ease-in-out infinite;
}
@keyframes ctScan { 0%, 100% { top: 8%; } 50% { top: 88%; } }

/* Reveal on scroll. `html.ct-anim` is set by the layout before first paint
   and is the ONLY thing that pre-hides these, so nothing can be left
   invisible when JS is off, when the observer is missing, or under
   prefers-reduced-motion (where the layout never sets the class at all). */
.ct2027 .ct-reveal {
  transition: opacity .75s cubic-bezier(.22, 1, .36, 1),
              transform .75s cubic-bezier(.22, 1, .36, 1);
}
.ct-anim .ct2027 .ct-reveal { opacity: 0; transform: translateY(20px); }
.ct-anim .ct2027 .ct-reveal.is-in { opacity: 1; transform: none; }
.ct2027 .ct-reveal--d1 { transition-delay: .10s; }
.ct2027 .ct-reveal--d2 { transition-delay: .20s; }
.ct2027 .ct-reveal--d3 { transition-delay: .30s; }

@media (prefers-reduced-motion: reduce) {
  .ct2027 .ct-scanline { animation: none; opacity: .4; }
  .ct2027 .ct-reveal { transition: none; opacity: 1; transform: none; }
  .ct-anim .ct2027 .ct-reveal { opacity: 1; transform: none; }
}

/* ==========================================================================
   SECTION — hero
   ========================================================================== */
.ct2027 .ct-hero { position: relative; padding: 44px 0 96px; }
/* Column split and headline size are tuned together. The headline carries a
   hard <br> after "campus," and that break has to survive: at 60px the line
   measures ~595px against a 532px column and wraps to a stair-stepped three
   lines with a two-word orphan in the middle. 54px against a 579px column
   leaves ~44px of slack, which survives a font-metric shift. */
.ct2027 .ct-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  gap: 56px; align-items: center;
}
.ct2027 .ct-hero__copy .ct-eyebrow { margin-bottom: 22px; }
.ct2027 .ct-hero h1 {
  margin: 0 0 20px;
  font-size: clamp(38px, 3.9vw, 54px);
  font-weight: 800; line-height: 1.08; letter-spacing: -0.035em;
}
/* Room under the last row so the floating badge overlaps the frame's corner,
   not its content. Without it the badge sat squarely on top of "Proof of
   funds / flagged" — the one row in the panel that carries any tension. */
/* Final-review Fix 3: all five panels now share one min-height so the stage
   stops reflowing as the switcher cycles. Admissions (5 rows) rendered its
   body at 331px against every other panel's 275px (4 rows) — a 56px jump
   (measured stage total: 437px vs 381px) the auto-advance repeated every
   ~4.2s, on a loop that never stops, on the first thing anyone sees on the
   site. 332px is admissions' own natural body height (measured, +1px
   rounding headroom), so admissions is unaffected and the four shorter
   panels grow UP to match it rather than admissions shrinking down to them. */
.ct2027 .ct-hero__panel .ct-frame__body { padding-bottom: 46px; min-height: 332px; }
.ct2027 .ct-hero__cta { display: flex; flex-wrap: wrap; gap: 14px; }
.ct2027 .ct-hero__proof {
  display: flex; align-items: center;
  margin-top: 38px; padding-top: 28px;
  border-top: 1px solid var(--ct-line);
}
.ct2027 .ct-hero__proof .ct-stat + .ct-stat {
  margin-left: 30px; padding-left: 30px;
  border-left: 1px solid var(--ct-line);
}

.ct2027 .ct-hero__stage { position: relative; }
/* Final-review Fix 3: each panel is now its own positioning context, since
   the admissions-only badge is nested inside .ct-hero__panel (a sibling of
   .ct-frame, not a descendant) rather than absolutely positioned off the
   whole stage. See the badge markup's own comment in index.php. */
.ct2027 .ct-hero__panel { position: relative; }
.ct2027 .ct-hero__switch {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0 0 16px;
}
.ct2027 .ct-hero__switch [role="tab"] {
  position: relative;
  -webkit-appearance: none; appearance: none;
  padding: 9px 15px; border-radius: 999px;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  color: var(--ct-text-mut);
  font-family: inherit; font-size: 12.5px; font-weight: 700; line-height: 1;
  letter-spacing: .02em; cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.ct2027 .ct-hero__switch [role="tab"]:hover {
  color: var(--ct-text); border-color: var(--ct-line-strong);
}
.ct2027 .ct-hero__switch [role="tab"]:focus-visible {
  outline: 2px solid var(--ct-cyan); outline-offset: 2px;
}
/* Selected state mirrors the nav's active link: a raised surface plus a
   gradient underline. Deliberately NOT a gradient fill — the hero already
   spends a gradient on the H1 and another on the primary CTA, and small
   white-on-gradient text drops to ~3.7:1 at the cyan stop in light theme. */
.ct2027 .ct-hero__switch [role="tab"][aria-selected="true"] {
  color: var(--ct-text);
  background: var(--ct-surface-2);
  border-color: var(--ct-line-strong);
}
.ct2027 .ct-hero__switch [role="tab"][aria-selected="true"]::after {
  content: ""; position: absolute; left: 15px; right: 15px; bottom: -5px;
  height: 2px; border-radius: 2px; background: var(--ct-grad);
}

/* A hidden tabpanel must actually be gone. `.ct-frame` sets display:block at
   (0,1,0), which beats the UA sheet's `[hidden] { display: none }`, so
   without this every panel would stack on top of the last. Bootstrap's
   reboot happens to ship `[hidden]{display:none!important}` today; relying
   on that would make this page depend on a legacy bundle staying loaded. */
.ct2027 [role="tabpanel"][hidden] { display: none; }

@media (max-width: 992px) {
  .ct2027 .ct-hero { padding: 30px 0 76px; }
  .ct2027 .ct-hero__grid { grid-template-columns: minmax(0, 1fr); gap: 44px; }
  .ct2027 .ct-lede { max-width: none; }
}
@media (max-width: 600px) {
  .ct2027 .ct-hero h1 { letter-spacing: -0.025em; }
  .ct2027 .ct-lede { font-size: 16px; margin-bottom: 26px; }
  .ct2027 .ct-hero__proof { margin-top: 30px; padding-top: 22px; }
  .ct2027 .ct-hero__proof .ct-stat + .ct-stat { margin-left: 18px; padding-left: 18px; }
  .ct2027 .ct-stat b { font-size: 25px; }
  .ct2027 .ct-stat span { font-size: 10.5px; letter-spacing: .05em; }
  .ct2027 .ct-frame__body { padding: 4px 16px 12px; }
  /* Same stable-height fix as the desktop rule above, re-measured at this
     breakpoint: rows are shorter here (13.5px font, 12px row padding), so
     admissions' own natural body height is 316px, not 332px. */
  .ct2027 .ct-hero__panel .ct-frame__body { padding-bottom: 40px; min-height: 316px; }
  .ct2027 .ct-row { font-size: 13.5px; gap: 12px; padding: 12px 0; }
  .ct2027 .ct-badge { max-width: none; left: 0; right: 0; bottom: -22px; padding: 10px 14px; }
  .ct2027 .ct-hero__switch [role="tab"] { padding: 9px 13px; font-size: 12px; }
}

/* ==========================================================================
   SECTION — command centre stage
   The "wow" moment: the whole product staged on a tilted 3D console, KPI
   counters that resolve as the section scrolls into view, and a 12-bar
   sparkline that draws itself. Placed second precisely so it can be
   ambitious without risking the hero's already-approved composition.

   Deliberately NO maple here — the hero already spent the one accent above
   the fold, and premium reads as restraint: hierarchy, spacing and scale
   carry the "wow", not a second colour. check.js measures this (<= 1).
   ========================================================================== */
.ct2027 .ct-cc { position: relative; padding: 108px 0 96px; }

.ct2027 .ct-cc__head { max-width: 640px; margin: 0 auto 60px; text-align: center; }
.ct2027 .ct-cc__head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-cc h2 {
  margin: 0 0 18px;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-cc__lede { margin: 0 auto 0; max-width: 58ch; text-align: center; }

/* Stage: the perspective viewport the console sits inside. The tilt lives on
   a dedicated child (`.ct-stage__tilt`) rather than on `.ct-stage` itself so
   `perspective` and `transform` never have to fight for the same box. */
.ct2027 .ct-stage {
  position: relative;
  perspective: 1400px;
  padding: 0 0 12px;
}
.ct2027 .ct-stage__tilt {
  transform: rotateX(11deg);
  transform-origin: 50% 0;
  transition: transform .6s ease;
}
.ct2027 .ct-console.ct-frame {
  max-width: 980px; margin: 0 auto;
  /* base frame shadow + a second, colour-washed glow layer underneath the
     stage — both come from tokens, nothing raw. */
  box-shadow: var(--ct-shadow), 0 60px 130px 0 var(--ct-glow);
}

.ct2027 .ct-console__body { display: flex; min-height: 380px; }

.ct2027 .ct-console__side {
  width: 208px; flex: none;
  padding: 20px 14px;
  display: flex; flex-direction: column; gap: 3px;
  background: var(--ct-surface);
  border-right: 1px solid var(--ct-line);
}
.ct2027 .ct-console__item {
  padding: 10px 13px;
  border-left: 3px solid transparent;
  border-radius: 0 var(--ct-r-sm) var(--ct-r-sm) 0;
  font-size: 13.5px; font-weight: 600; line-height: 1.3;
  color: var(--ct-text-mut);
}
.ct2027 .ct-console__item.is-on {
  color: var(--ct-text);
  background: var(--ct-surface-2);
  border-left-color: var(--ct-cyan);
}

.ct2027 .ct-console__main { flex: 1 1 auto; min-width: 0; padding: 24px 28px 28px; }

.ct2027 .ct-kpis {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
  margin-bottom: 30px;
}
/* `.ct-kpi` is tile CHROME ONLY. The number-over-label typography is the
   shared `.ct-stat` component, composed in the markup as
   `.ct-kpi.ct-stat.ct-stat--lg` — not re-declared here. The one property
   that genuinely belongs on this class and not on `.ct-stat` is
   tabular-nums: `.ct-stat` numbers are static text, but these digits are
   live-counting, and proportional digit widths make the count-up
   animation visibly jitter left-right as it ticks. That is a real,
   counter-specific need, not a styling preference. */
.ct2027 .ct-kpi {
  padding: 18px 16px;
  border-radius: var(--ct-r-md);
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
}
.ct2027 .ct-kpi b { font-variant-numeric: tabular-nums; }

.ct2027 .ct-console__chart {
  height: 130px;
  display: flex; align-items: flex-end; gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--ct-line);
}
.ct2027 .ct-bar {
  flex: 1 1 auto;
  border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, var(--ct-cyan), var(--ct-blue));
  height: var(--h);
  transition: height .9s cubic-bezier(.22, 1, .36, 1);
  transition-delay: var(--d, 0s);
}
/* The chart "draws itself" by riding the SAME reveal system as the rest of
   the page: `.ct-stage` is itself a `.ct-reveal`, so the moment
   initReveals()'s IntersectionObserver adds `.is-in`, the bars animate from
   0 to their target `--h`. No separate JS module needed — and because the
   zeroing only happens under `.ct-anim` (added pre-paint, never added at
   all under prefers-reduced-motion), reduced-motion visitors and no-JS
   visitors both get the final bars immediately, by construction. */
.ct-anim .ct2027 .ct-stage .ct-bar { height: 0; }
.ct-anim .ct2027 .ct-stage.is-in .ct-bar { height: var(--h); }

@media (prefers-reduced-motion: reduce) {
  .ct2027 .ct-stage__tilt { transform: none; }
  .ct2027 .ct-bar { transition: none; height: var(--h); }
}

/* Below 900px a tilted console costs legibility for no benefit — flatten it
   and let the console stack sidebar-over-content like a phone screenshot.
   Task 13: the threshold was 899px; measured directly (a fine-grained width
   sweep, not just the verification matrix's 6 fixed widths), `rotateX(11deg)`
   under `perspective:1400px` inflates .ct-stage__tilt's on-screen bounding
   box by a near-constant ~6.3% versus its flat width, REGARDLESS of viewport
   -- harmless while `.ct-container`'s own max-width:1200 leaves it room to
   bleed into empty margin, but from 900px up to 1229px the container has
   ZERO outside margin (container width == viewport width there), so that
   same 6.3% bleed spills straight past the real page edge: a genuine,
   continuous `document.documentElement.scrollWidth > clientWidth` overflow
   confirmed at every tested width from 900px through 1220px (3-13px),
   clearing to 0 at 1230px and holding clear through 1920px+. Extending this
   already-shipped, already-verified flat/stacked layout up through 1240px
   (10px of margin past the measured 1230px clear point) removes the
   overflow at its source rather than clipping it with `overflow:hidden`
   (which the task brief rules out) — the tradeoff is that the tilted "wow"
   console no longer renders in the 900-1240px band (roughly tablet-landscape
   / small-laptop widths), where it previously rendered but was silently
   forcing a horizontal scrollbar. Flagged in the task-13 report as a
   design-affecting call worth the product owner's sign-off, not something
   to wave through silently. */
@media (max-width: 1240px) {
  .ct2027 .ct-stage__tilt { transform: none; }
  .ct2027 .ct-console__body { flex-direction: column; min-height: 0; }
  .ct2027 .ct-console__side {
    width: auto; flex-direction: row; flex-wrap: wrap; gap: 6px;
    border-right: 0; border-bottom: 1px solid var(--ct-line);
  }
  .ct2027 .ct-console__item { border-left: 0; border-radius: 999px; }
}
@media (max-width: 640px) {
  .ct2027 .ct-cc { padding: 88px 0 72px; }
  .ct2027 .ct-cc__head { margin-bottom: 40px; }
  .ct2027 .ct-kpis { grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 22px; }
  .ct2027 .ct-kpi { padding: 14px 14px; }
  .ct2027 .ct-kpi.ct-stat--lg b { font-size: 26px; }
  .ct2027 .ct-console__main { padding: 20px 16px 22px; }
  .ct2027 .ct-console__chart { height: 96px; gap: 5px; }
}

/* ==========================================================================
   SECTION — trusted strip
   Reuses the exact chip content that has always lived in the
   landing-page-2025 TRUSTED STRIP, verbatim — that section has never listed
   named institutions, only five capability chips under one label, so there
   is nothing else to "carry across". Composes `.ct-glass` for the surface
   (its first real consumer, alongside the role cards below) and the
   existing `.ct-chip` primitive for each item — no new chip-shaped class.
   ========================================================================== */
.ct2027 .ct-trusted { position: relative; padding: 64px 0 96px; }
.ct2027 .ct-trusted__inner {
  padding: 30px 32px;
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  text-align: center;
}
.ct2027 .ct-trusted__label {
  margin: 0; color: var(--ct-text-mut);
  font-size: 13.5px; font-weight: 700; letter-spacing: .02em;
}
.ct2027 .ct-trusted__row { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }

@media (max-width: 640px) {
  .ct2027 .ct-trusted__inner { padding: 24px 20px; gap: 14px; }
}

/* ==========================================================================
   SECTION — role cards ("Built for everyone on campus")
   Five personas, each a glass card: icon chip, title, paragraph, three
   ticked bullets. `.ct-role` COMPOSES `.ct-glass` for its surface and
   `.ct-ichip` for its icon tile rather than re-declaring either — the
   duplication trap this task exists to close (Task 5's `.ct-kpi` had to be
   reworked to compose `.ct-stat` the same way). Only true card-specific
   concerns (layout, spacing, hover) live here.

   Layout: flexbox with `justify-content: center`, not a fixed-column grid.
   A 3-across row at this content width leaves a 2-card last row — centering
   it is what keeps that row from reading as stranded/ragged instead of a
   deliberate close to the section. See qa/ct2027/check.js "roles layout".

   No maple here either — the restraint rule (at most one accent per
   viewport) already spent its budget in the hero above.
   ========================================================================== */
.ct2027 .ct-roles { position: relative; padding: 12px 0 112px; }
.ct2027 .ct-roles__head { max-width: 640px; margin: 0 auto 52px; text-align: center; }
.ct2027 .ct-roles__head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-roles h2 {
  margin: 0;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}

.ct2027 .ct-roles__grid {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 24px;
}
.ct2027 .ct-role {
  flex: 1 1 300px; max-width: 336px;
  padding: 30px 26px;
  display: flex; flex-direction: column; gap: 16px;
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-role:hover { transform: translateY(-4px); border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }
.ct2027 .ct-role h3 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -0.01em; }
.ct2027 .ct-role p { margin: 0; color: var(--ct-text-mut); font-size: 14.5px; line-height: 1.65; }

.ct2027 .ct-role__list { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ct2027 .ct-role__list li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 13.5px; font-weight: 600; line-height: 1.45;
  color: var(--ct-text);
}
.ct2027 .ct-role__list i { flex: none; margin-top: 3px; color: var(--ct-ok); font-size: 12px; }

@media (max-width: 640px) {
  .ct2027 .ct-roles__head { margin-bottom: 36px; }
  .ct2027 .ct-role { padding: 26px 22px; }
}

/* ==========================================================================
   SECTION — module spotlights
   Five numbered editorial blocks inside ONE section (.ct-spots), alternating
   copy/panel sides via .ct-spot--flip. The flip is done with CSS `order` on
   two fixed children (.ct-spot__copy always first in the DOM, .ct-spot__panel
   always second) so source order — and heading-before-detail reading order
   for assistive tech — never changes, only the visual side does.

   Zero maple accents in this section (not just <= 1) — see the restraint
   note in index.php above the $spots array.
   ========================================================================== */
.ct2027 .ct-spots { position: relative; padding: 100px 0 108px; }

.ct2027 .ct-spot {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
}
.ct2027 .ct-spot + .ct-spot {
  margin-top: 96px;
  padding-top: 96px;
  border-top: 1px solid var(--ct-line);
}
.ct2027 .ct-spot--flip .ct-spot__copy { order: 2; }
.ct2027 .ct-spot--flip .ct-spot__panel { order: 1; }

.ct2027 .ct-spot__copy .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-spot h2 {
  margin: 0 0 14px;
  font-size: clamp(26px, 2.6vw, 36px);
  font-weight: 800; line-height: 1.18; letter-spacing: -0.02em;
}
/* Kicker: a short, coloured tagline between the H2 and the lede. Sized and
   weighted to clear the WCAG LARGE-text threshold (>=18.66px bold) on
   purpose — a solid --ct-cyan measures only 3.46:1 on the light background,
   which clears the 3:1 large-text floor but not the 4.5:1 body floor this
   suite otherwise enforces. Same reasoning as .ct-maple-text above; see
   qa/ct2027/check.js "module spotlights contrast" for the assertion that
   pins this down instead of assuming it. */
.ct2027 .ct-spot__kicker {
  margin: 0 0 16px;
  font-size: 19px; font-weight: 800; line-height: 1.3;
  letter-spacing: -0.005em;
  color: var(--ct-cyan);
}
.ct2027 .ct-spot .ct-lede { margin-bottom: 30px; }

/* Feature list: two columns of ticked items. Spotlight 01's items carry a
   label + description (the settled brief copy); spotlights 02-05 carry a
   single-line label only — both variants share the same tick + flex-row
   shell, distinguished purely by whether a <div> (label+desc) or a bare
   <span> (label only) follows the icon. */
.ct2027 .ct-spot__features {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 18px 24px;
}
.ct2027 .ct-spot__features li { display: flex; align-items: flex-start; gap: 10px; }
.ct2027 .ct-spot__features li i {
  flex: none; margin-top: 4px;
  color: var(--ct-ok); font-size: 12px;
}
.ct2027 .ct-spot__features li > span {
  color: var(--ct-text); font-size: 14px; font-weight: 600; line-height: 1.5;
}
.ct2027 .ct-spot__features li div strong {
  display: block; margin-bottom: 4px;
  color: var(--ct-text); font-size: 14px; font-weight: 700; line-height: 1.4;
}
.ct2027 .ct-spot__features li div span {
  color: var(--ct-text-mut); font-size: 13px; line-height: 1.55;
}

.ct2027 .ct-spot__panel { min-width: 0; }

/* Spotlight 01 — the four-step strip, reusing .ct-chip for each step so the
   only new CSS here is the connecting layout and the decorative arrow. */
.ct2027 .ct-spot__steps {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 8px; padding-top: 16px;
  border-top: 1px solid var(--ct-line);
}
.ct2027 .ct-spot__step-arrow { color: var(--ct-text-dim); font-size: 11px; }

/* Spotlight 02 — the cohort board: a grid of tiles composing .ct-glass for
   chrome (never re-declaring background/border/blur — see index.php's
   comment on why), same pattern as .ct-role. Illustrative programme names
   and seat counts, same convention as the hero/command-centre mockups —
   presentational, not live figures. */
.ct2027 .ct-spot__board {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.ct2027 .ct-spot__cohort {
  padding: 16px 18px;
  display: flex; flex-direction: column; gap: 10px;
}
.ct2027 .ct-spot__cohort b { font-size: 13.5px; font-weight: 800; color: var(--ct-text); line-height: 1.35; }
.ct2027 .ct-spot__cohort-tags { display: flex; flex-wrap: wrap; gap: 6px; }

/* Spotlight 05 — the two-card split, each card composing .ct-glass +
   .ct-ichip + .ct-role__list, exactly the vocabulary the role cards above
   already established — no new tick-list styling needed. */
.ct2027 .ct-spot__split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
}
.ct2027 .ct-spot__split-card {
  padding: 20px 20px 22px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
}
.ct2027 .ct-spot__split-card h4 { margin: 2px 0 0; font-size: 15.5px; font-weight: 800; color: var(--ct-text); }
.ct2027 .ct-spot__split-card .ct-role__list { margin-top: 4px; }

@media (max-width: 899px) {
  .ct2027 .ct-spot { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .ct2027 .ct-spot + .ct-spot { margin-top: 64px; padding-top: 64px; }
  .ct2027 .ct-spot--flip .ct-spot__copy,
  .ct2027 .ct-spot--flip .ct-spot__panel { order: initial; }
  .ct2027 .ct-spot__features { grid-template-columns: minmax(0, 1fr); }
  .ct2027 .ct-spot__board,
  .ct2027 .ct-spot__split { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   SECTION — module chip cloud ("Every module your college could need")
   Seven category rows, each a .ct-glass strip with a category label and a
   wrapped row of .ct-chip tags — the tag content is carried byte-for-byte
   from landing-page-2025's MODULES section (see index.php's comment above
   the $moduleGroups array). No new chip-shaped class: this is .ct-chip's
   natural home, reused rather than forked.
   ========================================================================== */
.ct2027 .ct-cloud { position: relative; padding: 12px 0 104px; }
.ct2027 .ct-cloud__head { max-width: 640px; margin: 0 auto 48px; text-align: center; }
.ct2027 .ct-cloud__head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-cloud h2 {
  margin: 0 0 16px;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-cloud .ct-lede { margin: 0 auto; max-width: 58ch; text-align: center; }

.ct2027 .ct-cloud__groups { display: flex; flex-direction: column; gap: 14px; }
.ct2027 .ct-cloud__group {
  display: flex; align-items: baseline; gap: 22px;
  padding: 18px 24px;
}
.ct2027 .ct-cloud__cat {
  flex: 0 0 168px;
  margin: 0; color: var(--ct-text-mut);
  font-size: 12.5px; font-weight: 800; line-height: 1.3;
  letter-spacing: .08em; text-transform: uppercase;
}
.ct2027 .ct-cloud__row { flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: 8px; }

@media (max-width: 767px) {
  .ct2027 .ct-cloud__group { flex-direction: column; align-items: flex-start; gap: 10px; padding: 16px 18px; }
  .ct2027 .ct-cloud__cat { flex: none; }
}

/* ==========================================================================
   SECTION — how it works
   Four .ct-step cards composing .ct-glass, same pattern as .ct-role and
   .ct-spot__cohort. The big step number reuses --ct-cyan at display scale —
   the same large-text exception as the spotlight kicker: --ct-cyan measures
   only 3.46:1 on the light background (below the 4.5:1 body floor) but
   clears the 3:1 WCAG LARGE-text floor at >=18.66px bold. Pinned at 28px/800
   here, checked directly by qa/ct2027/check.js "how it works contrast"
   rather than assumed.
   ========================================================================== */
.ct2027 .ct-how { position: relative; padding: 96px 0 96px; }
.ct2027 .ct-how__head { max-width: 640px; margin: 0 auto 48px; text-align: center; }
.ct2027 .ct-how__head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-how h2 {
  margin: 0 0 16px;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-how__lede { margin: 0 auto; max-width: 58ch; text-align: center; }

.ct2027 .ct-how__rail { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }
.ct2027 .ct-step { padding: 26px 22px; display: flex; flex-direction: column; gap: 10px; }
.ct2027 .ct-step__num {
  font-size: 28px; font-weight: 800; line-height: 1;
  letter-spacing: -0.02em; color: var(--ct-cyan);
}
.ct2027 .ct-step h3 { margin: 4px 0 0; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; }
.ct2027 .ct-step p { margin: 0; color: var(--ct-text-mut); font-size: 13.5px; line-height: 1.6; }

@media (max-width: 899px) {
  .ct2027 .ct-how__rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .ct2027 .ct-how__rail { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   SECTION — pricing teaser
   States ONE figure — ~$1 per student — and links to /pricing, where the
   real tiers live (Task 10). No second figure belongs in this card. The
   amount reuses .ct-grad-text for the same display-headline gradient
   treatment as the hero H1, rather than inventing a second display style.
   ========================================================================== */
.ct2027 .ct-price { position: relative; padding: 12px 0 100px; }
.ct2027 .ct-price__inner {
  max-width: 620px; margin: 0 auto;
  padding: 48px 40px;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  text-align: center;
}
.ct2027 .ct-price__amount { display: flex; align-items: baseline; justify-content: center; gap: 10px; }
.ct2027 .ct-price__amount b {
  font-size: clamp(46px, 6vw, 68px); font-weight: 800; line-height: 1;
  letter-spacing: -0.03em;
}
.ct2027 .ct-price__amount span {
  font-size: 14px; font-weight: 700; color: var(--ct-text-mut);
  text-transform: uppercase; letter-spacing: .06em;
}
.ct2027 .ct-price__lede { margin: 0; max-width: 42ch; }

@media (max-width: 640px) {
  .ct2027 .ct-price__inner { padding: 38px 26px; }
}

/* ==========================================================================
   SECTION — video guides
   Three .ct-clip buttons (real <button>, keyboard-operable for free) that
   open a shared lightbox — .ct-clip-modal composes .ct-glass for its panel
   chrome. The <iframe> src is set only on open and cleared on close, so
   nothing loads from youtube.com until a visitor actually asks for it.
   `.ct-clip-modal__panel` deliberately does NOT use `.ct-frame` for its
   chrome (unlike the mockup panels elsewhere): .ct-frame sets
   `overflow:hidden`, which would clip the close button's negative offset —
   the exact trap documented for .ct-badge--br. .ct-glass has no overflow
   rule, so the corner-mounted close button is safe.

   Stacking-context trap: `#ct-main { position:relative; z-index:1 }` (base
   layer, above) puts every section — including this modal, nested inside
   .ct-clips — into a stacking context capped at z-index:1 among ITS
   siblings. `.ct-nav` is a SIBLING of #ct-main at z-index:200, in the outer
   context, so no z-index on anything inside #ct-main (however high) can
   ever paint above the nav — check.js caught this as the nav topbar
   intercepting a click meant for the backdrop. Rather than restructure the
   DOM (out of scope for this task) or touch #ct-main's base z-index (Task
   3's fixed-nav-clearance foundation, load-bearing for pages 5-12), the
   modal simply doesn't extend behind the nav: `top` starts at --ct-nav-h
   instead of 0, so there is no overlap for the stacking order to matter.
   ========================================================================== */
.ct2027 .ct-clips { position: relative; padding: 96px 0 100px; }
.ct2027 .ct-clips__head { max-width: 640px; margin: 0 auto 48px; text-align: center; }
.ct2027 .ct-clips__head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-clips h2 {
  margin: 0 0 16px;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-clips__lede { margin: 0 auto; max-width: 58ch; text-align: center; }

.ct2027 .ct-clips__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
/* .ct-clip composes .ct-glass in markup (index.php) for its surface chrome
   — background/border/border-radius/backdrop-filter live there once, not
   here. Only layout and interaction properties belong on this class. */
.ct2027 .ct-clip {
  display: flex; flex-direction: column;
  padding: 0; margin: 0; overflow: hidden;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-clip:hover { transform: translateY(-4px); border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }
.ct2027 .ct-clip:focus-visible { outline: 2px solid var(--ct-cyan); outline-offset: 3px; }
.ct2027 .ct-clip__thumb { position: relative; display: block; aspect-ratio: 16 / 9; background: var(--ct-bg-alt); }
.ct2027 .ct-clip__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.ct2027 .ct-clip__play {
  position: absolute; inset: 0; margin: auto;
  width: 54px; height: 54px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--ct-grad);
  color: var(--ct-on-accent);
  font-size: 17px;
  box-shadow: var(--ct-shadow-cta);
  transition: transform .3s ease;
}
.ct2027 .ct-clip:hover .ct-clip__play { transform: scale(1.08); }
.ct2027 .ct-clip__cap {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 16px 18px;
  font-size: 14.5px; font-weight: 700; color: var(--ct-text);
}

.ct2027 .ct-clip-modal {
  position: fixed; top: var(--ct-nav-h); right: 0; bottom: 0; left: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  opacity: 0; visibility: hidden;
  transition: opacity .25s ease, visibility 0s linear .25s;
}
.ct2027 .ct-clip-modal.is-open {
  opacity: 1; visibility: visible;
  transition: opacity .25s ease, visibility 0s linear;
}
.ct2027 .ct-clip-modal__backdrop { position: absolute; inset: 0; background: rgba(2, 6, 23, .82); }
.ct2027 .ct-clip-modal__panel { position: relative; z-index: 1; width: min(920px, 100%); padding: 14px; }
.ct2027 .ct-clip-modal__close {
  position: absolute; top: -16px; right: -16px; z-index: 2;
  width: 38px; height: 38px; flex: none; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line-strong);
  color: var(--ct-text);
  cursor: pointer;
}
.ct2027 .ct-clip-modal__frame {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--ct-r-md); overflow: hidden;
  background: var(--ct-media-bg);
}
.ct2027 .ct-clip-modal__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

@media (max-width: 899px) {
  .ct2027 .ct-clips__grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 480px) {
  .ct2027 .ct-clip-modal { padding: 12px; }
  .ct2027 .ct-clip-modal__close { top: -12px; right: -4px; }
}

/* ==========================================================================
   SECTION — final CTA
   THE restraint-rule payoff: this section's own background is the page's
   second and last maple moment (the hero eyebrow is the first — see
   classtrack-2027.js is not involved, this is pure CSS). --ct-wash-3 is
   applied directly to `.ct-cta` itself so it is a real, rendered
   background-image on a real element, not the sitewide ambient
   `.ct2027::before` wash (which check.js's restraint scans correctly don't
   count, since that pseudo-element lives outside every section's own
   subtree). Nothing else in this section — heading, lede, either button —
   may use --ct-maple, --ct-maple-soft or --ct-grad-maple. See qa/ct2027/
   check.js "final CTA restraint" and "page-wide maple budget".
   ========================================================================== */
.ct2027 .ct-cta {
  position: relative;
  padding: 100px 0 140px;
  background: radial-gradient(ellipse at 50% 0%, var(--ct-wash-3) 0%, transparent 70%);
}
.ct2027 .ct-cta__inner {
  max-width: 720px; margin: 0 auto;
  padding: 56px 48px;
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  text-align: center;
}
.ct2027 .ct-cta h2 {
  margin: 0;
  font-size: clamp(28px, 3.2vw, 42px);
  font-weight: 800; line-height: 1.16; letter-spacing: -0.03em;
}
.ct2027 .ct-cta__lede { margin: 0 auto; max-width: 50ch; }
.ct2027 .ct-cta__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; margin-top: 6px; }

@media (max-width: 640px) {
  .ct2027 .ct-cta { padding: 80px 0 100px; }
  .ct2027 .ct-cta__inner { padding: 40px 26px; }
}

/* ==========================================================================
   SECTION — inner-page shared furniture (Why Us, Facts)
   Task 9 is the first inner page after the homepage. Everything below
   composes the existing vocabulary (.ct-glass, .ct-ichip, .ct-eyebrow,
   .ct-lede, .ct-btn, .ct-chip, .ct-stat) rather than re-declaring chrome —
   the exact debt this task was warned not to reintroduce. `.ct-cta`,
   `.ct-spots`/`.ct-spot` and `.ct-trusted`/`.ct-trusted__inner`/
   `.ct-trusted__row` are reused VERBATIM from the homepage sections further
   up this file — not redefined here — since their existing rules never
   baked in a homepage-specific selector or copy.

   No maple on either inner-page hero: the homepage's maple eyebrow carries
   a specific claim ("Built for Canadian colleges") that doesn't belong on
   every inner page. Each page still spends exactly one maple, on its own
   final CTA section (reusing `.ct-cta`'s existing `--ct-wash-3` background),
   so the closing moment reads the same way sitewide. See "page maple
   budget" in qa/ct2027/check.js for both pages.

   Per-section CTA buttons from the 2025 source (one "Try ClassTrack Today!"
   after every reason/card grid) are deliberately dropped: the homepage's
   own role cards and module spotlights carry no per-section CTA either,
   relying on the hero + final CTA as the two conversion points. That is a
   presentation change (fewer buttons), not a substance change — every
   argument and destination the buttons pointed to is still carried, just
   not repeated five times down the page. */

/* Page hero: the centred, single-column intro every inner page opens with.
   The homepage's `.ct-hero` is a two-column live-mockup layout that has
   nothing to mirror on a page with no product panel to show; this shares
   the same H1 scale and gradient-text treatment so the pages still read as
   one site. */
.ct2027 .ct-page-hero { position: relative; padding: 72px 0 100px; text-align: center; }
.ct2027 .ct-page-hero__inner { max-width: 740px; margin: 0 auto; }
.ct2027 .ct-page-hero .ct-eyebrow { margin-bottom: 22px; }
.ct2027 .ct-page-hero h1 {
  margin: 0 0 20px;
  font-size: clamp(34px, 4.2vw, 52px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-page-hero .ct-lede { margin: 0 auto 30px; max-width: 56ch; }
.ct2027 .ct-page-hero__cta { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }

/* Inner head: the centred eyebrow/h2/lede block content sections open with.
   One definition, five consumers across the two pages built in this task
   (why-us roles/spots/love heads, facts stats/checklist heads) rather than
   a per-section duplicate of the same three rules. The homepage's own
   per-section `__head` classes each earned staying separate (different
   tuned bottom margins per section); these five didn't need that, so they
   share one. */
.ct2027 .ct-inner-head { max-width: 640px; margin: 0 auto 44px; text-align: center; }
.ct2027 .ct-inner-head .ct-eyebrow { margin-bottom: 20px; }
.ct2027 .ct-inner-head h2 {
  margin: 0 0 16px;
  font-size: clamp(28px, 3.2vw, 40px);
  font-weight: 800; line-height: 1.16; letter-spacing: -0.03em;
}
.ct2027 .ct-inner-head .ct-lede { margin: 0 auto; max-width: 58ch; }

/* Card section: padding-only wrapper shared by Why Us's two persona-card
   grids (built-for-every-role, what-they-love) — layout, not chrome. */
.ct2027 .ct-card-section { position: relative; padding: 12px 0 100px; }

.ct2027 .ct-why-cards { display: flex; flex-wrap: wrap; justify-content: center; gap: 22px; }
.ct2027 .ct-why-card {
  flex: 1 1 250px; max-width: 300px;
  padding: 28px 24px;
  display: flex; flex-direction: column; gap: 14px;
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-why-card:hover { transform: translateY(-4px); border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }
.ct2027 .ct-why-card h3 { margin: 0; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; }
.ct2027 .ct-why-card h3 small { font-weight: 700; color: var(--ct-text-mut); }
.ct2027 .ct-why-card p { margin: 0; color: var(--ct-text-mut); font-size: 14px; line-height: 1.65; }

/* Icon card: an icon tile over one short supporting line, inside a glass
   panel — the standard spot-panel for a claim that doesn't need a
   browser-chrome mockup. Composes .ct-glass + .ct-ichip--grad; only layout
   lives here. Five consumers: three Why Us spot panels (cheaper/support/
   eco), the Why Us payoff panel, and the Facts "future" panel. Every
   caption is a verbatim or near-verbatim clause lifted from the copy
   beside it, not a new line invented for the panel — see the views. */
.ct2027 .ct-icon-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 16px;
  padding: 36px 32px;
}
.ct2027 .ct-icon-card p {
  margin: 0; color: var(--ct-text); font-size: 16px; font-weight: 600; line-height: 1.55;
}

@media (max-width: 640px) {
  .ct2027 .ct-page-hero { padding: 56px 0 76px; }
  .ct2027 .ct-page-hero h1 { letter-spacing: -0.02em; }
  .ct2027 .ct-inner-head { margin-bottom: 32px; }
  .ct2027 .ct-why-card { padding: 24px 20px; }
  .ct2027 .ct-icon-card { padding: 28px 24px; }
}

/* ==========================================================================
   SECTION — Facts stat strip ("ClassTrack, by the numbers")
   The four figures here are real and already published: they are the EXACT
   figures on the current live homepage's own "ClassTrack by the numbers"
   strip (landing-page-2025/index.php, aria-label "ClassTrack by the
   numbers"), reused verbatim rather than invented for this page.
   landing-page-2025/facts.php itself — the page this view otherwise carries
   its substance from — has NO numeric content anywhere on it; that mismatch
   against this task's brief (which frames Facts as "stat-led") is recorded
   in task-9-report.md rather than papered over. Reuses the [data-count-to]
   contract verbatim, per the brief.
   ========================================================================== */
.ct2027 .ct-facts-stats { position: relative; padding: 12px 0 104px; }
.ct2027 .ct-facts-stats__bar {
  display: flex; flex-wrap: wrap; justify-content: center;
  padding: 40px 32px;
}
.ct2027 .ct-facts-stats__bar .ct-stat { align-items: center; text-align: center; padding: 0 40px; }
.ct2027 .ct-facts-stats__bar .ct-stat + .ct-stat { border-left: 1px solid var(--ct-line); }

@media (max-width: 767px) {
  .ct2027 .ct-facts-stats__bar { padding: 28px 18px; gap: 26px 0; }
  .ct2027 .ct-facts-stats__bar .ct-stat { flex: 1 1 42%; padding: 0 14px; }
  /* Same selector shape as the divider rule above (`.ct-stat + .ct-stat`),
     not a bare `.ct-stat` — a bare selector is LOWER specificity than the
     rule it's meant to beat and silently loses regardless of source order.
     Caught by qa/ct2027/check.js "facts layout" ("no stray divider border
     survives the mobile wrap"), which measures the computed border, not
     just that some rule exists. */
  .ct2027 .ct-facts-stats__bar .ct-stat + .ct-stat { border-left: 0; }
}

/* ==========================================================================
   SECTION — pricing plans (Task 10, /pricing)
   Four tier cards. Each `.ct-plan` composes `.ct-glass` for its surface and
   `.ct-ichip` for its icon tile — the same duplication-avoidance discipline
   as every earlier card (`.ct-role`, `.ct-why-card`, `.ct-step`). The price
   readout composes `.ct-stat`/`.ct-stat--lg` verbatim (a number-over-label
   component already built for exactly this shape) and the inclusion list
   composes `.ct-role__list` verbatim — nothing here re-declares chrome that
   already exists.

   One accent only (`--ct-cyan`, via the plain `.ct-ichip` every other card
   in this file already uses). The 2025 source coloured each card
   differently (`--hl: var(--hi-sky/yellow/mint/coral)`); this design reads
   premium through hierarchy and spacing instead, per the sitewide restraint
   rule — maple stays reserved for the final CTA only (see qa/ct2027/
   check.js "pricing composition and restraint").

   `.ct-price-cta` wraps each card's CTA button. PREMISE CORRECTION: this
   task's brief describes it as an EXISTING wrapper with an existing
   max-width:480px full-width rule from Task 3. Neither existed anywhere in
   this file before this task (grepped in full) — Task 3's own 480px rule
   only ever targeted `.ct-hero__cta .ct-btn` and `.ct-cta__actions .ct-btn`.
   The class and its mobile-width rule are introduced BY this task (the rule
   lives in the legacy counter-block below, alongside its two siblings, so
   all three CTA groups share one rule); see task-10-report.md. */
.ct2027 .ct-plans {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
}
.ct2027 .ct-plan {
  padding: 30px 24px;
  display: flex; flex-direction: column; gap: 16px;
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-plan:hover { transform: translateY(-4px); border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }
.ct2027 .ct-plan h3 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -0.01em; }
/* The inclusion list grows to fill the card so every card's CTA lands at
   the same visual baseline regardless of how many lines its tier lists
   (Free Trial: 15, Enterprise: 10) — same `margin-top: 4px` micro-nudge
   convention as `.ct-spot__split-card .ct-role__list` above. */
.ct2027 .ct-plan .ct-role__list { flex: 1 1 auto; margin: 4px 0 0; }
.ct2027 .ct-price-cta { margin-top: 2px; }

/* The hero's large "~$1 per student" figure reuses `.ct-price__amount`
   verbatim from the homepage's own pricing teaser (the class carries no
   ancestor requirement — see its own definition above); this is the only
   page-specific spacing tweak it needs. */
.ct2027 .ct-page-hero .ct-price__amount { margin: 6px 0 26px; }

@media (max-width: 992px) {
  .ct2027 .ct-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .ct2027 .ct-plans { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .ct2027 .ct-plan { padding: 24px 20px; }
}

/* ==========================================================================
   SECTION — Screenshot gallery (Task 11, /gallery)
   53 deck JPGs (themes/base/sampleFiles/Presentation, confirmed by count
   before writing this) opened in a Fancybox 5 lightbox — the PHP loop, the
   LazyLoad usage and every Fancybox attribute are unchanged from
   landing-page-2025/gallery.php (see gallery.php's own header comment);
   only this grid and its frame are new.

   The JPGs are 2021-era light-background slides (white canvas, purple UI,
   red annotation arrows) that will never match the dark glass design and
   are not styled to pretend otherwise. Each one gets a deliberate light
   frame via --ct-artifact-bg (the token block's one permitted raw-hex
   exception, per this task's brief) so it reads as a framed artifact on
   display, not a colour mismatch. The caption sits OUTSIDE that white
   frame, on the page's own background, so its text can safely use
   --ct-text-mut — painting it INSIDE the white frame would hit the same
   trap --ct-text/--ct-text-mut hit everywhere else on a light surface:
   both render near-white in dark theme, invisible on white.
   ========================================================================== */
.ct2027 .ct-shots { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 22px; }

.ct2027 .ct-shot-card { margin: 0; display: flex; flex-direction: column; gap: 12px; }

.ct2027 .ct-shot {
  background: var(--ct-artifact-bg); /* intentional: these are light-background artifacts */
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-md);
  padding: 8px;
  box-shadow: var(--ct-shadow);
  transition: transform .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-shot-card:hover .ct-shot { transform: translateY(-4px); box-shadow: var(--ct-shadow-cta); }
.ct2027 .ct-shot a { display: block; line-height: 0; border-radius: calc(var(--ct-r-md) - 3px); overflow: hidden; }
.ct2027 .ct-shot img { display: block; width: 100%; height: auto; }

.ct2027 .ct-shot-card__cap {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin: 0; padding: 0 2px;
  font-size: 12.5px; font-weight: 700; color: var(--ct-text-mut);
}
.ct2027 .ct-shot-card__cap .ct-chip { padding: 4px 10px; font-size: 11px; }

@media (max-width: 1100px) {
  .ct2027 .ct-shots { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .ct2027 .ct-shots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
}
@media (max-width: 480px) {
  .ct2027 .ct-shots { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   SECTION — Contact split + map (Task 11, /contact-us)
   `.ct-contact` is the form-card + info-panel split. The form card
   composes .ct-glass + .ct-glass--lift for its surface (background/border/
   blur/shadow live there, not re-declared here — same discipline as every
   other card in this file); .ct-glass--lift was defined back when .ct-glass
   itself was, but had zero consumers until this static card needed a
   permanent (not hover-only) elevation.

   Field chrome below targets the live TActiveForm instance by its stable
   `#contact-form` id rather than a class added to the widget's own
   `options` — the widget call in contact.php is byte-for-byte the 2025
   source's, verified against a live curl capture (form-group/control-label/
   form-control/help-block, standard Yii2 bootstrap3 ActiveField output).
   ========================================================================== */
.ct2027 .ct-contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 48px;
  align-items: start;
}

.ct2027 .ct-contact__form { padding: 36px 34px; display: flex; flex-direction: column; }
.ct2027 .ct-contact__form .ct-ichip { margin-bottom: 14px; }
.ct2027 .ct-contact__form h3 { margin: 0 0 8px; font-size: 21px; font-weight: 800; letter-spacing: -0.01em; }
.ct2027 .ct-contact__form > p { margin: 0 0 24px; color: var(--ct-text-mut); font-size: 14.5px; line-height: 1.6; }

.ct2027 #contact-form .form-group { margin-bottom: 18px; }
.ct2027 #contact-form .control-label {
  display: block; margin-bottom: 8px;
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ct-text-mut);
}
.ct2027 #contact-form .form-control {
  display: block; width: 100%; padding: 13px 16px;
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-sm);
  color: var(--ct-text);
  font: inherit; font-size: 14.5px;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.ct2027 #contact-form .form-control::placeholder { color: var(--ct-text-dim); }
.ct2027 #contact-form .form-control:focus { outline: none; border-color: var(--ct-cyan); box-shadow: 0 0 0 3px var(--ct-glow); }
.ct2027 #contact-form textarea.form-control { min-height: 108px; resize: vertical; }
.ct2027 #contact-form .help-block { margin: 6px 0 0; font-size: 12.5px; font-weight: 600; color: var(--ct-warn); min-height: 0; }
.ct2027 #contact-form .help-block:empty { display: none; }
.ct2027 #contact-form .g-recaptcha { margin: 2px 0 6px; transform-origin: 0 0; }
.ct2027 .ct-contact-form__submit { margin-top: 4px; }

.ct2027 .ct-contact__info { padding: 10px 4px; }
.ct2027 .ct-contact__info .ct-eyebrow { margin-bottom: 18px; }
.ct2027 .ct-contact__info h2 {
  margin: 0 0 14px;
  font-size: clamp(24px, 2.6vw, 32px);
  font-weight: 800; line-height: 1.2; letter-spacing: -0.02em;
}
.ct2027 .ct-contact__info > p { margin: 0 0 28px; color: var(--ct-text-mut); font-size: 14.5px; line-height: 1.65; }

.ct2027 .ct-contact-info { display: flex; flex-direction: column; gap: 20px; margin-bottom: 28px; }
.ct2027 .ct-contact__info-cta { margin-top: 6px; }
.ct2027 .ct-contact-info__row { display: flex; align-items: flex-start; gap: 16px; }
.ct2027 .ct-contact-info__row b { display: block; margin-bottom: 4px; font-size: 14px; font-weight: 800; color: var(--ct-text); }
.ct2027 .ct-contact-info__row p { margin: 0; color: var(--ct-text-mut); font-size: 14px; line-height: 1.5; }
.ct2027 .ct-contact-info__row a { color: var(--ct-text-mut); text-decoration: none; transition: color .2s ease; }
.ct2027 .ct-contact-info__row a:hover { color: var(--ct-cyan); }

/* Map: a bordered glass frame around the third-party iframe so a blocked
   or slow embed still leaves a visible, non-empty box, never a hole. */
.ct2027 .ct-contact-map { padding: 12px; }
.ct2027 .ct-contact-map iframe { max-width: 100%; }

@media (max-width: 899px) {
  .ct2027 .ct-contact { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}
@media (max-width: 640px) {
  .ct2027 .ct-contact__form { padding: 28px 24px; }
}
@media (max-width: 360px) {
  /* Google's reCAPTCHA checkbox widget has a fixed ~304px intrinsic width
     that does not itself respond to the viewport; scale it down rather
     than let it force horizontal overflow at 320px. */
  .ct2027 #contact-form .g-recaptcha { transform: scale(0.87); }
}

/* ==========================================================================
   SECTION — Book a Meeting (Task 12, /demo/meeting-schedule/book-meeting)
   The view (protected/modules/demo/views/meeting-schedule/add.php) is NOT
   edited by this task — it is a live, functional TActiveForm with real slot
   availability + timezone-conversion AJAX and a real reCAPTCHA-gated save.
   Every selector below targets a class/id that view already emits (`ct-hero`,
   `ct-head`, `ct-pillars`, `ct-card`, `ct-hl`, `ct-section`, `ct-form-panel`,
   `#meeting-form`, `#slots-container`, `#available-slots`, `#add-button`,
   `.ct-cta`, `.ct-thanks-ico`, `#loader`) — mirroring the precedent this
   task was pointed at, `.ct2026 .ct-booking .ct-hero{...}` in
   classtrack-2026.css (NOT `.demo-booking`, which turned out to be a
   different, older class from the legacy landing2025 bundle — see
   task-12-report.md). Everything here is scoped under `.ct2027 .ct-booking`
   (this page's own outer wrapper div) even where the class itself doesn't
   exist anywhere else in this file today, so nothing here can ever leak to
   another page regardless of what future vocabulary reuses the same names.

   ONE required exception to that scoping, first: see the body:has() fix
   immediately below — that one has to target `body`, since the wrapper div
   is a descendant of the very element that's broken.
   ========================================================================== */

/* landing2025/styles.css (loaded last before this file) carries
   `body:has(#meeting-form){background:none}` — written for THAT era's own
   `.demo-booking .page-title-top` header background, with no awareness of
   `.ct2027`. Selectors Level 4 counts a `:has()` argument's specificity as
   that of the most specific selector inside it, so `:has(#meeting-form)`
   contributes ID-level specificity — `body:has(#meeting-form)` outranks the
   plain-class base `.ct2027{background:var(--ct-bg)}` rule above and wins,
   silently dropping ONLY this page back to a bare (transparent, so
   effectively browser-default white) body. Invisible under 2026 (already a
   light/paper theme, so "no background" looks the same), but a confirmed,
   measured regression under 2027's dark theme: before this rule existed,
   `.ct-head p`'s computed contrast came back 1.00:1 (white text on a white
   fallback) in a live run. Re-declared with one extra class + the element
   type for specificity (0,1,1,1) vs the legacy rule's (0,1,0,1) — no
   !important needed, and none of landing2025's own CSS is touched. */
body.ct2027:has(#meeting-form) { background: var(--ct-bg); }

/* #loader is a sibling of .ct-booking (a direct child of #ct-main, rendered
   before it), not a descendant — scoped by its own unique id, matching how
   the 2026 precedent scoped it too. Opaque, not translucent: a translucent
   scrim would need a raw rgba() alpha value duplicating --ct-bg's hex
   outside the token block, so this reuses --ct-bg-alt as a plain opaque
   fill instead — same visual job (dim the page while the ajax submit is in
   flight), zero raw hex. */
.ct2027 #loader {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: var(--ct-bg-alt);
}
.ct2027 #loader .spinner {
  width: 52px; height: 52px;
  border: 5px solid var(--ct-line);
  border-top-color: var(--ct-cyan);
  border-radius: 50%;
  animation: ctBookingSpin 1s linear infinite;
}
@keyframes ctBookingSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ct2027 #loader .spinner { animation: none; }
}

/* Hero: `.ct-hero` is the HOMEPAGE's two-column live-mockup class (see the
   "SECTION — hero" block earlier in this file); this page's `.ct-hero` has
   none of the `.ct-hero__*` BEM children that layout depends on, so the
   base rule just contributes a plain section padding, harmlessly. Rescoped
   here, single-column and centred, on the same footing as `.ct-page-hero`
   (the inner-page pattern used by Why Us/Facts/etc.) rather than
   redeclaring `.ct-page-hero` itself, since the view emits `.ct-hero`, not
   `.ct-page-hero`, and this task doesn't touch the view. */
.ct2027 .ct-booking .ct-hero { padding: 72px 0 56px; text-align: center; }
.ct2027 .ct-booking .ct-head { max-width: 740px; margin: 0 auto; }
.ct2027 .ct-booking .ct-head .ct-eyebrow { margin-bottom: 22px; }
.ct2027 .ct-booking .ct-head h1 {
  margin: 0 0 20px;
  font-size: clamp(34px, 4.2vw, 52px);
  font-weight: 800; line-height: 1.14; letter-spacing: -0.03em;
}
.ct2027 .ct-booking .ct-head p {
  margin: 0 auto 8px; max-width: 56ch;
  font-size: 17.5px; line-height: 1.72; color: var(--ct-text-mut);
}

/* .ct-hl: an inline "highlight" span the view already carries (the
   `--hl:var(--hi-yellow)` custom property it sets inline is never consumed
   here or in the 2026 precedent it was written against — dead, harmless,
   left alone). Given the same gradient-clip-text treatment as the site's
   existing `.ct-grad-text`, since that's what this class visually promises
   ("highlight" a word) and it otherwise renders as plain unstyled text. */
.ct2027 .ct-booking .ct-hl { color: var(--ct-cyan); font-style: normal; }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .ct2027 .ct-booking .ct-hl {
    background: var(--ct-grad);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}

/* Pillars: three glass cards (Universities/Schools/Tutors). The view gives
   each `<article>` only `class="ct-card"` — not the second `ct-glass` class
   every other card composes with elsewhere in this file (Why Us's
   `.ct-why-card ct-glass`, etc.) — so the glass surface (background/border/
   radius/blur) is redeclared directly here rather than composed, since the
   view cannot be edited to add the second class. */
.ct2027 .ct-booking .ct-pillars {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 22px;
  margin-top: 44px;
}
.ct2027 .ct-booking .ct-card {
  flex: 1 1 250px; max-width: 300px;
  padding: 28px 24px;
  display: flex; flex-direction: column; gap: 14px;
  text-align: left;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-lg);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ct2027 .ct-booking .ct-card:hover { transform: translateY(-4px); border-color: var(--ct-line-strong); box-shadow: var(--ct-shadow); }
.ct2027 .ct-booking .ct-card__ico {
  width: 46px; height: 46px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--ct-r-md);
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  color: var(--ct-cyan);
  font-size: 18px;
}
.ct2027 .ct-booking .ct-card h3 { margin: 0; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; color: var(--ct-text); }
.ct2027 .ct-booking .ct-card p { margin: 0; color: var(--ct-text-mut); font-size: 14px; line-height: 1.65; }

/* Sections: the view's two plain `<section class="ct-section">` elements
   (one also carries `ct-section--sand`) have no padding of their own
   anywhere in this file. `:not(.ct-section--sand)` is the "Need immediate
   assistance" section — the only .ct-section on this page WITHOUT --sand,
   so it needs no extra selector to isolate it. */
.ct2027 .ct-booking .ct-section { position: relative; padding: 12px 0 100px; }
/* Schedule-form section: a subtly distinct panel replacing 2026's cream
   "sand" gradient (`linear-gradient(180deg, #faf6ff, #f3ecff)`) with the
   dark-theme equivalent "this section is a different surface" cue used
   elsewhere in this file (`.ct-footer`, `.ct-nav__topbar`: `--ct-bg-alt`). */
.ct2027 .ct-booking .ct-section--sand {
  background: var(--ct-bg-alt);
  border-block: 1px solid var(--ct-line);
}
/* "Need immediate assistance" section: this page's one deliberate maple
   moment (see "book a meeting maple restraint" in qa/ct2027/check.js),
   mirroring the sitewide final-CTA wash (`--ct-wash-3`) other pages apply
   directly to `.ct-cta` itself. Applied to the outer `.ct-section` here
   instead, because — unlike every other page — this view has no separate
   `.ct-cta__inner` wrapper for the wash + card to layer onto; `.ct-cta`
   below plays the "card" role instead, so it needs to stay a legible
   opaque surface rather than getting the wash directly. */
.ct2027 .ct-booking .ct-section:not(.ct-section--sand) {
  padding: 100px 0 140px;
  background: radial-gradient(ellipse at 50% 0%, var(--ct-wash-3) 0%, transparent 70%);
}

/* Form panel: composes the same "static, permanently-elevated card" look as
   `.ct-glass--lift` (contact form precedent) — border-strong + shadow, not
   just hover-lift — again redeclared rather than composed since the view
   gives this element only `class="ct-form-panel meeting-schedule"` /
   `class="ct-form-panel thankyou-section"`. */
.ct2027 .ct-booking .ct-form-panel {
  background: var(--ct-surface);
  border: 1px solid var(--ct-line-strong);
  border-radius: var(--ct-r-lg);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
  box-shadow: var(--ct-shadow);
  padding: clamp(28px, 4vw, 48px);
}
.ct2027 .ct-booking .ct-form-panel .ct-form-title {
  margin: 0 0 28px;
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; text-align: center;
}
.ct2027 .ct-booking .ct-thanks-ico {
  width: 72px; height: 72px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 1.9rem; color: var(--ct-on-accent);
  background: var(--ct-grad);
  margin: 0 auto 20px;
}

/* Field chrome — same discipline as `#contact-form` (Task 11): targets the
   live TActiveForm's own stable `#meeting-form` id and the standard Yii2
   bootstrap3 ActiveField output (form-group/control-label/form-control/
   help-block), confirmed byte-for-byte against a live curl capture of this
   exact page rather than assumed from the model source. */
.ct2027 #meeting-form .form-group { margin-bottom: 18px; }
.ct2027 #meeting-form .control-label {
  display: block; margin-bottom: 8px;
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ct-text-mut);
}
.ct2027 #meeting-form .form-control {
  display: block; width: 100%; padding: 13px 16px;
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-sm);
  color: var(--ct-text);
  font: inherit; font-size: 14.5px;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.ct2027 #meeting-form .form-control::placeholder { color: var(--ct-text-dim); }
.ct2027 #meeting-form .form-control:focus { outline: none; border-color: var(--ct-cyan); box-shadow: 0 0 0 3px var(--ct-glow); }
.ct2027 #meeting-form textarea.form-control { min-height: 96px; resize: vertical; }
.ct2027 #meeting-form .help-block { margin: 6px 0 0; font-size: 12.5px; font-weight: 600; color: var(--ct-warn); min-height: 0; }
.ct2027 #meeting-form .help-block:empty { display: none; }

/* Date field: kartik DatePicker (TYPE_COMPONENT_PREPEND) renders Bootstrap
   4's real `.input-group-prepend > .input-group-text` shape on THIS site
   (confirmed via a live curl capture) — not `.input-group-addon`
   (Bootstrap 3), which is what the 2026 precedent's own CSS actually
   targets. Layout (flex/radius joins) stays Bootstrap's; only colour/theme
   is touched here. */
.ct2027 #meeting-form .input-group-text {
  display: flex; align-items: center;
  background: var(--ct-surface-2);
  border: 1px solid var(--ct-line);
  color: var(--ct-text-mut);
}
.ct2027 #meeting-form .kv-date-remove:hover { color: var(--ct-cyan); cursor: pointer; }

/* Timezone field: krajee Select2, theme "krajee-bs3" on this install
   (confirmed live — `s2options.themeCss` = `.select2-container--krajee-bs3`
   — a different variant than the plain "krajee" the 2026 precedent's CSS
   targets). Only the trigger box is themed, same scope boundary as every
   other third-party overlay on this site (contact form's reCAPTCHA, the
   gallery's Fancybox): the dropdown RESULTS panel is select2's own overlay,
   appended outside this form's subtree, and is left at its default chrome
   — consistent with the fact that 2026 never reskinned it either. Two
   selectors (bs3 + plain) covered defensively since the shipped krajee
   asset version isn't pinned by this task. */
.ct2027 #meeting-form .select2-container--krajee-bs3 .select2-selection,
.ct2027 #meeting-form .select2-container--krajee .select2-selection,
.ct2027 #meeting-form .select2-selection {
  border: 1.5px solid var(--ct-line) !important;
  border-radius: var(--ct-r-sm) !important;
  background: var(--ct-surface-2) !important;
  color: var(--ct-text) !important;
  min-height: 44px;
}
/* !important on this block AND the two rules below (__rendered /
   __placeholder): krajee's own bundled select2-krajee-bs3.min.css sets
   color on all three of these elements at the same specificity (two
   classes) and loads BEFORE this file; load order alone is not a safe
   enough guarantee to skip !important for a vendored asset this task
   doesn't control the version of. */
.ct2027 #meeting-form .select2-selection__rendered { color: inherit !important; }
/* --ct-text-mut, not --ct-text-dim: measured against this element's real
   composited background (.select2-selection's --ct-surface-2 overlay, not
   flat --ct-bg), --ct-text-dim scored 3.95:1 in dark theme -- under the
   4.5:1 floor this suite holds placeholders to same as body copy. --ct-
   text-mut passes in both themes on the same composited surface and stays
   visually distinct from an entered value, which renders at full-strength
   --ct-text via .select2-selection__rendered above. See "book a meeting
   contrast" in check.js for the pinned assertion. */
.ct2027 #meeting-form .select2-selection__placeholder { color: var(--ct-text-mut) !important; }

/* "No Slots Available" checkbox: the view's plain Yii2 checkbox() output
   (`<label><input type="checkbox">...</label>`, no `.control-label` class),
   themed natively via accent-color rather than fighting it with a custom
   checkbox — same "style what exists" boundary this task's brief asks for. */
.ct2027 #meeting-form .field-meetingschedule-no_slots_available label {
  display: inline; font-weight: 500; color: var(--ct-text);
}
.ct2027 #meeting-form .field-meetingschedule-no_slots_available input[type="checkbox"] {
  accent-color: var(--ct-cyan); margin-right: 6px;
}
/* Native <input type=time> (the "no slots available" fallback field) picks
   up .form-control chrome above for free; only its browser-native icon
   needs a theme-aware fix — WebKit's default clock glyph renders near-black
   and is close to invisible on the dark surface behind it. */
:root[data-ct-theme="dark"] .ct2027 #meeting-form input[type="time"]::-webkit-calendar-picker-indicator { filter: invert(1); }

.ct2027 #slots-container h3 { font-size: 1.05rem; font-weight: 800; margin: 1rem 0 .6rem; color: var(--ct-text); }
.ct2027 #available-slots { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .5rem; }
.ct2027 #available-slots .btn { border-radius: var(--ct-r-md); padding: .5em .9em; font-weight: 600; border: 1.5px solid var(--ct-line); }
.ct2027 #available-slots .slot-button.btn-primary { background: var(--ct-surface-2); color: var(--ct-text); border-color: var(--ct-line); }
.ct2027 #available-slots .slot-button.btn-success { background: var(--ct-grad); color: var(--ct-on-accent); border-color: transparent; }
.ct2027 #available-slots .btn-secondary { background: var(--ct-surface); color: var(--ct-text-dim); opacity: .65; border-color: var(--ct-line); }
.ct2027 #available-slots .alert-info {
  background: var(--ct-surface-2); border: 1px solid var(--ct-line);
  color: var(--ct-text-mut); border-radius: var(--ct-r-md);
  padding: .6rem 1rem; margin: 0;
}
/* Bootstrap 4's `.text-success` utility ships its own colour at
   `!important`; `--ct-ok` is this file's token specifically for
   "verified/success text, contrast-safe on glass" (see the COMPONENTS
   header comment), so the transient "Selected: ..." confirmation uses it
   instead of Bootstrap's un-themed green. */
.ct2027 #meeting-form .text-success { color: var(--ct-ok) !important; }

.ct2027 [id="add-button"].btn-success {
  background: var(--ct-grad);
  border: 0; border-radius: 999px;
  padding: .95em 2.4em; font-weight: 700;
  color: var(--ct-on-accent);
  box-shadow: var(--ct-shadow-cta);
  transition: transform .3s ease, filter .25s ease;
}
.ct2027 [id="add-button"].btn-success:hover { filter: brightness(1.06); transform: translateY(-2px); }
.ct2027 [id="add-button"].btn-success:focus-visible { outline: 2px solid var(--ct-cyan); outline-offset: 3px; }

/* Final CTA card ("Need immediate assistance"). The view gives this element
   only `class="ct-cta ct-reveal"` — no `.ct-cta__inner` wrapper exists here
   the way it does on every other page (gallery/contact/why-us all wrap
   `.ct-cta__inner.ct-glass` inside a `.ct-cta` SECTION). On this page
   `.ct-cta` is a DIV playing both roles at once, so it gets the card
   treatment directly: max-width, centring, and an opaque glass surface
   (the section wash above provides the "second layer" the wrapper would
   have). */
.ct2027 .ct-booking .ct-cta {
  max-width: 720px; margin: 0 auto;
  padding: 56px 48px;
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  text-align: center;
  background: var(--ct-surface);
  border: 1px solid var(--ct-line);
  border-radius: var(--ct-r-lg);
  backdrop-filter: blur(var(--ct-blur));
  -webkit-backdrop-filter: blur(var(--ct-blur));
}
.ct2027 .ct-booking .ct-cta h2 {
  margin: 0;
  font-size: clamp(28px, 3.2vw, 42px);
  font-weight: 800; line-height: 1.16; letter-spacing: -0.03em;
}
.ct2027 .ct-booking .ct-cta p { margin: 0; max-width: 50ch; }

/* Known trap, pressure-tested by "book a meeting contrast" in check.js:
   the untouched view bakes in literal inline `style="color:#fff"` (both
   mailto/gallery links, and the "Enterprise accounts:" lead-in) and
   `style="...color:rgba(245,239,227,.72)..."` (the disclaimer paragraph) —
   written for a solid dark/purple 2026 panel. In 2027's LIGHT theme,
   `--ct-surface` (the card background set two rules above) is OPAQUE WHITE
   (`#ffffff`), so that literal white text is invisible there — confirmed by
   a live 1.00:1 computed-contrast reading before this rule existed. Inline
   `style=""` out-specifies any external class selector regardless of
   `.ct-booking`/`.ct2027` nesting, and the view is off-limits, so
   !important is the only lever available. Scoped to exactly the elements
   the trap sits on. Original relative hierarchy preserved: the bold
   lead-in still reads brighter than the muted disclaimer around it. */
.ct2027 .ct-booking .ct-cta a { color: var(--ct-text) !important; }
.ct2027 .ct-booking .ct-cta a:hover,
.ct2027 .ct-booking .ct-cta a:focus-visible { color: var(--ct-cyan) !important; }
.ct2027 .ct-booking .ct-cta a:focus-visible { outline: 2px solid var(--ct-cyan); outline-offset: 3px; border-radius: 4px; }
.ct2027 .ct-booking .ct-cta p:last-of-type { color: var(--ct-text-mut) !important; }
.ct2027 .ct-booking .ct-cta p:last-of-type strong { color: var(--ct-text) !important; }

@media (max-width: 640px) {
  .ct2027 .ct-booking .ct-hero { padding: 56px 0 40px; }
  .ct2027 .ct-booking .ct-form-panel { padding: 28px 22px; }
  .ct2027 .ct-booking .ct-section:not(.ct-section--sand) { padding: 80px 0 100px; }
  .ct2027 .ct-booking .ct-cta { padding: 40px 26px; }
}
@media (max-width: 360px) {
  /* Google's reCAPTCHA checkbox widget has a fixed ~304px intrinsic width
     (same fix as Task 11's contact form, same widget). */
  .ct2027 #meeting-form .g-recaptcha { transform: scale(0.87); transform-origin: 0 0; }
}

/* NOTE for the diff reviewer: this page's own mobile counter-rule (needed
   because `.ct-booking` breaks the RESPONSIVE + LEGACY-COUNTER-BLOCK's
   `#ct-main > section` child combinator at the very end of this file) is
   added directly into that existing block, not here — see the edit there
   and its own comment for why. */

/* ==========================================================================
   SECTION — module browser (the searchable "browse every module" dialog)

   .ct-mods-modal__panel composes .ct-glass in the markup, so this block must
   NOT re-declare background / border / border-radius / backdrop-filter —
   .ct-glass is the single source of that chrome. Only layout, sizing and
   the dialog's own behaviour live here.
   ========================================================================== */
.ct2027 .ct-cloud__more { margin-top: 34px; text-align: center; }

.ct2027 .ct-mods-modal {
  position: fixed; inset: 0; z-index: 960;
  display: none; align-items: center; justify-content: center;
  padding: 24px;
}
.ct2027 .ct-mods-modal.is-open { display: flex; }
.ct2027 .ct-mods-modal__backdrop {
  position: absolute; inset: 0;
  background: rgba(2, 6, 23, .82);   /* scrim, not a brand surface */
  backdrop-filter: blur(4px);
}
.ct2027 .ct-mods-modal__panel {
  position: relative; z-index: 1;
  width: min(1040px, 100%); max-height: min(86vh, 800px);
  display: flex; flex-direction: column;
  padding: 26px 26px 8px;
  box-shadow: var(--ct-shadow);
}
.ct2027 .ct-mods-modal__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 18px;
}
.ct2027 .ct-mods-modal__title { font-size: 26px; letter-spacing: -.02em; margin: 0; }
.ct2027 .ct-mods-modal__sub { color: var(--ct-text-mut); font-size: 14px; margin: 6px 0 0; }
.ct2027 .ct-mods-modal__close {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  color: var(--ct-text); cursor: pointer;
  transition: border-color .2s ease, background .2s ease;
}
.ct2027 .ct-mods-modal__close:hover { border-color: var(--ct-cyan); background: var(--ct-surface-2); }

.ct2027 .ct-mods-modal__search {
  position: relative; margin-top: 20px;
}
.ct2027 .ct-mods-modal__search i {
  position: absolute; left: 15px; top: 50%; transform: translateY(-50%);
  color: var(--ct-text-mut); font-size: 14px; pointer-events: none;
}
.ct2027 .ct-mods-modal__search input {
  width: 100%; padding: 13px 15px 13px 42px;
  border-radius: var(--ct-r-md);
  background: var(--ct-surface-2); border: 1px solid var(--ct-line);
  color: var(--ct-text); font-size: 15px; font-family: inherit;
}
.ct2027 .ct-mods-modal__search input::placeholder { color: var(--ct-text-mut); }
.ct2027 .ct-mods-modal__search input:focus {
  outline: none; border-color: var(--ct-cyan);
}
.ct2027 .ct-mods-modal__count {
  color: var(--ct-text-mut); font-size: 12.5px; margin: 12px 2px 0;
  letter-spacing: .04em; text-transform: uppercase;
}

.ct2027 .ct-mods-modal__body {
  overflow-y: auto; overflow-x: hidden;
  margin-top: 16px; padding: 4px 4px 24px;
}
.ct2027 .ct-mods-group + .ct-mods-group { margin-top: 30px; }
.ct2027 .ct-mods-group__cat {
  font-size: 12px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ct-cyan); margin: 0 0 14px;
}
.ct2027 .ct-mods-group__row { display: flex; flex-wrap: wrap; gap: 10px 9px; }
.ct2027 .ct-mods-modal__empty {
  color: var(--ct-text-mut); font-size: 15px; text-align: center; padding: 30px 0;
}

@media (max-width: 600px) {
  .ct2027 .ct-mods-modal { padding: 14px; }
  .ct2027 .ct-mods-modal__panel { padding: 20px 18px 6px; max-height: 88vh; }
  .ct2027 .ct-mods-modal__title { font-size: 22px; }
}

/* Both modals live inside #ct-main, which is its own stacking context
   (position:relative; z-index:1). #back2Top is a SIBLING of #ct-main at
   z-index 949, so it paints above anything inside it no matter how high the
   modal's own z-index goes — raising the modal cannot win this. Hiding the
   scroll button while a dialog is open is the correct fix anyway: a floating
   "back to top" over a modal is meaningless. Set by both open()/close().
   !important because the base #back2Top.show rule also sets display. */
.ct2027.ct-modal-open #back2Top { display: none !important; }

/* ==========================================================================
   SECTION — module detail (hover line + per-module dialog with CSS mock-ups)

   .ct-mod-detail__panel composes .ct-glass in the markup; this block must not
   re-declare background / border / border-radius / backdrop-filter.
   ========================================================================== */
.ct2027 .ct-chip--detail {
  position: relative; cursor: pointer;
  font: inherit; font-size: 12.5px;
  border: 1px solid var(--ct-line); background: var(--ct-surface);
  color: var(--ct-text-mut);
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.ct2027 .ct-chip--detail { padding: 8px 14px; }
.ct2027 .ct-chip--detail:hover,
.ct2027 .ct-chip--detail:focus-visible {
  border-color: var(--ct-cyan); color: var(--ct-text); background: var(--ct-surface-2);
  transform: translateY(-1px);
}

/* One shared tooltip, anchored to the PANEL rather than the scrolling list.
   A tooltip inside the scroll container widened it and produced a horizontal
   scrollbar — `overflow-y:auto` forces `overflow-x` to compute as auto, so the
   overflow became scrollable rather than simply invisible. Positioned by JS and
   clamped to the panel, it cannot overflow anything. */
.ct2027 .ct-mods-tip {
  position: absolute; z-index: 6; max-width: 300px;
  padding: 10px 13px; border-radius: var(--ct-r-md);
  background: var(--ct-bg-alt); border: 1px solid var(--ct-line-strong);
  color: var(--ct-text); font-size: 12.5px; line-height: 1.55; text-align: left;
  box-shadow: var(--ct-shadow);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .16s ease;
}
.ct2027 .ct-mods-tip.is-on { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .ct2027 .ct-mods-tip { transition: none; } }

.ct2027 .ct-mods-modal__meta {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin: 13px 2px 0;
}
.ct2027 .ct-mods-modal__hint { margin: 0; font-size: 12px; color: var(--ct-text-mut); }

.ct2027 .ct-mod-detail {
  position: fixed; inset: 0; z-index: 970;
  display: none; align-items: center; justify-content: center; padding: 24px;
}
.ct2027 .ct-mod-detail.is-open { display: flex; }
.ct2027 .ct-mod-detail__backdrop {
  position: absolute; inset: 0;
  background: rgba(2, 6, 23, .86);   /* scrim */
  backdrop-filter: blur(5px);
}
.ct2027 .ct-mod-detail__panel {
  position: relative; z-index: 1;
  width: min(1000px, 100%); max-height: min(88vh, 820px);
  overflow-y: auto; padding: 34px 34px 30px;
  box-shadow: var(--ct-shadow);
}
.ct2027 .ct-mod-detail__close {
  position: absolute; top: 18px; right: 18px;
  width: 38px; height: 38px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  color: var(--ct-text); cursor: pointer;
  transition: border-color .2s ease, background .2s ease;
}
.ct2027 .ct-mod-detail__close:hover { border-color: var(--ct-cyan); background: var(--ct-surface-2); }
.ct2027 .ct-mod-detail__title { font-size: 32px; letter-spacing: -.025em; margin: 14px 0 12px; }
.ct2027 .ct-mod-detail__grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 30px; align-items: start; margin-top: 26px;
}
.ct2027 .ct-mod-detail__points { list-style: none; margin: 0; padding: 0; display: grid; gap: 17px; }
.ct2027 .ct-mod-detail__points li { display: flex; gap: 11px; align-items: flex-start; }
.ct2027 .ct-mod-detail__points i { color: var(--ct-cyan); font-size: 13px; margin-top: 4px; }
.ct2027 .ct-mod-detail__points strong { display: block; color: var(--ct-text); font-size: 14.5px; margin-bottom: 3px; }
.ct2027 .ct-mod-detail__points span { color: var(--ct-text-mut); font-size: 13.5px; line-height: 1.6; }
.ct2027 .ct-mod-detail__custom {
  margin: 28px 0 0; padding: 15px 17px; border-radius: var(--ct-r-md);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  color: var(--ct-text-mut); font-size: 13.5px; line-height: 1.6;
}
.ct2027 .ct-mod-detail__custom i { color: var(--ct-cyan); margin-right: 9px; }
.ct2027 .ct-mod-detail__custom strong { color: var(--ct-text); }

/* ----- mock-up internals ----- */
.ct2027 .ct-mk-course__title { font-weight: 700; font-size: 15px; margin-bottom: 13px; display: flex; align-items: center; gap: 9px; }
.ct2027 .ct-mk-lesson {
  display: flex; align-items: center; gap: 11px; padding: 10px 12px; margin-bottom: 7px;
  border-radius: var(--ct-r-sm); background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px; color: var(--ct-text-mut);
}
.ct2027 .ct-mk-lesson span:nth-child(2) { flex: 1; color: var(--ct-text); }
.ct2027 .ct-mk-lesson__n {
  width: 21px; height: 21px; border-radius: 50%; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-surface-2); font-size: 11px; color: var(--ct-text-mut);
}
.ct2027 .ct-mk-lesson.is-now { border-color: var(--ct-cyan); }
.ct2027 .ct-mk-lesson.is-locked { opacity: .6; }
.ct2027 .ct-mk-done { color: var(--ct-cyan); font-weight: 700; }
.ct2027 .ct-mk-part { color: var(--ct-warn); font-weight: 700; }
.ct2027 .ct-mk-soon, .ct2027 .ct-mk-pc { color: var(--ct-text-mut); }
.ct2027 .ct-mk-bar { height: 6px; border-radius: 99px; background: var(--ct-surface-2); margin: 14px 0 10px; overflow: hidden; }
.ct2027 .ct-mk-bar i { display: block; height: 100%; background: var(--ct-grad); }
.ct2027 .ct-mk-foot { font-size: 12px; color: var(--ct-text-mut); }
.ct2027 .ct-mk-foot b { color: var(--ct-text); }

.ct2027 .ct-mk-q { font-size: 14px; color: var(--ct-text); margin-bottom: 13px; }
.ct2027 .ct-mk-opt {
  padding: 10px 12px; margin-bottom: 7px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px; color: var(--ct-text-mut);
  display: flex; justify-content: space-between; align-items: center;
}
.ct2027 .ct-mk-opt.is-right { border-color: var(--ct-cyan); color: var(--ct-text); }
.ct2027 .ct-mk-opt.is-right i { color: var(--ct-cyan); }
.ct2027 .ct-mk-score {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 13px; padding-top: 12px; border-top: 1px solid var(--ct-line);
  font-size: 12px; color: var(--ct-text-mut);
}
.ct2027 .ct-mk-score b { color: var(--ct-cyan); }

.ct2027 .ct-mk-att {
  display: flex; align-items: center; gap: 12px; padding: 10px 12px; margin-bottom: 7px;
  border-radius: var(--ct-r-sm); background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px; color: var(--ct-text);
}
.ct2027 .ct-mk-att span:first-child { flex: 1; }
.ct2027 .ct-mk-att.is-flag { border-color: var(--ct-warn); }
.ct2027 .ct-mk-p { color: var(--ct-cyan); font-weight: 700; }
.ct2027 .ct-mk-l { color: var(--ct-warn); font-weight: 700; }
.ct2027 .ct-mk-a { color: var(--ct-danger); font-weight: 700; }
.ct2027 .ct-mk-pc.is-low { color: var(--ct-warn); font-weight: 700; }

.ct2027 .ct-mk-doc {
  padding: 15px 16px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px; color: var(--ct-text-mut); line-height: 1.7;
}
.ct2027 .ct-mk-doc p { margin: 0 0 9px; }
.ct2027 .ct-mk-doc__h { font-weight: 700; color: var(--ct-text); font-size: 14px; margin-bottom: 11px; }
.ct2027 .ct-mk-f {
  color: var(--ct-cyan); background: var(--ct-surface-2);
  padding: 1px 6px; border-radius: 5px; font-size: 11.5px;
}
.ct2027 .ct-mk-sign {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--ct-line-strong);
  font-size: 11.5px;
}
.ct2027 .ct-mk-sign i { color: var(--ct-cyan); font-size: 15px; }
.ct2027 .ct-mk-pack { display: flex; flex-wrap: wrap; gap: 7px; margin: 12px 0 11px; }
.ct2027 .ct-mk-mail { margin-bottom: 11px; }
.ct2027 .ct-mk-mail__r {
  display: flex; gap: 11px; padding: 8px 12px; margin-bottom: 6px;
  border-radius: var(--ct-r-sm); background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px;
}
.ct2027 .ct-mk-mail__r span { color: var(--ct-text-mut); min-width: 52px; }
.ct2027 .ct-mk-mail__r b { color: var(--ct-text); font-weight: 600; }

@media (max-width: 860px) {
  .ct2027 .ct-mod-detail__grid { grid-template-columns: 1fr; gap: 24px; }
  .ct2027 .ct-mod-detail__panel { padding: 26px 20px 22px; }
  .ct2027 .ct-mod-detail__title { font-size: 25px; }
  .ct2027 .ct-chip__tip { max-width: 220px; }
}

/* ----- mock-up library: additional types (grid, stat, chat, table, media,
   form, cal, card) used by the full module catalogue ----- */
.ct2027 .ct-mods-group__note {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 0 0 12px; padding: 11px 13px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  color: var(--ct-text-mut); font-size: 12.5px; line-height: 1.6;
}
.ct2027 .ct-mods-group__note i { color: var(--ct-cyan); margin-top: 3px; }
.ct2027 .ct-mods-group__note strong { color: var(--ct-text); }

.ct2027 .ct-mk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ct2027 .ct-mk-gcard {
  padding: 11px 12px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
}
.ct2027 .ct-mk-gcard b { font-size: 13px; color: var(--ct-text); }

.ct2027 .ct-mk-kpis { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 12px; }
.ct2027 .ct-mk-kpi {
  padding: 11px 12px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
}
.ct2027 .ct-mk-kpi b { display: block; font-size: 19px; letter-spacing: -.02em; color: var(--ct-text); }
.ct2027 .ct-mk-kpi s { display: block; text-decoration: none; font-size: 10.5px; color: var(--ct-text-mut); margin-top: 2px; }
.ct2027 .ct-mk-spark { display: flex; align-items: flex-end; gap: 5px; height: 54px; }
.ct2027 .ct-mk-spark i { flex: 1; border-radius: 4px 4px 0 0; background: var(--ct-grad); }

.ct2027 .ct-mk-msg {
  padding: 9px 12px; margin-bottom: 7px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12.5px; max-width: 88%;
}
.ct2027 .ct-mk-msg.is-me { margin-left: auto; border-color: var(--ct-cyan); }
.ct2027 .ct-mk-msg__who { display: block; font-size: 10.5px; color: var(--ct-text-mut); margin-bottom: 3px; }
.ct2027 .ct-mk-msg__t { color: var(--ct-text); }

.ct2027 .ct-mk-table { display: grid; gap: 6px; }
.ct2027 .ct-mk-tr {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 10px;
  padding: 9px 12px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
  font-size: 12px; color: var(--ct-text);
}
.ct2027 .ct-mk-tr.is-head { background: var(--ct-surface-2); color: var(--ct-text-mut); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; }

.ct2027 .ct-mk-player {
  display: flex; align-items: center; gap: 11px; padding: 16px 14px; margin-bottom: 11px;
  border-radius: var(--ct-r-sm); background: var(--ct-surface-2); border: 1px solid var(--ct-line);
  font-size: 12.5px; color: var(--ct-text);
}
.ct2027 .ct-mk-player i {
  width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ct-grad); color: var(--ct-on-accent); font-size: 11px;
}

.ct2027 .ct-mk-field {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 10px 12px; margin-bottom: 6px; border-radius: var(--ct-r-sm);
  background: var(--ct-surface); border: 1px solid var(--ct-line); font-size: 12.5px;
}
.ct2027 .ct-mk-field span { color: var(--ct-text-mut); }
.ct2027 .ct-mk-field b { color: var(--ct-text); font-weight: 600; }

.ct2027 .ct-mk-week { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
.ct2027 .ct-mk-day {
  padding: 12px 8px; border-radius: var(--ct-r-sm); text-align: center;
  background: var(--ct-surface); border: 1px solid var(--ct-line);
}
.ct2027 .ct-mk-day.is-on { border-color: var(--ct-cyan); background: var(--ct-surface-2); }
.ct2027 .ct-mk-day s { display: block; text-decoration: none; font-size: 10.5px; color: var(--ct-text-mut); }
.ct2027 .ct-mk-day b { display: block; font-size: 13px; color: var(--ct-text); margin-top: 3px; }

.ct2027 .ct-mk-idcard {
  display: flex; gap: 14px; padding: 15px; border-radius: var(--ct-r-md);
  background: var(--ct-surface); border: 1px solid var(--ct-line);
}
.ct2027 .ct-mk-idcard__ph {
  width: 62px; height: 74px; flex: 0 0 auto; border-radius: var(--ct-r-sm);
  display: flex; align-items: center; justify-content: center;
  background: var(--ct-surface-2); border: 1px solid var(--ct-line);
  color: var(--ct-text-mut); font-size: 22px;
}
.ct2027 .ct-mk-idcard__b { display: grid; gap: 7px; align-content: center; }
.ct2027 .ct-mk-idcard__b s { display: block; text-decoration: none; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--ct-text-mut); }
.ct2027 .ct-mk-idcard__b b { font-size: 13px; color: var(--ct-text); }

@media (max-width: 860px) {
  .ct2027 .ct-mk-grid, .ct2027 .ct-mk-kpis { grid-template-columns: 1fr; }
  .ct2027 .ct-mk-week { grid-template-columns: repeat(3, 1fr); }
}

/* ----- Book a Meeting: field alignment -------------------------------------
   The date picker renders as a BS4 .input-group (two icon buttons + input).
   Our .form-control sets width:100%, which in a flex input-group leaves no
   room beside the buttons, so the group wrapped and the input dropped onto a
   second line — taller than, and out of line with, every other field.
   BS4's own contract is flex:1 1 auto / width:1% on the input; restore it.
   ------------------------------------------------------------------------- */
.ct2027 #meeting-form .input-group { flex-wrap: nowrap; }
.ct2027 #meeting-form .input-group > .form-control {
  flex: 1 1 auto; width: 1%; min-width: 0;
}
.ct2027 #meeting-form .input-group > .input-group-prepend > .btn,
.ct2027 #meeting-form .input-group > .input-group-append > .btn { height: 54px; }

/* Krajee's select2 renders 44px while every other control is 54px, so the
   timezone field sat 10px short of its row. Match the row. */
.ct2027 #meeting-form .select2-container .select2-selection { height: 54px; }
.ct2027 #meeting-form .select2-container .select2-selection__rendered { line-height: 52px; }
.ct2027 #meeting-form .select2-container .select2-selection__arrow { height: 52px; }

/* The submit button is centred; the captcha above it was left-aligned, which
   read as a broken column. Centre it to match. */
.ct2027 #meeting-form .g-recaptcha,
.ct2027 #meeting-form .field-meetingschedule-recaptcha { display: flex; justify-content: center; }

/* ==========================================================================
   RESPONSIVE + LEGACY COUNTER-BLOCK
   The legacy styles.css bundle (LandingPageAsset — still required for the
   Bootstrap login modal and the growl flash) ships a @media (max-width:767px)
   containing `section { padding: 25px 0 !important }` and
   `h2 { font-size: 25px !important }`. Those collapse every section and
   flatten every heading. The rules below counter them at equal weight.

   `:first-of-type` only has to restore a normal top gap: #ct-main already
   reserves the whole fixed-nav height via --ct-nav-h, so this value is pure
   breathing room between the nav and the first heading. It used to be 150px,
   which STACKED on that reserved height and produced ~220px of dead space at
   390px.

   DO NOT put a nested comment inside this block. CSS comments do not nest; a
   stray inner close-marker ends the block early and error recovery silently
   swallows every following rule. Verify with qa/ct2027/css-balance.js.
   ========================================================================== */
@media (max-width: 767px) {
  .ct2027 #ct-main > section { padding: 64px 0 !important; }
  .ct2027 #ct-main > section:first-of-type { padding-top: 36px !important; }
  .ct2027 h2 { font-size: 30px !important; }
  .ct2027 .ct-container { padding: 0 18px; }
  /* Task 12 (/demo/meeting-schedule/book-meeting) addendum: that page's view
     wraps its three <section>s in an extra `.ct-booking` div, so they are
     NOT direct children of #ct-main and the child-combinator rule two lines
     up cannot reach them -- without this they'd fall through to
     landing2025's bare `section, section.min { padding: 25px 0 !important }`
     uncountered, while every other page gets 64px/36px. Same values,
     reached through one more level of descendant nesting instead of a
     direct child. See qa/ct2027/check.js "book a meeting composition". */
  .ct2027 .ct-booking section { padding: 64px 0 !important; }
  .ct2027 .ct-booking section:first-of-type { padding-top: 36px !important; }
}
@media (max-width: 600px) {
  .ct2027 .ct-nav__actions .ct-btn { display: none; }
}
@media (max-width: 480px) {
  .ct2027 .ct-hero__cta .ct-btn,
  .ct2027 .ct-cta__actions .ct-btn,
  /* Added by Task 10 — see the "pricing plans" section comment above for
     why this rule (not just the class) had to be introduced rather than
     merely preserved. */
  .ct2027 .ct-price-cta .ct-btn { width: 100%; justify-content: center; }
}
