/* site/styles.css — the DEFERRED half of the stylesheet.
 *
 * WHY THE CSS IS SPLIT. AC-8.3 wants LCP < 2.5s on simulated 4G, and a single
 * blocking <link> in <head> costs a whole round trip before the first pixel.
 * So each page inlines the rules its above-the-fold region needs (header, hero,
 * the two CTAs, the problem lines) and loads this file non-blocking. Everything
 * below the fold lives here. The trade is a small flash of unstyled below-fold
 * content on a cold cache, which is invisible at 390x844 because it is all
 * off-screen when the swap lands.
 *
 * WHY THE TOKENS ARE DEFINED IN BOTH PLACES. The :root block below repeats the
 * inline one. It has to: this file may load before, after, or instead of (print
 * -> all swap failing) the inline block, and a colour with only one definition
 * in a file that may not have arrived yet is an unstyled page. Identical values
 * mean the duplication is harmless; changing one without the other is the bug.
 *
 * DIMENSION CODES AND ITEM IDS: none here, none anywhere in site/. Hard rule 1.
 * This file carries no score labels at all — the static pages show the Harmony
 * Score trend only, and the app reads dimension labels from the API.
 */

:root {
  /* THE PawPaw PALETTE (the style guide in docs/design/, §1). The --pp-* values are
   * copied from web/src/styles.css's :root, because the site does not load the
   * app bundle; the site's own token names below are re-pointed at them, so
   * every rule in this file keeps reading --ink, --accent, --warm and so on.
   * Keep these values, the dark block below, and each page's inline critical
   * block in step with the app's tokens — change one, change all of them.
   *
   * --ink-3 keeps its AA history: it WAS #7d8892 and failed 4.5:1 on every
   * ground it sat on (measured in Chrome at 390x844 on 2026-09-14; the
   * contrast sweep in e2e/a11y.spec.js re-measures it). Every user of it is
   * small text: .eyebrow, .hero-cap, .card-kind, .moment-date, .legal, the
   * trend's <th>, and .chart-axis (AC-8.6's "readable at 360px"). #6b635b is
   * 5.52:1 on --bg, 5.90:1 on --card, 5.20:1 on --bg-2, 5.31:1 on peach. */
  --pp-page: #faf7f2;
  --pp-surface: #ffffff;
  --pp-surface-2: #f5f0e8;
  --pp-sage: #e4ede4;
  --pp-peach: #fdf1e8;
  --pp-lavender: #ecebf6;
  --pp-ink: #2a2521;           /* 14.2:1 on page */
  --pp-ink-2: #5e5750;         /* 6.6:1 page, 5.9:1 sage */
  --pp-sage-ink: #3d6a4b;      /* 5.2:1 on sage */
  --pp-lavender-ink: #4b4277;  /* 7.6:1 on lavender */
  --pp-orange: #c4592a;        /* LARGE figures and callout borders only */
  --pp-orange-ink: #a8471b;    /* orange at text size — 5.3:1 on peach */
  --pp-charcoal: #302a26;      /* hero band and table header */
  --pp-charcoal-ink: #ffffff;  /* 14.1:1 */
  --pp-charcoal-soft: #d9d1c7; /* 9.4:1 */
  --pp-charcoal-accent: #f5a26f; /* 6.9:1 */
  --pp-charcoal-line: #4a423c;  /* edges inside the charcoal hero */
  /* The action colour ON charcoal. --pp-green (#3a6b52) is 1.9:1 against the
   * charcoal hero, so a button there would be a label with no edge; the dark
   * theme's green is 7.2:1 on it and carries its own dark ink (8.5:1). */
  --pp-hero-green: #82c6ae;
  --pp-hero-green-ink: #12201b;
  --pp-green: #3a6b52;         /* 6.2:1 with white ink, 5.8:1 on page */
  --pp-green-ink: #ffffff;
  --pp-hairline: #eadfd3;
  --pp-line-strong: #948878;   /* an OPERABLE control's edge — 3.25:1 on page */
  --pp-radius: 10px;
  --pp-radius-sm: 6px;
  --pp-radius-pill: 999px;
  --pp-eyebrow-size: .72rem;
  --pp-eyebrow-weight: 700;
  --pp-eyebrow-track: .12em;

  --ink: var(--pp-ink);
  --ink-2: var(--pp-ink-2);
  --ink-3: #6b635b;
  --bg: var(--pp-page);
  --bg-2: var(--pp-surface-2);
  --card: var(--pp-surface);
  --line: var(--pp-hairline);
  --line-2: var(--pp-line-strong);
  --accent: var(--pp-green);
  --accent-ink: var(--pp-green-ink);
  --accent-soft: var(--pp-sage);
  --warm: var(--pp-orange-ink);
  --warm-soft: var(--pp-peach);
  /* The ink that sits ON --warm, and it has to be a token because --warm
   * INVERTS between the themes: a dark rust in light, a light orange in dark.
   * Two places fill a shape with --warm and put type on top of it
   * (.annotations b, .chart-mark-num), and a literal #ffffff there measured
   * 2.17:1 in dark — the trend chart's annotation numerals were unreadable
   * (Chrome, 2026-09-14, /demo). White on #a8471b is 5.86:1; #241a0e on
   * #f59560 is 7.61:1. */
  --warm-ink: #ffffff;
  --shade: rgba(42, 37, 33, 0.08);
  --radius: var(--pp-radius);
}

