/* ═══════════════════════════════════════════════════════════════
   TABTRICKLE — v2 design system
   Derived from the Tab a0 keycap logo kit (see docs/brand-asset-inventory.md).

   Colour rule that generates this entire layout:
     lime #BCEE09 on ink   = 14.5:1  → accent on dark surfaces
     lime #BCEE09 on white =  1.37:1 → NEVER lime type on light
     ink on lime fill      = 14.5:1  → primary buttons
   So: ink sections carry the lime; light sections carry ink type and use
   lime only as a solid fill behind dark text.
   ═══════════════════════════════════════════════════════════════ */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  /* ─── Kit palette ─── */
  --ink:        #0A0A0A;
  --key:        #121212;
  --key-edge:   #1F1F1F;
  --lime:       #BCEE09;
  --lime-deep:  #93BC00;
  --silver:     #A8AAAC;
  --paper:      #F5F6F7;
  --paper-raise:#FFFFFF;
  --ink-soft:   #3A3A3C;

  /* Text tiers */
  --on-dark-strong: #F5F6F7;
  --on-dark-body:   #C9CBCD;   /* 11.2:1 on ink */
  --on-dark-muted:  #999999;   /* 4.6:1 on ink — contrast-verified floor */
  --on-light-strong:#0A0A0A;
  --on-light-body:  #3A3A3C;   /* 10.4:1 on paper */
  --hairline-dark:  #1E1E1E;
  --hairline-light: #E2E4E6;

  /* ─── Legacy aliases (blog.css + not-yet-migrated templates) ─── */
  --green: var(--lime);
  --green-dim: var(--lime-deep);
  --green-glow: rgba(188, 238, 9, 0.18);
  --copper: var(--silver);
  --copper-dim: var(--on-dark-muted);
  --black: #000000;
  --dark: var(--ink);
  --grey: var(--silver);
  --grey-mid: #999999;
  --white: var(--on-dark-strong);
  --off-white: var(--on-dark-body);
  --bg-light: var(--paper);
  --bg-dark: var(--ink);
  --text-on-light: var(--on-light-strong);
  --text-on-dark: var(--on-dark-body);

  /* Type */
  --font-display: 'Outfit', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Space */
  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 2rem;
  --space-lg: 4rem;
  --space-xl: 8rem;
  --section-y: clamp(4.5rem, 9vw, 7.5rem);
  --radius: 12px;
  --radius-sm: 8px;

  /* Keycap depth */
  --cap-lift: 0 4px 0 var(--lime-deep), 0 7px 18px rgba(0,0,0,.35);
  --cap-press: 0 1px 0 var(--lime-deep), 0 2px 6px rgba(0,0,0,.3);
}

html { font-size: 16px; -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; }}

body {
  font-family: var(--font-display);
  background: var(--ink);
  color: var(--on-dark-body);
  line-height: 1.65;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
/* height:auto is load-bearing. Images carry width/height attributes to
   reserve space against CLS; without this the attribute height is taken
   literally whenever CSS sets only the width. .about-photo rendered
   260x738 against a 1.084 natural aspect because of it. Rules that set an
   explicit height (.founder-photo) are more specific and still win. */
img, svg, video { max-width: 100%; height: auto; display: block; }
button, input { font: inherit; color: inherit; }

:focus-visible { outline: 2px solid var(--lime); outline-offset: 2px; border-radius: 3px; }
/* Lime ring is invisible on paper — use ink on light surfaces. */
.section-light :focus-visible, .btn-primary:focus-visible { outline-color: var(--ink); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--lime); color: var(--ink);
  padding: .75rem 1.25rem; font-weight: 700; border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }

/* ─── Layout primitives ─── */
.container { width: min(1180px, 100% - 3rem); margin-inline: auto; }
@media (max-width: 480px) { .container, .container-narrow { width: min(1180px, 100% - 2rem); }}

.section { padding: var(--section-y) 0; }
.section-dark  { background: var(--ink);  color: var(--on-dark-body); }
.section-key   { background: var(--key);  color: var(--on-dark-body); }
.section-light { background: var(--paper); color: var(--on-light-body); }
.section-light h1, .section-light h2, .section-light h3, .section-light h4 { color: var(--on-light-strong); }
.section-dark h1, .section-dark h2, .section-dark h3, .section-dark h4,
.section-key h1, .section-key h2, .section-key h3, .section-key h4 { color: var(--on-dark-strong); }

/* ─── Type scale ─── */
h1, h2, h3, h4 { font-family: var(--font-display); line-height: 1.12; letter-spacing: -0.02em; font-weight: 700; text-wrap: balance; }
h1 { font-size: clamp(2.35rem, 5.6vw, 4.15rem); font-weight: 800; letter-spacing: -0.033em; }
h2 { font-size: clamp(1.85rem, 3.9vw, 2.9rem); }
h3 { font-size: clamp(1.2rem, 2vw, 1.45rem); letter-spacing: -0.012em; }
p  { text-wrap: pretty; }

.eyebrow {
  display: inline-block;
  font-size: .74rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--lime);
  margin-bottom: 1.15rem;
}
.section-light .eyebrow { color: var(--ink-soft); }
.eyebrow-rule::before {
  content: ""; display: inline-block;
  width: 26px; height: 2px; background: currentColor;
  vertical-align: middle; margin-right: .7rem;
}

.section-head { max-width: 66ch; margin-bottom: clamp(2.5rem, 4vw, 3.75rem); }
.section-head p { margin-top: 1rem; font-size: 1.06rem; }

