/* =============================================================================
   dAIologue -- landing_v2 (Beta revamp)
   public/landing_v2.css -- the ONE stylesheet for both landing pages, the live
   homepage and the share-link beta page (both render public/landing/lv2/). Linked
   with asset_v(), so an edit reaches browsers without a hand-bumped ?v=. The beta
   page's fork, public/landing_v2/landing_v2.css, was archived 2026-09-19.

   Every selector is scoped under .lv2 -- this page also includes real
   partials from public/landing/ (try2.php, level_test2.php, fwp2.php,
   contact2.php, footer2.php) which style themselves via landing2.css and
   use their own class names (.lp-*, .lt-*). None of THIS file's rules
   should ever match bare tags outside .lv2, or they'd leak onto that
   reused markup. The documented exceptions are the id-keyed overlays, the
   body backdrop, and the #try/#level-test/#contact scroll-margin list --
   each is commented where it appears.

   ---------------------------------------------------------------------------
   RECONCILED 2026-09-14. Two versions of this file had diverged:

     * landing_v2.css  -- the original, plus a hero background image added in
                          a separate session.
     * landing_v2A.css -- the UI/UX unification pass (indexA typography,
                          sticky nav wrapper, tab groups, flow diagram,
                          guide cards, cookie + mic overlays).

   index.php is on the unification markup, so the original CSS left the page
   badly broken: the cookie and mic dialogs rendered as visible inline page
   content (their display:none lives here), the nav was not sticky, and both
   flow-diagram layouts drew at once.

   This file is the merge: unification work as the base, the hero image kept
   and promoted to a full-page backdrop, plus two fixes that existed in
   neither copy (the mobile nav, and the flow-diagram specificity fix).
   landing_v2A.css is now redundant.
   ============================================================================= */

.lv2 {
  --lv2-bg: #0b0e1a;
  --lv2-panel: #131a2b;
  --lv2-border: rgba(255,255,255,0.08);
  --lv2-text: #eef1fb;
  /* 2026-09-14: both brightened so body copy reads at full strength rather
     than looking washed out over the backdrop. Hierarchy is now carried by
     size and weight rather than by dimming the text. Old values were
     #b7c0dc / #7c88ac. */
  --lv2-text-2: #e4e8f6;
  --lv2-text-3: #c2cade;

  /* Bright edge on the hero's device frames, so they read as objects sitting
     on the photograph rather than dark shapes dissolving into it. */
  --lv2-frame-edge: rgba(226,232,246,0.85);
  --lv2-accent: #5865f2;
  --lv2-accent-dim: rgba(88,101,242,0.15);

  --lv2-skill-listen: #6366f1;
  --lv2-skill-respond: #10b981;
  --lv2-skill-phoneme: #f59e0b;
  --lv2-skill-pron: #f43f5e;
  --lv2-skill-fwp: #8b5cf6;

  /* 2026-09-14: the Fraunces headline serif was dropped in the unification
     pass -- indexA.php's system is DM Sans 800 for headings and DM Mono for
     labels, and running a third face here was the main thing making the two
     pages look like different products. --lv2-serif is kept as an alias
     rather than deleted so any rule still referencing it degrades to the
     unified stack instead of falling back to a browser default serif.
     NOTE: index.php no longer requests Fraunces, so a literal 'Fraunces'
     anywhere in this file now silently renders as Georgia. */
  --lv2-serif: 'DM Sans', system-ui, sans-serif;
  --lv2-mono: 'DM Mono', monospace;

  color: var(--lv2-text);
  line-height: 1.55;
}

/* =============================================================================
   PAGE BACKDROP
   The bookshelf image was on .lv2-hero only. Two changes, both requested:

   1. FULL PAGE, not just the hero. Done with fixed pseudo-elements on <body>
      rather than `background-attachment: fixed`, which iOS Safari ignores and
      which causes repaint jank on Android. The image therefore sits still
      while the page scrolls over it, at any viewport.

   2. LIGHTER SCRIM. The original overlay ran rgba(...,0.55) -> 0.96 over an
      image that was already dark, which is why it read as "muted until it
      disappears". The scrim is now ~0.30 -> 0.52 and is exposed as the two
      variables below -- tune those, not the gradient, to take the image up
      or down. Raise them if any text over the photo gets hard to read.

   .lv2 must stay transparent for any of this to show; it used to paint solid
   --lv2-bg, which would cover the backdrop completely.

   ASSET SIZES (optimised 2026-09-14). The master,
   large-collection-old-books-wooden-shelves-generated-by-ai.jpg, is 6067x3467
   and 8.56 MB -- 21 megapixels for something that sits behind a scrim and a
   set of glass panels. It now lives OUTSIDE the served tree, in
   /assets-source/ at the repo root (see the README there for why that is not
   web-reachable and how to regenerate). What this page loads are downscaled
   re-encodes made from it:

     bookshelf-bg-1920.webp   242 KB   1920x1097   (36x smaller)
     bookshelf-bg-1920.jpg    351 KB   1920x1097   (25x smaller)
     bookshelf-bg-1280.webp   133 KB   1280x731    (66x smaller)
     bookshelf-bg-1280.jpg    173 KB   1280x731    (51x smaller)

   1920 covers a full-HD viewport; 1280 is swapped in below 780px, where the
   backdrop is behind panels and the softness is invisible.
   ========================================================================== */
:root {
  /* Light on purpose. Legibility is now carried by the glass panels (see
     "CONTENT ON GLASS" below), not by drowning the photo -- so this only has
     to knock back the brightest parts of the image, mainly the window. */
  --lv2-scrim-top: rgba(11,14,26,0.28);
  --lv2-scrim-bottom: rgba(11,14,26,0.46);
}

/* To revert to hero-only: delete the body::before/::after block below and
   restore `.lv2 { background: var(--lv2-bg); }`. */

body::before,
body::after { content: ''; position: fixed; inset: 0; pointer-events: none; }

body::before {
  z-index: -2;
  /* Plain url() FIRST as the fallback -- browsers without image-set()/type()
     support (Safari < 17, older Chromium) fail to parse the next declaration
     and keep this one. Do not reorder these two lines. */
  /* 'assets/...' not '../assets/...': this stylesheet moved from
   public/landing_v2/ to public/ on 2026-09-15, so relative URLs now resolve
   from public/ directly. The old form would point at /assets/, which does not
   exist -- and a missing background image fails silently, leaving the page on
   its fallback colour with no console error worth noticing. */
background-image: url('assets/images/bookshelf-bg-1920.jpg');
  background-image: image-set(
    url('assets/images/bookshelf-bg-1920.webp') type('image/webp'),
    url('assets/images/bookshelf-bg-1920.jpg')  type('image/jpeg'));
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}
body::after {
  z-index: -1;
  background: linear-gradient(180deg, var(--lv2-scrim-top) 0%, var(--lv2-scrim-bottom) 100%);
}

/* Transparent so the backdrop above is visible through every .lv2 block.
   Revert to `background: var(--lv2-bg)` to go back to the flat dark page. */
.lv2 { background: transparent; }

/* The two reused partials painted their own opaque panels over the backdrop,
   which is why Try it and Level Test read as solid black bands. Cleared here
   rather than at source, because both sources are SHARED with index.php and
   indexA.php, which still want their solid surfaces:

     #try            -- landing2.css:669, `background: var(--lp-surface)` (#0f1220)
     #lt-section-wrap -- level-test.php:140, `background: var(--bg)` (#0d0f1a)

   This file is only loaded by landing_v2/index.php, so overriding here cannot
   reach the other landing pages. Another documented exception to the
   "never match outside .lv2" rule, alongside the scroll-margin list below.

   SPECIFICITY NOTE: level-test.php is require_once'd INSIDE the div it styles,
   so its <style> block sits in the body, AFTER this stylesheet's <link>. At
   equal specificity it would win on source order -- hence the two-id selector
   rather than a bare `#lt-section-wrap`. #try needs no such trick: landing2.css
   is linked before this file, so equal specificity already resolves our way.

   #try keeps its top/bottom borders -- they still read as section dividers. */
#try { background: transparent; border-top: none; border-bottom: none; }
#level-test #lt-section-wrap { background: transparent; }

/* =============================================================================
   CONTENT ON GLASS
   The photo has high LOCAL contrast (a bright window, pale book spines), so no
   single flat scrim keeps the image alive AND 14px body copy readable. Instead
   of compromising on one number, every READING surface gets its own translucent
   panel: text always sits on a controlled background, while the image stays
   vivid in the hero, the gutters, and the gaps between panels.

   The hero deliberately gets NO panel -- it is where the image should carry the
   page, and a 52px/800-weight headline holds up against it unaided. Its lede is
   handled by a local left-weighted scrim instead (see .lv2-hero::before).

   Panels are capped at --lp-max-w so the new .lv2 sections and the reused .lp-*
   partials line up edge to edge. Applying this to the partials means unscoped
   selectors -- the same documented exception as the background clearing above,
   and safe for the same reason: this file is loaded only by landing_v2.
   ========================================================================== */
:root {
  /* 0.72 -> 0.65: ~10% less opaque, so more of the backdrop reads through
     the panels. Lowering this trades legibility for image, which is why the
     text tokens above were brightened at the same time -- change them
     together, not one without the other. */
  --lv2-glass: rgba(13,17,30,0.65);
  --lv2-glass-border: rgba(255,255,255,0.09);
  --lv2-glass-radius: 20px;
  --lv2-glass-pad-y: 56px;
  --lv2-glass-pad-x: 40px;
}

/* #try, #level-test and the old #fwp-demo are NOT in this list any more --
   they now live inside the #try-it tab panels, which are already inside a
   glass panel. Giving them their own would nest a panel in a panel. */