@media (prefers-color-scheme: dark) {
  :root {
    --pp-page: #16140f;
    --pp-surface: #221f1a;
    --pp-surface-2: #2a2620;
    --pp-sage: #1f2b22;
    --pp-peach: #33241a;
    --pp-lavender: #262338;
    --pp-ink: #f3eee6;
    --pp-ink-2: #bdb4a7;
    --pp-sage-ink: #afd4b8;
    --pp-lavender-ink: #cfc6ef;
    --pp-orange: #f59560;
    --pp-orange-ink: #f59560;
    --pp-charcoal: #0d0b09;
    --pp-charcoal-ink: #f3eee6;
    --pp-charcoal-soft: #bdb4a7;
    --pp-charcoal-accent: #f59560;
    --pp-charcoal-line: #3a342c;
    --pp-green: #82c6ae;
    --pp-green-ink: #12201b;
    --pp-hairline: #3a342c;
    --pp-line-strong: #787260;
    --ink-3: #9c9385;          /* 6.07:1 on --bg, 4.96:1 on --bg-2 */
    --warm-ink: #241a0e;
    --shade: rgba(0, 0, 0, 0.4);
  }
}

/* ------------------------------------------------------------- tap targets */
/* MEASURED IN CHROME AT 390x844, 2026-09-14, BEFORE THESE RULES: the header
 * navigation links rendered 22.3px tall, the three footer links 17px, and the
 * trend table's <summary> 21.3px. WCAG 2.5.8 (AA) puts the floor at 24x24 CSS
 * px and F§1.3's primary target is a phone held in one hand, so the product's
 * own floor is 44px — web/src/styles.css sets --tap: 44px for exactly this
 * reason and site/ had no equivalent.
 *
 * PADDING, NOT HEIGHT. An anchor is inline; `height` does nothing to it and
 * `min-height` needs a display change that would break the header's flex row
 * and the footer's wrap. Vertical padding grows the hit box on an inline box
 * and leaves the layout alone — the header .wrap is already min-height 52px,
 * so 44px of link fits inside it without moving anything.
 *
 * NOT COVERED, DELIBERATELY: links inside a sentence (.prose, .annotations,
 * .chart-caption). WCAG 2.5.8 exempts a target "in a sentence or block of
 * text" because padding there would visibly break the line rhythm, and the
 * exemption exists precisely so prose does not have to be re-typeset. */
.site-header .brand,
.site-header nav a,
.footer-nav a {
  display: inline-block;
  padding-block: 11px;
}