/* ═══════════════ KEYCAP CONTROLS ═══════════════ */
/* ─── Buttons ARE keycaps ───────────────────────────────────────────
   Not CSS pretending to be a keycap — the moulding is the logo render
   itself, 9-sliced so one cap stretches to any label width.

   border-image takes a 68px slice from each edge of a 2x cap texture. The
   corners and walls are drawn from the real photograph and never distort;
   only the middle stretches, and the middle was rebuilt per row when the
   texture was generated so it has no legend and no horizontal detail to
   smear. border-width is what reserves room for the wall, so the label sits
   on the face rather than over the moulding.

   Colourways match the mark: matte black and titanium are the two caps in
   the logo. Lime is the same titanium cap re-tinted by luminance, which is
   how a real keycap set is made — identical moulding, different colour.
   There is no lime cap in the logo itself, which is why a flat lime slab
   never read as one.

   Textures are injected as custom properties by _capstyles.html.jinja,
   because hash_assets() renames assets but does not rewrite CSS url(). */
.btn, .cta-button {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-weight: 700; font-size: 1rem; line-height: 1;
  letter-spacing: .01em;
  min-height: 56px;
  padding: .5rem 1.35rem;
  border-style: solid;
  border-width: 17px 20px 19px;
  /* Never leave border-color unset: it defaults to currentColor, which on
     the ghost cap is a ~20px WHITE frame wherever border-image does not
     paint (older mobile browsers, decode failure, subpixel edges). Each
     button overrides this with its own cap face colour below. */
  border-color: var(--ink);
  border-image-slice: 68 fill;
  border-image-width: 17px 20px 19px;
  border-image-repeat: stretch;
  cursor: pointer; text-align: center;
  transition: transform .11s ease, filter .13s ease;
}
/* The label needs to clear the moulded wall, not sit on it. */
.btn > *, .cta-button > * { position: relative; }

/* background-color is the cap's own face colour, sampled from each texture.
   border-image fill paints over it, so it changes nothing visually — but it
   is load-bearing twice over: contrast checkers cannot see a border-image,
   so without it the dark label is measured against the page behind the
   button and reads 1.16:1 (this cost 4 accessibility points before it was
   added), and if the texture ever fails to load the button is still a solid
   cap with a legible label rather than invisible text on the page. */
.btn-primary, .cta-button {
  border-image-source: var(--cap-lime);
  border-color: #90B308;
  background-color: #90B308;   /* lime face — 11.0:1 with the label */
  color: #14200A;
}
.btn-ghost, .cta-button-secondary {
  border-image-source: var(--cap-dark);
  border-color: #2A2A2B;
  background-color: #2A2A2B;   /* matte black face — 16.1:1 */
  color: var(--on-dark-strong);
}
.section-light .btn-ghost, .section-light .cta-button-secondary {
  border-image-source: var(--cap-titanium);
  border-color: #BABAB8;
  background-color: #BABAB8;   /* titanium face — 14.1:1 */
  color: var(--on-light-strong);
}

/* Same three gestures as the rail: hover lights the cap, click depresses it.
   Filters, not shadows — the cap's own edge is in the texture, so a
   box-shadow would draw a rectangle around a rounded object. */
/* Deliberately no filter on hover: filter creates a stacking context, which
   would pull the z-index:-1 glow in front of the cap. Lift plus glow only. */
.btn:hover, .cta-button:hover { transform: translateY(-1px); }
.btn-ghost:hover, .cta-button-secondary:hover { color: var(--lime); }
.btn:active, .cta-button:active { transform: translateY(3px); }

/* Standby glow — the cap looks powered rather than painted on.
   Sits behind the cap (z-index:-1 against a parent with no stacking
   context), so only the halo around the moulding shows. Animates opacity
   alone, which the compositor handles without repainting the button. */
.btn::before, .cta-button::before {
  content: "";
  position: absolute; inset: -16px; z-index: -1;
  background: radial-gradient(closest-side, rgba(188,238,9,.62), rgba(188,238,9,0) 72%);
  opacity: .3;
  pointer-events: none;
  animation: cap-glow 5.5s ease-in-out infinite;
}
.btn-ghost::before, .cta-button-secondary::before { background: radial-gradient(closest-side, rgba(188,238,9,.46), rgba(188,238,9,0) 72%); }
.section-light .btn-ghost::before, .section-light .cta-button-secondary::before { background: radial-gradient(closest-side, rgba(10,10,10,.16), rgba(10,10,10,0) 72%); }
.btn:hover::before, .cta-button:hover::before { animation-name: cap-glow-strong; }
.btn:active::before, .cta-button:active::before { animation: none; opacity: .22; }

@keyframes cap-glow        { 0%, 100% { opacity: .4; } 50% { opacity: .82; } }
@keyframes cap-glow-strong { 0%, 100% { opacity: .8; } 50% { opacity: 1; } }
.btn:focus-visible, .cta-button:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }
.section-light .btn-ghost:focus-visible, .section-light .cta-button-secondary:focus-visible { outline-color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn:hover, .btn:active,
  .btn-primary:hover, .btn-primary:active,
  .btn-ghost:hover, .btn-ghost:active,
  .cta-button, .cta-button:hover, .cta-button:active,
  .cta-button-secondary:hover, .cta-button-secondary:active { transition: none; transform: none; }}

.btn-row { display: flex; flex-wrap: wrap; gap: .9rem; align-items: center; }

.textlink { display: inline-flex; align-items: center; gap: .4rem; font-weight: 600; color: var(--lime); border-bottom: 1px solid transparent; padding-bottom: 1px; }
.textlink:hover { border-bottom-color: currentColor; }
.section-light .textlink { color: var(--ink); border-bottom-color: rgba(10,10,10,.28); }
.section-light .textlink:hover { border-bottom-color: var(--ink); }

