/* ============================================================================
   chapter-demos.css — the ten animated mini-dashboards in the homepage
   "Ten chapters" timeline. They replace ten static screenshots.

   Each chapter gets its OWN layout on purpose: a reader scrolling ten slots
   that all look alike stops reading. What they share is the surface, the type
   scale and the motion language, so ten different shapes still read as one
   product.

   Tokens only, per the design constitution. Every demo is inert markup plus
   data attributes; js/chapter-demos.js animates on first scroll into view and
   does nothing at all under prefers-reduced-motion (values are shown at their
   final state instead, so the content is never lost).
   ========================================================================== */

.cd {
  --cd-pad: 20px;
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 320px;
  padding: var(--cd-pad);
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--panel);
  color: var(--text);
  font-family: var(--font);
  overflow: hidden;
}

[data-theme="dark"] .cd {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 60%), var(--panel);
}

/* Shared chrome ---------------------------------------------------------- */

.cd-head {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 auto;
}

.cd-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--green);
  flex: none;
  box-shadow: 0 0 0 0 var(--green);
}

.cd-title {
  font-family: var(--font-heading);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.cd-sub {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.cd-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 13px 14px;
}

.cd-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

/* A number that counts up. The final value lives in data-count so the DOM is
   always truthful even if the script never runs. */
.cd-num {
  font-family: var(--font-heading);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.cd-delta {
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cd-delta.up { color: var(--green); }
.cd-delta.down { color: var(--red); }

/* Reveal: children lift in on view, staggered by --i. */
.cd [data-cd-in] {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.5s cubic-bezier(0.2, 0.7, 0.3, 1), transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.cd.is-live [data-cd-in] {
  opacity: 1;
  transform: none;
}

/* Loop rewind. The demos replay for as long as they are on screen, and the
   rewind between runs has to be invisible: without this the card would play
   itself backwards — bars draining, the line un-drawing — which reads as a
   glitch rather than a restart. While this class is on, every animated
   property snaps to its start with no transition and no animation.
   Each selector is deliberately more specific than the rule it overrides, so
   none of this needs !important. */
.cd.is-rewind [data-cd-in],
.cd.is-rewind .cd-engine .fill,
.cd.is-rewind .cd-h2h .bar i,
.cd.is-rewind .cd-bars .col i,
.cd.is-rewind .cd-ad .track i,
.cd.is-rewind .cd-spark path.area,
.cd.is-rewind .cd-spark path.line,
.cd.is-rewind .cd-spark .head,
.cd.is-rewind .cd-ring .val,
.cd.is-rewind .cd-donut .seg {
  transition: none;
  animation: none;
}

/* The chat demo's typing dots are hidden rather than removed, so the sequence
   can be played again. */
.cd-typing.is-off { display: none; }

/* --- 1. AI Visibility: three engines, presence bars, citation counter ----- */

.cd-engines {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.cd-engine {
  display: grid;
  grid-template-columns: 76px 1fr 38px;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 600;
}

.cd-engine .track {
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--bg-2);
  overflow: hidden;
}

.cd-engine .fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pill);
  background: var(--gradient);
  transition: width 1.1s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 130ms);
}
.cd.is-live .cd-engine .fill { width: var(--w, 0%); }

.cd-engine .pct {
  text-align: right;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- 2. Domain Overview: authority ring + traffic sparkline --------------- */

.cd-split {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
  align-items: center;
}

.cd-ring {
  width: 92px;
  height: 92px;
  flex: none;
}
.cd-ring circle { fill: none; stroke-width: 8; }
.cd-ring .bg { stroke: var(--bg-2); }
.cd-ring .val {
  stroke: url(#cdRingGrad);
  stroke-linecap: round;
  /* Drawn from 12 o'clock; dashoffset is set by the script from data-pct. */
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  transition: stroke-dashoffset 1.4s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.cd-ring text {
  fill: var(--text);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 22px;
}

.cd-spark { width: 100%; height: 54px; display: block; }
.cd-spark path.line {
  fill: none;
  stroke: var(--purple);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Length is measured and set by the script so the line draws itself. */
  transition: stroke-dashoffset 1.6s cubic-bezier(0.3, 0.6, 0.2, 1);
}
.cd-spark path.area { fill: url(#cdAreaGrad); opacity: 0; transition: opacity 0.9s ease 0.5s; }
.cd.is-live .cd-spark path.area { opacity: 1; }

.cd-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }

/* --- 3. Competitor Battle: head-to-head rows ----------------------------- */

.cd-vs {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-heading);
  font-size: 12px;
  font-weight: 800;
}
.cd-vs .who { flex: 1; text-align: center; }
.cd-vs .who.b { color: var(--magenta-bright); }
.cd-vs .badge {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 3px 8px;
}

.cd-h2h { display: flex; flex-direction: column; gap: 9px; }
.cd-h2h .row { display: grid; grid-template-columns: 1fr 88px 1fr; align-items: center; gap: 8px; }
.cd-h2h .metric {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.cd-h2h .bar { height: 9px; border-radius: var(--radius-pill); background: var(--bg-2); overflow: hidden; display: flex; }
.cd-h2h .bar.l { justify-content: flex-end; }
.cd-h2h .bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pill);
  transition: width 1.05s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.cd-h2h .bar.l i { background: var(--purple); }
.cd-h2h .bar.r i { background: var(--magenta); }
.cd.is-live .cd-h2h .bar i { width: var(--w, 0%); }

/* --- 4. Content Engine: pipeline of topic cards -------------------------- */

.cd-pipe { display: flex; flex-direction: column; gap: 8px; }
.cd-topic {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 10px 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.cd-topic .t { font-size: 12.5px; font-weight: 600; }
.cd-topic .m { font-family: var(--font-mono); font-size: 10px; color: var(--muted); margin-top: 3px; }
.cd-score {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  font-family: var(--font-heading);
  font-size: 13px;
  font-weight: 800;
  background: rgba(20, 184, 127, 0.14);
  color: var(--green);
}
.cd-score.warm { background: rgba(245, 158, 11, 0.14); color: var(--amber); }

/* --- 5. Backlink Profile: live rows streaming in ------------------------- */

.cd-stream { display: flex; flex-direction: column; gap: 7px; }
.cd-link {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
}
.cd-link .dr {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--purple-bright);
  text-align: center;
}
.cd-link .host { font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cd-link .tag {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--green);
  background: rgba(20, 184, 127, 0.13);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
}

/* --- 6. Brand Performance: sentiment donut + legend ---------------------- */

.cd-donut { width: 104px; height: 104px; flex: none; }
.cd-donut circle { fill: none; stroke-width: 13; }
.cd-donut .seg {
  stroke-linecap: butt;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  transition: stroke-dasharray 1.3s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 160ms);
}
.cd-legend { display: flex; flex-direction: column; gap: 8px; }
.cd-legend .row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.cd-legend i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.cd-legend .v { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--muted); }

/* --- 7. Social Analytics: grouped bar chart ----------------------------- */

.cd-bars { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; align-items: end; height: 116px; }
.cd-bars .col { display: flex; flex-direction: column; justify-content: flex-end; gap: 3px; height: 100%; }
.cd-bars .col i {
  display: block;
  width: 100%;
  height: 0;
  border-radius: var(--radius-xs) var(--radius-xs) 0 0;
  transition: height 0.9s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 60ms);
}
.cd-bars .col i.a { background: var(--purple); }
.cd-bars .col i.b { background: var(--magenta); opacity: 0.75; }
.cd.is-live .cd-bars .col i { height: var(--h, 0%); }
.cd-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; font-family: var(--font-mono); font-size: 9px; color: var(--muted); text-align: center; }