/* No rule is needed on the header row itself: `.site-header .wrap` in each
 * page's critical block carries min-height:52px and NO vertical padding, so a
 * 44px link fits inside it and AC-8.1's fold does not move. Measured after
 * this change at 390x844: the row is still 52px on all three pages. */

.data-table summary {
  /* 21.3px of line box plus 12px each side. `display` is left alone on
   * purpose: `display:flex` on a <summary> removes Chrome's disclosure
   * triangle, which is the only thing that says the row can be opened. */
  padding-block: 12px;
}

/* --------------------------------------------- NO WORD MAY WIDEN THE PAGE */
/* MEASURED IN CHROME, 2026-09-14, /demo at a 360px viewport: the
 * document scrolled sideways by 60px. e2e/a11y.spec.js asserts it cannot
 * ("lays out at 360, 390, 768 and 1280 with no horizontal scroll"), and the
 * walk that found it named a single leaf:
 *
 *   h3.stack-gap  scrollWidth 402  clientWidth 324  overflow-wrap: normal
 *   "The {NUMBER_WORDS[len(S['sections'])].lower()} parts of her story — ..."
 *
 * TWO SEPARATE DEFECTS, and this rule fixes one of them. The string is an
 * unrendered template placeholder that leaked into authored copy — that is a
 * CONTENT bug in site/demo.html, it is reported and not fixed here
 * (another lane owns that file), and it will be corrected to prose.
 *
 * The LAYOUT defect outlives it. A 44-character token with no break
 * opportunity is wider than 324px at this type size, `overflow-wrap: normal`
 * refuses to break inside a word, so the heading's content box grows, the
 * growth propagates to <body>, and the WHOLE PAGE scrolls sideways — every
 * section, not just the heading. That is the failure mode a phone user meets
 * as "the page drifts left when I scroll" and it is why the assertion is on
 * documentElement.scrollWidth rather than on one element's box.
 *
 * The placeholder is not the only string that can do it. A pet's name typed
 * without spaces, a URL pasted into an annotation, a long hyphen-free German
 * or Tamil compound, a hashtag — authored content is written by people and one
 * of them will eventually paste a link. Breaking inside an over-long word is
 * always better than shipping a sideways-scrolling page: the word stays
 * readable on two lines, and nothing else on the page moves.
 *
 * `anywhere` and not `break-word`: they differ in exactly the case that
 * matters here. Both break an over-long word, but `break-word` does not let
 * the break shrink the element's min-content contribution, so an h3 inside a
 * grid or flex track keeps its 402px intrinsic width and the track — and the
 * page — is still too wide. `anywhere` does, which is what makes the
 * documentElement assertion go green. The cost is that it may also break a
 * word when a narrower line would have been merely ugly, which at 360px is
 * the right trade.
 *
 * Scoped to the containers that hold AUTHORED TEXT, not to `*`: a blanket
 * rule would also license breaking inside the nav and the CTA labels, where a
 * "Take the free / Quiz" split across two lines is a worse bug than the one
 * being fixed.
 *
 * ONE LIMITATION, the same one the --ink-2 comment above carries: this is the
 * DEFERRED sheet, so on a cold cache the first paint is still the sideways
 * one and the page settles when the swap lands. Measured after the swap,
 * which is what a reader sees and what openStatic() in e2e/a11y.spec.js waits
 * for. Closing it properly means adding the rule to each page's inline
 * critical block, in files this lane does not own. */
