/* ==========================================================================
   RIA Advisory — PRODUCT DETAIL PAGE  ·  rebuild 2026-08-13 (v7)

   Template for all nine product detail pages (products/<slug>/index.html).
   The first build is products/test-automation/.

   2026-08-13 REVISION — six pieces of client feedback against their
   reference product site ("similar but not 100% copied"):
     · NEW Challenge/Value split after the hero, panels sliding in from
       opposite sides (right delayed .15s) via per-class [data-r] overrides.
     · How It Works rebuilt as VIDEO TABS — two wide tabs with progress bars
       driven by the playing video; the pair auto-advances and loops.
     · Key Features expander replaced with a LIGHT version of their card
       carousel: 4 upright cards, "+" expands in place, prev/next arrows.
     · Testimonial restructured to their editorial shape: name at h2 scale,
       big italic display quote (navy → sky), photo slot over a grid texture.
     · Integrations rebuilt as a vertical auto-scroll marquee of cards with
       a left sticky heading block.
     · Why-choose tiles re-lit: white cards with a gradient hover payoff.

   v7 — five corrections after the client reviewed v6. Each one is a fix for
   something that was measurably wrong, not a restyle; read the band comment
   before changing any of them back:
     · CHALLENGE/VALUE no longer rides [data-r]. It sat inside the site
       reveal's load-time band, so it animated before anyone scrolled to it.
       It now has its own IntersectionObserver and a 56px travel you can see.
     · VIDEO TABS: the progress fill is the WHOLE TAB BACKGROUND swept left
       to right via scaleX(var(--p)), not a 4px hairline. The panel became a
       2-column split — tinted copy card + video on a gradient bed.
     · KEY FEATURES: fixed min-height and the absolutely pinned "+" are gone
       (they produced a dead gap); a one-line teaser now carries the card.
     · TESTIMONIAL: the empty photo box is deleted. The quote is the band —
       large, NOT italic — with a compact attribution row. The photo variant
       lives behind `.pd-say--photo`, dormant until an asset exists.
     · WHY CHOOSE: -10px lift on a .4s curve, declared at a specificity that
       actually beats `.js .r3 [data-r].in { transform: none }`.

   v8 — three more corrections:
     · TESTIMONIAL now uses the real headshot (assets/img/vijay.png) via the
       `.pd-say--photo` variant that was already written and dormant.
     · VIDEOS are downloaded locally (assets/video/) instead of hotlinked from
       the client's S3 bucket — fixes the "no supported format" playback
       failure — and `preload="auto"` so buffering starts on page load rather
       than waiting for the tab to scroll into view.
     · KEY FEATURES rebuilt again: the one-line teaser is gone (collapsed
       cards show ONLY icon + title, per instruction); "+" now reveals a
       single short sentence, not a paragraph-plus-bullet-list ("pointers");
       and the row is a genuine slider at every width — prev/next always
       scroll the track by one card, never toggle which card is open. A 5th
       card (Audit-Ready Reporting) was added, which is why a slider is now
       load-bearing rather than decorative.

   Band map (backgrounds must keep alternating — check before touching):
     1  HERO            dark navy stage
     2  SUB-NAV         slim sticky strip (chrome, not a band)
     3  CHALLENGE/VALUE white band · white panel + sky-tint panel
     4  HOW IT WORKS    paper · video tabs, then three RULED columns
     5  KEY FEATURES    white band · light card carousel
     6  PRODUCT LEAD    tinted band · editorial testimonial + grid texture
     7  INTEGRATIONS    paper · vertical marquee rail
     8  WHY CHOOSE      white (.section--paper) · three light cards
     9  CASE STUDY      paper · existing cs-* component (untouched)
     10 CTA             white · centred warm-gradient panel

   Namespace: pd-  . Everything else is a SHARED class defined elsewhere —
   .wrap/.wrap--wide/.section/.section--paper/.btn/.grad-text/.oc-head/
   .graded/.arrow and the cs-* case-study family. Nothing here overrides them.

   CLIENT RULES baked in (do not "improve" these away):
     · NO circles, blobs or radial gradients — linear only. Even the
       window-chrome dots on the UI mocks are rounded SQUARES. (The hero
       dashboard's donut is a data-viz chart from the client's reference,
       not a decorative motif.)
     · No "01 · / 02 ·" numbering in visible copy.
     · Dark bands are allowed on THIS page (the client pointed at their own
       dark hero as the better reference) but never two dark bands in a row.
     · Never wrap motion in a reduced-motion media query — this client's
       machine reports "reduce" system-wide and it kills every animation.
     · The hero dashboard mock reproduces the client's own reference
       screenshot (figures included) — do not restyle it as abstract bars.

   REVEAL OVERRIDES: the site rule is `.js .r3 [data-r]` (0,3,0) with `.in`
   at (0,4,0) — and `.in` leaves `clip-path: inset(0)` behind, which clips
   shadows/lifts forever. Every pd- element that carries [data-r] AND casts
   a shadow gets a higher-specificity `.in` rule here that sets
   `clip-path: none`. Do the same on any clone of this page.

   HOUSE RULES: clamp()/calc() need SPACES around +/- . Grid/flex children
   that hold text need min-width: 0 .
   ========================================================================== */

.r3 main > section { scroll-margin-top: 138px; }

/* ==========================================================================
   HEADER OPACITY FIX — every page that loads this stylesheet uses a compact
   .pd-hero panel whose heading sits immediately under the header. The site
   default (round3-final.css) is a translucent 85%-opacity blurred header,
   tuned for the homepage's full-viewport video hero: by the time a scroll
   is enough to make it stick, the hero's own headline (vertically centred
   in 100vh) is long gone, so only plain background shows through. Here the
   heading starts right at the top of a short panel, so a small scroll makes
   the sticky header visually collide with — and let show through — the
   hero H1, reading as broken overlapping text. Force it opaque on this
   whole page family; homepage/industries pages don't load this file and
   keep the frosted-glass look. */
.r3-header { background: var(--paper); backdrop-filter: none; -webkit-backdrop-filter: none; }

/* ==========================================================================
   BAND HEADS — variants so consecutive bands never open the same way.
     .pd-bhead            title left, supporting slot right
     .pd-bhead--right     same, supporting slot flushed right
   The centred .oc-head is used ONCE, above the case study.
   ========================================================================== */
.pd-bhead { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: clamp(1.2rem, .9rem + 2vw, 3rem); align-items: end;
  margin-bottom: clamp(2rem, 1.5rem + 2vw, 3.2rem); }
.pd-bhead > * { min-width: 0; }
.pd-bhead__lbl { display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--font-mono, monospace); font-size: .72rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--navy); }
.pd-bhead__lbl::before { content: ""; width: 26px; height: 1px; background: var(--orange); }
.pd-bhead h2 { font-size: clamp(1.7rem, 1.15rem + 2.4vw, 2.8rem); line-height: 1.08;
  letter-spacing: -.03em; margin: .85rem 0 0; }
.pd-bhead__aside { display: flex; flex-direction: column; align-items: flex-start; gap: 1.1rem; }
.pd-bhead__aside p { color: var(--slate); font-size: 1rem; line-height: 1.64; }
.pd-bhead--right .pd-bhead__aside { align-items: flex-end; }
.pd-bhead--right .pd-bhead__aside p { text-align: left; }
/* A full-width photo strip BETWEEN a band head and whatever follows it (a
   ruled list, a stat row) — not stacked inside .pd-bhead__aside, which
   forces the two heading columns to bottom-align against very different
   content heights and opens a dead gap above the shorter one. */
.pd-photoband { margin: clamp(2rem, 1.5rem + 2vw, 3.2rem) 0; border-radius: 20px;
  overflow: hidden; box-shadow: 0 30px 60px -40px rgba(11,24,48,.4); }
.pd-photoband img { width: 100%; height: clamp(220px, 22vw, 380px); object-fit: cover; display: block; }

