/*
 * NDG Design System — Component Styles
 */

/* ---------------------------------------------------------------------
   Base — only rules Tailwind preflight doesn't cover
   --------------------------------------------------------------------- */
html {
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--text-primary);
  background: var(--surface-page);
  -webkit-font-smoothing: antialiased;
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
}
body {
  font-size: var(--fs-lg);
  line-height: var(--lh-relaxed);
  font-weight: var(--fw-regular);
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sharp);
}
.skip {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
.skip:focus {
  left: 1rem; top: 1rem; width: auto; height: auto;
  background: var(--action-primary); color: #fff;
  padding: 10px 16px; border-radius: var(--radius-sharp); z-index: 1000;
}

/* ---------------------------------------------------------------------
   Prose link fallback

   input.css resets `a` to `color: inherit; text-decoration: none` so that
   nav/card/button links aren't painted browser-blue. The cost is that a
   plain link in running copy renders as body text — invisible as a link.
   This restores it for unclassed links only: anything carrying a class was
   styled deliberately, and :where() keeps the container part at zero
   specificity so every component rule still wins.

   Navigation components are excluded below: the footer columns and the
   NETLAB+ content sidebar are unclassed <li><a> too, and underlining them
   turns tidy link lists into a wall of rules.
   --------------------------------------------------------------------- */
:where(p, li, dd, dt, td, th, blockquote, figcaption) a:not([class]) {
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 2px;
}
:where(p, li, dd, dt, td, th, blockquote, figcaption) a:not([class]):hover {
  color: var(--action-primary-hover);
}
:where(.site-footer, .content-sidebar, .site-nav, .breadcrumb, .dropdown,
       .pagination, .mobile-nav-group) :where(p, li, dd, dt, td, th) a:not([class]) {
  color: inherit;
  text-decoration: none;
}

/* ---------------------------------------------------------------------
   Quote form: collapsible vendor groups + sticky running total
   --------------------------------------------------------------------- */
/* Inline only — see .form-section. Clearing the UA fieldset margin with m-0
   also cleared the block margin from the wrapper's space-y-4, so the five
   vendor groups rendered as one flush stack. */
/* No border reset here: these groups draw a card border through Tailwind
   utilities, and an unlayered `border: 0` would outrank them. */
.quote-group { margin-inline: 0; }
.quote-group-details > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.quote-group-details > summary::-webkit-details-marker { display: none; }
.quote-group-details > summary::after {
  content: "+"; margin-left: auto;
  font-family: var(--font-mono); font-weight: var(--fw-regular);
  color: var(--text-subtle);
}
.quote-group-details[open] > summary::after { content: "\2212"; transform: none; }
.quote-group-details[open] > summary { border-bottom: 1px solid var(--border-subtle); }
.quote-group-count {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--text-link-on-light);
  background: var(--surface-callout);
  padding: 2px 8px; border-radius: var(--radius-full);
}
.quote-group-count:empty { display: none; }
.quote-group-details.has-selection > summary { color: var(--text-link-on-light); }

/* Sticky so the total stays visible while you work down the course lists —
   it previously sat several screens below the inputs that change it. */
.quote-total-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  background: var(--surface-inverse);
  color: var(--text-on-inverse);
  box-shadow: var(--shadow-lg);
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--dur-normal) var(--ease-out),
              transform var(--dur-normal) var(--ease-out);
  pointer-events: none;
}
.quote-total-bar.is-active { opacity: 1; transform: none; }
.quote-total-label {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wider);
  color: var(--text-on-inverse-muted);
}
.quote-total-value { font-size: var(--fs-xl); font-weight: var(--fw-bold); }

/* Collapsible lab-detail sections (Supported Labs, Using/Enabling the Labs).
   These carry long tables and were the bulk of a lab page's height. */
.content-body .lab-section {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  margin: 0 0 var(--space-4);
}
.content-body .lab-section > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  font-weight: var(--fw-bold); color: var(--text-primary);
  background: var(--surface-raised);
  border-radius: var(--radius-sharp);
}
.content-body .lab-section > summary::-webkit-details-marker { display: none; }
.content-body .lab-section > summary::after {
  content: "+"; font-family: var(--font-mono);
  color: var(--text-subtle); font-weight: var(--fw-regular);
}
.content-body .lab-section[open] > summary {
  border-bottom: 1px solid var(--border-default);
  border-radius: var(--radius-sharp) var(--radius-sharp) 0 0;
}
.content-body .lab-section[open] > summary::after { content: "\2212"; transform: none; }
.content-body .lab-section-body { padding: var(--space-4); }
.content-body .lab-section-body > :last-child { margin-bottom: 0; }

/* Collapsible documentation groups. The item count on the summary means you
   can judge a section's size before opening it. */
.doc-accordion > details { border-bottom: 1px solid var(--border-default); }
.doc-accordion > details:last-child { border-bottom: 0; }
.doc-accordion > details > summary { padding: var(--space-3) 0; }
.doc-accordion .ndg-list { padding-bottom: var(--space-3); }
.doc-count {
  margin-left: auto; margin-right: var(--space-3);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  color: var(--text-subtle);
}

/* ---------------------------------------------------------------------
   Legacy document lists (.ndg-list)

   1,500+ uses across the NETLAB+ documentation pages, and no rule anywhere —
   so they fell through to the generic .content-body ul (disc bullets, base
   size) while hand-ported pages on the same screen render the same content as
   a compact icon list. That mismatch is the inconsistent link sizing on the
   docs pages.

   Legacy markup puts the file-type icon *after* the link; ported markup puts
   it first. `order: -1` reconciles the two without touching the templates.

   Scoped carefully: the class is also on 40 <ol> procedures and 155 lists
   containing nested lists. Stripping markers from an ordered list, or setting
   every <li> to flex, destroys those — so the marker reset is <ul>-only and
   the flex/icon-reordering applies only to rows that actually carry an icon.
   --------------------------------------------------------------------- */
ul.ndg-list {
  list-style: none;
  padding-left: 0;
}
.ndg-list {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-sm);
}
.ndg-list li { margin: 0 0 var(--space-2); }

/* Icon-bearing file links only. Legacy markup puts the file-type icon after
   the link; ported pages put it first. order:-1 reconciles the two.
   CONSTRAINT: flex blockifies every inline child, so an <em>/<strong> in the
   middle of the sentence becomes its own column. Sentence content with
   inline markup must sit in one <span> (see df-101/_course_sections.html). */
ul.ndg-list > li:has(> i, > .fa, > .fas, > .far) {
  display: flex; align-items: baseline; gap: 6px;
}
ul.ndg-list > li > i,
ul.ndg-list > li > .fa,
ul.ndg-list > li > .fas,
ul.ndg-list > li > .far {
  order: -1;
  flex-shrink: 0;
  width: 1rem; text-align: center;
  color: var(--text-subtle);
}
ul.ndg-list > li > i.fa-file-pdf { color: var(--status-error-fg); }

/* ---------------------------------------------------------------------
   Small-text floor

   Legacy NETLAB+ pages nest <small> inside copy that is already reduced, so
   the sizes compound down to roughly 10px of "legal type". max() pins a 13px
   floor while still stepping down from normal-size parents.
   --------------------------------------------------------------------- */
:where(.content-body, .legal-doc) small { font-size: max(0.8125rem, 0.9em); }

/* ---------------------------------------------------------------------
   Table baseline

   .content-body and .legal-doc style their tables, but legacy support pages
   emit a bare <table> inside neither wrapper and rendered with no borders at
   all. Scoped to unclassed tables so .compare-table, .table-mono and the
   pricing tables keep their own treatment; :where() zeroes specificity.
   --------------------------------------------------------------------- */
:where(table:not([class])) {
  width: 100%; border-collapse: collapse;
  margin: 0 0 var(--space-4);
  font-size: var(--fs-sm);
}
:where(table:not([class])) th,
:where(table:not([class])) td {
  border: 1px solid var(--border-default);
  padding: 8px 12px; text-align: left; vertical-align: top;
}
:where(table:not([class])) th {
  background: var(--surface-raised);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}

/* ---------------------------------------------------------------------
   Layout primitives
   --------------------------------------------------------------------- */
/* One page width. 6xl is the shell for every page; the wider 7xl is opt-in via
   `container max-w-7xl` and reserved for the two-column sidebar layouts, which
   spend ~360px of it on the sidebar. This default used to be 7xl, so any page
   mixing a bare `.container` with an explicit `max-w-6xl` section — including
   / and /products/ — stepped 128px narrower partway down. */
.container { width: 100%; max-width: var(--max-w-6xl); margin: 0 auto; }
@layer components { .container { padding: 0 var(--space-8); } }