.lv2 .lv2-section > .lv2-wrap,
#contact > .lp-section {
  background: var(--lv2-glass);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
  backdrop-filter: blur(10px) saturate(115%);
  border: 1px solid var(--lv2-glass-border);
  border-radius: var(--lv2-glass-radius);
  padding: var(--lv2-glass-pad-y) var(--lv2-glass-pad-x);
  box-shadow: 0 18px 50px rgba(0,0,0,0.35);
}

/* Section spacing/borders are set on the base `.lv2 .lv2-section` rule further
   down, NOT overridden here -- that rule comes later in the file and would win
   at equal specificity. Same trap as the nav breakpoints. */

/* .lp-section carries `max-width: var(--lp-max-w); margin: 0 auto` from
   landing2.css, so the partials are already the right width -- but it also
   sets `padding: 72px var(--lp-pad)`, which the shorthand above replaces. */

/* Hero: no panel, just a local scrim weighted to the text column so the lede
   is readable while the right-hand side of the photo stays open. Fades out
   before the Fun With Phonetics frame, which is opaque anyway. */
.lv2 .lv2-hero { position: relative; }
.lv2 .lv2-hero::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg,
    rgba(11,14,26,0.88) 0%, rgba(11,14,26,0.70) 32%,
    rgba(11,14,26,0.28) 56%, rgba(11,14,26,0) 72%);
}
.lv2 .lv2-hero > * { position: relative; }

/* Footer gets the same treatment so the page ends on a readable surface.
   .lp-footer-inner was footer2.php's shell; this page renders footer_lv2.php
   now, so the glass moved onto .lv2-foot-inner further down this file. Rule
   removed rather than left as dead weight -- nothing on this page has that
   class any more. */

/* ---------- Footer, landing_v2's own (2026-09-15) ----------
   Markup is public/footer_lv2.php, not landing/footer2.php, so these rules
   own the whole footer rather than patching a shared partial. Three columns --
   Contact / Features / Documentation -- with the powered-by line and the
   copyright centred beneath them.

   The #lp-footer id is kept from the old markup so landing2.css's footer band
   (the dark background) still applies; everything inside is .lv2-foot-*.

   COLOURS ARE LITERALS, not var(--lv2-text-*). The footer sits outside every
   .lv2 wrapper and those tokens are declared ON .lv2, so a var() here resolves
   empty and the colour silently inherits -- same trap as the mic overlay. Keep
   these in step with --lv2-text-2 (#e4e8f6) and --lv2-text-3 (#c2cade) by hand.

   The old footer's muted rgba(148,163,184,0.78) is gone entirely: it was
   dimmer than anything else on the page after the glass panels were lightened. */
#lp-footer .lv2-foot-inner {
  background: var(--lv2-glass);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
  backdrop-filter: blur(10px) saturate(115%);
  border: 1px solid var(--lv2-glass-border);
  border-radius: var(--lv2-glass-radius);
  padding: 32px var(--lv2-glass-pad-x);
  max-width: 1080px; margin: 0 auto;
}

/* auto-fit + minmax rather than three fixed columns: at ~700px the three
   collapse to two and then to one on their own, with no breakpoint to keep in
   sync with the rest of the file. */
#lp-footer .lv2-foot-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 28px 40px; margin-bottom: 30px;
}
#lp-footer .lv2-foot-head {
  font-family: var(--lv2-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: #e4e8f6; margin: 0 0 12px;
}
#lp-footer .lv2-foot-note { font-size: 12.5px; line-height: 1.55; color: #c2cade; margin: 0 0 10px; }
#lp-footer .lv2-foot-list { list-style: none; margin: 0; padding: 0; }
#lp-footer .lv2-foot-list li { margin-bottom: 7px; }
#lp-footer .lv2-foot-list a {
  font-size: 13px; color: #c2cade; text-decoration: none;
}
#lp-footer .lv2-foot-list a:hover,
#lp-footer .lv2-foot-list a:focus-visible { color: #e4e8f6; text-decoration: underline; }

/* 2026-09-16: the contact address is no longer a mailto: link (footer_lv2.php
   has the reasoning), so it needs the size the <a> rule was giving it -- a bare
   <li> inherits the body size and would sit a pixel larger than the links
   beside it, which reads as a mistake rather than a distinction.
   Same colour as the links but NOT the hover/underline treatment, and
   user-select:text so it stays easy to copy, which is the whole point of
   leaving it visible. */
#lp-footer .lv2-foot-list .lv2-foot-plain {
  font-size: 13px;
  color: #c2cade;
  user-select: text;
}

/* Bottom block, centred, copyright last so it is the final thing on the page.
   The rule above it is the only divider -- the columns already read as groups
   and boxing them would add furniture the page does not need. */
#lp-footer .lv2-foot-bottom {
  border-top: 1px solid var(--lv2-border);
  padding-top: 18px; text-align: center;
}
#lp-footer .lv2-foot-poweredby {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 12px; color: #c2cade; margin-bottom: 8px;
}
#lp-footer .lv2-foot-poweredby img { height: 16px; width: auto; display: block; opacity: 0.92; }
#lp-footer .lv2-foot-copy { font-size: 12px; color: #c2cade; }

/* The phone overrides for these panels live at the END of the file, next to
   NAV BREAKPOINTS -- one of them targets .lv2-section, which is defined later
   than this block and would otherwise win on source order. */

.lv2 a { color: inherit; }

/* UI/UX UNIFICATION (2026-09-14) -- this page's shell mirrors indexA.php's
   typographic system instead of running a parallel one: centred section heads,
   an emphasised keyword per heading, and one shared width constraint.
   landing2.css is already loaded on this page (the reused partials need it),
   so the --lp-* tokens below are CONSUMED rather than copied -- change the
   palette or max-width there and this page follows automatically, which is the
   whole point. The .lv2- class names are kept so no .lp-* markup ever lands
   inside .lv2 and the isolation guarantee in the file header still holds. */
.lv2 .lv2-wrap { max-width: var(--lp-max-w); margin: 0 auto; padding: 0 var(--lp-pad); }

/* Emphasised keyword. Mirrors .lp-headline em / .lp-heading em exactly --
   <em> inside a heading takes the accent→violet gradient; .lv2-em / .lv2-em2
   are the flat inline variants for body copy, matching .lp-em / .lp-em2. */
.lv2 h1 em, .lv2 h2 em, .lv2 h3 em {
  font-style: normal;
  background: linear-gradient(130deg, var(--lp-accent), var(--lp-violet));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.lv2 .lv2-em2 { color: #818cf8; }

/* Eyebrow -- mirrors .lp-eyebrow */
.lv2 .lv2-eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--lp-accent); margin-bottom: 10px;
}


/* ---------- Nav ---------- */
/* The sticky must live on the WRAPPER, not on .lv2-nav. A sticky element is
   bounded by its containing block, and the wrapping <div class="lv2"> is only
   as tall as the header itself -- so sticking .lv2-nav inside it did nothing:
   the nav scrolled away with its own 64px parent. The wrapper's containing
   block is <body>, which spans the page, so the wrapper sticks correctly.
   Note this selector is deliberately NOT `.lv2 .lv2-nav-wrap` -- the element
   carries both classes, so it can never match a descendant form. */
.lv2-nav-wrap { position: sticky; top: 0; z-index: 50; }

.lv2 .lv2-nav {
  background: rgba(11,14,26,0.9);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--lv2-border);
}

.lv2 .lv2-nav-beta {
  font-size: 14px; font-weight: 600; text-decoration: none; color: var(--lv2-text);
  background: var(--lv2-accent-dim); border: 1px solid rgba(88,101,242,0.45);
  border-radius: 999px; padding: 7px 16px; white-space: nowrap;
}
.lv2 .lv2-nav-beta:hover { background: rgba(88,101,242,0.3); }
/* The Beta pill is in the scroll spy too, so the nav is never left with nothing
   marked while you read that section. No underline here -- it is already the
   loudest thing in the row, so it just firms up instead of gaining decoration
   that would read as a second, competing state. */
.lv2 .lv2-nav-beta.is-current {
  background: rgba(88,101,242,0.34); border-color: rgba(88,101,242,0.85);
}

/* Anchor targets sit under the 64px sticky nav without this. Three of these
   ids (#try, #level-test, #contact) belong to reused partials OUTSIDE .lv2, so
   this rule has to be unscoped -- kept to an explicit id list so it can't leak. */
#try-it, #try, #level-test, #how, #beta, #contact { scroll-margin-top: 80px; }

.lv2 .lv2-nav-inner { display: flex; align-items: center; gap: 28px; height: 64px; }
/* The wordmark is wrapped in a #top link. display:flex so the anchor is exactly
   as tall as the SVG -- an inline <a> would add descender space and push the
   64px nav row out of alignment. */
.lv2 .lv2-brand-home { display: flex; align-items: center; text-decoration: none; }
.lv2 .lv2-brand-logo { height: 24px; width: auto; display: block; }
.lv2 .lv2-nav-links { display: flex; gap: 22px; margin-left: auto; font-size: 14px; color: var(--lv2-text-2); }
/* nowrap (2026-09-20): flex items shrink before they overflow, so a long label
   -- "Conas a oibríonn sé", "So funktioniert es" -- wrapped onto a second line
   INSIDE the 64px bar rather than pushing the bar wide. The result looked like
   a layout fault and, worse, hid the overflow from the breakpoint measurement:
   the bar "fitted" at any width because its contents folded. Let them stay one
   line; the per-language breakpoint in landing/lv2/_nav_bp.php is what decides
   when there is no longer room for them. */
.lv2 .lv2-nav-links a { text-decoration: none; white-space: nowrap; }
.lv2 .lv2-nav-links a:hover { color: var(--lv2-text); }

/* Active section. Set by the scroll spy at the foot of index.php, which also
   sets aria-current -- so this is decoration on top of something announced,
   not the only signal.
   The underline is a pseudo-element rather than border-bottom or
   text-decoration: it can sit at a fixed distance below the 14px text without
   changing the link's box, so nothing in the 64px nav row shifts as the
   highlight moves between items of different widths. */