@media (max-width: 900px) {
  .pd-bhead { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .pd-bhead--right .pd-bhead__aside { align-items: flex-start; }
}

/* ==========================================================================
   BAND 1 · HERO — dark cinematic stage
   Copy left at large scale against small mono labels; the product mock on the
   right runs PAST the container edge (the section clips it at the viewport)
   and is tilted a few degrees for depth. This is the one band the client
   explicitly wanted dark.
   ========================================================================== */
/* ==========================================================================
   BAND 1 · HERO — the client's own product hero
   A large rounded panel floats on the dark navy gradient: headline, one
   paragraph and a white "Schedule a Demo" pill on the left; on the right a
   faithful mock of the Test Automation dashboard in a browser frame.
   ========================================================================== */
.pd-hero { position: relative; isolation: isolate; overflow: hidden; color: #fff;
  background:#fff;
  padding-top:30px;
  margin-bottom: 30px}
/* The shared .wrap--wide caps at 1340px — far narrower than the reference,
   whose panel spans ~92% of the viewport. The hero (and ONLY the hero) gets
   a wider container so the dashboard mock has the width its 4 KPI cards
   need; at 1340px they squeeze and every label wraps onto three lines. */
.pd-hero > .wrap { max-width: 1760px;
  padding-inline: clamp(1.25rem, .6rem + 2.4vw, 3.4rem); }


/* The panel — the "large rounded rectangle" from the reference. Four things
   are load-bearing; do not "tidy" any of them back:

   1. IT STAYS INSIDE THE CONTAINER. Normal .wrap margins on BOTH sides, all
      four corners rounded. An earlier build bled it to the viewport edge —
      wrong, the reference's panel is a contained card floating on white.
   2. THE COPY IS BOTTOM-ALIGNED, not vertically centred. The reference
      leaves the panel's whole upper-left area empty except the badge, then
      sets headline → lead → button flush to the bottom padding.
   3. THE MOCK IS FLUSH to the panel's RIGHT and BOTTOM edge, inset from the
      top only. It is clipped by the panel's `overflow: hidden`, so the
      dashboard is cut mid-chart exactly like the reference.
   4. ONE PADDING VALUE (--pd-pad) drives the badge inset, the copy's left
      and bottom, and the mock's top inset — they line up in the reference.
   ========================================================================== */
.pd-hero__grid { position: relative; z-index: 1; overflow: hidden; display: grid;
  --pd-pad: clamp(1.5rem, 1rem + 2.2vw, 3.75rem);
  --pd-badge: 52px;                        /* kept in sync with .pd-ui__badge */
  grid-template-columns: minmax(0, .71fr) minmax(0, 1fr);
  gap: 0; align-items: stretch;
  border-radius: 28px;
  padding: 0;
  /* Deep navy bottom-left → brighter blue top-right, along the diagonal.
     Linear only (client rule) and no translucency: the panel sits on WHITE
     now, so alpha would wash it out to the wrong colour entirely. */
  background: linear-gradient(32deg, #0B1D3B 0%, #102A50 42%, #16376B 68%, #1D4E8E 100%);
  box-shadow: 0 40px 80px -54px rgba(11,24,48,.6); }
.pd-hero__grid > * { min-width: 0; }

/* Bottom-aligned. The big top padding is what reserves the reference's
   empty upper area (the badge lives in it, absolutely positioned). */
/* TOP PADDING MUST CLEAR THE BADGE. The badge is absolutely positioned at
   (--pd-pad, --pd-pad) and is OUT OF FLOW, so it reserves no space. With only
   --pd-pad here the copy runs straight under it — invisible on Test
   Automation, whose tall dashboard mock forces a tall panel, but a direct
   collision on any page with longer copy (Experience 360) or on a smaller
   laptop screen, where the copy grows and its top edge climbs into the
   badge. Declared as a sum so it stays correct as --pd-pad scales. */
.pd-hero__copy { min-width: 0; align-self: end;
  padding: calc(var(--pd-pad) + var(--pd-badge) + 1rem) clamp(1.2rem, .8rem + 1.6vw, 2.6rem)
           var(--pd-pad) var(--pd-pad); }
/* Below 1040px the badge is hidden, so the clearance is dead weight. */
@media (max-width: 1040px) {
  .pd-hero__copy { padding-top: var(--pd-pad); }
}
/* The reference headline is THIN and large — not the 800 display weight. */
.pd-hero h1 { color: #fff; font-weight: 300; font-size: clamp(2rem, 1.35rem + 2.2vw, 3.25rem);
  line-height: 1.08; letter-spacing: -.015em; margin: 0 0 1.2rem; }
.pd-hero__lead { color: rgba(228,236,247,.84); font-size: clamp(.95rem, .92rem + .22vw, 1.06rem);
  line-height: 1.72; max-width: 34rem; margin: 0 0 2.1rem; }

.pd-hero__cta { display: flex; gap: .8rem; flex-wrap: wrap; }
/* The reference CTA is a LARGE WHITE pill with dark text, a dark outline and
   an arrow — not the solid orange button. */
.pd-hero__cta .btn { padding: .95em 1.6em; font-size: 1rem; }
.pd-hero__cta .btn--accent { background: #fff; color: var(--navy);
  border: 1px solid rgba(11,24,48,.22);
  box-shadow: 0 18px 40px -20px rgba(0,0,0,.55); }
.pd-hero__cta .btn--accent:hover { background: var(--orange); color: #241503;
  border-color: var(--orange);
  box-shadow: 0 16px 34px -16px rgba(247,148,31,.75); }

/* Flush to the panel's right + bottom edge; inset from the top only. */
.pd-hero__media { position: relative; min-width: 0; align-self: end;
  margin-top: var(--pd-pad); }
.pd-hero__media .pd-ui { width: 100%; max-width: none;
  border-radius: 12px 0 0 0; border-right: 0; border-bottom: 0; }
/* Image hero — every product EXCEPT Test Automation. Their screenshots
   already ship with browser chrome baked into the picture, so the image is
   the whole visual: no .pd-ui frame around it, just the same flush-right,
   flush-bottom placement and the one rounded top-left corner. */
.pd-hero__shot { display: block; width: 100%; height: auto;
  border-radius: 12px 0 0 0;
  box-shadow: 0 40px 80px -50px rgba(0,0,0,.65); }

/* ==========================================================================
   .pd-hero--light — the LIGHT panel variant
   Careers, Life at RIA and Leadership use the same soft sky wash the client
   approved on the Contact page instead of the navy product panel. Defined
   ONCE here rather than copied into three page stylesheets, so the four
   light heroes cannot drift apart.
   Selectors are `.pd-hero--light .x` (0,2,0) so they beat the base
   `.pd-hero__grid` rules (0,1,0) without !important.
   ========================================================================== */
.pd-hero--light { color: var(--ink); }
.pd-hero--light .pd-hero__grid {
  background: linear-gradient(32deg, #DCE9F7 0%, #E8F1FA 38%, #F2F7FC 68%, #FBFDFE 100%);
  border: 1px solid rgba(29,65,121,.16);
  box-shadow: 0 30px 60px -46px rgba(11,24,48,.35); }
/* Navy badge, not orange — on a light panel the orange tile shouts. */
.pd-hero--light .pd-ui__badge {
  background: linear-gradient(150deg, var(--navy) 0%, #2f5f9e 100%);
  box-shadow: 0 16px 28px -14px rgba(29,65,121,.7); }
.pd-hero--light h1 { color: var(--ink); }
.pd-hero--light .pd-hero__lead { color: var(--slate); }
/* The white pill disappears on a light panel — solid navy instead. */
.pd-hero--light .pd-hero__cta .btn--accent {
  background: var(--navy); color: #fff; border: 1px solid var(--navy);
  box-shadow: 0 16px 30px -18px rgba(29,65,121,.8); }
.pd-hero--light .pd-hero__cta .btn--accent:hover {
  background: #2f5f9e; color: #fff; border-color: #2f5f9e;
  box-shadow: 0 18px 34px -16px rgba(29,65,121,.9); }
/* round3v2.css's `.r3 .btn--ghost` paints EVERY ghost button solid navy
   site-wide (it was written for dark hero panels only). On a light panel
   that leaves navy text on a navy fill — invisible. Background must be
   reset here too, not just color. */
.pd-hero--light .pd-hero__cta .btn--ghost { background: transparent; color: var(--navy); border-color: rgba(29,65,121,.3); }
.pd-hero--light .pd-hero__cta .btn--ghost::after { content: none; }
.pd-hero--light .pd-hero__cta .btn--ghost svg { color: var(--navy); }
.pd-hero--light .pd-hero__cta .btn--ghost:hover { background: rgba(29,65,121,.06); color: var(--navy); }
.pd-hero--light .pd-hero__cta .btn--ghost:hover svg { color: var(--navy); }

@media (max-width: 1040px) {
  /* Stacked: the flush-right trick stops making sense — the mock becomes a
     normal contained card again. */
  .pd-hero__grid { grid-template-columns: minmax(0, 1fr); border-radius: 24px;
    padding: var(--pd-pad); gap: clamp(1.6rem, 1.2rem + 2vw, 2.6rem); }
  .pd-hero__copy { padding: 0; align-self: start; }
  .pd-hero__lead { max-width: 60ch; }
  .pd-hero__media { margin-top: 0; align-self: stretch; }
  .pd-hero__media .pd-ui { border-radius: 12px; border-right: 1px solid rgba(11,24,48,.14);
    border-bottom: 1px solid rgba(11,24,48,.14); }
}
@media (max-width: 620px) {
  .pd-hero__grid { border-radius: 18px; }
}

/* ==========================================================================
   .pd-hero--svc — service sub-page hero refinement (2026-09-01)
   The base treatment bleeds the visual to the panel's right + bottom edge,
   uncropped, at whatever height the image's own ratio dictates — correct
   for a product dashboard screenshot, wrong for a stock photo: on the
   service pages it read as one oversized, uncontained image dominating the
   panel and stretching it unnecessarily tall. This modifier frames the
   photo as a normal contained card instead — inset on every side, fixed
   ratio, fully rounded — and gives copy equal (not minority) width. */
.pd-hero--svc .pd-hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, .86fr); align-items: center; }
.pd-hero--svc .pd-hero__media { align-self: center; margin-top: 0;
  padding: var(--pd-pad) var(--pd-pad) var(--pd-pad) 0; }
.pd-hero--svc .pd-hero__shot { border-radius: 20px; aspect-ratio: 4 / 3; object-fit: cover;
  box-shadow: 0 30px 60px -30px rgba(11,24,48,.45); }
@media (max-width: 1040px) {
  /* The unconditional rule above has higher specificity (0,2,0) than the
     base .pd-hero__grid mobile stack (0,1,0) inside this same breakpoint in
     round3-final.css's neighbour block, so without repeating it here it
     silently wins at EVERY width and the hero never stacks on tablet/phone —
     text and photo stay side-by-side down to 375px, both crushed unreadable. */
  .pd-hero--svc .pd-hero__grid { grid-template-columns: minmax(0, 1fr); align-items: stretch; }
  .pd-hero--svc .pd-hero__media { padding: 0; }
  .pd-hero--svc .pd-hero__shot { aspect-ratio: 16 / 10; }
}

/* ==========================================================================
   BAND 2 · STICKY SUB-NAV
   A wayfinding strip, not a card. Sits directly under the 78px site header
   and highlights the section you are reading.
   ========================================================================== */
.pd-subnav { position: sticky; top: 78px; z-index: 60;
  background: rgba(255,255,255,.94); backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--line); }
.pd-subnav__in { display: flex; align-items: center; gap: clamp(.6rem, .4rem + .8vw, 1.4rem);
  height: 56px; overflow-x: auto; scrollbar-width: none; }
.pd-subnav__in::-webkit-scrollbar { display: none; }
.pd-subnav__brand { flex: none; display: flex; align-items: center; gap: .55rem;
  padding-right: clamp(.6rem, .4rem + .8vw, 1.3rem); border-right: 1px solid var(--line);
  font-family: var(--font-mono, monospace); font-size: .68rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--navy); white-space: nowrap; }
