/*
Theme Name: Thabor
Theme URI: https://thabor.cz
Author: Thabor
Author URI: https://thabor.cz
Description: Block theme for Thabor, an AI solutions company. Dark, premium, restrained "datasheet" aesthetic for a defence/government and academia audience.
Version: 0.19.2
Requires at least: 6.5
Tested up to: 6.7
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: thabor
*/

/* ============================================================ FONTS
   Self-hosted to keep visitor data off third-party CDNs (no IP leak to
   Google) — required by the same data-sovereignty stance the site sells,
   and avoids the GDPR exposure of hot-linking Google Fonts. One brand face,
   Rethink Sans (per the 2026-09 brand manual), for display + body; JetBrains
   Mono kept for labels. Variable woff2, one file per family + Latin subset;
   latin-ext carries Czech diacritics (Příkop, Česká, Krajský). Licensed under
   the SIL OFL (redistributable). */
@font-face {
  font-family: 'Rethink Sans';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('assets/fonts/rethink-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Rethink Sans';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('assets/fonts/rethink-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('assets/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('assets/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Extra tints derived from the accent (not expressible as theme.json presets). */
  --thabor-accent-soft: rgba(116, 149, 186, 0.12);
  --thabor-accent-line: rgba(116, 149, 186, 0.28);
  --thabor-accent-bright: #9AB6C4;
  --thabor-tracking-label: 0.16em;
  /* Left/right inset that lines up labels with the wide content spine inside the
     hero signal band. The 50vw term is capped at the band's half-width (800px, half
     of the 1600px band cap) so on an ultrawide the labels stay anchored to the
     capped, centred band instead of flying to mid-screen (and swapping sides). */
  --thabor-inset: max(var(--wp--style--root--padding-left, 1.5rem),
    calc(min(50vw, 800px) - var(--wp--style--global--wide-size, 1220px) / 2 + var(--wp--style--root--padding-left, 1.5rem)));
}

/* ============================================================ BASE */
/* Offset in-page anchor jumps (header nav, footer links → #services, #software,
   #contact) so the target section lands below the sticky header instead of hidden
   beneath it. */
html { scroll-padding-top: 5rem; }
/* Keep a hyphenated compound (e.g. "high-stakes" in the hero) on one line. */
.nowrap { white-space: nowrap; }
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

a { text-decoration: none; }
.thabor-main :where(p) { text-wrap: pretty; }
:where(h1, h2, h3, h4) { text-wrap: balance; }

:where(:focus-visible) {
  outline: 2px solid var(--wp--preset--color--accent);
  outline-offset: 3px;
}

/* Skip link */
.skip-link {
  position: absolute;
  left: 1rem;
  top: -100%;
  z-index: 100;
  padding: 0.75rem 1rem;
  background: var(--wp--preset--color--accent);
  color: var(--wp--preset--color--accent-ink);
  border-radius: 5px;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.8rem;
}
.skip-link:focus { top: 1rem; }

/* Reset WP's default top margin on the footer band. */
.wp-site-blocks > footer { margin-block-start: 0; }

/* Mono eyebrow / label: the through-line micro-typography. */
.eyebrow {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: var(--thabor-tracking-label);
  text-transform: uppercase;
  color: var(--wp--preset--color--muted);
}
.eyebrow--accent { color: var(--wp--preset--color--accent); }

/* ========================================================== HEADER */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--wp--preset--color--border-strong);
  padding-block: 0;
}
/* The frosted background lives on a ::before, NOT on .site-header itself.
   A backdrop-filter (like filter/transform) on an element makes it the
   containing block for any position:fixed descendant — which would trap the
   mobile nav overlay inside the ~64px header instead of letting it cover the
   viewport. Keeping the blur on a child pseudo-element preserves the glass
   look while leaving the header free of a fixed-positioning containing block. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--wp--preset--color--background) 84%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  backdrop-filter: blur(10px) saturate(140%);
}
.site-header__inner {
  min-height: 4rem;
  align-items: center;
}
/* Brand logo. The site-title block still renders the linked "Thabor" text
   (kept in the DOM for accessibility and SEO); CSS swaps it for the white
   horizontal logo from the 2026-09 brand kit, served from the theme like the
   fonts — url() resolves relative to style.css. */
.wordmark {
  display: inline-flex;
  align-items: center;
}
.wordmark a {
  display: block;
  width: 124px;
  height: 30px;
  background: url('assets/brand/thabor-logo-horizontal-white.svg') no-repeat left center;
  background-size: contain;
  text-indent: -200vw;
  overflow: hidden;
  white-space: nowrap;
  transition: opacity 0.15s ease;
}
.wordmark a:hover { opacity: 0.82; }
.primary-nav {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.74rem;
  letter-spacing: 0.04em;
}
.primary-nav .wp-block-navigation-item__content { color: var(--wp--preset--color--muted); }
.primary-nav .wp-block-navigation-item__content:hover { color: var(--wp--preset--color--text); }
.nav-cluster { align-items: center; gap: 1.4rem; }

/* Mobile overlay menu. WP core's responsive overlay defaults to a white panel
   with black text and centred items — jarring against the dark datasheet UI.
   Restyle it to the brand: a full-bleed dark surface, left-aligned links sized
   for touch, and a visible close control.

   The background/colour selectors mirror core's own
   `.wp-block-navigation:not(.has-background) …__responsive-container.is-menu-open`
   (specificity 0,4,0) and add `.primary-nav` so ours wins regardless of load
   order. Scoped to this nav; only applies once the container is the open modal. */
.wp-block-navigation.primary-nav:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open {
  background: var(--wp--preset--color--background);
  color: var(--wp--preset--color--text);
  padding: 5.5rem 1.75rem 2.5rem;
}
.primary-nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
  gap: 0.4rem;
}
.primary-nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
  color: var(--wp--preset--color--text);
  font-size: 1.15rem;
  padding-block: 0.55rem;
}
.primary-nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content:hover {
  color: var(--wp--preset--color--accent);
}
.primary-nav .wp-block-navigation__responsive-container-close {
  color: var(--wp--preset--color--text);
}
.primary-nav .wp-block-navigation__responsive-container-close:hover {
  color: var(--wp--preset--color--accent);
}

/* Language switcher (EN / CS) — sits in the header nav-cluster and the footer
   bar. Mono and sized to match the primary nav; the active language is full
   text colour and non-interactive, the other is a muted link. */
.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.5ch;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.lang-switch__opt { color: var(--wp--preset--color--muted); text-decoration: none; }
a.lang-switch__opt:hover { color: var(--wp--preset--color--text); }
.lang-switch__opt.is-active { color: var(--wp--preset--color--text); font-weight: 500; }
.lang-switch__sep { color: var(--wp--preset--color--faint); }