.lv2 .lv2-nav-links a.is-current { color: var(--lv2-text); position: relative; }
.lv2 .lv2-nav-links a.is-current::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -7px; height: 2px;
  background: var(--lv2-accent); border-radius: 2px;
}
.lv2 .lv2-nav-signin { font-size: 14px; font-weight: 600; color: var(--lv2-text-2); text-decoration: none; padding: 8px 4px; white-space: nowrap; }
.lv2 .lv2-nav-signin:hover { color: var(--lv2-text); }

/* ---------- Mobile nav ----------
   Measured: the bar's content needs 651px (logo 111 + links 297 + Sign in 53
   + Beta 66 + gaps), so the 780px breakpoint used everywhere else in this file
   leaves real slack rather than collapsing at the last moment.

   CAVEAT for when this page gets its language switcher: those labels run
   through t(), and German/Polish nav wording is materially wider than English.
   At ~35% wider the bar needs ~865px and would break BETWEEN 780px and 865px.
   Raise the breakpoint (or shorten the labels) at the same time as wiring
   translations -- it is fine today only because $ui_lang is pinned to 'en'.

   The responsive half of this lives at the END of the file -- see
   "NAV BREAKPOINTS" -- because it must come after the .lv2-nav-links /
   .lv2-nav-signin / .lv2-nav-beta rules above to win on source order. */
.lv2 .lv2-nav-toggle {
  display: none; background: none; border: none; cursor: pointer; padding: 8px 4px;
  flex-direction: column; justify-content: center; gap: 5px; margin-left: auto;
}
.lv2 .lv2-nav-toggle span {
  display: block; width: 22px; height: 2px; border-radius: 2px; background: var(--lv2-text);
  transition: transform 0.25s, opacity 0.2s;
}
.lv2 .lv2-nav-toggle.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.lv2 .lv2-nav-toggle.is-open span:nth-child(2) { opacity: 0; }
.lv2 .lv2-nav-toggle.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.lv2 .lv2-mobile-nav {
  display: none; flex-direction: column;
  background: rgba(11,14,26,0.98); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--lv2-border);
  padding: 8px var(--lp-pad) 16px;
}
.lv2 .lv2-mobile-nav.is-open { display: flex; }
.lv2 .lv2-mobile-nav a {
  text-decoration: none; color: var(--lv2-text-2); font-size: 16px; font-weight: 500;
  padding: 13px 4px; border-bottom: 1px solid rgba(255,255,255,0.05);
}
.lv2 .lv2-mobile-nav a:last-child { border-bottom: none; }
.lv2 .lv2-mobile-nav a:hover, .lv2 .lv2-mobile-nav a:focus-visible { color: var(--lv2-text); }
/* Active section in the panel. A left bar rather than the desktop underline:
   these are stacked full-width rows already separated by bottom borders, so
   another horizontal line would just read as a heavier divider. Transparent
   border on every row so the marked one does not shunt the text sideways.
   :not() excludes the Beta pill deliberately -- ".lv2 .lv2-mobile-nav a" is
   (0,2,1) and beats the pill's own ".lv2 .lv2-mobile-nav-beta" (0,2,0) despite
   coming first, so without it the border shorthand below loses and the pill
   renders with a 3px left edge and 1px everywhere else. */
.lv2 .lv2-mobile-nav a:not(.lv2-mobile-nav-beta) { border-left: 3px solid transparent; padding-left: 10px; }
.lv2 .lv2-mobile-nav a.is-current:not(.lv2-mobile-nav-beta) { color: var(--lv2-text); border-left-color: var(--lv2-accent); }
.lv2 .lv2-mobile-nav-beta.is-current { border-color: rgba(88,101,242,0.85); background: rgba(88,101,242,0.34); }
.lv2 .lv2-mobile-nav-beta {
  margin-top: 12px; text-align: center; color: var(--lv2-text) !important;
  background: var(--lv2-accent-dim); border: 1px solid rgba(88,101,242,0.45);
  border-radius: 10px; font-weight: 600 !important;
}

/* ---------- Hero ----------
   The background image that used to live here is now the page-wide backdrop
   at the top of this file. */
/* 88px top used to leave the wordmark stranded well below the sticky nav.
   40px closes that gap so nav and wordmark read as one block of header. */
.lv2 .lv2-hero { padding: 40px 0 64px; }
/* .lv2-badge (the "Limited beta" pill) and .lv2-motto were removed 2026-09-14.
   The motto is now the h1 itself, and the beta status is carried by the nav's
   Beta pill and the whole Beta section, so the hero no longer repeats it. */
/* Hero text stays LEFT aligned -- indexA centres section heads but not the
   hero, whose two-column grid puts the demo alongside. Values mirror
   .lp-headline / .lp-sub. */
.lv2 .lv2-hero h1 {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: clamp(32px, 8vw, 52px); font-weight: 800; line-height: 1.1;
  letter-spacing: -1.5px; color: var(--lp-text); max-width: 15ch; margin-bottom: 18px;
}
/* ---------- Rotating hero copy ----------
   Every slide's copy is rendered, then stacked into ONE css grid cell. The
   container therefore sizes to the tallest block and stays put as slides
   change -- showing one at a time any other way would make the hero (and the
   buttons under it) jump on every advance.
   visibility, not just opacity: a transparent-but-visible block would still be
   read out by a screen reader and still catch clicks. */
.lv2 .lv2-hero-copy { display: grid; margin-bottom: 30px; }
.lv2 .lv2-hero-copy-item {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity 600ms ease, visibility 0s linear 600ms;
}
.lv2 .lv2-hero-copy-item.is-active {
  opacity: 1; visibility: visible;
  transition: opacity 600ms ease, visibility 0s;
}
.lv2 .lv2-hero-copy-item h2 {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: clamp(20px, 2.4vw, 25px); font-weight: 700; line-height: 1.2;
  letter-spacing: -0.4px; color: var(--lv2-text); margin: 0 0 10px;
}
.lv2 .lv2-hero-copy-item p {
  font-size: 16px; color: var(--lv2-text); line-height: 1.7;
  max-width: 46ch; margin: 0;
}
@media (prefers-reduced-motion: reduce) {
  .lv2 .lv2-hero-copy-item { transition: none; }
}

/* Both CTAs on one row. nowrap keeps them together; the labels are short
   enough now that the column has room, and they are allowed to wrap again
   below the breakpoint where the grid stacks. */
.lv2 .lv2-hero-ctas { display: flex; gap: 12px; flex-wrap: nowrap; margin-bottom: 20px; }
.lv2 .lv2-hero-ctas .lv2-btn-primary,
.lv2 .lv2-hero-ctas .lv2-btn-ghost {
  padding: 12px 20px; font-size: 14.5px; white-space: nowrap;
}
.lv2 .lv2-btn-primary {
  background: var(--lv2-accent); color: #fff; border: none; padding: 13px 22px; border-radius: 8px;
  font-size: 15px; font-weight: 600; cursor: pointer; text-decoration: none; display: inline-block;
}
/* .lv2-btn-ghost has no markup on this page since the hero's "Become a beta
   tester" button was removed on 2026-09-15 -- the Apply tab's "Apply as a
   school" used it too, and that went with the beta tab group.
   KEPT, not deleted: both callers return when the beta reopens (the archived
   copy in public/landing_v2/ still uses it), and it is the only ghost-button
   style in this file. Deleting it would mean rewriting it from scratch then. */
.lv2 .lv2-btn-ghost {
  background: rgba(11,14,26,0.45); color: var(--lv2-text); border: 1px solid var(--lv2-border); padding: 13px 22px;
  border-radius: 8px; font-size: 15px; font-weight: 600; cursor: pointer; text-decoration: none; display: inline-block;
}
/* .lv2-qr-note removed 2026-09-14 -- the QR/no-sign-up promise is still made
   by the Try it section heading and the teacher note there. */

/* Wordmark above the hero. Sized by height so the 148x32 SVG keeps its ratio
   whatever the viewport; the width/height attributes on the tag are there to
   reserve space and stop the hero shifting as it loads. */
.lv2 .lv2-hero-logo {
  display: block; margin: 0 auto 44px;
  height: 46px; width: auto;
}
@media (max-width: 780px) {
  .lv2 .lv2-hero-logo { height: 34px; margin-bottom: 30px; }
}
.lv2 .lv2-hero-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 48px; align-items: start; }
@media (max-width: 780px) {
  .lv2 .lv2-hero-grid { grid-template-columns: 1fr; }
  /* Single column: let the CTAs wrap again rather than squeeze or overflow. */
  .lv2 .lv2-hero-ctas { flex-wrap: wrap; }
}
/* .lv2-skill-dots removed 2026-09-14 -- five unlabelled colour dots that read
   as decoration rather than meaning. The --lv2-skill-* tokens they used stay:
   pane 3's scorecard still colours its bars from them. */

/* Fun With Phonetics moved out of the hero and under Try it (2026-09-14).
   Its label is the only styling this file still supplies; the demo itself
   is now outside .lv2 like every other reused partial, so nothing here can
   reach its internals. */
/* Intro above the Fun With Phonetics demo. Replaced the old
   "FUN WITH PHONETICS — MICRO-LESSON" mono label: that treatment (11px,
   uppercase, letter-spaced) is for a one-word tag, and this is a sentence --
   uppercase mono prose is hard to read and looks like a system message. */
.lv2 .lv2-fwp-intro { text-align: center; margin-bottom: 22px; }
.lv2 .lv2-fwp-intro h3 {
  font-size: 19px; font-weight: 700; letter-spacing: -0.3px;
  color: var(--lp-text); margin: 0 0 8px;
}
.lv2 .lv2-fwp-intro p {
  font-size: 14.5px; line-height: 1.6; color: var(--lv2-text-2);
  margin: 0 auto; max-width: 54ch;
}