/* ---------------------------------------------------------------------
   Theme toggle
   --------------------------------------------------------------------- */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-std),
              color var(--dur-fast) var(--ease-std),
              border-color var(--dur-fast) var(--ease-std);
}
.theme-toggle:hover {
  background: var(--surface-callout);
  color: var(--text-link-on-light);
  border-color: var(--action-primary);
}
.theme-toggle .fa-sun { display: none; }
[data-theme="dark"] .theme-toggle .fa-sun { display: inline-block; }
[data-theme="dark"] .theme-toggle .fa-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .fa-sun { display: inline-block; }
  :root:not([data-theme="light"]) .theme-toggle .fa-moon { display: none; }
}

/* ---------------------------------------------------------------------
   Logo / wordmark
   --------------------------------------------------------------------- */
.wordmark {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display);
  font-weight: var(--fw-extrabold);
  font-size: var(--fs-xl);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  line-height: 1;
}
.wordmark-logo { height: 2em; width: auto; flex-shrink: 0; display: block; }
.wordmark-logo .logo-text { fill: currentColor; }
.wordmark-logo .logo-bar  { fill: var(--action-primary); transform-box: fill-box; transform-origin: bottom; }
.wordmark.on-dark { color: #fff; }
.wordmark.on-dark .logo-bar { fill: var(--color-brand-shimmer); }

/* Bars primitive — used by the logo, loaders, and level indicators.
   Logo SVG order: bar 1 = tallest (x=32.6), bar 4 = shortest (x=0).
   Hover stagger runs shortest→tallest so the bars "build up" left-to-right. */
@keyframes bars-stair-grow {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}
@keyframes bars-stair-loop {
  0%, 100% { transform: scaleY(0.12); }
  40%, 60% { transform: scaleY(1); }
}
/* Hover triggers on the logo itself only — not on a wrapper. Anywhere
   the wordmark appears, hovering it animates the bars. */
.wordmark:hover .logo-bar {
  animation: bars-stair-grow var(--dur-slow) var(--ease-out) both;
}
.wordmark:hover .logo-bar:nth-of-type(4) { animation-delay: 0ms; }
.wordmark:hover .logo-bar:nth-of-type(3) { animation-delay: 70ms; }
.wordmark:hover .logo-bar:nth-of-type(2) { animation-delay: 140ms; }
.wordmark:hover .logo-bar:nth-of-type(1) { animation-delay: 210ms; }
@media (prefers-reduced-motion: reduce) {
  .wordmark:hover .logo-bar { animation: none; }
  html { scroll-behavior: auto; }
}

/* Bars loader — looped variant of the same primitive. Uses explicit
   pixel widths per size so the bars render even outside a sized parent. */
.bars-loader {
  display: inline-flex; align-items: end; gap: 3px;
  width: 27px; height: 24px;
  vertical-align: middle;
  color: var(--text-link-on-light);
}
.bars-loader.is-sm { width: 18px; height: 16px; gap: 2px; }
.bars-loader.is-lg { width: 40px; height: 36px; gap: 5px; }
.bars-loader span {
  display: block; flex: 1;
  background: currentColor; border-radius: 1px;
  transform-origin: bottom;
  animation: bars-stair-loop 1.5s var(--ease-out) infinite both;
}
.bars-loader span:nth-child(1) { height: 25%; animation-delay: 0ms; }
.bars-loader span:nth-child(2) { height: 50%; animation-delay: 100ms; }
.bars-loader span:nth-child(3) { height: 75%; animation-delay: 200ms; }
.bars-loader span:nth-child(4) { height: 100%; animation-delay: 300ms; }
@media (prefers-reduced-motion: reduce) {
  .bars-loader span { animation: none; transform: scaleY(1); }
}

/* Level indicator — static 4-bar primitive mirroring the logo geometry.
   Use .is-active on the first N bars to indicate level. */
.level-bars {
  display: inline-flex; align-items: end; gap: 2px;
  height: 12px; vertical-align: middle;
}
.level-bars span {
  display: block; width: 3px;
  background: var(--border-strong); border-radius: 1px;
}
/* --text-link-on-light is the per-theme accent (ink on light, glow on dark);
   --action-primary has no dark override and read UNLIT next to slate bars. */
.level-bars span.is-active { background: var(--text-link-on-light); }
.level-bars span:nth-child(1) { height: 25%; }
.level-bars span:nth-child(2) { height: 50%; }
.level-bars span:nth-child(3) { height: 75%; }
.level-bars span:nth-child(4) { height: 100%; }
.level-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  color: var(--text-secondary);
}

/* ---------------------------------------------------------------------
   Buttons
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 22px;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  line-height: 1;
  letter-spacing: var(--tracking-snug);
  border-radius: var(--radius-full);
  border: 1.5px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-std),
              color var(--dur-fast) var(--ease-std),
              border-color var(--dur-fast) var(--ease-std);
}
/* A nowrap label longer than a phone column overflows the viewport
   ("Getting Started with LMS and ILT Services" hit 409px on a 390px
   screen) — let labels wrap where the pill can't grow instead. */
@media (max-width: 640px) {
  .btn { white-space: normal; line-height: 1.3; }
}
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5; cursor: not-allowed;
}
.btn-primary    { background: var(--action-primary); color: var(--action-primary-text); }
.btn-primary:hover:not(:disabled) { background: var(--action-primary-hover); }
.btn-outline    { background: transparent; color: var(--text-primary); border-color: var(--border-default); }
.btn-outline:hover:not(:disabled) { border-color: var(--action-primary); color: var(--text-link-on-light); }
.btn-ghost      { background: transparent; color: var(--text-link-on-light); padding: 8px 12px; }
.btn-ghost:hover:not(:disabled) { color: var(--action-primary-hover); }
.btn-sm  { padding: 10px 18px; font-size: var(--fs-sm); }
.btn-lg  { padding: 18px 36px; font-size: var(--fs-lg); }
.btn-destructive { background: var(--action-destructive); color: #fff; }
.btn-destructive:hover:not(:disabled) { background: var(--action-destructive-hover); }
.btn-icon {
  width: 36px; height: 36px; padding: 0; border-radius: 50%;
}
.btn-group {
  display: inline-flex; align-items: center;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full); overflow: hidden;
  background: var(--surface-page);
}
.btn-group > button {
  padding: 8px 14px; font-size: var(--fs-sm);
  background: transparent; color: var(--text-secondary);
  border: 0; cursor: pointer; font-family: inherit;
  transition: background var(--dur-fast) var(--ease-std);
  border-right: 1px solid var(--border-subtle);
}
.btn-group > button:last-child { border-right: 0; }
.btn-group > button.is-active {
  background: var(--surface-raised);
  color: var(--text-primary);
  font-weight: var(--fw-semibold);
}
/* Button loading state — inline bars-loader sized to fit inside a button label. */
.btn-loader {
  display: inline-flex; align-items: end; gap: 2px;
  width: 14px; height: 12px;
  vertical-align: middle;
  color: currentColor;
}
.btn-loader span {
  display: block; flex: 1;
  background: currentColor; border-radius: 1px;
  transform-origin: bottom;
  animation: bars-stair-loop 1.5s var(--ease-out) infinite both;
}
.btn-loader span:nth-child(1) { height: 25%; animation-delay: 0ms; }
.btn-loader span:nth-child(2) { height: 50%; animation-delay: 100ms; }
.btn-loader span:nth-child(3) { height: 75%; animation-delay: 200ms; }
.btn-loader span:nth-child(4) { height: 100%; animation-delay: 300ms; }
@media (prefers-reduced-motion: reduce) {
  .btn-loader span { animation: none; transform: scaleY(1); }
}

/* ---------------------------------------------------------------------
   Form inputs
   --------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label {
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  color: var(--text-primary); letter-spacing: var(--tracking-snug);
}
.field-hint  { font-size: var(--fs-xs); color: var(--text-muted); }
.field-error { font-size: var(--fs-xs); color: var(--status-error-fg); font-weight: var(--fw-medium); }

/* A grouped block (a fieldset of checkboxes, say) is a section, not another
   field, so the form's base rhythm reads too tight around it.
   margin-inline only: these fieldsets carried Tailwind's m-0 to clear the UA
   2px inline margin, and that also zeroed the block margin the form's
   space-y-* had set — the two groups ended up touching. Extra separation is
   padding for the same reason, so space-y-* keeps owning the block margins. */
.form-section { border: 0; margin-inline: 0; padding: var(--space-3) 0; }

.input, .textarea, .select {
  width: 100%;
  padding: 12px 14px;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  color: var(--text-primary);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  transition: border-color var(--dur-fast) var(--ease-std),
              box-shadow var(--dur-fast) var(--ease-std);
}
.input::placeholder, .textarea::placeholder { color: var(--text-subtle); }
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--action-primary);
  box-shadow: 0 0 0 4px var(--surface-tint);
}
.input[aria-invalid="true"] { border-color: var(--status-error-fg); }
.input:disabled { background: var(--surface-raised); color: var(--text-muted); cursor: not-allowed; }
.textarea { min-height: 96px; resize: vertical; line-height: var(--lh-normal); }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%236B7689' stroke-width='1.5'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px;
  padding-right: 36px;
}
.input-search-wrap { position: relative; display: flex; align-items: center; }
.input-search-wrap .icon {
  position: absolute; left: 14px; font-size: 14px;
  color: var(--text-muted); pointer-events: none;
}
.input-search { padding-left: 42px; }

