/* ============================================================
   VividCharts — /homepage-prototype/ (VIV-8678, parent VIV-8677)

   INTERNAL WIREFRAME PROTOTYPE. Every rule in this file is scoped under
   `.hp-proto` (the prototype page's body class) or a `.hp-` prefixed class, so
   nothing here can reach another page. Do NOT move any of it into styles.css —
   that file is shared by every page and the blast radius is the whole site.

   Design tokens (colours, radii, shadows, type) come from styles.css. This
   file only adds layout and the wireframe-specific slot / placeholder /
   unresolved-marker treatments.
   ============================================================ */

/* Sticky nav is 72–88px tall; give every anchor target clearance so
   `href="#reporting"` doesn't land under the header. */
.hp-proto .hp-sec { scroll-margin-top: 96px; }

/* ---------- Prototype-local colour token ---------- */
/* `--brand-strong` (#349DA1) is a HOVER/PRESSED colour, not a text colour: on
   white it measures 3.24:1, under the 4.5:1 WCAG AA floor for text at this
   size (VIV-8759, found by rendered QA at 390px). `--brand-deep` is the same
   teal stepped down in LIGHTNESS only — hue 182.1 vs 182.2 and saturation
   0.512 vs 0.512 in HSL, so it is the identical brand hue, just dark enough to
   be legible as type: 4.90:1 on white and 4.56:1 on `--brand-tint`, clearing
   AA on both prototype surfaces. Declared here, not in styles.css, per the
   file header: this file is the prototype's only blast radius. Reuse it for
   the remaining brand-teal contrast nodes when the broader pass lands. */
.hp-proto { --brand-deep: #297C7F; }

/* ---------- Shared section rhythm ---------- */
.hp-proto .hp-sec { padding: 88px 0; }
.hp-proto .hp-h2 { font-size: clamp(28px, 3.4vw, 42px); line-height: 1.08; }
.hp-proto .hp-body { color: var(--ink-2); font-size: 16.5px; margin-top: 18px; }
.hp-kicker {
  font-size: 12.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--ink-3); margin: 0;
}

/* ---------- Wireframe primitives: slots, placeholders, unresolved ---------- */

/* A SLOT is deliberately empty space reserved for an asset that does not exist
   yet. Dashed + tinted so a reviewer can count them at a glance. */
.hp-slot-label {
  display: block;
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--ink-3);
}
.hp-slot-caption {
  display: block; margin-top: 10px; max-width: 34ch;
  font-size: 14px; line-height: 1.45; color: var(--ink-3);
}

/* `.hp-placeholder-tag` (the amber PLACEHOLDER pill) and the whole `.hp-needs`
   / `.hp-needs-title` / `.hp-needs-body` group stood here until VIV-8775 landed
   marketing's copy pack. Every instance of both — three cards in S6, two
   service items in S7, and the two "Needs your input" boxes in S6 and S8 — had
   its question answered, so all five rules are DELETED, not left unexercised.
   Same reasoning as `.hp-cta-pending` and `.hp-detail-slot` before them: on
   this page an amber pill and a dashed edge are the load-bearing signal for
   "not real yet", and one sitting unused in the stylesheet is how it comes back
   on copy that IS real. If a future section genuinely needs the treatment,
   write it back deliberately with the section that needs it.

   `.hp-slot-label` / `.hp-slot-caption` above are the exception and stay: S2's
   tab-panel image fallback still has no asset and still uses both. */

/* ============================================================
   S1 — HERO
   ============================================================ */
/* White base + the live homepage's graph-paper grid. `position: relative` and
   `overflow: hidden` are what let `.hp-hero-bg` fill the section without the
   masked grid bleeding past it; `.hp-hero .wrap` is lifted above it. The three
   grid declarations below are copied verbatim from styles.css `.hero-bg .grid`
   (same var(--line), 64px pitch, radial mask, .55 opacity) — porting them under
   the .hp-hero namespace keeps the blast radius inside this stylesheet. */
/* NO `padding` here on purpose (VIV-8749). This rule used to carry
   `padding: 96px 0 84px` and it was DEAD CODE: `.hp-hero` is (0,1,0) and
   `.hp-proto .hp-sec` above is (0,2,0), so the section rule won and the hero
   has always rendered 88/88 at base width, 64/64 at 880 and 52/52 at 560 — not
   96/84. Measured on the staging build before this ticket: computed
   padding-bottom 88, and the hero -> tabs boundary 176px, not the 172 the
   source arithmetic predicts. Leaving the dead shorthand in place would keep
   two numbers in the file that disagree with the page, so it is gone; the
   padding-bottom this ticket actually needs is set below at matching
   specificity, and padding-TOP is deliberately left to `.hp-proto .hp-sec`,
   which is exactly where it has effectively come from all along. The hero's
   top whitespace is a separate marketing decision (VIV-8747 item 7) and this
   ticket does not move it by a pixel. */
.hp-hero { position: relative; overflow: hidden; background: #fff; }

/* Hero -> tabs is ONE `.hp-sec` padding unit, split evenly with
   `.hp-overview`'s padding-top: 44 + 44 = 88 (VIV-8749 item 6). It used to be
   88 + 88 = 176 and the board read it as "quite a bit of space between the
   tabs and the hero tagline". Same rule VIV-8739 applied to the logos strip —
   a boundary between two things that belong together is one unit, not two —
   so the tab frame groups upward with the hero it explains.

   `.hp-proto .hp-hero`, not `.hp-hero`: it has to TIE `.hp-proto .hp-sec` and
   sit after it, for the reason the paragraph above spells out. That tie is
   also why this is restated in the 880 and 560 blocks, which re-declare
   `.hp-proto .hp-sec` further down. */
.hp-proto .hp-hero { padding-bottom: 44px; }
.hp-hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hp-hero-bg .grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%);
  opacity: .55;
}

/* ---- Motion layer (VIV-8756) -------------------------------------------
   Restores the two pieces of the live hero background that VIV-8678 left out:
   the travelling sparks (`styles.css .grid-dots` / `.spark`) and the teal glow
   (`styles.css .hero-bg .glow`). Marketing's ruling on VIV-8750 Decision 1
   (Option A): these, not the copy, are the direct cause of "the top of the
   hero looks a little bland". Values are copied verbatim from styles.css —
   same 5px dot, same rgba(63,193,199,...) trail and glow, same 64px-pitch
   rhythm — so the prototype reads as the same brand surface as prod.

   Namespaced (`.hp-grid-dots`, `.hp-spark`, `@keyframes hp-spark-*`) rather
   than reusing the live class names. This page DOES load styles.css, so bare
   `.grid-dots`/`.spark` would inherit the global rules and the prototype's
   motion layer would have two owners in two stylesheets. `.grid` and `.glow`
   can stay bare because their live rules are scoped under `.hero-bg` and never
   reach this tree; `.grid-dots` and `.spark` are unscoped and would.

   NO PADDING, NO LAYOUT. Everything here is inside `.hp-hero-bg`, which is
   `position: absolute; inset: 0` — it cannot change the hero's height or move
   anything below it. */