/* ============================================================ HERO */
.hero {
  position: relative;
  border-bottom: 1px solid var(--wp--preset--color--border-strong);
  padding-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--80);
}
/* Faint blueprint grid behind the headline: turns the hero's empty upper band into
   intentional instrument texture. Purely decorative (a ::before, out of the a11y
   tree), masked to fade out well above the signal so it never fights the canvas. */
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* Vertical "columns" rise up behind the headline, fading softly in every direction —
   including UP toward the header, so the grid never reads as a block that should
   connect to the menu. */
.hero::before {
  background-image: linear-gradient(to right, rgba(116, 149, 186, 0.11) 1px, transparent 1px);
  background-size: 72px 72px;
  -webkit-mask-image: radial-gradient(115% 86% at 32% 42%, #000 0%, #000 26%, transparent 78%);
  mask-image: radial-gradient(115% 86% at 32% 42%, #000 0%, #000 26%, transparent 78%);
}
/* Horizontal rules share the same field but are masked lower, so no "ceiling" line
   hugs the top edge — the grid is open at the top and the columns carry it upward. */
.hero::after {
  background-image: linear-gradient(to bottom, rgba(116, 149, 186, 0.11) 1px, transparent 1px);
  background-size: 72px 72px;
  -webkit-mask-image: radial-gradient(108% 72% at 34% 56%, #000 0%, #000 24%, transparent 72%);
  mask-image: radial-gradient(108% 72% at 34% 56%, #000 0%, #000 24%, transparent 72%);
}
/* Single-column cinematic hero: the headline, a full-bleed signal-field band, the
   sub, and a row of credibility marks stack down one spine. Sits above the grid. */
.hero__inner {
  display: block;
  position: relative;
  z-index: 1;
}
.hero__lead { min-width: 0; }
.hero__kicker {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  color: var(--wp--preset--color--accent);
  margin-bottom: 1.1rem;
}
.hero__title {
  font-size: var(--wp--preset--font-size--display);
  line-height: 1.03;
  max-width: 17ch;
  overflow-wrap: break-word;
}
/* EN headline breaks after "for" by hand (two lines on desktop), so it needs
   the full width rather than the 17ch measure. */
.hero__title--wide { max-width: none; }
.hero__sub {
  color: var(--wp--preset--color--muted);
  max-width: 54ch;
  margin-top: 1.3rem;
}
/* ==================================== HERO SIGNAL FIELD
   Full-bleed canvas instrument under the headline: five noisy candidate signals
   converge through an automated evidence-check gate and a human-review gate into a
   single evidence-backed result — the assurance thesis, in motion. Animated by
   thabor_hero_signal() in functions.php; a calm static frame renders under reduced
   motion, and the mono labels stand on their own with no JS at all. */
.hero-signal {
  position: relative;
  /* Full-bleed up to a cap, then centred. Letting the band span 100vw on an
     ultrawide pushes the %-positioned gates far to the right, where the human
     gate collides with the content-anchored "Evidence-backed results" label.
     Capping keeps the whole instrument composed and its labels aligned. */
  width: min(100vw, 1600px);
  margin-left: calc(50% - min(50vw, 800px));
  height: clamp(250px, 38vh, 380px);
  /* Tightened from clamp(2rem,4vw,3.5rem): the old gap left a dead zone between
     the headline and the band. */
  margin-block: clamp(1.25rem, 2.5vw, 2.25rem);
  /* Footprint as a soft steel WEDGE: an apex near the resolve zone that broadens
     rightward into "evidence-backed results", Gaussian-blurred so its edges melt into
     the black rather than reading as a shape. Left-to-right opacity ramp means it
     brightens as the result settles. Painted behind the transparent canvas. */
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 100' preserveAspectRatio='none'><defs><filter id='m' x='-25%25' y='-25%25' width='150%25' height='150%25'><feGaussianBlur stdDeviation='7'/></filter><linearGradient id='w' x1='0' y1='0' x2='1' y2='0'><stop offset='0.12' stop-color='rgb(154,182,196)' stop-opacity='0'/><stop offset='0.5' stop-color='rgb(154,182,196)' stop-opacity='0.33'/><stop offset='0.86' stop-color='rgb(154,182,196)' stop-opacity='0'/></linearGradient></defs><polygon points='50,50 120,6 120,94' fill='url(%23w)' filter='url(%23m)'/></svg>") 0 0 / 100% 100% no-repeat;
}
/* Fade the signal's right edge so the resolved line dissolves into "evidence-backed
   results" instead of being sliced off at the band boundary. */
.hero-signal canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  -webkit-mask-image: linear-gradient(to right, #000 88%, transparent 100%);
  mask-image: linear-gradient(to right, #000 88%, transparent 100%);
}
.hero-signal .sig-lab {
  position: absolute;
  top: clamp(10px, 6%, 20px);
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.6;
  z-index: 1;
}
.hero-signal .sig-lab b { display: block; font-weight: 400; }
.hero-signal .sig-lab--l { left: var(--thabor-inset); color: var(--wp--preset--color--muted); }
.hero-signal .sig-lab--r {
  right: var(--thabor-inset);
  text-align: right;
  color: var(--thabor-accent-bright);
  max-width: 11rem;
}
.hero-signal .gate {
  position: absolute;
  top: clamp(10px, 6%, 20px);
  transform: translateX(-50%);
  text-align: center;
  z-index: 1;
  font-family: var(--wp--preset--font-family--mono);
  line-height: 1.5;
}
.hero-signal .gate .n { display: block; font-size: 0.62rem; letter-spacing: 0.1em; color: var(--wp--preset--color--accent); }
.hero-signal .gate .s { display: block; font-size: 0.52rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--wp--preset--color--faint); }
.hero-signal .gate--auto { left: 38%; }
.hero-signal .gate--human { left: 66%; }
/* Below tablet there isn't room for the mid-band gate labels alongside the two
   end labels; the solid (automated) and dashed (human) gate lines in the canvas
   still mark the checkpoints, so drop the gate text and keep Noise -> Evidence. */
@media (max-width: 820px) {
  .hero-signal .gate { display: none; }
}
@media (max-width: 520px) {
  .hero-signal .sig-lab { font-size: 0.6rem; }
  .hero-signal .sig-lab--r { max-width: 8.5rem; }
}

/* Datasheet spec panel: the dossier as a vertical key/value card that fills
   the right column of the hero. */
.hero__dossier {
  display: grid;
  grid-template-columns: 1fr;
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 8px;
  background: var(--wp--preset--color--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
}
/* Key/value grid: a fixed key column so the values line up in one vertical
   column across every row, instead of drifting (they were right-aligned to the
   panel edge, which left their start positions ragged). 4.75rem clears the
   widest label ("First step"). */
.dossier-cell {
  display: grid;
  grid-template-columns: 4.75rem 1fr;
  column-gap: 2.25rem;
  align-items: baseline;
  padding: 0.95rem 1.25rem;
  border-top: 1px solid var(--wp--preset--color--border);
}
.dossier-cell:first-child { border-top: 0; }
/* The cells are plain groups (not constrained layout), so WP no longer injects
   margin-inline:auto on the children. Pin both columns to the start of their
   grid track so keys and values each line up on one vertical edge; the margin
   reset (high enough specificity to win outright) stays as insurance. */
.dossier-cell > .meta-k,
.dossier-cell > .meta-v { margin-left: 0 !important; margin-right: 0 !important; justify-self: start; }
.dossier-cell .meta-v { margin-top: 0; text-align: left; }
.meta-k {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--muted);
}
.meta-v {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.82rem;
  color: var(--wp--preset--color--text);
  margin-top: 0.3rem;
}

/* ====================================================== SECTIONS */
/* Single content spine: the section background/rule is full-bleed, but every
   text block and card grid inside aligns to the same wide left edge.
   WP's `.is-layout-constrained > .alignwide` only caps at wideSize (1220px)
   with no 100% ceiling, so on narrow viewports the wide wrap can overrun the
   screen. Re-cap it at 100% of the (padded) band so content always wraps. */
.hero__inner.alignwide,
.section__wrap.alignwide {
  max-width: min(var(--wp--style--global--wide-size, 1220px), 100%) !important;
}
.section { padding-block: var(--wp--preset--spacing--80); }
.section--ruled { border-top: 1px solid var(--wp--preset--color--border); }
.section__head { max-width: 60ch; margin-bottom: 2rem; }
.section__head .eyebrow { display: block; margin-bottom: 1rem; }
.section__title { font-size: var(--wp--preset--font-size--xx-large); }
.section__intro {
  color: var(--wp--preset--color--muted);
  max-width: 52ch;
  margin-top: 1rem;
}

/* Decorative section index: a large, faint mono numeral in the top-right dead space
   of a section — an "arrival point" that breaks the monotone scroll. Strictly
   decorative: the markup carries aria-hidden and it is unselectable / non-interactive,
   so it never enters the accessibility tree, heading order, or copy. Only added to
   sections whose top-right is genuinely empty (single-column heads). */
.section__wrap:has(.section-index) { position: relative; }
.section-index {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  font-family: var(--wp--preset--font-family--mono);
  font-size: clamp(2.5rem, 5vw, 4.5rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--wp--preset--color--faint);
  opacity: 0.16;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}
/* On narrow screens the head wraps under the numeral, so drop it (decorative — no
   information is lost). */
@media (max-width: 760px) {
  .section-index { display: none; }
}

/* Capability statement */
.statement { border-top: 1px solid var(--wp--preset--color--border); }
/* Two-column statement: the problem (bright) left, the response (muted) right. */
.statement .section__wrap {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.statement__lead {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2.4rem);
  line-height: 1.3;
  letter-spacing: -0.015em;
  max-width: 26ch;
}
/* The response reads as supporting copy, a clear step down from the claim on
   the left rather than a co-equal statement. */
.statement__rest {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: clamp(1.1rem, 0.98rem + 0.5vw, 1.4rem);
  line-height: 1.5;
  letter-spacing: -0.005em;
  color: var(--wp--preset--color--muted);
  max-width: 42ch;
  margin-top: 0;
}

/* ====================================================== CARDS */
.cards .wp-block-column {
  border: 1px solid var(--wp--preset--color--border-strong);
  /* Accent "tab" along the top edge gives each of the four cards its own identity
     and makes them read as filed instrument cards, not four identical rectangles. */
  border-top: 2px solid var(--thabor-accent-line);
  background: var(--wp--preset--color--surface);
  padding: clamp(1.5rem, 2.5vw, 2rem);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
  transition: border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.cards .wp-block-column:hover {
  border-color: var(--thabor-accent-line);
  border-top-color: var(--wp--preset--color--accent);
  transform: translateY(-3px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 12px 28px -14px rgba(0, 0, 0, 0.75);
}
.card-icon {
  width: 3rem;
  height: 3rem;
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 7px;
  /* Accent-tinted well behind the glyph so the icon has presence instead of
     nearly disappearing as a hairline in an empty box. */
  background: var(--thabor-accent-soft);
  position: relative;
  margin-bottom: 1.2rem;
  flex: none;
  transition: border-color 0.25s ease, background-color 0.25s ease;
}
.cards .wp-block-column:hover .card-icon {
  border-color: var(--wp--preset--color--accent);
  background: color-mix(in srgb, var(--wp--preset--color--accent) 18%, transparent);
}
/* Custom line icons (masked SVG), one per commitment card. The mask recolours
   the artwork to the accent; swap the --card-icon URL to change a glyph. The
   service glyphs borrow the mark's shapes (crenel, flanks, flared cap). */
.card-icon::before {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 26px;
  height: 26px;
  background-color: var(--wp--preset--color--accent);
  -webkit-mask: var(--card-icon) center / contain no-repeat;
  mask: var(--card-icon) center / contain no-repeat;
}
/* Per-card glyph, keyed by an explicit modifier class on the icon (not the
   column order) so it resolves on the front end AND in the Site Editor canvas,
   and survives card reordering. */
.card-icon--shield::before { --card-icon: url("assets/icons/shield-check.svg"); }
.card-icon--user::before   { --card-icon: url("assets/icons/user.svg"); }
.card-icon--record::before { --card-icon: url("assets/icons/record.svg"); }
.card-icon--server::before { --card-icon: url("assets/icons/server-lock.svg"); }
.card-icon--briefcase::before { --card-icon: url("assets/icons/briefcase.svg"); }
.card-icon--folio::before  { --card-icon: url("assets/icons/folio.svg"); }
.card-icon--flask::before  { --card-icon: url("assets/icons/flask.svg"); }
.card-k {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--wp--preset--color--accent);
}
.card-title { font-size: 1.35rem; margin-top: 0.7rem; }
.card-text { color: var(--wp--preset--color--muted); margin-top: 0.6rem; font-size: 0.98rem; }

/* ====================================================== STEPS */
.steps .wp-block-column {
  border-top: 1px solid var(--wp--preset--color--border-strong);
  padding-top: 1.3rem;
  text-align: left;
}
.step-num {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 1.2rem;
  color: var(--wp--preset--color--accent);
  letter-spacing: 0.04em;
}
.step-title { font-size: 1.25rem; margin-top: 0.6rem; }
.step-text { color: var(--wp--preset--color--muted); margin-top: 0.6rem; font-size: 0.98rem; max-width: 38ch; }

/* ====================================================== CONTACT */
.contact { border-top: 1px solid var(--wp--preset--color--border); }
/* Two-column contact: the invitation on the left, a compact contact spec
   panel on the right (mirrors the hero dossier). */
.contact .section__wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.7fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
.contact__main { min-width: 0; }
.contact__aside {
  display: grid;
  grid-template-columns: 1fr;
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 8px;
  background: var(--wp--preset--color--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
  align-self: start;
}
.contact__title { font-size: var(--wp--preset--font-size--x-large); }
.contact__text { color: var(--wp--preset--color--muted); max-width: 48ch; }
.contact__note {
  color: var(--wp--preset--color--faint);
  font-size: 0.9rem;
  max-width: 50ch;
  margin-top: 0.9rem;
}

/* Section cross-link (mono, accent) used to point a homepage section at its
   deeper page, e.g. Assurance -> Security & Assurance. */
.section__link {
  margin-top: 1.75rem;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.8rem;
  letter-spacing: 0.03em;
}
.section__link a { color: var(--wp--preset--color--accent); }
.section__link a:hover { color: var(--wp--preset--color--text); }

/* ====================================================== FOOTER */
.site-footer {
  border-top: 1px solid var(--wp--preset--color--border);
  background: var(--wp--preset--color--surface-2);
  padding-block: var(--wp--preset--spacing--70);
}
.footer-legal { color: var(--wp--preset--color--faint); font-size: 0.875rem; line-height: 1.7; margin-top: 1rem; }
.footer-col__title {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: var(--thabor-tracking-label);
  text-transform: uppercase;
  color: var(--wp--preset--color--muted);
  /* The heading→list gap lives on the <ul> margin-top below, NOT here: WP's
     `:where(.is-layout-flow) > *` layout rule zeroes this heading's
     margin-block-end (it ties on specificity and wins on source order). */
  margin-bottom: 0;
}
.footer-col ul { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: 0.3rem; line-height: 1.35; }
.footer-col a { color: var(--wp--preset--color--muted); font-size: 0.875rem; }
.footer-col a:hover { color: var(--wp--preset--color--text); }
.footer-bar {
  border-top: 1px solid var(--wp--preset--color--border);
  margin-top: var(--wp--preset--spacing--70);
  padding-top: 1.5rem;
  color: var(--wp--preset--color--faint);
  font-size: 0.75rem;
  align-items: center;
}
.footer-bar a { color: var(--wp--preset--color--muted); }

/* Page / 404 title sections + prose */
/* Every page starts close under the header: WP's blockGap puts 24px above
   <main> (and between the title band and the body), so zero it on every page and
   let each page's first band set a 2rem top: the page-head here, .hero and
   #software below. */
.wp-site-blocks > .thabor-main { margin-block-start: 0; }
.page-head { border-bottom: 1px solid var(--wp--preset--color--border-strong); padding-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--50); }
.page-head + .page-body,
.page-head + .section { margin-block-start: 0; padding-top: var(--wp--preset--spacing--50); }
.page-title { font-size: var(--wp--preset--font-size--xx-large); }
.page-kicker { font-family: var(--wp--preset--font-family--mono); font-size: 0.8rem; letter-spacing: 0.1em; color: var(--wp--preset--color--accent); margin-bottom: 1rem; }
.page-body { padding-block: var(--wp--preset--spacing--70) var(--wp--preset--spacing--80); }
/* Inner pages (About, Security, legal, 404) anchor their content to the same
   wide left spine as the header, footer and homepage via the alignwide __inner
   wrappers, then cap the prose to a readable measure so it reads as a left
   column rather than a block centred away from the spine. */
.page-body__inner > * { max-width: 47.5rem; }
/* The post content itself spans the spine so the editorial blocks below can run
   wide; its prose children keep the 47.5rem measure. */
.page-body__inner > .wp-block-post-content { max-width: none; }
.page-body .wp-block-post-content > * { max-width: 47.5rem; margin-left: 0 !important; margin-right: 0 !important; }
.page-body .wp-block-post-content > :is(.content-cards, .content-profile, .content-cta, .content-note) { max-width: none; }
.page-body :where(h2) { margin-top: 2.5rem; }
.page-body :where(h3) { margin-top: 2rem; }
.page-body :where(ul, ol) { padding-left: 1.4em; }
.page-body :where(li::marker) { color: var(--wp--preset--color--faint); }
.page-body :where(a) { text-decoration: underline; text-underline-offset: 0.2em; }

/* ================================== EDITORIAL BLOCKS (content/build.mjs: cards,
   facts, button, className). Same card and dossier language as the homepage. */
.content-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
  margin-top: 2.5rem;
}
.content-card {
  border: 1px solid var(--wp--preset--color--border-strong);
  border-top: 2px solid var(--thabor-accent-line);
  background: var(--wp--preset--color--surface);
  padding: clamp(1.25rem, 2vw, 1.75rem);
}
/* WP's flow layout gives every child after the first a 24px top margin; zero
   it inside the grids and panels so cards, panel and button line up. */
.content-cards > *,
.content-profile > .content-facts,
.content-cta > * { margin-block: 0 !important; }
.content-card > * { margin-block: 0; }
.content-card__n {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--wp--preset--color--accent);
}
.page-body .content-card__title { margin-top: 0.9rem; font-size: 1.2rem; line-height: 1.25; }
.content-card > p:last-child { margin-top: 0.7rem; color: var(--wp--preset--color--muted); font-size: 0.95rem; line-height: 1.6; }

/* Founder: heading and line on the left, dossier panel on the right. */
.content-profile {
  display: grid;
  grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr);
  column-gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
  margin-top: 3rem;
}
.content-profile > * { grid-column: 1; margin-block: 0; }
.content-profile > h2 { margin-top: 0; }
.content-profile > p { margin-top: 1rem; }
.content-profile > .content-facts { grid-column: 2; grid-row: 1 / span 4; }
.content-facts {
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 8px;
  background: var(--wp--preset--color--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
}
.content-facts .dossier-cell { grid-template-columns: 7.5rem 1fr; margin: 0; }
.content-facts .meta-v { line-height: 1.55; }

/* Closing call to action: one panel, heading left, button right. */
.content-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin-top: 3rem;
  padding: clamp(1.25rem, 2.5vw, 1.75rem) clamp(1.25rem, 2.5vw, 2rem);
  border: 1px solid var(--wp--preset--color--border-strong);
  border-top: 2px solid var(--thabor-accent-line);
  background: var(--wp--preset--color--surface);
}
.page-body .content-cta > h2 { margin-top: 0; font-size: var(--wp--preset--font-size--x-large); }

/* A ruled note after the main content (e.g. vulnerability disclosure). */
.content-note { margin-top: 3.5rem; padding-top: 2rem; border-top: 1px solid var(--wp--preset--color--border); }
.content-note > * { max-width: 47.5rem; }
.page-body .content-note > h2 { margin-top: 0; font-size: var(--wp--preset--font-size--x-large); }

@media (max-width: 1000px) {
  .content-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .content-cards { grid-template-columns: 1fr; }
  .content-profile { grid-template-columns: 1fr; }
  .content-profile > .content-facts { grid-column: 1; grid-row: auto; margin-top: 1.5rem !important; }
  .content-facts .dossier-cell { grid-template-columns: 6.5rem 1fr; column-gap: 1.25rem; }
}

/* Legal document version flag: a small badge showing the single published
   version (rendered by legal/build.mjs from the source JSON). */
.legal-meta {
  display: inline-block;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--wp--preset--color--muted);
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 4px;
  padding: 0.35rem 0.7rem;
  margin-bottom: 1.75rem;
}
.legal-meta a { color: inherit; }

/* ====================================================== EQUAL CARDS (reference pattern) */
.equal-cards { gap: 1.25rem; }
.equal-cards > .wp-block-column {
  display: flex;
  flex-direction: column;
  flex-grow: 0;
}
.equal-cards > .wp-block-column > .wp-block-group { display: flex; flex-direction: column; flex-grow: 1; }
/* WP's constrained layout sets margin-left/right:auto !important on child
   blocks, which centres the icon, kicker and title inside these flex groups.
   Override (with !important, to beat WP's) so cards and steps stay left-aligned
   and consistent with the spine. */
.equal-cards > .wp-block-column > .wp-block-group > * {
  margin-left: 0 !important;
  margin-right: 0 !important;
  max-width: 100%;
}

/* Buttons share one box model so solid and outline variants line up at the same
   height when sat side by side. theme.json pads the solid fill in rem; WP core's
   outline style pads in em off the (smaller) button font-size, which otherwise
   leaves the outline button visibly shorter. Give every button a 1px border
   (transparent on the solid) and identical padding so both resolve to one box. */
.wp-block-button__link { border: 1px solid transparent; white-space: nowrap; }

/* Secondary / outline button (hero "How we work"). theme.json fills buttons with
   the accent; the outline variant is transparent with a hairline border. */
.wp-block-button.is-style-outline > .wp-block-button__link {
  padding: 0.62rem 1.2rem;
  background: transparent;
  color: var(--wp--preset--color--text);
  border: 1px solid var(--wp--preset--color--border-strong);
}
.wp-block-button.is-style-outline > .wp-block-button__link:hover {
  border-color: var(--wp--preset--color--accent);
  background: var(--thabor-accent-soft);
  color: var(--wp--preset--color--text);
}

/* Header CTA is a compact pill: the nav bar is slim and its labels are small,
   so the full-size accent button read as oversized up there. Tighten its
   padding (it keeps the global font size and nowrap). */
.site-header .wp-block-button__link { padding: 0.45rem 1rem; }

/* Phone header: logo + menu toggle + CTA + EN/CS must fit one row (it needed
   ~393px with the old logo, 415px with the new one, so 360-390px phones got a
   widened, clipped page). Tighten the cluster first; below 390px, where even
   that cannot fit the full logo, show the brand kit's standalone symbol (an
   official variant). The site-title text stays the link's accessible name.
   The gap needs .site-header in front: WP's generated layout class
   (.wp-container-core-group-is-layout-*) sets the cluster gap and outranks a
   bare .nav-cluster. */
@media (max-width: 480px) {
  .site-header .nav-cluster { gap: 0.9rem; }
  .site-header .wp-block-button__link { padding: 0.45rem 0.75rem; }
}
@media (max-width: 389px) {
  .site-header .wordmark a {
    width: 26px;
    background-image: url('assets/brand/thabor-symbol-white.svg');
  }
}

/* Primary CTA emphasis: the contact email is the page's key action, so it carries
   more presence than the compact header pill — larger, with a soft accent glow and a
   lift + brighter fill on hover. (The header pill keeps its tightened size.) */
.contact .wp-block-button > .wp-block-button__link {
  font-size: 0.82rem;
  padding: 0.85rem 1.75rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 8px 24px -10px rgba(116, 149, 186, 0.55);
  transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}
.contact .wp-block-button > .wp-block-button__link:hover {
  background: color-mix(in srgb, var(--wp--preset--color--accent) 90%, #fff);
  color: var(--wp--preset--color--accent-ink);
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 12px 30px -10px rgba(116, 149, 186, 0.7);
}

/* ====================================================== MOTION */
/* Hidden start state only when JS runs (.thabor-js is set in <head> before first
   paint), so a no-JS visitor still sees every section. */
.thabor-js .animate-on-scroll {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.thabor-js .animate-on-scroll.is-visible { opacity: 1; transform: translateY(0); }
/* Per-item reveal (the Services head, each card, the link): each fades in as it
   enters, so a section is never shown half-faded at the fold. Start-state gated
   behind .is-armed (added only by the script), so no-JS / the editor see it all.
   `translate`, not `transform`, so the cards' hover lift is left alone. */
.reveal-item.is-armed {
  opacity: 0;
  translate: 0 28px;
  transition: opacity 1s ease var(--reveal-delay, 0s), translate 1s ease var(--reveal-delay, 0s);
}
.cards .wp-block-column.reveal-item.is-armed {
  transition: opacity 1s ease var(--reveal-delay, 0s), translate 1s ease var(--reveal-delay, 0s),
    border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.reveal-item.is-armed.is-visible { opacity: 1; translate: 0 0; }
.fade-up { opacity: 0; transform: translateY(18px); animation: thaborFadeUp 0.6s ease forwards; }
@keyframes thaborFadeUp { to { opacity: 1; transform: translateY(0); } }

/* Keep animated content visible inside the block editor (reveal JS doesn't run there). */
.editor-styles-wrapper .animate-on-scroll,
.editor-styles-wrapper .fade-up {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  .animate-on-scroll, .fade-up { opacity: 1 !important; transform: none !important; }
  .reveal-item { opacity: 1 !important; translate: none !important; }
}

/* ====================================================== RESPONSIVE */
@media (max-width: 900px) {
  .hero__inner { grid-template-columns: 1fr; gap: clamp(1.75rem, 5vw, 2.75rem); align-items: stretch; }
  .statement .section__wrap { grid-template-columns: 1fr; gap: 1rem; }
  .statement__lead, .statement__rest { max-width: 38ch; }
  .contact .section__wrap { grid-template-columns: 1fr; gap: 1.75rem; }
}

/* =============================================== EVAL MATRIX */
/* Concrete proof artifact in the homepage Assurance section: an illustrative
   acceptance record that makes the "validate & test" method tangible. Built as
   a datasheet panel to match the hero dossier and contact aside. Selectors carry
   an extra `table` step so they outrank WP core's block-library table defaults
   (cell borders, the 3px thead rule) regardless of stylesheet load order. */
.eval-matrix {
  margin-top: 0;
  max-width: 760px;
  border: 1px solid var(--wp--preset--color--border-strong);
  border-top-color: var(--thabor-accent-line);
  border-radius: 8px;
  background: var(--wp--preset--color--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.eval-matrix__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1.25rem;
  background: color-mix(in srgb, var(--wp--preset--color--accent) 4%, transparent);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.eval-matrix__label,
.eval-matrix__tag {
  margin: 0;
  font-family: var(--wp--preset--font-family--mono);
  text-transform: uppercase;
}
.eval-matrix__label {
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--wp--preset--color--muted);
}
.eval-matrix__tag {
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  color: var(--wp--preset--color--faint);
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 999px;
  padding: 0.2rem 0.65rem;
}
/* Override WP core's `.wp-block-table{overflow-x:auto}`: with overflow-x set,
   overflow-y computes to `auto` too, so the row-reveal's transform:translateY(6px)
   briefly pushes content past the box and flashes a scrollbar while the record
   animates in. This table is a styled card stack that never needs to scroll, and
   the .eval-matrix panel already clips with overflow:hidden. */
.eval-matrix__table { margin: 0; overflow: visible; }
.eval-matrix__table table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--wp--preset--font-family--mono);
}
.eval-matrix__table table thead { border-bottom: 0; }
.eval-matrix__table table th,
.eval-matrix__table table td {
  border: 0;
  border-top: 1px solid var(--wp--preset--color--border);
  padding: 0.62rem 1.25rem;
  font-size: 0.8rem;
  text-align: left;
  vertical-align: baseline;
}
.eval-matrix__table table thead th {
  border-top: 0;
  font-weight: 500;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wp--preset--color--muted);
}
.eval-matrix__table table tbody td { color: var(--wp--preset--color--muted); }
.eval-matrix__table table tbody td:first-child { color: var(--wp--preset--color--text); }
/* Result column: right-aligned values with an accent pass tick. */
.eval-matrix__table table th:last-child,
.eval-matrix__table table td:last-child { text-align: right; white-space: nowrap; }
.eval-matrix__table table tbody td:last-child { color: var(--wp--preset--color--text); }
/* Pass ticks use the brighter "verified" tone (not the steel process accent), so
   the page encodes claim (steel) vs. verified (bright) chromatically. */
.eval-matrix__table table .tick { color: var(--thabor-accent-bright); margin-left: 0.45rem; }
.eval-matrix__note {
  margin-top: 1.5rem;
  max-width: 760px;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--wp--preset--color--faint);
}
/* On phones the three columns can't fit side by side (and horizontal scroll
   would hide the all-important Result column), so stack each row into a card:
   the test case on its own line, then the criterion and its result beneath it. */
@media (max-width: 600px) {
  .eval-matrix__bar { padding: 0.75rem 1rem; }
  .eval-matrix__table table,
  .eval-matrix__table table tbody { display: block; }
  .eval-matrix__table table thead { display: none; }
  .eval-matrix__table table tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    padding: 0.85rem 1rem;
    border-top: 1px solid var(--wp--preset--color--border);
  }
  .eval-matrix__table table tr:first-child { border-top: 0; }
  .eval-matrix__table table td { display: block; border: 0; padding: 0; font-size: 0.8rem; }
  .eval-matrix__table table td:first-child { flex: 1 1 100%; margin-bottom: 0.4rem; }
  .eval-matrix__table table td:nth-child(2) { color: var(--wp--preset--color--muted); }
  .eval-matrix__table table td:last-child {
    margin-left: auto;
    text-align: right;
    white-space: nowrap;
    color: var(--wp--preset--color--text);
  }
}

