/* ============================================================
   Zip AI — DATAVIZ KIT  (dataviz.css)
   Built 2026-08-16 (s163) to close the measured gap in
   design/FUNNEL-REFERENCE-SPEC.md §2: "Data graphics — theirs 3 graphs
   + 6 data screenshots on ONE page · ours 0 sitewide across 18,400 words."

   🔴 THE RULE (spec §7.1): every graphic here must plot a number we
   MEASURED and OWN, and must render its SOURCE + DATE on the graphic.
   A component used without .dv-src filled in is a bug, not a style choice.

   SCOPE / SAFETY
   - Standalone file. Loads AFTER shared.css. Every selector is prefixed
     `.dv-` or lives under `.dv`, so it cannot touch an existing page.
   - Declaration-only until a page opts in by adding the markup.
   - Does NOT define or override any .lit box-shadow
     (feedback_zipai_glow_is_one_shared_file — glow lives in glow3.css).
   - No <details> anywhere: the AI-citation effect is UNMEASURED (FACTS.md).

   MEDIUM CHOICE (deliberate, documented in refs/DATAVIZ-KIT.md)
   - Ring + timeline = inline SVG (they need real geometry).
   - Bars + stat tiles = HTML/CSS (Thai labels wrap natively; SVG <text>
     cannot wrap, and Thai has no word spaces — SVG would clip).
   - Zero raster, zero chart library, zero Higgsfield credits.
   - Thai NEVER baked into a pixel (feedback_nano_banana_thai_text_garbles).

   CLS: every SVG carries a viewBox and is sized width:100%/height:auto,
   so the browser derives an intrinsic ratio and reserves the box. 0.0000.
   ============================================================ */

.dv{
  /* --- palette: bright sky/mint only, never dark
         (feedback_zip_ai_website_bright_not_dark). ONE accent hue. --- */
  --dv-accent  : #0D8A5A;   /* the one green. Do not add a second. */
  --dv-accent-2: #6FC7A3;   /* same hue, lighter step */
  --dv-accent-3: #D6F0E4;   /* mint wash */
  --dv-warn    : #C0392B;   /* the red annotation box ONLY. Never a fill. */
  --dv-neutral : #C9D2CE;   /* "no data / not ours" segments */
  --dv-track   : #EDF3F0;
  --dv-ink     : #17211D;
  --dv-ink-2   : #55635C;
  --dv-ink-3   : #7C8983;
  --dv-paper   : #FFFFFF;

  --dv-r-card  : 10px;      /* = --r3-card  */
  --dv-r-media : 22px;      /* funnel spec §3 figure frame */
  --dv-r-pill  : 999px;
  /* funnel spec §3, the one copyable shadow */
  --dv-shadow  : 0 4px 10px rgba(0,0,0,.04), 0 16px 40px rgba(0,0,0,.08);

  --dv-num     : "Anuphan", system-ui, sans-serif;
  --dv-body    : "IBM Plex Sans Thai", "Sarabun", system-ui, sans-serif;

  font-family: var(--dv-body);
  color: var(--dv-ink);
  font-variant-numeric: lining-nums tabular-nums;
  /* 🔴 width:100% is LOAD-BEARING, added 2026-08-17 — do not drop it.
     `.dv` is a grid item on index/line-oa, and a grid item with only a
     max-width sizes to its CONTENT. The kit therefore inherited its width
     from whatever its widest text happened to be: while type.css was
     wrongly inflating the chrome to 19px, the figure column measured 581px
     by accident. Fixing the type shrank it to 474px, which scaled
     svg.dv-line down until its 11.5-unit date labels rendered at 8.8px and
     tripped ops/visual_qa.py SVG-TEXT-TINY on index.html [desktop].
     The column width must come from the LAYOUT, never from the copy. */
  width: 100%;
  max-width: 581px;                 /* funnel spec §3 see-row figure column */
  margin-inline: auto;
}
.dv *, .dv *::before, .dv *::after{ box-sizing: border-box }