/* ═══════════════ HEADER ═══════════════ */
.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  background: rgba(10,10,10,.72);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: background-color .25s ease, border-color .25s ease;
}
.site-header.scrolled { background: rgba(10,10,10,.94); border-bottom-color: var(--hairline-dark); }
.site-header nav {
  width: min(1180px, 100% - 3rem); margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; padding: .85rem 0;
}
@media (max-width: 480px) { .site-header nav { width: min(1180px, 100% - 2rem); }}

.nav-logo { display: flex; align-items: center; flex-shrink: 0; }
/* The wordmark is 8 keycaps wide — below ~32px tall the legends stop reading. */
.nav-logo picture { display: block; line-height: 0; }
/* 44px, not 34px: the keycap bevel, brushed metal and cast shadow are the
   brand, and below ~40px tall there aren't enough pixels for any of it to
   read — the mark flattens into a green smudge. */
/* aspect-ratio is pinned to the master (1876x295) rather than left to the
   decoded file. Each generated width rounds its height independently
   (220/35 = 6.286, 440/69 = 6.377), so with width:auto the header box changed
   size depending on which srcset candidate won — a real CLS regression. */
.nav-logo img { height: 44px; width: auto; aspect-ratio: 1876 / 295; }
.nav-logo .nav-logo-mark { display: none; }
.nav-logo .nav-logo-mark img { height: 40px; width: 40px; aspect-ratio: 1 / 1; }

.nav-links { display: flex; align-items: center; gap: .35rem; }
.nav-links > a {
  font-size: .93rem; font-weight: 550;
  color: var(--on-dark-body);
  padding: .55rem .78rem; border-radius: 8px;
  transition: color .18s ease, background-color .18s ease;
}
.nav-links > a:hover { color: var(--on-dark-strong); background: rgba(255,255,255,.05); }
.nav-links > a.nav-active { color: var(--lime); }

/* ─── Products disclosure ─────────────────────────────────────────
   Built on <details>, so it opens by click and by keyboard with no script,
   and is never hover-only — which is what makes it usable on touch. app.js
   adds close-on-Escape and close-on-outside-click; neither is required for
   the menu to work. */