h1, h2, h3, h4,
.prose p, .prose li,
.card-name, .card-note, .moment-title, .moment-meta,
.annotations li, .pending-box li, .lede {
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- sections */

/* padding-BLOCK: .section also carries .wrap, and a `padding: 40px 0`
 * shorthand at equal specificity zeroes .wrap's 18px side gutter — the
 * headline and the CTAs ended up flush against the edge of a 390px screen. */
.section {
  padding-block: 40px;
  border-top: 1px solid var(--line);
}

.section:first-of-type { border-top: 0; }

.section > h2 {
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.25;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
}

.section > .lede {
  color: var(--ink-2);
  margin: 0 0 20px;
  max-width: 62ch;
}

/* THE REPORT'S SECTION BAND (the style guide's .pp-band): the section's own
 * existing label, set as a full-width sage strip with a centred uppercase
 * label. The box it draws is sized in each page's critical block too, with the
 * same values, because on /demo and /behaviourist the first eyebrow is inside
 * the initial viewport (see the CLS note there). `max-width: none` opts out of
 * the 66ch floor on `main p`: the band spans the column, as it does in the
 * report, and its label is far under 60 characters so no measure applies. */
.eyebrow {
  font-size: var(--pp-eyebrow-size);
  font-weight: var(--pp-eyebrow-weight);
  letter-spacing: var(--pp-eyebrow-track);
  line-height: 1.4;
  text-transform: uppercase;
  text-align: center;
  color: var(--pp-sage-ink);
  background: var(--pp-sage);
  border-radius: var(--pp-radius-sm);
  padding: 8px 16px;
  max-width: none;
  margin: 0 0 14px;
}

.prose p { max-width: 66ch; margin: 0 0 1em; }
.prose p:last-child { margin-bottom: 0; }

/* READABLE MEASURE, AND IT IS A CLAIM ABOUT THE WIDE END OF F§1.3.
 *
 * MEASURED 2026-09-13 at 1280x800, before this rule existed: sixteen distinct
 * element shapes across the three pages ran between 122 and 157 characters per
 * line — `.chart-caption` at 157, `ol.steps li` at 133, `ul.recognise li` at
 * 132, `.pull p` at 122. `.wrap` is 1120px and only `.prose p` and
 * `.section > .lede` carried a cap, so every caption, numbered step, note and
 * pull-quote took the full column. 45-90 characters is the readable range; 157
 * is roughly twice a line anyone tracks by eye.
 *
 * `:where()` CONTRIBUTES NO SPECIFICITY, so this is a FLOOR that every existing
 * class rule still overrides: `.section > .lede` keeps its 62ch and anything
 * that deliberately wants more can say so with one class. It is written as a
 * floor rather than as a list of the sixteen offenders because the seventeenth
 * caption is the one a list would miss — and e2e/width.spec.js measures the
 * widest paragraph on the page at the AMBIENT viewport, so a future element
 * that escapes it fails there rather than shipping.
 *
 * NOTHING CHANGES BELOW ~1024px. 66ch is wider than the content box at 390px
 * (354px against ~443px), which is why the mobile project cannot see this rule
 * at all and why it is the wide end of the range that needed it. */
:where(main) :is(p, li) { max-width: 66ch; }

.pull {
  margin: 26px 0;
  /* The box carries the measure for its own paragraph (see `.pull p` below,
     which opts out of the cap so the quote fills the box it is given). The
     same 66ch as everything else: `ch` scales with the element's own font
     size, so one number is one measure at every size. Measured 122
     characters per line before this. */
  max-width: 66ch;
  padding: 18px 20px;
  /* The report's orange-bordered callout (.pp-callout): peach ground, one
     1px orange edge all round, small radius. */
  border: 1px solid var(--pp-orange);
  background: var(--pp-peach);
  border-radius: var(--pp-radius-sm);
  color: var(--ink);
  font-size: 1.08rem;
  font-weight: 600;
  line-height: 1.45;
}

.pull p { margin: 0; max-width: none; }

/* ------------------------------------------------- the illustrative marker */
/* THIS MARKER'S JOB REVERSED ON 2026-09-19, and the class name is older than
 * its meaning. It used to mark copy that was NOT YET sourced from a real dog's
 * real history, awaiting a named human's sign-off. The owner retired that dog
 * from the product (owner-decisions §1.4, option B), so the demo page is
 * illustrative throughout and the marker now says so: Rio is invented, and
 * every badge is there to keep that fact on screen rather than in a footnote.
 *
 * Either way it is the same rule underneath — an invented detail that does not
 * announce itself would quietly undermine every true thing next to it. */

.pending {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--pp-orange-ink);
  background: var(--pp-peach);
  border: 1px solid var(--pp-orange);
  border-radius: var(--pp-radius-pill);
  padding: 3px 9px;
  vertical-align: middle;
}

