/* Blog dark theme - brings /blog and every post onto the same premium dark scheme as the landing
 * page. Loaded from <head>, but every rule is scoped with `body.bd-dark` so it outranks the
 * single-class rules that practice.css and the per-page inline <style> blocks define.
 * Tokens match public/index.html: ink #05060d, surfaces rgba(255,255,255,.03), brand #7c8cff,
 * aqua #34e0d0. */

body.bd-dark {
  background: #05060d;
  color: #e7e9f5;
}
body.bd-dark h1, body.bd-dark h2, body.bd-dark h3, body.bd-dark h4 { color: #fff; }
body.bd-dark p, body.bd-dark li { color: rgba(255,255,255,.62); }
body.bd-dark a { color: #9aa6ff; }
body.bd-dark a:hover { color: #fff; }
body.bd-dark strong { color: #fff; }

/* ---- Floating glass nav ----
   Every number here is copied from body.ph-theme .landing-nav in public/index.html. They used to
   differ (top 12 vs 16, width -32 vs -40, height 60 vs 64, links 38 vs 36, CTA 42 vs 40, .5s vs
   .35s), which is why the bar visibly shifted when moving between the landing page and a blog or
   practice page. Keep the two in sync when either changes.
   Unlike the landing page the pill is shown AT REST too. The landing nav floats over a tall hero
   with a grid and three drifting glows, so a transparent bar there reads as intentional; blog and
   content pages open on a flat panel, where the same transparent bar just looks like unstyled links
   with no container. Geometry still matches the landing exactly - only the resting chrome differs. */
body.bd-dark .ph {
  /* Auto-margin centring, no transform - see the matching note in public/index.html. A transformed
     ancestor becomes the backdrop root (so the blur below would sample nothing) and forces the bar
     onto a composited layer at a half-pixel offset on odd viewport widths. */
  position: fixed; top: 16px; left: 0; right: 0; margin-inline: auto; z-index: 1000;
  width: calc(100% - 40px); max-width: 1152px; height: auto;
  box-sizing: border-box; padding: 12px 20px;
  display: flex; align-items: center; justify-content: flex-start;
  /* 8px, matching the landing (public/index.html, refinement layer section 5). If these two
     numbers ever disagree the header visibly re-corners when you move between the landing and
     any content page - which is exactly what it did. */
  border: 1px solid transparent; border-radius: 8px;
  /* No bar at the top of the page, matching the landing. The logo, links and CTA are wanted from the
     first frame; a filled pill sitting over the first screenful is not. Everything that makes it a
     bar - this fill and the border - is bound to `nav-scrolled`, which nav-scroll.js sets only
     after a real scroll gesture. The band on ::before is bound to `nav-past-top`, on raw offset,
     because it is what stops content showing through this transparent bar.
     When it does appear it is solid, not glass. The bar used to paint nothing itself and lean on a
     blurred ::before, which read as a reflective panel floating over the cards - brightest at the
     left and right edges where the sheen gradient met the border. One flat colour, one hairline
     border, no outer shadow: the old 0 18px 50px -25px glow was what made the edges look lit. */
  background: transparent; box-shadow: none;
  transition: background .4s ease, border-color .4s ease;
}
body.bd-dark.nav-scrolled .ph {
  background: rgba(9, 13, 24, .98);
  border-color: rgba(255,255,255,.08);
  /* Appearing has to beat the scroll. A leisurely fade-in means a fast flick drags a heading up
     behind a bar that is still see-through; leaving can stay slow because nothing is hidden on the
     way out. Same reasoning as the landing page. */
  transition-duration: .09s;
}
/* Frosted panel as a sibling layer behind the nav's content rather than a property on the nav -
   see the matching note in public/index.html. backdrop-filter makes its element a backdrop root and
   composites every descendant against the blurred backdrop; keeping the white CTA a SIBLING of the
   filtered layer instead of a child means it is painted once and never resampled. Absolute
   positioning keeps it out of the flex flow (abspos children are not flex items), and inset:-1px
   spans the border box so the panel sits under the 1px border instead of inside it. */
body.bd-dark .ph::before { content: none; }
/* Dark band behind the pill so scrolled content never shows through the top gap. */
body.bd-dark::before {
  content: ''; position: fixed; top: 0; left: 0; right: 0; height: 120px; z-index: 990;
  pointer-events: none; opacity: 0; transition: opacity .12s linear;
  /* Solid to ~94px - the bar ends near 74px, so cards get roughly 20px of clean dark below it
     before the band fades, instead of sliding up against its underside. */
  background: linear-gradient(180deg, #05060d 0%, #05060d 78%, rgba(5,6,13,0) 100%);
}
body.bd-dark.nav-past-top::before { opacity: 1; }
/* The band is the layer that actually hides content under the pill, and a scroll listener runs after
   the scroll has already painted - about two frames late. Binding its opacity to scroll offset moves
   the decision onto the compositor, evaluated in the same frame as the scroll, with the class toggle
   left as the fallback where animation-timeline is unsupported. 1px step at the same 16px threshold
   nav-scroll.js uses for `nav-past-top`, so the two agree - which is the reason the band follows
   offset rather than the gesture: the compositor path cannot know about a gesture anyway. */
@keyframes cg-ph-band { from { opacity: 0; } to { opacity: 1; } }
@supports (animation-timeline: scroll()) {
  body.bd-dark::before {
    animation: cg-ph-band 1ms linear both;
    animation-timeline: scroll(root block);
    animation-range: 16px 17px;
  }
}
/* Until the first paint settles the nav snaps to its state instead of fading in - browsers restore
   the scroll position after the script runs, so a refresh part-way down the page would otherwise
   animate the pill and band in from nothing every time. Mirrors the landing page's .nav-ready. */
body.bd-dark:not(.nav-ready) .ph,
body.bd-dark:not(.nav-ready)::before { transition: none !important; }

body.bd-dark .ph-brand, body.bd-dark .ph-word { color: #fff; }

/* pricing.html is an APP page: it loads styles.min.css + app-dark.css + this file, but NOT
   practice.css - and practice.css is where every content page gets its .ph-brand / .ph-mark
   layout. Without them the brand fell back to display:block and .cg-mark's width/height:100%
   resolved against an unsized parent, so the logo painted at its intrinsic 64px, stacked the
   wordmark underneath it, and made the fixed header 111px instead of 66px. Worse, the box only
   reached that size once the SVG had loaded, so the header visibly grew a beat after paint - the
   flicker at the top of /pricing. Sizing .ph-mark reserves the space up front. Values copied from
   practice.css; keep the two in step. */
body.bd-dark .ph-brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 700; font-size: 18px; letter-spacing: 0; line-height: 1;
}
body.bd-dark .ph-brand:hover { text-decoration: none; }
body.bd-dark .ph-mark {
  width: 32px; height: 32px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
}
body.bd-dark .ph-mark .cg-mark { width: 100%; height: 100%; display: block; }
/* Wordmark = the logo's #2dd4bf, same as .cg-grid-accent in styles.css. Not the aqua UI accent. */
body.bd-dark .ph-accent { color: #2dd4bf; }

/* The markup puts the section links, Sign In and the CTA inside one .ph-nav, which bunches them all
   against the right edge. `display: contents` dissolves that wrapper so every link becomes a direct
   flex child of .ph, and the two auto margins below split the leftover space evenly - the same
   logo / links / actions rhythm the landing page gets from space-between, with the CTA pulled out of
   the link group. Done in CSS so no blog or practice page markup has to change. */
body.bd-dark .ph-nav { display: contents; }

/* Section links: same box as body.ph-theme .landing-nav-links a. The .ph-cta / .ph-signin rules
   below must out-specify this one - the earlier version lost every shared property to it, which
   resized the CTA and, on hover, erased its white background entirely (dark text on a dark bar,
   so the button looked like it vanished). */
body.bd-dark .ph-nav > a {
  position: relative; height: 36px; padding: 0 14px; border-radius: 8px;
  display: inline-flex; align-items: center;
  font-size: 14px; font-weight: 500; color: rgba(255,255,255,.65);
  text-decoration: none; transition: color .3s;
}
body.bd-dark .ph-nav > a + a { margin-left: 4px; }
body.bd-dark .ph-nav > a:first-child { margin-left: auto; }
body.bd-dark .ph-nav > a:hover { color: #fff; background: transparent; }

/* Gradient underline on hover, matching the landing page. Section links only. */
body.bd-dark .ph-nav > a:not(.ph-signin):not(.ph-cta)::after {
  content: ''; position: absolute; left: 14px; right: 14px; bottom: 7px; height: 1px;
  background: #34e0d0;
  transform: scaleX(0); transform-origin: left; transition: transform .3s;
}
body.bd-dark .ph-nav > a:not(.ph-signin):not(.ph-cta):hover::after { transform: scaleX(1); }

/* Bare text like the landing page's Sign In - no link padding or 36px box. With padding it ran 28px
   wider, and since the auto margins split the leftover space evenly that pushed the whole section-link
   group 14px left of where it sits on the landing page. */
body.bd-dark .ph-nav > a.ph-signin {
  margin-left: auto; padding: 0; height: auto; border-radius: 0;
  color: rgba(255,255,255,.7) !important;
}
body.bd-dark .ph-nav > a.ph-signin:hover { color: #fff !important; background: transparent; }
body.bd-dark .ph-nav > a.ph-cta {
  height: 40px; padding: 0 16px; margin-left: 12px; border-radius: 8px;
  background: #fff; color: #05060d !important; font-weight: 600; font-size: 14px;
  box-shadow: none; transition: transform .3s, background .3s;
}
body.bd-dark .ph-nav > a.ph-cta:hover { background: #eef1ff; transform: translateY(-2px); }

/* The nav is fixed, so content needs to clear it. */
body.bd-dark .hub-hero, body.bd-dark .hero { padding-top: 104px; }

/* ---- Hub hero + post hero ---- */
body.bd-dark .hub-hero, body.bd-dark .hero {
  background: #05060d; border-bottom: 1px solid rgba(255,255,255,.08);
}
body.bd-dark .hub-hero h1, body.bd-dark .hero h1 { color: #fff; letter-spacing: -.02em; }
body.bd-dark .hub-hero p, body.bd-dark .hero p { color: rgba(255,255,255,.55); }
body.bd-dark .hub-tag, body.bd-dark .hero-tag {
  display: inline-block; font-size: 11px; font-weight: 500; letter-spacing: .18em;
  text-transform: uppercase; color: #7c8cff;
  border: 1px solid rgba(124,140,255,.25); background: rgba(91,107,255,.1);
  padding: 6px 14px; border-radius: 999px;
}
body.bd-dark .crumb, body.bd-dark .crumb a { color: rgba(255,255,255,.45); }
body.bd-dark .crumb span { color: rgba(255,255,255,.7); }

/* ---- Hub cards ---- */
body.bd-dark .hub-main { background: transparent; }
body.bd-dark .blogcard {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1);
  border-radius: 16px;
  transition: transform .45s cubic-bezier(.16,1,.3,1), border-color .45s, box-shadow .45s;
}
body.bd-dark .blogcard:hover {
  transform: translateY(-6px); border-color: rgba(124,140,255,.45);
  box-shadow: 0 24px 60px -24px rgba(64,72,230,.65);
}
body.bd-dark .blogcard a { color: inherit; text-decoration: none; }
body.bd-dark .blogcard h2, body.bd-dark .blogcard h3 { color: #fff; }
body.bd-dark .blogcard p { color: rgba(255,255,255,.55); }
body.bd-dark .bc-meta { color: rgba(255,255,255,.4); }

/* ---- Pager ---- */
body.bd-dark .blog-pager a, body.bd-dark .blog-pager span.bp-off {
  background: rgba(255,255,255,.03); border-color: rgba(255,255,255,.1); color: #fff;
}
body.bd-dark .blog-pager a:hover { border-color: rgba(124,140,255,.5); }
body.bd-dark .blog-pager .bp-off { color: rgba(255,255,255,.3); }
body.bd-dark .blog-pager .bp-status { color: rgba(255,255,255,.45); background: none; }

/* ---- Post body ---- */
body.bd-dark .bp-body { color: rgba(255,255,255,.68); }
body.bd-dark .bp-body h2, body.bd-dark .bp-body h3 { color: #fff; }
body.bd-dark .bp-body a { color: #9aa6ff; }
body.bd-dark .bp-meta { color: rgba(255,255,255,.4); }
body.bd-dark .bp-body table { border-color: rgba(255,255,255,.1); }
body.bd-dark .bp-body th, body.bd-dark .bp-body td {
  border-color: rgba(255,255,255,.1); color: rgba(255,255,255,.68);
}
body.bd-dark .bp-body th { background: rgba(255,255,255,.04); color: #fff; }
body.bd-dark .bp-body code, body.bd-dark .bp-body pre {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1);
  color: #cfd5ff; border-radius: 8px;
}
body.bd-dark .bp-body blockquote {
  border-left: 2px solid rgba(124,140,255,.5); background: rgba(255,255,255,.03);
  color: rgba(255,255,255,.62);
}
body.bd-dark .bp-note, body.bd-dark .bp-faq, body.bd-dark .block, body.bd-dark .faq {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1);
  border-radius: 16px; color: rgba(255,255,255,.62);
}
/* .block is the ONLY member of the group above with no padding of its own - practice.css gives it
   `margin-top:36px` and nothing else, while .faq carries 14px/18px and .bp-note 12px/16px. Once the
   dark layer turned it into a bordered panel, its <h2> sat flush against the border while the
   .domains rows and .qcards inside kept their own inset, which read as a misaligned heading.
   Separate rule, not merged above, so the other three keep their existing padding. */
body.bd-dark .block { padding: 22px 24px; }
@media (max-width: 640px) {
  body.bd-dark .block { padding: 18px 16px; }
}
body.bd-dark .bp-note strong, body.bd-dark .bp-faq strong { color: #fff; }
body.bd-dark .bp-faq summary, body.bd-dark .faq summary { color: #fff; }
/* The blog markup uses BOTH <a> and <span> tag pills, and the source rule is
   `.bp-tags a,.bp-tags span{background:#eef2ff;color:var(--navy)}`. Styling only the anchor left the
   spans on their light lavender fill while --navy flipped to the light brand tint - brand blue on
   #eef2ff measured a 1.62 contrast ratio, i.e. unreadable. Both selectors must be covered. */
body.bd-dark .bp-tags a,
body.bd-dark .bp-tags span {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.1);
  color: rgba(255,255,255,.7); border-radius: 999px;
}
body.bd-dark .bp-tags a:hover { border-color: rgba(124,140,255,.5); color: #fff; }
body.bd-dark .bp-related { border-top: 1px solid rgba(255,255,255,.1); }
body.bd-dark .bp-related a { color: #9aa6ff; }

/* ---- CTAs ---- */
body.bd-dark .bp-cta {
  background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.025));
  border: 1px solid rgba(255,255,255,.1); border-radius: 24px;
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
}
body.bd-dark .bp-cta h2, body.bd-dark .bp-cta h3 { color: #fff; }
body.bd-dark .bp-cta p { color: rgba(255,255,255,.6); }
body.bd-dark .btn { border-radius: 12px; font-weight: 600; }
/* Solid cyan, not a blue-to-cyan gradient. The indigo end of that ramp is the last of the old
   palette on the public pages, and on a page whose only accent is cyan a two-colour button
   reintroduces a hue nothing else uses. One rule, so all 415 dark pages agree - the certification
   paths previously carried a .path-cta override for exactly this, which is now redundant. */
body.bd-dark .btn-primary {
  background: #34e0d0; color: #05060d !important; border: 0;
}
body.bd-dark .btn-primary:hover { background: #5ceadd; }
body.bd-dark .btn-primary:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
/* The same single accent now covers every "this one is active" surface on the public pages:
   primary buttons, the selected vendor chip on /practice, the selected tool tab, progress meters
   and the nav hover underline. They all used the same #5b6bff -> #34e0d0 ramp, which put an indigo
   nothing else on the page uses into the one colour that is supposed to mean "act here". */
body.bd-dark .btn-ghost, body.bd-dark .btn:not(.btn-primary) {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.15); color: #fff;
}

/* ---- GEO fact blocks ---- */
body.bd-dark .cg-geo-fact {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1); border-radius: 16px;
}
body.bd-dark .cg-geo-fact h3 { color: #fff; }
body.bd-dark .cg-geo-fact p { color: rgba(255,255,255,.6); }
body.bd-dark .cg-geo-trust {
  background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.025));
  border: 1px solid rgba(255,255,255,.1); border-radius: 24px;
}
body.bd-dark .cg-geo-trust h2 { color: #fff; }
body.bd-dark .cg-geo-trust p { color: rgba(255,255,255,.6); }
body.bd-dark .cg-geo-trust strong { color: #34e0d0; }

/* ---- Footer ---- */
body.bd-dark .cg-ftr {
  background: #05060d; border-top: 1px solid rgba(255,255,255,.08); color: rgba(255,255,255,.55);
}
body.bd-dark .cg-ftr h4 { color: #fff; }
body.bd-dark .cg-ftr a { color: rgba(255,255,255,.55); }
body.bd-dark .cg-ftr a:hover { color: #fff; }
body.bd-dark .cg-ftr-bottom, body.bd-dark .cg-ftr-legal { color: rgba(255,255,255,.35); }
body.bd-dark .cg-ftr-social a svg { fill: rgba(255,255,255,.55); }
body.bd-dark .cg-ftr-social a:hover svg { fill: #fff; }

@media (max-width: 768px) {
  body.bd-dark .ph { top: 8px; width: calc(100% - 16px); height: 56px; padding: 0 12px; }
  body.bd-dark .ph-nav a { padding: 0 9px; font-size: 12px; }
  /* The desktop min-width keeps the CTA box identical across pages so the nav does not shift
     between the landing page and a content page. Below 768px the bar is tight and the label
     is the only flexible item, so the pin is released. */
  body.bd-dark .ph-cta { height: 36px; padding: 0 12px; border-radius: 10px; margin-left: 8px; min-width: 0; }
  body.bd-dark::before { height: 76px; }
  body.bd-dark .hub-hero, body.bd-dark .hero { padding-top: 88px; }
}


/* ============================================================================================
   PUBLIC CONTENT PAGES - DARK LAYER
   Extends this file from /blog only to every remaining public page: /practice (169),
   /study-guide (168), /compare (15), /tools (5) and 33 root SEO / info / legal pages.
   The logged-in app (dashboard, exam, history, profile, bookmarks, study-plan, admin) is
   deliberately NOT converted and never loads this file.

   Those pages are styled by public/css/practice.css plus one of only 14 distinct per-page inline
   <style> blocks. The inline blocks sit in the body, so they win any specificity tie against a
   stylesheet - every rule below therefore carries the `body.bd-dark` prefix, which outranks the
   single-class selectors both sources use.
   ============================================================================================ */

/* ---- 1. Token overrides -------------------------------------------------------------------
   practice.css routes most of its colour through :root custom properties, so redefining them on
   body.bd-dark flips the majority of the sheet in one step; the blocks below only have to deal
   with hardcoded literals. --navy is a TEXT colour almost everywhere (links, .fact-n, .qnum,
   .ecard-code), so it becomes the brand tint; the two places it is used as a BACKGROUND
   (.btn-primary) or as text on white (.ph-cta, .vchip:hover) are overridden explicitly. */
body.bd-dark {
  --navy: #7c8cff;
  --ink: rgba(255,255,255,.92);
  --muted: rgba(255,255,255,.45);
  --teal: #34e0d0;
  --teal2: #34e0d0;
  --line: rgba(255,255,255,.1);
  --bg: #05060d;
}

/* ---- 2. Nav clearance ---------------------------------------------------------------------
   practice.css ships a 64px sticky full-width bar; this file makes it a fixed floating pill, so
   whatever each page type opens with has to clear it. The wrappers differ by page type:
   .page-hero (practice / study-guide / best-* / about), .hub-hero (vendor hubs / paths /
   compare), .tool-wrap (tools), .info-wrap (methodology, corrections, legal), .wrap (404). */
body.bd-dark .page-hero { padding-top: 92px; }
body.bd-dark .page-hero .hero { padding-top: 8px; }
body.bd-dark .tool-wrap,
body.bd-dark .info-wrap,
body.bd-dark .wrap { padding-top: 104px; }
/* Pages whose first child is a bare <main> with no wrapper class. */
body.bd-dark > main:first-of-type:not([class]) { padding-top: 104px; }

/* ---- 3. Page hero band --------------------------------------------------------------------
   The light theme paints a navy-to-teal gradient here. Flatten it to ink, matching what the blog
   hero already does, so every content page shares one treatment. Deliberately NOT the landing's
   glowing hero: that stays the marketing centrepiece, and adding glows here would also restyle
   the already-approved blog hub, which uses the same .hub-hero class. */
body.bd-dark .page-hero {
  background: #05060d;
  border-bottom: 1px solid rgba(255,255,255,.08);
  color: #fff;
}
body.bd-dark .hub-hero { padding-top: 104px; }
body.bd-dark .page-hero .hero h1 { color: #fff; letter-spacing: -.02em; }
body.bd-dark .page-hero .lede { color: rgba(255,255,255,.72); }
body.bd-dark .page-hero .sub,
body.bd-dark .hero-note { color: rgba(255,255,255,.5); }
body.bd-dark .lede { color: rgba(255,255,255,.72); }
body.bd-dark .sub, body.bd-dark .muted { color: rgba(255,255,255,.45); }
body.bd-dark .page-hero .crumb,
body.bd-dark .page-hero .crumb a { color: rgba(255,255,255,.45); }
body.bd-dark .page-hero .crumb span { color: rgba(255,255,255,.75); }

/* ---- 4. Shared surfaces -------------------------------------------------------------------
   Every white card in the light theme becomes the same translucent panel. */
body.bd-dark .fact,
body.bd-dark .domains li,
body.bd-dark .qcard,
body.bd-dark .cat-grid a,
body.bd-dark .ecard a,
body.bd-dark .trust-panel,
body.bd-dark .sg-concepts li,
body.bd-dark .crit-list li,
body.bd-dark .tool-card,
body.bd-dark .tool-hub-card,
body.bd-dark .cpf-path,
body.bd-dark .rm-vendor,
body.bd-dark .diffs li,
body.bd-dark .links a {
  background: rgba(255,255,255,.03);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px;
  color: rgba(255,255,255,.72);
}
/* Slightly recessed variants (the light theme used #f7f9fc / #f8fafc rather than #fff). */
body.bd-dark .sg-tips li,
body.bd-dark .rm-node,
body.bd-dark .cpf-path li,
body.bd-dark .disclaimer-box {
  background: rgba(255,255,255,.02);
  border: 1px solid rgba(255,255,255,.08);
  color: rgba(255,255,255,.66);
}
body.bd-dark .ecard a:hover,
body.bd-dark .cat-grid a:hover,
body.bd-dark .rm-node:hover {
  border-color: rgba(124,140,255,.45);
  background: rgba(255,255,255,.06);
  box-shadow: 0 18px 44px -22px rgba(64,72,230,.6);
}

/* ---- 5. Facts / stats ---------------------------------------------------------------------- */
body.bd-dark .fact-n { color: #fff; }
body.bd-dark .fact-l, body.bd-dark .dcount { color: rgba(255,255,255,.45); }
body.bd-dark .hub-stats span { color: rgba(255,255,255,.5); }
body.bd-dark .hub-stats strong { color: #fff; }
body.bd-dark .no-results { color: rgba(255,255,255,.45); }

/* ---- 6. Headings and body copy ------------------------------------------------------------- */
body.bd-dark .block h2, body.bd-dark .vsec-head h2 { color: #fff; }
body.bd-dark .vsec-head { border-bottom: 1px solid rgba(255,255,255,.1); }
body.bd-dark .vsec-count { color: rgba(255,255,255,.45); }

/* ---- 7. Exam cards -------------------------------------------------------------------------- */
body.bd-dark .ecard-code { color: #fff; }
body.bd-dark .ecard-name { color: rgba(255,255,255,.58); }
body.bd-dark .ecard-meta { color: #34e0d0; }
body.bd-dark .ecard-cta { color: #7c8cff; }
body.bd-dark .ecard .lvl {
  background: rgba(124,140,255,.14); color: #9aa6ff; border-radius: 999px;
}
body.bd-dark .ecard-why { color: rgba(255,255,255,.5); }

/* ---- 8. Question cards (practice pages - the highest-volume component) --------------------- */
body.bd-dark .qnum { color: #7c8cff; }
body.bd-dark .qtext { color: #fff; }
body.bd-dark .qdom {
  color: rgba(255,255,255,.6); background: rgba(255,255,255,.06); border-radius: 6px;
}
body.bd-dark .qmulti {
  color: #ffd7a1; background: rgba(255,180,84,.14); border-radius: 6px;
}
body.bd-dark .opt {
  border: 1px solid rgba(255,255,255,.1); color: rgba(255,255,255,.72); border-radius: 8px;
}
body.bd-dark .opt.ok {
  border-color: rgba(52,224,208,.4); background: rgba(52,224,208,.1); color: #fff;
}
body.bd-dark .opt-k { color: rgba(255,255,255,.4); }
body.bd-dark .opt.ok .opt-k, body.bd-dark .opt-badge { color: #34e0d0; }
body.bd-dark .qexp {
  color: rgba(255,255,255,.68); background: rgba(255,255,255,.03);
  border-left: 3px solid #34e0d0;
}
body.bd-dark .qexp strong { color: #fff; }

/* ---- 9. Explanation blocks ------------------------------------------------------------------ */
body.bd-dark .exp-answer {
  color: #34e0d0; background: rgba(52,224,208,.1);
  border: 1px solid rgba(52,224,208,.28); border-radius: 8px;
}
body.bd-dark .exp-detail { color: rgba(255,255,255,.68); }
body.bd-dark .exp-why-h { color: rgba(255,255,255,.42); }
body.bd-dark .exp-why li { color: rgba(255,255,255,.58); }
body.bd-dark .exp-why .wk { color: #ff8b86; }

/* ---- 10. Study guides ----------------------------------------------------------------------- */
body.bd-dark .sg-domain h2 { color: #fff; }
body.bd-dark .sg-dmeta { color: rgba(255,255,255,.45); }
body.bd-dark .sg-concepts li { border-left: 3px solid #34e0d0; }
body.bd-dark .sg-toc a {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
  color: rgba(255,255,255,.8);
}
body.bd-dark .sg-toc a:hover { background: rgba(255,255,255,.1); color: #fff; }
body.bd-dark .page-hero .sg-toc a {
  background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.16); color: #fff;
}
body.bd-dark .page-hero .sg-toc a:hover { background: rgba(255,255,255,.14); }
body.bd-dark .how-to { color: rgba(255,255,255,.68); }

/* ---- 11. Vendor hubs: search + chips --------------------------------------------------------- */
body.bd-dark .hub-search input {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14);
  color: #fff; box-shadow: none; border-radius: 12px;
}
body.bd-dark .hub-search input::placeholder { color: rgba(255,255,255,.35); }
body.bd-dark .hub-search input:focus { outline: 2px solid rgba(124,140,255,.6); outline-offset: 2px; }
body.bd-dark .vchip {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14);
  color: rgba(255,255,255,.8);
}
body.bd-dark .vchip span { background: rgba(255,255,255,.1); }
/* The light theme inverts to a white chip here; on ink use the brand fill instead so the label
   never ends up dark-on-dark via --navy. */
body.bd-dark .vchip:hover,
body.bd-dark .vchip.active {
  background: #34e0d0; color: #05060d;
  border-color: transparent;
}
body.bd-dark .vchip.active span { background: rgba(5,6,13,.16); color: #05060d; }

/* ---- 12. Intro / trust panels ---------------------------------------------------------------- */
body.bd-dark .trust-panel { border-radius: 16px; box-shadow: none; }
body.bd-dark .trust-panel h2 { color: #fff; }
body.bd-dark .trust-panel li { color: rgba(255,255,255,.62); }
body.bd-dark .trust-panel li::before { color: #34e0d0; }

/* ---- 13. FAQ blocks --------------------------------------------------------------------------- */
body.bd-dark .faq h3 { color: #fff; }
body.bd-dark .faq p { color: rgba(255,255,255,.62); }

/* ---- 14. Comparison tables (/compare) --------------------------------------------------------- */
body.bd-dark .cmp {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px; overflow: hidden;
}
body.bd-dark .cmp th, body.bd-dark .cmp td {
  border-color: rgba(255,255,255,.1); color: rgba(255,255,255,.7);
}
body.bd-dark .cmp thead th { background: rgba(255,255,255,.06); color: #fff; }
body.bd-dark .cmp tbody th { background: rgba(255,255,255,.03); color: #fff; }
body.bd-dark .diffs li { color: rgba(255,255,255,.68); }
body.bd-dark .pill-row a {
  background: rgba(124,140,255,.12); border: 1px solid rgba(124,140,255,.25);
  color: #9aa6ff;
}
body.bd-dark .pill-row a:hover { background: rgba(124,140,255,.2); color: #fff; }

/* ---- 15. Roadmap / certification paths -------------------------------------------------------- */
body.bd-dark .rm-code, body.bd-dark .rm-name { color: #fff; }
body.bd-dark .rm-tier-label {
  background: rgba(52,224,208,.12); border: 1px solid rgba(52,224,208,.28); color: #34e0d0;
}
body.bd-dark .rm-arrow { color: rgba(255,255,255,.3); }
body.bd-dark .rm-intro, body.bd-dark .path-tier { color: rgba(255,255,255,.65); }
body.bd-dark .tier-h { color: #fff; }
body.bd-dark .cpf-tier { color: #fff; }
body.bd-dark .cpf-meta, body.bd-dark .cpf-note { color: rgba(255,255,255,.45); }
body.bd-dark .cpf-tab {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.12);
  color: rgba(255,255,255,.75);
}
body.bd-dark .cpf-tab.active {
  background: #34e0d0; color: #05060d; border-color: transparent;
}

/* ---- 16. Tools -------------------------------------------------------------------------------- */
body.bd-dark .tool-sub { color: rgba(255,255,255,.5); }
body.bd-dark .tool-field label { color: rgba(255,255,255,.75); }
body.bd-dark .tool-field input, body.bd-dark .tool-field select {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12); color: #fff;
}
body.bd-dark .tool-field input[readonly] { background: rgba(255,255,255,.02); color: rgba(255,255,255,.55); }
body.bd-dark .tool-field input::placeholder { color: rgba(255,255,255,.3); }
body.bd-dark .meter { background: rgba(255,255,255,.08); }
body.bd-dark .meter-fill { background: #34e0d0; }
body.bd-dark .hint, body.bd-dark .info-updated { color: rgba(255,255,255,.4); }
body.bd-dark .err-msg { color: #ff8b86; }
body.bd-dark .chk-item { color: rgba(255,255,255,.7); }
body.bd-dark .disclaimer-box { color: rgba(255,255,255,.5); border-radius: 12px; }
/* Readiness bands keep their traffic-light meaning, retinted for ink. */
body.bd-dark .result-panel.band-ready {
  background: rgba(52,224,208,.1); border-color: rgba(52,224,208,.35); color: rgba(255,255,255,.8);
}
body.bd-dark .result-panel.band-almost {
  background: rgba(255,180,84,.1); border-color: rgba(255,180,84,.35); color: rgba(255,255,255,.8);
}
body.bd-dark .result-panel.band-notready {
  background: rgba(255,107,107,.1); border-color: rgba(255,107,107,.35); color: rgba(255,255,255,.8);
}
body.bd-dark .band-ready .band-badge { background: #34e0d0; color: #05060d; }
body.bd-dark .band-almost .band-badge { background: #ffb454; color: #05060d; }
body.bd-dark .band-notready .band-badge { background: #ff6b6b; color: #05060d; }
body.bd-dark .result-panel h2, body.bd-dark .result-panel h3, body.bd-dark .next-action { color: #fff; }

/* ---- 17. Info / legal pages -------------------------------------------------------------------- */
body.bd-dark .info-cta { border-top: 1px solid rgba(255,255,255,.1); }
body.bd-dark .links a.primary,
body.bd-dark .links a.primary:hover {
  background: #34e0d0; color: #05060d; border-color: transparent;
}
body.bd-dark .prose, body.bd-dark .lead { color: rgba(255,255,255,.68); }

/* ---- 18. Legacy slim footer (.pf) --------------------------------------------------------------- */
body.bd-dark .pf { background: #05060d; border-top: 1px solid rgba(255,255,255,.08); }
body.bd-dark .pf-links a { color: rgba(255,255,255,.6); }
body.bd-dark .pf-links a:hover { color: #fff; }
body.bd-dark .pf-disclaimer, body.bd-dark .pf-copy { color: rgba(255,255,255,.32); }

/* ---- 19. Site footer (.cg-ftr) - mirror the landing footer's reference geometry ----------------
   Same decode as .lf-* in public/index.html: rail 1152 with px-5 so content is 1112, six tracks at
   gap 40 with the brand spanning two (344) and four 152px link columns, pt-16 / pb-14, and the
   bottom bar on its own py-7 hairline. */
body.bd-dark .cg-ftr { padding: 64px 20px 0; font-size: 13px; }
body.bd-dark .cg-ftr-top {
  max-width: 1112px; display: grid; grid-template-columns: repeat(6, 1fr); gap: 40px;
  padding-bottom: 56px; border-bottom: none;
}
body.bd-dark .cg-ftr-brand { grid-column: span 2; }
body.bd-dark .cg-ftr-cols {
  grid-column: span 4; display: grid; grid-template-columns: subgrid; gap: 40px;
}
@supports not (grid-template-columns: subgrid) {
  body.bd-dark .cg-ftr-cols { display: contents; }
}
body.bd-dark .cg-ftr-brand p { color: rgba(255,255,255,.5); font-size: 14px; line-height: 1.625; max-width: 320px; }
body.bd-dark .cg-ftr-about,
body.bd-dark .cg-ftr-col h4 {
  color: rgba(255,255,255,.8); font-size: 12px; line-height: 1.35; font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em; margin: 0 0 16px; min-height: 32px;
}
/* .58 not .45 - see the note on .lf-col a in index.html; .45 measured 4.47:1, under AA. */
body.bd-dark .cg-ftr-col a {
  color: rgba(255,255,255,.58); font-size: 14px; font-weight: 400; line-height: 1.4; padding: 5px 0;
}
body.bd-dark .cg-ftr-col a:first-of-type { padding-top: 0; }
body.bd-dark .cg-ftr-col a:hover { color: #fff; }
body.bd-dark .cg-ftr-col a.cg-ftr-all { color: #34e0d0; font-weight: 500; }
body.bd-dark .cg-ftr-col a.cg-ftr-all:hover { color: #7cf0e4; }
body.bd-dark .cg-ftr-bottom {
  max-width: 1112px; margin: 0 auto; padding: 28px 0;
  border-top: 1px solid rgba(255,255,255,.08); font-size: 12px; color: rgba(255,255,255,.4);
}
body.bd-dark .cg-ftr-social a {
  width: 36px; height: 36px; border-radius: 12px;
  border: 1px solid rgba(255,255,255,.1); background: rgba(255,255,255,.03);
  color: rgba(255,255,255,.6);
  transition: transform .3s, border-color .3s, color .3s, background .3s;
}
body.bd-dark .cg-ftr-social a:hover {
  transform: translateY(-2px); border-color: rgba(124,140,255,.5); background: rgba(255,255,255,.06);
}
body.bd-dark .cg-ftr-social a:nth-child(1):hover svg { fill: #fff; }
body.bd-dark .cg-ftr-social a:nth-child(2):hover svg { fill: #0a66c2; }
body.bd-dark .cg-ftr-social a:nth-child(3):hover svg { fill: #ff0000; }
body.bd-dark .cg-ftr-social a:nth-child(4):hover svg { fill: #e1306c; }

@media (max-width: 1023px) {
  body.bd-dark .cg-ftr-top { grid-template-columns: 1fr 1fr; }
  body.bd-dark .cg-ftr-brand { grid-column: 1 / -1; }
  body.bd-dark .cg-ftr-cols { grid-column: 1 / -1; grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  body.bd-dark .cg-ftr-top { grid-template-columns: 1fr; gap: 32px; }
  body.bd-dark .cg-ftr-cols { grid-template-columns: 1fr; gap: 32px; }
}

/* ---- 20. Narrow screens ------------------------------------------------------------------------- */
@media (max-width: 768px) {
  body.bd-dark .page-hero { padding-top: 78px; }
  body.bd-dark .hub-hero { padding-top: 88px; }
  body.bd-dark .tool-wrap,
  body.bd-dark .info-wrap,
  body.bd-dark .wrap { padding-top: 88px; }
}


/* ============================================================================================
   EXAM CARDS - PREMIUM POLISH
   The generic surface treatment in section 4 made these cards legible but flat: they read as a
   plain list under a polished hero. This block replaces that treatment for .ecard only, giving
   each card a raised panel, a vendor chip, a teal question-count stat, and a real secondary
   action instead of trailing grey text. Search, filter and hero behaviour are untouched - this
   is presentation only.
   ============================================================================================ */

/* Fixed 4 / 2 / 1 tracks rather than practice.css's auto-fill minmax(280px), so a row never
   reflows to an odd count. At the 1216px content width that is 4 x 292px. */
body.bd-dark .ecards {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  align-items: stretch;
}
@media (max-width: 1023px) { body.bd-dark .ecards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px)  { body.bd-dark .ecards { grid-template-columns: 1fr; } }

/* Grid items stretch by default and the link fills the item, so every card in a row is the same
   height; min-height keeps short cards from collapsing below the tall ones' rhythm. */
body.bd-dark .ecard { display: flex; }
body.bd-dark .ecard a {
  position: relative;
  display: flex; flex-direction: column; gap: 0;
  width: 100%; min-height: 176px; height: auto;
  padding: 16px 16px 14px;
  background: #0f172a;
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 14px;
  box-shadow: 0 1px 0 rgba(255,255,255,.03) inset;
  transition: transform .35s cubic-bezier(.16,1,.3,1), border-color .35s, box-shadow .35s, background .35s;
}
body.bd-dark .ecard a:hover {
  transform: translateY(-4px);
  background: #111827;
  border-color: rgba(52,224,208,.45);
  box-shadow: 0 22px 50px -22px rgba(52,224,208,.38), 0 0 0 1px rgba(52,224,208,.10);
}
body.bd-dark .ecard a:focus-visible {
  outline: 2px solid rgba(124,140,255,.7); outline-offset: 3px;
}

/* Vendor chip. .lvl is the equivalent slot on the collection hubs (it carries the exam level),
   so the two share one look. */
body.bd-dark .ecard-pill,
body.bd-dark .ecard .lvl {
  align-self: flex-start;
  margin: 0 0 10px;
  padding: 3px 9px;
  font-size: 10px; font-weight: 600; line-height: 1.5;
  letter-spacing: .14em; text-transform: uppercase;
  color: #9aa6ff;
  background: rgba(124,140,255,.12);
  border: 1px solid rgba(124,140,255,.26);
  border-radius: 999px;
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}

/* Title. Two lines are reserved so the stat pill and divider sit on the same baseline across a
   row even when one title wraps and its neighbour does not. */
body.bd-dark .ecard-code {
  display: block;
  color: #fff; font-size: 15px; font-weight: 600;
  line-height: 1.35; letter-spacing: -.01em;
  min-height: 2.7em;
}
body.bd-dark .ecard-name {
  color: rgba(255,255,255,.5); font-size: 12.5px; line-height: 1.45; margin-top: 2px;
}
body.bd-dark .ecard-why { color: rgba(255,255,255,.45); font-size: 12px; line-height: 1.5; margin-top: 6px; }

/* Question count as a teal stat pill instead of a plain metadata line. */
body.bd-dark .ecard-meta {
  align-self: flex-start;
  margin: 10px 0 0;
  padding: 4px 10px;
  font-size: 11.5px; font-weight: 600; line-height: 1.5;
  color: #34e0d0;
  background: rgba(52,224,208,.10);
  border: 1px solid rgba(52,224,208,.24);
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* Secondary action pinned to the bottom, above a hairline, so it reads as the card's affordance
   rather than leftover text. margin-top:auto is what keeps it aligned across mismatched cards. */
body.bd-dark .ecard-cta {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid rgba(255,255,255,.07);
  display: flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: #9aa6ff;
  transition: color .3s;
}
body.bd-dark .ecard a:hover .ecard-cta { color: #34e0d0; }

/* Section headings ("AWS  12 exams") - more air and a lighter rule. */
body.bd-dark .vsec { margin-top: 48px; }
body.bd-dark .vsec-head {
  align-items: baseline; gap: 12px;
  margin-bottom: 20px; padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,.08);
}
body.bd-dark .vsec-head h2 {
  margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.01em; color: #fff;
}
body.bd-dark .vsec-count {
  font-size: 11px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255,255,255,.42);
}

@media (max-width: 600px) {
  body.bd-dark .ecard a { min-height: 0; }
  body.bd-dark .ecard-code { min-height: 0; }
}

/* Study-guide cards reuse .ecard but carry no vendor pill and no question-count stat, so the
   176px floor above left ~50px of dead space under the title. Scope the taller floor to cards
   that actually have a stat row; grid stretch still equalises every row either way. */
body.bd-dark .ecard a { min-height: 132px; }
body.bd-dark .ecard a:has(.ecard-meta) { min-height: 176px; }

/* ---- Card refinements: two stats on one row, tighter box ----------------------------------
   The cards were taller than their content warranted. Rather than only shrinking them, the
   question count is now paired with a coarse level pill (Foundational / Intermediate / Advanced,
   derived from the exam name - null levels simply omit the pill), and the padding and title
   reserve are tightened. Net effect: more information in a shorter card. */
body.bd-dark .ecard-stats {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 8px;
}
body.bd-dark .ecard-stats .ecard-meta { margin: 0; }
body.bd-dark .ecard-level {
  padding: 4px 10px;
  font-size: 11.5px; font-weight: 600; line-height: 1.5;
  color: rgba(255,255,255,.62);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 999px;
  white-space: nowrap;
}
/* Tighter box now that the stat row carries the extra content. */
body.bd-dark .ecard a { padding: 14px 15px 12px; }
body.bd-dark .ecard a:has(.ecard-meta) { min-height: 168px; }
body.bd-dark .ecard-pill, body.bd-dark .ecard .lvl { margin-bottom: 8px; }
body.bd-dark .ecard-cta { padding-top: 10px; }

/* 4 columns only where a card still has room (>=1181px gives ~270px each). Below that it is the
   2-up tablet layout - four columns at 1024px would squeeze the stat row onto two lines. */
@media (max-width: 1279px) { body.bd-dark .ecards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px)  { body.bd-dark .ecards { grid-template-columns: 1fr; } }

/* ---- Final card polish ---------------------------------------------------------------------
   1. Whole-card click target. The <a> already wraps all the card content, but it carries the 14px
      radius, and Chrome clips hit-testing to the border radius - so the four rounded corners fell
      through to the <li> behind. A square stretched-link overlay makes the full card rectangle
      clickable, corners included.
   2. Long titles clamp to two lines so a stray long name cannot push one card's rhythm out of step
      with its row (only one exam in the catalog currently runs to three lines).
   3. A little more air between the hero stats and the first vendor section. */
body.bd-dark .ecard { position: relative; }
body.bd-dark .ecard a::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
}
/* Keep the real content above the overlay so text stays selectable and hover targets still work. */
body.bd-dark .ecard a > * { position: relative; z-index: 2; }

body.bd-dark .ecard-code {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
body.bd-dark .ecard-name {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

body.bd-dark .hub-main { padding-top: 16px; }

/* ============================================================================================
   PUBLIC PRICING PAGE
   pricing.html is a hybrid: logged-out visitors get this dark shell, logged-in users keep the
   original light billing UI inside the app sidebar. `bd-dark` is added to <body> by the page's
   inline script ONLY when there is no examToken, so every rule here is inert for signed-in users.
   Prices, plan names, the launch offer and all checkout behaviour are untouched - presentation
   only.
   ============================================================================================ */
body.bd-dark .pricing-page { color: rgba(255,255,255,.7); }
body.bd-dark .pricing-account-card,
body.bd-dark .pricing-proof-grid div,
body.bd-dark .voucher-redeem-section {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1);
  border-radius: 16px; color: rgba(255,255,255,.7);
}
body.bd-dark .pricing-account-card span,
body.bd-dark .pricing-proof-grid span { color: rgba(255,255,255,.45); }
body.bd-dark .pricing-account-card strong,
body.bd-dark .pricing-proof-grid strong { color: #fff; }
body.bd-dark .pricing-account-card p { color: rgba(255,255,255,.55); }
body.bd-dark .pricing-assurance-row span {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.1);
  color: rgba(255,255,255,.6); border-radius: 999px;
}
body.bd-dark .pricing-assurance-row span::before { color: #34e0d0; }
body.bd-dark .pricing-current-context { color: rgba(255,255,255,.5); }

/* Launch offer band - keep the promo prominent, retinted onto ink. */
body.bd-dark .pricing-offer-band {
  background: linear-gradient(90deg, rgba(91,107,255,.22), rgba(52,224,208,.12));
  border: 1px solid rgba(52,224,208,.28); border-radius: 16px; color: #fff;
}
body.bd-dark .pricing-offer-band > div > span { color: #34e0d0; }
body.bd-dark .pricing-offer-band strong { color: #fff; }
body.bd-dark .pricing-offer-band p { color: rgba(255,255,255,.7); }
body.bd-dark .pricing-offer-code {
  background: rgba(255,255,255,.12); color: #34e0d0; border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
body.bd-dark .pricing-offer-band .btn { background: #fff; color: #05060d; }
body.bd-dark .pricing-offer-band .btn:hover { background: #eef1ff; }

/* Plan cards - the SaaS treatment used by the landing pricing grid. */
body.bd-dark .pricing-card {
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.1);
  border-radius: 20px; color: rgba(255,255,255,.7);
  transition: transform .35s cubic-bezier(.16,1,.3,1), border-color .35s, box-shadow .35s;
}
body.bd-dark .pricing-card:hover {
  transform: translateY(-4px); border-color: rgba(124,140,255,.45);
  box-shadow: 0 26px 60px -26px rgba(64,72,230,.6);
}
body.bd-dark .pricing-card.popular {
  border-color: rgba(124,140,255,.5);
  background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.025));
  box-shadow: 0 40px 100px -40px rgba(91,107,255,.9);
}
body.bd-dark .pricing-card h3, body.bd-dark .pricing-card-top { color: #fff; }
/* The accent hairline each card carries along its top edge. */
body.bd-dark .pricing-card::before { background: rgba(255,255,255,.14); }
body.bd-dark .pricing-card-single-exam::before { background: #34e0d0; }
body.bd-dark .pricing-card-pro-monthly::before { background: #7c8cff; }
body.bd-dark .pricing-card-pro-annual::before { background: linear-gradient(90deg,#5b6bff,#34e0d0); }
body.bd-dark .pricing-popular-tag {
  background: linear-gradient(90deg,#5b6bff,#34e0d0); color: #05060d;
}
body.bd-dark .pricing-current-tag { background: rgba(52,224,208,.18); color: #34e0d0; }
body.bd-dark .pricing-plan-kicker {
  background: rgba(124,140,255,.12); color: #9aa6ff; border-radius: 999px;
}
body.bd-dark .pricing-card-pro-annual .pricing-plan-kicker {
  background: rgba(52,224,208,.12); color: #34e0d0;
}
body.bd-dark .pricing-plan-fit,
body.bd-dark .pricing-card-note {
  background: rgba(255,255,255,.02); border: 1px solid rgba(255,255,255,.08);
  color: rgba(255,255,255,.6); border-radius: 12px;
}
body.bd-dark .pricing-card-pro-annual .pricing-plan-fit {
  background: rgba(52,224,208,.06); border-color: rgba(52,224,208,.2);
}
body.bd-dark .pricing-old-price { color: rgba(255,255,255,.35); }
body.bd-dark .pricing-discount-note,
body.bd-dark .pricing-grandfathered {
  background: rgba(52,224,208,.1); border: 1px solid rgba(52,224,208,.28); color: #7cf0e4;
}
body.bd-dark .pricing-action .btn-secondary {
  background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.15); color: #fff;
}
body.bd-dark .pricing-trust-line { color: rgba(255,255,255,.4); }
body.bd-dark .pricing-about, body.bd-dark .pricing-compare, body.bd-dark .pricing-faq {
  color: rgba(255,255,255,.65);
}
body.bd-dark .pricing-about h2, body.bd-dark .pricing-compare h2, body.bd-dark .pricing-faq h2 { color: #fff; }
body.bd-dark .voucher-redeem-controls .form-input {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.12); color: #fff;
}

/* ============================================================================================
   EXAM LIBRARY (/practice) - RESTRAINED PASS
   The hub had three wrapped rows of 21 vendor pills, a stat block of three big numbers, and
   blue-tinted cards with a teal glow on hover. Against the reworked landing it read as a louder,
   more colourful page. This block brings it onto the same surface language: one neutral dark
   fill, one hairline border, 8px radius, no gradients and no glow, with cyan used only to mark
   state and the question count.

   Everything here is scoped to `.ecards.lib` or to the hub's own hero, because .ecard is shared
   with the study-guide hub, the best-* pages, the vendor hubs and the collection hubs - those
   keep their existing treatment until the same pass is applied to them.
   ============================================================================================ */

/* ---- hero: no eyebrow pill, tighter rhythm ---- */
body.bd-dark .hub-hero { padding-bottom: 26px; }
body.bd-dark .hub-hero h1 { font-size: 32px; margin: 0 0 8px; }
body.bd-dark .hub-hero p { font-size: 15px; margin: 0 auto 20px; max-width: 620px; }

/* ---- search: icon inside the field ---- */
body.bd-dark .hub-search { position: relative; max-width: 560px; }
body.bd-dark .hub-search-ic {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: rgba(255,255,255,.38); pointer-events: none;
}
body.bd-dark .hub-search input {
  padding: 12px 16px 12px 40px; border-radius: 8px;
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.12);
  color: #fff; box-shadow: none; font-size: 14px;
}
body.bd-dark .hub-search input:focus {
  outline: 2px solid rgba(34,211,238,.55); outline-offset: 2px; border-color: rgba(34,211,238,.4);
}

/* ---- filters: six pills, flat, cyan only for the active one ---- */
body.bd-dark .vchips { gap: 8px; margin-top: 14px; }
body.bd-dark .vchip {
  border-radius: 8px; padding: 7px 12px; font-size: 13px; font-weight: 500;
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.12);
  color: rgba(255,255,255,.72); box-shadow: none;
  transition: border-color .2s, background .2s, color .2s;
}
body.bd-dark .vchip span {
  background: rgba(255,255,255,.07); color: inherit; border-radius: 5px;
  padding: 1px 6px; font-size: 11px; font-variant-numeric: tabular-nums;
}
body.bd-dark .vchip:hover { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.22); color: #fff; }
body.bd-dark .vchip.active,
body.bd-dark .vchip.active:hover {
  background: rgba(34,211,238,.10); border-color: rgba(34,211,238,.45); color: #22d3ee;
}
body.bd-dark .vchip.active span { background: rgba(34,211,238,.16); color: #22d3ee; }
body.bd-dark .vchip:focus-visible { outline: 2px solid rgba(34,211,238,.6); outline-offset: 2px; }
/* The dialog opener is deliberately quieter than a filter - it is a way in, not a choice. */
body.bd-dark .vchip-more { background: transparent; border-style: dashed; color: rgba(255,255,255,.6); }
body.bd-dark .vchip-more svg { width: 13px; height: 13px; flex: 0 0 auto; opacity: .8; }
body.bd-dark .vchip-more.active { border-style: solid; }

/* ---- one inline summary line instead of three stat blocks ---- */
body.bd-dark .hub-summary {
  margin: 14px 0 0; font-size: 12.5px; color: rgba(255,255,255,.45);
  font-variant-numeric: tabular-nums;
}
body.bd-dark .hub-summary span { color: rgba(255,255,255,.75); font-weight: 500; }
body.bd-dark .hub-summary i { font-style: normal; color: rgba(255,255,255,.25); margin: 0 4px; }

/* ---- the All vendors dialog ---- */
body.bd-dark .vpanel-backdrop { position: fixed; inset: 0; z-index: 200; background: rgba(3,4,9,.72); }
body.bd-dark .vpanel {
  position: fixed; z-index: 201; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: min(680px, calc(100vw - 32px)); max-height: min(70vh, 620px);
  display: flex; flex-direction: column;
  background: #0a0d14; border: 1px solid rgba(255,255,255,.12); border-radius: 8px;
  box-shadow: 0 24px 60px -24px rgba(0,0,0,.8);
}
body.bd-dark .vpanel[hidden], body.bd-dark .vpanel-backdrop[hidden] { display: none; }
body.bd-dark .vpanel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 18px; border-bottom: 1px solid rgba(255,255,255,.1);
}
body.bd-dark .vpanel-head h2 { margin: 0; font-size: 15px; font-weight: 600; color: #fff; }
body.bd-dark .vpanel-close {
  appearance: none; cursor: pointer; width: 30px; height: 30px; line-height: 1;
  display: grid; place-items: center;
  background: transparent; border: 1px solid rgba(255,255,255,.12); border-radius: 6px;
  color: rgba(255,255,255,.6);
}
body.bd-dark .vpanel-close svg { width: 15px; height: 15px; }
body.bd-dark .vpanel-close:hover { color: #fff; border-color: rgba(255,255,255,.28); }
body.bd-dark .vpanel-close:focus-visible { outline: 2px solid rgba(34,211,238,.6); outline-offset: 2px; }
body.bd-dark .vpanel-list { display: flex; flex-wrap: wrap; gap: 8px; padding: 18px; overflow-y: auto; }
body.bd-dark.vpanel-open { overflow: hidden; }

/* ---- cards ---- */
/* .ecard is no longer an <a> wrapper here: the anchor sits on the code alone (see the note in
   generate-exam-pages.js), so the earlier premium `.ecard a` panel rules have to be unwound or
   they would paint a 176px card around the title text. */
body.bd-dark .ecards.lib .ecard a {
  display: inline; width: auto; min-height: 0; height: auto; padding: 0;
  background: none; border: 0; border-radius: 0; box-shadow: none; transform: none;
  color: inherit; text-decoration: none;
}
body.bd-dark .ecards.lib .ecard a:hover { background: none; border: 0; box-shadow: none; transform: none; }

body.bd-dark .ecards.lib { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; align-items: stretch; }
@media (max-width: 1279px) { body.bd-dark .ecards.lib { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1023px) { body.bd-dark .ecards.lib { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 639px)  { body.bd-dark .ecards.lib { grid-template-columns: 1fr; } }

body.bd-dark .ecards.lib .ecard {
  position: relative;
  display: flex; flex-direction: column;
  min-height: 160px; padding: 14px 15px 12px;
  background: rgba(255,255,255,.025);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 8px;
  box-shadow: none;
  transition: border-color .2s, background .2s;
}
/* Border only. No lift, no glow - a translate on hover made a 152-card grid feel restless. */
body.bd-dark .ecards.lib .ecard:hover {
  background: rgba(255,255,255,.04);
  border-color: rgba(34,211,238,.35);
  transform: none; box-shadow: none;
}
/* Whole-card click is done in practice-hub.js, NOT with a stretched ::after overlay.
   The overlay version was broken: `.ecard > * { z-index: 2 }` raised .ecard-name, .ecard-stats and
   .ecard-cta above it (equal z-index, later in DOM order wins), so clicking any of them selected
   text instead of navigating - most of the card was dead. Putting the overlay on top instead would
   fix the click and kill text selection. The JS handler gives both: it navigates on click unless
   the user actually selected something, and it honours ctrl/cmd/middle-click for a new tab. */
/* Kill the stretched overlay the earlier premium block puts on `.ecard a::after` - it still
   matches the title anchor here and would be a second, competing hit layer over the whole card. */
body.bd-dark .ecards.lib .ecard a::after { content: none; }
body.bd-dark .ecards.lib .ecard { cursor: pointer; }
/* :has() for a real keyboard tab; :focus-within is the fallback so the ring can never go missing. */
body.bd-dark .ecards.lib .ecard:has(a:focus-visible),
body.bd-dark .ecards.lib .ecard:focus-within {
  outline: 2px solid rgba(34,211,238,.75); outline-offset: 2px; border-color: rgba(34,211,238,.5);
}

/* Vendor as quiet eyebrow text, not a pill. */
body.bd-dark .ecards.lib .ecard-vendor {
  display: block; margin: 0 0 8px;
  font-size: 10px; font-weight: 600; line-height: 1.4;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255,255,255,.4);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.bd-dark .ecards.lib .ecard-code {
  display: block; min-height: 2.6em;
  color: #fff; font-size: 14.5px; font-weight: 600; line-height: 1.35; letter-spacing: -.01em;
}
/* 81 of 152 exams have no subtitle and 16 wrap to two lines. The slot is always rendered and
   reserves one line, so a missing subtitle no longer pulls the metadata up by 19px. */
body.bd-dark .ecards.lib .ecard-name {
  display: block; min-height: 1.45em;
  color: rgba(255,255,255,.5); font-size: 12.5px; line-height: 1.45; margin-top: 2px;
}
/* margin-top:auto here rather than on .ecard-cta: the stats row and the action then both sit a
   fixed distance off the card foot, so a two-line subtitle eats the slack above them instead of
   pushing them down out of line with the rest of the row. */
body.bd-dark .ecards.lib .ecard-stats {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: auto; padding-top: 10px;
}
/* Question count as plain cyan metadata - it was a bordered pill competing with the difficulty. */
body.bd-dark .ecards.lib .ecard-meta {
  margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
  font-size: 12px; font-weight: 500; color: #22d3ee; font-variant-numeric: tabular-nums;
}
/* Difficulty is the only thing left carrying a badge. */
body.bd-dark .ecards.lib .ecard-level {
  padding: 2px 7px; border-radius: 5px;
  font-size: 10.5px; font-weight: 500; line-height: 1.5; letter-spacing: 0; text-transform: none;
  color: rgba(255,255,255,.55);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.1);
}
body.bd-dark .ecards.lib .ecard-cta {
  margin-top: 10px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.07);
  display: flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 500; color: rgba(255,255,255,.55);
  transition: color .2s;
}
body.bd-dark .ecards.lib .ecard-cta svg { width: 13px; height: 13px; flex: 0 0 auto; transition: transform .2s; }
body.bd-dark .ecards.lib .ecard:hover .ecard-cta { color: #22d3ee; }
body.bd-dark .ecards.lib .ecard:hover .ecard-cta svg { transform: translateX(3px); }

@media (max-width: 639px) {
  body.bd-dark .hub-hero h1 { font-size: 26px; }
  body.bd-dark .hub-search { max-width: none; }
  body.bd-dark .vchips { justify-content: flex-start; }
  body.bd-dark .ecards.lib .ecard-code { min-height: 0; }
}

/* Phones get a bottom sheet rather than a centred dialog: it is within thumb reach, and the
   categories go two-up so 16 of them do not become a long scroll. */
@media (max-width: 639px) {
  body.bd-dark .vpanel {
    top: auto; bottom: 0; left: 0; transform: none;
    width: 100%; max-width: none; max-height: 80vh;
    border-radius: 12px 12px 0 0; border-bottom: 0;
  }
  body.bd-dark .vpanel-head { padding: 14px 16px; }
  body.bd-dark .vpanel-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 16px; }
  body.bd-dark .vpanel-list .vchip { justify-content: space-between; width: 100%; }
}

/* Content pages kept the nav CTA at every width while the landing dropped it below 520px, so on a
   phone the header carried logo + Sign In + a white button and ran out of room. Match the landing:
   below 520 the CTA goes and Sign In carries the conversion path. Guides have their own, better
   placed call to action at the foot of the article (.ln-examcta). */
@media (max-width: 520px) {
  body.bd-dark .ph-nav > a.ph-cta { display: none; }
}

/* "Certify these skills" - the honest call to action on a guide. Same neutral surface, hairline
   border and 8px radius as every other card in the restrained pass; cyan is used only on the links. */
body.bd-dark .ln-examcta {
  margin: 40px 0 8px;
  padding: 20px 22px;
  background: rgba(255,255,255,.025);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 8px;
}
body.bd-dark .ln-examcta h2 {
  margin: 0; font-size: 15px; font-weight: 600; color: #fff; letter-spacing: 0;
}
body.bd-dark .ln-examcta p {
  margin: 8px 0 0; font-size: 13.5px; line-height: 1.6; color: rgba(255,255,255,.6);
}
body.bd-dark .ln-examcta ul {
  margin: 14px 0 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: 8px;
}
body.bd-dark .ln-examcta li { margin: 0; }
body.bd-dark .ln-examcta a {
  display: inline-block; padding: 7px 12px;
  border: 1px solid rgba(34,211,238,.35); border-radius: 8px;
  background: rgba(34,211,238,.08); color: #22d3ee;
  font-size: 13px; font-weight: 500; text-decoration: none;
  transition: background .2s, border-color .2s;
}
body.bd-dark .ln-examcta a:hover {
  background: rgba(34,211,238,.14); border-color: rgba(34,211,238,.55); text-decoration: none;
}
body.bd-dark .ln-examcta a:focus-visible { outline: 2px solid rgba(34,211,238,.75); outline-offset: 2px; }
@media (max-width: 639px) {
  body.bd-dark .ln-examcta { padding: 16px; }
  body.bd-dark .ln-examcta ul { flex-direction: column; }
  body.bd-dark .ln-examcta a { display: block; text-align: center; }
}

/* ── The account chip ─────────────────────────────────────────────────────────────────────────
   A circle of initials and a name, not a lone letter. "A" on its own reads as a placeholder - as
   though the page had not finished working out who you are - which is the opposite of what an
   account control should say.

   .ph-nav is display:contents, so THIS wrapper is the flex child of .ph, and it is also the
   positioning context the dropdown hangs from. That is why the button moved inside it. */
body.bd-dark .ph-acct { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; }

/* A pill, not a button-button: it opens a menu rather than committing to anything, so it sits at the
   weight of a nav item with a hairline around it. */
body.bd-dark .ph-acct .ph-user {
  display: inline-flex; align-items: center; gap: 7px;
  height: 32px; padding: 0 10px 0 3px;
  border-radius: 999px;
  background: rgba(52,224,208,.07);
  border: 1px solid rgba(52,224,208,.24);
  color: rgba(255,255,255,.82);
  font: inherit; font-size: 12.5px; font-weight: 500;
  cursor: pointer; text-decoration: none;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
body.bd-dark .ph-acct .ph-user::after { display: none; }
body.bd-dark .ph-acct .ph-user:hover {
  background: rgba(52,224,208,.14); border-color: rgba(52,224,208,.44); color: #fff;
}
body.bd-dark .ph-acct .ph-user:focus-visible {
  outline: none; border-color: rgba(52,224,208,.7);
  box-shadow: 0 0 0 2px rgba(52,224,208,.22);
}
/* Open: reads as pressed, not merely hovered. */
body.bd-dark .ph-acct.is-open .ph-user {
  background: rgba(52,224,208,.16); border-color: rgba(52,224,208,.55); color: #fff;
}
body.bd-dark .ph-acct .ph-user-ini {
  width: 26px; height: 26px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: rgba(52,224,208,.16);
  color: #7ff0e4; font-size: 10.5px; font-weight: 600; letter-spacing: .01em;
}
body.bd-dark .ph-acct .ph-user-name {
  /* Long display names and email locals both turn up here. Cap and ellipsis, so no name can stretch
     the header - the width is settled at first paint either way. */
  max-width: 108px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── Auth-aware header: two navs in the markup, one painted ───────────────────────────────────
   js/auth-boot.js stamps html.cg-authed (or html.cg-anon) from localStorage BEFORE first paint, so
   these two rules decide the header in the same paint as everything else. That is the whole fix for
   the flicker: nothing is swapped after load because nothing needs to be.

   `!important` is required, not decorative. `body.bd-dark .ph-nav > a` is (0,2,2) and sets
   `display: inline-flex`; `html:not(.cg-authed) .ph-only-auth` is (0,2,1) and would lose, leaving
   both navs on screen at once. The `hidden` attribute has exactly this problem on these pages too.

   Keyed on `:not(.cg-authed)` rather than on `.cg-anon` so that a crawler and a no-JS visitor -
   neither of which runs auth-boot.js, so neither gets any class - still receive a complete public
   header. The public set is also the version we want indexed. */
html:not(.cg-authed) .ph-only-auth { display: none !important; }
html.cg-authed .ph-only-anon { display: none !important; }

/* Both from the custom properties auth-boot.js set on <html>, rendered through `content` so they
   are there in the FIRST paint. There is no body to write text into when that script runs, and
   filling the chip afterwards would be a second paint inside the very thing this design exists to
   stop flickering. The accessible name is the button's aria-label, which js/account-menu.js later
   upgrades to the real name - an attribute, so no pixels move. */
/* `:empty` is what makes this a FALLBACK rather than the only source. At first paint the spans are
   empty, so the custom properties render and the chip is complete in that paint. When
   js/account-menu.js later writes the same glyphs as real text the spans stop being :empty and this
   stops applying - so nothing is ever drawn twice, and the visible result is identical because both
   derive the initials and the first name from the same value by the same rule. Keep those two
   derivations in step.
   Why bother: pseudo-element content is not selectable, not findable by ctrl-F, and absent entirely
   if the property is missing - which happens when a token exists but examUser does not. */
body.bd-dark .ph-acct .ph-user-ini:empty::after { content: var(--cg-ini, ""); }
body.bd-dark .ph-acct .ph-user-name:empty::after { content: var(--cg-first, ""); }

/* The section the reader is in. Same gradient underline the other links get on hover, but held on -
   it says "you are here" in the language the nav already speaks rather than adding a colour. */
body.bd-dark .ph-nav > a.ph-current { color: #fff; }
body.bd-dark .ph-nav > a.ph-current::after { transform: scaleX(1); }

/* The signed-in CTA, and the one honest thing to say before /api/dashboard/next answers.
   It used to ship as "Start practice" and become "Resume practice", which meant every reader with an
   unfinished attempt was told the wrong thing for the first fraction of a second. So it ships
   NEUTRAL - "Practice", true in both branches - and resolves once. Width is reserved for the longest
   final label, so resolving cannot move anything.

   `.is-pending` is quieter but NOT pointer-events:none. A dead button is worse than a quiet one:
   /next can 401 or time out, and a reader who clicks in that window should still reach the
   catalogue, which is where the href already points. learn-auth.js clears the class on every
   outcome including failure, so this state never sticks. */
body.bd-dark .ph-nav > a.ph-cta.ph-only-auth { min-width: 148px; justify-content: center; }
/* Signed in, this is a UTILITY, not a conversion. The base .ph-cta is solid white because on the
   landing page and on a public guide it is the one thing the page is asking for; for a reader who
   already has an account it is "get back to what you were doing", and solid white made it the
   brightest object in the header - louder than the identity chip beside it and louder than the page.

   Neutral surface and a hairline instead. No new colour: this leaves the cyan chip as the only
   accent in the right-hand cluster, which is the hierarchy that was missing - identity in colour,
   action in weight. The public CTA is untouched and stays solid white, the same split the Plans page
   makes between its signed-in and public buttons.

   No hover lift either. A 2px rise suits a page's primary call to action; on a utility button next
   to an account menu it reads as fidgety. */
body.bd-dark .ph-nav > a.ph-cta.ph-only-auth {
  background: rgba(255,255,255,.09);
  border: 1px solid rgba(255,255,255,.20);
  color: #fff !important;
  font-weight: 500;
}
body.bd-dark .ph-nav > a.ph-cta.ph-only-auth:hover {
  background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.34); transform: none;
}
body.bd-dark .ph-nav > a.ph-cta.ph-only-auth:focus-visible {
  outline: none; border-color: rgba(52,224,208,.7); box-shadow: 0 0 0 2px rgba(52,224,208,.22);
}
/* Pending: quieter again, so the resolved state is a step UP rather than a change of colour. */
body.bd-dark .ph-nav > a.ph-cta.ph-only-auth.is-pending {
  background: rgba(255,255,255,.04);
  border-color: rgba(255,255,255,.12);
  color: rgba(255,255,255,.5) !important;
}

/* THE AUTO MARGINS HAVE TO MOVE, or the signed-in bar collapses against the logo.
   The section above puts `margin-left: auto` on `.ph-nav > a:first-child` and on `.ph-signin`, and
   both of those are `.ph-only-anon` - so in the signed-in state both sit on a display:none element,
   where an auto margin does nothing, and the whole group bunches left with dead space to its right.
   That is what "sparse right-side cluster" looked like from the other direction.

   So the same two-auto-margin split is restated onto the elements that ARE visible: Dashboard opens
   the link group, the avatar opens the action group. Leftover space divides evenly between them and
   the bar gets the landing page's logo / links / actions rhythm.

   The selectors carry html.cg-authed to out-specify the `:first-child` and `.ph-signin` rules above
   without touching the public header. */
html.cg-authed body.bd-dark .ph-nav > a.ph-only-auth[href="/dashboard"] { margin-left: auto; }
html.cg-authed body.bd-dark .ph-nav > .ph-acct { margin-left: auto; margin-right: 4px; }

/* ── The account menu ─────────────────────────────────────────────────────────────────────────
   Closed with display:none rather than the hidden attribute: /learn pages inline a blanket
   `[hidden] { display: none !important }`, and depending on a rule from another stylesheet to keep a
   menu shut is a coupling worth not having.

   .ph is position:fixed, z-index 1000, with no overflow clipping, so the panel escapes the header
   cleanly and needs only a local z-index. */
body.bd-dark .ph-acct-menu { display: none; }
body.bd-dark .ph-acct.is-open .ph-acct-menu {
  display: block;
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 10;
  min-width: 194px; padding: 6px;
  border-radius: 8px;
  /* Opaque. The header's own fill only appears once the page is scrolled, so a translucent panel
     would sit over raw article text at the top of the page and be unreadable. */
  background: #0d1420;
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 18px 40px -14px rgba(0,0,0,.7);
}
body.bd-dark .ph-acct-head {
  padding: 8px 10px 10px; margin: 0 0 6px;
  border-bottom: 1px solid rgba(255,255,255,.07);
}
body.bd-dark .ph-acct-name {
  margin: 0; font-size: 13px; font-weight: 600; color: #fff; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 240px;
}
/* The role in the accent: of everything in this panel it is the one fact that changes what the
   reader can actually do. */
body.bd-dark .ph-acct-status {
  margin: 2px 0 0; font-size: 11px; font-weight: 600; letter-spacing: .02em; color: #7ff0e4;
}
body.bd-dark .ph-acct-item {
  display: block; width: 100%; box-sizing: border-box;
  padding: 8px 10px; border: 0; border-radius: 6px;
  background: none; text-align: left; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 500; color: rgba(255,255,255,.72);
  text-decoration: none;
  transition: background .15s ease, color .15s ease;
}
body.bd-dark .ph-acct-item:hover {
  background: rgba(255,255,255,.06); color: #fff; text-decoration: none;
}
/* Inside a panel there is no hover to explain where you are, so keyboard focus needs its own mark.
   Restrained: an inset hairline in the accent, not a glow. */
body.bd-dark .ph-acct-item:focus-visible {
  outline: none; background: rgba(255,255,255,.06); color: #fff;
  box-shadow: inset 0 0 0 1px rgba(52,224,208,.55);
}
/* Sign out sits last and quieter than the action above it - it is the exit, not a destination, and
   nobody arrives here looking for it by accident. It brightens to full on hover and focus so it
   never becomes hard to hit, which is the line between "quiet" and "hidden".
   No second divider: with two items the one under the identity block is the whole structure, and a
   rule between every row would turn a small menu into a table. */
body.bd-dark .ph-acct-out {
  margin-top: 2px;
  color: rgba(255,255,255,.5);
}
body.bd-dark .ph-acct-out:hover,
body.bd-dark .ph-acct-out:focus-visible { color: #fff; }

@media (max-width: 720px) {
  /* Narrow: the account chip and the button carry the signed-in state, and three text links do not
     fit beside them. Dashboard survives because it is the way back into the app; Learn is the page
     you are on, and Exams is one tap further in from the dashboard. */
  html.cg-authed body.bd-dark .ph-nav > a.ph-only-auth:not(.ph-cta):not([href="/dashboard"]) {
    display: none !important;
  }
  /* Initials only on a phone. The name is the first thing worth dropping when the bar runs out of
     room, and the circle still says whose session this is. */
  body.bd-dark .ph-acct .ph-user-name { display: none; }
  /* gap: 0 as well - the flex gap survives a display:none child and left 7px of dead space inside
     the pill, making a 26px circle sit in a 34px button. */
  body.bd-dark .ph-acct .ph-user { padding: 0 3px; gap: 0; }
}