.hp-hero-bg .glow {
  position: absolute; left: 50%; top: -120px; transform: translateX(-50%);
  width: 900px; height: 520px;
  background: radial-gradient(ellipse at center, rgba(63,193,199,.20), rgba(63,193,199,0) 62%);
  filter: blur(4px);
}
/* Same mask as `.hp-hero-bg .grid` above, on purpose: the sparks have to fade
   out exactly where the grid lines they travel along do. */
.hp-grid-dots {
  position: absolute; inset: 0; pointer-events: none; overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%);
}
.hp-spark {
  position: absolute; width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 7px 1px rgba(63,193,199,.5);
  opacity: 0; will-change: transform, opacity;
}
.hp-spark::before { content: ""; position: absolute; border-radius: 999px; }
.hp-spark-h { margin-top: -2px; }    /* centre on a horizontal grid line */
.hp-spark-h::before {
  width: 28px; height: 2px; right: 3px; top: 1.5px;
  background: linear-gradient(90deg, rgba(63,193,199,0), rgba(63,193,199,.45));
}
.hp-spark-v { margin-left: -2px; }   /* centre on a vertical grid line */
.hp-spark-v::before {
  width: 2px; height: 28px; bottom: 3px; left: 1.5px;
  background: linear-gradient(180deg, rgba(63,193,199,0), rgba(63,193,199,.45));
}
@keyframes hp-spark-x {
  0%   { transform: translateX(-30px); opacity: 0; }
  9%   { opacity: .8; }
  88%  { opacity: .8; }
  100% { transform: translateX(calc(100vw + 30px)); opacity: 0; }
}
@keyframes hp-spark-y {
  0%   { transform: translateY(-30px); opacity: 0; }
  12%  { opacity: .6; }
  78%  { opacity: .6; }
  100% { transform: translateY(540px); opacity: 0; }
}
/* Matches the LIVE homepage exactly, measured (not read) in Chromium with the
   media feature forced: styles.css:553 drops `.grid-dots` to `display: none`
   under reduce, and leaves `.hero-bg .glow` alone — the glow is a static
   gradient, it has nothing to reduce. Prototype does the same: sparks gone,
   grid and glow untouched. */
@media (prefers-reduced-motion: reduce) { .hp-grid-dots { display: none; } }
/* ---- end motion layer -------------------------------------------------- */

.hp-hero .wrap { position: relative; z-index: 1; }
.hp-hero-h1 {
  margin-top: 22px;
  font-size: clamp(38px, 6vw, 68px);
  line-height: 1.02;
  /* 22ch, not the 16ch this rule carried for "The operating system for
     ServiceNow teams" (VIV-9046 item 2). The new headline is 40 characters and
     16ch broke it three ways — "From what" / "happened to what" /
     "happens next." — a ragged stack with a 9-character first line. 22ch is the
     shortest cap that clears "From what happened to" (21ch) in one line, so the
     break lands between the two halves of the sentence: "From what happened to"
     / "what happens next." Below 640px `max-width: none` takes over and the
     container is narrower than this anyway. */
  max-width: 22ch;
}
/* "happens next" carries the emphasis. The first pass at this recoloured the
   type `--brand-deep` (#297C7F) and the board rejected both the colour and the
   treatment — a colour swap is the least interesting way to emphasise two
   words, and it forces the darkened teal because tinting TYPE is what runs into
   VIV-8759's 4.5:1 floor.

   So the type does not change colour at all: it stays `--ink`, 16.1:1 on this
   white hero, and the emphasis is a hand-drawn marker sweep UNDER the words in
   the brand teal the board named (#3FC1C7). Decorative background, not type —
   WCAG 1.4.3 does not apply to it, so the prototype's AA-safe-teal workaround
   is not needed here and `--brand-deep` stays for the eyebrow nodes that really
   are type.

   The sweep DRAWS ITSELF left to right, which is the whole point of the line:
   the headline is about forward progress, so the emphasis moves forward. It
   tapers thin-to-thick and rises slightly to the right for the same reason.

   Why an inline SVG and not a `linear-gradient` bar: a gradient can only make a
   rectangle, and a rectangle reads as an `<u>`, not as someone underlining the
   phrase by hand. The teal is a literal here because a data URI cannot see CSS
   custom properties — it is `--brand` (#3FC1C7) from styles.css:18, and the
   rest of this file's motion layer already inlines the same value as rgba() for
   the same reason.

   Painted as a BACKGROUND rather than an `::after` so a wrap between "happens"
   and "next" draws the sweep on both fragments (`box-decoration-break: clone`)
   instead of one bar floating across the gap. */
.hp-hl {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 12' preserveAspectRatio='none'%3E%3Cpath d='M1 8C55 4 130 2.5 199 1L199 6.5C130 8 55 9.5 1 11Z' fill='%233FC1C7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  /* 88%, not 100%: the h1's content box runs well past the baseline at
     line-height 1.02, so a sweep pinned to the box bottom floats away from the
     words and reads as a rule rather than an underlining. 88% tucks it against
     the baseline, just grazing the descender of the p in "happens" the way a
     marker would. */
  background-position: 0 88%;
  background-size: 100% .2em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  animation: hp-hl-draw .85s var(--ease) .25s both;
}
@keyframes hp-hl-draw {
  from { background-size: 0 .2em; }
  to   { background-size: 100% .2em; }
}
/* Same contract as the spark layer above: under `reduce` the sweep is fully
   drawn on first paint, never absent — it is the emphasis the board asked for,
   not decoration that can be dropped. */
@media (prefers-reduced-motion: reduce) {
  .hp-hl { animation: none; background-size: 100% .2em; }
}
.hp-hero-sub {
  margin-top: 22px; max-width: 58ch;
  font-size: clamp(17px, 1.6vw, 20px); color: var(--ink-2);
}
.hp-hero-actions { margin-top: 34px; display: flex; flex-wrap: wrap; gap: 14px; }

/* ============================================================
   S2 — OVERVIEW TABS
   ============================================================ */
.hp-overview { background: var(--white); }

/* No `margin-top`: the `.hp-kicker` this frame used to sit 26px below is gone
   (VIV-9046 item 1), so the frame is the first thing in the section `.wrap` and
   the section padding is the whole gap. Leaving the 26px would have added it on
   top of the padding and pushed S2 down. */
.hp-tabframe { margin-top: 0; }

