/* experience.css — the "see it before you trust it" sections on /.
 *
 * NAMESPACE: .xp-*  (site.css owns the base, aesthetics.css the register,
 * product-shot.css the .ps-* full shot). Loads after all three.
 *
 * ── WHY CROPS AND NOT NEW SCREENSHOTS ──────────────────────────────────────
 * There is exactly ONE product capture in this repo (product/command-hub.webp,
 * 1600x1298). A second one needs a vite run, and index.html's own §2.5 comment
 * records that the last attempt was "refused by the RAM guard at 4.5 GB swap and
 * elevated memory pressure" — correctly. So these sections show REGIONS of the
 * capture we already have, at a readable size, rather than inventing anything.
 *
 * The mechanism is `background-position` in percentages, which has the exact
 * property this needs: `background-position: 58% 17%` aligns the 58%/17% point
 * of the IMAGE with the 58%/17% point of the BOX. So a crop is just "the
 * coordinates of the thing, plus a zoom" — no negative offsets to recompute
 * whenever a container resizes, and it degrades correctly at every width.
 *
 * One 45 KB file is downloaded once and serves every crop plus the full shot
 * below them. Separate cropped files would have cost four more requests and
 * four more things to keep in sync with a re-capture.
 *
 * ── WHY NOT A SIMULATED INTERFACE ──────────────────────────────────────────
 * A hand-built HTML recreation would look better and would be a fabrication.
 * docs/customer/DEMO.md settles this for the product already: the browser demo
 * refuses to script a fake Council answer because "there is no cheap honest
 * version of this surface, so it says what it is." A marketing page that fakes
 * what the product itself declines to fake is worse, not better. Everything
 * below is real pixels from a real build, or the product's own wording quoted.
 *
 * NO INLINE STYLE ANYWHERE. The production CSP is `style-src 'self'` and the
 * build hard-fails on inline style — /privacy shipped visually broken for about
 * a month exactly that way (D-no-inline-styles-2026-07-08).
 */

/* ── shared section rhythm ─────────────────────────────────────────────── */
.xp {
  border-top: 1px solid var(--hairline);
}

.xp-head {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 28px;
}

.xp-title {
  font-family: var(--mono);
  font-size: clamp(20px, 1.1rem + 1vw, 27px);
  font-weight: 500;
  letter-spacing: -0.01em;
  /* >= 1.25 is enforced by verify-site.mjs's descender guard on h1/h2/h3.
     1.3 rather than exactly 1.25: the comparison is on a computed float ratio,
     and sitting precisely on a threshold is how a guard flips red on a browser
     that rounds a fraction of a pixel differently. */
  line-height: 1.3;
  margin: 0;
  color: var(--fg);
  text-wrap: balance;
}

.xp-sub {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
  margin: 0;
  max-width: 58ch;
}

/* ── the crop viewport ─────────────────────────────────────────────────
 * aspect-ratio carries the height so there is no layout shift while the one
 * shared image decodes. --xp-zoom / --xp-x / --xp-y are set per crop by the
 * modifier classes below, so this block never repeats itself.
 */
.xp-crop {
  position: relative;
  width: 100%;
  aspect-ratio: var(--xp-ar, 16 / 5);
  background-image: url('/product/command-hub.webp');
  background-repeat: no-repeat;
  background-size: var(--xp-zoom, 130%) auto;
  background-position: var(--xp-x, 58%) var(--xp-y, 17%);
  background-color: #0d0d10;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  overflow: hidden;
}

/* A hairline top edge reads as glass rather than as a flat rectangle — the
 * same treatment product-shot.css gives the full capture, kept consistent. */
.xp-crop::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* ── SOLVED, not eyeballed — scripts/studio/crop-solve.mjs ─────────────────────────────
 * These values USED to be each region's centre coordinates, and the comment here said so
 * outright: "Coordinates are the CENTRE of each region". That contradicted this file's own
 * header 90 lines above, which states the mechanism correctly — a percentage background
 * position ALIGNS the image's p point with the box's p point:
 *
 *     offset = (boxSize - imageSize) x percentage
 *
 * so a region sitting at 58%/17% of the capture rendered at 58%/17% OF THE BOX, not in its
 * middle. Centring only happens at 50%. The visible result was the Trail crop cutting the
 * Memory panel down its spine and slicing "Rheumatology follow-up" and "Symptom notes —
 * week of…" mid-word against the right edge — two half-things instead of one whole one.
 *
 * The solve, given zoom z, image ratio r = 1298/1600, box ratio a = boxH/boxW, and the
 * region centre (fx, fy) as fractions of the image:
 *
 *     X = (0.5 - fx*z) / (1 - z)          Y = (0.5*a - fy*z*r) / (a - z*r)
 *
 * Every term carries one factor of box width, so it cancels: these are correct at 390px and
 * at 1440px alike, which is what lets them be static values.
 *
 * Zoom is now derived rather than picked — each region fills ~95% of the box width — and the
 * script warns if a region then overflows vertically. Aspect ratios were opened up (40/7 ->
 * 40/9, 40/3 -> 40/4) because the old boxes were too letterboxed to hold their own region.
 * `--xp-y: 104.3%` is intentional and legal: background-position percentages may exceed 100%.
 *
 * RE-RUN `node scripts/studio/crop-solve.mjs` IF THE CAPTURE IS REPLACED and paste its
 * output here. The rectangles it solves from are measured off the file, not estimated. */