/* ---------- Beta programme pitch ----------
   Mirrors indexA's beta2.php card: lead paragraph, ticked feature list, one
   CTA, then the "spots are limited" note. Sits between the section head and
   the tab group so it is read whichever tab happens to be open.
   Tick glyphs come from ::before rather than markup -- they are decoration,
   and a screen reader reading "check" before each item adds nothing. */
.lv2 .lv2-beta-pitch { max-width: 62ch; margin: 0 auto 42px; text-align: center; }
.lv2 .lv2-beta-pitch-lead {
  font-size: 16px; line-height: 1.7; color: var(--lv2-text); margin: 0 0 26px;
}
.lv2 .lv2-beta-pitch-list {
  list-style: none; padding: 22px 26px; margin: 0;
  display: flex; flex-direction: column; gap: 13px;
  background: rgba(255,255,255,0.035); border: 1px solid var(--lv2-border);
  border-radius: 14px; text-align: left;
}
.lv2 .lv2-beta-pitch-list li {
  position: relative; padding-left: 28px;
  font-size: 15px; line-height: 1.55; color: var(--lv2-text-2);
}
.lv2 .lv2-beta-pitch-list li::before {
  content: '\2713'; position: absolute; left: 0; top: 0;
  color: var(--lv2-skill-respond); font-weight: 700; font-size: 15px;
}
/* .lv2-beta-pitch-cta removed with the "Apply now" button it wrapped. The
   note now follows the list directly, so it carries the spacing. */
.lv2 .lv2-beta-pitch-note {
  font-size: 13px; line-height: 1.6; color: var(--lv2-text-3); margin: 22px 0 0;
}

/* ---------- Teacher note in the Try it intro ----------
   Left-aligned inside a centred section on purpose: it is a paragraph to be
   read, not a headline, and centred prose of this length is hard work. */
.lv2 .lv2-teacher-note {
  max-width: 62ch; margin: 0 auto 32px; padding: 18px 22px;
  background: var(--lp-accent-dim); border: 1px solid rgba(99,102,241,0.3);
  border-radius: 12px; text-align: left;
}
.lv2 .lv2-teacher-note-tag {
  display: inline-block; font-family: var(--lv2-mono); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--lp-accent);
  margin-bottom: 8px;
}
.lv2 .lv2-teacher-note p { font-size: 14.5px; line-height: 1.65; color: var(--lv2-text-2); margin: 0; }
.lv2 .lv2-teacher-note p strong { color: var(--lv2-text); font-weight: 600; }
.lv2 .lv2-teacher-note-cta { margin-top: 10px !important; color: var(--lv2-text) !important; font-weight: 600; }

/* ---------- Demo panels (Presentation / Practice / Level Test) ----------
   The three reused partials each bring their own .lp-section padding and, in
   try2.php's case, its own section header. Inside the tabbed #try-it panel
   both are redundant: the outer glass panel supplies the padding and the
   section head above the tabs supplies the heading, so showing try2's header
   would print "Try it — no login required" twice on the page. */
.lv2 .lv2-demo-panel > section > .lp-section,
.lv2 .lv2-demo-panel > .lp-section { padding-left: 0; padding-right: 0; padding-top: 0; max-width: 100%; }
.lv2 .lv2-demo-panel #try .lp-section-header { display: none; }
/* level-test.php styles #lt-section-wrap with its own dark panel + radius;
   keep it transparent here so it sits on the glass like the other two.
   padding-top also goes: that 32px was separating its panel from the section
   above it, and inside the tab panel there is nothing to separate from. */
.lv2 .lv2-demo-panel #lt-section-wrap {
  background: transparent;
  padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 8px;
}
.lv2 .lv2-demo-panel #lt-main { padding-top: 0; }

/* "Check your English level" -- dropped from this page 2026-09-15. The tab is
   labelled Level Test, inside a section headed "Try it", so the headline was
   the third thing in a row naming the same thing.

   Hidden rather than removed from the markup: level-test.php is shared with
   the standalone /level-test.php page, where the headline is the page title and
   belongs, and lt_boot.js writes into #lt-intro-headline for its
   post-registration variant. See the note at the include in index.php.

   display:none, not visibility/opacity -- it also takes the element out of the
   accessibility tree, which is what makes it safe for this to be the page's
   second <h1>. Un-hiding it brings that problem back. */
.lv2 .lv2-demo-panel #lt-intro-headline { display: none; }

/* ---------- Level Test surfaces, matched to the Practice cards ----------
   2026-09-15, on request: the Level Test panel read as a different product to
   the Practice one next to it. level-test.php gives its own surfaces a flat
   rgba(255,255,255,0.05) wash, a 1px rgba(255,255,255,0.1) hairline, an 8px
   radius and no shadow; landing2.css gives .lp-card a solid --lp-card panel, a
   0.13 border, --lp-r corners and an indigo ring over a soft drop shadow. Two
   tabs of one section, two different card languages.

   Values are taken as TOKENS, not copied numbers -- var(--lp-card) and
   var(--lp-r) are the same custom properties .lp-card itself reads, so if the
   Practice cards are restyled these follow rather than drifting apart again.
   The one literal pair is the border and shadow, which .lp-card also writes
   literally; matching it exactly is the point.

   Scoped to .lv2 .lv2-demo-panel so it reaches only this page's embedded copy.
   level-test.php is shared with indexA.php and the standalone /level-test.php
   page, both of which keep the partial's own look. Specificity (0,3,0) beats
   the partial's own (0,1,0) rules regardless of source order, so no !important.

   .lt-pill keeps its 100px radius and takes only the surface and border -- a
   chip with a card's ring and drop shadow reads as a button, and there are five
   of them in a row. */
.lv2 .lv2-demo-panel .lt-meta-cell {
  background: var(--lp-card);
  border: 1px solid rgba(255,255,255,0.13);
  border-radius: var(--lp-r);
  box-shadow: 0 0 0 1px rgba(99,102,241,0.08), 0 4px 16px rgba(0,0,0,0.25);
}
.lv2 .lv2-demo-panel .lt-pill {
  background: var(--lp-card);
  border: 1px solid rgba(255,255,255,0.13);
  box-shadow: 0 0 0 1px rgba(99,102,241,0.08), 0 4px 16px rgba(0,0,0,0.25);
}

/* Text brightness, rebound at the token rather than per class.

   level-test.php declares its own three-step scale -- on :root when it is the
   standalone page, and on #lt-section-wrap when it is embedded (its line 125
   else-branch, which is the one that applies here):

     --text1: rgba(255,255,255,.92)
     --text2: rgba(255,255,255,.65)
     --text3: rgba(255,255,255,.38)

   .38 in particular is very faint -- it is what the "Questions / Minutes /
   CEFR range" labels under each number were rendering in -- and the whole
   scale sits below the brightness this page settled on when the glass panels
   were lightened.

   Rebinding the three properties, rather than restyling #lt-intro-body,
   .lt-qr-label, .lt-pill and .lt-meta-label one at a time, means every rule in
   the partial that reads var(--text1/2/3) follows -- including the question,
   answer and results screens that are display:none until the test starts and
   so cannot be styled by inspection.

   --border goes up with them: .10 against the brighter text and the solid
   card surfaces above reads as a smudge rather than an edge. 0.13 is what
   .lp-card uses, so the Level Test's internal edges now match the Practice
   cards' too.

   Specificity: the partial declares these on #lt-section-wrap (1,0,0); this is
   (1,2,0), so it wins on merit and only inside this page's demo panel. The
   standalone page and indexA keep the original scale. */
.lv2 .lv2-demo-panel #lt-section-wrap {
  --text1: var(--lv2-text);
  --text2: var(--lv2-text-2);
  --text3: var(--lv2-text-3);
  --border: rgba(255,255,255,0.13);

  /* --surface goes to the Practice card colour for the same reason: it is the
     partial's base panel fill, used by .lt-q-card, .lt-input, .lt-btn--ghost,
     .lt-meta-cell and .lt-pill. Checked every rule that reads it before
     rebinding -- they are all cards, pills, buttons, inputs and boxes; nothing
     uses it as a scrim or overlay where a solid fill would be wrong.

     --surface2 (.09) is deliberately NOT rebound. It is the hover and filled
     state -- .lt-opt:hover, .lt-letter-box--filled, .lt-conv-bubble -- and it
     works by being lighter than the base. Rebinding both to the same solid
     would flatten those states into invisibility. Leaving it translucent keeps
     a filled letter box and a hovered option visibly ahead of the card they
     sit on, which is now a real improvement: at .05 base against .09 hover the
     two were nearly indistinguishable. */
  --surface: var(--lp-card);
}

/* .lt-q-card is the question screen's main panel -- the Practice-tab
   equivalent on every screen after Start. It takes --surface above, so it is
   already the right colour and radius; this adds the ring and drop shadow,
   which the partial has no token for. */
.lv2 .lv2-demo-panel .lt-q-card {
  box-shadow: 0 0 0 1px rgba(99,102,241,0.08), 0 4px 16px rgba(0,0,0,0.25);
}

/* ---------- Presentation tab, matched to the Practice cards ----------
   Same exercise as the Level Test above, one tab across. fwp2.css frames the
   demo with background rgba(255,255,255,0.03) and a rgba(255,255,255,0.08)
   hairline at 16px -- fainter even than the Level Test's was, and its own
   comment calls it a "subtle card treatment". Next to a solid Practice card it
   read as an unfinished panel rather than a quieter one.

   fwp2.css defines no custom properties for its surfaces, so unlike the Level
   Test these are set per class rather than rebound at the token. */
.lv2 .lv2-demo-panel .fwp2-hero-demo {
  background: var(--lp-card);
  border: 1px solid rgba(255,255,255,0.13);
  border-radius: var(--lp-r);
  box-shadow: 0 0 0 1px rgba(99,102,241,0.08), 0 4px 16px rgba(0,0,0,0.25);
}