/* --- 8. PPC Research: spend rows + cost tiles ---------------------------- */

.cd-ads { display: flex; flex-direction: column; gap: 8px; }
.cd-ad {
  display: grid;
  grid-template-columns: 1fr 64px;
  gap: 10px;
  align-items: center;
  font-size: 12px;
}
.cd-ad .kw { font-weight: 600; }
.cd-ad .track { height: 8px; border-radius: var(--radius-pill); background: var(--bg-2); overflow: hidden; margin-top: 5px; }
.cd-ad .track i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--cyan), var(--purple));
  transition: width 1s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 100ms);
}
.cd.is-live .cd-ad .track i { width: var(--w, 0%); }
.cd-ad .cost { text-align: right; font-family: var(--font-mono); font-size: 12px; font-weight: 700; }

/* --- 9. Analyst AI: chat with a typed answer ---------------------------- */

.cd-chat { display: flex; flex-direction: column; gap: 10px; }
.cd-msg { max-width: 88%; padding: 10px 13px; border-radius: var(--radius); font-size: 12.5px; line-height: 1.5; }
.cd-msg.you { align-self: flex-end; background: var(--gradient); color: var(--on-brand); border-bottom-right-radius: var(--radius-xs); }
.cd-msg.ai { align-self: flex-start; background: var(--card); border: 1px solid var(--border); border-bottom-left-radius: var(--radius-xs); }
.cd-typing { display: inline-flex; gap: 4px; padding: 2px 0; }
.cd-typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); animation: cdBlink 1.2s infinite; }
.cd-typing i:nth-child(2) { animation-delay: 0.18s; }
.cd-typing i:nth-child(3) { animation-delay: 0.36s; }
@keyframes cdBlink { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }
/* The caret only shows while the script is mid-type. */
.cd-type.is-typing::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  vertical-align: -2px;
  margin-left: 2px;
  background: var(--purple);
  animation: cdCaret 0.9s steps(1) infinite;
}
@keyframes cdCaret { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }

/* --- 10. Blog Studio: a post moving through the publishing pipeline ------
   The only demo shaped as a sequence rather than a measurement: a title being
   written, its status advancing, the languages it lands in, then the totals.
   Everything is scoped under .cd-blog — the nine demos above depend on the bare
   .cd-* rules, so none of those are touched. Its own breakpoints live at the
   bottom of this block rather than in the shared media queries, for the same
   reason.
   ------------------------------------------------------------------------- */

/* Smaller than the 26px default: three tiles share a row under a demo that
   already carries four rows above them. */
.cd-blog .cd-num { font-size: 19px; }

.cd-blog-editor {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
}

.cd-blog-elabel {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
  flex: none;
}

.cd-blog-editor .cd-type {
  font-family: var(--font-heading);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--text);
}

.cd-blog-pipe {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.cd-blog-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--card);
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  white-space: nowrap;
}
.cd-blog-step svg { width: 12px; height: 12px; flex: none; }
/* The end of the pipeline is the only state that gets a colour. */
.cd-blog-step.is-done { color: var(--green); border-color: var(--green); }

.cd-blog-arrow { width: 13px; height: 13px; flex: none; color: var(--muted); }

.cd-blog-sched {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  font-size: 11.5px;
}
.cd-blog-sched > svg { width: 14px; height: 14px; flex: none; color: var(--purple); }
.cd-blog-when { font-weight: 700; color: var(--text-2); white-space: nowrap; }
.cd-blog-schnote { color: var(--muted); }

.cd-blog-langs {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

.cd-blog-lang {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--bg-2);
  color: var(--text-2);
}
/* The source language, so the other five read as translations of something. */
.cd-blog-lang.on {
  background: var(--gradient);
  border-color: transparent;
  color: var(--on-brand);
}

.cd-blog-more {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
}

@media (max-width: 1180px) {
  .cd-blog-editor .cd-type { font-size: 12.5px; }
  .cd-blog-step { padding: 5px 9px; font-size: 10.5px; }
  .cd-blog-pipe { gap: 6px; }
}

@media (max-width: 560px) {
  .cd-blog .cd-num { font-size: 17px; }
  .cd-blog-editor { gap: 8px; padding: 10px 11px; align-items: flex-start; flex-direction: column; }
  .cd-blog-editor .cd-type { font-size: 12.5px; }
  .cd-blog-sched { gap: 7px; font-size: 11px; }
  .cd-blog-lang { padding: 3px 7px; }
}

/* Very narrow: the arrows and the schedule note are decoration around text
   that already says the same thing, so they go rather than shrink. */
@media (max-width: 400px) {
  .cd-blog-arrow { display: none; }
  .cd-blog-schnote { display: none; }
  .cd-blog-step { font-size: 10px; }
}

/* Motion off: show every end state immediately, animate nothing. ---------- */