/* Assurance section: two-column composition — heading/intro spine on the left,
   the acceptance-record datasheet on the right (mirrors the hero and contact).
   Keep the media query after the base rule so the mobile stack wins the cascade. */
#assurance .section__wrap,
#software .section__wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
/* The Software panel grows when its install notes open; top-align so the app
   intro on the left stays put instead of re-centring. */
#software .section__wrap { align-items: start; row-gap: var(--wp--preset--spacing--50); }
#software.section { padding-top: var(--wp--preset--spacing--60); }
.assurance__lead,
.assurance__panel { min-width: 0; }
#assurance .section__head,
#software .section__head { margin-bottom: 0; }
/* These two override WP's blockGap:0 rule (same 0,1,0 specificity) on the lead
   and panel groups, which was zeroing the link's and note's own margin-top. */
.assurance__lead .section__link { margin-top: 2.5rem; }
.assurance__panel .eval-matrix__note { margin-top: 1.5rem; }
/* ACH alpha status: a solid pill on the download card and a notice above it,
   so the pre-release state is read before anything is downloaded. */
.eval-matrix__tag--alpha {
  color: var(--wp--preset--color--accent-ink);
  background: var(--wp--preset--color--accent);
  border-color: var(--wp--preset--color--accent);
  font-weight: 600;
}
.release-notice {
  margin: 0;
  padding: 0.85rem 1rem;
  border: 1px solid color-mix(in srgb, var(--wp--preset--color--accent) 45%, transparent);
  border-left: 3px solid var(--wp--preset--color--accent);
  border-radius: 6px;
  background: var(--thabor-accent-soft);
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--wp--preset--color--text);
}
.release-notice strong { color: var(--wp--preset--color--accent-strong); }
/* WP zeroes margin-block-end on flow children, so the gap sits on the card. */
.assurance__panel .release-notice + .eval-matrix { margin-top: 1rem; }
@media (max-width: 900px) {
  #assurance .section__wrap,
  #software .section__wrap { grid-template-columns: 1fr; gap: 1.75rem; }
}