/* Accent. fwp2 ships its own purple, #6c63ff, which is NOT the one the rest of
   this section uses: the Practice level buttons and the Level Test's Start
   button are both --lp-accent (#6366f1). Three tabs of one section were
   running two purples, visible on the demo's Continue button and its slide
   dots.
   Rebindable because every solid use in fwp2.css reads
   var(--color-accent, #6c63ff) -- so defining the property on the panel
   redirects all of them at once. The handful of literal rgba(108,99,255,...)
   glows and hairlines in that file do not follow, but at 0.2-0.4 alpha the hue
   difference is not perceptible; the solid fills were the visible problem.
   --lp-accent rather than --lv2-accent (#5865f2, the nav and hero CTA colour):
   inside these demo panels the Practice cards set the reference, and matching
   them was the request. */
.lv2 .lv2-demo-panel[data-panel="presentation"] { --color-accent: var(--lp-accent); }

/* Muted chrome, brought up to the same text scale the Level Test now uses.
   The prev/next arrows and the mic message both sit at #9a9ab0, which is
   dimmer than anything else on the page after the glass panels were
   lightened. */
.lv2 .lv2-demo-panel .fwp2-prog-arrow {
  color: var(--lv2-text-2); border-color: rgba(255,255,255,0.28);
}
.lv2 .lv2-demo-panel .fwp2-mic-msg { color: var(--lv2-text-2); }

/* The stray line above the Level Test intro is #lt-progress-track -- a 4px
   rail with an empty #lt-progress-fill in it. It is the question-progress bar,
   which is meaningful DURING the test and reads as a pointless divider before
   it starts. Hidden only while the intro screen is the active one, so it comes
   back the moment the test begins.
   Scoped here rather than fixed in level-test.php, which is shared with
   indexA.php and the standalone /level-test.php page.
   :has() is well supported now; where it is not, the rule is simply dropped
   and the bar stays as it is today -- no worse, never broken.

   !important is load-bearing here and not laziness: level-test.php writes
   style="display:flex;..." straight onto #lt-progress-wrap as an inline
   attribute, which outranks any selector no matter how specific. Since that
   file is shared, overriding the attribute is the only way to reach it. */
.lv2 .lv2-demo-panel #lt-shell:has(#lt-intro.lt-active) #lt-progress-wrap { display: none !important; }

/* =============================================================================
   HERO CAROUSEL ("stage")
   Native CSS scroll-snap does the scrolling -- swipe, trackpad and keyboard
   all work without JS, and with JS off this degrades to an ordinary
   horizontal scroller rather than a stuck single pane. The script only syncs
   the dots and drives the arrows.
   ========================================================================== */
.lv2 .lv2-stage { position: relative; }
.lv2 .lv2-stage-track {
  display: flex; gap: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* NOTE: `scroll-behavior: smooth` is deliberately NOT set here. It applies
     to scrollLeft assignment as well as scrollTo(), so when smooth scrolling
     does not actually animate (observed in embedded/preview browsers: the
     request is accepted, nothing moves) it would also swallow the JS
     fallback and leave the arrows dead. Scroll behaviour is owned by the
     script instead, which requests smooth and hard-sets if it did not take. */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;                 /* the dots are the affordance */
}
.lv2 .lv2-stage-track::-webkit-scrollbar { display: none; }
.lv2 .lv2-pane {
  flex: 0 0 100%; min-width: 0;
  scroll-snap-align: center; scroll-snap-stop: always;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 4px 2px 0;
  /* The fade half of the "fade-in scroll": the outgoing pane drops back while
     the incoming one comes up, over roughly the same span as the 1.6s glide.
     Panes are full-track width, so this is only visible during transit --
     which is exactly where it should be. JS toggles .is-active. */
  opacity: 0.25;
  transition: opacity 1400ms cubic-bezier(0.4, 0, 0.2, 1);
}
.lv2 .lv2-pane.is-active { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .lv2 .lv2-pane { opacity: 1; transition: none; }
}