@media (prefers-reduced-motion: reduce) {
  .cd [data-cd-in] { opacity: 1; transform: none; transition: none; }
  .cd .fill, .cd-h2h .bar i, .cd-ad .track i { width: var(--w, 0%); transition: none; }
  .cd-bars .col i { height: var(--h, 0%); transition: none; }
  .cd-ring .val, .cd-donut .seg, .cd-spark path.line { transition: none; }
  .cd-spark path.area { opacity: 1; transition: none; }
  .cd-typing i, .cd-type.is-typing::after { animation: none; }
}

@media (max-width: 700px) {
  .cd { --cd-pad: 15px; min-height: 280px; }
  .cd-num { font-size: 22px; }
  .cd-ring { width: 76px; height: 76px; }
  .cd-donut { width: 86px; height: 86px; }
}

/* ============================================================================
   Container override + responsive.

   .ch-video is locked to aspect-ratio 16/10 because it was built to frame a
   cropped screenshot. A demo is real content with a real minimum height: at a
   340px mobile column that ratio yields a 212px box, and the demo clips. So
   where a frame CONTAINS a demo, the ratio is dropped and the content sizes
   itself. :has() carries this; the min-heights below are the floor either way.

   The hover zoom/pan built for images is also neutralised — panning a live
   dashboard on mouse-move reads as a bug, not an affordance.
   ========================================================================== */

.ch-video.has-demo {
  aspect-ratio: auto;
}

.ch-video.has-demo .vframe,
.ch-video.has-demo .vinner {
  position: relative;
  inset: auto;
  height: auto;
  transform: none !important;
}

.ch-video.has-demo .vinner { overflow: visible; }

/* ---- Desktop (default above) ------------------------------------------- */

/* ---- Tablet: the timeline is still two columns but narrower, so the demos
   lose a little padding and the densest grids drop a column. ------------- */
@media (max-width: 1180px) {
  .cd { --cd-pad: 17px; min-height: 300px; }
  .cd-num { font-size: 23px; }
  .cd-engine { grid-template-columns: 66px 1fr 34px; }
  .cd-h2h .row { grid-template-columns: 1fr 74px 1fr; }
}

/* ---- The timeline collapses to one column here, so a demo gets the FULL
   width rather than half. It can afford to breathe again. ---------------- */
@media (max-width: 980px) {
  .cd { --cd-pad: 20px; min-height: 320px; }
  .cd-num { font-size: 27px; }
  .cd-engine { grid-template-columns: 84px 1fr 40px; }
  .cd-h2h .row { grid-template-columns: 1fr 92px 1fr; }
  .cd-bars { height: 128px; }
}

/* ---- Mobile: single column, tighter type, and the three-up KPI rows go to
   two so the numbers stay legible instead of shrinking to fit. ----------- */
@media (max-width: 560px) {
  .cd { --cd-pad: 14px; min-height: 0; gap: 11px; }
  .cd-title { font-size: 12px; }
  .cd-sub { font-size: 9px; }
  .cd-num { font-size: 21px; }
  .cd-kpis { grid-template-columns: repeat(2, 1fr); }
  /* A 3-up grid with an odd count leaves a gap; let the last tile span. */
  .cd-kpis > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  .cd-split { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .cd-ring { width: 84px; height: 84px; }
  .cd-donut { width: 92px; height: 92px; }
  .cd-legend { width: 100%; }
  .cd-engine { grid-template-columns: 68px 1fr 34px; font-size: 11.5px; }
  .cd-h2h .row { grid-template-columns: 1fr 64px 1fr; }
  .cd-h2h .metric { font-size: 8.5px; }
  .cd-bars { height: 96px; gap: 5px; }
  .cd-days { gap: 5px; }
  .cd-msg { max-width: 94%; font-size: 12px; }
  .cd-topic { padding: 9px 10px; }
  .cd-topic .t { font-size: 12px; }
  .cd-link { grid-template-columns: 24px 1fr auto; padding: 8px 10px; }
  .cd-link .host { font-size: 11.5px; }
}

/* ---- Very narrow: drop what is decorative rather than shrinking it. ---- */
@media (max-width: 400px) {
  .cd-sub { display: none; }
  .cd-link .tag { display: none; }
  .cd-days { font-size: 8px; }
  .cd-h2h .row { grid-template-columns: 1fr 52px 1fr; }
}

/* ============================================================================
   Depth layer. Everything here is decorative: it sits behind the content,
   never carries meaning, and is switched off entirely under reduced motion.
   ========================================================================== */

/* A faint grid drawn once and reused, so the surface is not flat panel colour.
   Masked to fade out downward so it never competes with the content. */
.cd::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--border) 1px, transparent 1px),
    linear-gradient(to bottom, var(--border) 1px, transparent 1px);
  background-size: 34px 34px;
  opacity: 0.5;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 72%);
  mask-image: linear-gradient(to bottom, #000 0%, transparent 72%);
}