/* VIV-9046 item 1: the "We help ServiceNow teams:" eyebrow moved out of the
   section and into the frame as a dark bar reading "Your ServiceNow partner
   to:", so the three-phase promise heads the tabs instead of floating above
   them. `--white` on `--dark-ash` (#23222D) is 15.7:1 — AA clear with room.
   NOT `var(--on-ash)`: that token is declared inside the `.hp-story` scope
   further down this file and does not resolve out here, so it would fall back
   to the inherited ink and render dark-on-dark.
   Type is the page's existing eyebrow treatment (see `.hp-kicker`), not a new
   scale. The bar takes the frame's rounded top and `.hp-tablist` is squared off
   below it, so the two read as one unit. */
.hp-tabbar {
  margin: 0;
  padding: 11px 22px;
  background: var(--dark-ash);
  color: var(--white);
  border-radius: var(--r-md) var(--r-md) 0 0;
  text-align: center;
  font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}

.hp-tablist {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 0;
  border: 1px solid var(--line); border-radius: 0;
  overflow: hidden;
}
.hp-tab {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  text-align: left;
  padding: 18px 22px;
  font-family: var(--font-body);
  background: var(--light-cream);
  border: 0; border-right: 1px solid var(--line);
  border-bottom: 3px solid transparent;
  cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.hp-tablist .hp-tab:last-child { border-right: 0; }
.hp-tab:hover { background: #EDE4DE; }
/* ONE label per tab, the board's copy verbatim (VIV-8717). NO `text-transform`
   anywhere on this rule or its selected state — the copy is lowercase on
   purpose and upper-casing it would silently rewrite approved wording. The
   unselected tab is muted rather than hidden, so all three stages still read at
   rest; what no longer reads at rest is the offer NAME, which the board
   replaced. Type is a hair smaller than the old 19px offer name because these
   labels are 4-5 words, not one, and three of them share a 1/3 column. */
.hp-tab-label {
  font-size: 17.5px; font-weight: 600; line-height: 1.25;
  letter-spacing: -0.01em; color: var(--ink-2);
}
.hp-tab[aria-selected="true"] {
  background: var(--white);
  border-bottom-color: var(--brand);
}
.hp-tab[aria-selected="true"] .hp-tab-label { color: var(--ink); font-weight: 700; }
.hp-tab:focus-visible { outline: 3px solid var(--brand); outline-offset: -3px; }

/* The three panels occupy ONE grid cell, so the frame is always as tall as the
   tallest panel and selecting a tab cannot change the height of anything below
   it. Measured before this rule: the frame moved S5/S6/S7 by 25-59px depending
   on viewport width. */
.hp-panels { display: grid; }
.hp-panels > .hp-panel { grid-area: 1 / 1; }

/* Column split and padding are tuned for the MEDIA slot, not the copy
   (VIV-8717 item 2 — "the tab graphics are hard to see"). Measured at the
   1200px `--maxw` with the 40px gutter: the panel's inner box is 1048px after
   padding and 1012px after the gap, so the 1.2fr media column renders ~607px
   wide against the previous ~486px. With the 16/10 ratio below that is a slot
   ~379px tall where it used to be ~304px: +25% in each dimension, +56% in
   area. `minmax(0, …)` on both tracks so a wide iframe can never blow the
   grid out — an `fr` track's default `min-width: auto` would let it.
   The copy column lands at ~405px, which still clears `.hp-panel-body`'s 46ch
   (~368px) and the 20ch headline cap, so nothing in the copy re-wraps badly. */
.hp-panel {
  display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: 36px;
  align-items: center;
  padding: 36px;
  border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 var(--r-md) var(--r-md);
  background: var(--white);
}
/* `hidden` stays the authoritative state — the attribute is what the script
   sets and what assistive tech reads. We only swap the UA's `display:none` for
   `visibility:hidden` so the panel keeps reserving its grid cell. Both remove
   the subtree from the accessibility tree and from the tab order, so nothing
   inside a hidden panel is reachable; the only difference is that the box is
   still measured. */
.hp-panel[hidden] { display: grid; visibility: hidden; pointer-events: none; }
.hp-panel:focus-visible { outline: 3px solid var(--brand); outline-offset: -3px; }
/* .hp-panel-kicker is gone with the "Tab 1 — Align" line it styled (VIV-8717
   item 3); the headline is the panel's first element now, so it carries no top
   margin. */
/* Narrow-only line (VIV-8749). It exists in ONE markup tree with the wide
   presentation, so the gate is CSS, not a second copy of the markup: hidden
   here, un-hidden in the 880px block where the tablist that would otherwise
   carry the stage is gone. Type scale is set there, next to everything else
   the narrow block re-scales.
   `.hp-panel-offer` shared this rule and is DELETED (VIV-9046 item 6) — the
   board asked for the offer names to come out of mobile. `.hp-panel-stage`
   stays: it is the AA-guarded eyebrow the contrast suite asserts on. */
.hp-panel-stage { display: none; margin: 0; }
.hp-panel-h { margin: 0; font-size: clamp(24px, 2.4vw, 32px); max-width: 20ch; }
.hp-panel-body { margin-top: 16px; color: var(--ink-2); font-size: 16px; max-width: 46ch; }
.hp-panel-actions {
  margin-top: 26px; display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
}
/* `.hp-cta-pending` lived here — the muted, non-interactive treatment VIV-8749
   item 5 gave the Act CTA while it had no destination. DELETED in VIV-8750:
   marketing ruled that rendering is "funnel friction and reads as broken".
   VIV-8750 then gave Act an interim `/contact-us/` target and VIV-8771 (board)
   removed the Act CTA outright, so the Act offer has no `href` again — but the
   answer is still absence, not a revived dead control. The template gates the
   whole `.hp-panel-actions` wrapper on `o.href`, so a destination-less offer
   emits no element and this rule has nothing to style. It is not kept "just in
   case" on purpose — an unexercised fallback is how a banned treatment quietly
   comes back. See the frontmatter note in src/homepage-prototype.njk. */
.hp-panel-slot {
  min-height: 260px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 4px; padding: 28px;
  border: 1.5px dashed var(--line-strong); border-radius: var(--r-md);
  background: var(--brand-tint);
}

/* Media variant — the slot holds real artwork instead of a placeholder label,
   so the padding goes and the frame fills the box edge to edge. `aspect-ratio`
   (source artwork is 1000x640) gives the slot a deterministic height instead of
   one that depends on what the iframe happens to report, and `min-height: 0`
   lets that ratio win over the placeholder's 260px floor. Measured: the
   resulting height stays below the copy column's at every viewport from 881px
   up, so the panel — and therefore everything below the tab frame — does not
   move. See the .hp-panels one-cell note above for why that matters. */
.hp-panel-slot-media {
  min-height: 0; padding: 0;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  /* Solid, not dashed: the dashed rule is this prototype's "placeholder, copy
     pending" signal and this slot now holds real artwork. Same 1.5px width, so
     the box measures identically either way. */
  border-style: solid; border-color: var(--line);
  /* Positioning context for the double-buffered pair (VIV-8704). */
  position: relative;
}
/* Both frames are absolutely positioned and stacked, NOT laid out in the flex
   flow, so the restart in homepage-prototype.js can mount a second one without
   the slot changing size and without the panel — or anything below the tab
   frame — moving by a pixel. The slot's height comes from `aspect-ratio`, so
   taking the frames out of flow costs nothing. */
.hp-hero-anim { position: absolute; inset: 0; display: block; width: 100%; height: 100%; border: 0; }
/* The pending twin: loaded, laid out and PAINTING (so it really does reach the
   animation), but transparent and inert until it is promoted. `opacity: 0`
   rather than `visibility`/`display`, both of which would stop it rendering —
   the whole point is that it finishes unpacking off-screen. */
.hp-hero-anim[data-hp-hero-anim-twin] { opacity: 0; pointer-events: none; }

/* ============================================================
   S3 — EARLY CUSTOMER LOGOS
   ============================================================ */
/* Real six-logo strip (partials/customer-logos.njk). All of the visual styling
   comes from styles.css `.trust-strip` / `.trust-logos` / `.trust-logo`, which
   already carry their own padding: 40px/8px, dropping to 28px/4px at 540px
   (styles.css's own breakpoint — note 540, not this file's 560).

   ASYMMETRIC ON PURPOSE (VIV-8739, superseding VIV-8704). VIV-8704 moved the
   strip here from under the hero and re-derived its padding so the gap on each
   side equalled every other section boundary on the page. That produced
   88 + 48 + 40 = 176px above the logos at 1440 — a full double-section
   boundary — and the board read it as "quite a bit of space ... that feels
   like a lot of white space". The symmetry rationale is therefore withdrawn.

   The rule now: the overview -> strip boundary is ONE `.hp-sec` padding unit,
   not two. The strip groups upward with the tabs it closes; the strip ->
   #problem boundary is untouched and still carries the full page rhythm, so
   the section reads as the tail of the overview rather than as a band of its
   own.

     top    = .hp-overview padding-bottom + this padding-top + .trust-strip pt
              == one .hp-sec padding unit
     bottom = UNCHANGED from VIV-8704 (.hp-sec padding - .trust-strip pb)

   1440: 40 + 8 + 40 = 88   /  bottom 88-8  = 80
    880: 24 + 0 + 40 = 64   /  bottom 64-8  = 56
    560: 12 + 0 + 40 = 52   /  bottom 52-8  = 44
    540: 24 + 0 + 28 = 52   /  bottom 52-4  = 48

   Specificity has to match `.hp-proto .hp-sec` or the section padding wins —
   and because the two selectors TIE, the later `@media` copies of
   `.hp-proto .hp-sec` win on source order. That is why the narrow-width
   overrides are repeated in the media blocks below; without them the strip
   silently regains 64px/52px of symmetric padding at 880px and 560px. The
   SAME trap applies to `.hp-proto .hp-overview`, which is why its
   padding-bottom is also restated in all four blocks rather than once.
   There are FOUR copies, not two, because `.trust-strip`'s own step happens at
   540px while this file's steps at 880/560 — the 560 copy covers the 541-560
   sliver (where the strip is still 40/8) and a 540 copy covers real phones.
   The old .hp-logo-row / .hp-logo-slot / .hp-logos-wrap / .hp-logos-kicker
   placeholder rules are deleted — nothing else used them. */
/* padding-TOP is the other half of the hero boundary (VIV-8749 item 6):
   44 + 44 = 88 = one `.hp-sec` unit at this width, where it used to be
   84 + 88 = 172. It carries the SAME tie-with-`.hp-proto .hp-sec` hazard the
   paragraph above describes for padding-bottom, so it is restated in the 880
   and 560 blocks — the two that re-declare `.hp-proto .hp-sec` after this
   point. The 540 block does not re-declare it and therefore needs no restate.
   padding-BOTTOM below is the logos boundary and is UNCHANGED. */
.hp-proto .hp-overview { padding-top: 44px; padding-bottom: 40px; }
.hp-proto .hp-logos { padding: 8px 0 80px; background: #fff; }

/* ============================================================
   S4 — CUSTOMER PROBLEM
   ============================================================ */
.hp-problem { background: var(--white); }
.hp-problem-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 64px; }
.hp-problem-lede .hp-h2 { margin-top: 18px; max-width: 18ch; }
.hp-pains { list-style: none; margin: 0; padding: 0; display: grid; gap: 30px; }
.hp-pain { border-top: 1px solid var(--line); padding-top: 18px; }
.hp-pain-num {
  font-size: 13px; font-weight: 700; color: var(--brand-strong);
  font-variant-numeric: tabular-nums; letter-spacing: var(--tracking-caps);
}
.hp-pain-h { margin-top: 8px; font-size: 19px; }
.hp-pain-b { margin-top: 8px; color: var(--ink-2); font-size: 15.5px; }

/* ============================================================
   S5 / S6 / S7 — DETAIL SECTIONS
   These are ordinary page-flow sections. Nothing in the S3 tab state
   addresses them, so no tab selection can hide, collapse or reorder them.
   ============================================================ */
.hp-detail { background: var(--white); }
.hp-detail-alt { background: var(--light-cream); }
.hp-detail-kicker {
  display: flex; align-items: center; gap: 14px;
  margin: 0; font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--ink-3);
}
.hp-detail-num {
  font-size: 15px; color: var(--brand); font-variant-numeric: tabular-nums;
}
.hp-detail-num::after {
  content: ""; display: inline-block; vertical-align: middle;
  width: 26px; height: 1px; margin-left: 12px; background: var(--line-strong);
}
.hp-detail-h { margin-top: 16px; max-width: 22ch; }
/* S6's section lede. Measure-capped like `.hp-detail-h` because it is the only
   body copy on the page that runs the full `.wrap` width rather than sitting in
   a `.hp-detail-grid` column. */
.hp-detail-lede { max-width: 58ch; }
.hp-detail-grid {
  margin-top: 40px;
  display: grid; grid-template-columns: 1.1fr 1fr; gap: 52px; align-items: start;
}
.hp-detail-grid--copyfirst { grid-template-columns: 1fr 1.1fr; }
/* `.hp-detail-slot` (dashed 300px box) and `.hp-detail-slot--wide` stood here
   until VIV-8774 put real screenshots in all three of their instances. Both
   rules are DELETED rather than kept "for the next slot": a dashed box is this
   prototype's load-bearing "no asset exists yet" signal, and an unused one
   sitting in the stylesheet is how it quietly comes back on a section that has
   an asset. `.hp-slot-label` / `.hp-slot-caption` stay — the quote slot and the
   tab-panel fallback still use them. */

/* ---------- Screenshot frame (VIV-8774) ---------- */
/* Browser-chrome frame for a real product screenshot. /platform/ (`.p-vis`)
   and /convene/ (`.cv-stage`) already ship this treatment; the declarations are
   COPIED here rather than either class being reused, because both carry their
   own page's type scale and padding and would apply it to board-approved copy
   on this page. Same rule `.hp-hero-bg` follows against `.hero`.
   Solid border, never dashed — dashed means "placeholder" on this page. */
.hp-shotframe {
  margin: 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  /* So the image can shrink inside its grid track instead of forcing the
     row wider than the column (same reason as `.cv-case-vis`). */
  min-width: 0;
}
.hp-shotframe-bar {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 15px;
  background: var(--light-cream);
  border-bottom: 1px solid var(--line);
}
.hp-shotframe-dots { display: flex; gap: 6px; flex: none; }
.hp-shotframe-dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--line-strong); }
/* --ink-2, not --ink-3: ink-3 on --light-cream measures 4.21:1, under the
   4.5:1 AA floor for text this size. Same finding as `.cv-stage-title`. */