/* Software section (ACH): short feature list under the intro, the download
   table in the datasheet card, and a collapsed "opening an unsigned build" note.
   blockGap:0 on the lead/panel groups zeroes sibling margins, so set them here. */
.assurance__lead .feature-list {
  margin: 1.5rem 0 0;
  padding: 0;
  list-style: none;
}
.feature-list li {
  position: relative;
  padding: 0.55rem 0 0.55rem 1.4rem;
  border-top: 1px solid var(--wp--preset--color--border);
  color: var(--wp--preset--color--text);
}
.feature-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.15rem;
  width: 0.5rem;
  height: 1px;
  background: var(--wp--preset--color--accent);
}
.eval-matrix__table td a { color: var(--wp--preset--color--accent); }
.eval-matrix__table td a:hover { color: var(--wp--preset--color--text); }
.eval-matrix__table td a.eval-matrix__sum { margin-left: 0.6rem; color: var(--wp--preset--color--muted); font-size: 0.85em; }
.eval-matrix__table td a.eval-matrix__sum:hover { color: var(--wp--preset--color--text); }
.assurance__panel .install-help {
  margin-top: 1rem;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.7rem;
  line-height: 1.55;
  color: var(--wp--preset--color--muted);
}
.install-help summary {
  cursor: pointer;
  color: var(--wp--preset--color--accent);
}
.install-help summary:hover { color: var(--wp--preset--color--text); }
.install-help ul { margin: 0.75rem 0 0; padding-left: 1.1rem; }
.install-help li + li { margin-top: 0.4rem; }
.install-help p { margin: 0.75rem 0 0; }
.install-help code { font-size: inherit; overflow-wrap: anywhere; }
.assurance__panel .install-help + .install-help { margin-top: 0.6rem; }
/* App screenshot under the two columns, spanning the full grid width. The
   dashed --empty slot is rendered for logged-in editors only. */