.nav-menu { position: relative; }
.nav-menu > summary {
  list-style: none;
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .93rem; font-weight: 550;
  color: var(--on-dark-body);
  padding: .55rem .78rem; border-radius: 8px;
  cursor: pointer;
  transition: color .18s ease, background-color .18s ease;
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::after {
  content: ""; width: 7px; height: 7px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .18s ease;
}
.nav-menu[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.nav-menu > summary:hover { color: var(--on-dark-strong); background: rgba(255,255,255,.05); }

.nav-menu-panel { display: grid; gap: .25rem; }
.nav-menu-panel > a {
  display: grid; gap: .1rem;
  padding: .7rem .85rem; border-radius: 8px;
  color: var(--on-dark-body);
  transition: color .18s ease, background-color .18s ease;
}
.nav-menu-panel > a:hover { background: rgba(255,255,255,.05); }
.nav-menu-panel > a.nav-active .nav-menu-name { color: var(--lime); }
.nav-menu-name { font-size: .93rem; font-weight: 650; color: var(--on-dark-strong); }
.nav-menu-note { font-size: .8rem; color: var(--on-dark-muted); }

@media (min-width: 1001px) {
  .nav-menu[open] > .nav-menu-panel {
    position: absolute; top: calc(100% + .5rem); left: 0;
    min-width: 20rem;
    padding: .5rem;
    background: rgba(10,10,10,.97);
    border: 1px solid var(--hairline-dark);
    border-radius: var(--radius-sm);
    box-shadow: 0 12px 32px rgba(0,0,0,.55);
    backdrop-filter: blur(8px);
    z-index: 60;
  }}
@media (prefers-reduced-motion: reduce) {
  .nav-menu > summary, .nav-menu > summary::after, .nav-menu-panel > a { transition: none; }}

.nav-cta {
  margin-left: .55rem;
  background: var(--lime); color: var(--ink) !important;
  font-weight: 700; font-size: .9rem;
  padding: .7rem 1.15rem !important; border-radius: 9px;
  box-shadow: 0 3px 0 var(--lime-deep);
  border-top: 1px solid rgba(255,255,255,.5);
  transition: transform .12s ease, box-shadow .12s ease, background-color .2s ease;
}
.nav-cta:hover { background: #C8F529 !important; }
.nav-cta:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--lime-deep); }

.nav-toggle {
  display: none; background: none; border: 0; cursor: pointer;
  width: 44px; height: 44px; padding: 10px;
  flex-direction: column; justify-content: center; gap: 5px; border-radius: 8px;
}
.nav-toggle span { display: block; height: 2px; width: 100%; background: var(--on-dark-strong); border-radius: 2px; transition: transform .22s ease, opacity .22s ease; }

@media (max-width: 1000px) {
  .nav-toggle { display: flex; }
  .nav-logo img { height: 38px; }
  .nav-links {
    position: fixed; inset: 62px 0 auto 0;
    flex-direction: column; align-items: stretch; gap: .55rem;
    background: var(--ink);
    border-top: 1px solid var(--hairline-dark);
    border-bottom: 1px solid var(--hairline-dark);
    padding: .85rem .9rem 1.5rem;
    max-height: calc(100dvh - 62px); overflow-y: auto;
    transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: opacity .2s ease, transform .2s ease;
  }
  .nav-links.open { transform: none; opacity: 1; pointer-events: auto; }
  /* Same chassis language as the desktop rail: a keycap per destination,
     not a hairline-divided list. So mobile carries the same brand object,
     just reflowed into a column instead of a letter strip. */
  .nav-links > a:not(.nav-cta) {
    display: flex; align-items: center; gap: .7rem;
    padding: 1rem 1.1rem; font-size: 1.02rem; font-weight: 600;
    border-radius: var(--radius-sm);
    background: var(--key);
    border: 1px solid var(--hairline-dark);
    box-shadow: 0 2px 0 rgba(0,0,0,.5);
    transition: border-color .16s ease, transform .1s ease, box-shadow .1s ease;
  }
  .nav-links > a:not(.nav-cta)::before {
    content: ""; flex-shrink: 0; width: 8px; height: 8px; border-radius: 2px;
    background: var(--on-dark-muted);
    transition: background-color .16s ease;
  }
  .nav-links > a:not(.nav-cta):hover { border-color: var(--lime); background: rgba(255,255,255,.05); }
  .nav-links > a:not(.nav-cta).nav-active { color: var(--lime); border-color: var(--lime); }
  .nav-links > a:not(.nav-cta).nav-active::before, .nav-links > a:not(.nav-cta):hover::before { background: var(--lime); }
  .nav-links > a:not(.nav-cta):active { transform: translateY(2px); box-shadow: 0 0 0 rgba(0,0,0,.5); }
  .nav-cta { margin: .35rem .1rem 0; text-align: center; }

  /* The disclosure adopts the same keycap chassis as the sibling links, and
     its panel sits inline rather than floating — a dropdown overlaying a
     full-screen mobile menu would cover the links underneath it. */
  .nav-menu > summary {
    display: flex; align-items: center; gap: .7rem;
    padding: 1rem 1.1rem; font-size: 1.02rem; font-weight: 600;
    border-radius: var(--radius-sm);
    background: var(--key);
    border: 1px solid var(--hairline-dark);
    box-shadow: 0 2px 0 rgba(0,0,0,.5);
  }
  .nav-menu > summary::before {
    content: ""; flex-shrink: 0; width: 8px; height: 8px; border-radius: 2px;
    background: var(--on-dark-muted);
  }
  .nav-menu > summary::after { margin-left: auto; }
  .nav-menu[open] > summary { border-color: var(--lime); }
  .nav-menu[open] > summary::before { background: var(--lime); }
  .nav-menu-panel { padding: .55rem 0 .1rem .9rem; gap: .5rem; }
  .nav-menu-panel > a {
    padding: .8rem 1rem;
    border-radius: var(--radius-sm);
    background: var(--key);
    border: 1px solid var(--hairline-dark);
  }
  .nav-menu-panel > a.nav-active { border-color: var(--lime); }}
@media (max-width: 560px) {
  .nav-logo .nav-logo-full { display: none; }
  .nav-logo .nav-logo-mark { display: block; }}
@media (prefers-reduced-motion: reduce) { .nav-links, .nav-toggle span { transition: none; }}

/* ═══════════════ HERO ═══════════════ */
.hero {
  position: relative;
  padding: calc(var(--section-y) + 4.5rem) 0 var(--section-y);
  background: radial-gradient(120% 80% at 82% 8%, rgba(188,238,9,.07), transparent 58%), var(--ink);
  border-bottom: 1px solid var(--hairline-dark);
  overflow: hidden;
}
.hero-sub { font-size: clamp(1.06rem, 1.75vw, 1.3rem); color: var(--on-dark-body); max-width: 54ch; margin-bottom: 2.1rem; }
@keyframes wireFlow { to { top: 100%; } }

@media (max-width: 900px) {
  .hero { padding-top: calc(var(--section-y) + 2rem); }}
.proof-honest { padding: 1.5rem 1.7rem; border-radius: var(--radius); background: var(--paper-raise); border: 1px dashed #C6C9CC; }
.proof-honest p { color: var(--on-light-body); max-width: 70ch; }
.proof-honest strong { color: var(--on-light-strong); }

input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 999px; background: #26262A; cursor: pointer; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--lime); border-top: 1px solid rgba(255,255,255,.6);
  box-shadow: 0 3px 0 var(--lime-deep), 0 4px 10px rgba(0,0,0,.45);
  transition: transform .12s ease, box-shadow .12s ease;
}
input[type="range"]::-webkit-slider-thumb:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--lime-deep); }
input[type="range"]::-moz-range-thumb { width: 26px; height: 26px; border-radius: 8px; border: 0; background: var(--lime); box-shadow: 0 3px 0 var(--lime-deep), 0 4px 10px rgba(0,0,0,.45); }
input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: #26262A; }
input[type="range"]:focus-visible { outline: 2px solid var(--lime); outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { input[type="range"]::-webkit-slider-thumb { transition: none; }}

/* Numbered steps on a light surface (/system/ "the four parts") */
.steps-light { display: grid; grid-template-columns: 1fr; gap: 1.15rem; }
@media (min-width: 700px)  { .steps-light { grid-template-columns: repeat(2, 1fr); }}
.step-light {
  background: var(--paper-raise); border: 1px solid var(--hairline-light);
  border-radius: var(--radius); padding: 1.9rem 1.75rem;
}
.step-light-n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; height: 34px; padding: 0 .6rem; margin-bottom: 1.1rem;
  border-radius: 8px; background: var(--lime); color: var(--ink);
  font-weight: 800; font-size: .9rem; letter-spacing: .02em;
  box-shadow: 0 3px 0 var(--lime-deep); border-top: 1px solid rgba(255,255,255,.5);
}
.step-light h3 { margin-bottom: .65rem; }
.step-light p { color: var(--on-light-body); font-size: .98rem; margin-bottom: 1rem; }