.pending-box {
  border: 1px dashed var(--line-2);
  background: var(--pp-peach);
  border-radius: var(--radius);
  padding: 16px 18px;
  color: var(--ink-2);
  font-size: 0.94rem;
}

.pending-box > :first-child { margin-top: 0; }
.pending-box > :last-child { margin-bottom: 0; }
.pending-box ul { margin: 10px 0 0; padding-left: 20px; }
.pending-box li { margin: 4px 0; }

/* ------------------------------------------------------------ entity cards */
/* These deliberately match the app's own entity card: name, the kind/subtype
 * line, how they get on, and the note. The aspiration has to be legible as
 * "this is what my dog's World will look like" (F§8.2 item 2), which only
 * works if the visual language is the same one the app uses. */

.group { margin: 0 0 26px; }
/* A group label is the report's eyebrow: small, green, letter-spaced. */
.group h3 {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: var(--pp-eyebrow-track);
  text-transform: uppercase;
  color: var(--pp-sage-ink);
  margin: 0 0 10px;
}

.cards {
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 980px) {
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

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

.card-name {
  font-weight: 700;
  font-size: 1.02rem;
  margin: 0 0 2px;
}

.card-kind {
  color: var(--ink-3);
  font-size: 0.82rem;
  margin: 0 0 10px;
}

.card-note {
  margin: 10px 0 0;
  color: var(--ink-2);
  font-size: 0.92rem;
  line-height: 1.5;
}

/* how_they_get_on, rendered as a word and never as a colour alone: a colour
 * chip with no text is unreadable to a colour-blind parent and meaningless in
 * a screen reader. */
.getson {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 600;
  border-radius: var(--pp-radius-sm);
  padding: 2px 9px;
  border: 1px solid var(--line);
  color: var(--ink-2);
  background: var(--bg-2);
}

/* The report's "at a glance" tiles: sage for the good news, peach for the
   second group. Each keeps its word — the colour is never the only signal. */
.getson[data-q="great"] { color: var(--pp-sage-ink); border-color: var(--pp-sage); background: var(--pp-sage); }
.getson[data-q="tense"] { color: var(--pp-orange-ink); border-color: var(--pp-peach); background: var(--pp-peach); }

/* ----------------------------------------------------------------- moments */

.moment {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.moment + .moment { margin-top: 14px; }

.moment-body { padding: 14px 16px; }
.moment-date { color: var(--ink-3); font-size: 0.8rem; margin: 0 0 4px; }
.moment-title { font-size: 1.05rem; margin: 0 0 8px; }
.moment-meta { color: var(--ink-2); font-size: 0.86rem; margin: 10px 0 0; }

/* Every media slot reserves its box from the markup (width/height or
 * aspect-ratio) so a late-arriving photo cannot shift the text under it.
 * Layout shift is both a Lighthouse penalty (AC-8.3) and the thing that makes
 * a reader lose their place mid-sentence. */
.media-slot {
  aspect-ratio: 3 / 2;
  width: 100%;
  display: grid;
  place-items: center;
  background: var(--bg-2);
  border-bottom: 1px solid var(--line);
  color: var(--ink-3);
  font-size: 0.82rem;
  text-align: center;
  padding: 12px;
}

/* ------------------------------------------------------------------- chart */
/* Inline SVG, no charting library (PLAN-BUILD.md D7: this is the only chart in
 * the product). The colours come from the same custom properties as the rest
 * of the page, which is how AC-8.6's "identical in light and dark" is met —
 * there is one chart, not a light one and a dark one. */

.chart-wrap {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 12px 10px;
}

.chart {
  display: block;
  width: 100%;
  height: auto;
  /* The viewBox is 340 units wide — roughly a 360px phone minus the gutters —
   * so at the narrowest supported width the chart renders close to 1:1 and the
   * in-SVG type stays at its true size. AC-8.6 is readable at 360px. */
  max-width: 560px;
  margin: 0 auto;
}

/* 10 SVG units, not 9. The viewBox is 340 wide and at a 360px viewport the
 * chart lays out at ~310px after the page gutter and this card's own padding,
 * so everything inside is scaled to about 0.91 — 9 units rendered at 8.2px,
 * which is under the size an axis can be read at on a phone. AC-8.6 is
 * "readable at 360px", so the in-SVG type is sized for the SCALED result and
 * the card gives back four pixels of padding at that width (below). */
.chart-axis { fill: var(--ink-3); font-size: 10px; font-family: inherit; }
.chart-grid { stroke: var(--line); stroke-width: 1; }
.chart-line { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { fill: var(--card); stroke: var(--accent); stroke-width: 2; }
.chart-mark { fill: var(--warm); }
/* --warm-ink, never a literal white: this numeral is painted on top of a
 * --warm dot and --warm inverts between the themes (see the token).
 *
 * 10px, NOT 9px, for the same reason the axis is 10px — and measured the same
 * way. At a 360px viewport this chart lays out at 306px against a 340-unit
 * viewBox, a scale of 0.9, so a 9px numeral rendered at 8.1px on glass. These
 * four numerals are the only thing tying a marker on the line to its entry in
 * the .annotations list beside it, and 8.1px is under the size that tie can be
 * read at on a phone held at arm's length. 10px renders at 9px, the same as
 * every other label in the chart. AC-8.6 is "readable at 360px", and
 * e2e/a11y.spec.js measures the RENDERED size rather than this declaration.
 *
 * It still fits: the marker is r="7", so 14 viewBox units across, and the
 * cap-height of a 10px digit is about 7. Each numeral's own y is cy+3, which
 * was centred for 9px and now sits about half a unit high — 0.45px at this
 * scale, which is under the width of the circle's own antialiasing. */
.chart-mark-num { fill: var(--warm-ink); font-size: 10px; font-weight: 700; font-family: inherit; text-anchor: middle; }
/* NO text-anchor here. A CSS declaration beats an SVG presentation attribute,
 * so `text-anchor: middle` in this class silently overrode the per-label
 * `text-anchor="end"` and centred the last value on the right edge of the
 * viewBox — "76" rendered as "6". Each label sets its own anchor. */
.chart-value { fill: var(--ink); font-size: 10px; font-weight: 700; font-family: inherit; }

@media (max-width: 420px) {
  /* Four pixels of card padding each side, handed back to the chart. Paid for
   * by the page gutter it sits inside, which is 18px and is not negotiable. */
  .chart-wrap { padding: 12px 8px 10px; }
}

.chart-caption {
  margin: 10px 2px 0;
  font-size: 0.84rem;
  color: var(--ink-2);
  line-height: 1.5;
}

/* The annotations live in an ordered list BESIDE the chart rather than as text
 * inside the SVG. Four labels inside a 340-unit viewBox collide at 360px, and
 * SVG text cannot wrap. A numbered list is readable at every width, and it is
 * the only form a screen reader can follow. */
.annotations {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

@media (min-width: 720px) {
  .annotations { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.annotations li {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: 10px;
  align-items: start;
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--ink-2);
}

.annotations b {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--warm);
  /* Not #ffffff. These numerals tie each annotation to a marker in the SVG, so
   * an unreadable one breaks the chart's only legend — and #ffffff measured
   * 2.17:1 on the dark-mode --warm. */
  color: var(--warm-ink);
  font-size: 0.78rem;
}

.annotations strong { color: var(--ink); }

/* The data table behind the chart. An SVG polyline is not readable to a screen
 * reader and is not indexable text; <details> keeps the numbers in the DOM for
 * both without spending fold space. */
.data-table { margin: 14px 0 0; font-size: 0.86rem; }
.data-table summary { cursor: pointer; color: var(--accent); font-weight: 600; }
/* The report's table (.pp-table): a charcoal header row over hairline-ruled
   rows on a white ground. */
.data-table table { border-collapse: collapse; margin: 12px 0 0; width: 100%; max-width: 420px;
  background: var(--card); border: 1px solid var(--line); }
.data-table th, .data-table td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--line); }
.data-table thead th { background: var(--pp-charcoal); color: var(--pp-charcoal-soft); font-weight: 600; border-bottom-color: var(--pp-charcoal); }
.data-table tbody th { color: var(--ink-3); font-weight: 600; }

/* -------------------------------------------------------------------- chat */

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

.chat-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 12px;
}

.chat-thread:empty { display: none; }

/* `88%` ALONE IS A PERCENTAGE OF WHATEVER THE CARD HAPPENS TO BE, which is a
 * measure rule at 390px and not one at 1280px. MEASURED on /demo at
 * 4140: `.chat` lays out 354px at 390px (88% -> 287px, a correct bubble) and
 * 1084px at 1280px (88% -> 929px, about 120 characters of 0.95rem type). The
 * page caps its own prose at 66ch thirty lines up for exactly that reason; a
 * transcript is prose too, and the assistant's replies are the longest text on the page.
 *
 * `min()` keeps BOTH rules and lets the binding one win at each end: 88% still
 * shapes the bubble at 390px, where 66ch (~554px at this font size) is far
 * wider than the card and cannot bind, and 66ch caps the measure from about
 * 630px up. So the narrow layout is byte-identical and only the wide one moves.
 * DELETE WHEN: nothing — this is the rule, not a patch over one. */
.bubble {
  max-width: min(88%, 66ch);
  padding: 10px 13px;
  border-radius: var(--radius);
  font-size: 0.95rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

.bubble-parent {
  align-self: flex-end;
  background: var(--accent);
  color: var(--accent-ink);
  border-bottom-right-radius: 4px;
}

.bubble-assistant {
  align-self: flex-start;
  background: var(--bg-2);
  color: var(--ink);
  border-bottom-left-radius: 4px;
}

.bubble-assistant .who {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: var(--pp-eyebrow-track);
  text-transform: uppercase;
  color: var(--pp-orange-ink);
  margin-bottom: 4px;
}

/* F§1.3 at the wide end: the composer was the widest control in the product.
 * `flex: 1 1 auto` on the textarea below, with nothing capping this row, made
 * it grow with the card. MEASURED at 4140, the widest interactive control on
 * every screen of the product at 1280px:
 *
 *     #demo-input (this textarea)                 969px   <- widest by 275px
 *     .kq-input, the results CTAs (34rem column)  496px
 *     the account screen's email field (capped)   352px
 *     every button in the app and the site       <=238px
 *
 * 969px of single-row composer (`rows="1"`, min-height 46px) sat under a
 * paragraph capped at 665px on the same page, so the field was 300px wider
 * than the column of text it belonged to. 66ch is that same cap, which is why
 * this is the number rather than a new one: the composer now lines up with the
 * prose above it instead of overhanging it.
 *
 * The cap goes on the ROW and not on the textarea, so the Send button stays
 * beside the field. Capping the textarea alone would have left the button
 * marooned 969px out. At 390px the row is 354px, far under 66ch, and nothing
 * about the narrow layout changes. */
.chat-form { display: flex; gap: 8px; align-items: flex-end; max-width: 66ch; }

.chat-form textarea {
  flex: 1 1 auto;
  min-height: 46px;
  max-height: 140px;
  resize: vertical;
  font: inherit;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-2);
  border-radius: var(--pp-radius-sm);
  padding: 12px;
}

.chat-status {
  margin: 10px 0 0;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink-2);
}