.pd-subnav__brand i { color: var(--orange); font-size: .8rem; }
.pd-subnav__links { display: flex; align-items: center; gap: .1rem; }
.pd-subnav__links a { position: relative; padding: .5rem .8rem; font-size: .86rem; font-weight: 600;
  color: var(--slate); white-space: nowrap; transition: color .25s ease; }
.pd-subnav__links a::after { content: ""; position: absolute; left: .8rem; right: .8rem; bottom: -1px;
  height: 2px; background: var(--navy); transform: scaleX(0); transform-origin: left;
  transition: transform .35s cubic-bezier(.22,1,.36,1); }
.pd-subnav__links a:hover { color: var(--navy); }
.pd-subnav__links a.is-active { color: var(--navy); }
.pd-subnav__links a.is-active::after { transform: scaleX(1); }
.pd-subnav__cta { flex: none; margin-left: auto; display: inline-flex; align-items: center; gap: .45rem;
  font-size: .84rem; font-weight: 600; color: var(--navy); white-space: nowrap; }
.pd-subnav__cta i { font-size: .72rem; transition: transform .3s ease; }
.pd-subnav__cta:hover i { transform: translateX(4px); }

@media (max-width: 720px) { .pd-subnav__cta { display: none; } }

/* ==========================================================================
   BAND 3 · CHALLENGE / VALUE — two-panel comparison, animated from the sides
   Left: white bordered panel, red frown tiles (the problem). Right: sky-tint
   panel, blue smile tiles (the payoff). Glyph tiles are rounded SQUARES.

   MOTION — read this before "tidying" it. The panels do NOT carry [data-r].
   round3-final.js's reveal() runs at load and reveals everything already
   inside innerHeight * 0.92; this band sits directly under the hero, so it
   was inside that band on arrival and the slide-in finished before anyone
   scrolled to it — the client's "ye kab animate hota hai, dikh bhi nhi rha".
   The page's own IntersectionObserver (interaction block 5) adds `.is-in`
   to `#challenge` only when the section is 35% on screen, then unobserves.
   The travel is 56px — deliberately large enough to actually SEE.
   Gated on `.js` so a no-JS reader still gets the panels at full opacity.
   ========================================================================== */
.pd-cv { background: var(--white); border-bottom: 1px solid var(--line); }
.pd-cv__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, .8rem + 1vw, 1.6rem); align-items: stretch; }
.pd-cv__p { min-width: 0; border-radius: 20px;
  padding: clamp(1.6rem, 1.3rem + 1.2vw, 2.4rem); }
.pd-cv__l { background: var(--white); border: 1px solid var(--line);
  box-shadow: 0 18px 40px -32px rgba(11,24,48,.35); }
