/*!
 * TKD Open Build — public stylesheet (§B7.3). Hand-written; no Tailwind
 * build, no CDN CSS.
 *
 * SOURCES (copied 2026-09-29):
 *   - D:\TKD\TurnKey Directories Child Theme\tkd-child\page-2-0-demo.php
 *       <style> 70–1857  precompiled Tailwind v4.3: only the utilities the
 *                        copied chrome and the plugin templates use are
 *                        re-declared below, flattened (no CSS nesting,
 *                        no color-mix, min-width media queries).
 *       <style> 1883–3951 shared custom CSS: tokens, fallback fonts, chips,
 *                        buttons, header CTA, page stage, panels, folder
 *                        tabs, footer, nav brand, touch targets.
 *       <style> 4193–4413 page-level rules: panel content width, panel tint,
 *                        hero-on-blue inversions, footer on phones.
 *   - page-template-skeleton.php 1871–1883 and TEMPLATE-GUIDE.md 30–42:
 *                        the design tokens.
 *   - page-contact.php 4349–4379: the .cf-* form styles.
 *
 * DELIBERATE DIFFERENCES FROM THE SOURCE (each for §B7.5 WCAG 2.2 AA or
 * "everything works without JS"):
 *   - .reveal only hides content when <html> has .tkd-ob-js (set by a
 *     one-line script in parts/head.php and removed again after 4 s if
 *     open-build.js never ran). The source hid it unconditionally.
 *   - --ink-mute is never used for text under 18px: .cf-hint and
 *     .cf-label span use --ink-dim; the folder-tab label uses --ink-dim
 *     instead of rgba(17,24,39,.55) (3.3:1).
 *   - Text on the blue stage is brighter: hero sub/note #fff (source .86/.62),
 *     footer text and links #fff (source .85), so small text keeps 4.5:1.
 *     WHY #fff and not .92 (round 4, 2026-10-13): the staging axe-core run
 *     measured .92 white (#eef3ff) on the stage's #2865fa at 4.36:1 in the
 *     footer, under 4.5:1; pure white is 4.85:1.
 *   - Visible focus rings everywhere: 2px outline + the focus-ring token.
 *   - `nav a[href="https://turnkeydirectories.com/"]` became `nav .nav-home`
 *     (the link now comes from home_url()).
 *   - A panel's direct child that has its own max-w-* utility keeps it (the
 *     source's --content-w rule overrode it to the full panel width).
 *   - prefers-reduced-motion switches off every animation and transition.
 *   - (phase 10, second staging test) The nav pill is near-opaque white
 *     (.92 + a 10px blur; solid white where backdrop-filter is missing)
 *     instead of the source's 85 % white, so panel text no longer shows
 *     through it; anchors and focused fields stop below it through
 *     scroll-margin-top instead of html { scroll-padding-top }.
 *
 * Class vocabulary for plugin templates (anything else has no rule):
 *   layout   body > section panels (+ .folder-tab[data-tab], .section-cut,
 *            .panel-tint, .tkd-ob-w-48/56/64/72 content widths), #hero on
 *            the blue stage, the utilities in section 4
 *   parts    .btn .btn-blue .btn-primary .btn-ghost, .chip (+ -line -pale
 *            -blue -ink -amber -success -danger), .r-card .card-white
 *            .card-accent .shadow-pop .lift, .section-id, .reveal
 *            (+ -delay-1…6), details.faq / .faq-plus
 *   forms    .cf-row .cf-label .cf-input .cf-select .cf-area .cf-hint
 *            .cf-error .cf-counter .cf-fieldset .cf-legend .cf-choices
 *            .cf-choice .cf-check .cf-check-label .cf-actions,
 *            .tkd-ob-errors or .tkd-ob-alert.tkd-ob-alert--error (summary),
 *            .tkd-ob-notice(--success|--warning|--error) or
 *            .tkd-ob-alert(--info|--success|--warning), .tkd-ob-hp
 *   content  .tkd-ob-stats/.tkd-ob-stat, .tkd-ob-video/.tkd-ob-facade,
 *            .tkd-ob-shot + <dialog class="tkd-ob-lightbox"> (built by JS),
 *            .tkd-ob-table(-wrap), ol.tkd-ob-steps, .tkd-ob-legal-* (terms
 *            panels), .tkd-ob-prose, .tkd-ob-link, .tkd-ob-disclosure,
 *            .sr-only
 *   phase 4  .tkd-ob-badge(--active|--completed|--ended|--done|--waived|
 *            --pending|--founding), case study (.tkd-ob-case-badges,
 *            .tkd-ob-meter, .tkd-ob-milestone*, .tkd-ob-update*,
 *            .tkd-ob-quote, .tkd-ob-shots, .tkd-ob-notes, .tkd-ob-counts,
 *            .tkd-ob-video-wrap/-alt, .tkd-ob-testimonial, .tkd-ob-ugc),
 *            landing index (.tkd-ob-index-*), upload preview
 *            (.tkd-ob-preview, built by JS) — section 11b
 *   phase 7  Bunny upload widget (.tkd-ob-bunny, -ui, -progress, -track,
 *            -bar, -pct, -status, -actions) — section 11b
 *   phase 9  .tkd-ob-badge--level / --earned, .tkd-ob-points-* (portal
 *            Points panel, case-study total), .tkd-ob-review-card,
 *            .tkd-ob-spotlight(-title|-name|-link), .tkd-ob-leaderboard
 *            (-table|-caption|-title|-rank|-num|-note) — section 11c
 *            poppable backdrop icons: .geo.is-popping, .geo.is-spawning,
 *            .geo-pop-particle (set by initPop() in open-build.js) — section
 *            12, before the reduced-motion block
 *   phase 10 (the contract's shared class table) .tkd-ob-earn-list /
 *            -item / -pts / -note / -empty, .tkd-ob-founder-note,
 *            fieldset.tkd-ob-form-section (+ -title),
 *            .tkd-ob-form-progress (-text, -bar > span; hidden without
 *            JS), details.tkd-ob-legal (#terms / #privacy),
 *            .tkd-ob-discord-btn, .tkd-ob-gs-steps > .tkd-ob-gs-step
 *            (.is-done | .is-current | .is-todo), .tkd-ob-gs-done —
 *            section 11d (the form parts in section 10, the legal
 *            <details> next to the terms panels)
 *   round 3  p.tkd-ob-claim-error (the milestone claim form's "a note or
 *            a link" message, shown by open-build.js) — section 10,
 *            after the error summary
 */

/* ======================================================================
   1. Design tokens (skeleton 1871–1883 / TEMPLATE-GUIDE 30–42, §B7.3)
   ====================================================================== */
:root {
  --ink:         #111827;
  --ink-dim:     #4B5563;
  --ink-mute:    #9CA3AF; /* decoration and >= 18px text only (§B7.5) */
  --border:      #E5E7EB;
  --border-soft: #F1F3F7;
  --surface:     #FFFFFF;
  --surface-2:   #F8FAFC;
  --surface-3:   #F1F4F9;
  --blue:        #1A5BFA;
  --blue-deep:   #154EE0;
  --blue-soft:   #DBEAFE;
  --blue-pale:   #EFF6FF;

  --ob-cta-gradient:  linear-gradient(135deg, #1A5BFA 0%, #154EE0 55%, #0F2A8B 100%);
  --ob-panel-radius:  28px 80px 28px 80px;
  --ob-panel-shadow:  0 30px 80px -30px rgba(8, 30, 110, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.6);
  --ob-focus-ring:    0 0 0 3px rgba(26, 91, 250, 0.12);
  --ob-ease:          cubic-bezier(0.22, 1, 0.36, 1);

  /* Status colours (§B7.3): success, amber; danger for errors. */
  --ob-success:      #15803D;
  --ob-success-bg:   #F0FDF4;
  --ob-success-line: #BBF7D0;
  --ob-amber:        #92400E;
  --ob-amber-bg:     #FEF3C7;
  --ob-amber-line:   #FDE68A;
  --ob-danger:       #B91C1C;
  --ob-danger-bg:    #FEF2F2;
  --ob-danger-line:  #FECACA;
}

/* Tailwind registers --tw-leading as non-inheriting; without this a
   leading-* on a parent would leak into a child's text-* line height. */
@property --tw-leading { syntax: "*"; inherits: false; }

/* ======================================================================
   2. Metric-matched fallback fonts (page-2-0-demo.php 1916–1933)
   Local system fonts sized like Inter / JetBrains Mono so the web-font
   swap causes no reflow. The lists name fonts that exist on Windows/macOS
   (Arial, Helvetica), Android (Roboto, Noto) and Linux (Liberation).
   ====================================================================== */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial'), local('Helvetica Neue'), local('Helvetica'),
       local('Roboto'), local('Liberation Sans'), local('Noto Sans');
  ascent-override: 90.20%;
  descent-override: 22.48%;
  line-gap-override: 0.00%;
  size-adjust: 107.40%;
}
@font-face {
  font-family: 'JetBrains Mono Fallback';
  src: local('Courier New'), local('Roboto Mono'), local('Noto Sans Mono'),
       local('Droid Sans Mono'), local('Liberation Mono'), local('DejaVu Sans Mono');
  ascent-override: 102.00%;
  descent-override: 30.00%;
  line-gap-override: 0.00%;
  size-adjust: 100.00%;
}

/* ======================================================================
   3. Preflight (the parts of Tailwind's base layer these pages rely on)
   ====================================================================== */
*, ::after, ::before, ::backdrop { box-sizing: border-box; margin: 0; padding: 0; border: 0 solid; }
html { line-height: 1.5; -webkit-text-size-adjust: 100%; tab-size: 4; -webkit-tap-highlight-color: transparent; scroll-behavior: smooth; }
/* Anchors (#apply, #terms) and focused fields/error summaries must not
   land under the sticky nav pill (~84px tall with its top offset).
   Phase 10 (second staging test, fixlist item 5): scroll-margin-top on
   the targets replaces the earlier `html { scroll-padding-top: 96px }`.
   A browser adds the two together when it scrolls an element into view,
   so keeping both would stop every jump about 196px down. The nav's own
   descendants are reset to 0: they never scroll away (sticky), and a
   margin above them could make focusing one scroll the page up. While
   the consent bar shows, open-build.js sets scroll-padding-bottom inline
   on <html> (the bar's height), so a focused field stops above it.
   Review r1 #3 (WCAG 2.2 2.4.11 Focus Not Obscured, §B7.5): the margin
   also covers EVERY focusable element (links, buttons, [tabindex],
   iframes, video), not only the form fields and anchors. scroll-margin
   only applies to the element being scrolled to, and a browser does not
   scroll a focused link that is already inside the viewport, so a link
   or button without it (the error-summary links, the consent links, the
   hero buttons) could take focus right under the opaque pill and stay
   hidden there. The list is wrapped in :where() (specificity 0), so the
   nav reset below wins for every nav descendant, links and inputs
   included. html { scroll-padding-top } is NOT restored: the nav links
   sit inside its band, so focusing one would scroll the page, and the
   padding cannot be reset per element. The ids of #apply, #expect,
   #terms, #privacy, #costs, #earn and #tkd-ob-content are covered by
   [id]. */
:where([id], body > section, body > details,
  a[href], area[href], button, [tabindex]:not([tabindex="-1"]), iframe, video,
  input:not([type="hidden"]), select, textarea, summary,
  .cf-row, .cf-fieldset, .tkd-ob-form-section,
  .tkd-ob-alert, .tkd-ob-errors, .tkd-ob-notice, .tkd-ob-claim-error,
  [data-tkd-ob-focus], [data-tkd-ob-error-summary]) { scroll-margin-top: 100px; }
