/* ── Self-hosted fonts (was Google Fonts CDN) ─────────────────────────────
   Only the weights the marketing page uses: Plus Jakarta Sans 400/500/600/
   700/800, JetBrains Mono 400/500. latin subset, woff2, display:swap. */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/plus-jakarta-sans-v12-latin-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/plus-jakarta-sans-v12-latin-500.woff2") format("woff2");
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/plus-jakarta-sans-v12-latin-600.woff2") format("woff2");
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/plus-jakarta-sans-v12-latin-700.woff2") format("woff2");
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/assets/fonts/plus-jakarta-sans-v12-latin-800.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-v24-latin-regular.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-v24-latin-500.woff2") format("woff2");
}

/* ── Blueprint design tokens ───────────────────────────────────────────── */
:root {
  --paper: #faf9f6;        /* warm off-white body */
  --paper-2: #f2f0ea;      /* panel fill */
  --ink: #16181d;          /* text */
  --ink-2: #5c6066;        /* secondary text */
  --red: #dc2626;          /* eyebrow/accent — matches existing site accent */
  --mint: #3ecf8e;         /* diagram accent */
  --blue: #4a7dff;         /* diagram accent 2 */
  --dark: #0f1115;         /* pipeline interlude bg */
  --dark-ink: #eef0f4;
  --mono: 'JetBrains Mono', ui-monospace, "SFMono-Regular", Menlo, monospace;
  --rule: #d8d5cc;
}

/* ── Blueprint detail system ───────────────────────────────────────────── */
.bp-eyebrow { font-family: var(--mono); font-size: 13px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--red); }
.bp-eyebrow::before { content: "[ "; color: var(--ink-2); }
.bp-eyebrow::after  { content: " ]"; color: var(--ink-2); }
.bp-marks { position: relative; }
.bp-marks::before, .bp-marks::after { content: "+"; position: absolute;
  color: var(--rule); font-family: var(--mono); font-size: 14px; }
.bp-marks::before { top: -8px; left: -8px; } .bp-marks::after { bottom: -8px; right: -8px; }
.bp-grid { background-image: radial-gradient(var(--rule) 1px, transparent 1px);
  background-size: 28px 28px; }

/* ── Screen-reader-only (visually hidden, still in the a11y tree) ──────── */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ── Shared focus-visible treatment ─────────────────────────────────────
   One consistent keyboard-focus ring across every interactive element (nav
   links, CTA/pricing buttons, the finding-card + FAQ <summary> disclosures)
   instead of the inconsistent browser default. Keyboard-only via :focus-visible
   so pointer clicks don't ring. The blue token clears contrast on both the warm
   paper body and the dark interlude / ink CTA bands. The FAQ summary keeps its
   own inset variant (more specific rule below) for its full-width block shape. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* ── Reveal utility (IntersectionObserver / GSAP toggles .in) ────────────
   The opacity:0 start-state is gated on html.js so it only applies when JS is
   present to later add .in. With JS disabled or failed to load the class is
   never set, so .reveal falls through to its default full visibility — a
   content-hiding bug (invisible-forever sections) can't happen. The .js class
   is set by a tiny synchronous inline <head> script, before first paint. */
@media (prefers-reduced-motion: no-preference) {
  html.js .reveal { opacity: 0; transform: translateY(14px);
    transition: opacity .5s ease, transform .5s ease; }
  html.js .reveal.in { opacity: 1; transform: none; }
}