/* ---------- shared chrome: title / source stamp / legend ----------

   🔴 SPECIFICITY, measured 2026-08-17 — do NOT flatten these back to a bare
   single class. The kit's chrome renders as <p>, <figcaption> and <li>, and
   css/type.css ships
       body.type-v2 :is(article,section) p / li / figcaption      = (0,1,3)
   which outranks a bare `.dv-title` (0,1,0). MEASURED on every page that
   loads this file: chart titles rendered 19px/600 #0A0A0A body prose,
   subtitles 19px/600 instead of 13px, source stamps 19px/600 instead of
   11.5px, and legend rows 18.5px instead of 13.5px — the figures had no
   internal hierarchy at all. Two pages had already patched it page-locally,
   which is the wrong altitude for a shared kit.

   The `.dv ` prefix lands these at (0,2,x). That is ONE class more than
   type.css and the minimum that wins — no !important, and nothing here
   outruns a page's own deliberate restyle (case-mdcc re-points the kit at
   the anchor's type language at (0,3,1) and still wins, by design).
   Every kit part is inside a `.dv` root — verified, 0 orphans across all
   5 pages — so the prefix changes what WINS, never what MATCHES.

   font-weight is declared explicitly on .dv-sub / .dv-src / .dv-legend li
   because the kit had relied on plain inheritance there, and inheritance is
   exactly what type.css overrode. Raising specificity alone left the source
   stamps bold at 600 — that is the half-fix line-oa.html shipped.
   ------------------------------------------------------------------ */
.dv .dv-title{
  font-family: var(--dv-num); font-weight: 700;
  font-size: clamp(15px, 1.1vw + 12px, 18px); line-height: 1.35;
  margin: 0 0 2px;
}
.dv .dv-sub{
  font-size: 13px; font-weight: 400; line-height: 1.5;
  color: var(--dv-ink-2); margin: 0 0 14px;
}

/* 🔴 REQUIRED on every component. Renders the provenance ON the graphic. */
.dv .dv-src{
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  margin: 12px 0 0; padding-top: 10px;
  border-top: 1px solid var(--dv-track);
  font-size: 11.5px; font-weight: 400; line-height: 1.5; color: var(--dv-ink-3);
}
.dv-src b{ color: var(--dv-ink-2); font-weight: 600 }
.dv-src time{ white-space: nowrap }

.dv-legend{ list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 7px }
/* (0,2,1) — beats type.css `body.type-v2 :is(article,section) li` (0,1,3),
   which was rendering these 13.5px legend rows at 18.5px/600 with a 12px
   bottom margin the grid `gap` was already supplying. See the note above. */
.dv .dv-legend li{
  display: grid; grid-template-columns: 12px 1fr auto; gap: 9px;
  align-items: center; font-size: 13.5px; font-weight: 400; line-height: 1.45;
  margin: 0;
}
.dv-legend i{ width: 12px; height: 12px; border-radius: 3px; background: var(--dv-c, var(--dv-neutral)) }
.dv-legend b{ font-family: var(--dv-num); font-weight: 700; font-size: 14px }

/* ============================================================
   1. RING / DONUT  —  .dv-ring
   Data goes in as CSS custom props on each segment:
     --dv-pct  : share of the whole, 0–100
     --dv-from : where the segment starts, 0–100 (running total)
     --dv-c    : segment colour
   Circumference is fixed at r=54 → 2πr = 339.292; 1% = 3.39292.
   ============================================================ */
.dv-ring-wrap{ display: grid; grid-template-columns: minmax(0,190px) 1fr; gap: 22px; align-items: center }
.dv-ring{ width: 100%; height: auto; display: block }        /* viewBox 0 0 140 140 */
.dv-ring circle{ fill: none; stroke-width: 17; stroke-linecap: butt }
.dv-ring .dv-ring-track{ stroke: var(--dv-track) }
.dv-ring .dv-ring-seg{
  stroke: var(--dv-c, var(--dv-neutral));
  stroke-dasharray: calc(var(--dv-pct) * 3.39292) 339.292;
  stroke-dashoffset: calc(var(--dv-from) * -3.39292);
  transform: rotate(-90deg); transform-origin: 70px 70px;
}
.dv-ring .dv-ring-big{
  font-family: var(--dv-num); font-weight: 700; font-size: 33px;
  fill: var(--dv-ink); text-anchor: middle;
}
.dv-ring .dv-ring-cap{ font-size: 10.5px; fill: var(--dv-ink-3); text-anchor: middle }

/* ============================================================
   2. HORIZONTAL BAR COMPARISON  —  .dv-bars
   One .dv-bar per row; the value is PRINTED on the bar, never in a tooltip.
     --dv-pct : bar length 0–100 (share of the row's own max)
     --dv-c   : bar colour (omit → accent)
   .is-before uses the neutral tone; .is-after uses the accent.
   ============================================================ */