.ach-shot {
  grid-column: 1 / -1;
  margin: 0;
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 10px;
  overflow: hidden;
  background: var(--wp--preset--color--surface);
  box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.8);
}
.ach-shot img { display: block; width: 100%; height: auto; }
/* On a phone the full window would shrink past legibility: keep it at a
   readable width and let the frame scroll sideways instead. */
@media (max-width: 700px) {
  .ach-shot:not(.ach-shot--empty) { overflow-x: auto; overscroll-behavior-x: contain; }
  .ach-shot img { width: 760px; max-width: none; }
}
.ach-shot--empty {
  aspect-ratio: 16 / 10;
  display: grid;
  place-items: center;
  border-style: dashed;
  background: transparent;
  box-shadow: none;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.75rem;
  line-height: 1.7;
  text-align: center;
  color: var(--wp--preset--color--muted);
}

/* Caption above the acceptance record: frames the artifact as method (criteria
   agreed up front), not invented proof. blockGap:0 on the panel zeroes sibling
   margins, so set the spacing explicitly. */
.eval-matrix__caption {
  margin: 0 0 1.1rem;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--wp--preset--color--muted);
  max-width: 60ch;
}
.assurance__panel .eval-matrix__caption { margin-bottom: 1.1rem; }

/* =============================================== PIPELINE PANEL
   "How a result is produced" as a framed datasheet instrument — a sibling to the
   eval-matrix and dossier panels, so it reads as a built artifact rather than
   floating circles. A header bar names it, a footer legend explains the accent,
   and inside sits a LINEAR source -> record trace (a pipeline, not a cycle): five
   station discs on a rail with a continuous left-to-right marching flow, the two
   control gates accented and pulsing as checkpoints. The flow runs continuously
   (the page's always-in-motion element); stations reveal left-to-right on scroll
   and replay, gated behind .flow.is-armed/.is-running which the reveal script
   (functions.php) toggles. Reflows to a vertical timeline on narrow screens. */