.chat-status[data-tone="problem"] {
  color: var(--ink);
  background: var(--pp-peach);
  border: 1px solid var(--pp-orange);
  border-radius: var(--pp-radius-sm);
  padding: 10px 12px;
}

.chat-status:empty { display: none; }

/* --------------------------------------------------------- consent / share */
/* AC-8.23. The prefill is shown, editable, and declinable — so it is a real
 * form, not a confirm() dialog, and it is in the DOM (hidden) rather than
 * built by script, so the no-JS page still explains what would be sent. */

.share {
  margin: 16px 0 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  padding: 16px;
}

.share h3 { margin: 0 0 8px; font-size: 1.05rem; font-weight: 700; }

.share-consent {
  margin: 0 0 10px;
  font-size: 0.92rem;
  color: var(--ink);
  font-weight: 600;
}

.share textarea {
  width: 100%;
  /* CAP THE MEASURE at the wide end (desktop.spec.js:259). Without it this
     control grew with the column — 1050px at 1280px — which reads as a text box
     that lost its shape, and a 1050px line is past any readable measure anyway.
     40rem (640px) stays under the 680px control limit the suite enforces and,
     with width:100%, still shrinks to the phone. The Send button lives in
     .share-actions BELOW, so unlike the demo's .chat-form (which caps the row to
     keep its button beside the field) the cap can sit on the control here. */
  max-width: 40rem;
  min-height: 132px;
  font: inherit;
  font-size: 0.92rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-2);
  border-radius: var(--pp-radius-sm);
  padding: 12px;
  resize: vertical;
}