/* Controls */
.lv2 .lv2-stage-ui { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 16px; }
.lv2 .lv2-stage-arrow {
  background: rgba(11,14,26,0.55); border: 1px solid var(--lv2-border); color: var(--lv2-text-2);
  width: 30px; height: 30px; border-radius: 50%; cursor: pointer;
  font-size: 17px; line-height: 1; display: flex; align-items: center; justify-content: center;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.lv2 .lv2-stage-arrow:hover:not(:disabled) { color: var(--lv2-text); border-color: rgba(255,255,255,0.25); }
.lv2 .lv2-stage-arrow:disabled { opacity: 0.3; cursor: default; }
.lv2 .lv2-stage-dots { display: flex; gap: 7px; }
.lv2 .lv2-stage-dots button {
  width: 7px; height: 7px; padding: 0; border-radius: 50%; cursor: pointer;
  background: rgba(255,255,255,0.22); border: none; transition: background 0.2s, width 0.2s;
}
.lv2 .lv2-stage-dots button[aria-current="true"] { background: var(--lp-accent); width: 20px; border-radius: 4px; }

/* ---------- Device frames ---------- */
/* padding-bottom leaves room for pane 1's phone, which overhangs the laptop's
   bottom edge. Panes with a laptop on its own do not need it. */
.lv2 .lv2-devices { position: relative; width: 100%; padding-bottom: 34px; }
.lv2 .lv2-devices--solo { padding-bottom: 0; }

/* Screenshots inside the device frames. Both are cropped with object-fit
   rather than squashed, because neither source matches its frame:
     laptop  -- frame 16:9 (1.78), shot 2.18:1. `left center` keeps the
                sidebar and branding and trims from the right edge.
     phone   -- frame 9:17 (0.53), shot 0.87:1. `top` keeps the header and
                the first rows of tiles, which is the recognisable part.
   Swapping in a true phone-aspect capture later needs no CSS change --
   object-fit simply stops having anything to crop. */
.lv2 .lv2-laptop-screen picture,
.lv2 .lv2-phone-screen picture { display: block; width: 100%; height: 100%; }
.lv2 .lv2-laptop-screen img,
.lv2 .lv2-phone-screen img { width: 100%; height: 100%; display: block; }
.lv2 .lv2-laptop-screen img { object-fit: cover; object-position: left center; }
.lv2 .lv2-phone-screen img  { object-fit: cover; object-position: top center; }

.lv2 .lv2-laptop { width: 100%; }
/* The bright edge is a box-shadow ring rather than a real border: border would
   change the frame's box and shift the 16:9 screen inside it, whereas a ring
   hugs the existing rounded corners and costs no layout. Tune or recolour it
   from --lv2-frame-edge alone -- setting that to an accent tint also works. */
.lv2 .lv2-laptop-screen {
  aspect-ratio: 16 / 9;
  background: #0a0d18;
  border: 6px solid #232a3d; border-bottom-width: 8px;
  border-radius: 12px 12px 4px 4px;
  overflow: hidden;
  box-shadow:
    0 0 0 2px var(--lv2-frame-edge),
    0 22px 50px rgba(0,0,0,0.55),
    inset 0 0 0 1px rgba(255,255,255,0.05);
}
/* The lid's foot: a trapezoid-ish bar, wider than the screen. */
.lv2 .lv2-laptop-foot {
  height: 9px; width: 112%; margin: 0 -6%;
  background: linear-gradient(180deg, #2b3348, #1a2032);
  border-radius: 0 0 8px 8px;
  box-shadow: 0 10px 22px rgba(0,0,0,0.5);
}
.lv2 .lv2-laptop-foot::after {
  content: ''; display: block; width: 16%; height: 3px; margin: 0 auto;
  background: rgba(255,255,255,0.12); border-radius: 0 0 4px 4px;
}

/* Phone overlaps the laptop's lower-right corner -- the "same lesson, both
   screens" idea only reads if they physically overlap. */
/* Same --lv2-frame-edge ring as the laptop. The phone sits later in the DOM,
   so its ring paints over the laptop where the two overlap, which is what
   keeps the overlap legible rather than muddy. */
.lv2 .lv2-phone {
  position: absolute; right: -2%; bottom: 0; width: 23%; min-width: 86px;
  border: 5px solid #232a3d; border-radius: 18px;
  background: #0a0d18; overflow: hidden;
  box-shadow:
    0 0 0 2px var(--lv2-frame-edge),
    0 16px 34px rgba(0,0,0,0.6),
    inset 0 0 0 1px rgba(255,255,255,0.05);
}
.lv2 .lv2-phone-screen { aspect-ratio: 9 / 17; }

/* ---------- Drawn UI (swap these blocks for real screenshots later) ------- */
.lv2 .lv2-ui {
  width: 100%; height: 100%; display: flex; flex-direction: column;
  font-family: 'DM Sans', system-ui, sans-serif; color: var(--lv2-text);
  background: linear-gradient(165deg, #141b2e, #0b0f1c);
}
.lv2 .lv2-ui-bar {
  display: flex; align-items: center; gap: 5px;
  padding: 7px 10px; border-bottom: 1px solid rgba(255,255,255,0.07);
  font-size: 9px; flex-shrink: 0;
}
.lv2 .lv2-ui-title { margin-left: 6px; color: var(--lv2-text-2); font-weight: 600; letter-spacing: 0.01em; }
.lv2 .lv2-ui-cefr {
  margin-left: auto; font-family: var(--lv2-mono); font-size: 9px;
  color: var(--lp-accent); background: var(--lp-accent-dim);
  border: 1px solid rgba(99,102,241,0.4); border-radius: 5px; padding: 2px 7px;
}
/* Panes 1 and 2 were drawn mockups until 2026-09-14, when real screenshots
   replaced them. The rules only those mockups used went with them -- the
   dashboard rows/chips/score, the phone card's word/IPA/ring/mic, and the
   fake QR panel. What remains under .lv2-ui* is only what pane 3's scorecard
   still draws: .lv2-ui, .lv2-ui-bar, .lv2-ui-title, .lv2-ui-cefr. */

/* Pane 2 -- the real Share Link dialog. It carries its own dark background and
   rounded corners, so it needs a size cap and a shadow to lift it off the
   backdrop, not a device frame. */
.lv2 .lv2-devices--qr { display: flex; flex-direction: column; align-items: center; padding-bottom: 0; }
.lv2 .lv2-shot-modal { display: block; width: 100%; max-width: 232px; }
.lv2 .lv2-shot-modal img {
  width: 100%; height: auto; display: block;
  border-radius: 14px; box-shadow: 0 20px 44px rgba(0,0,0,0.55);
}
.lv2 .lv2-joiners { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-top: 18px; }
.lv2 .lv2-joiner {
  font-size: 10px; color: var(--lv2-text-2); background: rgba(11,14,26,0.6);
  border: 1px solid var(--lv2-border); border-radius: 999px; padding: 4px 10px;
}
.lv2 .lv2-joiner b { color: var(--lv2-skill-respond); }
.lv2 .lv2-joiner--pending { opacity: 0.45; }

/* Pane 3 -- scorecard */
.lv2 .lv2-devices--score { display: flex; justify-content: center; padding-bottom: 0; }
.lv2 .lv2-scorecard {
  width: 100%; max-width: 340px; background: linear-gradient(165deg, #141b2e, #0b0f1c);
  border: 1px solid var(--lv2-border); border-radius: 14px; overflow: hidden;
  box-shadow: 0 22px 50px rgba(0,0,0,0.5);
}
.lv2 .lv2-scorecard .lv2-ui-bar { font-size: 11px; padding: 11px 14px; }
.lv2 .lv2-bar-row { display: flex; align-items: center; gap: 10px; padding: 9px 14px; }
.lv2 .lv2-bar-label { font-size: 11px; color: var(--lv2-text-2); flex: 0 0 38%; }
.lv2 .lv2-bar-track { flex: 1; height: 5px; border-radius: 3px; background: rgba(255,255,255,0.08); overflow: hidden; }
.lv2 .lv2-bar-track i { display: block; height: 100%; border-radius: 3px; }
.lv2 .lv2-bar-val { font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; flex: 0 0 22px; text-align: right; }

/* prefers-reduced-motion for the carousel is handled in the script (it picks
   'auto' over 'smooth'), since scroll behaviour is owned there -- see the note
   on .lv2-stage-track above. */

/* ---------- Section shell (class-based, never bare `section`) ----------
   Values mirror .lp-section / .lp-section-header / .lp-heading / .lp-lead in
   landing2.css. Headings are DM Sans 800 rather than the Fraunces serif this
   page previously used, so the new sections and the reused .lp-* partials
   share one voice. */
/* Sections now supply only the GAP between glass panels -- each panel's own
   padding does the internal spacing, and the old full-bleed border-top would
   draw a line across the photo between two floating panels. */
.lv2 .lv2-section { padding: 26px 0; }
.lv2 .lv2-section-head { margin: 0 auto 36px; text-align: center; }
.lv2 .lv2-section-head h2 {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: clamp(24px, 5vw, 36px); font-weight: 800; line-height: 1.15;
  letter-spacing: -0.5px; color: var(--lp-text); margin-bottom: 14px;
}
/* No opacity here. Translucent text over a photographic backdrop lets the
   image bleed through the glyphs themselves, which is what made this look
   washed out -- a solid colour stays crisp at any panel opacity. */
.lv2 .lv2-section-head p {
  font-size: 16px; color: var(--lv2-text); line-height: 1.7;
  max-width: 62ch; margin: 0 auto;
}

/* ---------- Generic tab system (How it works AND Beta) ----------
   Scoped per .lv2-tab-group so two tab groups on one page never interfere. */
.lv2 .lv2-tab-list { display: flex; gap: 4px; flex-wrap: wrap; justify-content: center; border-bottom: 1px solid var(--lv2-border); margin-bottom: 28px; }
.lv2 .lv2-tab-btn {
  background: none; border: none; border-bottom: 2px solid transparent; color: var(--lv2-text-3);
  font-size: 15px; font-weight: 600; padding: 10px 18px 12px; cursor: pointer; margin-bottom: -1px;
}
.lv2 .lv2-tab-btn.active { color: var(--lv2-text); border-bottom-color: var(--lv2-accent); }
.lv2 .lv2-tab-panel { display: none; }
.lv2 .lv2-tab-panel.active { display: block; }
.lv2 .lv2-tab-panel ul { list-style: none; display: flex; flex-direction: column; gap: 14px; padding: 0; margin: 0; }
.lv2 .lv2-tab-panel li { padding-left: 22px; position: relative; color: var(--lv2-text-2); font-size: 15px; }
.lv2 .lv2-tab-panel li::before { content: ''; position: absolute; left: 0; top: 8px; width: 6px; height: 6px; border-radius: 50%; background: var(--lv2-accent); }
/* Unused since 2026-09-15 -- its only markup was the "draft" note under the
   For-admins panel, dropped when that panel was rewritten. Kept with .lv2-todo
   as the pair to reach for the next time a section ships provisional copy. */

/* ---------- Install app -- card deck matching Topic Library (.tl-card)
   conventions: minmax(190px,220px) width cap, 14px radius, 0 4px 18px shadow */
.lv2 .lv2-install-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 220px)); justify-content: center; gap: 16px; margin-top: 8px; }
@media (max-width: 780px) {
  .lv2 .lv2-install-grid {
    display: flex; grid-template-columns: none; overflow-x: auto; gap: 14px; scroll-snap-type: x mandatory;
    padding-bottom: 6px; margin: 8px calc(-1 * var(--lp-pad)) 0; padding-left: var(--lp-pad); padding-right: var(--lp-pad);
    -webkit-overflow-scrolling: touch; justify-content: flex-start;
  }
  .lv2 .lv2-install-card { flex: 0 0 190px; scroll-snap-align: center; }
}
.lv2 .lv2-install-card {
  background: var(--lv2-panel); border: 1px solid var(--lv2-border); border-radius: 14px; padding: 22px;
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px;
  box-shadow: 0 4px 18px rgba(0,0,0,0.35);
}
.lv2 .lv2-install-card h3 { font-size: 15px; margin: 0; }
.lv2 .lv2-install-card p { font-size: 13px; line-height: 1.5; color: var(--lv2-text-3); margin: 0; }

/* Real PWA icon in place of the old fake-QR block. 56px from a 192px source,
   so it stays sharp on a high-DPI screen. Radius matches how the OS rounds an
   installed app icon. */
.lv2 .lv2-install-icon {
  width: 56px; height: 56px; flex-shrink: 0; display: block;
  border-radius: 14px; box-shadow: 0 4px 18px rgba(0,0,0,0.4);
}

/* The three app cards, folded in from the removed "Download app" tab. */
.lv2 .lv2-install-apps { margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--lv2-border); }
.lv2 .lv2-install-apps-lead {
  text-align: center; font-size: 14px; color: var(--lv2-text-2); margin: 0 0 20px;
}

/* Removed with that tab: .lv2-install-qr (decorative fake QR blocks),
   .lv2-install-btn-row (iOS/Android buttons implying store apps that do not
   exist) and .lv2-install-card--soon (the admin "Coming soon" state, wrong
   because admin/manifest.json ships).

   .lv2-install-soon-badge was kept past that cull because the Admin Guide card
   in the Guide tab still used it. That card was removed on 2026-09-15, so the
   badge went too -- it now has no markup anywhere. */
.lv2 .lv2-install-note { font-size: 14px; color: var(--lv2-text-2); background: var(--lv2-accent-dim); border: 1px solid rgba(88,101,242,0.3); border-radius: 10px; padding: 14px 18px; margin-bottom: 24px; }
/* .lv2-install-roles was a text-only version of the per-profile note; it was
   replaced by the .lv2-install-apps card grid below, which shows the real
   icons and manifest names instead of describing them. */

/* 2026-09-19 (refactor audit Phase 2): moved here from the beta page's fork,
   public/landing_v2/landing_v2.css, which is no longer linked -- both landing
   pages now share this one stylesheet. Only the beta page ($lv2Beta) renders
   the Install tab these rules style; on the homepage they match nothing. */
/* ── Install on your phone (2026-09-16) ─────────────────────────────────────
   Note the deliberate echo of the removed block above: .lv2-install-qr and
   .lv2-install-btn-row were culled for implying store apps that do not exist.
   .lv2-install-os is named differently precisely so nobody reads it as those
   coming back: the two platform columns give the actual PWA install route
   rather than store buttons. THAT decision stands.
   2026-09-20: the QR that used to sit beside them is gone too (Joe) -- it made
   sense while the beta lived behind a share token, but this page is the
   homepage now, so the code pointed at the page you are already on. */
.lv2 .lv2-install-phone { margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--lv2-border); }
.lv2 .lv2-install-phone h3 { font-size: 17px; margin: 0 0 8px; text-align: center; }
.lv2 .lv2-install-phone-lead {
  text-align: center; font-size: 14px; line-height: 1.6;
  color: var(--lv2-text-2); margin: 0 auto 22px; max-width: 620px;
}

/* 2026-09-20 (Joe): the QR came out of the Install tab, so this is a single
   column now -- the two OS blocks get the full width at every size. The rules
   it needed (.lv2-qr-block, .lv2-qr, .lv2-qr-caption, .lv2-qr-url, and the
   `order: 2` that used to drop the code below the steps on a phone) are gone
   with it; nothing else in the tree uses those class names. The grid stays
   rather than collapsing to a plain block: .lv2-install-os-grid below is still
   a grid of its own and the gap here is what separates the two. */
.lv2 .lv2-install-phone-grid {
  display: grid; grid-template-columns: 1fr; gap: 28px 34px; align-items: start;
}

.lv2 .lv2-install-os-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 18px;
}
/* --lv2-panel, not an invented --lv2-surface: the palette here is
   bg / panel / border / text / text-2 / text-3, and .lv2-install-card above
   uses the same panel colour, so these sit at the same depth as the app cards
   they follow. */