.pipe {
  margin-top: clamp(3rem, 4vw, 4.5rem);
  border: 1px solid var(--wp--preset--color--border-strong);
  /* Accent-capped top edge + inset highlight / drop shadow: the panel reads as a
     lit instrument raised off the page rather than a flat outline. */
  border-top-color: var(--thabor-accent-line);
  border-radius: 10px;
  background: var(--wp--preset--color--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 2px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.pipe__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1.4rem;
  background: color-mix(in srgb, var(--wp--preset--color--accent) 4%, transparent);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.pipe__label,
.pipe__tag {
  margin: 0;
  font-family: var(--wp--preset--font-family--mono);
  text-transform: uppercase;
}
.pipe__label {
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--wp--preset--color--muted);
}
.pipe__tag {
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  color: var(--wp--preset--color--faint);
  border: 1px solid var(--wp--preset--color--border-strong);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  white-space: nowrap;
}
.pipe__body { padding: clamp(2.5rem, 4vw, 3.5rem) clamp(1.5rem, 3vw, 2.5rem); }
.pipe__note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.75rem;
  margin: 0;
  padding: 0.85rem 1.4rem;
  border-top: 1px solid var(--wp--preset--color--border);
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.68rem;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--wp--preset--color--faint);
}
.pipe__leg { display: flex; align-items: center; gap: 0.55rem; }
/* Legend markers mirror the two gate treatments: filled = automated, dashed
   ring = human-owned. */