/* Brand wash in the top corner, matching the homepage's own gradients. */
.cd::after {
  content: "";
  position: absolute;
  top: -30%;
  right: -18%;
  width: 62%;
  height: 72%;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(124, 92, 252, 0.16), transparent 68%);
  filter: blur(6px);
}

.cd > * { position: relative; z-index: 1; }

/* The live dot actually pulses, which is what sells "live". */
.cd.is-live .cd-dot { animation: cdPulse 2.4s ease-out infinite; }
@keyframes cdPulse {
  0% { box-shadow: 0 0 0 0 rgba(20, 184, 127, 0.55); }
  70% { box-shadow: 0 0 0 7px rgba(20, 184, 127, 0); }
  100% { box-shadow: 0 0 0 0 rgba(20, 184, 127, 0); }
}

/* A highlight sweeps each bar once as it finishes filling. */
.cd-engine .fill,
.cd-ad .track i { position: relative; overflow: hidden; }
.cd.is-live .cd-engine .fill::after,
.cd.is-live .cd-ad .track i::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.45), transparent);
  transform: translateX(-100%);
  animation: cdSweep 1.5s ease-out 0.7s;
}
@keyframes cdSweep { to { transform: translateX(100%); } }

/* The ring and the drawn line get a soft glow so they read as lit, not printed. */
.cd-ring .val { filter: drop-shadow(0 0 5px rgba(124, 92, 252, 0.5)); }
.cd-spark path.line { filter: drop-shadow(0 1px 4px rgba(124, 92, 252, 0.4)); }

/* A dot rides the end of the traffic line once it has drawn. */
.cd-spark .head {
  fill: var(--magenta-bright);
  opacity: 0;
  transition: opacity 0.4s ease 1.5s;
}
.cd.is-live .cd-spark .head { opacity: 1; animation: cdBeat 2s ease-in-out 1.8s infinite; }
@keyframes cdBeat { 0%, 100% { r: 3.2; } 50% { r: 4.6; } }

/* Cards lift very slightly on hover — enough to feel alive, not enough to move
   the layout. */
.cd-card, .cd-topic, .cd-link { transition: transform 0.25s ease, border-color 0.25s ease; }
.cd-card:hover, .cd-topic:hover, .cd-link:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
}

@media (prefers-reduced-motion: reduce) {
  .cd::before, .cd::after { display: none; }
  .cd.is-live .cd-dot,
  .cd.is-live .cd-engine .fill::after,
  .cd.is-live .cd-ad .track i::after,
  .cd.is-live .cd-spark .head { animation: none; }
  .cd-spark .head { opacity: 1; transition: none; }
  .cd-card:hover, .cd-topic:hover, .cd-link:hover { transform: none; }
}

/* ---- Sizing correction -----------------------------------------------------
   .cd was height:100% + overflow:hidden, inherited from the image it replaced.
   An image can be cropped; a dashboard cannot — measured at three widths, every
   demo clipped its last row by ~22px. A demo is content, so it sizes to content
   and the frame follows it. min-height keeps short demos from looking thin.
   -------------------------------------------------------------------------- */
.cd {
  height: auto;
  overflow: visible;
  border-radius: 6px;
  border: 1px solid var(--border);
}
.ch-video.has-demo .vinner { height: auto; overflow: visible; }
.ch-video.has-demo .vframe { position: relative; height: auto; }
/* The grid and wash are clipped to the card, not the page. */
.cd::before, .cd::after { border-radius: inherit; }
.cd { isolation: isolate; }