.lv2 .lv2-install-os {
  background: var(--lv2-panel); border: 1px solid var(--lv2-border);
  border-radius: 12px; padding: 16px 18px;
}
.lv2 .lv2-install-os h4 {
  font-size: 14px; margin: 0 0 10px; display: flex; align-items: center; gap: 8px;
}
.lv2 .lv2-install-os ol { margin: 0; padding-left: 18px; }
.lv2 .lv2-install-os li {
  font-size: 13px; line-height: 1.6; color: var(--lv2-text-2); margin-bottom: 6px;
}
.lv2 .lv2-install-os li:last-child { margin-bottom: 0; }
/* --lv2-text is the brightest of the three; there is no --lv2-text-1. */
.lv2 .lv2-install-os strong { color: var(--lv2-text); font-weight: 600; }

/* ---------- Course / level rail ----------
   The Course section was removed from index.php on 2026-09-14 (it described
   the older course-centric model). These rules are kept, unused, in case a
   replacement section wants them -- delete if none arrives. */
.lv2 .lv2-rail { display: flex; align-items: center; margin: 36px 0 30px; }

/* ---------- How it works / numbered flows ----------
   Step copy stays left-aligned (a numbered sequence reads badly centred) but
   the block itself is centred inside the unified 900px measure. */
.lv2 .lv2-steps { display: flex; flex-direction: column; gap: 26px; max-width: 60ch; margin: 0 auto; }
.lv2 .lv2-step { display: flex; gap: 18px; }
.lv2 .lv2-step-num { font-size: 22px; font-weight: 800; color: var(--lp-accent); width: 32px; flex-shrink: 0; letter-spacing: -0.5px; }
.lv2 .lv2-step h3 { font-size: 16px; font-weight: 600; margin: 0 0 4px; }
.lv2 .lv2-step p { color: var(--lv2-text-2); font-size: 14px; margin: 0; }
/* The beta access path runs longer than the how-it-works steps and reads
   better with a little more measure. */
.lv2 .lv2-steps--wide { max-width: 68ch; }
.lv2 .lv2-step em { font-style: normal; color: var(--lv2-text); font-weight: 600; }

/* ---------- Beta section furniture ----------
   .lv2-subhead was removed 2026-09-14 -- the Beta section's three stacked
   subheads became the Guide/Apply/Install/Download tab group. */
.lv2 .lv2-body-copy { color: var(--lv2-text); font-size: 16px; line-height: 1.7; max-width: 62ch; margin: 0 auto 18px; text-align: center; }
.lv2 .lv2-cta-row { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-top: 32px; }
/* The three profile agreements, listed in Apply step 2. */
.lv2 .lv2-agreement-links {
  list-style: none; padding: 0; margin: 12px 0 0;
  display: flex; flex-direction: column; gap: 8px;
}
.lv2 .lv2-agreement-links li {
  position: relative; padding-left: 18px;
  font-size: 14px; line-height: 1.5; color: var(--lv2-text-3);
}
.lv2 .lv2-agreement-links li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--lp-accent);
}
.lv2 .lv2-inline-link { color: var(--lv2-text); text-decoration: underline; text-underline-offset: 2px; }
.lv2 .lv2-inline-link:hover { color: #fff; }

/* ---------- Access flow diagram ----------
   Two layouts swapped at the file's usual 780px breakpoint: a six-across
   track scaled down to phone width is illegible, so the wide SVG is replaced
   by a vertical one rather than shrunk. All colour comes from landing2.css's
   --lp-* tokens so the figure tracks the palette like everything else. */
.lv2 .lv2-flow-fig { margin: 0 0 44px; padding: 0; }
.lv2 .lv2-flow-fig svg { width: 100%; height: auto; display: block; overflow: visible; }
/* The svg selector above is (0,2,1); these must match it or they lose and
   BOTH layouts render at once. Hence svg.<class> rather than bare .<class>. */
.lv2 .lv2-flow-fig svg.lv2-flow-fig--tall { display: none; }
@media (max-width: 780px) {
  .lv2 .lv2-flow-fig svg.lv2-flow-fig--wide { display: none; }
  .lv2 .lv2-flow-fig svg.lv2-flow-fig--tall { display: block; }
}
.lv2 .lv2-flow-cap {
  text-align: center; font-size: 13px; line-height: 1.6; color: var(--lv2-text-3);
  margin: 18px auto 0; max-width: 52ch;
}

/* Nodes */
.lv2 .lv2-fx-node { fill: var(--lp-accent-dim); stroke: var(--lp-accent); stroke-width: 1.5; }
.lv2 .lv2-fx-node--wait { fill: var(--lp-amber-dim); stroke: var(--lp-amber); }
.lv2 .lv2-fx-node--mail { fill: rgba(139,92,246,0.18); stroke: var(--lp-violet); }
.lv2 .lv2-fx-num { font-family: 'DM Sans', system-ui, sans-serif; font-size: 15px; font-weight: 800; fill: var(--lp-text); }

/* Connectors -- dashed marks the stretch that waits on a human */
.lv2 .lv2-fx-link { stroke: rgba(255,255,255,0.18); stroke-width: 2; }
.lv2 .lv2-fx-link--wait { stroke: var(--lp-amber); stroke-opacity: 0.45; stroke-dasharray: 5 6; }

/* Actor chips + labels */
.lv2 .lv2-fx-actor { font-family: var(--lv2-mono); font-size: 10px; letter-spacing: 0.08em; }
.lv2 .lv2-fx-you  { fill: var(--lp-accent); }
.lv2 .lv2-fx-us   { fill: var(--lp-amber); }
.lv2 .lv2-fx-mail { fill: var(--lp-violet); }
.lv2 .lv2-fx-label { font-family: 'DM Sans', system-ui, sans-serif; font-size: 13px; fill: var(--lv2-text); }

/* Phase banding */
.lv2 .lv2-fx-phase { font-family: var(--lv2-mono); font-size: 10px; letter-spacing: 0.1em; fill: var(--lv2-text-3); }
.lv2 .lv2-fx-divider { stroke: rgba(255,255,255,0.14); stroke-width: 1; stroke-dasharray: 3 5; }

/* ---------- Guide cards ---------- */
.lv2 .lv2-guide-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: 16px; margin-top: 8px; }
.lv2 .lv2-guide-card {
  background: var(--lv2-panel); border: 1px solid var(--lv2-border); border-radius: 14px; padding: 20px;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start; text-decoration: none;
  box-shadow: 0 4px 18px rgba(0,0,0,0.35);
}
a.lv2-guide-card:hover { border-color: rgba(88,101,242,0.5); }
.lv2 .lv2-guide-card--primary { border-color: rgba(88,101,242,0.45); background: linear-gradient(160deg, rgba(88,101,242,0.14), var(--lv2-panel) 60%); }
/* .lv2-guide-card--soon (opacity 0.55) went with the Admin Guide card on
   2026-09-15. Nothing else in the grid is a non-link placeholder. */
.lv2 .lv2-guide-step { font-family: var(--lv2-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--lv2-text-3); }
.lv2 .lv2-guide-card h4 { font-size: 16px; font-weight: 600; margin: 0; }
.lv2 .lv2-guide-card p { font-size: 13px; color: var(--lv2-text-3); margin: 0; }

/* Schools card. Tinted differently from the guide cards on purpose -- it asks
   for a conversation rather than opening a guide, and that difference should
   be visible before the card is read. */
.lv2 .lv2-guide-card--contact {
  border-color: rgba(16,185,129,0.35);
  background: linear-gradient(160deg, rgba(16,185,129,0.10), var(--lv2-panel) 60%);
}
a.lv2-guide-card--contact:hover { border-color: rgba(16,185,129,0.6); }
.lv2 .lv2-guide-card--contact .lv2-guide-step { color: var(--lv2-skill-respond); }
.lv2 .lv2-guide-cue {
  margin-top: auto; padding-top: 10px;
  font-size: 13px; font-weight: 600; color: var(--lv2-skill-respond);
}

/* ---------- Beta signup modal -- same visual language as the real
   l3_boot.js session modal (dark rounded box, backdrop blur) ---------- */