/* ═══════════════ FAQ ═══════════════ */
.faq-list { display: grid; gap: 1px; background: var(--hairline-dark); border: 1px solid var(--hairline-dark); border-radius: var(--radius); overflow: hidden; }
.faq-row { background: var(--key); padding: 1.65rem 1.7rem; }
.faq-row h3 { font-size: 1.08rem; margin-bottom: .55rem; }
.faq-row p { font-size: .98rem; color: var(--on-dark-body); max-width: 72ch; }

/* ═══════════════ FINAL CTA ═══════════════ */
.final-cta { text-align: center; }
.final-cta h2 { margin-bottom: 1rem; }
.final-cta p { margin: 0 auto 2rem; max-width: 56ch; font-size: 1.08rem; }
.final-cta .btn-row { justify-content: center; }

/* ═══════════════ FOOTER ═══════════════ */
.footer-strap { background: var(--lime); color: var(--ink); text-align: center; font-weight: 700; font-size: .82rem; letter-spacing: .16em; text-transform: uppercase; padding: .95rem 1rem; }
.site-footer { background: #060606; border-top: 1px solid var(--hairline-dark); padding: clamp(3rem,6vw,4.5rem) 0 2rem; }
.footer-inner { width: min(1180px, 100% - 3rem); margin-inline: auto; }
@media (max-width: 480px) { .footer-inner { width: min(1180px, 100% - 2rem); }}
.footer-grid { display: grid; grid-template-columns: 1.7fr 1fr 1.35fr 1.35fr; gap: clamp(1.75rem,3vw,3rem); }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; }}
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; }}

.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: .55rem; }
.footer-col > a { font-size: .93rem; color: var(--on-dark-muted); transition: color .18s ease; }
.footer-col > a:hover { color: var(--lime); }
.footer-col-title { font-size: .78rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--on-dark-strong); margin-bottom: .5rem; }
.footer-logo { height: 36px; width: auto; aspect-ratio: 1876 / 295; margin-bottom: .55rem; }
.footer-text { font-size: .93rem; color: var(--on-dark-muted); max-width: 30ch; }
.footer-address { font-size: .9rem; color: var(--on-dark-muted); line-height: 1.75; font-style: normal; }
.footer-address a { color: var(--on-dark-body); }
.footer-address a:hover { color: var(--lime); }
.footer-socials { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .85rem; }
.footer-socials a {
  display: grid; place-items: center; width: 40px; height: 40px;
  border-radius: 9px; color: var(--on-dark-muted);
  background: var(--key); box-shadow: inset 0 0 0 1px #232326;
  transition: color .18s ease, background-color .18s ease, transform .12s ease;
}
.footer-socials a:hover { color: var(--lime); background: #17171A; }
.footer-socials a:active { transform: translateY(1px); }
.footer-credit { width: min(1180px,100% - 3rem); margin: 3rem auto 0; padding-top: 1.75rem; border-top: 1px solid var(--hairline-dark); font-size: .85rem; color: var(--on-dark-muted); text-align: center; }

/* ═══════════════ COOKIE BANNER ═══════════════ */
#cookie-consent {
  position: fixed; inset: auto 0 0 0; z-index: 150;
  background: var(--key); border-top: 1px solid var(--hairline-dark);
  padding: 1.15rem 1rem; display: none;
  box-shadow: 0 -10px 30px rgba(0,0,0,.4);
}
.cookie-inner { width: min(1180px,100% - 2rem); margin-inline: auto; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; }
.cookie-inner p { font-size: .87rem; line-height: 1.55; color: var(--on-dark-body); max-width: 74ch; }
.cookie-inner p a { color: var(--lime); text-decoration: underline; }
.cookie-buttons { display: flex; gap: .65rem; flex-shrink: 0; }
.cookie-btn { padding: .7rem 1.15rem; border: 0; border-radius: 9px; font-weight: 650; font-size: .9rem; cursor: pointer; transition: transform .12s ease, box-shadow .12s ease; }
.cookie-btn-reject { background: transparent; color: var(--on-dark-body); box-shadow: inset 0 0 0 1px #2E2E30; }
.cookie-btn-reject:hover { color: var(--on-dark-strong); box-shadow: inset 0 0 0 1px #45454A; }
.cookie-btn-accept { background: var(--lime); color: var(--ink); box-shadow: 0 3px 0 var(--lime-deep); border-top: 1px solid rgba(255,255,255,.5); }
.cookie-btn-accept:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--lime-deep); }
@media (max-width: 700px) { .cookie-buttons { width: 100%; } .cookie-btn { flex: 1; }}

/* ═══════════════ MOTION ═══════════════ */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s cubic-bezier(.22,.61,.36,1), transform .6s cubic-bezier(.22,.61,.36,1); }
.reveal-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; }}

@media (prefers-reduced-motion: no-preference) {
  .hero-rise { opacity: 0; transform: translateY(14px); animation: heroRise .72s cubic-bezier(.22,.61,.36,1) forwards; }
  .hero-rise-1 { animation-delay: .04s; }
  .hero-rise-2 { animation-delay: .13s; }
  .hero-rise-3 { animation-delay: .22s; }
  .hero-rise-4 { animation-delay: .31s; }
  .hero-rise-5 { animation-delay: .40s; }}
@keyframes heroRise { to { opacity: 1; transform: none; } }

/* ═══════════════ LEGACY / NOT-YET-MIGRATED PAGE STYLES ═══════════════
   Service landing pages, About, FAQ and /system/ still use the v1 class
   names. These keep them coherent under the v2 palette until their markup
   is migrated. */

.keyline-frame { border-radius: var(--radius); overflow: hidden; }