.hp-shotframe-title {
  font-size: 11.5px; font-weight: 700; color: var(--ink-2);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}
/* `width`/`height` attributes on every `<img>` give the intrinsic ratio, so an
   un-cropped shot reserves its own box before it loads and nothing below it
   moves. Do not remove them. */
.hp-shot { display: block; width: 100%; height: auto; }

/* The wide (S6) variant. The CAB source is 1600x1453 — near-square — and would
   render ~1017px tall across the 1120px content column, i.e. a full-page
   screenshot dump. Cropped to 12/7 it shows the top 1600/(12/7) = 933px of
   1453 (64.2%), a fraction that is width-independent, so the cut lands in the
   same place at every viewport: just below the note field under the
   Approve / Reject / Modify row and just above the Documents card. That row is
   the reason this shot was chosen, so the crop must not climb above ~y=800.
   Change the image and you must re-measure where the line falls. */
.hp-shotframe--wide { margin-top: 40px; }
.hp-shot--crop {
  aspect-ratio: 12 / 7;
  object-fit: cover;
  object-position: top center;
}
.hp-bullets {
  margin: 26px 0 0; padding: 0; list-style: none; display: grid; gap: 14px;
}
.hp-bullets li {
  position: relative; padding-left: 26px; font-size: 16px; color: var(--ink-2);
}
.hp-bullets li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 12px; height: 2px; background: var(--brand);
}
/* One declaration for every detail-section CTA. It was `.hp-detail-copy .btn`
   while S5 and S7 were the only sections with a button and both sat in a copy
   column; S6's `Explore Convene` (VIV-8855) hangs at section level instead,
   with no `.hp-detail-copy` to inherit from, so the selector widens to the
   section rather than the new button getting a one-off rule of its own. Every
   `.btn` inside a `.hp-detail` is a section CTA and wants the same 28px. */