.checkbox, .radio, .switch {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); color: var(--text-primary);
  cursor: pointer;
}
/* flex-start + a 2px nudge keeps the box on the first line of wrapping labels */
.checkbox, .radio { display: flex; align-items: flex-start; }
/* Per-theme accent: --action-primary made a checked box dark-blue-on-dark */
input[type="checkbox"], input[type="radio"] { accent-color: var(--text-link-on-light); }
.checkbox input, .radio input { width: 16px; height: 16px; margin-top: 2px; accent-color: var(--text-link-on-light); cursor: pointer; flex-shrink: 0; }
.switch {
  --w: 34px; --h: 20px;
}
.switch input {
  appearance: none; width: var(--w); height: var(--h);
  background: var(--border-default); border-radius: var(--radius-full);
  position: relative; cursor: pointer; outline: none; flex-shrink: 0;
  transition: background var(--dur-fast) var(--ease-std);
}
.switch input::before {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; background: #fff; border-radius: 50%;
  transition: transform var(--dur-fast) var(--ease-std);
  box-shadow: var(--shadow-sm);
}
.switch input:checked { background: var(--action-primary); }
.switch input:checked::before { transform: translateX(14px); }

.slider { width: 100%; accent-color: var(--text-link-on-light); }

.file-upload {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 14px 16px;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-sharp);
  background: var(--surface-raised);
  cursor: pointer;
  font-size: var(--fs-sm); color: var(--text-secondary);
}
.file-upload:hover { border-color: var(--action-primary); color: var(--text-primary); }
.file-upload .icon { font-size: 18px; color: var(--text-link-on-light); flex-shrink: 0; }

/* ---------------------------------------------------------------------
   Badges, pills, chips
   --------------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 12px;
  background: var(--surface-callout);
  color: var(--text-link-on-light);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  border-radius: var(--radius-full);
}
.badge-vendor { background: var(--surface-inverse); color: #fff; }
.badge-success { background: var(--status-success-bg); color: var(--status-success-text); }
.badge-warning { background: var(--status-warning-bg); color: var(--status-warning-text); }
.badge-error   { background: var(--status-error-bg);   color: var(--status-error-text); }
.badge-neutral { background: var(--surface-sunken); color: var(--text-secondary); }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full);
  font-family: inherit;
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-std),
              color var(--dur-fast) var(--ease-std),
              background var(--dur-fast) var(--ease-std);
}
.chip:hover { border-color: var(--action-primary); color: var(--text-primary); }
.chip.is-active {
  background: var(--action-primary); color: #fff;
  border-color: var(--action-primary); font-weight: var(--fw-bold);
}

/* ---------------------------------------------------------------------
   Cards
   --------------------------------------------------------------------- */
@layer components { .card { padding: var(--space-6); } }
.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sharp);
  transition: border-color var(--dur-fast) var(--ease-std),
              background var(--dur-fast) var(--ease-std);
}
.card.is-interactive { cursor: pointer; }
.card.is-interactive:hover { border-color: var(--action-primary); background: var(--surface-tint); }
.card.is-raised { box-shadow: var(--shadow-lg); }
.card.is-tinted { background: var(--surface-tint); border-color: transparent; }

/* Course card — 16:9 header image, body with topic pill / title / desc / meta strip,
   state-dependent footer slot, overlay badges (top-left "New", top-right cert),
   4-bar level indicator. Whole card click via stretched link. */
/* Grid for course and lab cards. The tracks are held near a fixed width
   instead of dividing the container, because the header images are 350x150
   sources: a card that stretches with the viewport upscales them. 349px is
   the largest three-up track in a max-w-6xl container (the image inside the
   1px card borders renders at 347px, within 1% of the source); 352px — the
   exact 1:1 width — drops the grid to two columns. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 349px));
  justify-content: start;
  gap: var(--space-5);
}

.course-card {
  display: flex; flex-direction: column;
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  overflow: hidden;
  position: relative;
  transition: border-color var(--dur-fast) var(--ease-std),
              box-shadow var(--dur-fast) var(--ease-std),
              transform var(--dur-fast) var(--ease-std);
}
.course-card:hover {
  border-color: var(--action-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.course-card .card-image {
  position: relative;
  /* Matches the 350x150 header images; 16/7 cropped every card slightly. */
  aspect-ratio: 7 / 3;
  background: var(--surface-callout);
  overflow: hidden;
}
.course-card .card-image img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.course-card .card-image .placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-link-on-light); opacity: 0.55;
}
.course-card .card-image .placeholder i { font-size: 64px; }
.course-card .badge-overlay {
  position: absolute; top: var(--space-3);
  display: inline-flex; align-items: center;
  padding: 4px 10px;
  background: color-mix(in oklab, var(--surface-page) 90%, transparent);
  border-radius: var(--radius-sharp);
  font-size: 11px; font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-primary);
  box-shadow: var(--shadow-xs);
  backdrop-filter: blur(4px);
}
.course-card .badge-overlay.is-cert { right: var(--space-3); }
.course-card .card-body {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4) var(--space-5) var(--space-5);
  flex: 1;
}
.course-card h2, .course-card h3, .course-card h4 {
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  line-height: 1.3; letter-spacing: var(--tracking-snug);
}
.course-card .desc {
  font-size: var(--fs-sm); color: var(--text-secondary);
  line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
.course-card .meta-strip {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  font-size: var(--fs-xs); color: var(--text-muted);
  font-variant-numeric: tabular-nums; font-weight: var(--fw-medium);
}
.course-card .meta-strip span {
  display: inline-flex; align-items: center; gap: 4px;
}
.course-card .meta-strip i {
  font-size: 11px; opacity: 0.85;
}
.course-card .card-footer {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--space-3); margin-top: auto;
  padding-top: var(--space-3);
  font-size: var(--fs-sm);
}
.course-card .price {
  font-weight: var(--fw-bold); color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.course-card a.stretched {
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
}
/* The card clips overflow, so the stretched link's focus shadow is invisible —
   surface keyboard focus on the card itself instead. */
.course-card:focus-within {
  box-shadow: var(--focus-ring);
  border-color: transparent;
}
.course-card a.stretched:focus-visible { outline: none; }


.article-card {
  padding: var(--space-6);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  transition: border-color var(--dur-fast) var(--ease-std), box-shadow var(--dur-fast) var(--ease-std);
}
.article-card:hover { border-color: var(--action-primary); box-shadow: var(--shadow-md); }
.article-card .article-kicker {
  font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: var(--fw-bold);
  color: var(--text-link-on-light); letter-spacing: var(--tracking-wider);
  text-transform: uppercase; margin-bottom: var(--space-2);
}
.article-card h4 { font-size: var(--fs-xl); font-weight: var(--fw-extrabold); margin-bottom: var(--space-3); }
.article-card p { font-size: var(--fs-base); color: var(--text-secondary); line-height: var(--lh-normal); }
.article-card .byline {
  margin-top: var(--space-4);
  font-size: var(--fs-sm); color: var(--text-muted);
}

/* ---------------------------------------------------------------------
   Tables
   --------------------------------------------------------------------- */
.table {
  width: 100%; border-collapse: collapse;
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  overflow: hidden;
  font-size: var(--fs-sm);
}
.table th, .table td {
  padding: 12px 16px;
  text-align: left;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: top;
}
.table tr:last-child th, .table tr:last-child td { border-bottom: 0; }
.table th {
  font-weight: var(--fw-bold);
  color: var(--text-secondary);
  background: var(--surface-raised);
}
.table-striped tbody tr:nth-child(even) { background: var(--surface-raised); }
.table-mono td { font-family: var(--font-mono); color: var(--text-primary); font-variant-numeric: tabular-nums; }

.compare-table {
  width: 100%; border-collapse: collapse;
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  overflow: hidden;
  font-size: var(--fs-sm);
}
.compare-table th, .compare-table td {
  padding: 14px 16px; text-align: center;
  border-bottom: 1px solid var(--border-subtle);
}
.compare-table th:first-child, .compare-table td:first-child {
  text-align: left; font-weight: var(--fw-semibold); color: var(--text-primary);
}
.compare-table thead th {
  font-weight: var(--fw-extrabold); color: var(--text-primary);
  background: var(--surface-raised);
}
.compare-table thead .col-recommended {
  background: var(--surface-callout); color: var(--text-link-on-light);
  position: relative;
}
.compare-table tbody .col-recommended { background: var(--surface-tint); }
.compare-table .check { color: var(--text-link-on-light); font-size: 14px; }
.compare-table .dash { color: var(--text-subtle); }

.kv-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column;
}
.kv-list li {
  display: grid; grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--fs-sm);
}
.kv-list li:first-child { padding-top: 0; }
.kv-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.kv-list .key { color: var(--text-muted); font-weight: var(--fw-medium); }
.kv-list .val { color: var(--text-primary); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------
   Status / feedback
   --------------------------------------------------------------------- */
.banner {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: var(--space-3); align-items: start;
  padding: 14px 18px;
  border-radius: var(--radius-sharp);
  border: 1px solid transparent;
  font-size: var(--fs-sm);
}
.banner .b-icon { font-size: 18px; margin-top: 1px; flex-shrink: 0; }
.banner .b-title { font-weight: var(--fw-bold); color: var(--text-primary); margin-bottom: 2px; }
.banner .b-text { color: var(--text-secondary); }
.banner-info    { background: var(--surface-callout); border-color: color-mix(in oklab, var(--action-primary) 25%, transparent); }
.banner-info .b-icon    { color: var(--text-link-on-light); }
.banner-success { background: var(--status-success-bg); border-color: var(--status-success-border); }
.banner-success .b-icon { color: var(--status-success-fg); }
.banner-warning { background: var(--status-warning-bg); border-color: var(--status-warning-border); }
.banner-warning .b-icon { color: var(--status-warning-fg); }
.banner-error   { background: var(--status-error-bg);   border-color: var(--status-error-border); }
.banner-error   .b-icon { color: var(--status-error-fg); }

.toast {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: var(--space-3); align-items: center;
  padding: 12px 16px;
  background: var(--surface-inverse); color: var(--text-on-inverse);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm);
  max-width: 380px;
}
.toast .t-icon { font-size: 18px; color: var(--color-brand-shimmer); }
.toast .t-close { color: var(--text-on-inverse-muted); background: transparent; border: 0; cursor: pointer; padding: 4px; }