.share-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 12px 0 0;
}

/* ----------------------------------------------------------- help-me lists */

.recognise {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

/* The report's rows (.pp-rows): white rows, hairline edges, small radius,
   and the callout's orange as the lead edge — these are the signs to act on. */
.recognise li {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--pp-orange);
  border-radius: var(--pp-radius-sm);
  padding: 12px 14px;
  line-height: 1.5;
}

.steps { margin: 0; padding-left: 22px; }
.steps li { margin: 0 0 10px; line-height: 1.55; }
.steps li::marker { color: var(--pp-orange-ink); font-weight: 800; }
.steps li:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------ footer */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--bg-2);
  margin-top: 48px;
  padding: 28px 0 40px;
  font-size: 0.9rem;
  color: var(--ink-2);
}

.footer-nav {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin: 0 0 18px;
  padding: 0;
}

/* F§7.3 / AC-7.2. There is no close button, no data-dismiss hook and no
 * animation on this block anywhere in site/ — the disclaimer is not
 * dismissible, and the cheapest way to guarantee that is for no code to exist
 * that could hide it. */
.disclaimer {
  border: 1px solid var(--line);
  border-radius: var(--pp-radius-sm);
  background: var(--card);
  padding: 14px 16px;
  margin: 0 0 18px;
  line-height: 1.55;
  color: var(--ink);
}

.disclaimer strong { display: block; margin-bottom: 4px; }

.legal { margin: 0; color: var(--ink-3); font-size: 0.84rem; line-height: 1.6; }

/* ------------------------------------------------------------- invitation */

/* The report's closing callout: peach ground, an orange edge, and the
   takeaway set as the report's orange closing line. 1.3rem at 700 is large
   text (>= 19px bold), and --pp-orange-ink is 5.3:1 on peach anyway. */
.invite {
  text-align: center;
  background: var(--pp-peach);
  border: 1px solid var(--pp-orange);
  border-radius: var(--pp-radius);
  padding: 28px 20px;
}

.invite h2 { margin: 0 0 6px; font-size: 1.3rem; font-weight: 800; color: var(--pp-orange-ink); }
.invite p { margin: 0 0 18px; color: var(--ink-2); }

/* --------------------------------------------------------------- utilities */

.stack-gap { margin-top: 26px; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