.hp-detail .btn { margin-top: 28px; }

/* S7's team photo (VIV-8855). Deliberately NOT `.hp-shotframe`: no chrome bar,
   no dots, no title. That treatment means "this is a capture of the product",
   and the subject here is five people. The only borrowed declaration is the
   corner radius, so the photo sits on the same geometry as the screenshots
   above it, and `display: block` + `height: auto` so the width/height
   attributes still reserve the box before the file arrives. */
.hp-teamshot { margin: 28px 0 0; border-radius: var(--r-md); overflow: hidden; }
.hp-teamshot img { display: block; width: 100%; height: auto; }

/* S6 capability cards */
.hp-cards {
  list-style: none; margin: 32px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
}
/* Solid hairline, was `1.5px dashed var(--line-strong)` while the three cards
   held "Capability one/two/three". Real copy does not wear the dashed edge. */
.hp-card {
  padding: 24px; background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-md);
}
.hp-card-h { font-size: 19px; }
.hp-card-b { margin-top: 10px; color: var(--ink-2); font-size: 15.5px; }

/* S7 service items */
.hp-svc-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 26px; }
.hp-svc { border-top: 1px solid var(--line); padding-top: 20px; }
/* `.hp-svc--placeholder` (dashed top rule on items 2 and 3) is deleted with
   the placeholder bodies it marked — all three items share `.hp-svc` now. */
.hp-svc-h { font-size: 19px; }
.hp-svc-b { margin-top: 8px; color: var(--ink-2); font-size: 15.5px; }

/* ============================================================
   S8 — CUSTOMER STORY  (the page's one dark band)
   ============================================================ */
/* `--dark-ash` is the brand's designated dark-band token (styles.css:15,
   "chrome / dark bands") and the only dark token that is not teal. Non-teal is
   the requirement, not a preference: S10 immediately below this one is already
   a `--brand` band, so a teal S8 would read as one long teal run.

   EVERY colour in this section is re-declared against it, because every one of
   them assumed `--light-cream` and most were invisible on ash. Measured
   (WCAG 2.x relative luminance) against `#23222D`:

     --ink          #202020   1.04:1   invisible
     --ink-2        #383838   1.34:1   invisible
     --ink-3        #707070   3.17:1   under the 4.5:1 small-text floor
     Teradata mark  #3D4952   1.70:1   invisible — the SVG's own wordmark

   The two tokens below are the replacements. They are solid hexes rather than
   `rgba(255,255,255,a)` on purpose: an alpha has to be composited against
   whatever sits behind it before it can be measured, and this section has TWO
   surfaces — the band itself, and the `.06` white card that flattens to
   `#302F3A` over it. scripts/test-prototype-contrast.js group 4 measures each
   token against both.

     --on-ash    #FFFFFF   15.69:1 on the band   13.18:1 on a card
     --on-ash-2  #C1C1C4    8.74:1 on the band    7.55:1 on a card

   `--on-ash-2` is exactly `rgba(255,255,255,.72)` flattened on the band. It
   exists so the two-level hierarchy `--ink` / `--ink-3` used to carry on cream
   survives the inversion instead of collapsing into a single white; both
   levels clear 4.5:1 with room, so the hierarchy costs no legibility. */
.hp-story {
  background: var(--dark-ash);
  --on-ash: #FFFFFF;
  --on-ash-2: #C1C1C4;
}
/* `.hp-kicker` is a shared rule used by several light sections, so this is an
   override scoped to the dark band rather than an edit to the base rule. */
.hp-story .hp-kicker { color: var(--on-ash-2); }
.hp-story-grid {
  margin-top: 26px;
  display: grid; grid-template-columns: 1.4fr 1fr; gap: 44px; align-items: stretch;
}
/* `.hp-quote-slot` / `.hp-quote-desc` are renamed to `.hp-quote` /
   `.hp-quote-text` and de-dashed (VIV-8775). The old names said "slot" because
   the box was empty; it holds a real, attributed Teradata quote now, and the
   1.5px dashed `--line-strong` edge it wore is this page's "not real yet"
   signal. Neither old selector survives — nothing in the template referenced
   them once the slot was filled. */