.lv2-beta-modal-overlay { display: none; position: fixed; inset: 0; z-index: 9999; background: rgba(0,0,0,0.75); backdrop-filter: blur(6px); align-items: center; justify-content: center; padding: 20px; }
.lv2-beta-modal-overlay.open { display: flex; }
.lv2-beta-modal-box { background: #0b0e1a; border: 1px solid rgba(255,255,255,0.15); border-radius: 20px; padding: 32px 28px 28px; max-width: 420px; width: 100%; position: relative; box-shadow: 0 24px 64px rgba(0,0,0,0.6); max-height: 90vh; overflow-y: auto; font-family: 'DM Sans', system-ui, sans-serif; color: #eef1fb; }
.lv2-beta-modal-close { position: absolute; top: 14px; right: 16px; background: none; border: none; color: #7c88ac; font-size: 22px; cursor: pointer; line-height: 1; padding: 4px; }
.lv2-beta-modal-close:hover { color: #eef1fb; }
.lv2-beta-modal-box .lv2-section-head { margin-bottom: 8px; }
.lv2-beta-form { display: flex; flex-direction: column; gap: 16px; margin-top: 24px; }
.lv2-beta-form label { font-size: 13px; color: #b7c0dc; display: flex; flex-direction: column; gap: 6px; }
.lv2-beta-form input[type=text], .lv2-beta-form input[type=email], .lv2-beta-form textarea {
  background: #0b0e1a; border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; padding: 10px 12px;
  color: #eef1fb; font-family: 'DM Sans', system-ui, sans-serif; font-size: 14px;
}
.lv2-role-radio-row { display: flex; gap: 16px; font-weight: normal; }
.lv2-role-radio-row label { flex-direction: row; align-items: center; gap: 6px; }

/* ---------- Cookie consent + mic rate-limit overlays ----------
   Ported from indexA.php. Both are body-level fixed overlays, so like
   .lv2-beta-modal-* above they are deliberately NOT scoped under .lv2 --
   id selectors are specific enough that nothing can leak onto the reused
   .lp-* partials. Behaviour is identical to indexA's (same ids, same
   localStorage key, same event); only the palette is this page's.

   The ids and the dai_cookie_ok key are kept EXACTLY as indexA/index.php
   use them on purpose: same origin, so a visitor who accepted the cookie
   notice on one landing page must not be asked again on this one.

   ⚠ These display:none rules are load-bearing. index.php renders both
   dialogs as ordinary markup in the body, so without this file they appear
   as visible inline page content -- which is exactly what happened while
   the two CSS copies were out of step. */
#cookie-overlay, #mic-limit-overlay {
  display: none; position: fixed; inset: 0;
  align-items: center; justify-content: center; padding: 20px;
  font-family: 'DM Sans', system-ui, sans-serif;
}
#cookie-overlay { z-index: 9500; background: rgba(4,6,14,0.82); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
#mic-limit-overlay { z-index: 9999; background: rgba(4,6,14,0.78); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
#cookie-overlay.is-open, #mic-limit-overlay.is-open { display: flex; }

/* Cookie notice */
#cookie-overlay { opacity: 0; transition: opacity 0.3s; }
#cookie-overlay.is-visible { opacity: 1; }
#cookie-box {
  background: linear-gradient(160deg, #18203a 0%, #0b0e1a 100%);
  border: 1px solid rgba(88,101,242,0.35); border-radius: 20px;
  padding: 34px 30px 26px; max-width: 470px; width: 100%; text-align: center;
  color: #eef1fb; box-shadow: 0 32px 80px rgba(0,0,0,0.7);
  max-height: 90vh; overflow-y: auto;
}
#cookie-box img { height: 34px; width: auto; margin-bottom: 16px; }
#cookie-box h2 { font-size: 19px; font-weight: 800; margin: 0 0 12px; letter-spacing: -0.3px; }
#cookie-box p { font-size: 14px; line-height: 1.65; color: #b7c0dc; margin: 0 0 10px; }
#cookie-box p strong { color: #eef1fb; font-weight: 600; }
#cookie-box p a { color: #8f9bff; white-space: nowrap; }
#cookie-accept {
  width: 100%; margin-top: 16px; padding: 13px; border-radius: 10px; border: none;
  background: #5865f2; color: #fff; font-size: 15px; font-weight: 600;
  cursor: pointer; font-family: inherit;
}
#cookie-accept:hover { background: #4752d4; }
#cookie-box .cookie-foot { font-size: 11px; color: #7c88ac; margin: 14px 0 0; }

/* Mic rate-limit notice */
/* 20px, not 18: .lv2-beta-modal-box and #cookie-box are both 20, so this was
   the one dialog on the page with its own corner radius. */
#mic-limit-box {
  background: #0b0e1a; border: 1px solid rgba(255,255,255,0.15); border-radius: 20px;
  padding: 28px 24px 22px; max-width: 340px; width: 100%; text-align: center;
  color: #eef1fb; box-shadow: 0 24px 64px rgba(0,0,0,0.6);
}
#mic-limit-box h3 { font-size: 17px; font-weight: 700; margin: 0 0 10px; letter-spacing: -0.02em; }
/* #e4e8f6 literal, not var(--lv2-text-2), even though that is exactly the
   value it names. The --lv2-* tokens are declared on .lv2, and both overlays
   sit at body level outside every .lv2 wrapper -- verified: the property
   resolves EMPTY here, so the var() would be invalid at computed-value time
   and the colour would silently inherit. Keep these two literals in step with
   --lv2-text-2 by hand, or move the token declarations to :root. */
#mic-limit-box p { font-size: 14px; color: #e4e8f6; margin: 0 0 20px; line-height: 1.55; }
#mic-limit-box .mic-limit-btns { display: flex; flex-direction: column; gap: 10px; }
#mic-limit-box button { border-radius: 10px; padding: 12px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit; }

/* EMPHASIS SWAPPED 2026-09-15. These two were the wrong way round: "Come back
   later" -- which dismisses the overlay and scrolls the visitor to the top,
   ending the demo -- was the filled accent button, while "Continue without
   audio scoring" -- which keeps them in the activity they were part-way
   through -- was the quiet one. On a page whose entire job is to get someone
   to try the product, the loudest control was the one that sends them away.

   Neither button does anything but dismiss; the limit is enforced server-side
   and mic_shared.js has already returned null by the time this fires. So this
   is purely about which option is offered first, and continuing is plainly it.

   Body and ghost text also moved off #b7c0dc onto --lv2-text-2, matching the
   brighter scale the rest of the page settled on. */
#mic-limit-continue { background: #5865f2; border: none; color: #fff; }
#mic-limit-continue:hover { background: #4752d4; }
#mic-limit-return { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); color: #e4e8f6; }
#mic-limit-return:hover { background: rgba(255,255,255,0.13); }

/* ---------- Footer ---------- */

/* =============================================================================
   NAV BREAKPOINTS -- deliberately last in the file.
   These carry the same specificity (0,2,0) as the base .lv2-nav-links /
   .lv2-nav-signin / .lv2-nav-beta rules above, so a media query alone does not
   beat them -- only source order does. Sitting them next to the mobile-nav
   component (which is defined before those base rules) left the desktop links
   visible at phone width. Keep this block at the bottom.
   ========================================================================== */
/* 2026-09-20: THIS IS NOW MOBILE-FIRST, AND THE WIDTH AT WHICH THE DESKTOP BAR
   COMES BACK IS SET PER LANGUAGE, by landing/lv2/_nav_bp.php in the <head>.
   See that file for the measured table and how to re-measure.

   It used to be one hardcoded `@media (max-width: 780px)`, measured against
   English labels. Measuring all 25 landing languages showed 24 of them need
   MORE room: Irish 1038px, Portuguese 1005, French 1000, German 965 -- against
   English's 780. Between roughly 780 and 1040 every translated bar was
   overfull; it did not LOOK broken only because the labels wrapped onto a
   second line inside the 64px bar (see the nowrap note above).

   Mobile-first rather than a single wide breakpoint, for two reasons: a fixed
   1038px would hand English a hamburger across 250px of desktop it fits in
   comfortably; and if the inline block ever fails to load, what is left is the
   phone nav, which works at every width -- the other way round leaves an
   overflowing bar on phones.

   Everything that leaves the bar reappears in the panel, Beta included. */
.lv2 .lv2-nav-links,
.lv2 .lv2-nav-signin,
.lv2 .lv2-nav-beta { display: none; }
.lv2 .lv2-nav-toggle { display: flex; }

/* Glass panels on phones: the gutters vanish, so a 20px radius and 40px of
   side padding only eat the measure. Tighten both and close the gaps. Here
   rather than beside the glass block for the same source-order reason --
   .lv2-section is defined after it. */
@media (max-width: 780px) {
  :root { --lv2-glass-radius: 14px; --lv2-glass-pad-y: 40px; --lv2-glass-pad-x: 20px; }
  .lv2 .lv2-section { padding: 14px 0; }

  /* Inset the panels so they still read as cards with the photo around them.
     Full-bleed would hide the radius and leave only thin horizontal stripes of
     image between sections, which looks like a rendering fault, not a design. */
  .lv2 .lv2-section > .lv2-wrap,
  #contact > .lp-section,
  #lp-footer .lv2-foot-inner { margin-left: 12px; margin-right: 12px; }

  /* The hero scrim is a 100deg gradient tuned for the DESKTOP two-column
     layout, fading out to the right where the demo panel sits. Once the grid
     stacks, the text runs full width and the right-hand half of every line
     lands on unscrimmed photo. Go vertical and full-width here instead. */
  .lv2 .lv2-hero::before {
    background: linear-gradient(180deg, rgba(11,14,26,0.70) 0%, rgba(11,14,26,0.84) 100%);
  }

  /* Lighter backdrop for phones -- 133 KB rather than 242 KB, and this is
     exactly where the saving matters most. Same fallback ordering as the
     desktop rule at the top of the file. */
  body::before {
    background-image: url('assets/images/bookshelf-bg-1280.jpg');
    background-image: image-set(
      url('assets/images/bookshelf-bg-1280.webp') type('image/webp'),
      url('assets/images/bookshelf-bg-1280.jpg')  type('image/jpeg'));
  }
}

/* ── Language picker ─────────────────────────────────────────────────────────
   MOVED 2026-09-20 to public/assets/css/lang_picker.css, loaded by lv2/_head.php
   immediately after this file (and by the eight registration pages, which load
   no other stylesheet -- phase 2 of the registration translation, Addendum 130).

   The rules are unchanged apart from a var() fallback on each --lv2-* token, so
   the picker also renders on a page that does not define them. A var() fallback
   is ignored when the property IS set, so nothing here changed for this page.

   If the picker ever loses its styling on the landing page, the cause is that
   _head.php stopped loading lang_picker.css -- not anything in this file.
   ─────────────────────────────────────────────────────────────────────────── */


/* 2026-09-21 (website management) -- the announcement banner and the Admin's
   preview strip, both printed by landing/lv2/_banner.php above the sticky nav.
   In normal flow on purpose: it scrolls away with the page. */
.lv2.lv2-site-banner { background: linear-gradient(90deg, #4f46e5, #0ea5e9); color: #fff; font-size: 14px; font-weight: 600; }
.lv2 .lv2-site-banner-inner { padding: 9px 16px; text-align: center; line-height: 1.45; }
.lv2 .lv2-site-banner-inner a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.lv2 .lv2-site-banner-inner a:hover { text-decoration-thickness: 2px; }
.lv2.lv2-preview-strip { background: #f59e0b; color: #1a1300; font-size: 13px; font-weight: 700; text-align: center; padding: 7px 16px; }