.pipe__dot {
  width: 9px;
  height: 9px;
  flex: none;
  border-radius: 50%;
}
.pipe__dot--auto {
  background: var(--wp--preset--color--accent);
  border: 1px solid var(--wp--preset--color--accent);
}
.pipe__dot--human {
  background: transparent;
  border: 1px dashed var(--wp--preset--color--accent);
}

/* --- The linear trace inside the panel. */
.rail {
  position: relative;
  --node: 84px;       /* station diameter */
  --rail-y: 42px;     /* station-centre offset from the top (= --node / 2) */
}
/* The track: a faint baseline with the marching accent flow on top, inset to the
   first/last station centres (each of five cells is 20% wide). */
.rail__track {
  position: absolute;
  top: var(--rail-y);
  left: 10%;
  right: 10%;
  height: 0;
  pointer-events: none;
}
.rail__base {
  position: absolute;
  inset: 0;
  top: -0.5px;
  height: 1px;
  background: var(--wp--preset--color--border-strong);
}
.rail__flow {
  position: absolute;
  inset: 0;
  top: -1px;
  height: 2px;
  background: repeating-linear-gradient(90deg,
    var(--wp--preset--color--accent) 0 6px, transparent 6px 15px);
  opacity: 0.8;
  animation: railMarch 1.4s linear infinite;
}
/* Offset matches the 15px dash period (6px dash + 9px gap) so the march loops
   seamlessly instead of snapping. Positive = dashes travel left-to-right. */
@keyframes railMarch { to { background-position-x: 15px; } }