.progress-linear {
  height: 8px; width: 100%;
  background: var(--surface-sunken);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.progress-linear .fill {
  height: 100%;
  background: var(--action-primary);
  border-radius: var(--radius-full);
  transition: width var(--dur-normal) var(--ease-std);
}
.skeleton {
  background: linear-gradient(90deg, var(--surface-raised) 0%, var(--surface-sunken) 50%, var(--surface-raised) 100%);
  background-size: 200% 100%;
  animation: skel 1.4s ease-in-out infinite;
  border-radius: var(--radius-sharp);
}
@keyframes skel { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skeleton-line { height: 12px; }
.skeleton-block { height: 96px; }

.empty-state {
  padding: var(--space-12);
  text-align: center;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-sharp);
  background: var(--surface-raised);
}
.empty-state h2, .empty-state h3, .empty-state h4 { font-size: var(--fs-lg); font-weight: var(--fw-extrabold); margin-bottom: var(--space-2); }
.empty-state p { color: var(--text-muted); font-size: var(--fs-sm); margin-bottom: var(--space-5); }

/* ---------------------------------------------------------------------
   Overlays
   --------------------------------------------------------------------- */
.modal {
  width: 100%; max-width: 480px;
  background: var(--surface-page);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-2xl);
  overflow: hidden;
}
.modal-head {
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
  display: flex; align-items: center; justify-content: space-between;
}
.modal-head h3 { font-size: var(--fs-lg); }
.modal-body { padding: var(--space-6); font-size: var(--fs-sm); color: var(--text-secondary); }
.modal-foot {
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border-subtle);
  display: flex; justify-content: flex-end; gap: var(--space-3);
  background: var(--surface-raised);
}

.drawer {
  width: 100%; max-width: 380px;
  background: var(--surface-page);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-2xl);
  overflow: hidden;
  margin-left: auto;
}
.drawer-head { padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--border-subtle); display: flex; align-items: center; justify-content: space-between; }
.drawer-body { padding: var(--space-6); font-size: var(--fs-sm); color: var(--text-secondary); }

.tooltip {
  display: inline-block;
  padding: 6px 10px;
  background: var(--surface-inverse); color: var(--text-on-inverse);
  border-radius: 6px;
  font-size: var(--fs-xs); font-weight: var(--fw-medium);
  letter-spacing: var(--tracking-snug);
}
.popover {
  max-width: 280px;
  padding: var(--space-4);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm); color: var(--text-secondary);
}
.popover .pop-title { font-weight: var(--fw-bold); color: var(--text-primary); margin-bottom: 4px; }
.dropdown {
  min-width: 200px;
  padding: 6px;
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-lg);
}
.dropdown a {
  display: block;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: var(--fs-sm); color: var(--text-secondary);
  /* Panel sizes to its longest label; wrapped nav items read as two rows. */
  white-space: nowrap;
}
.dropdown a:hover { background: var(--surface-callout); color: var(--text-primary); }
.dropdown hr { border: 0; border-top: 1px solid var(--border-subtle); margin: 6px 0; }
/* Product grouping inside a panel that spans both products. */
.dropdown-heading {
  padding: 6px 12px 2px;
  font-family: var(--font-mono);
  font-size: 10px; font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-widest); text-transform: uppercase;
  color: var(--text-subtle);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Navigation patterns
   --------------------------------------------------------------------- */
.site-header {
  background: color-mix(in oklab, var(--surface-page) 92%, transparent);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--border-default);
}
.site-header-inner {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: var(--space-8);
  padding: var(--space-4) var(--space-8);
}
.site-nav { display: flex; align-items: center; gap: var(--space-8); flex: 1; min-width: 0; }
.site-nav a {
  color: var(--text-muted); font-weight: var(--fw-medium); font-size: var(--fs-base);
}
.site-nav a:hover { color: var(--text-primary); }
/* Current page: color plus a non-color cue (WCAG 1.4.1). */
.site-nav a.is-active {
  color: var(--text-link-on-light);
  text-decoration: underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 2px;
}
.header-search {
  position: relative; flex: 1; min-width: 120px; max-width: 360px;
}
.header-search i {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  font-size: 14px; color: var(--text-muted); pointer-events: none;
}
.header-search input {
  width: 100%; padding: 8px 12px 8px 36px;
  border: 1px solid var(--border-default); border-radius: var(--radius-sharp);
  background: var(--surface-page);
  font-family: inherit; font-size: var(--fs-sm); color: var(--text-primary);
}
.header-search input:focus-visible {
  outline: none; border-color: var(--border-focus); box-shadow: var(--focus-ring);
}

.breadcrumb {
  font-size: var(--fs-sm); color: var(--text-muted);
}
.breadcrumb ol {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  list-style: none; padding: 0;
}
.breadcrumb li::after { content: "/"; margin-left: 8px; color: var(--text-subtle); }
.breadcrumb li:last-child::after { content: ""; }
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--text-link-on-light); }
.breadcrumb li:last-child { color: var(--text-primary); font-weight: var(--fw-semibold); }