/* On cream these cards were `rgba(255,255,255,.6)` over a warm `--line`
   hairline. Both flip for the dark band: 60% white on ash is a muddy grey
   block, and `--line` (#E4DFD8) at 11.84:1 is a near-white rule, far too loud
   for a 1px edge. A .06 white wash + a .14 white hairline keeps the card a
   surface rather than a slab. `.hp-quote` and `.hp-metric` MUST stay identical
   here — they sit side by side in the same grid and any difference between
   them reads as a rendering bug. */
.hp-quote {
  margin: 0; padding: 34px;
  border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-md);
  background: rgba(255,255,255,.06);
}
/* Two marks now, one per container: Teradata over the quote it said, CDW over
   the numbers it published. They share a rule rather than borrowing
   `.trust-logo img` from styles.css, which is tuned for a six-up row at 28px
   and hard-codes `opacity:.85` so no single logo dominates a strip. Here 24px
   is deliberately SMALLER than the strip's 28px so each mark reads as its
   card's source line and not as a second headline.

   POLARITY. Both are forced to pure white. This is not a style preference:
   the Teradata SVG's primary wordmark ships `#3D4952`, which measures 1.70:1
   on `--dark-ash` — it disappears. Recolouring is therefore forced on that
   mark, and once one mark is recoloured, leaving CDW in its `#e31836` red
   (3.33:1 — clears the 3:1 graphical-object bar, but only just) makes the pair
   asymmetric and reads as a bug rather than a decision. Monochrome customer
   marks are also this site's existing register, not an invention here: the S3
   strip greyscales all six, and `styles.css:838` already inverts the
   Everforth/GlideFast lockup for exactly this wrong-polarity reason.

   `brightness(0)` collapses every channel to black while preserving alpha;
   `invert(1)` then takes it to `#FFFFFF` exactly. Composing them this way —
   rather than `grayscale(1) invert(1)`, which lands on whatever grey each
   source colour happens to invert to — is what makes "15.69:1" a literal
   measured claim about both marks rather than an approximation. */
.hp-quote-logo,
.hp-results-logo {
  display: block;
  width: auto; height: 24px; max-width: 100%;
  filter: brightness(0) invert(1);
}
/* `width`/`height` on the tags are the intrinsic 600x114, which reserves the
   box against CLS; `height` + `width:auto` resolves it to ~126px wide. */
.hp-quote-logo { margin-bottom: 26px; }
/* No margin of its own: `.hp-results` is a grid with an 18px gap, so the logo
   sits on the same rhythm the heading, strip and citation already agree on.
   Intrinsic 916x468, so 24px tall resolves to ~47px wide. */
.hp-quote-text {
  margin: 0; font-size: clamp(18px, 2vw, 24px); line-height: 1.35;
  color: var(--on-ash); max-width: 30ch;
}
.hp-attrib { margin-top: 30px; display: grid; gap: 2px; }
/* `.hp-attrib-name` inherited the page text colour, which is `--ink` — 1.04:1
   here. Inheriting is no longer a no-op on this band, so it is set explicitly. */
.hp-attrib-name { font-weight: 700; font-size: 15px; color: var(--on-ash); }
.hp-attrib-co { font-size: 15px; color: var(--on-ash-2); }

/* The metrics are CDW's and the quote above is Teradata's. `.hp-results` is
   the wrapper that keeps them apart: it owns the heading that says whose
   numbers these are, so the strip can never read as the quoted customer's own
   results. It is the container, not decoration — see the template note. */
.hp-results { display: grid; align-content: start; gap: 18px; }
.hp-results-h {
  margin: 0; font-size: 12.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--on-ash-2);
}
.hp-metrics { list-style: none; margin: 0; padding: 0; display: grid; gap: 18px; }
/* Identical surface to `.hp-quote` above — see the note there. */
.hp-metric {
  padding: 22px 24px;
  border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-md);
  background: rgba(255,255,255,.06);
}
/* Was em-dash-only in `--ink-3` while no metric was approved, then full
   `--ink` once real published figures landed — a muted number reads as a
   sample, which is the mistake the em-dash rule existed to prevent. That
   reasoning is unchanged and `--on-ash` is its dark-band equivalent: the
   strongest value the surface has, not a step down from it. */
.hp-metric-val {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 40px; line-height: 1; color: var(--on-ash);
}
.hp-metric-label {
  display: block; margin-top: 10px; font-size: 13px; color: var(--on-ash-2);
  text-transform: uppercase; letter-spacing: var(--tracking-caps); font-weight: 700;
}
/* The "Source: CDW published case study." line (VIV-8855). No margin of its
   own — `.hp-results` is a grid with an 18px gap, so the spacing is the same
   spacing the heading and the strip already agree on. Sentence case, unlike
   `.hp-results-h`: it is a citation, not a second label.

   SUPERSEDED (VIV-8863). This line used to read `--ink-2`, NOT `--ink-3`,
   because the section background was `--light-cream` and `--ink-3` on cream
   measured 4.21:1 — under the AA floor for 13px text. That background is gone,
   and on `--dark-ash` BOTH of those inks are invisible (1.34:1 and 3.17:1), so
   the distinction they encoded no longer exists. The line now takes
   `--on-ash-2`, the same secondary as the heading above it, at 8.74:1. The
   reason it is visually quieter than the numbers is unchanged; only the
   mechanism is. (`.hp-shotframe-title` still cites the 4.21:1 measurement and
   still sits on cream — that one is untouched and still correct.) */
.hp-results-source { margin: 0; font-size: 13px; color: var(--on-ash-2); }

/* ============================================================
   S9 — ARCHITECTURE
   ============================================================ */
.hp-arch { background: var(--white); }
.hp-arch-grid {
  margin-top: 22px;
  display: grid; grid-template-columns: 1.05fr 1fr; gap: 52px; align-items: center;
}
.hp-arch-copy .hp-h2 { max-width: 16ch; }
.hp-chips {
  list-style: none; margin: 30px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 12px;
}
.hp-chip {
  padding: 10px 16px; font-size: 14px; font-weight: 600; color: var(--ink-2);
  background: var(--brand-tint); border: 1px solid var(--brand-tint-2);
  border-radius: var(--r-sm);
}

/* ---------- Official partner badges (VIV-8927) ---------- */
/* The same two files /about/ ships, under the chips they corroborate. These
   declarations are COPIED from `.ab-badges` / `.ab-badge` (src/about.css)
   rather than the live classes being reused, for the same reason `.hp-boundary`
   copies `.boundary` below: `.ab-badges` is written for /about/'s dark
   partnership band and paints a translucent-WHITE panel behind the tiles with a
   white hairline border, both of which vanish on this section's `--white`.

   The tiles arrive with their own dark navy field, so on white they need no
   panel at all to separate from the surface; what they do need is the same
   breathing room, so the 16px inter-badge gap is kept verbatim and the panel's
   padding is dropped rather than re-tinted.

   `inline-flex` + `flex-wrap`, so at 390px the pair wraps under itself instead
   of forcing the copy column wider than the single-column grid allows. The
   intrinsic 108x108 is pinned on the imgs as width/height attributes as well,
   so the row reserves its height before the PNGs decode and the chips above it
   never reflow. */