body > nav *, body > nav [id] { scroll-margin-top: 0; }
hr { height: 0; color: inherit; border-top-width: 1px; }
h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }
a { color: inherit; text-decoration: inherit; }
b, strong { font-weight: bolder; }
code, kbd, samp, pre { font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace; font-size: 1em; }
small { font-size: 80%; }
table { text-indent: 0; border-color: inherit; border-collapse: collapse; }
summary { display: list-item; }
ol, ul, menu { list-style: none; }
img, svg, video, canvas, audio, iframe, embed, object { display: block; vertical-align: middle; }
img, video { max-width: 100%; height: auto; }
button, input, select, optgroup, textarea { font: inherit; letter-spacing: inherit; color: inherit; border-radius: 0; background-color: transparent; opacity: 1; }
button, input:where([type='button'], [type='reset'], [type='submit']) { appearance: button; }
button { cursor: pointer; }
::placeholder { opacity: 1; color: #6B7280; }
textarea { resize: vertical; }
[hidden]:where(:not([hidden='until-found'])) { display: none !important; }

/* Base (page-2-0-demo.php 1934–1948) */
body {
  font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
  background: #1A5BFA;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "ss01" on, "cv11" on;
  min-height: 100vh;
}
/* Phase 10 (no sideways scroll at 360 / 390 / 768 px): a safety net
   behind the component rules (min-width: 0, overflow-wrap, the folder-tab
   cap). `clip` (not `hidden`) creates no scroll container, so the sticky
   nav keeps working; a browser without `clip` ignores the line. */
body { overflow-x: clip; }
.font-display { font-family: 'Inter', 'Inter Fallback', sans-serif; letter-spacing: -0.02em; }
.font-mono { font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #E5E7EB; border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: #CBD5E1; }
::selection { background: var(--blue); color: #fff; }

/* Inline lucide icons (TKD_OB_Render::icon()) replace <iconify-icon>. */
.tkd-ob-icon { display: inline-block; flex-shrink: 0; width: 1em; height: 1em; vertical-align: -0.125em; }

/* ======================================================================
   4. Utilities — flattened equivalents of the Tailwind v4 rules the
   chrome and templates use (spacing = n x 0.25rem; sm 40rem, md 48rem,
   lg 64rem). A class not listed here has NO rule: add it before use.
   ====================================================================== */
.block{display:block}
.inline-block{display:inline-block}
.inline-flex{display:inline-flex}
.flex{display:flex}
.grid{display:grid}
.hidden{display:none}
.relative{position:relative}
.absolute{position:absolute}
.fixed{position:fixed}
.sticky{position:sticky}
.inset-0{inset:0}
.top-0{top:0}
.top-4{top:1rem}
.left-4{left:1rem}
.right-4{right:1rem}
.z-50{z-index:50}
.z-\[60\]{z-index:60}
.z-\[1\]{z-index:1}
.z-\[2\]{z-index:2}
.overflow-hidden{overflow:hidden}
.w-full{width:100%}
.mx-auto{margin-inline:auto}
.flex-col{flex-direction:column}
.flex-wrap{flex-wrap:wrap}
.flex-1{flex:1}
.flex-shrink-0{flex-shrink:0}
.min-w-0{min-width:0}
.items-center{align-items:center}
.items-start{align-items:flex-start}
.justify-between{justify-content:space-between}
.justify-center{justify-content:center}
.text-center{text-align:center}
.text-left{text-align:left}
.list-none{list-style-type:none}
.cursor-pointer{cursor:pointer}
.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}
.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.gap-1{gap:0.25rem}
.gap-2{gap:0.5rem}
.gap-2\.5{gap:0.625rem}
.gap-3{gap:0.75rem}
.gap-4{gap:1rem}
.gap-5{gap:1.25rem}
.gap-6{gap:1.5rem}
.gap-8{gap:2rem}
.w-1\.5{width:0.375rem}
.h-1\.5{height:0.375rem}
.w-8{width:2rem}
.h-8{height:2rem}
.w-9{width:2.25rem}
.h-9{height:2.25rem}
.w-10{width:2.5rem}
.h-10{height:2.5rem}
.w-11{width:2.75rem}
.h-11{height:2.75rem}
.max-w-sm{max-width:24rem}
.max-w-xl{max-width:36rem}
.max-w-2xl{max-width:42rem}
.max-w-3xl{max-width:48rem}
.max-w-4xl{max-width:56rem}
.max-w-5xl{max-width:64rem}
.max-w-6xl{max-width:72rem}
.max-w-7xl{max-width:80rem}
.p-5{padding:1.25rem}
.p-6{padding:1.5rem}
.p-8{padding:2rem}
.p-10{padding:2.5rem}
.px-2{padding-inline:0.5rem}
.px-3{padding-inline:0.75rem}
.px-4{padding-inline:1rem}
.px-5{padding-inline:1.25rem}
.px-6{padding-inline:1.5rem}
.py-1\.5{padding-block:0.375rem}
.py-2{padding-block:0.5rem}
.py-2\.5{padding-block:0.625rem}
.py-3{padding-block:0.75rem}
.py-4{padding-block:1rem}
.py-14{padding-block:3.5rem}
.py-16{padding-block:4rem}
.py-20{padding-block:5rem}
.pt-2{padding-top:0.5rem}
.pt-4{padding-top:1rem}
.pt-6{padding-top:1.5rem}
.pt-12{padding-top:3rem}
.pt-16{padding-top:4rem}
.pt-20{padding-top:5rem}
.pb-1{padding-bottom:0.25rem}
.pb-5{padding-bottom:1.25rem}
.pb-12{padding-bottom:3rem}
.pb-16{padding-bottom:4rem}
.pb-20{padding-bottom:5rem}
.mx-1{margin-inline:0.25rem}
.my-1{margin-block:0.25rem}
.mt-1{margin-top:0.25rem}
.mt-2{margin-top:0.5rem}
.mt-3{margin-top:0.75rem}
.mt-4{margin-top:1rem}
.mt-5{margin-top:1.25rem}
.mt-6{margin-top:1.5rem}
.mt-8{margin-top:2rem}
.mt-12{margin-top:3rem}
.mb-2{margin-bottom:0.5rem}
.mb-3{margin-bottom:0.75rem}
.mb-4{margin-bottom:1rem}
.mb-5{margin-bottom:1.25rem}
.mb-6{margin-bottom:1.5rem}
.mb-7{margin-bottom:1.75rem}
.mb-8{margin-bottom:2rem}
.mb-10{margin-bottom:2.5rem}
.mb-12{margin-bottom:3rem}
.font-medium{font-weight:500}
.font-semibold{font-weight:600}
.font-bold{font-weight:700}
.uppercase{text-transform:uppercase}
.leading-relaxed{--tw-leading:1.625;line-height:1.625}
.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}
.tracking-tight{letter-spacing:-0.025em}
.tracking-\[0\.14em\]{letter-spacing:0.14em}
.tracking-\[0\.15em\]{letter-spacing:0.15em}
.tracking-\[0\.18em\]{letter-spacing:0.18em}
.tracking-\[0\.22em\]{letter-spacing:0.22em}
.text-white{color:#fff}
.text-white\/80{color:rgba(255,255,255,0.8)}
.text-\[var\(--ink\)\]{color:var(--ink)}
.text-\[var\(--ink-dim\)\]{color:var(--ink-dim)}
.text-\[var\(--blue\)\]{color:var(--blue)}
.text-\[10px\]{font-size:10px}
.text-\[11px\]{font-size:11px}
.text-\[12px\]{font-size:12px}
.text-\[13px\]{font-size:13px}
.text-\[14px\]{font-size:14px}
.text-\[15px\]{font-size:15px}
.text-\[17px\]{font-size:17px}
.text-\[18px\]{font-size:18px}
.text-\[20px\]{font-size:20px}
.text-lg{font-size:1.125rem;line-height:var(--tw-leading,calc(1.75 / 1.125))}
.text-xl{font-size:1.25rem;line-height:var(--tw-leading,calc(1.75 / 1.25))}
.text-2xl{font-size:1.5rem;line-height:var(--tw-leading,calc(2 / 1.5))}
.text-3xl{font-size:1.875rem;line-height:var(--tw-leading,calc(2.25 / 1.875))}
.text-4xl{font-size:2.25rem;line-height:var(--tw-leading,calc(2.5 / 2.25))}
.bg-white{background-color:#fff}
.bg-white\/85{background-color:rgba(255,255,255,0.85)}
.bg-\[var\(--surface-2\)\]{background-color:var(--surface-2)}
.bg-\[var\(--surface-3\)\]{background-color:var(--surface-3)}
.bg-\[var\(--blue\)\]{background-color:var(--blue)}
.bg-\[var\(--blue-pale\)\]{background-color:var(--blue-pale)}
.border{border-style:solid;border-width:1px}
.border-t{border-top-style:solid;border-top-width:1px}
.border-b{border-bottom-style:solid;border-bottom-width:1px}
.border-\[var\(--border\)\]{border-color:var(--border)}
.rounded-full{border-radius:9999px}
.rounded-lg{border-radius:8px}
.rounded-xl{border-radius:12px}
.shadow-2xl{box-shadow:0 25px 50px -12px rgba(0,0,0,0.25)}
.shadow-\[0_4px_20px_-8px_rgba\(17\,24\,39\,0\.08\)\]{box-shadow:0 4px 20px -8px rgba(17,24,39,0.08)}
.backdrop-blur-xl{-webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px)}
.transition{transition-property:color,background-color,border-color,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(0.4,0,0.2,1);transition-duration:150ms}
.space-y-2 > :not(:last-child){margin-block-start:0;margin-block-end:0.5rem}
.space-y-3 > :not(:last-child){margin-block-start:0;margin-block-end:0.75rem}
.space-y-4 > :not(:last-child){margin-block-start:0;margin-block-end:1rem}
@media (hover:hover){
  .hover\:bg-\[var\(--surface-2\)\]:hover{background-color:var(--surface-2)}
  .hover\:bg-\[var\(--surface-3\)\]:hover{background-color:var(--surface-3)}
  .hover\:text-\[var\(--ink\)\]:hover{color:var(--ink)}
}
@media (min-width:40rem){
  .sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
  .sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
  .sm\:gap-8{gap:2rem}
  .sm\:px-6{padding-inline:1.5rem}
  .sm\:px-8{padding-inline:2rem}
  .sm\:p-8{padding:2rem}
  .sm\:p-10{padding:2.5rem}
  .sm\:p-14{padding:3.5rem}
  .sm\:text-4xl{font-size:2.25rem;line-height:var(--tw-leading,calc(2.5 / 2.25))}
  .sm\:text-5xl{font-size:3rem;line-height:var(--tw-leading,1)}
  .sm\:flex-row{flex-direction:row}
  .sm\:items-center{align-items:center}
}
@media (min-width:48rem){
  .md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
  .md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
  .md\:flex{display:flex}
  .md\:hidden{display:none}
}
@media (min-width:64rem){
  .lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
}

/* ======================================================================
   5. Shared components (page-2-0-demo.php 1949–2200)
   ====================================================================== */

/* Radii */
.r-card    { border-radius: 8px; }
.r-ctl     { border-radius: 8px; }
.r-pill    { border-radius: 9999px; }
.r-card-lg { border-radius: 12px; }

/* Flow lines inside the stage */
.ambient-flow { position: absolute; inset: 0; opacity: 0.55; }

/* Entrance animation (nav) */
@keyframes animationIn {
  0%   { opacity: 0; transform: translateY(24px); }
  100% { opacity: 1; transform: translateY(0); }
}
.anim-in { animation: animationIn 0.6s var(--ob-ease) both; }

/* Scroll reveal — hidden only while JS runs (see header note). The
   compositor hint is released once revealed (theme note: left on, every
   .reveal held its own GPU layer and Android evicted them). */
.tkd-ob-js .reveal {
  opacity: 0;
  transform: translateY(36px);
  transition: opacity 0.9s var(--ob-ease), transform 0.9s var(--ob-ease);
  will-change: opacity, transform;
}
.tkd-ob-js .reveal.is-visible { opacity: 1; transform: none; will-change: auto; }
.reveal-delay-1 { transition-delay: 0.08s; }
.reveal-delay-2 { transition-delay: 0.16s; }
.reveal-delay-3 { transition-delay: 0.24s; }
.reveal-delay-4 { transition-delay: 0.32s; }
.reveal-delay-5 { transition-delay: 0.40s; }
.reveal-delay-6 { transition-delay: 0.48s; }

/* Hover lift on cards */
.lift { transition: transform 0.35s var(--ob-ease), box-shadow 0.35s var(--ob-ease), border-color 0.35s; }
.lift:hover {
  transform: translateY(-4px);
  box-shadow: 0 28px 60px -20px rgba(17, 24, 39, 0.18), 0 8px 16px -8px rgba(17, 24, 39, 0.08);
}

/* Pulse dot */
@keyframes pulseDot { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.pulse-dot { animation: pulseDot 1.6s ease-in-out infinite; }

/* Chips */
.chip {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: 9999px;
}
.chip-line    { background: #fff;                  color: var(--ink);        border: 1px solid var(--border); }
.chip-pale    { background: var(--blue-pale);      color: var(--blue-deep);  border: 1px solid #BFDBFE; }
.chip-blue    { background: var(--blue);           color: #fff;              border: 1px solid var(--blue); }
.chip-ink     { background: var(--ink);            color: #fff;              border: 1px solid var(--ink); }
.chip-amber   { background: var(--ob-amber-bg);    color: var(--ob-amber);   border: 1px solid var(--ob-amber-line); }
/* Plugin additions (status badges always carry text, §B7.5). */
.chip-success { background: var(--ob-success-bg);  color: var(--ob-success); border: 1px solid var(--ob-success-line); }
.chip-danger  { background: var(--ob-danger-bg);   color: var(--ob-danger);  border: 1px solid var(--ob-danger-line); }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 13px 22px; border-radius: 8px;
  transition: transform .15s ease, background .2s ease, color .2s ease, border-color .2s ease, box-shadow .2s ease;
  line-height: 1;
  cursor: pointer;
  text-align: center;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--ink); color: #fff; box-shadow: 0 8px 20px -8px rgba(17, 24, 39, 0.4); }
.btn-primary:hover { background: #000; box-shadow: 0 12px 28px -10px rgba(17, 24, 39, 0.5); }
.btn-blue { background: var(--blue); color: #fff; box-shadow: 0 8px 20px -8px rgba(26, 91, 250, 0.5); }
.btn-blue:hover { background: var(--blue-deep); box-shadow: 0 14px 30px -10px rgba(26, 91, 250, 0.6); }
.btn-ghost { background: #fff; color: var(--ink); border: 1px solid var(--border); }
.btn-ghost:hover { border-color: var(--ink); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; box-shadow: none; }

/* Header CTA — brand-blue pill */
.cta-nav {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 20px;
  border-radius: 999px;
  background: var(--blue);
  color: #fff;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  box-shadow: 0 8px 20px -8px rgba(26, 91, 250, 0.5);
  transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
}
.cta-nav:hover { background: var(--blue-deep); color: #fff; box-shadow: 0 12px 26px -10px rgba(26, 91, 250, 0.6); }
.cta-nav:active { transform: translateY(1px); }
.cta-nav .cta-arrow { font-size: 14px; flex-shrink: 0; transition: transform 0.25s var(--ob-ease); }
.cta-nav:hover .cta-arrow { transform: translateX(2px); }

/* Section id label */
.section-id {
  display: flex; align-items: center; gap: 1.25rem;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 600; letter-spacing: .3em; text-transform: uppercase;
  color: var(--ink-dim);
}
.section-id .num { color: var(--ink); padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; background: #fff; }
.section-id .rule { height: 1px; width: 72px; background: linear-gradient(to right, var(--border), transparent); }

/* Cards */
.card-white { background: #fff; border: 1px solid var(--border); }
.shadow-pop { box-shadow: 0 20px 50px -20px rgba(17, 24, 39, 0.20), 0 6px 16px -8px rgba(17, 24, 39, 0.08); }
.card-accent { background: linear-gradient(160deg, var(--blue-pale) 0%, #FFFFFF 100%) !important; border: 1px solid var(--blue-soft) !important; }
.card-accent-strong { background: linear-gradient(160deg, #DCE9FE 0%, #F4F8FF 100%) !important; border: 2px solid var(--blue) !important; }

/* FAQ — native <details>, no JS */
details > summary { list-style: none; cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }
.faq-plus .tkd-ob-icon { transition: transform 0.2s ease; }
details[open] .faq-plus .tkd-ob-icon { transform: rotate(45deg); }

/* ======================================================================
   6. Page stage — fixed blue backdrop (page-2-0-demo.php 2206–2269,
   2575–2608). Fixed, pointer-events:none, z-index:-2: it sits behind
   every panel and never scrolls.
   ====================================================================== */
.page-stage { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: -2; }
.page-stage .blue-fill {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 90% 70% at 50% 0%, #2A6BFF 0%, transparent 70%),
    radial-gradient(ellipse 80% 60% at 50% 100%, #0E3CC3 0%, transparent 70%),
    linear-gradient(180deg, #1A5BFA 0%, #1747D2 100%);
}
.page-stage .spots {
  position: absolute; inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, 0.22) 1.2px, transparent 1.2px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 50%, black 0%, black 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 90% 80% at 50% 50%, black 0%, black 60%, transparent 100%);
  animation: spotsShimmer 7s ease-in-out infinite;
}
@keyframes spotsShimmer { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
.page-stage .grid-lines {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 100% 90% at 50% 50%, black 30%, transparent 80%);
          mask-image: radial-gradient(ellipse 100% 90% at 50% 50%, black 30%, transparent 80%);
}

/* White directory icons drifting in the side gutters */
.geo { position: absolute; pointer-events: none; will-change: transform; color: #FFFFFF; }
.geo svg { display: block; width: 100%; height: 100%; opacity: 0.55; }
@keyframes gentleWobble    { 0%, 100% { transform: rotate(-5deg); } 50% { transform: rotate(5deg); } }
@keyframes gentleWobbleRev { 0%, 100% { transform: rotate(5deg); }  50% { transform: rotate(-5deg); } }
.wobble      { animation: gentleWobble    7s ease-in-out infinite; }
.wobble-rev  { animation: gentleWobbleRev 9s ease-in-out infinite; }
.wobble-slow { animation: gentleWobble    14s ease-in-out infinite; }
.wobble-fast { animation: gentleWobble    5s ease-in-out infinite; }
@keyframes blobFloatA {
  0%   { transform: translate(0, 0) scale(1); }
  33%  { transform: translate(70px, -55px) scale(1.10); }
  66%  { transform: translate(-55px, 90px) scale(0.92); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes blobFloatB {
  0%   { transform: translate(0, 0) scale(1); }
  25%  { transform: translate(-90px, 70px) scale(1.06); }
  50%  { transform: translate(80px, 55px) scale(0.90); }
  75%  { transform: translate(60px, -95px) scale(1.12); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes blobFloatC {
  0%   { transform: translate(0, 0) rotate(0deg) scale(1); }
  50%  { transform: translate(-110px, -30px) rotate(180deg) scale(1.10); }
  100% { transform: translate(0, 0) rotate(360deg) scale(1); }
}
@keyframes blobFloatD {
  0%   { transform: translate(0, 0) scale(0.95); }
  40%  { transform: translate(100px, 40px) scale(1.08); }
  80%  { transform: translate(-70px, -60px) scale(0.92); }
  100% { transform: translate(0, 0) scale(0.95); }
}
.blob-a { animation: blobFloatA 18s ease-in-out infinite; }
.blob-b { animation: blobFloatB 24s ease-in-out infinite; }
.blob-c { animation: blobFloatC 32s linear infinite; }
.blob-d { animation: blobFloatD 21s ease-in-out infinite; }

/* Slow rotating rings — hidden until webfonts settle (theme CLS fix:
   a non-painted element cannot register a layout shift). */
@keyframes slowSpin { to { rotate: 360deg; } }
.slow-spin { animation: slowSpin 60s linear infinite; transform-origin: center; visibility: hidden; }
html.fonts-ready .slow-spin { visibility: visible; }

/* Off-screen pausing of looping animations (JS tags .anim-loop). Scoped
   to loops only: pausing one-shot entrance animations froze content. */
.anim-idle .anim-loop,
.anim-idle.anim-loop { animation-play-state: paused !important; }
.anim-idle .anim-loop-px::before,
.anim-idle .anim-loop-px::after { animation-play-state: paused !important; }
.anim-idle, .anim-idle * { will-change: auto !important; }

/* ======================================================================
   7. Panels — sections become white cards on the blue (2272–2372).
   No <main> wrapper: these are `body > section` rules (§B7.3).
   ====================================================================== */
body > section:not(#hero) {
  background: #FFFFFF;
  border-radius: var(--ob-panel-radius);
  margin: 28px auto;
  box-shadow: var(--ob-panel-shadow);
  position: relative;
  z-index: 1;
  max-width: min(calc(100vw - 24px), 80rem);
}
body > section:not(#hero).section-cut { border-radius: var(--ob-panel-radius); }

/* Content width inside a panel: every direct child is centred at
   --content-w (page-2-0-demo.php 4288–4300). Unlike the source, a child
   that carries its own max-w-* utility keeps it: plugin templates size
   their blocks with max-w-*, and the source rule (specificity 1,0,3)
   silently overrode those to full width. */
body > section:not(#hero) > *:not(style):not([class*="max-w-"]) { max-width: var(--content-w, 100%); }
body > section:not(#hero) > *:not(style) { margin-inline: auto; }
.tkd-ob-w-48 { --content-w: 48rem; }
.tkd-ob-w-56 { --content-w: 56rem; }
.tkd-ob-w-64 { --content-w: 64rem; }
.tkd-ob-w-72 { --content-w: 72rem; }

/* Soft blue wash for alternate panels (4312–4316) */
body > section:not(#hero).panel-tint { background: linear-gradient(158deg, #FFFFFF 0%, #F6FAFF 52%, #E8F1FE 100%); }

/* Folder-tab "bookmark" on top of a panel: data-tab="01 · THE DEAL" */
body > section:not(#hero).folder-tab::before { display: none; }
body > section:not(#hero).folder-tab::after {
  content: attr(data-tab);
  position: absolute;
  top: -22px;
  left: 64px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  padding: 0 18px;
  background-color: #FFFFFF;
  border-radius: 16px 16px 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 -4px 12px -4px rgba(8, 30, 110, 0.20);
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim); /* source rgba(17,24,39,.55) is 3.3:1 */
  font-weight: 600;
  white-space: nowrap;
  z-index: 2;
}
body > section:not(#hero).panel-tint.folder-tab::after { background-color: #FFFFFF; }
@media (max-width: 480px) {
  body > section:not(#hero).folder-tab::after { left: 28px; max-width: calc(100% - 56px); overflow: hidden; text-overflow: ellipsis; }
}
/* Phase 10: a long tab label must never stick out of a tablet-width
   panel either (the tab is absolutely placed, so it would widen the page). */
@media (min-width: 481px) and (max-width: 768px) {
  body > section:not(#hero).folder-tab::after { max-width: calc(100% - 92px); overflow: hidden; text-overflow: ellipsis; }
}

/* Asymmetric "cut" corners for cards in the hero (2289–2294) */
#hero .hero-cut-outer { border-radius: 28px 80px 28px 80px; }
#hero .hero-cut-inner { border-radius: 14px 56px 14px 56px; }
@media (max-width: 640px) {
  #hero .hero-cut-outer { border-radius: 20px 44px 20px 44px; }
  #hero .hero-cut-inner { border-radius: 12px 30px 12px 30px; }
}

/* Hero sits directly on the blue stage, so its type inverts (4336–4362) */
#hero .hero-eyebrow {
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.28);
  color: #FFFFFF;
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
}
#hero .hero-eyebrow .tkd-ob-icon { color: #FFFFFF; }
#hero h1 { color: #FFFFFF; text-shadow: 0 2px 24px rgba(8, 30, 110, 0.35); }
#hero h1 .accent { color: #BFDBFE; }
#hero .hero-sub  { color: #FFFFFF; }
#hero .hero-note { color: #FFFFFF; }
#hero .tkd-ob-link, #hero p a { color: #FFFFFF; text-decoration: underline; text-underline-offset: 3px; }
#hero .btn-ghost {
  background: rgba(255, 255, 255, 0.10);
  color: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.35);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
}
#hero .btn-ghost:hover { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.55); }
#hero .btn-blue { background: #FFFFFF; color: var(--blue); box-shadow: 0 10px 30px -10px rgba(8, 30, 110, 0.55); }
#hero .btn-blue:hover { background: #EFF6FF; }
@media (max-width: 400px) { #hero h1 { font-size: 32px; } }

/* ======================================================================
   8. Footer on the blue (2427–2456, 4264–4283)
   ====================================================================== */
body > footer {
  background: rgba(255, 255, 255, 0.06);
  -webkit-backdrop-filter: blur(12px);
          backdrop-filter: blur(12px);
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  color: #ffffff;
  margin: 0;
  border-radius: 0;
  max-width: 100%;
}
/* Pure white footer text (axe, round 4): see the header note. Links are
   already white, so hover underlines them instead of changing colour. */
body > footer a, body > footer .tkd-ob-consent-open { color: #ffffff; }
body > footer a:hover, body > footer .tkd-ob-consent-open:hover { color: #ffffff; text-decoration: underline; text-underline-offset: 3px; }
body > footer .text-\[var\(--ink\)\],
body > footer .text-\[var\(--ink-dim\)\] { color: #ffffff !important; }
body > footer h4, body > footer h5 { color: #fff !important; }
body > footer .footer-cols { display: grid; grid-template-columns: 2.2fr 1fr 1fr 1fr 1fr; gap: 40px; }
@media (max-width: 760px) {
  body > footer .footer-cols { grid-template-columns: 1fr 1fr; gap: 30px 24px; align-items: start; }
  body > footer .footer-cols > :first-child { grid-column: 1 / -1; padding-bottom: 26px; border-bottom: 1px solid var(--border); }
}
@media (max-width: 560px) {
  /* centre the © line (its status chip neighbour left in 1.0.1) */
  body > footer .mt-12.pt-6 { flex-direction: column; align-items: center !important; text-align: center; gap: 12px; }
}

/* ======================================================================
   9. Nav brand, header CTA and touch targets (3836–3950)
   ====================================================================== */
.nav-logo-icon   { display: block; height: 30px; width: auto; max-width: none; }
.nav-logo-lockup { display: none;  height: 30px; width: auto; max-width: none; }
@media (min-width: 1024px) {
  .nav-logo-icon   { display: none; }
  .nav-logo-lockup { display: block; }
}
.cta-label-short { display: none; }
@media (max-width: 1023px) {
  .nav-logo-icon   { display: none; }
  .nav-logo-lockup { display: block; width: 140px; height: auto; max-width: 100%; }
  nav .nav-home    { flex-shrink: 1 !important; min-width: 0; } /* markup sets flex-shrink:0 inline */
  .cta-label-full  { display: none; }
  .cta-label-short { display: inline; }
  .cta-nav         { padding: 0 13px; }
}
@media (max-width: 365px) {
  .nav-logo-lockup { width: 124px; }
  nav .nav-home    { padding: 13px 0; margin: -13px 0; }
}
@media (max-width: 330px) {
  .nav-logo-lockup { width: 104px; }
  nav .nav-home    { padding: 15px 0; margin: -15px 0; }
  /* nav prefix (1.0.1, as the theme): outranks the max-width:768px .cta-nav
     rule below, which reset letter-spacing to 0.12em here. */
  nav .cta-nav     { padding: 0 9px; letter-spacing: 0.06em; }
}
@media (max-width: 768px) {
  /* Grow hit areas without moving anything: padding + matching negative margin. */
  nav .nav-home { padding: 12px 0; margin: -12px 0; }
  #nav-close    { width: 44px; height: 44px; margin: -4px; }
  .btn          { min-height: 44px; }
  .cta-nav      { height: 44px; font-size: 10px; letter-spacing: 0.12em; gap: 6px; }
  .cta-nav .cta-arrow { font-size: 13px; }
}
/* Without JS the drawer cannot open: hide its toggle (the footer has every link). */
html:not(.tkd-ob-js) #nav-toggle { display: none !important; }

/* "Start free" (1.0.1) is ~15 px wider than "Try free". Below 390 px the CTA
   drops its arrow (~19 px) so the wordmark keeps a gap to the hamburger
   (tkd-child-2026-10-16, page-features.php 3915–3919). */
@media (max-width: 389px) {
  .cta-nav .cta-arrow { display: none; }
}

/* ===== OPEN BUILD nav item (1.0.1, 2026-10-16) =====
   Copied from tkd-child-2026-10-16 page-features.php 3983–3998, one rule
   per line as there, so the two can be diffed. Only change: the font stack
   gains 'JetBrains Mono Fallback' like every mono rule in this file.
   ≥1280 pill + chip · 1024–1279 pill only, text links 8 px side padding ·
   <1024 no pill (the row is full; the footer links the program) · <768 a
   dot on the hamburger and the card first in the drawer. The pill and card
   markup is parts/nav.php; aria-current="page" on landing.php fills the pill.
   Declares no .cta-nav rule. */
.nav-ob{display:inline-flex;align-items:center;gap:6px;height:40px;padding:0 14px;flex-shrink:0;border-radius:999px;background:var(--blue-pale);border:1px solid #BFDBFE;color:var(--blue-deep);font-family:'JetBrains Mono','JetBrains Mono Fallback',monospace;font-size:12px;font-weight:600;line-height:1;white-space:nowrap;text-decoration:none;transition:background .2s ease,border-color .2s ease}
.nav-ob:hover{background:var(--blue-soft);border-color:#93C5FD;color:var(--blue-deep)}
.nav-ob:focus-visible,.nav-ob-card:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.nav-ob[aria-current="page"]{background:var(--blue-soft);border-color:var(--blue)}
.nav-ob-dot{width:6px;height:6px;border-radius:999px;background:var(--blue);flex-shrink:0}
.nav-ob-chip{margin-left:2px;padding:4px 7px;border-radius:999px;background:var(--blue);color:#fff;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
@media (max-width:1279px){.nav-ob-chip{display:none}.nav-links>a{padding-left:8px;padding-right:8px}.nav-links>a[href^="#"]::after{left:8px;right:8px}}
@media (max-width:1023px){.nav-ob{display:none}}
@media (max-width:767px){#nav-toggle{position:relative}#nav-toggle::after{content:"";position:absolute;top:7px;right:7px;width:7px;height:7px;border-radius:999px;background:var(--blue);box-shadow:0 0 0 2px var(--surface-2);animation:pulseDot 1.6s ease-in-out infinite}}
.nav-ob-card{display:block;margin:6px 4px 8px;padding:14px 16px;border-radius:16px;background:var(--blue-pale);border:1px solid #BFDBFE;text-decoration:none}
.nav-ob-card:hover{background:var(--blue-soft)}
.nav-ob-card-eyebrow{display:flex;align-items:center;gap:8px;font-family:'JetBrains Mono','JetBrains Mono Fallback',monospace;font-size:10px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--blue-deep)}
.nav-ob-card-line{display:block;margin-top:6px;font-size:14px;line-height:1.45;color:var(--ink)}
.nav-ob-card-go{display:inline-block;margin-top:8px;font-family:'JetBrains Mono','JetBrains Mono Fallback',monospace;font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--blue)}
@media (prefers-reduced-motion:reduce){.nav-ob-dot,#nav-toggle::after{animation:none!important}}

/* The sticky nav pill on plugin pages (phase 10, fixlist item 5). The
   markup's bg-white/85 + backdrop-blur-xl let panel text show through it
   while scrolling. Near-opaque white with a light blur where the browser
   has backdrop-filter, solid white where it has not (85 % white with no
   blur was the unreadable case). Everything else keeps the main-site look. */
body > nav > .r-pill { background-color: #FFFFFF; }
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  body > nav > .r-pill {
    background-color: rgba(255, 255, 255, 0.92);
    -webkit-backdrop-filter: blur(10px) saturate(1.4);
            backdrop-filter: blur(10px) saturate(1.4);
  }
}

/* ======================================================================
   10. Forms (page-contact.php 4349–4379) + plugin additions (§B7.5, §B8)
   ====================================================================== */
.cf-row { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.cf-label, .cf-legend {
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.cf-label span, .cf-legend span { color: var(--ink-dim); font-weight: 400; letter-spacing: .1em; }
.cf-input, .cf-select, .cf-area {
  width: 100%; font-family: 'Inter', 'Inter Fallback', sans-serif; font-size: 14.5px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 13px; line-height: 1.5;
  transition: border-color .16s ease, box-shadow .16s ease;
}
.cf-input:focus, .cf-select:focus, .cf-area:focus { outline: none; border-color: var(--blue); box-shadow: var(--ob-focus-ring); }
.cf-area { min-height: 150px; resize: vertical; }
.cf-select {
  appearance: none; -webkit-appearance: none; padding-right: 38px; cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234B5563' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
}
.cf-hint { font-size: 12.5px; color: var(--ink-dim); line-height: 1.5; } /* source: --ink-mute */
.cf-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 22px; }

/* Field errors (§B7.5: aria-invalid + aria-describedby) */
.cf-error { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; line-height: 1.45; color: var(--ob-danger); font-weight: 500; }
.cf-error .tkd-ob-icon { margin-top: 2px; }
.cf-input[aria-invalid="true"], .cf-select[aria-invalid="true"], .cf-area[aria-invalid="true"] { border-color: var(--ob-danger); }
.cf-input[aria-invalid="true"]:focus, .cf-select[aria-invalid="true"]:focus, .cf-area[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(185, 28, 28, 0.12); }

/* Live character counter (open-build.js; §B7.5 aria-live polite) */
.cf-counter { font-size: 12.5px; line-height: 1.5; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.cf-counter.is-short { color: var(--ob-amber); }
.cf-counter.is-over  { color: var(--ob-danger); }

/* Radio / checkbox groups */
.cf-fieldset { border: 0; padding: 0; margin: 0 0 16px; min-width: 0; }
.cf-legend { display: block; padding: 0; margin-bottom: 8px; }
.cf-choices { display: grid; gap: 8px; }
@media (min-width: 640px) { .cf-choices--inline { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } }
.cf-choice {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 13px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  font-size: 14.5px; line-height: 1.45; color: var(--ink); cursor: pointer;
  transition: border-color .16s ease, background .16s ease;
}
.cf-choice:hover { border-color: #CBD5E1; }
.cf-choice:has(input:checked) { border-color: var(--blue); background: var(--blue-pale); }
.cf-fieldset.is-invalid .cf-choice { border-color: var(--ob-danger-line); }
.cf-choice > label { flex: 1; cursor: pointer; }
.cf-fieldset > legend { padding: 0; margin-bottom: 8px; }
/* Consent checkbox row: box beside its label, hint/error underneath.
   Works for <label> wrapping the input and for input + label siblings. */
.cf-check { display: flex; align-items: flex-start; gap: 10px; font-size: 14.5px; line-height: 1.55; color: var(--ink); }
.cf-row.cf-check { display: grid; grid-template-columns: auto 1fr; column-gap: 10px; row-gap: 6px; align-items: start; }
.cf-row.cf-check > :not(input) { grid-column: 2; }
.cf-check-label { cursor: pointer; }
.cf-choice input[type="radio"], .cf-choice input[type="checkbox"], .cf-check input[type="checkbox"] {
  accent-color: var(--blue); width: 18px; height: 18px; margin-top: 2px; flex-shrink: 0;
}

/* Inputs are 16px below 640px so iOS does not zoom on focus (§B7.3).
   Phase 10: every text input, select and textarea, also one without a
   .cf-* class (a smaller font on any of them is what triggers the zoom). */
@media (max-width: 639px) {
  .cf-input, .cf-select, .cf-area, .cf-choice, .cf-check,
  input:not([type="checkbox"]):not([type="radio"]), select, textarea { font-size: 16px; }
}

/* Application form sections + progress cue (§C3 "Sections", phase 10).
   The four fieldsets carry the structure (their legends are the
   headings); the progress box is decoration that open-build.js fills
   (aria-hidden) and is not shown at all without JS. Nested radio-group
   fieldsets (.cf-fieldset) keep their own rules: only the section's
   direct legend is styled here. */
.tkd-ob-form-section { min-width: 0; margin: 0 0 8px; padding: 0; border: 0; }
.tkd-ob-form-section + .tkd-ob-form-section { margin-top: 28px; }
.tkd-ob-form-section > .tkd-ob-form-section-title {
  display: block; width: 100%; padding: 0; margin: 0 0 16px;
  font-family: 'Inter', 'Inter Fallback', sans-serif; font-size: 18px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.35;
  color: var(--ink); overflow-wrap: break-word;
}
/* Review r1 #8: the divider between sections sits on the LEGEND, not on
   the fieldset. The legend is the fieldset's rendered legend: it sits on
   the fieldset's top border, and a browser does not paint that border
   behind it; at width 100% it spans the whole fieldset, so a border-top
   on the fieldset was never drawn, and the fieldset's padding-top landed
   below the heading (40px heading-to-field in sections 2-4, 16px in 1).
   The legend paints its own border and padding: a full-width line with
   24px above every later heading, and 16px to the first field in every
   section. */
.tkd-ob-form-section + .tkd-ob-form-section > .tkd-ob-form-section-title { padding-top: 24px; border-top: 1px solid var(--border); }
.tkd-ob-form-progress {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  margin: 0 0 24px; padding: 10px 14px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--surface-2);
}
html:not(.tkd-ob-js) .tkd-ob-form-progress { display: none !important; }
.tkd-ob-form-progress-text {
  flex: 1 1 auto; min-width: 0;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; line-height: 1.5;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.tkd-ob-form-progress-bar {
  position: relative; display: block; flex: 1 1 140px; min-width: 96px; height: 6px;
  border-radius: 9999px; background: var(--border); overflow: hidden;
}
.tkd-ob-form-progress-bar > span {
  display: block; width: 0; height: 100%; border-radius: inherit;
  background: var(--blue); transition: width .3s var(--ob-ease);
}

/* Honeypot wrapper (§B8.2): off-screen, not display:none (bots skip those). */
.tkd-ob-hp { position: absolute !important; left: -9999px !important; top: auto; width: 1px; height: 1px; overflow: hidden; }

/* Error summary (§B7.5: role="alert", receives focus).
   .tkd-ob-alert--error is the same component under the landing
   template's name; .tkd-ob-alert-title / -list likewise. */
.tkd-ob-errors, .tkd-ob-alert.tkd-ob-alert--error {
  border: 1px solid var(--ob-danger-line); background: var(--ob-danger-bg); color: var(--ink);
  border-radius: 12px; padding: 16px 18px; margin-bottom: 22px;
}
.tkd-ob-errors:focus, .tkd-ob-alert.tkd-ob-alert--error:focus { outline: 2px solid var(--ob-danger); outline-offset: 2px; box-shadow: none; }
.tkd-ob-errors-title, .tkd-ob-alert.tkd-ob-alert--error .tkd-ob-alert-title { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 15px; line-height: 1.4; color: var(--ob-danger); margin-bottom: 6px; }
.tkd-ob-errors ul, .tkd-ob-alert-list { list-style: disc; padding-left: 1.25rem; }
.tkd-ob-errors li, .tkd-ob-alert-list li { font-size: 14px; line-height: 1.5; margin-top: 4px; }
.tkd-ob-errors a, .tkd-ob-alert.tkd-ob-alert--error a { color: var(--ob-danger); text-decoration: underline; text-underline-offset: 2px; }

/* Round 3 of the staging fixes: the milestone claim form's "a note or a
   link" message (p.tkd-ob-claim-error; open-build.js 6d shows it and
   moves focus to it, so it is also a scroll-margin target above). It
   reads like a field error (.cf-error); its focus ring is the error
   summary's. [hidden] keeps it out of view until then. */
.tkd-ob-claim-error { margin: 0 0 12px; font-size: 13px; line-height: 1.45; font-weight: 500; color: var(--ob-danger); }
.tkd-ob-claim-error:focus { outline: 2px solid var(--ob-danger); outline-offset: 2px; box-shadow: none; }

/* Notices (.tkd-ob-alert--info / --success / --warning are aliases) */
.tkd-ob-notice, .tkd-ob-alert {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 14px 16px; border-radius: 12px; margin-bottom: 22px;
  border: 1px solid var(--blue-soft); background: var(--blue-pale); color: var(--ink);
  font-size: 14.5px; line-height: 1.55;
}
.tkd-ob-alert { display: block; }
.tkd-ob-alert:focus, .tkd-ob-notice:focus { outline: 2px solid var(--blue); outline-offset: 2px; box-shadow: none; }
.tkd-ob-alert-title { font-weight: 600; font-size: 15px; line-height: 1.4; margin-bottom: 4px; }
.tkd-ob-notice > .tkd-ob-icon { margin-top: 3px; color: var(--blue); }
.tkd-ob-notice--success, .tkd-ob-alert--success { border-color: var(--ob-success-line); background: var(--ob-success-bg); }
.tkd-ob-notice--success > .tkd-ob-icon { color: var(--ob-success); }
.tkd-ob-notice--warning, .tkd-ob-alert--warning { border-color: var(--ob-amber-line); background: var(--ob-amber-bg); }
.tkd-ob-notice--warning > .tkd-ob-icon { color: var(--ob-amber); }
.tkd-ob-notice--error { border-color: var(--ob-danger-line); background: var(--ob-danger-bg); }
.tkd-ob-notice--error > .tkd-ob-icon { color: var(--ob-danger); }

/* Links inside running text must look like links, not only by colour. */
.tkd-ob-link,
.tkd-ob-prose a, .tkd-ob-legal-body a, .cf-check a, .cf-hint a, .cf-choice a,
.tkd-ob-notice a, .tkd-ob-alert a, .tkd-ob-disclosure a, .tkd-ob-consent a,
.tkd-ob-faq-item a {
  color: var(--blue); text-decoration: underline; text-underline-offset: 3px;
}
.tkd-ob-link:hover, .tkd-ob-prose a:hover { color: var(--blue-deep); }

/* ======================================================================
   11. Content components (phases 1–4)
   ====================================================================== */

/* Stat cards (case-study public stats, portal numbers) */
.tkd-ob-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.tkd-ob-stat { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; }
.tkd-ob-stat-val { font-family: 'Inter', 'Inter Fallback', sans-serif; font-weight: 600; font-size: 28px; letter-spacing: -0.02em; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }
.tkd-ob-stat-key {
  margin-top: 6px;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.tkd-ob-stat-note { margin-top: 4px; font-size: 12.5px; line-height: 1.45; color: var(--ink-dim); }

/* Click-to-load video facade (§B7.5, §B10.3): <div class="tkd-ob-video">
   <button class="tkd-ob-facade" data-tkd-ob-embed="…" aria-label="Play
   Month 3 video">…</button></div>. JS swaps in the iframe. */
.tkd-ob-video { position: relative; aspect-ratio: 16 / 9; border-radius: 16px; overflow: hidden; background: #0B1221; }
@supports not (aspect-ratio: 16 / 9) { .tkd-ob-video { height: 0; padding-top: 56.25%; } }
.tkd-ob-video > iframe, .tkd-ob-video-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tkd-ob-facade { position: absolute; inset: 0; display: block; width: 100%; height: 100%; padding: 0; border: 0; background: #0B1221; color: #fff; cursor: pointer; }
.tkd-ob-facade img { width: 100%; height: 100%; object-fit: cover; opacity: .9; transition: opacity .2s ease; }
.tkd-ob-facade:hover img { opacity: 1; }
.tkd-ob-facade-play {
  position: absolute; left: 50%; top: 50%; width: 68px; height: 68px; margin: -34px 0 0 -34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 9999px; background: var(--blue); color: #fff; font-size: 26px;
  box-shadow: 0 12px 30px -8px rgba(8, 30, 110, 0.6);
  transition: transform .2s var(--ob-ease);
}
.tkd-ob-facade:hover .tkd-ob-facade-play { transform: scale(1.06); }
.tkd-ob-facade:focus-visible { outline: 3px solid #fff; outline-offset: -6px; }
.tkd-ob-facade-label {
  position: absolute; left: 12px; bottom: 12px; padding: 5px 10px; border-radius: 9999px;
  background: rgba(11, 18, 33, 0.78); color: #fff;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace; font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
}

/* Screenshot thumbnails + native <dialog> lightbox (built by open-build.js) */
.tkd-ob-shot { display: block; border-radius: 12px; overflow: hidden; border: 1px solid var(--border); background: var(--surface-2); }
.tkd-ob-shot img { width: 100%; height: auto; }
.tkd-ob-lightbox { border: 0; padding: 0; margin: auto; background: transparent; color: #fff; overflow: visible; max-width: min(96vw, 1400px); max-height: 94vh; }
.tkd-ob-lightbox::backdrop { background: rgba(8, 15, 40, 0.82); }
.tkd-ob-lightbox-inner { position: relative; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.tkd-ob-lightbox-img { max-width: min(96vw, 1400px); max-height: 84vh; width: auto; height: auto; border-radius: 12px; background: #fff; box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.6); }
.tkd-ob-lightbox-cap { max-width: 60ch; font-size: 14px; line-height: 1.5; text-align: center; color: #fff; }
.tkd-ob-lightbox-close {
  position: absolute; top: -14px; right: -14px; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9999px; background: #fff; color: var(--ink); font-size: 20px;
  box-shadow: 0 8px 20px -6px rgba(0, 0, 0, 0.45);
}
@media (max-width: 640px) { .tkd-ob-lightbox-close { top: 8px; right: 8px; } }

/* Simple data table (landing costs, portal periods) */
.tkd-ob-table-wrap { overflow-x: auto; }
.tkd-ob-table { width: 100%; font-size: 14.5px; line-height: 1.5; color: var(--ink-dim); }
.tkd-ob-table th, .tkd-ob-table td { padding: 12px 14px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--border); }
.tkd-ob-table thead th {
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.tkd-ob-table tbody th { color: var(--ink); font-weight: 600; }
.tkd-ob-table tbody tr:last-child th, .tkd-ob-table tbody tr:last-child td { border-bottom: 0; }

/* Numbered steps: the preflight removes list markers, so an <ol> with
   .tkd-ob-steps gets its numbers back as badges (the list stays an <ol>
   for assistive tech). */
.tkd-ob-steps { counter-reset: tkd-ob-step; }
.tkd-ob-steps > li { counter-increment: tkd-ob-step; position: relative; padding-left: 64px !important; }
.tkd-ob-steps > li::before {
  content: counter(tkd-ob-step, decimal-leading-zero);
  position: absolute; left: 18px; top: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 9999px;
  background: var(--blue-pale); border: 1px solid var(--blue-soft); color: var(--blue-deep);
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace; font-size: 11px; font-weight: 700;
}

/* Terms / privacy panels (parts/terms.php: <section class="tkd-ob-legal">
   with a native <details>, so they open without JS) */
body > section.tkd-ob-legal { padding: 4px 16px; }
@media (min-width: 40rem) { body > section.tkd-ob-legal { padding: 8px 32px; } }
.tkd-ob-legal-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; padding: 22px 4px; }
.tkd-ob-legal-summary::after {
  content: ""; margin-left: auto; width: 10px; height: 10px;
  border-right: 2px solid var(--ink-dim); border-bottom: 2px solid var(--ink-dim);
  transform: rotate(45deg); transition: transform .2s ease;
}
details[open] > .tkd-ob-legal-summary::after { transform: rotate(-135deg); }
.tkd-ob-legal-title { font-family: 'Inter', 'Inter Fallback', sans-serif; font-weight: 600; font-size: 20px; letter-spacing: -0.01em; line-height: 1.3; color: var(--ink); }
.tkd-ob-legal-meta {
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.tkd-ob-legal-body { max-width: 48rem; padding: 0 4px 28px; }
.tkd-ob-legal-draft-note {
  padding: 12px 14px; border-radius: 10px; margin-bottom: 1em;
  border: 1px solid var(--ob-amber-line); background: var(--ob-amber-bg); color: var(--ob-amber);
}
.tkd-ob-legal-draft-note a { color: var(--ob-amber); text-decoration: underline; }
.tkd-ob-legal-pending { font-size: 14.5px; color: var(--ink-dim); }

/* Phase 10 (§C1, fixlist item 7): the terms and privacy notice are
   collapsed <details class="tkd-ob-legal" id="terms|privacy">, closed by
   default, their text still in the page. open-build.js opens one for its
   hash and for the consent checkboxes' links. As a direct body child the
   <details> is itself a white panel; inside a panel (any other section)
   it only gets the inner spacing. The summary is the whole clickable row
   (at least 44px tall) with the same chevron as the phase-1 markup. */
body > details.tkd-ob-legal {
  position: relative; z-index: 1; margin: 28px auto;
  max-width: min(calc(100vw - 24px), 80rem);
  background: #FFFFFF; border-radius: var(--ob-panel-radius); box-shadow: var(--ob-panel-shadow);
}
body > details.tkd-ob-legal,
body > section:not(.tkd-ob-legal) > details.tkd-ob-legal { padding: 4px 16px; }
@media (min-width: 40rem) {
  body > details.tkd-ob-legal,
  body > section:not(.tkd-ob-legal) > details.tkd-ob-legal { padding: 8px 32px; }
}
details.tkd-ob-legal > summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px;
  min-height: 44px; padding: 22px 4px; cursor: pointer;
}
details.tkd-ob-legal > summary::after {
  content: ""; margin-left: auto; width: 10px; height: 10px;
  border-right: 2px solid var(--ink-dim); border-bottom: 2px solid var(--ink-dim);
  transform: rotate(45deg); transition: transform .2s ease;
}
details.tkd-ob-legal[open] > summary::after { transform: rotate(-135deg); }
details.tkd-ob-legal > summary > h2,
details.tkd-ob-legal > summary > h3 {
  min-width: 0; font-family: 'Inter', 'Inter Fallback', sans-serif; font-weight: 600; font-size: 20px;
  letter-spacing: -0.01em; line-height: 1.3; color: var(--ink); overflow-wrap: break-word;
}
.tkd-ob-legal-body, .tkd-ob-prose { overflow-wrap: break-word; }

/* Plain-text / terms prose (parts/terms.php body, guide) */
.tkd-ob-prose, .tkd-ob-legal-body { font-size: 15px; line-height: 1.7; color: var(--ink-dim); }
.tkd-ob-prose > * + *, .tkd-ob-legal-body > * + * { margin-top: 0.9em; }
.tkd-ob-prose h2, .tkd-ob-prose h3, .tkd-ob-prose h4,
.tkd-ob-legal-body h2, .tkd-ob-legal-body h3, .tkd-ob-legal-body h4 { color: var(--ink); font-weight: 600; line-height: 1.3; margin-top: 1.6em; }
.tkd-ob-prose h2, .tkd-ob-legal-body h2 { font-size: 22px; }
.tkd-ob-prose h3, .tkd-ob-legal-body h3 { font-size: 18px; }
.tkd-ob-prose h4, .tkd-ob-legal-body h4 { font-size: 16px; }
.tkd-ob-prose ul, .tkd-ob-legal-body ul { list-style: disc; padding-left: 1.4em; }
.tkd-ob-prose ol, .tkd-ob-legal-body ol { list-style: decimal; padding-left: 1.4em; }
.tkd-ob-prose li + li, .tkd-ob-legal-body li + li { margin-top: .35em; }
.tkd-ob-prose strong, .tkd-ob-prose b, .tkd-ob-legal-body strong, .tkd-ob-legal-body b { color: var(--ink); }
.tkd-ob-prose code { background: var(--surface-3); padding: 1px 5px; border-radius: 4px; font-size: .9em; }
.tkd-ob-prose blockquote, .tkd-ob-legal-body blockquote { border-left: 3px solid var(--blue-soft); padding-left: 14px; color: var(--ink); }

/* Disclosure (parts/disclosure.php) */
.tkd-ob-disclosure { font-size: 13px; line-height: 1.6; color: var(--ink-dim); }
.tkd-ob-disclosure p + p { margin-top: 6px; }
.tkd-ob-disclosure--footer {
  max-width: 56rem; margin-bottom: 32px; padding-bottom: 24px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
  color: #ffffff;
}
body > footer .tkd-ob-disclosure--footer a { color: #fff; }

/* Tracking consent bar (§B7.4) — hidden by default, shown by JS only while
   no choice is stored. Accept and Decline are styled alike on purpose. */
.tkd-ob-consent {
  position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 70;
  max-width: 56rem; margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px;
  padding: 16px 18px; background: #fff; color: var(--ink);
  border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 20px 50px -20px rgba(8, 30, 110, 0.55);
}
.tkd-ob-consent-text { flex: 1 1 320px; min-width: 0; font-size: 14px; line-height: 1.55; color: var(--ink-dim); }
.tkd-ob-consent-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.tkd-ob-consent-open { padding: 0; background: none; font: inherit; text-align: left; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
/* Phones (phase 10, fixlist item 6): a compact bar, so it covers less of
   the form. Smaller text (--ink-dim on white is still 7.5:1), and Decline
   and Accept side by side in one row, each at least 44px tall and half
   the width (alike on purpose, as above). The bar clears the home
   indicator on notched phones. While it shows, open-build.js pads the
   page by its height. */
@media (max-width: 480px) {
  .tkd-ob-consent {
    left: 8px; right: 8px; bottom: calc(8px + env(safe-area-inset-bottom, 0px));
    gap: 8px; padding: 10px 12px; border-radius: 12px;
  }
  .tkd-ob-consent-text { flex-basis: 100%; font-size: 12.5px; line-height: 1.45; }
  .tkd-ob-consent-actions { flex: 1 1 100%; flex-wrap: nowrap; gap: 8px; }
  .tkd-ob-consent-actions > .btn { flex: 1 1 0; min-width: 0; min-height: 44px; padding: 0 10px; }
}

/* ======================================================================
   11b. Case study, builders index, upload preview (phase 4: §A10.1,
   §B11.2, §B11.5, §B7.5). Status badges always carry a word: colour is
   never the only signal. Text under 18px uses --ink or --ink-dim, never
   --ink-mute.
   ====================================================================== */

/* Badges */
.tkd-ob-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 9999px;
  border: 1px solid var(--border); background: #fff; color: var(--ink);
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; line-height: 1.2;
}
.tkd-ob-badge--active   { background: var(--blue-pale);     color: var(--blue-deep);  border-color: #BFDBFE; }
.tkd-ob-badge--completed,
.tkd-ob-badge--done     { background: var(--ob-success-bg); color: var(--ob-success); border-color: var(--ob-success-line); }
.tkd-ob-badge--ended,
.tkd-ob-badge--waived   { background: var(--surface-3);     color: var(--ink-dim);    border-color: var(--border); }
.tkd-ob-badge--pending  { background: #fff;                 color: var(--ink-dim); }
.tkd-ob-badge--founding { background: var(--ob-amber-bg);   color: var(--ob-amber);   border-color: var(--ob-amber-line); }

/* Hero badges sit on the blue stage */
.tkd-ob-case-badges { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
#hero .tkd-ob-badge { background: rgba(255, 255, 255, 0.14); color: #fff; border-color: rgba(255, 255, 255, 0.4); }
#hero .tkd-ob-badge--active,
#hero .tkd-ob-badge--ended { background: #fff; color: var(--ink); border-color: #fff; }
#hero .tkd-ob-badge--completed { background: var(--ob-success-bg); color: var(--ob-success); border-color: var(--ob-success-line); }
#hero .tkd-ob-badge--founding { background: var(--ob-amber-bg); color: var(--ob-amber); border-color: var(--ob-amber-line); }

/* 01 · PROGRESS */
.tkd-ob-progress-count { font-size: 15px; line-height: 1.5; color: var(--ink-dim); }
.tkd-ob-meter {
  height: 10px; max-width: 28rem; margin-inline: auto; overflow: hidden;
  border-radius: 9999px; border: 1px solid var(--border); background: var(--surface-3);
}
.tkd-ob-meter-fill { display: block; height: 100%; border-radius: inherit; background: var(--ob-cta-gradient); }
.tkd-ob-milestones { display: grid; gap: 10px; }
.tkd-ob-milestone {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 14px 16px; border: 1px solid var(--border); border-radius: 12px; background: #fff;
}
.tkd-ob-milestone-mark {
  flex-shrink: 0; width: 28px; height: 28px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9999px; border: 2px solid var(--border); color: #fff; font-size: 15px;
}
.tkd-ob-milestone--done .tkd-ob-milestone-mark   { background: var(--ob-success); border-color: var(--ob-success); }
.tkd-ob-milestone--waived .tkd-ob-milestone-mark { background: var(--ink-dim); border-color: var(--ink-dim); }
.tkd-ob-milestone-body { min-width: 0; flex: 1; }
.tkd-ob-milestone-title { font-size: 15px; font-weight: 600; line-height: 1.4; color: var(--ink); }
.tkd-ob-milestone-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; font-size: 13px; color: var(--ink-dim); }
.tkd-ob-evidence { display: grid; gap: 8px; margin-top: 10px; font-size: 14px; line-height: 1.55; color: var(--ink-dim); }
.tkd-ob-shot--small { max-width: 220px; }

/* 03 · UPDATES */
.tkd-ob-updates { display: grid; gap: 20px; }
.tkd-ob-update {
  display: grid; gap: 18px; min-width: 0;
  padding: 22px; border: 1px solid var(--border); border-radius: 16px; background: #fff;
}
@media (min-width: 40rem) { .tkd-ob-update { padding: 28px; } }
.tkd-ob-update--compact { gap: 12px; padding: 18px 20px; background: var(--surface-2); }
.tkd-ob-update-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 16px; }
.tkd-ob-update-title { font-family: 'Inter', 'Inter Fallback', sans-serif; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; color: var(--ink); }
.tkd-ob-update--compact .tkd-ob-update-title { font-size: 17px; }
.tkd-ob-update-date {
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim);
}
.tkd-ob-quote { border-left: 4px solid var(--blue); padding: 4px 0 4px 18px; font-size: 19px; font-weight: 500; line-height: 1.5; color: var(--ink); }
.tkd-ob-quote p::before { content: "\201C"; }
.tkd-ob-quote p::after  { content: "\201D"; }
.tkd-ob-shots { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.tkd-ob-shots--compact { grid-template-columns: repeat(auto-fill, minmax(160px, 220px)); }
.tkd-ob-shot-figure figcaption { margin-top: 6px; font-size: 12.5px; line-height: 1.4; color: var(--ink-dim); }
.tkd-ob-shot { transition: box-shadow .2s ease, border-color .2s ease; }
.tkd-ob-shot:hover { border-color: #CBD5E1; box-shadow: 0 10px 24px -12px rgba(17, 24, 39, 0.35); }
.tkd-ob-stat--text .tkd-ob-stat-key { margin-top: 0; }
.tkd-ob-stat-text { margin-top: 6px; font-size: 14px; line-height: 1.5; color: var(--ink); }
.tkd-ob-notes { display: grid; gap: 14px; }
.tkd-ob-note dt {
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.tkd-ob-note dd { margin-top: 4px; font-size: 15px; line-height: 1.65; color: var(--ink); }
.tkd-ob-counts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.tkd-ob-counts li { padding: 5px 10px; border-radius: 9999px; border: 1px solid var(--blue-soft); background: var(--blue-pale); font-size: 13px; line-height: 1.3; color: var(--ink); }

/* Participant text (plain text, auto-linked URLs): long URLs wrap. */
.tkd-ob-ugc { overflow-wrap: anywhere; }
.tkd-ob-ugc a, .tkd-ob-ugc-link { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }

/* Video facade additions (the base rules are in section 11). Without JS
   the player box is hidden and the plain "Watch on …" link remains. */
.tkd-ob-video-wrap { display: grid; gap: 8px; }
.tkd-ob-video-alt { font-size: 13px; line-height: 1.5; color: var(--ink-dim); }
html:not(.tkd-ob-js) .tkd-ob-video { display: none; }
.tkd-ob-video--loom .tkd-ob-facade,
.tkd-ob-video--bunny .tkd-ob-facade,
.tkd-ob-video--added .tkd-ob-facade { background: linear-gradient(135deg, #1A5BFA 0%, #154EE0 55%, #0F2A8B 100%); }
.tkd-ob-facade img { position: absolute; inset: 0; }
.tkd-ob-facade-play .tkd-ob-icon { width: 26px; height: 26px; margin-left: 3px; fill: currentColor; }
.tkd-ob-video--loom .tkd-ob-facade-play,
.tkd-ob-video--bunny .tkd-ob-facade-play,
.tkd-ob-video--added .tkd-ob-facade-play { background: #fff; color: var(--blue); }
.tkd-ob-video.is-playing .tkd-ob-facade-label { display: none; }

/* The promo video on the program page (#watch, owner 2026-10-14): the
   same facade at panel size. 16:9, the panel's width up to 56rem
   (.max-w-4xl on the wrap keeps the panel's own child rule off it),
   centred, rounded like the panels, a 1px border and the panel shadow;
   a bigger play disc. Hidden without JS like every .tkd-ob-video (the
   "Watch on YouTube" link below it stays). */
.tkd-ob-video--promo { border-radius: var(--ob-panel-radius); border: 1px solid var(--border); box-shadow: var(--ob-panel-shadow); }
.tkd-ob-video--promo .tkd-ob-facade-play { width: 84px; height: 84px; margin: -42px 0 0 -42px; }
.tkd-ob-video--promo .tkd-ob-facade-play .tkd-ob-icon { width: 32px; height: 32px; }
.tkd-ob-promo .tkd-ob-video-alt { text-align: center; }

/* 04 · RESULTS */
.tkd-ob-h3 { margin-bottom: 10px; font-size: 19px; font-weight: 600; line-height: 1.35; color: var(--ink); }
.tkd-ob-testimonial { padding: 22px 24px; border: 1px solid var(--blue-soft); border-radius: 16px; background: var(--blue-pale); }
.tkd-ob-testimonial blockquote { font-size: 18px; line-height: 1.6; color: var(--ink); }
.tkd-ob-testimonial figcaption {
  margin-top: 10px; font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim);
}

/* Landing: 05 · THE BUILDERS */
.tkd-ob-index-card { display: flex; flex-direction: column; gap: 10px; }
.tkd-ob-index-name { font-size: 18px; font-weight: 600; line-height: 1.3; color: var(--ink); }
.tkd-ob-index-niche { font-size: 14px; line-height: 1.5; color: var(--ink-dim); }
.tkd-ob-index-status { display: flex; flex-wrap: wrap; gap: 6px; }
.tkd-ob-index-quote { padding-left: 12px; border-left: 3px solid var(--blue-soft); font-size: 14.5px; line-height: 1.55; color: var(--ink); }
.tkd-ob-index-quote--empty { color: var(--ink-dim); font-style: italic; }
.tkd-ob-index-link { margin-top: auto; align-self: flex-start; }

/* Upload preview (open-build.js initPreview, portal forms) */
.tkd-ob-preview { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.tkd-ob-preview-item { max-width: 220px; }
.tkd-ob-preview-item img { width: auto; max-height: 160px; object-fit: contain; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2); }
.tkd-ob-preview-item figcaption { margin-top: 4px; font-size: 12.5px; line-height: 1.4; color: var(--ink-dim); overflow-wrap: anywhere; }

/* Portal update forms (phase 4, §B10.1 "say so next to the file inputs"):
   the "Select your screenshots again" note stands out in amber (with its
   info icon and words, never colour alone; #92400E on white is 7:1), and
   a current image on the edit form keeps a small gap above its file input.
   (The form checkboxes need no rule of their own: .cf-check input above.) */
.tkd-ob-files-note { color: var(--ob-amber); font-weight: 500; }
.tkd-ob-form .tkd-ob-shot { margin-bottom: 4px; }

/* Bunny upload widget (phase 7, §B10.5; TKD_OB_Portal::bunny_widget_html()
   + assets/js/bunny-upload.js). The script un-hides it; without JS only
   the link field shows. Progress and outcome are always words in the
   status line and the percentage, never the bar's colour alone (§B7.5);
   --ink-dim on --surface-2 is 7:1. The bar's width transition stops under
   prefers-reduced-motion (section 12). The status line is never
   display:none, so screen readers keep the live region. */
.tkd-ob-bunny { margin: -4px 0 16px; }
.tkd-ob-bunny > .cf-hint { margin-bottom: 8px; }
.tkd-ob-bunny-ui {
  display: flex; flex-direction: column; gap: 8px;
  padding: 14px 16px; border: 1px dashed #CBD5E1; border-radius: 12px; background: var(--surface-2);
}
.tkd-ob-bunny-progress { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.tkd-ob-bunny-track { position: relative; flex: 1 1 160px; height: 8px; border-radius: 9999px; background: var(--border); overflow: hidden; }
.tkd-ob-bunny-bar { display: block; width: 0; height: 100%; border-radius: inherit; background: var(--blue); transition: width .2s linear; }
.tkd-ob-bunny-pct { font-size: 12.5px; line-height: 1.4; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.tkd-ob-bunny-status { font-size: 14px; line-height: 1.5; color: var(--ink); overflow-wrap: anywhere; }
.tkd-ob-bunny-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.tkd-ob-bunny > .cf-error { margin-top: 6px; }

/* ======================================================================
   11c. Points, levels and badges; the landing "Builder of the month" and
   leaderboard; the portal review card (phase 9: §A7.5, §B9.7, §B11.5,
   §C1). Words carry every meaning: the level and each badge are text, the
   progress bar has a text line and role="progressbar", the rank is a
   sentence. Colour is never the only signal (§B7.5); text under 18px uses
   --ink or --ink-dim.
   ====================================================================== */

/* Level chip and earned badges (case-study hero, index cards, portal) */
.tkd-ob-badge--level  { background: var(--blue-pale); color: var(--blue-deep); border-color: #BFDBFE; }
.tkd-ob-badge--earned { background: #fff; color: var(--ink); border-color: var(--blue-soft); }
.tkd-ob-badge--level .tkd-ob-icon,
.tkd-ob-badge--earned .tkd-ob-icon { color: var(--blue); }
#hero .tkd-ob-badge--level { background: #fff; color: var(--blue-deep); border-color: #fff; }
/* Solid white in the hero too (phase-9 review r1 #6): the 14 % white
   "#hero .tkd-ob-badge" chip puts 11px white text at about 3.6–4.4:1 over
   the stage blue; --ink on #fff is 17.7:1 and the --blue icon 5.4:1. */
#hero .tkd-ob-badge--earned { background: #fff; color: var(--ink); border-color: #fff; }

/* Case study: the points total next to "X of N milestones done" */
.tkd-ob-points-total { white-space: nowrap; font-weight: 600; color: var(--ink); }
.tkd-ob-points-total > span[aria-hidden="true"] { margin: 0 6px; font-weight: 400; color: var(--ink-dim); }

/* Portal dashboard: 0N · POINTS */
.tkd-ob-points-progress-label { margin-bottom: 8px; font-size: 14px; line-height: 1.5; color: var(--ink); }
.tkd-ob-points-meter { max-width: none; margin-inline: 0; }
.tkd-ob-points-rank { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; font-size: 15px; line-height: 1.5; color: var(--ink); }
.tkd-ob-points-rank > .tkd-ob-icon { color: var(--blue); }
.tkd-ob-points-badges { display: flex; flex-wrap: wrap; gap: 8px; }
.tkd-ob-points-history { display: grid; gap: 8px; }
.tkd-ob-points-item {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 2px 12px;
  padding: 12px 16px; border: 1px solid var(--border); border-radius: 12px; background: #fff;
}
.tkd-ob-points-item-label { grid-column: 1; font-size: 14.5px; line-height: 1.45; color: var(--ink); overflow-wrap: anywhere; }
.tkd-ob-points-item-date { grid-column: 1; font-size: 12.5px; line-height: 1.4; color: var(--ink-dim); }
.tkd-ob-points-item-value {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  font-size: 15px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tkd-ob-points-more { margin-top: 10px; }
.tkd-ob-points-more > .tkd-ob-points-history { margin-top: 10px; }
.tkd-ob-points-more-toggle {
  display: inline-flex; align-items: center; min-height: 44px; cursor: pointer;
  font-size: 14.5px; font-weight: 500; color: var(--blue); text-decoration: underline; text-underline-offset: 3px;
}

/* Portal dashboard: the optional WordPress.org review card (§B9.7) */
.tkd-ob-review-card { border-left: 4px solid var(--blue-soft); }

/* Landing: "Builder of the month" above the builder cards */
.tkd-ob-spotlight { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.tkd-ob-spotlight-title {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace;
  font-size: 11px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: var(--blue-deep);
}
.tkd-ob-spotlight-name { font-size: 24px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.25; color: var(--ink); overflow-wrap: anywhere; }
.tkd-ob-spotlight .tkd-ob-index-quote { align-self: stretch; }
.tkd-ob-spotlight-link { margin-top: 6px; }

/* Landing: the top-10 leaderboard below the builder cards (a table with a
   caption; it scrolls inside its card on narrow phones, never the page) */
.tkd-ob-leaderboard-caption { caption-side: top; text-align: left; padding: 4px 14px 12px; }
.tkd-ob-leaderboard-title { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; color: var(--ink); }
.tkd-ob-leaderboard-rank,
.tkd-ob-leaderboard-num { white-space: nowrap; font-variant-numeric: tabular-nums; }
.tkd-ob-leaderboard-table .tkd-ob-leaderboard-rank { width: 1%; font-weight: 600; color: var(--ink); }
.tkd-ob-leaderboard-table .tkd-ob-leaderboard-num { text-align: right; }
.tkd-ob-leaderboard-note { margin-top: 12px; font-size: 13px; line-height: 1.6; color: var(--ink-dim); text-align: center; }

/* ======================================================================
   11d. The applicant's first minutes (phase 10: §C1 05b, §C3, §B8.1,
   §B9.7). The class names are the phase-10 contract's shared table; the
   templates print the words, this file only lays them out. Text under
   18px uses --ink or --ink-dim; every state also has words (§B7.5);
   long words wrap instead of widening the page.
   ====================================================================== */

/* Landing 05b: What you earn (#earn). The numbers come from
   TKD_OB_Points::rules() in the markup; .tkd-ob-earn-pts is an inline
   chip, so it reads well at the start of an item or inside a sentence. */
.tkd-ob-earn-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
@media (min-width: 40rem) { .tkd-ob-earn-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tkd-ob-earn-item {
  min-width: 0; padding: 14px 16px; border: 1px solid var(--border); border-radius: 12px; background: #fff;
  font-size: 14.5px; line-height: 1.6; color: var(--ink-dim); overflow-wrap: break-word;
}
.tkd-ob-earn-pts {
  display: inline-flex; align-items: center; margin-right: 4px; padding: 1px 8px; border-radius: 9999px;
  border: 1px solid #BFDBFE; background: var(--blue-pale); color: var(--blue-deep);
  font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; line-height: 1.5;
}
.tkd-ob-earn-note { overflow-wrap: break-word; }
/* The hidden-leaderboard sentence (never a placeholder name or score):
   a dashed box, so it reads as "not yet" rather than as a feature. Its
   size, colour and spacing come from the template's utilities. */
.tkd-ob-earn-empty {
  padding: 12px 14px; border: 1px dashed #CBD5E1; border-radius: 10px; background: var(--surface-2);
  overflow-wrap: break-word;
}

/* "I read every application myself. … — Alpi T." (the §C3 form intro;
   its size and colour come from the template's utilities; white wherever
   a template puts it on the blue hero). */
.tkd-ob-founder-note { overflow-wrap: break-word; }
#hero .tkd-ob-founder-note { color: #FFFFFF; }

/* Success pages: the "Join the Discord" button (existing .btn .btn-blue,
   white on the blue hero, section 7) sits in the hero's button row, whose
   gap spaces it. It wraps instead of overflowing on a narrow phone, and
   keeps a gap from a sentence if a template puts it right after one. */
.tkd-ob-discord-btn { max-width: 100%; white-space: normal; }
p > .tkd-ob-discord-btn { margin-top: 14px; }
p + .tkd-ob-discord-btn { margin-top: 4px; margin-bottom: 16px; }

/* Portal dashboard: Get started (§B9.7). An <ol> of steps with a number
   badge in the state's colour: green done, blue current, grey to do. The
   state is also in words (an sr-only "(done)" / "(current step)" /
   "(to do)" and the check icon of a done step), never colour alone; the
   number is marked as decoration where the browser supports alternative
   text for `content` (the second declaration; the <ol> numbers the
   steps for assistive tech). */
.tkd-ob-gs-steps { display: grid; gap: 10px; counter-reset: tkd-ob-gs; }
.tkd-ob-gs-step {
  counter-increment: tkd-ob-gs; position: relative; min-width: 0;
  padding: 14px 16px 14px 58px; border: 1px solid var(--border); border-radius: 12px; background: #fff;
  font-size: 14.5px; line-height: 1.55; color: var(--ink); overflow-wrap: break-word;
}
.tkd-ob-gs-step::before {
  content: counter(tkd-ob-gs);
  content: counter(tkd-ob-gs) / "";
  position: absolute; left: 16px; top: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 9999px;
  border: 2px solid var(--border); background: #fff; color: var(--ink-dim);
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace; font-size: 12px; font-weight: 700; line-height: 1;
}
.tkd-ob-gs-step.is-done { background: var(--surface-2); color: var(--ink-dim); }
.tkd-ob-gs-step.is-done::before { border-color: var(--ob-success); background: var(--ob-success); color: #fff; }
.tkd-ob-gs-step.is-done .tkd-ob-icon { color: var(--ob-success); }
.tkd-ob-gs-step.is-current { border-color: var(--blue); background: var(--blue-pale); box-shadow: var(--ob-focus-ring); }
.tkd-ob-gs-step.is-current::before { border-color: var(--blue); background: var(--blue); color: #fff; }
.tkd-ob-gs-step.is-todo { background: #fff; }
.tkd-ob-gs-step-title { display: block; font-weight: 600; }
.tkd-ob-gs-step-text { margin-top: 4px; font-size: 14px; line-height: 1.55; color: var(--ink-dim); }
.tkd-ob-gs-step .btn { max-width: 100%; white-space: normal; }
.tkd-ob-gs-done {
  display: flex; align-items: flex-start; gap: 10px; padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--ob-success-line); background: var(--ob-success-bg); color: var(--ink);
  font-size: 15px; line-height: 1.55; overflow-wrap: break-word;
}
.tkd-ob-gs-done > .tkd-ob-icon { margin-top: 3px; color: var(--ob-success); }

/* ======================================================================
   12. Accessibility helpers (§B7.5)
   ====================================================================== */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;
}
.tkd-ob-skip {
  position: absolute; left: 12px; top: -100px; z-index: 100;
  padding: 10px 16px; border-radius: 9999px; background: #fff; color: var(--ink);
  font-family: 'JetBrains Mono', 'JetBrains Mono Fallback', monospace; font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  box-shadow: 0 8px 20px -8px rgba(8, 30, 110, 0.5);
}
.tkd-ob-skip:focus { top: 12px; }
#tkd-ob-content:focus { outline: none; }

/* Visible focus everywhere: a 2px outline carries the contrast, the
   focus-ring token (§B7.3) adds the brand halo. White on the blue stage. */
:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; box-shadow: var(--ob-focus-ring); }
#hero :focus-visible, body > footer :focus-visible, .tkd-ob-lightbox :focus-visible { outline-color: #FFFFFF; }
.cf-input:focus-visible, .cf-select:focus-visible, .cf-area:focus-visible { outline: none; }

/* Poppable backdrop icons (§B7.3, owner 2026-10-10). COPIED from
   D:\TKD\TurnKey Directories Child Theme\tkd-child\page-turnkey.php
   lines 2479–2509 (2026-10-10); initPop() in open-build.js sets the
   classes. Changed on purpose (the snap-back fix): the source ran geoPop
   on the .geo itself, which replaced its blobFloat drift for the pop, so
   the icon jumped back to its start point before it burst. It now runs on
   the inner <svg>, and the .geo keeps drifting. The svg rests at opacity
   0.55 (.geo svg, section 6), so the pop's opacities are the source's
   x 0.55 (the same look), and the spawn-in ends at 0.55 (the source
   ended at 1 and then dropped back to 0.55). !important lets a pop win
   over a spawn-in that is still running (same specificity, later rule).
   The .geo keeps pointer-events:none and the stage z-index -2: the
   script hit-tests boxes, so nothing here can block a click. With
   reduced motion the script never runs. */
@keyframes geoPop {
  0%   { transform: scale(1) rotate(0deg); opacity: 0.47; filter: blur(0); }
  45%  { transform: scale(1.55) rotate(8deg); opacity: 0.28; filter: blur(0.4px); }
  100% { transform: scale(0.35) rotate(-14deg); opacity: 0; filter: blur(2px); }
}
.geo.is-popping > svg { animation: geoPop 0.42s cubic-bezier(0.4, 1.2, 0.5, 1) forwards !important; }

@keyframes geoParticle {
  0%   { transform: translate(0, 0) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx, 0), var(--dy, 0)) scale(0.2); opacity: 0; }
}
.geo-pop-particle {
  position: absolute; width: 6px; height: 6px; margin-left: -3px; margin-top: -3px;
  border-radius: 9999px;
  background: radial-gradient(circle, #FFFFFF 0%, rgba(255, 255, 255, 0.55) 55%, rgba(255, 255, 255, 0) 100%);
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.85), 0 0 22px rgba(191, 219, 254, 0.6);
  pointer-events: none; z-index: 5; will-change: transform, opacity;
  animation: geoParticle 0.6s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}

@keyframes geoSpawn {
  0%   { transform: scale(0.6); opacity: 0; }
  60%  { opacity: 0.44; }
  100% { transform: scale(1); opacity: 0.55; }
}
.geo.is-spawning > svg { animation: geoSpawn 0.55s cubic-bezier(0.22, 1, 0.36, 1) forwards; }

/* prefers-reduced-motion: no animations, no transitions, no smooth scroll
   (§B7.5). The SVG <animate> flow lines are paused by open-build.js. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
  .anim-in, .pulse-dot, .geo, .wobble, .wobble-rev, .wobble-slow, .wobble-fast,
  .blob-a, .blob-b, .blob-c, .blob-d, .slow-spin, .page-stage .spots { animation: none !important; }
  .tkd-ob-js .reveal { opacity: 1 !important; transform: none !important; }
  .lift:hover { transform: none; }
}