/* Stations */
.rail__steps {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
}
.rail__step {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* Station disc: a subtly domed, opaque face (masks the rail behind it, so the
   line reads as connectors) with a hairline inner bezel for instrument depth. */
.rail__node {
  width: var(--node);
  height: var(--node);
  /* border-box so the 1.5–2px border sits inside --node: every disc is exactly
     --node wide (gate and plain alike), so its centre lands on the rail offset
     (--rail-x / --rail-y = --node / 2). Without this the borders grow the disc
     past --node and the rail reads ~1–2px off-centre. */
  box-sizing: border-box;
  flex: none;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 32%,
    var(--wp--preset--color--surface), var(--wp--preset--color--surface-2));
  border: 1.5px solid var(--wp--preset--color--border-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
}
.rail__node::before {
  content: "";
  position: absolute;
  inset: 7px;
  border-radius: 50%;
  border: 1px solid var(--wp--preset--color--border);
  opacity: 0.7;
}
.rail__step--gate .rail__node {
  background: radial-gradient(circle at 50% 32%,
    color-mix(in srgb, var(--wp--preset--color--accent) 22%, var(--wp--preset--color--surface-2)),
    color-mix(in srgb, var(--wp--preset--color--accent) 9%, var(--wp--preset--color--surface-2)));
  border-color: var(--wp--preset--color--accent);
  border-width: 2px;
}
.rail__step--gate .rail__node::before { border-color: var(--thabor-accent-line); opacity: 1; }
/* Two kinds of control gate, each with an always-visible concentric ring so the
   distinction reads at any moment:
   • Auto gate (evidence check) — a SOLID ring + a live outward GLOW pulse: a
     machine checking continuously.
   • Human gate (review) — a STATIC DASHED ring: a person-owned manual checkpoint. */
.rail__step--gate-auto .rail__node::after {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 1.5px solid var(--wp--preset--color--accent);
  opacity: 0.6;
}
.rail__step--gate-auto .rail__node { animation: railPulse 2.8s ease-out infinite; }
@keyframes railPulse {
  0%        { box-shadow: 0 0 0 0 color-mix(in srgb, var(--wp--preset--color--accent) 45%, transparent); }
  70%, 100% { box-shadow: 0 0 0 13px transparent; }
}
.rail__step--gate-human .rail__node::after {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 1.5px dashed var(--wp--preset--color--accent);
  opacity: 0.75;
}
.rail__num {
  position: relative;
  z-index: 1;
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  color: var(--wp--preset--color--faint);
}
.rail__step--gate .rail__num { color: var(--wp--preset--color--accent); }
.rail__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 1rem;
}
/* Role kicker — the station's type, in the same mono micro-type as the cards. */
.rail__kicker {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--faint);
  margin-bottom: 0.45rem;
}
.rail__step--gate .rail__kicker { color: var(--wp--preset--color--accent); }
.rail__label {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.2;
}
.rail__desc {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.7rem;
  line-height: 1.45;
  color: var(--wp--preset--color--muted);
  margin-top: 0.45rem;
  max-width: 19ch;
}

/* Reveal: stations rise in left-to-right; gated + replayed by the script.
   Default (no .is-armed) = fully shown, so the editor / no-JS render are right. */
.flow.is-armed .rail__step { opacity: 0; transform: translateY(10px); }
.flow.is-running .rail__step {
  opacity: 1;
  transform: none;
  transition: opacity 0.5s ease, transform 0.5s ease;
  transition-delay: calc(var(--i, 0) * 0.13s + 0.1s);
}

/* Vertical timeline on narrow screens: the rail runs down the left edge and the
   stations stack with their text to the right. */
@media (max-width: 760px) {
  .rail { --rail-x: 42px; }
  .rail__track {
    top: var(--rail-y);
    bottom: var(--rail-y);
    left: var(--rail-x);
    right: auto;
    width: 0;
    height: auto;
  }
  .rail__base {
    inset: 0;
    left: -0.5px;
    width: 1px;
    height: auto;
  }
  .rail__flow {
    inset: 0;
    left: -1px;
    width: 2px;
    height: auto;
    background: repeating-linear-gradient(180deg,
      var(--wp--preset--color--accent) 0 6px, transparent 6px 15px);
    animation: railMarchV 1.4s linear infinite;
  }
  .rail__steps {
    flex-direction: column;
    align-items: stretch;
    gap: 2rem;
  }
  .rail__step {
    flex-direction: row;
    align-items: center;
    text-align: left;
    gap: 1.2rem;
  }
  .rail__text { margin-top: 0; align-items: flex-start; }
  .rail__desc { max-width: none; }
  /* Reveal stays VERTICAL here (inherits the base translateY): a horizontal
     translateX would shift each station off the centred vertical rail. It would
     also never clear — this media block sits after the base `.is-running
     {transform:none}` rule, so at equal specificity a re-declared `.is-armed`
     transform would win even once running, leaving every node offset for good. */
}
@keyframes railMarchV { to { background-position-y: 15px; } }

/* Reduced motion: freeze the marching flow and gate pulse; reveal the stations.
   The static trace stays fully legible. */
@media (prefers-reduced-motion: reduce) {
  .rail__flow, .rail__step--gate-auto .rail__node,
  .flow.is-running .rail__step { animation: none; }
  .rail__step, .flow.is-armed .rail__step { opacity: 1; transform: none; }
}

/* =============================================== SEQUENCED MOTION
   The acceptance record reveals row-by-row and counts its results up as each row
   lands — a test run completing. (The pipeline's own motion lives in the PIPELINE
   RAIL block above.)

   Every start-state below is gated behind an `.is-armed` class that ONLY the
   front-end reveal script (functions.php) adds, so the Site Editor canvas and
   any no-JS render show the final, static state untouched. The script also skips
   arming entirely under prefers-reduced-motion, and the count-up leaves the real
   number in the markup as its fallback. `.is-running` is added on intersection. */

/* --- Acceptance record: stagger rows in, pop each pass tick as its row lands.
   Tabular figures keep the right-aligned result column from jittering while the
   numbers count. --row drives the per-row delay so it survives row edits. */
.eval-matrix__table .num { font-variant-numeric: tabular-nums; }
.eval-matrix__table tbody tr:nth-child(1) { --row: 0; }
.eval-matrix__table tbody tr:nth-child(2) { --row: 1; }
.eval-matrix__table tbody tr:nth-child(3) { --row: 2; }
.eval-matrix__table tbody tr:nth-child(4) { --row: 3; }
.eval-matrix__table tbody tr:nth-child(5) { --row: 4; }
.eval-matrix.is-armed .eval-matrix__table tbody tr { opacity: 0; transform: translateY(6px); }
.eval-matrix.is-armed .eval-matrix__table tbody .tick { opacity: 0; transform: scale(0.6); display: inline-block; }
.eval-matrix.is-running .eval-matrix__table tbody tr {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s ease, transform 0.6s ease;
  transition-delay: calc(var(--row, 0) * 0.2s);
}
.eval-matrix.is-running .eval-matrix__table tbody .tick {
  opacity: 1;
  transform: none;
  transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.2, 1.4, 0.4, 1);
  transition-delay: calc(var(--row, 0) * 0.2s + 0.65s);
}

/* Insurance: if the acceptance record is ever armed under reduced motion, force
   the final state on its rows and ticks. The reveal script already skips arming
   in that case; this is belt-and-suspenders. */
@media (prefers-reduced-motion: reduce) {
  .eval-matrix .eval-matrix__table tbody tr,
  .eval-matrix .eval-matrix__table tbody .tick { opacity: 1 !important; transform: none !important; }
}