.dv-bars{ display: grid; gap: 15px; margin: 0 }
.dv-bar-grp{ display: grid; gap: 6px }
.dv-bar-lab{ font-size: 13px; line-height: 1.4; color: var(--dv-ink-2) }
.dv-bar-lab b{ color: var(--dv-ink); font-weight: 600 }
.dv-bar{
  position: relative; height: 30px; border-radius: 5px;
  background: var(--dv-track); overflow: hidden;
}
.dv-bar span{
  position: absolute; inset: 0 auto 0 0; width: calc(var(--dv-pct) * 1%);
  min-width: 62px;                       /* keeps the printed value legible */
  background: var(--dv-c, var(--dv-accent));
  border-radius: 5px;
  display: flex; align-items: center; justify-content: flex-end;
  padding-inline: 10px;
  font-family: var(--dv-num); font-weight: 700; font-size: 13px; color: #fff;
}
.dv-bar.is-before span{ background: var(--dv-neutral); color: var(--dv-ink) }
.dv-bar-delta{
  font-family: var(--dv-num); font-weight: 700; font-size: 13px;
  color: var(--dv-accent); white-space: nowrap;
}

/* ============================================================
   3. TIMELINE / TREND LINE  —  .dv-line   (viewBox 0 0 560 220)
   Points are hand-placed in the markup (x,y in viewBox units) so the
   series stays readable in the HTML source — crawlable, no JS.
   The LAST point gets .is-end and is drawn bigger + labelled.
   ============================================================ */
.dv-line{ width: 100%; height: auto; display: block; overflow: visible }
.dv-line .dv-axis{ stroke: var(--dv-track); stroke-width: 1 }
.dv-line .dv-plot{ fill: none; stroke: var(--dv-accent); stroke-width: 2.5;
                   stroke-linejoin: round; stroke-linecap: round }