.pd-cv__r { background: linear-gradient(150deg, #EAF3FB 0%, #E0EDF9 58%, #D6E7F6 100%);
  border: 1px solid rgba(77,159,215,.3);
  box-shadow: 0 18px 40px -32px rgba(29,65,121,.4); }
.pd-cv__k { display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--font-mono, monospace); font-size: .68rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; }
.pd-cv__k::before { content: ""; width: 22px; height: 1px; background: currentColor; opacity: .6; }
.pd-cv__k--neg { color: #A63029; }
.pd-cv__k--pos { color: var(--navy); }
.pd-cv__p h3 { color: var(--ink); font-size: clamp(1.25rem, 1.05rem + 1vw, 1.7rem);
  letter-spacing: -.024em; line-height: 1.18; margin: .8rem 0 1.25rem; }
.pd-cv__ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.pd-cv__ul li { display: flex; gap: .85rem; align-items: flex-start; min-width: 0;
  font-size: .95rem; line-height: 1.55; color: var(--slate); }
.pd-cv__ul li > span:last-child { min-width: 0; padding-top: .3rem; }
.pd-cv__r .pd-cv__ul li { color: #35455c; }
/* Glyph tiles: rounded SQUARES, never circles. */
.pd-cv__ico { flex: none; width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: 10px; font-size: .95rem; }
.pd-cv__ico--neg { background: rgba(166,48,41,.1); border: 1px solid rgba(166,48,41,.18); color: #A63029; }
.pd-cv__ico--pos { background: rgba(77,159,215,.16); border: 1px solid rgba(77,159,215,.34); color: #2B6CA8; }

/* Directional reveal — "fade left and fade right smoothly, lil delayed",
   fired by the section's own observer, not the site-wide one. */
.js .r3 .pd-cv__l,
.js .r3 .pd-cv__r { opacity: 0; will-change: transform, opacity;
  transition: opacity .85s cubic-bezier(.22,1,.36,1),
              transform .85s cubic-bezier(.22,1,.36,1); }
.js .r3 .pd-cv__l { transform: translateX(-56px); }
.js .r3 .pd-cv__r { transform: translateX(56px); transition-delay: .18s; }
.js .r3 .pd-cv.is-in .pd-cv__l,
.js .r3 .pd-cv.is-in .pd-cv__r { opacity: 1; transform: none; }

@media (max-width: 900px) {
  .pd-cv__grid { grid-template-columns: minmax(0, 1fr); }
  /* Stacked panels both rise instead of sliding sideways off-canvas — same
     observer, same timing, only the vector changes. */
  .js .r3 .pd-cv__l,
  .js .r3 .pd-cv__r { transform: translateY(40px); }
  .js .r3 .pd-cv.is-in .pd-cv__l,
  .js .r3 .pd-cv.is-in .pd-cv__r { transform: none; }
}

/* ==========================================================================
   BAND 4 · HOW IT WORKS — video tabs, then ruled columns

   THE TAB FILL. The client's reference fills the ACTIVE TAB'S ENTIRE
   BACKGROUND with a soft tint as the video plays — a wide sweep across the
   whole button, left to right. It is NOT a hairline at the bottom edge (the
   previous build's mistake, and the entire complaint). `.pd-vtab__fill` is
   an inset:0 layer BEHIND the label, drawn as scaleX(var(--p)) from a left
   origin; the JS writes --p on every `timeupdate`. scaleX, not width — it
   composites on the GPU, so the sweep is genuinely smooth — and a .12s
   linear transition interpolates the ~250ms gaps between timeupdate ticks
   instead of stepping. Inactive tabs sit at scaleX(0); on a switch the JS
   adds `.is-nofill` for one frame so the outgoing tab snaps back to empty
   rather than sweeping backwards.

   THE PANEL. A 2-column split, matching the reference: a soft-tinted copy
   card on the left (~38%), the video in a rounded frame on the right
   (~62%) over a linear gradient that bleeds slightly past it. The frame
   keeps a light gradient fallback while the remote video loads.
   ========================================================================== */
/* Tab count varies by product (Enrich 360 has three, the rest two), so the
   track reads it off the --tabcount the markup sets rather than hard-coding
   two columns. */
.pd-vtabs__list { display: grid; grid-template-columns: repeat(var(--tabcount, 2), minmax(0, 1fr));
  gap: .8rem; margin-bottom: clamp(1.4rem, 1.1rem + 1.2vw, 2.2rem); }
.pd-vtab { position: relative; isolation: isolate; overflow: hidden;
  min-width: 0; text-align: left; border-radius: 16px;
  border: 1px solid var(--line); background: var(--white);
  padding: 1.15rem 1.3rem 1.25rem; display: grid; gap: .3rem; align-content: start;
  transition: border-color .35s cubic-bezier(.22,1,.36,1),
              box-shadow .35s cubic-bezier(.22,1,.36,1),
              background .35s cubic-bezier(.22,1,.36,1); }
.pd-vtab:hover { border-color: var(--navy); }
.pd-vtab[aria-selected="true"] { border-color: var(--navy);
  background: linear-gradient(180deg, #FCFDFE 0%, #FFFFFF 100%);
  box-shadow: 0 18px 36px -26px rgba(29,65,121,.6); }
/* The playback fill: the WHOLE tab, swept left to right. */
.pd-vtab__fill { position: absolute; inset: 0; z-index: 0; display: block;
  transform-origin: left center; transform: scaleX(0);
  will-change: transform; transition: transform .12s linear;
  background: linear-gradient(90deg, rgba(247,148,31,.22) 0%, rgba(247,148,31,.14) 52%, rgba(77,159,215,.16) 100%); }
.pd-vtab[aria-selected="true"] .pd-vtab__fill { transform: scaleX(var(--p, 0)); }
/* One-frame snap-back when a tab is deselected — no reverse sweep. */
.pd-vtab.is-nofill .pd-vtab__fill { transition: none; }
.pd-vtab__t, .pd-vtab__d { position: relative; z-index: 1; min-width: 0; }
.pd-vtab__t { font-family: var(--font-display, sans-serif); font-weight: 800;
  font-size: 1.05rem; letter-spacing: -.02em; color: var(--ink); }
.pd-vtab__d { font-size: .85rem; line-height: 1.5; color: var(--slate); }

.pd-vpanel[hidden] { display: none; }
.pd-vpanel { animation: pdFade .5s cubic-bezier(.22,1,.36,1); }
.pd-vpanel:focus-visible { outline: 2px solid var(--sky); outline-offset: 8px; border-radius: 8px; }
@keyframes pdFade { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* -- the 2-column split ---------------------------------------------------- */
.pd-vsplit { display: grid; grid-template-columns: minmax(0, .38fr) minmax(0, .62fr);
  gap: clamp(1rem, .8rem + 1.2vw, 1.8rem); align-items: stretch; }
.pd-vsplit > * { min-width: 0; }
/* justify-content: center, NOT a margin-top:auto pill — the card stretches
   to the video's height and pushing the button to the floor would open the
   same dead gap the client called out in the feature cards. */
.pd-vsplit__copy { display: flex; flex-direction: column; justify-content: center; min-width: 0;
  border-radius: 18px; padding: clamp(1.4rem, 1.1rem + 1.2vw, 2.1rem);
  border: 1px solid rgba(77,159,215,.24);
  background: linear-gradient(158deg, #F1F7FC 0%, #EAF3FB 54%, #FDF7EF 100%); }
/* Rounded SQUARE accent, never a dot-circle. */
.pd-vsplit__dot { display: block; width: 14px; height: 14px; border-radius: 5px; flex: none;
  background: linear-gradient(140deg, var(--orange) 0%, #f7b45f 100%);
  box-shadow: 0 6px 14px -8px rgba(247,148,31,.9); }
/* Headings do not inherit colour on this site — set it on the tint. */
.pd-vsplit__copy h3 { color: var(--ink); font-size: clamp(1.15rem, 1rem + .75vw, 1.55rem);
  letter-spacing: -.026em; line-height: 1.2; margin: 1.05rem 0 .7rem; }
.pd-vsplit__copy p { color: var(--slate); font-size: .93rem; line-height: 1.62; margin-bottom: 1.5rem; }
.pd-vsplit__pill { align-self: flex-start;
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .68rem 1.25rem; border-radius: 999px; white-space: nowrap;
  border: 1px solid rgba(29,65,121,.22); background: var(--white);
  font-size: .86rem; font-weight: 600; color: var(--navy);
  transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s ease; }
.pd-vsplit__pill i { font-size: .72rem; color: var(--orange); transition: transform .3s ease; }
.pd-vsplit__pill:hover { background: var(--navy); border-color: var(--navy); color: #fff;
  transform: translateY(-2px); }
.pd-vsplit__pill:hover i { color: #fff; transform: translateX(4px); }

/* The video, on a gradient bed that bleeds slightly past the frame. */
.pd-vstage { position: relative; min-width: 0; display: flex; align-items: center; }
.pd-vstage::before { content: ""; position: absolute; z-index: 0;
  top: 14px; right: -10px; bottom: -12px; left: 18px; border-radius: 22px;
  background: linear-gradient(146deg, rgba(77,159,215,.26) 0%, rgba(29,65,121,.16) 46%, rgba(247,148,31,.14) 100%); }
.pd-vframe { position: relative; z-index: 1; width: 100%;
  aspect-ratio: 16 / 9; border-radius: 18px; overflow: hidden;
  border: 1px solid var(--line);
  background: linear-gradient(150deg, #EEF4FA 0%, #DFEAF5 55%, #D3E2F1 100%);
  box-shadow: 0 44px 88px -52px rgba(11,24,48,.55), 0 4px 16px -10px rgba(11,24,48,.2); }
.pd-vframe video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 900px) {
  .pd-vsplit { grid-template-columns: minmax(0, 1fr); }
  .pd-vstage::before { left: 10px; right: -6px; }
}
@media (max-width: 980px) {
  /* Three tabs stop fitting well before the two-tab pages do. */
  .pd-vtabs__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .pd-vtabs__list { grid-template-columns: minmax(0, 1fr); }
}

/* Ruled columns: hairline separators + a coloured top rule. NOT cards. */
.pd-ruled { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0;
  margin-top: clamp(2.6rem, 2rem + 2.4vw, 4.2rem); border-top: 1px solid var(--line-2); }
.pd-ruled__c { position: relative; min-width: 0;
  padding: clamp(1.5rem, 1.2rem + 1vw, 2.1rem) clamp(1.2rem, .9rem + 1.4vw, 2.2rem) .4rem;
  border-left: 1px solid var(--line-2); }
.pd-ruled__c:first-child { border-left: 0; padding-left: 0; }
.pd-ruled__c:last-child { padding-right: 0; }
.pd-ruled__c::before { content: ""; position: absolute; top: -2px; height: 3px; width: 58px;
  left: clamp(1.2rem, .9rem + 1.4vw, 2.2rem); }
.pd-ruled__c:first-child::before { left: 0; }
.pd-ruled__c:nth-child(1)::before { background: linear-gradient(90deg, var(--navy), #2f5f9e); }
.pd-ruled__c:nth-child(2)::before { background: linear-gradient(90deg, var(--sky), #8fc2e8); }
.pd-ruled__c:nth-child(3)::before { background: linear-gradient(90deg, var(--orange), #f7b45f); }
.pd-ruled__k { font-family: var(--font-mono, monospace); font-size: .64rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--slate); }
.pd-ruled__c h4 { font-size: 1.06rem; letter-spacing: -.018em; line-height: 1.25; margin: .7rem 0 .6rem; }
.pd-ruled__c > p { color: var(--slate); font-size: .91rem; line-height: 1.6; }
.pd-ruled__ul { list-style: none; margin: 1rem 0 0; padding: 0; }
.pd-ruled__ul li { display: flex; gap: .6rem; align-items: flex-start; min-width: 0;
  padding: .5rem 0; border-top: 1px solid var(--line); font-size: .85rem; line-height: 1.5; color: var(--ink); }
.pd-ruled__ul i { flex: none; margin-top: .28rem; font-size: .66rem; color: var(--sky); }

@media (max-width: 900px) {
  .pd-ruled { grid-template-columns: minmax(0, 1fr); }
  .pd-ruled__c { border-left: 0; border-top: 1px solid var(--line-2); padding-left: 0; padding-right: 0; }
  .pd-ruled__c:first-child { border-top: 0; }
  .pd-ruled__c::before { left: 0; }
}

/* ==========================================================================
   BAND · WHERE AI FITS — dark "human-led / AI-accelerated" split
   Added 2026-09-01 as the shared signature element for every service
   sub-page (services/<slug>/index.html) — the visual counterpart to the
   plain-text "Where AI fits" band the first build shipped with. One dark
   band per page, never adjacent to another dark band (same rule as the
   product-page bands above). Linear gradients + rounded-SQUARE dots only —
   no circles/blobs, per the standing client rule baked in above.
   ========================================================================== */
.pd-balance { position: relative; isolation: isolate; overflow: hidden; color: #fff;
  background: linear-gradient(155deg, var(--navy-deep) 0%, #142a52 46%, var(--navy) 100%); }
/* Real photo behind the band, dimmed under the same navy gradient — a plain
   two-column text panel on a flat colour reads flat; the photo gives the
   signature "Where AI fits" band the same visual weight as the hero/CTA. */
.pd-balance__img { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.pd-balance__img img { width: 100%; height: 100%; object-fit: cover; opacity: .32; }
.pd-balance__img::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(155deg, rgba(10,20,42,.92) 0%, rgba(20,42,82,.86) 46%, rgba(29,65,121,.82) 100%); }
.pd-balance::before { content: ""; position: absolute; inset: 0; z-index: 1; opacity: .5;
  background: repeating-linear-gradient(90deg, rgba(255,255,255,.05) 0 1px, transparent 1px 120px); }
.pd-balance > .wrap { position: relative; z-index: 2; }
.pd-balance .pd-bhead__lbl { color: #9fd0f4; }
.pd-balance .pd-bhead__lbl::before { background: var(--orange); }
.pd-balance .pd-bhead h2 { color: #fff; }
.pd-balance .pd-bhead__aside p { color: rgba(228,236,247,.78); }

.pd-balance__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  border: 1px solid rgba(255,255,255,.14); border-radius: 20px; overflow: hidden;
  background: rgba(255,255,255,.02); }
.pd-balance__grid > * { min-width: 0; }
.pd-balance__col { padding: clamp(1.6rem, 1.2rem + 1.6vw, 2.6rem); position: relative; }
.pd-balance__col + .pd-balance__col { border-left: 1px solid rgba(255,255,255,.14); }
.pd-balance__col--ai { background: rgba(77,159,215,.1); }
.pd-balance__kicker { display: flex; align-items: center; gap: .6rem;
  font-family: var(--font-mono, monospace); font-size: .72rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; margin-bottom: 1.3rem; }
.pd-balance__kicker i { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.pd-balance__col--human .pd-balance__kicker { color: rgba(228,236,247,.85); }
.pd-balance__col--human .pd-balance__kicker i { background: rgba(228,236,247,.85); }
.pd-balance__col--ai .pd-balance__kicker { color: #9fd0f4; }
.pd-balance__col--ai .pd-balance__kicker i { background: var(--sky); }
.pd-balance__list { list-style: none; margin: 0; padding: 0; }
.pd-balance__list li { padding: .85rem 0; border-top: 1px solid rgba(255,255,255,.1);
  font-size: .95rem; line-height: 1.5; color: rgba(228,236,247,.92); }
.pd-balance__list li:first-child { border-top: 0; padding-top: 0; }
.pd-balance__note { margin-top: 1.2rem; font-size: .83rem; line-height: 1.55; color: rgba(228,236,247,.58); }

@media (max-width: 860px) {
  .pd-balance__grid { grid-template-columns: minmax(0, 1fr); }
  .pd-balance__col + .pd-balance__col { border-left: 0; border-top: 1px solid rgba(255,255,255,.14); }
}

/* ==========================================================================
   BAND 5 · KEY FEATURES — a genuine slider, at every width
   2026-08-13 correction: the client called out two separate defects.
     (1) The collapsed card showed title + a one-line teaser — "only the
         title" should be visible. The teaser is gone; a card shows just the
         icon and the title until opened.
     (2) The prev/next arrows used to step which card was OPEN (an accordion
         wearing a slider's clothes). They now always scroll the track by one
         card-width, independent of the expand state — an actual slider. This
         is also why the row is a horizontal `overflow-x` track at EVERY
         width now, not only ≤1024px: a fixed 4-up grid has nothing left to
         slide once a 5th card exists.
   "+" still expands a card IN PLACE to reveal a SHORT description — one
   sentence, no bullet list ("pointers") — via max-height + opacity on one
   curve; the "+" rotates 45° into an "×". One card open at a time.
   ========================================================================== */
.pd-feat { position: relative; isolation: isolate; overflow: hidden;
  background: var(--white); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
/* Same vertical-rule motif as the hero, inverted for light. Linear only. */
.pd-feat::before { content: ""; position: absolute; inset: 0; z-index: 0; opacity: .8;
  background: repeating-linear-gradient(90deg, rgba(29,65,121,.055) 0 1px, transparent 1px 120px);
  mask-image: linear-gradient(180deg, #000 0%, transparent 62%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 62%); }
.pd-feat > .wrap { position: relative; z-index: 1; }

.pd-expnav { display: flex; align-items: center; gap: .55rem; }
.pd-expnav__b { width: 42px; height: 42px; display: grid; place-items: center; border-radius: 11px;
  border: 1px solid var(--line-2); background: var(--white); color: var(--navy); font-size: .8rem;
  transition: background .25s ease, border-color .25s ease, color .25s ease, transform .25s ease; }
.pd-expnav__b:hover { background: var(--sky-tint); border-color: var(--navy); transform: translateY(-2px); }
.pd-expnav__b:active { transform: translateY(0); }

/* The track — an overflow-x slider at EVERY width, not only ≤1024px, because
   prev/next now MOVE THIS ROW rather than toggle which card is open. Fixed
   px gap (not clamp) so the flex-basis calc below stays simple and exact. */
.pd-fc { display: flex; align-items: flex-start; gap: 18px;
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  padding-bottom: .6rem; scrollbar-width: none; -ms-overflow-style: none; }
.pd-fc::-webkit-scrollbar { display: none; }
/* Four full cards in view on desktop; calc backs out the three 18px gaps so
   the row never overflows by the gap's own width. */
.pd-fc__card {overflow: hidden; flex: 0 0 calc((100% - 54px) / 4); scroll-snap-align: start;
  position: relative; min-width: 0;
  display: flex; flex-direction: column;
  border-radius: 18px; border: 1px solid var(--line); background: var(--white);
  padding: 1.5rem 1.4rem;                    /* content-sized — no void */
  box-shadow: 0 1px 2px rgba(11,24,48,.04);
  transition: border-color .35s cubic-bezier(.22,1,.36,1),
              box-shadow .35s cubic-bezier(.22,1,.36,1),
              transform .35s cubic-bezier(.22,1,.36,1); }
/* Coloured top accent — a brand hairline that gives each card its own colour
   without leaving the navy / sky / orange palette. */
.pd-fc__card::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  border-radius: 18px 18px 0 0;
  background: linear-gradient(90deg, var(--navy) 0%, var(--sky) 100%); }
.pd-fc__card:nth-child(2)::before { background: linear-gradient(90deg, var(--sky) 0%, #8fc2e8 100%); }
.pd-fc__card:nth-child(3)::before { background: linear-gradient(90deg, var(--orange) 0%, #f7b45f 100%); }
.pd-fc__card:nth-child(4)::before { background: linear-gradient(90deg, var(--navy-deep) 0%, var(--navy) 100%); }
.pd-fc__card:nth-child(5)::before { background: linear-gradient(90deg, var(--sky) 0%, var(--orange) 100%); }
.pd-fc__card:hover { border-color: var(--line-2); box-shadow: 0 14px 30px -22px rgba(11,24,48,.35); }
.pd-fc__card.is-open { border-color: var(--sky); box-shadow: 0 26px 52px -32px rgba(11,24,48,.4); }
.pd-fc__ico { flex: none; width: 46px; height: 46px; display: grid; place-items: center;
  border-radius: 12px; background: linear-gradient(140deg, var(--navy) 0%, #2f5f9e 100%);
  border: 1px solid transparent; color: #fff; font-size: 1.05rem; margin-bottom: 1.1rem;
  transition: background .35s cubic-bezier(.22,1,.36,1), color .35s cubic-bezier(.22,1,.36,1),
              border-color .35s cubic-bezier(.22,1,.36,1); }
/* Per-card icon tints, cycling through the brand palette. */
.pd-fc__card:nth-child(2) .pd-fc__ico { background: linear-gradient(140deg, var(--sky) 0%, #6fb3df 100%); }
.pd-fc__card:nth-child(3) .pd-fc__ico { background: linear-gradient(140deg, var(--orange) 0%, #f7b45f 100%); }
.pd-fc__card:nth-child(4) .pd-fc__ico { background: linear-gradient(140deg, var(--navy-deep) 0%, var(--navy) 100%); }
.pd-fc__card:nth-child(5) .pd-fc__ico { background: linear-gradient(140deg, var(--sky) 0%, #2B6CA8 100%); }
.pd-fc__card.is-open .pd-fc__ico { background: linear-gradient(140deg, var(--navy) 0%, #2f5f9e 100%);
  border-color: transparent; color: #fff; }
/* Only child before the toggle when collapsed — "only the title visible". */
.pd-fc__card h3 { color: var(--ink); font-size: 1.12rem; letter-spacing: -.02em; line-height: 1.28; }
/* The in-place expander — a SHORT description (one sentence), not a bullet
   list. max-height is small on purpose: with barely 3 lines of real content,
   a large max-height (e.g. 34rem) makes the reveal feel like it stalls before
   settling, since the transition timing is keyed to the numeric max-height,
   not the true rendered height. */
.pd-fc__more { overflow: hidden; max-height: 0; opacity: 0; min-width: 0;
  transition: max-height .5s cubic-bezier(.22,1,.36,1), opacity .5s cubic-bezier(.22,1,.36,1); }
.pd-fc__card.is-open .pd-fc__more { max-height: 9rem; opacity: 1; }
.pd-fc__more > p { margin-top: .7rem; color: var(--slate); font-size: .9rem; line-height: 1.6; }
/* The "+" toggle, bottom-left like the reference; rotates 45° to an "×".
   IN FLOW directly after the title (or after the short description when the
   card is open) — never pinned, so it can never leave a gap above itself. */
.pd-fc__plus { align-self: flex-start; margin-top: 1.15rem; width: 40px; height: 40px; flex: none;
  display: grid; place-items: center; border-radius: 11px;
  border: 1px solid var(--line-2); background: var(--white); color: var(--navy); font-size: .85rem;
  transition: background .35s cubic-bezier(.22,1,.36,1), color .35s cubic-bezier(.22,1,.36,1),
              border-color .35s cubic-bezier(.22,1,.36,1); }
.pd-fc__plus:hover { border-color: var(--navy); background: var(--sky-tint); }
.pd-fc__plus i { transition: transform .35s cubic-bezier(.22,1,.36,1); }
.pd-fc__card.is-open .pd-fc__plus { background: var(--navy); border-color: var(--navy); color: #fff; }
.pd-fc__card.is-open .pd-fc__plus i { transform: rotate(45deg); }

@media (max-width: 1180px) {
  /* ~2.4 cards in view hints there is more to slide to. */
  .pd-fc__card { flex: 0 0 min(46vw, 320px); }
}
@media (max-width: 640px) {
  .pd-fc__card { flex: 0 0 min(84vw, 320px); }
}

/* ==========================================================================
   BAND 6 · PRODUCT LEAD QUOTE — the quote IS the composition

   There is no headshot asset, and the previous build reserved a big empty
   tinted box for one with a monogram floating in it — the client's "omggg
   horrible". So the photo box is gone. The band is now a wide, left-aligned
   editorial layout: orange glyph, the quote at display size (deliberately
   NOT italic — large italic sans reads awkward), navy with the closing
   sentence tinted sky, a hairline, then a compact attribution row with a
   small rounded-SQUARE monogram tile. The faint warm grid texture sits
   behind the WHOLE band now, masked so it fades out.

   PHOTO-READY. When a headshot arrives: add `pd-say--photo` to the section
   and uncomment the PHOTO SLOT in the markup. The two-column rules below
   do the rest — one class and one <img>, no other change.
   ========================================================================== */
.pd-say { position: relative; isolation: isolate; overflow: hidden;
  background: linear-gradient(112deg, #E4EFFA 0%, #EEF4FA 42%, #FBFBF8 100%); }
/* Faint warm grid behind the whole band. Linear only, masked to fade out. */
.pd-say::before { content: ""; position: absolute; inset: 0; z-index: 0; opacity: .55;
  background:
    repeating-linear-gradient(90deg, rgba(166,48,41,.1) 0 1px, transparent 1px 38px),
    repeating-linear-gradient(0deg, rgba(166,48,41,.1) 0 1px, transparent 1px 38px);
  mask-image: linear-gradient(118deg, #000 0%, rgba(0,0,0,.35) 52%, transparent 84%);
  -webkit-mask-image: linear-gradient(118deg, #000 0%, rgba(0,0,0,.35) 52%, transparent 84%); }
.pd-say > .wrap { position: relative; z-index: 1; }
.pd-say__in { position: relative; z-index: 1; max-width: 74rem; margin-inline: auto; }
.pd-say__copy { min-width: 0; }
/* The base reveal's .in leaves clip-path: inset(0) behind, which would clip
   the monogram's shadow forever. Re-set it at higher specificity. */
.js .r3 .pd-say__in[data-r].in { clip-path: none; }

/* NOT italic — the client's own note. Display weight carries it instead. */
.pd-say__q { font-family: var(--font-display, sans-serif); font-weight: 700;
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.5rem); line-height: 1.32; letter-spacing: -.026em;
  color: var(--navy); max-width: 34ch; }
.pd-say__q span { color: var(--sky); }

/* Attribution row sits ABOVE the quote (the client wanted the name card on
   top) under a hairline: small rounded-SQUARE monogram, name, role. */
.pd-say__by { display: flex; align-items: center; gap: .95rem; min-width: 0;
  margin-bottom: clamp(1.6rem, 1.2rem + 1.4vw, 2.4rem);
  padding-bottom: clamp(1.2rem, 1rem + 1vw, 1.8rem);
  border-bottom: 1px solid rgba(29,65,121,.18); }
.pd-say__mono { flex: none; width: 44px; height: 44px; display: grid; place-items: center;
  border-radius: 12px;
  background: linear-gradient(150deg, var(--navy) 0%, #2f5f9e 100%); color: #fff;
  font-family: var(--font-mono, monospace); font-size: .82rem; font-weight: 500; letter-spacing: .08em;
  box-shadow: 0 14px 28px -16px rgba(29,65,121,.85); }
.pd-say__who { min-width: 0; display: grid; gap: .28rem; }
.pd-say__who b { font-family: var(--font-display, sans-serif); font-weight: 800; color: var(--ink);
  font-size: 1.02rem; letter-spacing: -.016em; line-height: 1.2; }
.pd-say__role { font-family: var(--font-mono, monospace); font-size: .66rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--slate); }

/* ---- PHOTO VARIANT -------------------------------------------------------
   Add `pd-say--photo` to the section plus the PHOTO SLOT markup.

   ALIGNMENT (the thing that was wrong): the photo used to be centred against
   the WHOLE copy column — attribution row + quote — so its top floated above
   the quote's first line and the two read as misaligned. It now shares a grid
   ROW with the quote alone, so the photo's top and the quote's top are the
   same line. `display: contents` on .pd-say__copy promotes .pd-say__by and
   .pd-say__q to grid items so they can be placed individually; the wrapper
   div carries no semantics, so dissolving it costs nothing.

     row 1  │ attribution │  (empty)
     row 2  │ quote       │  photo
   ========================================================================== */
.pd-say--photo .pd-say__in { display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  grid-template-rows: auto 1fr;
  column-gap: clamp(1.6rem, 1.2rem + 2.6vw, 3.6rem);
  align-items: start; }
.pd-say--photo .pd-say__in > * { min-width: 0; }
.pd-say--photo .pd-say__copy { display: contents; }
.pd-say--photo .pd-say__by { grid-column: 1; grid-row: 1; }
.pd-say--photo .pd-say__q { grid-column: 1; grid-row: 2; max-width: none; }
.pd-say--photo .pd-say__photo { grid-column: 2; grid-row: 2; }
.pd-say__photo { position: relative; min-width: 0; }
.pd-say__frame { position: relative; aspect-ratio: 4 / 5;
  max-width: 340px; margin-inline: auto; border-radius: 22px; overflow: hidden;
  background: linear-gradient(160deg, #DCEBF8 0%, #C6DCF0 58%, #B3CFE9 100%);
  border: 1px solid rgba(29,65,121,.16);
  box-shadow: 0 34px 70px -42px rgba(11,24,48,.55); }
.pd-say__frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  display: block; }

@media (max-width: 760px) {
  /* Stacked: undo the explicit row/column placement, or `grid-column: 2`
     would conjure an implicit second column in a one-column grid. */
  .pd-say--photo .pd-say__in { grid-template-columns: minmax(0, 1fr); grid-template-rows: none;
    row-gap: clamp(1.6rem, 1.2rem + 2.6vw, 3.2rem); }
  .pd-say--photo .pd-say__copy { display: block; }
  .pd-say--photo .pd-say__by,
  .pd-say--photo .pd-say__q,
  .pd-say--photo .pd-say__photo { grid-column: auto; grid-row: auto; }
  .pd-say__q { max-width: none; }
}

/* ==========================================================================
   BAND 7 · INTEGRATIONS — left sticky heading, right vertical auto-scroll
   The column of cards is duplicated by JS (clones aria-hidden), then a CSS
   keyframe translates it to -50% — one full list-height — on a slow linear
   loop. Hover pauses it. The viewport fades at both edges via a
   linear-gradient mask. Card spacing is margin-bottom, NOT gap, so the
   -50% loop point stays exactly one list-height.
   ========================================================================== */
.pd-int { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  gap: clamp(1.8rem, 1.2rem + 3vw, 4.2rem); align-items: start; }
.pd-int > * { min-width: 0; }
.pd-int__head { position: sticky; top: 158px; }
.pd-int__head h2 { font-size: clamp(1.7rem, 1.15rem + 2.2vw, 2.6rem); line-height: 1.08;
  letter-spacing: -.03em; margin: .85rem 0 .95rem; }
.pd-int__head > p { color: var(--slate); font-size: 1rem; line-height: 1.64; max-width: 44ch; }
.pd-int__note { display: flex; gap: .6rem; align-items: flex-start; max-width: 44ch;
  margin-top: 1.4rem; padding-top: 1.15rem; border-top: 1px solid var(--line);
  font-size: .86rem; line-height: 1.55; color: var(--slate); }
.pd-int__note i { flex: none; margin-top: .26rem; font-size: .78rem; color: var(--sky); }
.pd-int__note span { min-width: 0; }
/* Reference decoration is concentric rings; rings are circles, so ours are
   concentric rounded-SQUARE outlines at very low opacity. Linear strokes. */
.pd-int__deco { display: block; position: relative; width: 158px; height: 158px;
  margin-top: 2.4rem; border: 1px solid rgba(29,65,121,.14); border-radius: 26px; }
.pd-int__deco::before { content: ""; position: absolute; inset: 18px;
  border: 1px solid rgba(29,65,121,.11); border-radius: 20px; }
.pd-int__deco::after { content: ""; position: absolute; inset: 36px;
  border: 1px solid rgba(247,148,31,.2); border-radius: 14px; }

.pd-int__view { height: 480px; overflow: hidden;
  mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%); }
.pd-int__col { min-width: 0; }
.pd-int__col.is-looping { animation: pdIntLoop 36s linear infinite; }
.pd-int__view:hover .pd-int__col.is-looping { animation-play-state: paused; }
@keyframes pdIntLoop { from { transform: translateY(0); } to { transform: translateY(-50%); } }

.pd-int__card { display: flex; gap: .95rem; align-items: flex-start; min-width: 0;
  margin-bottom: .85rem; padding: 1.05rem 1.15rem; border-radius: 14px;
  border: 1px solid var(--line); background: var(--white);
  box-shadow: 0 1px 2px rgba(11,24,48,.04); }
.pd-int__card > div { min-width: 0; }
.pd-int__card b { display: block; font-family: var(--font-display, sans-serif); font-weight: 700;
  font-size: .98rem; letter-spacing: -.014em; color: var(--ink); line-height: 1.3; }
.pd-int__card p { margin-top: .3rem; font-size: .86rem; line-height: 1.55; color: var(--slate); }
.pd-int__ico { flex: none; width: 42px; height: 42px; display: grid; place-items: center;
  border-radius: 11px; background: linear-gradient(150deg, var(--sky-tint) 0%, #F5F9FC 100%);
  border: 1px solid var(--line); color: var(--navy); font-size: 1rem; }

@media (max-width: 900px) {
  .pd-int { grid-template-columns: minmax(0, 1fr); }
  .pd-int__head { position: static; }
  .pd-int__deco { display: none; }
  .pd-int__view { height: 420px; }
}

/* ==========================================================================
   BAND 8 · WHY CHOOSE — three LIGHT cards on white
   Hover payoff: the card LIFTS 10px — the client asked for it to move up
   clearly and smoothly, so the travel is doubled from the old 6px and runs
   on a .4s ease-out curve with `will-change: transform`. The base state
   declares translateY(0) explicitly so the return trip animates too rather
   than snapping. Alongside it: border → sky, the shadow deepens, the icon
   tile flips to the brand navy→orange gradient, and a linear gradient
   hairline sweeps in along the top edge. `.is-hov` mirrors :hover as a
   test hook for static captures.

   WHY THE LONG SELECTORS BELOW. The tiles carry [data-r], and the site rule
   `.js .r3 [data-r].in` (0,4,0) sets `transform: none` — which outranks a
   bare `.pd-tile:hover` (0,2,0). That is why the old lift did nothing once
   the reveal had run: the hover transform was being thrown away. The lift
   and its transition are therefore declared at `.js .r3 .pd-tile[data-r]`
   specificity so they actually win. Keep them that way on any clone.
   ========================================================================== */
/* Band 8 background — the client asked for a colour behind Why RIA (it was
   plain .section--paper white). An in-theme sky wash lifts the white cards
   and keeps the band rhythm: paper (integrations) → tint → paper (case study). */
.r3 #why { background: linear-gradient(180deg, #E7F0F9 0%, #EDF4FB 52%, #F6FAFD 100%); }

.pd-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, .8rem + .8vw, 1.5rem); }
.pd-tiles > * { min-width: 0; }
.pd-tile { position: relative; isolation: isolate; overflow: hidden; min-width: 0;
  display: flex; flex-direction: column; color: var(--ink);
  border-radius: 20px; padding: clamp(1.5rem, 1.2rem + 1.2vw, 2.2rem);
  background: var(--white); border: 1px solid var(--line);
  box-shadow: 0 1px 2px rgba(11,24,48,.04);
  transform: translateY(0); will-change: transform;
  transition: transform .4s cubic-bezier(.22,1,.36,1),
              box-shadow .4s ease,
              border-color .3s ease; }
/* The sweeping top hairline. */
.pd-tile::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--orange) 0%, var(--sky) 55%, var(--navy) 100%);
  transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.22,1,.36,1); }
.pd-tile:hover, .pd-tile.is-hov { transform: translateY(-10px); border-color: var(--sky);
  box-shadow: 0 38px 68px -34px rgba(11,24,48,.5), 0 10px 22px -18px rgba(11,24,48,.3); }
.pd-tile:hover::before, .pd-tile.is-hov::before { transform: scaleX(1); }
/* Tiles carry [data-r]. Two things have to be reclaimed from the site rule:
   the leftover `clip-path: inset(0)` (which would clip the lift's shadow
   forever) and `transform: none`, which was swallowing the hover lift. */
.js .r3 .pd-tile[data-r].in { clip-path: none; transform: translateY(0);
  transition: transform .4s cubic-bezier(.22,1,.36,1),
              box-shadow .4s ease,
              border-color .3s ease; }
.js .r3 .pd-tile[data-r].in:hover,
.js .r3 .pd-tile[data-r].in.is-hov { transform: translateY(-10px); border-color: var(--sky);
  box-shadow: 0 38px 68px -34px rgba(11,24,48,.5), 0 10px 22px -18px rgba(11,24,48,.3); }
.pd-tile__ico { width: 46px; height: 46px; display: grid; place-items: center; border-radius: 13px;
  background: linear-gradient(150deg, var(--sky-tint) 0%, #F5F9FC 100%);
  border: 1px solid var(--line); font-size: 1.05rem; color: var(--navy); margin-bottom: 1.15rem;
  transition: background .35s cubic-bezier(.22,1,.36,1), color .35s cubic-bezier(.22,1,.36,1),
              border-color .35s cubic-bezier(.22,1,.36,1); }
.pd-tile:hover .pd-tile__ico, .pd-tile.is-hov .pd-tile__ico {
  background: linear-gradient(140deg, var(--navy) 0%, #2f5f9e 52%, var(--orange) 145%);
  border-color: transparent; color: #fff; }
.pd-tile h3 { color: var(--ink); font-size: clamp(1.08rem, 1rem + .5vw, 1.28rem);
  letter-spacing: -.02em; line-height: 1.2; margin-bottom: .6rem; }
.pd-tile p { color: var(--slate); font-size: .92rem; line-height: 1.6; }
/* margin-top:auto pins the rule to the bottom so the three tiles line up
   even though their paragraphs are different lengths. */
.pd-tile__k { display: block; margin-top: auto; padding-top: 1.3rem;
  border-top: 1px solid var(--line); font-family: var(--font-mono, monospace);
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase; color: var(--slate); }

@media (max-width: 900px) { .pd-tiles { grid-template-columns: minmax(0, 1fr); } }

/* ==========================================================================
   BAND 10 · CTA — centred warm-gradient panel (their reference's peach wash,
   rendered in RIA orange at low opacity, linear only).
   ========================================================================== */
.pd-cta { background: var(--white); }
.pd-cta__panel { position: relative; overflow: hidden; text-align: center; border-radius: 26px;
  padding: clamp(2.4rem, 1.8rem + 3.4vw, 4.6rem) clamp(1.4rem, 1rem + 3.4vw, 4.5rem);
  border: 1px solid rgba(247,148,31,.3);
  background:
    linear-gradient(134deg, rgba(247,148,31,.26) 0%, rgba(247,148,31,.1) 34%, rgba(255,255,255,.6) 62%, rgba(77,159,215,.16) 100%),
    linear-gradient(180deg, #FFFCF7 0%, #FFFFFF 100%);
  box-shadow: 0 40px 80px -60px rgba(11,24,48,.5); }
.pd-cta__panel::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--orange) 0%, #f7b45f 34%, var(--sky) 78%, transparent 100%); }
.pd-cta__lbl { font-family: var(--font-mono, monospace); font-size: .7rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--navy); }
.pd-cta__panel h2 { font-size: clamp(1.75rem, 1.2rem + 2.7vw, 3rem); line-height: 1.08;
  letter-spacing: -.032em; margin: 1rem auto .9rem; max-width: 18ch; }
.pd-cta__panel > p { color: var(--slate); font-size: clamp(1rem, .95rem + .3vw, 1.08rem);
  line-height: 1.66; max-width: 60ch; margin-inline: auto; }
.pd-cta__btns { display: flex; justify-content: center; gap: .8rem; flex-wrap: wrap;
  margin-top: clamp(1.6rem, 1.2rem + 1.4vw, 2.4rem); }
.pd-cta__links { display: flex; justify-content: center; flex-wrap: wrap;
  margin-top: clamp(1.8rem, 1.4rem + 1.4vw, 2.6rem); padding-top: clamp(1.2rem, 1rem + .8vw, 1.8rem);
  border-top: 1px solid rgba(29,65,121,.14); }
.pd-cta__links a { display: inline-flex; align-items: center; gap: .55rem; padding: .35rem 1.2rem;
  border-left: 1px solid rgba(29,65,121,.14); font-size: .88rem; font-weight: 600; color: var(--navy);
  transition: color .25s ease; }
.pd-cta__links a:first-child { border-left: 0; }
.pd-cta__links a i { font-size: .8rem; color: var(--orange); }
.pd-cta__links a:hover { color: var(--orange-600); }

@media (max-width: 560px) {
  .pd-cta__links a { flex: 1 1 100%; justify-content: center; border-left: 0;
    border-top: 1px solid rgba(29,65,121,.14); padding-block: .7rem; }
  .pd-cta__links a:first-child { border-top: 0; }
}

/* ==========================================================================
   PRODUCT DASHBOARD MOCK (hero only)
   A faithful reconstruction of the client's own Test Automation reference
   screenshot: a floating logo badge over real browser chrome (traffic
   lights, tab, padlock + full URL) → app title bar → a LEFT icon sidebar
   (Home/Project/Step/Assertion/Flow/Dataset/Results/Settings/Test Suite —
   NOT a row of top tabs, that was the earlier build's mistake) → date
   filter → KPI cards that carry their own breakdown list nested inside the
   card → recent-runs bar chart + pass/fail trend line chart. The figures
   come from the client's own reference screenshot.
   RULE: the badge is a rounded SQUARE, never a circle — the reference photo
   shows a circular mark, but this client's standing rule bans circles even
   on decorative glyphs (see the window-chrome dots below).
   ========================================================================== */
/* Sits in the PANEL's top-left corner — clear of the browser mock, exactly
   where the reference puts it. Rounded SQUARE, not a circle (standing client
   rule; the reference's own mark is round but circles are banned here). */
.pd-ui__badge { position: absolute; z-index: 3;
  top: var(--pd-pad); left: var(--pd-pad);
  width: var(--pd-badge, 52px); height: var(--pd-badge, 52px);
  display: grid; place-items: center; border-radius: 16px;
  background: linear-gradient(150deg, var(--orange) 0%, #f2678a 100%);
  box-shadow: 0 18px 32px -12px rgba(242,90,120,.55);
  color: #fff; font-size: 1.2rem; }
@media (max-width: 1040px) { .pd-ui__badge { display: none; } }

.pd-ui { position: relative; border-radius: 16px; overflow: hidden; background: #fff;
  border: 1px solid rgba(11,24,48,.14); color: var(--ink);
  box-shadow: 0 50px 90px -46px rgba(0,0,0,.6); }

/* -- browser chrome: dark navy strip with traffic lights + the active tab ---- */
.pd-ui__chrome { display: flex; align-items: flex-end; gap: .5rem;
  padding: .5rem .7rem 0;
  background: linear-gradient(135deg, #12264a 0%, #16335f 58%, #1D4179 100%); }
.pd-ui__dots { display: flex; gap: .32rem; flex: none; align-items: center;
  padding-bottom: .38rem; }
.pd-ui__dots i { display: block; width: 9px; height: 9px; border-radius: 3px; background: #ccd6e3; }
.pd-ui__dots i:nth-child(1) { background: #e15a50; }
.pd-ui__dots i:nth-child(2) { background: #e8b64c; }
.pd-ui__dots i:nth-child(3) { background: #6fbf83; }
.pd-ui__tab { display: inline-flex; align-items: center; gap: .6rem; flex: none;
  margin-left: .4rem; padding: .44rem .8rem .42rem 1rem;
  border-radius: 9px 9px 0 0; background: #fff;
  font-family: var(--font-display, sans-serif); font-size: .78rem; font-weight: 600;
  color: var(--ink); white-space: nowrap; }
.pd-ui__tab i { width: 10px; height: 10px; border-radius: 3px; flex: none;
  background: linear-gradient(140deg, var(--orange) 0%, #f7b45f 100%); }
.pd-ui__tabx { font-weight: 400; font-size: .8rem; line-height: 1; color: #9aa6b5; }
.pd-ui__tabbtn { flex: none; width: 24px; height: 26px; display: grid; place-items: center;
  margin-bottom: .1rem; border-radius: 6px; color: rgba(228,236,247,.72);
  font-size: 1rem; font-weight: 500; line-height: 1; }

/* -- address bar ---------------------------------------------------------- */
.pd-ui__bar { display: flex; align-items: center; gap: .6rem; padding: .5rem .8rem;
  background: linear-gradient(180deg, #f7f9fc 0%, #e9eef5 100%); border-bottom: 1px solid #e1e7f0; }
.pd-ui__navbtns { display: flex; gap: .05rem; flex: none; color: #9aa6b5; font-size: .56rem; }
.pd-ui__navbtns i { padding: .22rem .3rem; }
.pd-ui__addr { flex: 1; min-width: 0; display: flex; align-items: center; gap: .55rem;
  padding: .4rem 1rem; border-radius: 999px; background: #fff; border: 1px solid #e1e7f0;
  font-family: var(--font-display, sans-serif); font-size: .7rem; color: #5f6b7a;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pd-ui__addr i { font-size: .62rem; color: #6b7787; }
.pd-ui__menu { flex: none; color: #9aa6b5; font-size: .75rem; padding: .1rem .15rem; }

/* -- app title bar: the real white RIA logo, a divider, then the module
      name on TWO lines — exactly the reference's masthead. --------------- */
.pd-ui__topbar { display: flex; align-items: center; gap: 1rem;
  padding: .65rem 1.1rem; background: #1D4179; }
.pd-ui__logoimg { flex: none; height: 32px; width: auto; display: block;
  padding-right: 1rem; border-right: 1px solid rgba(255,255,255,.24); }
.pd-ui__apptitle { font-family: var(--font-display, sans-serif); font-weight: 700;
  font-size: .92rem; line-height: 1.18; color: #fff; }

/* -- shell: LEFT icon sidebar (LIGHT, per the reference) + content ------- */
.pd-ui__shell { display: flex; align-items: stretch; min-width: 0; }
.pd-ui__side { flex: none; width: 88px; display: flex; flex-direction: column;
  align-items: stretch; gap: .1rem; padding: .6rem .35rem; text-align: center;
  background: #fff; border-right: 1px solid #e8edf4; }
.pd-ui__side a { display: flex; flex-direction: column; align-items: center; gap: .32rem;
  padding: .48rem .2rem; border-radius: 7px;
  font-family: var(--font-display, sans-serif); font-size: .64rem; font-weight: 500;
  letter-spacing: .01em; color: #6b7787; }
.pd-ui__side a i { font-size: .98rem; color: #8c98a9; }
.pd-ui__side a.on { background: #eef3f9; color: var(--navy); font-weight: 700; }
.pd-ui__side a.on i { color: var(--navy); }
.pd-ui__main { flex: 1; min-width: 0; background: #f7f9fc; }

/* -- dashboard body ------------------------------------------------------- */
.pd-dash { padding: .85rem .95rem .9rem; display: grid; gap: .7rem; }
.pd-dash__date { display: flex; align-items: center; justify-content: flex-end; gap: 1rem; }
.pd-dash__date-lbl { font-family: var(--font-display, sans-serif); font-size: .74rem;
  color: #98a3b3; }
.pd-dash__date-val { display: inline-flex; align-items: center; gap: .5rem;
  padding: .38rem .85rem; border-radius: 7px; border: 1px solid #e5e9f0; background: #fff;
  font-family: var(--font-display, sans-serif); font-size: .76rem; color: var(--navy); white-space: nowrap; }
.pd-dash__date-val i { font-size: .72rem; color: var(--sky); }

/* KPI cards — each numeric card carries its own breakdown list nested
   inside it (the reference does NOT split these into separate panels). --- */
.pd-dash__kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .75rem;
  align-items: start; }
.pd-dash__kpis > * { min-width: 0; }
.pd-kpi { display: flex; flex-direction: column; gap: .3rem;
  padding: .75rem .7rem .6rem; border-radius: 8px; border: 1px solid #e8edf4; background: #fff; }
.pd-kpi__lbl { font-family: var(--font-display, sans-serif); font-size: .78rem; font-weight: 600;
  color: var(--ink); line-height: 1.3; }
.pd-kpi__val { font-family: var(--font-display, sans-serif); font-weight: 700; font-size: 2.05rem;
  line-height: 1.1; letter-spacing: -.02em; color: var(--navy); }
.pd-kpi__val small { font-size: .68rem; font-weight: 500; letter-spacing: 0; color: #8c98a9; }
/* Plain label→value rows separated by hairlines — the reference has no
   coloured dots in these lists. flex-wrap is the overlap safety net: the
   value drops to its own line rather than painting on top of a squeezed
   label (which is exactly what a non-wrapping row did at this scale). */
.pd-kpi__list { list-style: none; margin: .3rem 0 0; padding-top: .1rem;
  display: flex; flex-direction: column; width: 100%; min-width: 0;
  border-top: 1px solid #eef1f6; }
/* LABEL / VALUE ROW — this took three goes, so read before changing:
     · nowrap + min-width:0 let the label box shrink below its own
       min-content, and "Terminated/cancelled" is one unbreakable token, so
       on a ≤1440px laptop the text spilled out and painted OVER the value.
     · `overflow-wrap: anywhere` stopped the overlap but made min-content one
       character, so flex crushed short labels into "Bat/ch", "executi/on".
   The fix is to let the ROW wrap: the value keeps its natural width and
   simply drops to its own line (right-aligned via margin-left:auto) when the
   label needs the full width. Words stay whole; `break-word` is only a last
   resort and, unlike `anywhere`, does not affect min-content sizing. */
.pd-kpi__list li { display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: .55rem; row-gap: .1rem; min-width: 0;
  padding: .32rem 0; border-bottom: 1px solid #f4f6fa; }
.pd-kpi__list li:last-child { border-bottom: 0; }
/* Fluid, because the card width is fluid. At 1830px there is room for .66rem;
   on a 1280–1440px laptop the same card is ~125px wide and a long unbroken
   token like "Terminated/cancelled" no longer fits, so break-word chops it
   mid-word. Scaling the type down with the viewport keeps the token intact
   and leaves break-word as a genuine last resort. */
.pd-kpi__list li > span { flex: 1 1 auto; min-width: 0;
  font-size: clamp(.56rem, .3rem + .38vw, .66rem); line-height: 1.3;
  color: #5f6b7a; overflow-wrap: break-word; }
.pd-kpi__list li b { flex: none; margin-left: auto; font-family: var(--font-display, sans-serif);
  font-size: clamp(.6rem, .34rem + .38vw, .7rem); font-weight: 600;
  color: var(--ink); white-space: nowrap; }
.pd-kpi__list li b small { font-size: .56rem; font-weight: 500; color: #8c98a9; }
.pd-stat__dot { width: 7px; height: 7px; border-radius: 3px; flex: none; background: #cfd9e7; }
.pd-stat__dot--ok { background: #3BA55D; }
.pd-stat__dot--bad { background: #D64545; }
.pd-stat__dot--warn { background: #E5A93C; }
.pd-stat__dot--mut { background: #9AA3AE; }
/* The donut card stacks like the others (label on top, chart below) — the
   reference gives it the full card, not a squeezed side-by-side row. */
.pd-kpi--donut { align-items: stretch; }
.pd-donut { align-self: center; flex: none; width: 98px; height: 98px; margin-top: .2rem; }
.pd-donut svg { width: 100%; height: 100%; display: block; }

/* Charts ------------------------------------------------------------------ */
.pd-dash__charts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: .7rem; }
.pd-dash__charts > * { min-width: 0; }
.pd-chart { display: grid; gap: .45rem; padding: .75rem .85rem .6rem; border-radius: 8px;
  border: 1px solid #e8edf4; background: #fff; min-width: 0; }
.pd-chart__lbl { font-family: var(--font-display, sans-serif); font-size: .76rem; font-weight: 600;
  color: var(--ink); }
.pd-chart svg { width: 100%; height: 88px; display: block; }
.pd-chart__ticks { display: flex; justify-content: space-between; gap: .3rem;
  font-family: var(--font-display, sans-serif); font-size: .6rem; color: #aab3bf; }

@media (max-width: 640px) {
  .pd-ui__badge { width: 42px; height: 42px; font-size: 1rem; top: -16px; left: 16px; }
  .pd-ui__side { width: 52px; }
  .pd-ui__side a { font-size: .42rem; }
  .pd-dash__kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pd-dash__charts { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   Hide the global right-hand section rail on product detail pages.
   This page has its own sticky sub-nav doing exactly the same job, so the
   rail is redundant — and at ~1500px its pill lands on top of the hero mock
   that deliberately bleeds past the container edge, which reads as a bug.
   Scoped safely: this stylesheet only loads on product detail pages.
   ========================================================================== */
.r3-rail { display: none; }