/* .cta-button predates the keycap system and is still used on seven
   templates — about, more-info, blog posts and the four service pages.
   Left alone it meant most of the site's CTAs were flat lime rectangles
   while the homepage had moulded caps. It is now just an alias: the cap
   rules below select both, so there is one button in the design system,
   not two. Kept as a class rather than rewritten across seven templates
   so no markup or copy moves. */
.cta-button { margin-top: .5rem; }

/* The About header carries a portrait alongside the copy. The landing-page /
   more-info header does not — giving it the same two-column grid squeezed the
   h1 into the 260px photo column. Keep them separate. */
.about-header, .more-info-header {
  width: min(1180px,100% - 3rem); margin-inline: auto;
  padding: calc(var(--section-y) + 4rem) 0 var(--section-y);
}
.about-header {
  display: grid; grid-template-columns: minmax(0,1fr) 260px;
  gap: clamp(2rem,4vw,3.5rem); align-items: center;
}
@media (max-width: 800px) { .about-header { grid-template-columns: 1fr; }}

.more-info-header h1 { margin-bottom: 1.25rem; max-width: 20ch; }
.more-info-header > p { color: var(--on-dark-body); max-width: 64ch; font-size: 1.08rem; }
.about-header-copy h1 { margin-bottom: 1.25rem; }
.about-header-copy p { color: var(--on-dark-body); max-width: 60ch; margin-bottom: 1rem; }
.about-photo { width: 100%; max-width: 260px; border-radius: var(--radius); }
.about-story, .about-principles, .about-close, .explainers, .faq-groups { width: min(1180px,100% - 3rem); margin-inline: auto; padding-bottom: var(--section-y); }
.about-story p, .about-close p { color: var(--on-dark-body); max-width: 68ch; margin-bottom: 1.1rem; }
.about-story h2, .about-principles h2, .about-close h2, .explainers h2, .faq-groups h2 { margin-bottom: 1.5rem; }
.about-principles { display: grid; gap: 1rem; }
.principle { background: var(--key); border-radius: var(--radius); padding: 1.6rem 1.5rem; border: 1px solid var(--hairline-dark); }
.principle-label { display: block; font-size: .76rem; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: var(--lime); margin-bottom: .5rem; }
.principle p { color: var(--on-dark-body); }
.about-close-ctas { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: 1.5rem; }
/* Closing CTA band. Needs its own container — it sits directly under <main>
   on the landing pages, so without one it rendered full-bleed to x=0. */
.more-info-close {
  width: min(1180px, 100% - 3rem); margin-inline: auto;
  padding-bottom: var(--section-y);
  display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem;
}
@media (max-width: 480px) { .more-info-close { width: min(1180px, 100% - 2rem); }}
.more-info-close p { font-size: 1.15rem; color: var(--on-dark-strong); max-width: 40ch; margin: 0; }

.explainer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,280px),1fr)); gap: 1.15rem; }
.explainer-block { background: var(--key); border: 1px solid var(--hairline-dark); border-radius: var(--radius); padding: 1.75rem 1.6rem; }
.explainer-block h3 { margin-bottom: .6rem; }
.explainer-block p { color: var(--on-dark-body); font-size: .98rem; margin-bottom: .8rem; }

.faq-group { margin-bottom: 2.5rem; }
.faq-group h2 { margin-bottom: 1.25rem; }
.faq-item { background: var(--key); border: 1px solid var(--hairline-dark); border-radius: var(--radius-sm); padding: 1.5rem 1.6rem; margin-bottom: .85rem; }
.faq-item h3 { font-size: 1.06rem; margin-bottom: .5rem; }
.faq-item p { color: var(--on-dark-body); font-size: .98rem; max-width: 72ch; }

.blog-strip { width: min(1180px,100% - 3rem); margin-inline: auto; padding-bottom: var(--section-y); }
.blog-strip h2 { margin-bottom: 1.5rem; }
.blog-strip-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,270px),1fr)); gap: 1.15rem; }
.blog-strip-card { background: var(--key); border: 1px solid var(--hairline-dark); border-radius: var(--radius); padding: 1.6rem 1.5rem; transition: border-color .2s ease, transform .2s ease; }
.blog-strip-card:hover { border-color: #2E2E30; transform: translateY(-2px); }
.blog-strip-date { font-size: .76rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--on-dark-muted); }
.blog-strip-card h3 { margin: .5rem 0 .5rem; font-size: 1.08rem; }
.blog-strip-card p { font-size: .95rem; color: var(--on-dark-body); }
.blog-strip-link { display: inline-block; margin-top: 1.25rem; color: var(--lime); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .blog-strip-card:hover { transform: none; }}

.legal-page { width: min(820px,100% - 3rem); margin-inline: auto; padding: calc(var(--section-y) + 4rem) 0 var(--section-y); }
.legal-page h1 { margin-bottom: 1rem; }
.legal-page h2 { font-size: 1.4rem; margin: 2.5rem 0 .9rem; }
.legal-page p, .legal-page li { color: var(--on-dark-body); margin-bottom: .9rem; }
.legal-page ul, .legal-page ol { padding-left: 1.4rem; margin-bottom: 1.2rem; }
.legal-page a { color: var(--lime); text-decoration: underline; }
.legal-date { font-size: .9rem; color: var(--on-dark-muted); margin-bottom: 2rem; }
.legal-back { display: inline-block; margin-top: 2.5rem; color: var(--lime); font-weight: 600; }
.cookie-table { width: 100%; border-collapse: collapse; margin-bottom: 1.5rem; font-size: .93rem; }
.cookie-table th, .cookie-table td { text-align: left; padding: .75rem .8rem; border-bottom: 1px solid var(--hairline-dark); color: var(--on-dark-body); }
.cookie-table th { color: var(--on-dark-strong); font-weight: 650; }