.xp-crop--read  { --xp-ar: 40 / 9; --xp-zoom: 124%; --xp-x: 92.8%; --xp-y: 7.3%; }
.xp-crop--trail { --xp-ar: 11 / 9; --xp-zoom: 245%; --xp-x: 30.5%; --xp-y: 88.6%; }
.xp-crop--ask   { --xp-ar: 40 / 4; --xp-zoom: 124%; --xp-x: 92.8%; --xp-y: 104.3%; }

.xp-cap {
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--dim);
  margin: 12px 0 0;
}

/* ── the pulled quote: the product's own words, not ours ───────────────── */
.xp-quote {
  margin: 26px 0 0;
  padding-left: 18px;
  border-left: 1px solid var(--emerald-edge);
}

.xp-quote p {
  font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
  font-size: clamp(19px, 1rem + 1vw, 25px);
  line-height: 1.34;
  color: var(--fg);
  margin: 0;
  text-wrap: balance;
}

.xp-quote cite {
  display: block;
  margin-top: 12px;
  font-family: var(--mono);
  font-style: normal;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--dim);
}

/* ── two-up on wide screens; stacked on a phone ────────────────────────── */
.xp-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
  align-items: center;
}

@media (min-width: 860px) {
  .xp-split { grid-template-columns: 1.05fr 0.95fr; gap: 44px; }
  .xp-split .xp-head { margin-bottom: 0; }
}

/* ── the demo invitation ───────────────────────────────────────────────── */
.xp-try {
  border: 1px solid var(--emerald-edge);
  border-radius: var(--radius);
  background:
    linear-gradient(180deg, var(--emerald-wash), transparent 62%),
    var(--card);
  padding: clamp(22px, 4vw, 40px);
}

.xp-try-title {
  /* Was a hardcoded 'Playfair Display', Georgia, serif stack. It had been rendering as GEORGIA
     for this rule's whole life, because no @font-face for Playfair existed anywhere on the site
     — `public/fonts/` held exactly one file (Geist Mono). Someone specified the right face and
     the browser silently used the fallback, with nothing to report it. Adding the real woff2
     (site.css) fixed this rule retroactively; pointing it at the token keeps the two in sync. */
  font-family: var(--display);
  font-size: clamp(26px, 1.3rem + 2vw, 40px);
  /* CAUGHT BY THE GATE, not by eye: this was 1.14, and verify-site.mjs failed
     with `descender clip risk: ["Open it in this browser "]`. Playfair has long
     descenders and the p in "Open" was being clipped. The guard is the reason
     this never reached anyone — exactly the font-clip regression class it was
     written for. Do not tighten it back for looks. */
  line-height: 1.28;
  margin: 0 0 14px;
  color: var(--fg);
  text-wrap: balance;
}

.xp-try-sub {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
  margin: 0 0 26px;
  max-width: 54ch;
}

.xp-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
}

/* Deliberately the emerald-edged variant of .aes-cta rather than a new shape:
 * the page already has one button language and a second would read as a second
 * design system. This is the SAME button, marked as the primary action here. */
.xp-cta {
  display: inline-flex;
  align-items: center;
  padding: 14px 30px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--fg);
  border: 1px solid var(--emerald-edge);
  border-radius: var(--radius);
  background: var(--emerald-glow);
  transition: background var(--dur-ui) var(--ease-out),
              border-color var(--dur-ui) var(--ease-out);
}

.xp-cta:hover,
.xp-cta:focus-visible {
  background: hsl(160 84% 39% / 0.2);
  border-color: hsl(160 84% 39% / 0.45);
}

.xp-aside {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--dim);
  margin: 0;
}

/* ── what you'll find / what you won't ─────────────────────────────────── */
.xp-ledger {
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  overflow: hidden;
}

@media (min-width: 720px) {
  .xp-ledger { grid-template-columns: 1fr 1fr; }
}

.xp-ledger li {
  background: var(--bg);
  padding: 16px 18px;
  display: grid;
  gap: 6px;
}

.xp-ledger .k {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}

.xp-ledger .v {
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.62;
  color: var(--muted);
}

/* The honesty half is marked in the type, not with a red colour — a "what it
 * does not do" list styled as a warning reads as an apology. It is not one. */
.xp-ledger li[data-kind="no"] .k { color: var(--dim); }

@media (prefers-reduced-motion: reduce) {
  .xp-cta { transition: none; }
}