/* ============================================================================
   THE HERO — homepage "Product preview" section.

   One dense overview dashboard replacing images/ai-visibility-dashboard.jpg.
   Same animation system as the nine timeline demos above (class .cd, driven by
   js/chapter-demos.js), so it needs no JS of its own: [data-cd-in] staggers,
   .cd-num counts, .cd-ring draws, .cd-spark draws, .cd-engine bars fill.

   Everything here is scoped under .cd-hero. The nine timeline demos depend on
   the bare .cd-* rules, so none of those are edited — where the hero needs a
   different shape it overrides under its own class.

   IT FOLLOWS THE PAGE THEME. .product-inner used to be a dark browser mock in
   both themes; it now goes light with the homepage, so everything in here is
   plain themed tokens and needs no per-theme overrides. If a colour looks wrong
   in one theme, fix the token choice rather than adding a [data-theme] patch —
   that is the habit the old dark-mock frame forced, and it is what made this
   section awkward to change.
   ========================================================================== */

/* The wrapper held an <img> and sized itself from it. With real content inside
   there is nothing to size from, so the demo drives its own height. */
.product-shot-wrap.has-demo {
  display: block;
  width: 100%;
}

.cd-hero {
  /* .cd is built to fill a fixed video frame; the hero is free-standing. */
  height: auto;
  min-height: 0;
  padding: 0;
  gap: 14px;
  /* .product-inner already provides the surface and its own glow. A second
     grid + brand wash on top of it just muddies the panel edges. */
  isolation: auto;
}
/* Doubled class (0,2,0) to out-specify two base .cd rules the hero must not
   inherit: [data-theme="dark"] .cd, which would repaint --panel over the
   frame's own gradient, and the sizing-correction block, which gives every
   timeline demo its own edge. The timeline demos need that edge because they
   sit inside a video frame; the hero IS the screen, so a second rounded box
   drawn just inside the browser chrome reads as a stray outline. */
.cd.cd-hero {
  background: none;
  border: 0;
  border-radius: 0;
}
.cd-hero::before,
.cd-hero::after { display: none; }

.cd-hero-head { gap: 10px; }
.cd-hero-head .cd-title { font-size: 14px; }

.cd-hero-host {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
}

/* ---- KPI row ----------------------------------------------------------- */

.cd-hero-kpis {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}

.cd-hero-kpi {
  display: grid;
  gap: 5px;
  align-content: start;
}
.cd-hero-kpi .cd-num { font-size: 28px; }

/* ---- chart + ring ------------------------------------------------------ */

.cd-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.72fr) minmax(0, 1fr);
  gap: 12px;
}

.cd-hero-panel { padding: 15px 16px; }