/* ═══════════════════════════════════════════════════════════════
   KEYCAP RAIL — the actual logo mark, turned into navigation
   ───────────────────────────────────────────────────────────────
   The caps are sliced from the logo render itself (keyrail-sprite),
   not redrawn in CSS — an approximation read as "nearly the logo",
   which is worse than not using it. One sprite, one request, eight
   independently interactive links.

   Sizing: cells scale with viewport height so the full mark always
   fits on screen. At 8 cells of at most 50px the rail is 400px tall;
   on a short laptop it shrinks rather than running off the bottom.

   Placement: .container is min(1180px, 100% - 3rem), so the gutter is
   (100vw - 1180px) / 2. The rail only appears at >=1440px, where that
   gutter measures 130px against a 66px rail.
   ═══════════════════════════════════════════════════════════════ */

.keyrail { display: none; }

@media (min-width: 1360px) {
  /* The header STAYS at this breakpoint. It used to be hidden here on the
     grounds that the rail carried the same destinations — but the rail's
     keys are single letters whose labels appear only on hover or focus, so
     above 1360px the site had no readable navigation at all, and the widest
     desktops got the weakest wayfinding. The rail (currently not rendered —
     see _header.html.jinja) was only ever a shortcut layered on top of the
     nav, never a replacement for it. */

  .keyrail {
    /* Full-size caps. On 1330-1439 laptops the content column narrows to
       make room (see the band below) rather than the keys shrinking to
       something unreadable — at 1366 with the full 1180 column there is
       only ~85px of gutter, which leaves 6px of clearance and looks wrong.
       The 8vh term keeps all eight keys on screen on short laptops. */
    --cell-w: min(104px, calc(11vh * 1.32));
    --cell-h: calc(var(--cell-w) / 1.32);
    display: block;
    position: fixed;
    left: calc((100vw - 1180px) / 4);
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 40;
    border-radius: 10px;
    /* No overflow:hidden here — the hover label sits outside the rail's own
       box, and clipping it made the labels impossible to ever see. The caps
       round their own outer corners instead. */
    background: var(--ink);
    box-shadow: 0 0 0 1px #1C1C1E, 0 10px 30px rgba(0,0,0,.55);
  }
  .keyrail-list > li:first-child .keycap { border-radius: 10px 10px 0 0; }
  .keyrail-list > li:last-child .keycap { border-radius: 0 0 10px 10px; }
  .keycap::before { border-radius: inherit; }
  .keyrail-list { list-style: none; display: flex; flex-direction: column; position: relative; z-index: 1; }
  /* Standby glow behind the mark, matching the buttons. Behind the chassis,
     so it reads as the keyboard being powered rather than as a highlight. */
  .keyrail::before {
    content: "";
    position: absolute; inset: -18px; z-index: 0;
    border-radius: 20px;
    background: radial-gradient(closest-side, rgba(188,238,9,.34), rgba(188,238,9,0) 74%);
    pointer-events: none;
    animation: cap-glow 5.5s ease-in-out infinite;
  }

  .keycap {
    position: relative;
    display: grid; place-items: center;
    width: var(--cell-w); height: var(--cell-h);
    background: var(--ink);
    color: var(--lime);
    font-family: var(--font-mono); font-weight: 700;
    font-size: calc(var(--cell-h) * 0.34); line-height: 1;
    text-decoration: none;
    transition: transform .12s ease, filter .16s ease;
  }
  /* The sprite rides on ::before so the letter underneath stays as a
     fallback if the image ever fails to load. */
  .keycap::before {
    content: "";
    position: absolute; inset: 0;
    /* Injected inline by _keyrail.html.jinja via the hashed-asset lookup. */
    background-image: var(--keyrail-sprite);
    background-size: 100% 800%;
    background-repeat: no-repeat;
  }
  .keyrail-list > li:nth-child(1) .keycap::before { background-position: 0 0%; }
  .keyrail-list > li:nth-child(2) .keycap::before { background-position: 0 14.2857%; }
  .keyrail-list > li:nth-child(3) .keycap::before { background-position: 0 28.5714%; }
  .keyrail-list > li:nth-child(4) .keycap::before { background-position: 0 42.8571%; }
  .keyrail-list > li:nth-child(5) .keycap::before { background-position: 0 57.1429%; }
  .keyrail-list > li:nth-child(6) .keycap::before { background-position: 0 71.4286%; }
  .keyrail-list > li:nth-child(7) .keycap::before { background-position: 0 85.7143%; }
  .keyrail-list > li:nth-child(8) .keycap::before { background-position: 0 100%; }

  /* Three distinct gestures, matching the physical object:
       hover / focus  → the lime edge lights, cap holds its height;
       active (click) → the cap travels down;
       aria-current   → rests softly lit.
     Glow is a filter, not a box-shadow, because the caps are images on a
     dark deck — a box-shadow would draw a rectangle around the cell. */
  .keycap:hover, .keycap:focus-visible {
    filter: brightness(1.18) drop-shadow(0 0 5px rgba(188,238,9,.6));
  }
  .keycap:active { transform: translateY(2px); filter: brightness(1.05); }
  .keycap[aria-current] { filter: brightness(1.12) drop-shadow(0 0 6px rgba(188,238,9,.45)); }
  .keycap:focus-visible { outline: 2px solid var(--lime); outline-offset: -2px; }

  /* Label chip. Transient, hover/focus only, pointer-events:none so it can
     never intercept a click on the content it passes over. */
  .keycap-label {
    position: absolute; left: calc(100% + 10px); top: 50%;
    transform: translateY(-50%);
    padding: .38rem .6rem;
    background: var(--ink); color: var(--on-dark-strong);
    border: 1px solid var(--lime);
    border-radius: 6px;
    font-family: var(--font-mono); font-size: .7rem; font-weight: 400;
    letter-spacing: .04em; white-space: nowrap;
    opacity: 0; visibility: hidden;
    pointer-events: none;
    transition: opacity .14s ease;
    z-index: 2;
  }
  .keycap:hover .keycap-label,
  .keycap:focus-visible .keycap-label { opacity: 1; visibility: visible; }

  /* One-time settle on load: the mark types itself in, top to bottom.
     Opacity only — no layout involvement, and nothing waits on it. */
  .keyrail-list > li { animation: keycap-settle .3s ease-out backwards; }
  .keyrail-list > li:nth-child(1) { animation-delay: .04s; }
  .keyrail-list > li:nth-child(2) { animation-delay: .08s; }
  .keyrail-list > li:nth-child(3) { animation-delay: .12s; }
  .keyrail-list > li:nth-child(4) { animation-delay: .16s; }
  .keyrail-list > li:nth-child(5) { animation-delay: .20s; }
  .keyrail-list > li:nth-child(6) { animation-delay: .24s; }
  .keyrail-list > li:nth-child(7) { animation-delay: .28s; }
  .keyrail-list > li:nth-child(8) { animation-delay: .32s; }
  @keyframes keycap-settle { from { opacity: 0; } to { opacity: 1; } }}