.pagination { display: inline-flex; align-items: center; gap: 4px; }
.pagination a, .pagination span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px;
  padding: 0 10px;
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--text-secondary);
  border-radius: var(--radius-sharp);
}
.pagination a:hover { background: var(--surface-callout); color: var(--text-link-on-light); }
.pagination .is-active { background: var(--action-primary); color: #fff; }
/* Disabled items must be <span>, not <a> — pointer-events can't stop a
   focused link from being activated by keyboard. */
.pagination span.is-disabled { color: var(--text-subtle); }

.stepper {
  display: flex; flex-wrap: wrap;
  gap: var(--space-3);
  counter-reset: step;
  list-style: none; padding: 0; margin: 0;
}
.step {
  flex: 1 1 200px;
  display: flex; flex-direction: column; gap: 6px;
  padding: var(--space-4);
  border-top: 3px solid var(--border-default);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.step .step-num {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--text-muted); font-weight: var(--fw-bold);
}
.step .step-title { font-weight: var(--fw-bold); color: var(--text-primary); font-size: var(--fs-base); }
.step.is-complete { border-top-color: var(--action-primary); }
.step.is-complete .step-num { color: var(--text-link-on-light); }
.step.is-current { border-top-color: var(--action-primary); }
.step.is-current .step-title { color: var(--text-link-on-light); }

.tabs {
  display: flex; gap: var(--space-2);
  border-bottom: 1px solid var(--border-default);
}
.tab {
  padding: 12px 16px;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  border: 0; background: transparent;
  cursor: pointer; font-family: inherit;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tab:hover { color: var(--text-primary); }
.tab.is-active {
  color: var(--text-link-on-light);
  border-bottom-color: var(--action-primary);
}

.accordion details {
  padding: var(--space-5) var(--space-6);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  margin-bottom: var(--space-2);
}
/* Default disclosure affordance for bare <details> in page bodies — the
   native marker is suppressed globally (input.css), and styled components
   (.accordion +/−, .content-sidebar chevron, .prose-section) override this
   zero-specificity default with their own indicators. */
:where(details) > :where(summary) { cursor: pointer; }
:where(details) > :where(summary)::after {
  content: "\203A";
  display: inline-block; margin-left: 8px;
  color: var(--text-subtle);
  transition: transform var(--dur-fast) var(--ease-std);
}
:where(details[open]) > :where(summary)::after { transform: rotate(90deg); }
/* Components whose summaries carry their own chevron/plus icon opt out */
summary:has(i[class*="fa-chevron"])::after,
summary:has(i[class*="fa-angle"])::after,
.outline-section > summary::after,
.accordion-card > summary::after { content: none; }
@media (prefers-reduced-motion: reduce) {
  :where(details) > :where(summary)::after { transition: none; }
}

.accordion summary {
  list-style: none;
  cursor: pointer;
  display: flex; justify-content: space-between; align-items: center;
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.accordion summary::-webkit-details-marker { display: none; }
.accordion summary::after {
  content: "+";
  font-family: var(--font-mono); font-size: var(--fs-xl);
  font-weight: var(--fw-bold); color: var(--text-link-on-light);
}
.accordion details[open] summary::after { content: "\2212"; transform: none; }
.accordion details p {
  margin-top: var(--space-3);
  color: var(--text-secondary); font-size: var(--fs-base);
  line-height: var(--lh-normal);
}
.accordion .faq-link { font-size: var(--fs-sm); }
.accordion .faq-link a { color: var(--text-link-on-light); }

/* ---------------------------------------------------------------------
   Layout patterns
   --------------------------------------------------------------------- */
.hero {
  padding: 10rem var(--space-6) 8rem;
  background: var(--surface-page);
  text-align: center;
}
.hero h1 {
  font-size: var(--fs-7xl); line-height: 1.02;
  letter-spacing: var(--tracking-tightest);
  font-weight: var(--fw-extrabold);
  max-width: 52rem; margin: 0 auto var(--space-8);
}
.beat-2 {
  display: block; font-weight: var(--fw-regular); color: var(--text-secondary);
}
.hero .hero-ctas { display: flex; gap: var(--space-4); justify-content: center; flex-wrap: wrap; }

/* Campaign variant — a full-bleed dark gradient band. This is the only part
   of a campaign landing page the rest of the site had no equivalent for, and
   the only reason static/css/landing.css existed; everything else on those
   pages now uses the shared components.

   --campaign-accent tints the gradient. A new campaign page is on-brand with
   no CSS at all; give one its own colour by adding a theme class below.

   The default is a var() fallback, not a declaration on this rule: declaring
   it here would mean the element that consumes the variable also sets it, at
   a higher specificity than any single-class theme, so .theme-* could never
   win and every campaign would render brand blue. */
.hero.is-campaign {
  position: relative;
  overflow: hidden;
  padding: var(--space-20) var(--space-6) var(--space-16);
  color: var(--text-on-inverse);
  background: linear-gradient(125deg,
    var(--color-brand-primary-dark) 0%,
    var(--color-brand-primary) 45%,
    var(--campaign-accent, var(--color-brand-accent)) 130%);
}
.hero.is-campaign::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at 82% 18%, rgba(255,255,255,0.14), transparent 46%);
}
.hero.is-campaign > * { position: relative; z-index: 1; }
.hero.is-campaign h1 { color: var(--text-on-inverse); font-size: var(--fs-6xl); }
.hero.is-campaign .hero-sub {
  max-width: 48rem; margin: 0 auto var(--space-4);
  font-size: var(--fs-xl); color: var(--text-on-inverse);
}
.hero.is-campaign .hero-lead {
  max-width: 42rem; margin: 0 auto var(--space-8);
  color: var(--text-on-inverse-muted);
}

/* Buttons invert on the dark band: solid white primary, hairline ghost. */
.hero.is-campaign .btn-primary { background: #fff; color: var(--color-brand-primary); }
.hero.is-campaign .btn-primary:hover:not(:disabled) { background: var(--color-brand-shimmer); color: var(--color-brand-primary-dark); }
/* Any dark surface, not just the hero: .btn-outline's resting colour is
   --text-primary, which on .callout.is-dark is the same #1a2332 as the
   background — the secondary CTA label rendered invisible at 1:1 contrast. */
.hero.is-campaign .btn-outline,
.callout.is-dark .btn-outline { color: #fff; border-color: rgba(255,255,255,0.6); }
.hero.is-campaign .btn-outline:hover:not(:disabled),
.callout.is-dark .btn-outline:hover:not(:disabled) { background: rgba(255,255,255,0.12); border-color: #fff; color: #fff; }

/* Inline document link under the CTAs. */
.hero.is-campaign .hero-doc {
  display: inline-block; margin-top: var(--space-4);
  font-size: var(--fs-sm); color: var(--color-brand-shimmer);
  text-decoration: underline; text-underline-offset: 3px;
}
.hero.is-campaign .hero-doc:hover { color: #fff; }
.hero.is-campaign .hero-doc i { margin-right: 6px; }

/* At-a-glance facts strip. */
.hero-meta {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--space-3) var(--space-6);
  margin-top: var(--space-8);
  font-size: var(--fs-sm); color: var(--text-on-inverse-muted);
}
.hero-meta span { display: inline-flex; align-items: center; gap: 7px; }
.hero-meta i { color: var(--color-brand-shimmer); }

/* Per-campaign accents. One line each; nothing else may be themed here. */
.theme-proxmox { --campaign-accent: #E57000; }

@media (max-width: 720px) {
  .hero.is-campaign { padding: var(--space-12) var(--space-4) var(--space-10); }
  .hero.is-campaign h1 { font-size: var(--fs-4xl); }
}

/* Event detail rows — label/value facts for a dated event page (workshop,
   webinar): dates, time, cost, who it's for. */
.event-details {
  max-width: 820px; margin: 0 auto;
  padding: var(--space-1) var(--space-6);
  background: var(--surface-callout);
  border-left: 4px solid var(--color-brand-accent);
  border-radius: 0 var(--radius-sharp) var(--radius-sharp) 0;
}
.event-details .event-row { display: flex; flex-wrap: wrap; padding: var(--space-4) 0; }
.event-details .event-row + .event-row { border-top: 1px solid var(--border-subtle); }
.event-details .event-label {
  flex: 0 0 150px;
  font-family: var(--font-mono); font-size: 11px; line-height: 24px;
  font-weight: var(--fw-bold); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-link-on-light);
}
.event-details .event-val { flex: 1 1 280px; font-size: var(--fs-sm); line-height: 24px; color: var(--text-primary); }
/* .event-val is a div, so the unclassed-prose link fallback (p/li/td/…)
   doesn't reach it. Underline carries the affordance with inherited colour;
   a hover colour would need a token that works on the callout surface in
   both themes, which --action-primary-hover is not. */
.event-details .event-val a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.event-details .event-val a:hover { text-decoration-thickness: 2px; }
@media (max-width: 720px) {
  .event-details { padding: var(--space-1) var(--space-5); }
  .event-details .event-label { flex: 0 0 100%; margin-bottom: 2px; }
}

/* Course outline — one section per content type. Sections collapsible
   via accordion primitive; items inside are a flat list, no per-item
   dropdown nesting. */
.course-outline {
  display: flex; flex-direction: column;
  gap: var(--space-3);
}
.outline-section {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  overflow: hidden;
  background: var(--surface-page);
}
.outline-section > summary {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  background: var(--surface-raised);
  list-style: none;
  transition: background var(--dur-fast) var(--ease-std);
}
.outline-section > summary::-webkit-details-marker { display: none; }
.outline-section > summary:hover { background: var(--surface-callout); }
.outline-section[open] > summary { border-bottom: 1px solid var(--border-subtle); }
.outline-section .outline-type {
  flex: 1;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: var(--tracking-tight);
}
.outline-section .outline-meta {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-weight: var(--fw-medium);
}
.outline-chevron {
  font-size: 14px;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: transform var(--dur-normal) var(--ease-std);
  display: inline-block;
}
.outline-section[open] .outline-chevron { transform: rotate(180deg); }
.outline-items { display: flex; flex-direction: column; }
.outline-item {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.outline-item:last-child { border-bottom: none; }
.outline-item:nth-child(even) { background: var(--surface-raised); }
.outline-item-title {
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  line-height: 1.4;
}
.outline-item-desc {
  margin-top: var(--space-2);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  line-height: 1.55;
}

/* Collapsible prose sections (Course outline, Certification, Resources) —
   match the FAQ accordion box so the toggle clearly reads as a button. */
.prose-section {
  padding: var(--space-5) var(--space-6);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
}
.prose-section > summary {
  display: flex; align-items: center; gap: var(--space-3);
  cursor: pointer; list-style: none;
}
.prose-section > summary::-webkit-details-marker { display: none; }
.prose-section .prose-section-title {
  flex: 1; margin: 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: var(--tracking-tight);
}
.prose-section .prose-section-meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-weight: var(--fw-medium);
}
.prose-section > summary::after {
  content: "+";
  font-family: var(--font-mono); font-size: var(--fs-xl);
  font-weight: var(--fw-bold); color: var(--text-link-on-light);
  line-height: 1;
}
.prose-section[open] > summary::after { content: "\2212"; transform: none; }
/* Course-outline modules sit flush inside the box — no card-in-card. */
.prose-section .course-outline { gap: 0; }
.prose-section .course-outline .outline-section { border: 0; border-radius: 0; }
.prose-section .course-outline .outline-section + .outline-section { border-top: 1px solid var(--border-subtle); }
.prose-section .course-outline .outline-section > summary { background: transparent; padding-inline: 0; }
.prose-section .course-outline .outline-section > summary:hover { background: transparent; }
.prose-section .course-outline .outline-item { padding-inline: 0; }

/* Links that rely on the text-link color inside prose must carry a
   non-color cue at rest (WCAG 1.4.1) — underline them always. */
a.text-link { text-decoration: underline; text-underline-offset: 2px; }

/* .container is unlayered and would override Tailwind's layered max-w-*
   utilities on "container max-w-*" wrappers — restate the caps explicitly. */
.container.max-w-2xl { max-width: 42rem; }
.container.max-w-3xl { max-width: 48rem; }
.container.max-w-4xl { max-width: 56rem; }
.container.max-w-5xl { max-width: 64rem; }
.container.max-w-6xl { max-width: 72rem; }
.container.max-w-7xl { max-width: 80rem; }

/* ---------------------------------------------------------------------
   NETLAB+ content-tree layout — vendor sidebar + prose page body.
   Replaces the legacy Bootstrap list-group/collapse wrappers under
   layout/content/. Page bodies keep the {% block page %} contract.
   ------------------------------------------------------------------ */
.content-layout {
  display: grid; grid-template-columns: 240px minmax(0, 1fr);
  gap: var(--space-8); align-items: start;
  max-width: var(--max-w-7xl); margin: 0 auto;
  padding: var(--space-8) var(--space-6) var(--space-16);
}
.content-sidebar {
  position: sticky; top: 88px;
  border: 1px solid var(--border-default); border-radius: var(--radius-sharp);
  background: var(--card-bg);
  padding: var(--space-4);
  font-size: var(--fs-sm);
}
.content-sidebar .sidebar-logo { display: block; max-width: 180px; margin: 0 auto var(--space-4); }
.content-sidebar .sidebar-title {
  font-weight: var(--fw-bold); color: var(--text-primary);
  margin: 0 0 var(--space-2); font-size: var(--fs-sm);
}
.content-sidebar a { display: block; padding: 5px 8px; color: var(--text-secondary); border-radius: 4px; }
.content-sidebar a:hover { background: var(--surface-callout); color: var(--text-link-on-light); }
.content-sidebar a.is-active { color: var(--text-link-on-light); font-weight: var(--fw-semibold); }
.content-sidebar details { margin: 0; }
.content-sidebar summary {
  cursor: pointer; padding: 5px 8px; border-radius: 4px;
  color: var(--text-primary); font-weight: var(--fw-semibold);
  list-style: none; display: flex; align-items: center; justify-content: space-between;
}
.content-sidebar summary::-webkit-details-marker { display: none; }
.content-sidebar summary::after { content: "\203A"; transition: transform var(--dur-fast) var(--ease-std); color: var(--text-subtle); }
.content-sidebar details[open] > summary::after { transform: rotate(90deg); }
.content-sidebar summary:hover { background: var(--surface-callout); }
.content-sidebar ul { list-style: none; margin: 0; padding: 0 0 0 var(--space-3); }
.content-sidebar details details { padding-left: var(--space-2); }
@media (prefers-reduced-motion: reduce) {
  .content-sidebar summary::after { transition: none; }
}
.content-page-title {
  max-width: var(--max-w-7xl); margin: 0 auto;
  padding: var(--space-6) var(--space-6) 0;
  font-family: var(--font-display);
  font-size: var(--fs-2xl); font-weight: var(--fw-bold);
  color: var(--text-primary); letter-spacing: var(--tracking-tight);
}
/* pages that ship their own <h1> in pagetitle get the same look, unnested */
.content-page-title h1 { font: inherit; color: inherit; letter-spacing: inherit; margin: 0; }

/* Breadcrumb trail — pages emit <ol class="breadcrumb"> in the pagetitle block */
.content-breadcrumb .breadcrumb {
  display: flex; flex-wrap: wrap; gap: 6px;
  list-style: none; margin: 0; padding: 0;
  font-family: var(--font-mono); font-size: 11px;
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
}
/* Separators come from the generic .breadcrumb li::after rule — adding a
   ::before here doubled them ("/ /") on every content page. */
.content-breadcrumb .breadcrumb a { color: var(--text-muted); }
.content-breadcrumb .breadcrumb a:hover { color: var(--text-link-on-light); }
/* Prose defaults for legacy content bodies riding the new stack */
.content-body { color: var(--text-secondary); line-height: 1.6; }
.content-body h1, .content-body h2, .content-body h3, .content-body h4 {
  color: var(--text-primary); font-weight: var(--fw-bold);
  font-size: var(--fs-lg); margin: var(--space-8) 0 var(--space-3);
}
.content-body > :first-child { margin-top: 0; }
.content-body p { margin: 0 0 var(--space-4); }
.content-body ul { list-style: disc; margin: 0 0 var(--space-4); padding-left: var(--space-6); }
.content-body ol { list-style: decimal; margin: 0 0 var(--space-4); padding-left: var(--space-6); }
.content-body li { margin: var(--space-1) 0; }
/* :not(.btn) — a prose-link rule outranks .btn-primary on specificity and
   would otherwise repaint button labels link-blue on a blue fill. */
.content-body a:not(.btn) { color: var(--text-link-on-light); text-decoration: underline; }
.content-body img { max-width: 100%; height: auto; }
.content-body pre {
  overflow-x: auto;
  background: var(--surface-raised);
  border: 1px solid var(--border-default); border-radius: var(--radius-sharp);
  padding: var(--space-4);
  font-size: var(--fs-sm); line-height: 1.5;
  margin: 0 0 var(--space-4);
}
.content-body table { width: 100%; border-collapse: collapse; margin: 0 0 var(--space-4); font-size: var(--fs-sm); }
.content-body th, .content-body td { border: 1px solid var(--border-default); padding: 8px 12px; text-align: left; vertical-align: top; }
.content-body th { background: var(--surface-raised); color: var(--text-primary); }
.content-body .bg-warning {
  display: block; border: 1px solid var(--status-warning-border, var(--border-default));
  background: var(--status-warning-bg); color: var(--status-warning-text);
  border-radius: var(--radius-sharp); padding: var(--space-4); margin: 0 0 var(--space-4);
}
.content-body .text-danger { color: var(--status-error-text); } /* darker than -fg: passes 4.5:1 as body text */

/* Legacy bootstrap-era status classes still emitted by the app.py
   requirements helper (enroll-card links, outside .content-body) —
   global, theme-aware compat. Links keep an underline: color alone
   must not be the only affordance. */
.text-warning { color: var(--status-warning-text); }
.text-danger { color: var(--status-error-text); }
a.text-warning, a.text-danger { text-decoration: underline; }
.content-body .img-responsive { display: block; max-width: 100%; height: auto; }
.content-body .center-block { margin-left: auto; margin-right: auto; }
/* Legacy Webflow carousels. Their JS never loads on the new stack, so the
   dead arrow/dot chrome is hidden and the slides become a plain list.

   These "portfolio cards" wrapped a generic stock image, an inert overlay
   link (aria-hidden, tabindex="-1") and the real title link. The image is the
   same handful of vendor photos reused across every lab, so it carried no
   information while dominating the page — 49 templates' worth of visual
   noise. Only the title link says anything, so that is all that renders now.
   Done in CSS rather than across 49 templates of hand-written Webflow markup. */
.content-body .w-slider-mask,
.content-body .w-slide { display: block; position: static; width: auto; }
.content-body .portfolio-overlay,
.content-body .portfolio-wrapper { display: none; }
.content-body article.mix { max-width: none; margin: 0; }
.content-body .portfolio-text-wrapper {
  display: flex; align-items: baseline; gap: var(--space-2);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border-subtle);
  text-decoration: none;
}
.content-body .portfolio-text-wrapper::before {
  content: "\f0f6";  /* fa-file-lines */
  font: var(--fa-font-regular, normal 400 1em/1 "Font Awesome 6 Free");
  color: var(--text-subtle); flex-shrink: 0;
}
.content-body .portfolio-text-wrapper:hover { background: var(--surface-raised); }
.content-body .portfolio-tittle {
  margin: 0; font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  color: var(--text-link-on-light); text-decoration: underline;
}

/* Legacy Webflow grids in content bodies keep a side-by-side layout */
.content-body .w-row { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-start; }
.content-body .w-row > [class*="w-col"] { flex: 1 1 220px; min-width: 0; }
/* Legacy Bootstrap grids (thumbnail galleries etc.) get the same treatment */
.content-body .row { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-start; }
.content-body .row > [class*="col-"] { flex: 1 1 160px; min-width: 0; }
/* Legacy Bootstrap wrapper class, still on ~100 content-tree tables — must
   scroll everywhere, not only under .content-body (a Sec+ course table
   outside it rendered unreachable at 390px). */
/* Collapsed table borders paint half outside the table box, and a
   scrollport wrapper clips that half — outer borders were invisible on
   every wrapped table. Draw the border on the wrapper div instead. */
.table-responsive { overflow-x: auto; border: 1px solid var(--border-default); margin-bottom: var(--space-4); }
/* div-qualified to outrank later equal-specificity table rules
   (.legal-doc table) that would re-add a bottom margin inside the box */
div.table-responsive > table { margin-bottom: 0; }
/* Right-edge fade while more columns remain — applied by scroll-cues.js */
.scroll-x-more {
  -webkit-mask-image: linear-gradient(to left, transparent, #000 40px);
  mask-image: linear-gradient(to left, transparent, #000 40px);
}
.content-body .well {
  border: 1px solid var(--border-default); border-radius: var(--radius-sharp);
  background: var(--card-bg); padding: var(--space-6); margin: 0 0 var(--space-6);
}
@media (max-width: 960px) {
  .content-layout { grid-template-columns: 1fr; }
  .content-sidebar { position: static; }
}

/* Attachment list for the vanilla support-form uploader (ndg-upload.js) */
.upload-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.upload-item {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); color: var(--text-secondary);
  padding: 4px 0;
}
.upload-item.is-error { color: var(--status-error-fg); }
.upload-remove {
  border: 0; background: none; cursor: pointer;
  color: var(--text-muted); font-size: var(--fs-lg); line-height: 1;
  padding: 0 4px;
}
.upload-remove:hover { color: var(--status-error-fg); }

.trust-strip {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
  padding: var(--space-12) var(--space-6);
}
.trust-strip--4 { grid-template-columns: repeat(4, 1fr); }
.trust-stat { display: flex; flex-direction: column; gap: 4px; text-align: center; }
.trust-stat .num {
  font-size: var(--fs-5xl); font-weight: 900;
  color: var(--text-primary); font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-tight); line-height: 1;
}
.trust-stat .label {
  font-size: var(--fs-base); color: var(--text-muted); font-weight: var(--fw-medium);
}

.callout {
  padding: var(--space-5) var(--space-6);
  background: var(--surface-callout);
  border-radius: var(--radius-sharp);
  display: grid; grid-template-columns: auto 1fr;
  gap: var(--space-4); align-items: start;
}
.callout .c-icon {
  font-size: 24px; color: var(--text-link-on-light); flex-shrink: 0;
}
.callout h2, .callout h3, .callout h4 { font-size: var(--fs-base); margin-bottom: 4px; }
.callout p { font-size: var(--fs-sm); color: var(--text-secondary); }

/* Dark callout variant — standout section against the light surfaces.
   Use sparingly (1-2 per page max) for key conversion moments. */
.callout.is-dark {
  background: var(--surface-inverse);
  color: var(--text-on-inverse);
  padding: var(--space-8) var(--space-10);
  gap: var(--space-6);
  align-items: center;
}
.callout.is-dark .c-icon { color: var(--color-brand-shimmer); font-size: 32px; }
.callout.is-dark h2, .callout.is-dark h3, .callout.is-dark h4 { color: var(--text-on-inverse); font-size: var(--fs-xl); margin-bottom: var(--space-2); }
.callout.is-dark p { color: var(--text-on-inverse-muted); font-size: var(--fs-base); }
.callout.is-dark .callout-action {
  margin-left: auto; flex-shrink: 0;
}
.callout.is-dark.has-action { grid-template-columns: auto 1fr auto; }
.callout.is-dark.has-action.no-icon { grid-template-columns: 1fr auto; }
/* In dark theme the inverse surface blends into the page — keep the band
   distinct with a border. */
[data-theme="dark"] .callout.is-dark { border: 1px solid var(--border-strong); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .callout.is-dark { border: 1px solid var(--border-strong); }
}

.feature-card {
  padding: var(--space-5) var(--space-6);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  transition: border-color var(--dur-fast) var(--ease-std),
              box-shadow var(--dur-fast) var(--ease-std),
              transform var(--dur-fast) var(--ease-std);
}
.feature-card:hover {
  border-color: var(--action-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.feature-card h3 { font-size: var(--fs-base); font-weight: var(--fw-bold); margin-bottom: var(--space-2); }
.feature-card p { font-size: var(--fs-sm); color: var(--text-secondary); margin: 0; }
.feature-card.on-dark {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.08);
}
.feature-card.on-dark:hover {
  border-color: var(--color-brand-shimmer);
  box-shadow: none;
  transform: translateY(-2px);
}
.feature-card.on-dark h3 { color: var(--text-on-inverse); }
.feature-card.on-dark p { color: var(--text-on-inverse-muted); }

/* ---------------------------------------------------------------------
   Course / lab structure
   --------------------------------------------------------------------- */
.meta-panel {
  padding: var(--space-6);
  background: var(--card-bg);
  border-radius: var(--radius-sharp);
  border: 1px solid var(--border-default);
}
.meta-panel h2 {
  font-size: 11px; font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--text-muted); margin-bottom: var(--space-4);
  font-family: var(--font-mono);
}

.objectives { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
.objective {
  display: grid; grid-template-columns: auto 1fr;
  align-items: start; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  font-size: var(--fs-base);
}
.objective .num {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  font-weight: var(--fw-bold); color: var(--text-link-on-light);
  font-variant-numeric: tabular-nums;
}

.feature-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  background: var(--surface-raised);
  padding: var(--space-2);
  border-radius: var(--radius-sharp);
}
.feature {
  display: grid; grid-template-columns: auto 1fr;
  align-items: center; gap: 10px;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-page);
  border-radius: var(--radius-sharp);
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.feature .icon { width: 18px; height: 18px; flex-shrink: 0; }
.feature.is-on .icon { color: var(--text-link-on-light); }

.prereq-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.prereq-list li {
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border-left: 3px solid var(--action-primary);
  border-radius: 0 var(--radius-sharp) var(--radius-sharp) 0;
  font-size: var(--fs-base); color: var(--text-secondary);
}
.prereq-list strong { color: var(--text-primary); font-weight: var(--fw-bold); }

/* ---------------------------------------------------------------------
   Post-purchase / form confirmation
   --------------------------------------------------------------------- */
.confirmation {
  padding: var(--space-12);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  text-align: center;
}
.confirmation .check-circle {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--surface-callout); color: var(--text-link-on-light);
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: var(--space-5);
}
.confirmation h1, .confirmation h3 { font-size: var(--fs-3xl); margin-bottom: var(--space-3); }
.confirmation p { color: var(--text-secondary); margin: 0 auto var(--space-6); max-width: 32rem; }

/* ---------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------- */
.site-footer {
  padding: var(--space-12) var(--space-6);
  background: var(--surface-page);
  border-top: 1px solid var(--border-default);
}
.site-footer-newsletter {
  display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap;
  gap: var(--space-8);
  padding-top: var(--space-10); margin-bottom: var(--space-10);
  border-top: 1px solid var(--border-default);
}
.site-footer-newsletter .copy { max-width: 28rem; }
.site-footer-newsletter .copy h2 { font-size: var(--fs-lg); font-weight: var(--fw-bold); margin: 0 0 var(--space-1); }
.site-footer-newsletter .copy p { color: var(--text-muted); font-size: var(--fs-sm); margin: 0; }
.site-footer-newsletter .footer-newsletter-form { flex: 1; min-width: 300px; max-width: 28rem; }

/* Newsletter signup form (newsletter_signup macro) — token-based, theme-aware */
.newsletter-signup form { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.newsletter-signup input[type="email"] {
  flex: 1; min-width: 200px;
  padding: 10px 14px;
  border: 1px solid var(--border-default); border-radius: var(--radius-sharp);
  background: var(--surface-page); color: var(--text-primary);
  font-family: inherit; font-size: var(--fs-sm);
}
.newsletter-signup input[type="email"]:focus-visible {
  outline: none; border-color: var(--border-focus); box-shadow: var(--focus-ring);
}
.newsletter-signup .consent { flex-basis: 100%; }
.site-footer-grid {
  display: grid; grid-template-columns: 2.5fr 1fr 1fr 1fr;
  gap: var(--space-12);
  margin-bottom: var(--space-10);
}
.site-footer .footer-brand p { color: var(--text-muted); font-size: var(--fs-sm); margin-top: var(--space-3); }
.site-footer .footer-col h2 { font-size: var(--fs-sm); font-weight: var(--fw-bold); margin-bottom: var(--space-3); }
.site-footer .footer-col ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.site-footer .footer-col a { color: var(--text-muted); font-size: var(--fs-sm); }
.site-footer .footer-col a:hover { color: var(--text-link-on-light); }
.site-footer-bottom {
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;
  gap: var(--space-4); padding-top: var(--space-6);
  border-top: 1px solid var(--border-default);
  font-size: var(--fs-sm); color: var(--text-muted);
}
/* ---------------------------------------------------------------------
   Responsive — component rules
   --------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .objectives, .feature-grid { grid-template-columns: 1fr 1fr; }
  .site-footer-grid { grid-template-columns: 1fr 1fr 1fr; }
}
@media (max-width: 720px) {
  .objectives, .feature-grid { grid-template-columns: 1fr; }
  .trust-strip { grid-template-columns: 1fr; gap: var(--space-6); }
  .site-footer-grid { grid-template-columns: 1fr 1fr; }
  /* Hero scales down so the display headline doesn't hyphen-break words. */
  .hero { padding: 5rem var(--space-5) 4rem; }
  .hero h1 { font-size: var(--fs-4xl); text-wrap: balance; }
  /* Dark CTA band stacks: text above, full-width action below. */
  .callout.is-dark.has-action,
  .callout.is-dark.has-action.no-icon { grid-template-columns: 1fr; }
  .callout.is-dark { padding: var(--space-6); }
  .callout.is-dark .callout-action { margin-left: 0; width: 100%; }
  .callout.is-dark .callout-action .btn { width: 100%; justify-content: center; }
}

/* ---------------------------------------------------------------------
   Header — incremental-port additions.
   Sticky positioning lives here (not a utility class) so the header
   behaves identically on legacy pages, which load these rules from
   legacy-shim.css instead. z-index 900 stays below Bootstrap's modal
   stack (1040/1050) so legacy modals overlay the header.
   ------------------------------------------------------------------ */
.site-header { position: sticky; top: 0; z-index: 900; }
.header-utils { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }

/* Disclosure nav dropdown (links, not role=menu) */
.nav-dropdown { position: relative; display: inline-flex; }
.nav-dropdown-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; cursor: pointer;
  color: var(--text-muted); font-weight: var(--fw-medium); font-size: var(--fs-base);
}
.nav-dropdown-btn:hover { color: var(--text-primary); }
.nav-dropdown-btn[aria-expanded="true"] { color: var(--text-primary); }
.nav-caret { font-size: 10px; transition: transform var(--dur-fast) var(--ease-std); }
.nav-caret.is-open { transform: rotate(180deg); }
.nav-dropdown-panel {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 950;
}
@media (prefers-reduced-motion: reduce) {
  .nav-caret { transition: none; }
}

/* Desktop nav can't fit tablet widths (logo + links + dropdowns + utils),
   so the hamburger takes over below 960px — keep in sync with legacy-shim.css
   and the .mobile-only breakpoint in brands.css. */
@media (max-width: 960px) {
  .site-header-inner {
    grid-template-columns: 1fr auto;
    padding: var(--space-3) var(--space-4);
    gap: var(--space-4);
  }
  .site-nav { display: none; }
}
@media (max-width: 480px) {
  /* Narrow phones: the utility cluster must fit 320px — drop the duplicate
     sign-in button (it lives in the mobile panel) and tighten gaps. */
  .header-signin { display: none; }
  .header-utils { gap: var(--space-2); }
  .site-header-inner { gap: var(--space-2); }
  .site-footer-grid { grid-template-columns: 1fr; }
}

/* Mobile panel — lives inside the sticky header, so page scroll can't reach
   content past the viewport; cap below the header bar and scroll internally.
   72px ≥ the mobile header bar (space-3 padding + 40px buttons). */
#mobile-nav { max-height: calc(100vh - 72px); max-height: calc(100dvh - 72px); overflow-y: auto; }

/* Mobile panel groups */
.mobile-nav-group { display: flex; flex-direction: column; gap: var(--space-2); }
.mobile-nav-heading {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.mobile-nav-heading i { font-size: 10px; transition: transform var(--dur-normal) var(--ease-std); }
.mobile-nav-heading[aria-expanded="true"] i { transform: rotate(180deg); }
.mobile-nav-links { display: flex; flex-direction: column; gap: var(--space-2); padding-left: var(--space-3); border-left: 2px solid var(--border-subtle); }

/* Footer bottom-bar separator */
.footer-legal-row {
  display: inline-flex; flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  align-items: baseline;
}

/* Newsletter macro — de-utilitied layout (was Tailwind classes on OPR) */
.nl-status { font-size: var(--fs-sm); margin: 0; color: var(--text-secondary); }
.nl-status .fa-check-circle { color: var(--action-primary); margin-right: 4px; }
.nl-status.nl-error { color: var(--status-error-fg, #b91c1c); margin-bottom: 4px; }
.newsletter-signup .consent { flex-basis: 100%; display: inline-flex; align-items: flex-start; gap: 8px; cursor: pointer; font-weight: normal; }
.newsletter-signup .consent span { font-size: var(--fs-xs); color: var(--text-muted); }
.newsletter-signup .consent a { text-decoration: underline; color: inherit; }
.newsletter-signup .consent input[type="checkbox"] { margin-top: 2px; accent-color: var(--text-link-on-light); }

/* Hero product shot — framed screenshot beneath the hero CTAs */
.hero-shot {
  max-width: 60rem;
  margin: var(--space-16, 4rem) auto 0;
}

/* Browser chrome drawn rather than captured: sharp at any density, themeable,
   and the address bar shows our domain instead of the internal lab host that
   appears in a real screenshot. Decorative — aria-hidden in the markup. */
.browser-frame {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sharp);
  box-shadow: var(--shadow-xl);
  background: var(--surface-raised);
  overflow: hidden;
}
.browser-bar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 10px var(--space-3);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border-default);
}
.browser-dots { display: inline-flex; gap: 6px; flex-shrink: 0; }
.browser-dots i {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--border-strong);
}
.browser-url {
  flex: 1; max-width: 22rem;
  padding: 3px 12px;
  border-radius: var(--radius-full);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--text-subtle);
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hero-shot img,
.browser-frame img {
  display: block; width: 100%; height: auto;
  background: var(--surface-page);
}
/* Screenshots are captured in the light UI; dim them under the dark theme so
   the hero doesn't glare against the dark page. */