.hp-arch-badges {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: 16px;
  margin-top: 26px;
}
.hp-arch-badge { display: block; width: 108px; height: auto; }

/* ---------- Instance-boundary diagram (VIV-8774) ---------- */
/* Replaces the dashed "Diagram slot". /platform/ already ships an instance
   boundary as `.boundary` + `.b-tag` (src/platform.css) and these are its
   declarations, re-tinted for a WHITE surface: the live rules are written for
   that page's navy band and set white/62%-alpha type that is invisible here.
   Geometry (1.5px dashed, --r-lg, 22px padding, the -11px notched tag) is kept
   identical so the two pages read as one drawing system.

   The dashed border is the ONE deliberate exception to this page's
   "dashed = placeholder" rule: here it is the drawing, not a missing asset.
   It is teal and carries a label, where every placeholder box was grey-dashed
   and carried an uppercase "… slot" caption, so the two never read alike. */
.hp-boundary {
  position: relative;
  padding: 22px;
  /* rgba(63,193,199,.45) is `.boundary`'s own stroke, kept verbatim: it is the
     one declaration that has to survive the re-tint, because the dashed LINE is
     the whole drawing. `--brand-tint-2` was tried first and disappeared against
     the `--brand-tint` fill behind it. */
  border: 1.5px dashed rgba(63, 193, 199, .45);
  border-radius: var(--r-lg);
  background: var(--brand-tint);
}
.hp-boundary-tag {
  position: absolute; top: -11px; left: 18px;
  padding: 0 10px;
  /* The tag sits ON the border, so it needs the section's own background to
     punch the gap — `.hp-arch` is --white. */
  background: var(--white);
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  /* --brand-deep, not --brand: the prototype's AA-safe teal (VIV-8759). */
  color: var(--brand-deep);
}
/* Drawn in CSS with real text nodes rather than SVG <text>: an SVG scales its
   own type with the column, and at 390px this diagram's 12.5px body rendered at
   9.9px. Only the arrows are SVG, at a fixed 14x26. No transition, animation or
   transform anywhere below, so there is nothing for `prefers-reduced-motion` to
   switch off. Keep it that way. */
.hp-arch-node {
  background: var(--white);
  border: 1px solid var(--brand-tint-2);
  border-radius: var(--r-md);
  padding: 14px 16px;
}
/* The middle node is us; the teal border is the only thing distinguishing it,
   deliberately — a fill would make the other two read as disabled. */