/* Mid-size laptops (1360-1439): the rail appears, sized to whatever gutter
   the 1180px column leaves and pinned near the viewport edge rather than
   centred in the gutter, which buys ~18px of clearance at 1366.

   The column is deliberately NOT narrowed here. Narrowing it to 1080 was
   tried and reverted: it re-wraps every line, and with the webfont swapping
   in late that pushed CLS from 0.054 to 0.132 — over the 0.1 budget — at
   1350px, which happens to be exactly the width Lighthouse desktop tests.
   Nothing in this band touches layout, so CLS is untouched.

   Below 1360 there is no rail: the gutter cannot hold a cap at any size
   that is still legible. Those widths keep the header. */
@media (min-width: 1360px) and (max-width: 1439.98px) {
  .keyrail {
    --cell-w: min(104px, calc((100vw - 1180px) / 2 - 40px), calc(11vh * 1.32));
    left: 14px;
    transform: translateY(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .keycap, .keycap:hover, .keycap:focus-visible, .keycap:active, .keycap[aria-current] {
    transition: none;
  }
  /* Glows hold at a steady level instead of pulsing. */
  .btn::before, .btn:hover::before, .cta-button::before, .cta-button:hover::before,
  .keyrail::before, .hero-live-dot::before { animation: none; opacity: .5; }
  .keycap-label { transition: none; }
  .keyrail-list > li { animation: none; }}


/* ═══════════════ DBR ESTIMATE CALCULATOR ═══════════════
   Lives on /database-reactivation/ — moved off the homepage in the Client
   Acquisition Engine redesign, where the DBR chapter's job is to say what the
   product is, not to run a model. The arithmetic is frozen: see
   docs/website-v2-design-direction.md and the calculator section of app.js. */
.dbr-calculator {
  background: var(--key); border: 1px solid var(--hairline-dark);
  border-radius: var(--radius); padding: clamp(1.5rem, 3vw, 2.15rem);
  box-shadow: 0 18px 44px rgba(0,0,0,.4);
}
.dbr-calc-title { font-size: 1.3rem; margin-bottom: .4rem; }
.dbr-calc-sub { font-size: .95rem; color: var(--on-dark-muted); margin-bottom: 1.9rem; }

.dbr-calc-input { margin-bottom: 1.6rem; }
.dbr-calc-input label { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-bottom: .65rem; font-size: .95rem; color: var(--on-dark-body); }
.dbr-calc-value { font-weight: 700; font-size: 1.06rem; color: var(--lime); font-variant-numeric: tabular-nums; }

input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 999px; background: #26262A; cursor: pointer; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--lime); border-top: 1px solid rgba(255,255,255,.6);
  box-shadow: 0 3px 0 var(--lime-deep), 0 4px 10px rgba(0,0,0,.45);
  transition: transform .12s ease, box-shadow .12s ease;
}
input[type="range"]::-webkit-slider-thumb:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--lime-deep); }
input[type="range"]::-moz-range-thumb { width: 26px; height: 26px; border-radius: 8px; border: 0; background: var(--lime); box-shadow: 0 3px 0 var(--lime-deep), 0 4px 10px rgba(0,0,0,.45); }
input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: #26262A; }
input[type="range"]:focus-visible { outline: 2px solid var(--lime); outline-offset: 4px; }

.dbr-calc-results { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--hairline-dark); border: 1px solid var(--hairline-dark); border-radius: var(--radius-sm); overflow: hidden; margin: 1.9rem 0 1.1rem; }
.dbr-calc-result { background: var(--ink); padding: 1.3rem 1.2rem; }
.dbr-calc-result-label { font-size: .76rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--on-dark-muted); margin-bottom: .5rem; }
.dbr-calc-result-value { font-size: clamp(1.5rem, 3.4vw, 2rem); font-weight: 800; color: var(--on-dark-strong); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.dbr-calc-result-headline .dbr-calc-result-value { color: var(--lime); }
.dbr-calc-disclaimer { font-size: .85rem; line-height: 1.55; color: var(--on-dark-muted); }

.dbr-calculator-wrap { width: min(1180px, 100% - 3rem); margin: var(--section-y) auto 0; }
@media (max-width: 380px) { .dbr-calc-results { grid-template-columns: 1fr; }}