[data-theme="dark"] .browser-frame img { filter: brightness(0.84) contrast(1.03) saturate(0.95); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .browser-frame img { filter: brightness(0.84) contrast(1.03) saturate(0.95); }
}
@media (max-width: 720px) {
  .hero-shot { margin-top: var(--space-8); }
  .browser-url { display: none; }
}

/* Long-form legal documents (privacy policies, agreements). The agreement
   *_include.html files are shared with PDF rendering in app.py and keep their
   bare legacy markup — this class restores document styling on-screen under
   the Tailwind preflight reset. */
.legal-doc h1,
.legal-doc h2 { font-size: var(--fs-lg, 1.125rem); font-weight: var(--fw-bold); color: var(--text-primary); margin: 0 0 var(--space-4); }
.legal-doc h3,
.legal-doc h4,
.legal-doc h5 { font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--text-primary); margin: var(--space-8) 0 var(--space-3); }
.legal-doc > :first-child { margin-top: 0; }
.legal-doc p { margin: 0 0 var(--space-4); }
.legal-doc ul { list-style: disc; margin: 0 0 var(--space-4); padding-left: var(--space-6); }
.legal-doc ol { list-style: decimal; margin: 0 0 var(--space-4); padding-left: var(--space-6); }
/* Agreement clauses use typed lists — only type="a" exists in the legal
   docs today (CSS [type=] matching is case-insensitive, so add variants
   here with care if other letterings ever appear). */
.legal-doc ol[type="a"] { list-style-type: lower-alpha; }
.legal-doc ul ul, .legal-doc ol ol { margin-bottom: 0; }
.legal-doc li { margin: var(--space-1) 0; }
.legal-doc a:not(.btn) { color: var(--color-link); text-decoration: underline; }
.legal-doc a:not(.btn):hover { color: var(--action-primary-hover); }
.legal-doc table { width: 100%; border-collapse: collapse; margin: 0 0 var(--space-4); }
.legal-doc th, .legal-doc td { border: 1px solid var(--border-default); padding: 8px 12px; text-align: left; vertical-align: top; }
.legal-doc th { background: var(--surface-raised); font-weight: var(--fw-semibold); color: var(--text-primary); }

@media (max-width: 480px) {
  /* Narrow phones: let the form shrink instead of forcing horizontal scroll */
  .site-footer-newsletter .footer-newsletter-form { min-width: 0; width: 100%; }
  .newsletter-signup form { flex-direction: column; align-items: stretch; }
  .newsletter-signup input[type="email"] { min-width: 0; }
}