.cd-hero-ptop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.cd-hero-range,
.cd-hero-tag {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.cd-hero-chartcard { display: flex; flex-direction: column; }

/* Its own gradient id rather than the timeline's #cdAreaGrad: that def lives
   inside a demo further down the page, and the hero must not break if that
   demo is ever changed or removed. */
.cd-hero-spark { height: 128px; flex: 1 1 auto; }
.cd-hero-spark path.area { fill: url(#cdHeroArea); }
.cd-hero-spark path.line { stroke-width: 2.6; }
/* Chart furniture. A rising series leaves the top of the plot empty, which
   reads as a half-drawn card rather than a chart; three baselines give it a
   floor to sit on. Static on purpose — it is the grid, not the data. */
.cd-hero-spark path.grid { fill: none; stroke: var(--border); stroke-width: 1; }

.cd-hero-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 9px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.cd-hero-ringcard {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 9px;
  text-align: center;
}
.cd-hero-ring { width: 116px; height: 116px; }
.cd-hero-ring .val { stroke: url(#cdHeroRing); }
.cd-hero-ring text { font-size: 26px; }

.cd-hero-note {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-3);
  max-width: 22ch;
}

/* ---- AI visibility — the panel this whole section exists to show -------- */

.cd-hero-ai {
  border-color: var(--purple);
  background:
    linear-gradient(180deg, rgba(124, 92, 252, 0.09), transparent 62%),
    var(--card);
}

.cd-hero-engines {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 11px 26px;
}
.cd-hero-ai .cd-engine { grid-template-columns: 92px minmax(0, 1fr) 40px; }

/* ---- the loop strip ---------------------------------------------------- */

.cd-hero-loop {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 11px 15px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--bg-2);
}

.cd-hero-looplab {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--purple-bright);
  white-space: nowrap;
}

.cd-hero-steps {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
}

.cd-hero-step {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
}
.cd-hero-step svg { width: 14px; height: 14px; flex: none; color: var(--muted); }
.cd-hero-step.is-done { color: var(--green-bright); }
.cd-hero-step.is-done svg { color: var(--green-bright); }

.cd-hero-arrow { width: 14px; height: 14px; flex: none; color: var(--muted-soft); }

.cd-hero-auto {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--on-brand);
  background: var(--purple);
  border-radius: var(--radius-pill);
  padding: 4px 11px;
  white-space: nowrap;
}

/* ---- Responsive. The dashboard recomposes; it does not just shrink. ----- */

@media (max-width: 1180px) {
  .cd-hero-kpi .cd-num { font-size: 24px; }
  .cd-hero-ring { width: 104px; height: 104px; }
  .cd-hero-spark { height: 116px; }
  .cd-hero-ai .cd-engine { grid-template-columns: 84px minmax(0, 1fr) 38px; }
  .cd-hero-engines { gap: 10px 18px; }
}

/* Still two columns here — a 104px ring plus one line of note fits a ~250px
   column comfortably. Stacking this early left the ring card as a small dial
   against a wall of empty panel, which looked broken rather than roomy. Only
   the ratio tightens. */
@media (max-width: 980px) {
  .cd-hero-grid { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); }
  .cd-hero-note { max-width: 26ch; }
}

/* Two-up KPIs so the numbers stay full size instead of shrinking to fit, and
   the engines go single-column so a 46% bar is still a readable bar. The
   chart/ring split finally stacks, and the ring turns on its side so the card
   stays one compact band instead of a tall centred column. */
@media (max-width: 560px) {
  .cd-hero { gap: 11px; }
  .cd-hero-grid { grid-template-columns: minmax(0, 1fr); }
  .cd-hero-ringcard {
    grid-template-columns: auto minmax(0, 1fr);
    justify-items: start;
    text-align: left;
    align-items: center;
    gap: 5px 14px;
  }
  .cd-hero-ringcard .cd-label { grid-column: 2; align-self: end; }
  .cd-hero-ringcard .cd-ring { grid-row: 1 / 3; }
  .cd-hero-note { grid-column: 2; align-self: start; max-width: none; }
  .cd-hero-kpis { grid-template-columns: repeat(2, 1fr); gap: 9px; }
  .cd-hero-kpi { padding: 11px 12px; }
  .cd-hero-kpi .cd-num { font-size: 21px; }
  .cd-hero-panel { padding: 13px; }
  .cd-hero-engines { grid-template-columns: minmax(0, 1fr); gap: 9px; }
  .cd-hero-ai .cd-engine { grid-template-columns: 80px minmax(0, 1fr) 36px; font-size: 11.5px; }
  .cd-hero-spark { height: 96px; }
  /* Stretched to a phone's aspect this dot is an ellipse, not a dot. */
  .cd-hero-spark .head { display: none; }
  .cd-hero-ring { width: 88px; height: 88px; }
  .cd-hero-ring text { font-size: 24px; }
  .cd-hero-loop { gap: 10px; padding: 10px 12px; }
  .cd-hero-steps { gap: 7px; }
  .cd-hero-step { font-size: 11px; white-space: normal; }
}

/* Very narrow: drop decoration rather than shrink it, per the file's rule. */
@media (max-width: 400px) {
  .cd-hero-head .cd-sub { display: none; }
  .cd-hero-range { display: none; }
  .cd-hero-axis { font-size: 8.5px; }
  .cd-hero-arrow { display: none; }
  /* With the steps stacked, a row-aligned label centres itself against three
     lines of text and reads as floating. The whole strip becomes a column. */
  .cd-hero-loop { flex-direction: column; align-items: flex-start; gap: 9px; }
  .cd-hero-steps { flex-direction: column; align-items: flex-start; gap: 6px; }
}