/* ── Diff snippet lines (hero proof panel) ─────────────────────────────── */
.diff-ctx, .diff-add, .diff-del { display: block; }
.diff-ctx { color: #9aa0ab; }
.diff-add { color: #7ee0b0; background: rgba(62, 207, 142, .12); }
.diff-del { color: #ff8f8f; background: rgba(220, 38, 38, .12); }

/* ── Finding chip: a verdict that materializes over a diff/finding ─────── */
.finding-chip {
  font-family: "Plus Jakarta Sans", system-ui, sans-serif;
  font-size: 12.5px; font-weight: 700; line-height: 1.35;
  color: #fff; background: var(--red);
  padding: 8px 12px; border-radius: 8px;
  box-shadow: 0 10px 28px -8px rgba(220, 38, 38, .55);
}

/* ── Hero CTA reassurance note (echoes cta_final's promise) ────────────── */
.hero-cta-note { font-size: 13px; color: var(--ink-2); margin-top: 10px; }

/* ── Oversized proof stat (count-up on scroll into view) ───────────────── */
.stat { text-align: center; }
.stat-num {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(32px, 4.4vw, 50px); letter-spacing: -.02em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.stat-label { font-size: 13px; color: var(--ink-2); margin-top: 6px; }

/* ── Dark interlude (the pipeline keystone) ────────────────────────────────
   Full-bleed tonal shift: the section's own background paints edge-to-edge
   while its content stays inside .wrap. Section-scoped overrides brighten the
   blueprint accents (red eyebrow, bracket punctuation, dot grid) that were
   tuned for the paper body so they clear AA on #0f1115. */
.dark-interlude {
  background-color: var(--dark);
  color: var(--dark-ink);
  padding: 92px 0;
}
.dark-interlude.bp-grid {
  background-image: radial-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px);
}
/* --red on --dark is only ~3.9:1 (fails AA for this small label); lighten it. */
.dark-interlude .bp-eyebrow { color: #f26d6d; }
.dark-interlude .bp-eyebrow::before,
.dark-interlude .bp-eyebrow::after { color: #7c828d; }

.how-head { max-width: 640px; }
.how-head .bp-eyebrow { display: inline-block; margin-bottom: 18px; }
.how-title {
  font-size: clamp(28px, 3.6vw, 42px); font-weight: 800;
  letter-spacing: -.035em; line-height: 1.05; color: var(--dark-ink);
}
.how-sub {
  font-size: 16px; line-height: 1.6; margin-top: 18px;
  color: #b8bdc7; max-width: 560px;
}

/* Inline pipeline diagram: scales fluidly, never overflows its column. */
.pipeline-figure { max-width: 1000px; margin: 52px auto 0; }
.pipeline-svg { width: 100%; height: auto; display: block; overflow: visible; }

/* Five step cards — the pipeline's narrative in text: always in the DOM, so
   crawlers, reduced-motion, and narrow viewports get the complete story
   regardless of the diagram. */
.step-cards {
  list-style: none; padding: 0; margin: 56px 0 0;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 22px;
}
.step-card {
  background: rgba(255, 255, 255, .03);
  border: 1px solid rgba(255, 255, 255, .09);
  border-radius: 12px; padding: 22px 18px;
}
.step-num {
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  color: var(--mint); letter-spacing: .04em;
}
.step-title {
  font-size: 15px; font-weight: 700; color: var(--dark-ink);
  margin: 12px 0 8px;
}
.step-body { font-size: 13px; line-height: 1.55; color: #b8bdc7; }

/* Motion (static-first — the diagram is a complete, already-resolved picture
   without these). The dot rides the shared-engine spine (M215 260 H1000 =
   785 user units) via a transform-only loop; the two struck findings pulse
   via opacity. Both stay at their authored resting state under reduced
   motion. The fork/merge connectors at each end (trigger in, destination
   out) are static — only the shared middle animates. */
@media (prefers-reduced-motion: no-preference) {
  .dark-interlude .p-dot { animation: pipeline-dot 4.5s linear infinite; }
  .dark-interlude .p-strike { animation: strike-pulse 2.6s ease-in-out infinite; }
  /* Cards cascade in as the section enters the viewport. */
  .step-card.reveal:nth-child(1) { transition-delay: 0s; }
  .step-card.reveal:nth-child(2) { transition-delay: .07s; }
  .step-card.reveal:nth-child(3) { transition-delay: .14s; }
  .step-card.reveal:nth-child(4) { transition-delay: .21s; }
  .step-card.reveal:nth-child(5) { transition-delay: .28s; }
}
@keyframes pipeline-dot {
  0%   { transform: translateX(0);     opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { transform: translateX(785px); opacity: 0; }
}
@keyframes strike-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}

@media (max-width: 900px) {
  .dark-interlude { padding: 64px 0; }
  .step-cards { grid-template-columns: repeat(2, 1fr); gap: 18px; }
  .pipeline-figure { margin-top: 40px; }
}
@media (max-width: 560px) {
  .step-cards { grid-template-columns: 1fr; }
}

/* ── Shared light-section head (zero-config, bento) ─────────────────────── */
.section-head { max-width: 680px; }
.section-head .bp-eyebrow { display: inline-block; margin-bottom: 16px; }
.section-title {
  font-size: clamp(26px, 3.4vw, 40px); font-weight: 800;
  letter-spacing: -.035em; line-height: 1.06; color: var(--ink);
}
.section-lead {
  font-size: 16px; line-height: 1.65; margin-top: 18px;
  color: var(--ink-2); max-width: 600px;
}

/* ── Section 2b: Team layer (solo → team bridge) ─────────────────────────
   Sits right after the proof strip, ahead of the how-it-works dark interlude.
   Reuses the shared section-head type scale below; its 4 capability tiles
   (.team-tile/.team-icon/.team-tile-title/.team-tile-line) share every
   declaration with the Section 5 bento tiles — see the grouped selectors
   there — so only the grid's column count is defined here. */
.team-layer { background: var(--paper); padding: 88px 0; }
.team-layer .section-head { margin: 0 auto; max-width: 880px; text-align: center; }
.team-layer .section-title { letter-spacing: -.045em; }
.team-layer .section-lead { margin-left: auto; margin-right: auto; max-width: 600px; }
.team-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 18px; margin-top: 44px;
}

/* ── Section 4: Zero-config moment ───────────────────────────────────────
   Live-activity metaphor: a static "connected" source feeds two lanes of
   small arrival cards. Cards are real, always-in-DOM nodes (never JS-
   inserted/removed) — the animation only ever toggles opacity/transform on
   fixed elements, so the prefers-reduced-motion resting frame is the same
   markup, fully visible. Two lanes run on different-length, unevenly
   staggered loops so they never sync into a mechanical pulse, and each
   loop holds a card fully visible for most of its cycle so at least one
   lane's stack always reads as "populated" while the other refreshes. */
.zeroconf { background: var(--paper); padding: 88px 0; }
/* flex, not grid — a grid "1fr" column stretches .flow-lane wider than its
   220px-max stack, so the gap the eye reads between the source icon and
   lane 1 (the grid gap) and the gap it reads between lane 1 and lane 2
   (grid-cell-width minus content, on both sides) came out very different.
   Flex sizes every child to its content, so a single `gap` value is the
   true on-screen distance between all three columns, not just between
   grid tracks that are wider than what's drawn inside them. */
.zeroconf-flow {
  display: flex; align-items: flex-start; justify-content: center;
  flex-wrap: wrap; gap: 60px; margin-top: 44px;
}
.flow-source {
  display: flex; flex-direction: column; align-items: center;
  max-width: 140px; text-align: center; gap: 14px;
}
/* Fixed-height slot so the connect icon, card stacks, and agent scene all
   share one vertical center regardless of their own icon height or how
   many lines their label below wraps to — top-aligning these equal-height
   slots (see align-items above/below) is what makes the icons line up. */
.flow-icon-area { display: flex; align-items: center; justify-content: center; width: 100%; height: 158px; }
.flow-source-icon { position: relative; width: 80px; height: 80px; }
.flow-source-icon svg:first-child { width: 80px; height: 80px; display: block; }
.flow-connected-badge {
  position: absolute; right: -7px; bottom: -7px; width: 28px; height: 28px;
  background: var(--paper); border-radius: 50%;
}
.flow-source-label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
/* Static dashed stub (same treatment as .agent-link, no motion — this is the
   "it already happened" icon, per the section's design intent) spanning the
   exact 60px .zeroconf-flow gap below, so it reads as "one connect feeds the
   three lanes" without a literal fork. Hidden once that gap disappears at the
   900px breakpoint (.zeroconf-flow goes to flex-direction: column). */
.flow-source .flow-icon-area { position: relative; }
.flow-source .flow-icon-area::after {
  content: ""; position: absolute; top: 50%; right: -60px; width: 60px; height: 2px;
  transform: translateY(-50%);
  background-image: repeating-linear-gradient(90deg, var(--rule) 0 4px, transparent 4px 10px);
}
.flow-lanes { display: flex; align-items: flex-start; gap: 60px; }
.flow-lane { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.flow-stack { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 220px; }
.flow-card {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--rule); border-radius: 10px;
  background: var(--paper-2);
}
.flow-card svg { width: 24px; height: 24px; flex: none; }
.flow-card-failed { border-color: var(--red); }
.flow-card-line { flex: 1; height: 8px; border-radius: 4px; background: var(--rule); opacity: .5; }

/* Scheduled-scans lane: a rising maturity score, not a ring icon — plain
   numerals read clearer than digits crammed into an 18px ring, and the
   card border itself carries the low/mid/high color instead of a shape. */
.flow-card-score {
  font-size: 22px; line-height: 1; font-weight: 800; font-variant-numeric: tabular-nums;
  flex: none; width: 34px; text-align: center;
}
.flow-card-score-low { border-color: var(--red); }
.flow-card-score-low .flow-card-score { color: var(--red); }
.flow-card-score-mid { border-color: #d97706; }
.flow-card-score-mid .flow-card-score { color: #d97706; }
.flow-card-score-high { border-color: var(--mint); }
.flow-card-score-high .flow-card-score { color: var(--mint); }
.flow-lane-label { font-size: 13px; font-weight: 600; color: var(--ink-2); }

/* Agent-fix lane: one composed scene (file — link — agent), not a card
   stack — deliberately reads differently from the other two lanes so this
   capability doesn't blend into the "generic arrival" repetition. The
   issue/fixed paths are two full paths stacked in place (not text), and
   only ever crossfade via opacity, never swapped in the DOM. */
.agent-scene { display: flex; align-items: center; gap: 10px; height: 76px; }
.agent-file, .agent-bot { width: 56px; height: 56px; flex: none; }
.agent-file-fixed { opacity: 0; }
.agent-link { position: relative; width: 46px; height: 2px; flex: none;
  background-image: repeating-linear-gradient(90deg, var(--rule) 0 4px, transparent 4px 10px); }
.agent-dot {
  position: absolute; top: 50%; left: 37px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--mint); transform: translateY(-50%);
}
.zeroconf-tagline {
  margin-top: 32px; text-align: center; font-size: 15px;
  font-style: italic; color: var(--ink-2);
}

/* Motion (static-first — every card's resting/no-preference state is the
   same fully-opaque frame these keyframes animate away from and back to).
   Opacity-only: a translateY here would shift a card out of its fixed
   8px flex gap mid-fade, visibly unevening the spacing between cards
   while it's in transit — the stack must stay evenly spaced at every
   frame, not just at rest. */
@media (prefers-reduced-motion: no-preference) {
  .flow-lane:nth-child(1) .flow-card-a { animation: flow-card-cycle 5.2s ease-in-out infinite; }
  .flow-lane:nth-child(1) .flow-card-b { animation: flow-card-cycle 5.2s ease-in-out infinite; animation-delay: 1.8s; }
  .flow-lane:nth-child(1) .flow-card-c { animation: flow-card-cycle 5.2s ease-in-out infinite; animation-delay: 3.4s; }
  .flow-lane:nth-child(2) .flow-card-a { animation: flow-card-cycle 6.7s ease-in-out infinite; animation-delay: .6s; }
  .flow-lane:nth-child(2) .flow-card-b { animation: flow-card-cycle 6.7s ease-in-out infinite; animation-delay: 2.9s; }
  .flow-lane:nth-child(2) .flow-card-c { animation: flow-card-cycle 6.7s ease-in-out infinite; animation-delay: 4.7s; }
  /* Agent-fix scene: all three pieces share one 4.6s timeline so they stay
     in lockstep — the dot travels agent→file, arrives right as the issue
     line crossfades to the checkmark, and the agent icon pulses on arrival. */
  .agent-dot { animation: agent-dot-travel 4.6s ease-in-out infinite; }
  .agent-file-issue { animation: agent-issue-fade 4.6s ease-in-out infinite; }
  .agent-file-fixed { animation: agent-fixed-fade 4.6s ease-in-out infinite; }
  .agent-bot { animation: agent-bot-pulse 4.6s ease-in-out infinite; }
  /* These loops run indefinitely once revealed (WCAG 2.2.2 Pause/Stop/Hide
     territory past 5s) — hover/focus-within gives a discoverable way to
     freeze the resting frame without a dedicated pause control. */
  .zeroconf:hover :is(.flow-card-a, .flow-card-b, .flow-card-c, .agent-dot, .agent-file-issue, .agent-file-fixed, .agent-bot),
  .zeroconf:focus-within :is(.flow-card-a, .flow-card-b, .flow-card-c, .agent-dot, .agent-file-issue, .agent-file-fixed, .agent-bot) {
    animation-play-state: paused;
  }
}
@keyframes flow-card-cycle {
  0%, 100% { opacity: 0; }
  10%, 88% { opacity: 1; }
}
@keyframes agent-dot-travel {
  0%, 100% { left: 37px; opacity: 0; }
  10%      { opacity: 1; }
  60%      { left: 0; opacity: 1; }
  70%      { opacity: 0; }
}
@keyframes agent-issue-fade {
  0%, 60%  { opacity: 1; }
  70%, 95% { opacity: 0; }
  100%     { opacity: 1; }
}
@keyframes agent-fixed-fade {
  0%, 60%  { opacity: 0; }
  70%, 95% { opacity: 1; }
  100%     { opacity: 0; }
}
@keyframes agent-bot-pulse {
  0%, 60%, 100% { transform: scale(1); }
  70%           { transform: scale(1.12); }
}

/* ── Section 5: Capability bento ───────────────────────────────────────── */
.bento { background: var(--paper-2); padding: 88px 0; }
.bento .section-head { margin: 0 auto; max-width: 560px; text-align: center; }
.bento-outro {
  margin-top: 32px; text-align: center; font-size: 15px;
  font-style: italic; color: var(--ink-2);
}
.bento-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 18px; margin-top: 44px;
}
/* .team-tile/.team-icon (Section 2b) share every declaration below — grouped
   into the same selectors rather than duplicated — so the two sections' tiles
   look and animate identically without a second copy of the rules. */
.bento-tile, .team-tile {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 14px; padding: 24px 22px;
}
.bento-icon, .team-icon {
  width: 44px; height: 44px; border-radius: 10px;
  background: rgba(74, 125, 255, .10);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
}
.bento-icon svg, .team-icon svg { width: 26px; height: 26px; display: block; }
/* Small category eyebrow tag, printed above the tile title. Deliberately
   plain (no pill/background) per the "small non-intrusive label" brief —
   six tiles already have plenty of visual weight (icon + title + line). */
.bento-tag {
  display: block; font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 6px;
}
.bento-tile-title, .team-tile-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.bento-tile-line, .team-tile-line { font-size: 13px; line-height: 1.6; color: var(--ink-2); }
/* One real-finding line per tile (taste call B3) — reads like a finding a
   review actually produced, in the same mono voice as the hero diff mock. */
.bento-example {
  display: block; margin-top: 12px; padding: 8px 10px;
  font-family: var(--mono); font-size: 11.5px; line-height: 1.5;
  color: var(--ink-2); background: var(--paper-2);
  border-left: 2px solid var(--rule); border-radius: 0 6px 6px 0;
}

@media (prefers-reduced-motion: no-preference) {
  .bento-tile, .team-tile { transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
  .bento-tile:hover, .team-tile:hover {
    transform: translateY(-4px); border-color: var(--blue);
    box-shadow: 0 14px 30px -18px rgba(15, 17, 21, .35);
  }
  .bento-icon svg, .team-icon svg { transition: transform .25s ease; }
  .bento-tile:hover .bento-icon svg, .team-tile:hover .team-icon svg { transform: scale(1.14) rotate(-5deg); }
}

@media (max-width: 900px) {
  .team-layer, .zeroconf, .bento { padding: 60px 0; }
  .zeroconf-flow { flex-direction: column; align-items: center; gap: 28px; }
  /* Source stacks above the lanes here, so the horizontal stub pointing at a
     now-nonexistent right-hand gap would float disconnected — drop it. */
  .flow-source .flow-icon-area::after { display: none; }
  .team-grid, .bento-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .team-grid, .bento-grid { grid-template-columns: 1fr; }
  .flow-lanes { flex-direction: column; align-items: center; gap: 28px; }
}

/* ── Finding replay thread (embedded in the integrations right column) ──── */
.pr-thread {
  max-width: 620px; margin: 0;
  display: flex; flex-direction: column; gap: 18px;
}
.pr-comment { display: grid; grid-template-columns: 40px 1fr; gap: 14px; align-items: start; }
.pr-avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.pr-avatar-bot { background: var(--ink); color: var(--paper); font-weight: 800; font-size: 16px; }
.pr-avatar-bot img { width: 80%; height: 80%; display: block; }
/* The visitor's own coding agent, not a human — same mint accent as the
   zeroconf section's agent-bot icon, which this reuses. */
.pr-avatar-agent { background: rgba(62, 207, 142, .12); color: var(--mint); }
.pr-avatar-agent svg { width: 22px; height: 22px; }
.pr-box {
  position: relative; background: var(--paper);
  border: 1px solid var(--rule); border-radius: 10px;
  box-shadow: 0 1px 2px rgba(15, 17, 21, .04);
}
/* GitHub-style pointer triangle aiming back at the avatar */
.pr-box::before {
  content: ""; position: absolute; left: -7px; top: 15px;
  width: 12px; height: 12px; background: var(--paper-2);
  border-left: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  transform: rotate(45deg);
}
.pr-box-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 14px; font-size: 13px;
  background: var(--paper-2); border-bottom: 1px solid var(--rule);
  border-radius: 10px 10px 0 0;
}
.pr-author { font-weight: 700; color: var(--ink); }
.pr-bot {
  font-family: var(--mono); font-size: 10px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-2);
  border: 1px solid var(--rule); border-radius: 20px; padding: 1px 7px;
}
.pr-meta { font-size: 12px; color: var(--ink-2); }
.pr-box-body { padding: 14px; }
.pr-reply-text { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink); }

/* Finding-card replica — real component class names, marketing palette.
   Scoped to .integrations so these shared names never leak beyond this section. */
.integrations .fnd-card {
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--paper); overflow: hidden;
}
.integrations .fnd-card > summary {
  list-style: none; display: flex; align-items: center; flex-wrap: wrap;
  gap: 10px; padding: 12px 14px; cursor: default;
}
.integrations .fnd-card > summary::-webkit-details-marker { display: none; }
.integrations .sev-badge {
  font-family: var(--font); font-size: 10px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
}
.integrations .sev-high { color: #dc2626; }
.integrations .sev-high::before { content: "■"; font-size: 11px; color: #ef4444; }
.integrations .sev-medium { color: #d97706; }
.integrations .sev-medium::before { content: "■"; font-size: 11px; color: #f59e0b; }
.integrations .fnd-title { font-weight: 700; font-size: 14px; color: var(--ink); }
.integrations .fnd-file { font-family: var(--mono); font-size: 11px; color: var(--ink-2); }
.integrations .disclosure-caret { margin-left: auto; color: var(--ink-2); transition: transform .15s ease; }
.integrations .fnd-card[open] > summary .disclosure-caret { transform: rotate(90deg); }
.integrations .finding-detail { padding: 0 14px 14px; font-size: 13px; line-height: 1.55; color: var(--ink-2); }
.integrations .drawer-label { font-size: 11px; font-weight: 600; color: var(--ink); margin: 10px 0 3px; }
.integrations .fnd-line { margin: 0 0 4px; }
.integrations .cite-chip {
  display: inline-block; margin-top: 8px;
  font-family: var(--mono); font-size: 10.5px; color: var(--ink-2);
  background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: 4px; padding: 2px 7px;
}
/* Second beat of the "loop closes" story: appears next to the fixed finding's
   file chip once the human has replied (delayed further still, below). */
.fnd-resolved-tag {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font); font-size: 10px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; color: var(--mint);
}
.fnd-resolved-tag::before { content: "✓"; }
.integrations .pr-box-body .fnd-card + .fnd-card { margin-top: 10px; }

@media (prefers-reduced-motion: no-preference) {
  /* The reply reads as a later beat: it fades in after the bot comment. */
  .pr-reply.reveal { transition-delay: .5s; }
  /* The resolved tag is the third beat: it lands after the reply, closing
     the loop this demo tells (finding lands → human responds → it's fixed). */
  .fnd-resolved-tag.reveal { transition-delay: 1s; }
}

/* ── Section 8: Integrations (GitHub App) ───────────────────────────────── */
.integrations { background: var(--paper); padding: 88px 0; }
.integrations .section-head { margin: 0 auto; text-align: center; }
.integrations-grid {
  display: grid; grid-template-columns: 1fr 1.25fr;
  gap: 40px; margin-top: 44px; align-items: stretch;
}
.int-panel {
  display: flex; flex-direction: column;
  background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: 14px; padding: 28px 26px;
}
/* Pinned to the card's bottom edge regardless of how tall the body copy
   above it runs — the card now stretches to match the PR-thread column's
   height (align-items: stretch, above), so margin-top: auto has real extra
   flex space below the copy to absorb rather than a fixed guessed gap. */
.int-panel .btn { margin-top: auto; width: 100%; }
.int-icon {
  width: 48px; height: 48px; border-radius: 11px; color: var(--ink);
  background: rgba(74, 125, 255, .10);
  display: flex; align-items: center; justify-content: center; margin-bottom: 16px;
}
.int-icon svg { width: 26px; height: 26px; display: block; }
.int-title { font-size: 17px; font-weight: 700; color: var(--ink); margin-bottom: 8px; letter-spacing: -.01em; }
.int-body { font-size: 14px; line-height: 1.6; color: var(--ink-2); }
/* Agent-badge row — codqual reviews the diff itself, so the claim these
   badges make ("reviews every PR the same way") holds regardless of which
   tool authored the code; not a claim of a technical integration with any
   of them. Chip material matches the bento tile chips (Section 5); icons are the
   real brand marks (simple-icons, MIT-licensed) at a single flat tone so
   they sit inside this page's monochrome line-icon system. */
.int-agents { margin-top: 22px; }
.int-agents-label { font-size: 12.5px; color: var(--ink-2); margin-bottom: 10px; }
.int-agent-badges { display: flex; flex-wrap: wrap; gap: 8px; }
.int-agent-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 12px; color: var(--ink-2);
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 20px; padding: 5px 12px;
}
.int-agent-badge svg { width: 13px; height: 13px; flex: none; color: var(--ink); }
.int-agent-badge-you { font-weight: 700; color: var(--ink); }

/* ── Section 9: Maturity (score ring + tilted screenshot) ───────────────── */
.maturity { background: var(--paper-2); padding: 88px 0; }
.maturity .section-head { margin: 0 auto; text-align: center; }
.maturity-showcase {
  display: grid; grid-template-columns: auto 1fr;
  gap: 48px; align-items: center; margin-top: 48px;
}
.ring-wrap { position: relative; width: 200px; height: 200px; flex: none; justify-self: center; }
.score-ring { width: 100%; height: 100%; transform: rotate(-90deg); }
.ring-track { opacity: .5; }
/* Static resting state: ring already at 82% (offset 18 of a normalized 100). */
.ring-progress { stroke-dashoffset: 18; }
.ring-center {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
}
.ring-num {
  font-family: var(--mono); font-size: 40px; font-weight: 700;
  color: var(--ink); letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.ring-label { font-size: 12px; color: var(--ink-2); }
.tilt-panel { margin: 0; }
.tilt-panel img {
  display: block; width: 100%; height: auto; border-radius: 12px;
  border: 1px solid var(--rule);
  box-shadow: 0 30px 60px -30px rgba(15, 17, 21, .4);
  transform: perspective(1200px) rotateX(4deg) rotateY(-6deg);
}

@media (prefers-reduced-motion: no-preference) {
  /* Draw the ring from empty to 82% when it scrolls into view (offset only).
     html.js-gated: without JS the ring keeps its default 82%-filled resting
     state (offset 18) rather than sitting empty waiting for a .in that never comes. */
  html.js .ring-progress { stroke-dashoffset: 100; transition: stroke-dashoffset 1.5s cubic-bezier(.22, 1, .36, 1); }
  .ring-wrap.in .ring-progress { stroke-dashoffset: 18; }
}

@media (max-width: 900px) {
  .integrations, .maturity { padding: 60px 0; }
  .integrations-grid { grid-template-columns: 1fr; }
  /* Single column: int-panel no longer shares a stretched row with int-replay,
     so it's back to its own intrinsic height and margin-top: auto on the CTA
     has no free space to absorb — pin a real gap explicitly instead. */
  .int-panel .btn { margin-top: 24px; }
  .maturity-showcase { grid-template-columns: 1fr; gap: 32px; justify-items: center; }
  /* Drop the 3D tilt on narrow screens: rotateY would risk horizontal overflow. */
  .tilt-panel img { transform: none; }
}

/* ── Section 10a: Pricing ───────────────────────────────────────────────────
   auto-fit grid so the layout is correct whether there are 1 or 2 plan
   cards — a single card centers under the cap; more cards share the row.
   Reuses the bento/tile card language (paper fill, rule border) for one visual
   system; the highlighted plan lifts via a mint accent border + a badge. */
.pricing { background: var(--paper); padding: 88px 0; }
.pricing .section-head { margin: 0 auto; text-align: center; }
.price-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px; margin: 44px auto 0; max-width: 940px; align-items: start;
}
.price-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 14px; padding: 28px 26px;
}
.price-card.featured { border: 1.5px solid var(--mint); background: rgba(62, 207, 142, .05); }
.price-badge {
  position: absolute; top: -11px; left: 26px;
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase; color: var(--paper);
  background: var(--mint); border-radius: 20px; padding: 3px 10px;
}
.price-name { font-size: 15px; font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.price-tag {
  font-family: var(--mono); font-weight: 700; letter-spacing: -.02em;
  font-size: clamp(28px, 3.4vw, 38px); color: var(--ink);
  margin: 14px 0 4px; font-variant-numeric: tabular-nums;
}
/* Unit text sits on its own line below the price figure — not inline after it.
   Inline, the unit text shared line-space with the huge mono price digits, so
   at narrower card widths (3-up pricing grid) it wrapped mid-phrase and
   orphaned a single trailing word (e.g. "Free") on its own line. Block display
   gives it the card's full width to wrap normally, like any other text. */
.price-tag-unit { display: block; font-size: 14px; font-weight: 500; color: var(--ink-2); }
.price-desc { font-size: 13.5px; line-height: 1.6; color: var(--ink-2); margin-bottom: 18px; }
.price-features { list-style: none; margin: 0 0 24px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.price-feature { position: relative; padding-left: 26px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.price-feature::before {
  content: ""; position: absolute; left: 0; top: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: rgba(62, 207, 142, .16);
  /* mint check drawn with a background gradient — no extra markup per item */
}
.price-feature::after {
  content: ""; position: absolute; left: 5px; top: 6px;
  width: 6px; height: 3px; border-left: 1.5px solid var(--mint);
  border-bottom: 1.5px solid var(--mint); transform: rotate(-45deg);
}
.price-card .btn { margin-top: auto; width: 100%; }

/* 4-up pricing grid: widen the cap so four cards sit in a row on desktop and
   reflow to 2×2 / 1-col on narrower viewports via the existing auto-fit.
   align-items:stretch (overriding .price-grid's `start`) makes every card fill
   the row's height so cards read as equal-height and — since .price-card is a
   column flex with `.btn { margin-top:auto }` — their CTAs align on one baseline. */
.price-grid-4 { max-width: 1120px; align-items: stretch; }
.price-annual { font-family: var(--mono); font-size: 12px; color: var(--ink-2); margin: -2px 0 12px; }
.price-tag-sm { font-size: clamp(22px, 2.6vw, 28px); }
/* Enterprise: one full-width card below the grid — pitch on the left, feature
   list on the right; stacks on narrow screens. */
.price-enterprise {
  max-width: 1120px; margin: 20px auto 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 28px 40px; align-items: center;
  background: var(--ink); color: var(--paper);
  border-radius: 14px; padding: 32px 34px;
}
.price-enterprise .price-name { color: var(--paper); }
.price-enterprise .price-tag { color: var(--paper); margin: 10px 0 4px; }
.price-enterprise .price-desc { color: rgba(255,255,255,.72); margin-bottom: 18px; }
.price-enterprise .btn { width: auto; align-self: start; }
.price-features-ent { margin: 0; }
.price-enterprise .price-feature { color: var(--paper); }
.price-enterprise .price-feature::before { background: rgba(62, 207, 142, .28); }
@media (max-width: 720px) {
  .price-enterprise { grid-template-columns: 1fr; gap: 20px; }
}

/* ── Section 10b: Security FAQ ───────────────────────────────────────────────
   Native <details>/<summary> disclosures: accessible and keyboard-operable with
   zero JS, and the same semantic pattern as the finding-card replica. Each
   question is a leaf-text <summary> (data-i18n needs a markup-free node) with a
   CSS caret; the answer text lives in a leaf <p>. This markup also seeds the
   FAQPage ld+json in <head>. */
.security { background: var(--paper-2); padding: 88px 0; }
.security .section-head { margin: 0 auto; text-align: center; }
.faq-list { max-width: 760px; margin: 40px auto 0; display: flex; flex-direction: column; gap: 12px; }
.faq-item {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 12px; overflow: hidden;
}
.faq-item > summary {
  list-style: none; cursor: pointer; position: relative;
  padding: 18px 48px 18px 20px;
  font-size: 15.5px; font-weight: 700; color: var(--ink); line-height: 1.4;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; border-radius: 12px; }
/* Caret: a rotating chevron drawn from two borders, no extra markup. */
.faq-item > summary::after {
  content: ""; position: absolute; right: 22px; top: 22px;
  width: 8px; height: 8px; border-right: 2px solid var(--ink-2);
  border-bottom: 2px solid var(--ink-2); transform: rotate(45deg);
}
.faq-item[open] > summary::after { transform: rotate(-135deg); top: 25px; }
.faq-a { margin: 0; padding: 0 20px 20px; font-size: 14px; line-height: 1.65; color: var(--ink-2); }
@media (prefers-reduced-motion: no-preference) {
  .faq-item > summary::after { transition: transform .18s ease; }
}

/* ── Section 11: Final CTA — edge-to-edge ink band ───────────────────────────
   The closing safety net. An --ink band (distinct from the pipeline's --dark
   interlude) caps the page; the primary button inverts to paper-on-ink for
   contrast against the dark ground. */
.cta-final {
  background: var(--ink); color: var(--paper);
  padding: 96px 0; text-align: center;
}
.cta-final-title {
  font-size: clamp(28px, 3.8vw, 44px); font-weight: 800;
  letter-spacing: -.035em; line-height: 1.06; color: var(--paper);
  max-width: 640px; margin: 0 auto;
}
.cta-final-sub {
  font-size: 16px; line-height: 1.6; color: #b8bdc7;
  max-width: 520px; margin: 18px auto 0;
}
.cta-final .hero-cta { justify-content: center; margin-top: 34px; }
.cta-final .btn-primary { background: var(--paper); color: var(--ink); }
.cta-final .btn-primary:hover { background: #fff; }
.cta-final .btn-ghost { color: var(--paper); border-color: rgba(255, 255, 255, .28); }
.cta-final .btn-ghost:hover { background: rgba(255, 255, 255, .08); }

@media (max-width: 900px) {
  /* Light content sections share the 60px mobile rhythm of their siblings
     (bento/integrations/…); the two dark bands (dark-interlude, cta-final)
     keep a slightly taller 64px for emphasis. */
  .pricing, .security { padding: 60px 0; }
  .cta-final { padding: 64px 0; }
}

/* Paid-plan model line — the one emphasized feature: the capability
   differentiator vs the free tier's open-source model. */
.price-feature-hl { font-weight: 700; }