.hp-arch-node--brand { border-color: var(--brand); border-width: 1.5px; }
.hp-arch-t { display: block; font-size: 15px; font-weight: 700; color: var(--ink); }
.hp-arch-s { display: block; margin-top: 4px; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.hp-arch-arrow {
  display: block; margin: 8px auto;
  fill: none; stroke: var(--brand-deep); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
/* --brand-deep, not --brand: 4.56:1 on the --brand-tint wash this box sits on,
   where --brand measures well under the AA floor for 11.5px type (VIV-8759). */
.hp-arch-edge {
  margin: 14px 0 0; text-align: center;
  font-size: 11.5px; font-weight: 700; color: var(--brand-deep);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
}

/* ============================================================
   S10 — CLOSING CTA (teal band)
   ============================================================ */
.hp-closing { background: var(--brand); padding: 76px 0; }
.hp-closing-grid {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 32px;
}
.hp-closing-copy { max-width: 62ch; }
.hp-closing-h { color: var(--brand-fg); }
.hp-closing-b { color: var(--brand-fg); opacity: .84; }
.hp-closing-actions { display: flex; flex-wrap: wrap; gap: 14px; }
.hp-closing .btn-brand { --bg: var(--ink); --fg: var(--white); }
.hp-closing .btn-brand:hover { --bg: var(--ink-2); }
.hp-closing .btn-ghost.btn-on-dark { --fg: var(--brand-fg); border-color: rgba(32,32,32,.32); }
.hp-closing .btn-ghost.btn-on-dark:hover { --bg: rgba(255,255,255,.22); border-color: var(--ink); }

/* ============================================================
   NARROW SCREENS
   880px is an existing site breakpoint (styles.css), reused here on purpose.
   Below it the three offers read in sequence as Align -> Decide -> Act and
   nothing requires a tap to reveal an offer name.

   VIV-8749 changes HOW, not whether. Until now this block hid `.hp-tabframe`
   outright and swapped in a separate `<ol class="hp-stack">`; the artwork lives
   inside the panels, so the narrow view had no media at all. The frame now
   stays, and the rules below UNSTACK it: tablist gone, panels out of their
   shared grid cell, `[hidden]` overridden back to visible. Same three panels,
   same three iframes, one markup tree.
   ============================================================ */
@media (max-width: 880px) {
  /* Hero -> tabs boundary is ONE `.hp-sec` padding unit, not two (VIV-8749
     item 6, same rule VIV-8739 applied to the logos strip). 32 + 32 = 64.
     `.hp-proto .hp-overview` TIES `.hp-proto .hp-sec` at (0,2,0), so this copy
     has to restate padding-top or the 64px section value above it wins on
     source order — the identical trap the S3 comment documents for
     padding-bottom. */
  .hp-proto .hp-sec { padding: 64px 0; }
  .hp-proto .hp-hero { padding-bottom: 32px; }
  .hp-proto .hp-overview { padding-top: 32px; padding-bottom: 24px; }  /* 24 + 0 + 40 = 64 */
  .hp-proto .hp-logos { padding: 0 0 56px; }  /* top folded into .hp-overview / 64-8 */

  /* The tabs collapse — the board asked for that explicitly — but the frame
     they controlled stays in the layout. `.hp-tabframe { display: none }` used
     to live here and is what took the artwork away. */
  .hp-tablist { display: none; }
  /* `.hp-tabbar` is deliberately NOT in that rule. It is not a tab control —
     it is the "partner across all three phases" statement (VIV-9046 item 1),
     and with the tablist gone the unstacked panels are the only thing left for
     it to head, so hiding it here would drop the section's only framing line at
     exactly the width that needs it most. With the tabs gone it is a
     free-standing header above three free-standing cards, so it takes the full
     radius instead of the frame's top-only one and the same 18px gap
     `.hp-panels` uses between the cards. */
  .hp-tabbar { border-radius: var(--r-md); margin-bottom: 18px; }

  /* Out of the shared cell: three cards in flow, in source order.
     `minmax(0, 1fr)`, not `1fr`: an `fr` track's default `min-width: auto`
     lets a card grow past the wrap, and MEASURED at 320px it does — the new
     "VividCharts Platform" CTA is a `.btn`, and styles.css gives `.btn`
     `white-space: nowrap`, so its min-content is 262.8px against 242px of
     available panel width. The track cap is the belt; the wrapping rule in the
     560 block below is the braces. Same reasoning as the base `.hp-panel`
     rule, which caps both of its tracks for the iframe. */
  .hp-panels { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .hp-panels > .hp-panel { grid-area: auto; }

  /* `hidden` is NOT removed — homepage-prototype.js still owns it and is
     untouched by this ticket, and `select()` is unreachable anyway with the
     tablist gone. Overriding the COMPUTED display and visibility is what puts
     the subtree back in the accessibility tree and the tab order, which is the
     whole requirement: all three offers readable with no interaction. The base
     rule already forces `display: grid` on `[hidden]`; this adds the other
     two halves. */
  .hp-panel[hidden] { visibility: visible; pointer-events: auto; }

  /* Every panel is now a free-standing card, not the bottom half of a tab
     frame: single column, its own top border and full radius. */
  .hp-panel {
    grid-template-columns: minmax(0, 1fr); gap: 24px;
    padding: 24px;
    border-top: 1px solid var(--line);
    border-radius: var(--r-md);
  }

  /* The narrow-only copy line: the stage as a small brand-coloured eyebrow with
     the headline under it. NOT upper-cased — this renders the same
     board-authored `stage` string as the wide tab label, and a `text-transform`
     here would make the two presentations disagree on copy the frontmatter
     exists to keep identical.
     `.hp-panel-offer` sat between the two at 24px as the dominant line, and
     VIV-9046 item 6 deleted it: the board does not want the offer names in
     mobile. The headline inherits the dominant slot, so it keeps its 10px
     offset from the eyebrow above it. */
  .hp-panel-stage {
    display: block; font-size: 13px; font-weight: 700;
    letter-spacing: .01em; color: var(--brand-deep);
  }
  .hp-panel-h { margin-top: 10px; font-size: 19px; font-weight: 700; max-width: none; }
  .hp-panel-body { max-width: none; }

  .hp-problem-grid,
  .hp-detail-grid,
  .hp-detail-grid--copyfirst,
  .hp-arch-grid,
  .hp-story-grid { grid-template-columns: 1fr; gap: 34px; }
  /* One step down with the quote: `.hp-quote-text` bottoms out at 18px here,
     so a 24px mark would out-weigh the words it introduces. The CDW mark steps
     with it — the two are the same optical height by design, and the grid is
     single-column below this breakpoint, so they stack and any size difference
     between them becomes a direct vertical comparison. */
  .hp-quote-logo,
  .hp-results-logo { height: 20px; }
  .hp-quote-logo { margin-bottom: 22px; }

  /* The 12/7 crop comes OFF below the tab-frame breakpoint. It exists to stop
     a near-square 1600px shot eating 1017px of a 1120px-wide desktop column;
     at 348px the whole image is only 316px tall, so there is nothing to
     rescue, and cropping it there just throws away two thirds of the workspace
     for no height saved.

     The ratio is RESTATED as the source's own 1600/1453 rather than set to
     `auto`. `auto` looks like the obvious way to say "intrinsic ratio", but it
     overrides the UA stylesheet's width/height-attribute mapping, so the box
     reserves NOTHING until the bytes arrive: measured, that pushed every
     section below this one down by a further 316px on load at 390px. An
     explicit ratio keeps the reservation and, being exactly the source's own,
     crops nothing. Keep it in sync with the `width`/`height` attributes. */
  .hp-shot--crop { aspect-ratio: 1600 / 1453; }

  /* Screenshot after the copy on narrow screens. The old comment here read
     "they carry no information yet" — since VIV-8774 they do, but the order
     still holds: the copy is what a phone reader needs first, and a dense
     product screenshot is the supporting detail. */
  .hp-detail-grid .hp-shotframe { order: 2; }
  .hp-detail-grid .hp-detail-copy { order: 1; }

  .hp-cards { grid-template-columns: 1fr; }
  .hp-closing-grid { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 560px) {
  .hp-proto .hp-sec { padding: 52px 0; }
  /* Hero -> tabs: 26 + 26 = 52, one `.hp-sec` unit (VIV-8749 item 6). The
     padding-top restate is mandatory — this block re-declares the tying
     `.hp-proto .hp-sec` above. */
  .hp-proto .hp-overview { padding-top: 26px; padding-bottom: 12px; }  /* 12 + 0 + 40 = 52 — .trust-strip does not step until 540 */
  .hp-proto .hp-logos { padding: 0 0 44px; }  /* top folded into .hp-overview / 52-8 */
  /* `.hp-hero { padding: 64px 0 56px }` used to sit here and was dead for the
     same specificity reason as the base rule — the effective value at this
     width is and was `.hp-proto .hp-sec`'s 52px top. Only the bottom changes. */
  .hp-proto .hp-hero { padding-bottom: 26px; }
  .hp-hero-h1 { max-width: none; }
  /* Full-width, centred CTAs — the treatment `.hp-stack-cta` used to carry for
     the deleted narrow-screen stack, now applied to the panel CTA that
     replaced it. `white-space: normal` is the part that is genuinely NEW and
     load-bearing: styles.css `.btn` is `nowrap`, and "VividCharts Platform"
     (VIV-8749 item 3) is long enough at 13.5px + caps tracking that it cannot
     fit one line inside a 320px panel — it pushed `document.scrollWidth` to
     327 against a 320 viewport until this rule let it break. Scoped to
     `.hp-panel-actions` so styles.css and every other `.btn` on the page are
     untouched. */
  .hp-hero-actions .btn,
  .hp-panel-actions .btn { width: 100%; justify-content: center; }
  .hp-panel-actions .btn { white-space: normal; text-align: center; }
  .hp-panel-slot { min-height: 200px; padding: 22px; }
  /* ...but the media variant stays flush: its height is the aspect ratio's. */
  .hp-panel-slot-media { min-height: 0; padding: 0; }
  .hp-metric-val { font-size: 34px; }
}

/* 540px is styles.css's OWN breakpoint, where `.trust-strip` drops to 28px/4px
   of built-in padding. This block exists only so the logos boundary keeps
   matching the page rhythm past that step (top 24+0+28 = 52, bottom 52-4 = 48);
   it must stay after the 560px block above, which re-declares the tying
   `.hp-proto .hp-sec` — and which also carries the 12px `.hp-overview` value
   this block has to restate. */
@media (max-width: 540px) {
  .hp-proto .hp-overview { padding-bottom: 24px; }  /* 24 + 0 + 28 = 52 */
  .hp-proto .hp-logos { padding: 0 0 48px; }
}