.dv-line .dv-area{ fill: var(--dv-accent-3); opacity: .55 }
.dv-line .dv-dot{ fill: #fff; stroke: var(--dv-accent); stroke-width: 2.5 }
.dv-line .dv-dot.is-end{ fill: var(--dv-accent); r: 7 }
.dv-line .dv-pt-val{ font-family: var(--dv-num); font-weight: 700; font-size: 15px;
                     fill: var(--dv-ink); text-anchor: middle }
.dv-line .dv-pt-val.is-end{ fill: var(--dv-accent); font-size: 17px }
.dv-line .dv-pt-date{ font-size: 11.5px; fill: var(--dv-ink-3); text-anchor: middle }

/* ============================================================
   4. STAT TILE ROW  —  .dv-tiles   (2–4 tiles)
   Every tile carries its own dated source line. A tile without one
   is stat-art and must not ship.
   ============================================================ */
.dv-tiles{
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.dv-tile{
  background: var(--dv-paper); border: 1px solid var(--dv-track);
  border-radius: var(--dv-r-card); padding: 16px 15px 14px;
}
.dv-tile-num{
  font-family: var(--dv-num); font-weight: 700; line-height: 1.05;
  font-size: clamp(28px, 3.4vw + 16px, 40px); color: var(--dv-accent);
  display: block; letter-spacing: -.01em;
}
.dv-tile-num small{ font-size: .5em; font-weight: 700; margin-left: 2px }
.dv-tile-lab{ display: block; margin-top: 7px; font-size: 13.5px; line-height: 1.45; color: var(--dv-ink) }
.dv-tile-src{ display: block; margin-top: 8px; font-size: 11px; line-height: 1.45; color: var(--dv-ink-3) }

/* ============================================================
   5. ANNOTATED-FIGURE FRAME  —  .dv-fig
   The wrapper that turns a real screenshot into proof (funnel spec §0/§3).
   The WRAPPER owns radius + shadow; the <img> owns neither.
     .dv-box  → the red annotation box. Position with
                --dv-x/--dv-y/--dv-w/--dv-h, all in % of the image.
     .dv-cap  → Thai caption pill. HTML text, NEVER burned into the pixel
                (spec §6b — this is where we BEAT the reference).
   ============================================================ */
.dv-fig{
  position: relative; display: block; background: var(--dv-paper);
  border: 1px solid var(--dv-track); border-radius: var(--dv-r-media);
  box-shadow: var(--dv-shadow); overflow: hidden;
  transition: transform .18s ease, border-color .18s ease;
}
a.dv-fig:hover, a.dv-fig:focus-visible{ transform: translateY(-2px); border-color: var(--dv-accent) }
.dv-fig img{ display: block; width: 100%; height: auto }   /* width+height attrs required on the tag */
.dv-fig-inner{ position: relative; display: block }
.dv-box{
  position: absolute;
  left: calc(var(--dv-x) * 1%);  top:    calc(var(--dv-y) * 1%);
  width: calc(var(--dv-w) * 1%); height: calc(var(--dv-h) * 1%);
  border: 3px solid var(--dv-warn); border-radius: 4px;
  box-shadow: 0 0 0 2px rgba(255,255,255,.55);
  pointer-events: none;
}
.dv-box-lab{
  position: absolute; left: 0; bottom: 100%; margin-bottom: 5px;
  background: var(--dv-warn); color: #fff; white-space: nowrap;
  font-family: var(--dv-num); font-weight: 700; font-size: 11.5px;
  padding: 3px 9px; border-radius: var(--dv-r-pill);
}
.dv-cap{
  position: absolute; left: 12px; right: 12px; bottom: 12px;
  background: rgba(23,33,29,.86); color: #fff;
  font-size: 12.5px; line-height: 1.45; padding: 8px 13px;
  border-radius: var(--dv-r-pill);
}
.dv-cap b{ font-family: var(--dv-num); font-weight: 700 }
.dv-fig-src{                      /* source branded in-frame, funnel spec §0 */
  position: absolute; right: 12px; top: 12px;
  background: rgba(255,255,255,.92); color: var(--dv-ink-2);
  font-size: 11px; padding: 4px 10px; border-radius: var(--dv-r-pill);
}

/* ============================================================
   MOBILE — must read at 390px (measured target: 334px content column)
   ============================================================ */
@media (max-width: 640px){
  .dv-ring-wrap{ grid-template-columns: 1fr; gap: 16px; justify-items: center }
  .dv-ring{ max-width: 210px }
  .dv-legend{ width: 100% }
  .dv-tiles{ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)) }
  .dv-cap{ left: 8px; right: 8px; bottom: 8px; font-size: 11.5px; padding: 7px 11px }
  .dv-box-lab{ font-size: 10.5px; padding: 2px 7px }
  .dv-bar{ height: 28px }

  /* 🔴 SVG <text> scales WITH the viewBox. At 390px the .dv-line box renders
     ~298px wide = scale 0.53, so 11.5 units read as 6px — measured, unusable.
     Sizes below are in viewBox units and land at ~11-16px on screen. */
  .dv-line .dv-pt-date{ font-size: 21px }
  .dv-line .dv-pt-val { font-size: 28px }
  .dv-line .dv-pt-val.is-end{ font-size: 31px }
}

/* ============================================================
   MOTION — the only animation in this file, and it is opt-in.
   Add .dv-anim to a component to fade its ink in on load. No
   IntersectionObserver: the reference has none, and ours is what
   produced the 400px blank bands (s162). Spec §5.
   ============================================================ */
@keyframes dv-fade{ from{ opacity: 0 } to{ opacity: 1 } }
.dv-anim .dv-ring-seg, .dv-anim .dv-bar span, .dv-anim .dv-plot{
  animation: dv-fade .35s ease both;
}
@media (prefers-reduced-motion: reduce){
  .dv *, .dv *::before, .dv *::after{
    animation: none !important; transition: none !important;
  }
  a.dv-fig:hover{ transform: none }
}

/* ============================================================
   PRINT — these graphics go into client decks and leave-behinds.
   ============================================================ */
@media print{
  .dv{ max-width: 100%; break-inside: avoid }
  .dv-fig{ box-shadow: none; border: 1px solid #999 }
  .dv-tile{ border: 1px solid #999 }
  .dv-cap{ background: #17211D !important; -webkit-print-color-adjust: exact; print-color-adjust: exact }
  .dv-bar span, .dv-ring-seg, .dv-box{ -webkit-print-color-adjust: exact; print-color-adjust: exact }
  /* raised in step with the screen rule above — a bare `.dv-src` (0,1,0) would
     now lose to `.dv .dv-src` (0,2,0) and print grey instead of black. */
  .dv .dv-src, .dv .dv-tile-src{ color: #000 }
}
