/* site.css — shared structure, components, and motion (breakpoint-agnostic) */

/* ─── Reset ─────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
  scroll-padding-top: 0; /* anchor scrolls land with section top flush at viewport top */
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body { margin: 0; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
/* Page-level rubber-band off (desktop + mobile). Apply to both axes
   on both root elements so the bounce is fully suppressed regardless
   of which one the browser treats as the viewport scroll container.
   Internal scrollers (e.g., the detail sheet) keep their own
   overscroll-behavior. */
html, body { overscroll-behavior: none; }
img, picture { display: block; max-width: 100%; }
button { font: inherit; border: 0; background: transparent; padding: 0; cursor: pointer; color: inherit; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }

/* ─── Skip link ─────────────────────────────────────────────────────────── */

.pp-skip {
  position: fixed;
  top: -100px;
  left: 0;
  padding: 10px 16px;
  background: var(--pp-navy);
  color: var(--pp-cream-fg);
  font-family: var(--font-body);
  font-weight: 500;
  z-index: 1000;
  border-radius: 0 0 var(--r-card) 0;
  transition: top var(--t-200) var(--ease);
}
.pp-skip:focus { top: 0; outline: none; }

/* ─── Section base ──────────────────────────────────────────────────────── */

.pp-section {
  position: relative;
  width: 100%;
  min-height: 100svh;
}
[data-tone="cream"] { background-color: var(--pp-cream); color: var(--pp-navy); }
[data-tone="navy"]  { background-color: var(--pp-navy);  color: var(--pp-cream-fg); }
[data-tone="blue"]  { background-color: var(--pp-blue);  color: var(--pp-white); }
[data-tone="white"] { background-color: var(--pp-white); color: var(--pp-navy); }
[data-tone="photo"] { background-color: transparent; }

/* ─── Header lockup (logo + menu-logo + drop + splash + close) ─────────── */
/* .pp-header sits at z:100 — above all section content and above the menu
   overlay (z:40). Every lockup child inherits this stacking context, so the
   page logo, the cream menu logo, the drop, the splash and the X close all
   read on top of whatever's underneath, including the navy menu surface. */

.pp-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-header);          /* 100 — always above sections and menu */
  pointer-events: none;
  background: var(--pp-header-bg);
  transition: background-color var(--t-200) var(--ease);
}
.pp-header__logo,
.pp-header__drop,
.pp-menu__logo,
.pp-menu__splash,
.pp-menu__close {
  position: absolute;
  display: flex;
  align-items: center;
}
.pp-header__logo,
.pp-header__drop { pointer-events: auto; }
.pp-menu__logo,
.pp-menu__splash,
.pp-menu__close  { pointer-events: none; } /* close becomes interactive only when menu is open */

.pp-header__logo,
.pp-menu__logo  { justify-content: flex-start; }
.pp-header__drop,
.pp-menu__splash,
.pp-menu__close { justify-content: flex-end; }

.pp-header__logo  img,
.pp-header__drop  img,
.pp-menu__logo    img,
.pp-menu__splash  img,
.pp-menu__close   img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: 100%;
  object-fit: contain;
}
.pp-header__drop  img { transition: transform var(--t-200) var(--ease); }
.pp-header__drop:hover  img { transform: scale(1.06); }
.pp-header__drop:active img { transform: scale(0.94); }

/* Logo cross-fade: page logo (tone-matched) shows by default; cream menu logo
   overlays it during the open state. Both occupy the same top-left zone. */
.pp-header__logo { transition: opacity 240ms var(--ease); }
.pp-menu__logo   { opacity: 0; transition: opacity 240ms var(--ease); }
body:has(.pp-menu.is-open)    .pp-header__logo { opacity: 0; }
body:has(.pp-menu.is-open)    .pp-menu__logo   { opacity: 1; }
body:has(.pp-menu.is-closing) .pp-header__logo {
  opacity: 1;
  transition: opacity 240ms var(--ease);
}
body:has(.pp-menu.is-closing) .pp-menu__logo {
  opacity: 0;
  transition: opacity 240ms var(--ease);
}

/* Drip — accelerating gravity curve per chat instructions (overrides §10).
   Open: drop falls off bottom over 650ms.
   Close: drop drips back into place from above on the same curve.        */
@keyframes pp-drip-out {
  from { transform: translateY(0); }
  to   { transform: translateY(150vh); }
}
@keyframes pp-drip-in {
  from { transform: translateY(-150vh); }
  to   { transform: translateY(0); }
}
.pp-header__drop.is-dripping-out {
  animation: pp-drip-out 650ms var(--ease-gravity) forwards;
}
.pp-header__drop.is-dripping-in {
  animation: pp-drip-in 650ms var(--ease-gravity) forwards;
}

.pp-menu__splash[hidden] { display: none; }

/* ─── Hero ──────────────────────────────────────────────────────────────── */

.pp-section--hero {
  height: 100svh;
  min-height: 100svh;
  overflow: hidden;
}
/* Hero canvas — overlays the static fallback img inside .pp-hero__picture.
   Hidden until JS preloads frames + paints frame 2; .is-ready toggles in. */
.pp-hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 200ms var(--ease);
  pointer-events: none;
}
.pp-hero__canvas.is-ready { opacity: 1; }
.pp-hero__picture.is-canvas-ready .pp-hero__image {
  opacity: 0;
  transition: opacity 200ms var(--ease);
}
/* Scroll lock during hat playback — body overflow:hidden suppresses
   user wheel/touch scrolling, but window.scrollTo() still works
   programmatically (Phase 2 drives its own scroll). */
body.pp-hats-locked { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  body.pp-hats-locked { overflow: auto; }
}
.pp-hero__picture {
  position: absolute;
  inset: 0;
  display: block;
}
.pp-hero__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pp-hero__content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  color: var(--pp-white);
}
.pp-hero__title {
  font-family: var(--font-display);
  font-size: 9svh;
  line-height: 1.05;
  margin: 0;
}
.pp-hero__copy {
  font-family: var(--font-body);
  font-size: 3svh;
  line-height: 1.4;
  margin: 5svh 0 0 0;
}

.pp-hero__pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 7svh;
  border-radius: 100px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: clamp(14px, 1.8svh, 18px);
  letter-spacing: 0.01em;
  text-transform: lowercase;
  white-space: nowrap;
  transition: scale var(--t-200) var(--ease);
}
.pp-hero__pill--primary {
  background-color: var(--pp-blue);
  color: var(--pp-white);
  box-shadow: var(--shadow-button);
  margin-top: 5svh;
}
.pp-hero__pill--primary:active { scale: 0.98; }

.pp-hero__pill--secondary {
  background-color: var(--pp-white);
  color: var(--pp-blue);
  margin-top: 3svh;
}
.pp-hero__pill--secondary:active { scale: 0.98; }

/* ─── Focus rings ───────────────────────────────────────────────────────── */

:focus-visible {
  outline: 2px solid var(--pp-blue);
  outline-offset: 3px;
  border-radius: 4px;
}
.pp-hero__pill:focus-visible,
.pp-menu__item:focus-visible { outline-offset: 4px; }
.pp-header__drop:focus-visible { outline-offset: 6px; }
/* Close buttons get no focus ring — the X icon is unambiguous and the blue
   stroke reads as a glitch on touch when programmatically focused. */
.pp-menu__close:focus-visible,
.pp-detail__close:focus-visible { outline: none; }

/* ─── Menu overlay ──────────────────────────────────────────────────────── */
/* Open sequence (per latest chat overrides):
     0ms      drop starts falling (650ms accelerating)
     0ms      bg starts fading in (240ms)
     0ms      splash frame 1 (handled by JS — same instant as click)
     80ms     splash frame 2
     160ms    splash frame 3
     240ms    splash hidden, bg fully in, X starts fading in (200ms)
     440ms    X fully in; tips begins its 280ms slide
     650ms    drop done falling
     720/860/980/1080/1160/1220ms   tips / process / about us / services / contact / home land
   Close sequence (no splash, no drip — both are open-only):
     0ms      everything fades together: items (200ms), X (120ms),
              navy bg (240ms), menu logo (240ms), page logo back in (240ms);
              drop class is stripped → snaps to rest behind the fading X.
     240ms    cleanup, focus restored. */

.pp-menu {
  position: fixed;
  inset: 0;
  width: 100dvw;
  height: 100dvh;
  background: rgba(9, 26, 30, 0);    /* navy at 0 alpha until opening */
  color: var(--pp-cream-fg);
  z-index: var(--z-menu);             /* 40 — sits below the header lockup right side (50) */
  pointer-events: none;
  transition: background-color 240ms var(--ease);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.pp-menu[hidden] { display: none; }
.pp-menu.is-open {
  background-color: rgba(9, 26, 30, 1);
  pointer-events: auto;
}
.pp-menu.is-closing {
  background-color: rgba(9, 26, 30, 0);
  transition: background-color 240ms var(--ease);
  pointer-events: none;
}

.pp-menu__header {
  position: relative;
  flex: 0 0 13svh;
  /* Empty 13svh band that mirrors the page-header reservation, so menu items
     below it land below the lockup zone per mockup 2's spacing. */
}

/* X close button: lives in the page header (above the overlay), so its visibility
   selectors reach across the DOM with body:has(). Appears right after the bg
   fades in (240ms), exits fast (120ms) so the splash reverse can play next.    */
.pp-menu__close {
  opacity: 0;
  transition: opacity 200ms var(--ease);
}
body:has(.pp-menu.is-open) .pp-menu__close {
  opacity: 1;
  pointer-events: auto;
  transition: opacity 200ms var(--ease) 240ms;
}
body:has(.pp-menu.is-closing) .pp-menu__close {
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms var(--ease) 0ms;
}
.pp-menu__close img { transition: transform var(--t-200) var(--ease); }
.pp-menu__close:hover  img { transform: scale(1.06); }
.pp-menu__close:active img { transform: scale(0.94); }

/* ─── Menu nav + items ─────────────────────────────────────────────────── */

.pp-menu__nav {
  flex: 1 1 auto;
  display: flex;
  align-items: stretch;
  justify-content: center;
}
.pp-menu__items {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding-top: 13svh;
  padding-bottom: 19svh;
}

/* Each item slides 4svh down + fades in over 280ms.
   DOM order: home, contact, services, about us, process, tips.
   Items begin cascading once X is fully in (~440ms). Decelerating gaps
   140/120/100/80/60ms — total cascade 500ms, ends at 1220ms.
   transition-delay = (landing-time - 280ms) for each. */
.pp-menu__item {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 7svh;
  line-height: 1;
  color: var(--pp-cream-fg);
  text-transform: lowercase;
  opacity: 0;
  transform: translateY(-4svh);
  /* Cascade delay is applied per-property via this custom property so
     other animated properties (scale, color on hover) can stay at 0
     delay even while opacity/transform inherit the open-cascade timing. */
  --pp-cascade-delay: 0ms;
  transition:
    opacity 280ms var(--ease) var(--pp-cascade-delay),
    transform 280ms var(--ease) var(--pp-cascade-delay);
}
.pp-menu.is-open .pp-menu__item {
  opacity: 1;
  transform: translateY(0);
}
.pp-menu.is-open li:nth-child(6) .pp-menu__item { --pp-cascade-delay: 440ms; } /* tips     — lands  720ms */
.pp-menu.is-open li:nth-child(5) .pp-menu__item { --pp-cascade-delay: 580ms; } /* process  — lands  860ms */
.pp-menu.is-open li:nth-child(4) .pp-menu__item { --pp-cascade-delay: 700ms; } /* about us — lands  980ms */
.pp-menu.is-open li:nth-child(3) .pp-menu__item { --pp-cascade-delay: 800ms; } /* services — lands 1080ms */
.pp-menu.is-open li:nth-child(2) .pp-menu__item { --pp-cascade-delay: 880ms; } /* contact  — lands 1160ms */
.pp-menu.is-open li:nth-child(1) .pp-menu__item { --pp-cascade-delay: 940ms; } /* home     — lands 1220ms */

/* Close: items fade out together (no reverse cascade). */
.pp-menu.is-closing .pp-menu__item {
  opacity: 0;
  transform: translateY(0);
  --pp-cascade-delay: 0ms;
  transition: opacity 200ms var(--ease);
}

.pp-menu__item:hover  { opacity: 0.7; }
.pp-menu__item:active { transform: scale(0.98); }

/* ─── Buttons (per §9) ──────────────────────────────────────────────────── */
/* .pp-btn is the shared shape; .pp-btn-navy is the only variant built so far —
   primary blue / ghost will land when their sections need them. */

.pp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1.6vh 4vw;
  border-radius: var(--r-button);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: clamp(14px, 1.8svh, 18px);
  letter-spacing: 0.01em;
  text-transform: lowercase;
  white-space: nowrap;
  min-height: 48px;
  border: 0;
  cursor: pointer;
  transition: scale var(--t-200) var(--ease), box-shadow var(--t-200) var(--ease);
}
.pp-btn-navy {
  background-color: var(--pp-navy);
  color: var(--pp-cream-fg);
}
.pp-btn-navy:active { scale: 0.98; }

.pp-btn-cream {
  background-color: var(--pp-cream-fg);
  color: var(--pp-navy);
}
.pp-btn-cream:active { scale: 0.98; }

/* ─── Introduction (Section 02) ─────────────────────────────────────────── */
/* DOM order matches mobile flow (eyebrow → title → square → copy → button).
   Desktop pulls the square out as an absolute child so the text column can flex-center.
   Cream background is full-bleed (lives on the section's [data-tone="cream"] rule);
   .pp-intro carries no padding/border/margin of its own. */

.pp-intro {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.pp-intro__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 1.2svh;          /* white "1.2" font-height block */
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pp-navy);
  margin: 0;
  line-height: 1;
  min-height: 6svh;           /* "6" auto-sizing container — font centers inside */
  display: flex;
  align-items: center;
}
.pp-intro__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-navy);
  line-height: 1.2;
  margin: 0;
  text-align: left;
}
.pp-intro__copy {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: clamp(15px, 1.9svh, 18px);
  line-height: 1.6;
  color: var(--pp-navy);
}
.pp-intro__copy p { margin: 0; }
.pp-intro__copy p + p { margin-top: 1em; }

.pp-intro__square {
  margin: 0;
  border-radius: var(--r-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.pp-intro__square img,
.pp-intro__square video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Subtle scroll-parallax (set by reveal.js). The media is scaled up
     slightly so the translation never exposes the container's edges. */
  transform: translate3d(0, var(--pp-parallax-y, 0), 0) scale(1.08);
  will-change: transform;
}

/* ─── Intro Carousel ────────────────────────────────────────────────────
   Five-image cross-fade carousel inside `.pp-intro__square`. Slides are
   absolutely stacked; only the `.is-active` slide is opaque. The figure
   already establishes a positioning context (`position: absolute` on
   desktop, default block in flow on mobile — both work as containing
   blocks for the absolutely-positioned slides), so we don't override
   its `position` here. Buttons mirror the detail-card overlay style
   (navy-glass circles).

   iOS-specific resets on slides + nav/fullscreen buttons: the default
   `-webkit-tap-highlight-color` is a translucent grey that re-fires on
   every tap, so rapid clicking through the carousel made the whole
   image area look greyed-out. `user-select`/`touch-callout` off also
   prevents the long-press save-image menu interrupting taps. */
.pp-intro__square .pp-carousel__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  pointer-events: none;            /* inactive slides don't intercept clicks meant for the visible one */
  transition: opacity 600ms var(--ease);
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* width/height/object-fit/parallax-transform inherited from the
     `.pp-intro__square img` rule above. */
}
.pp-intro__square .pp-carousel__slide.is-active {
  opacity: 1;
  pointer-events: auto;
}

.pp-carousel__nav,
.pp-carousel__fullscreen {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5svh;
  height: 5svh;
  border-radius: 999px;
  border: 0;
  cursor: pointer;
  background: rgba(9, 26, 30, 0.55);
  color: var(--pp-white);
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--t-200) var(--ease), scale var(--t-200) var(--ease);
}
.pp-carousel__nav:hover,
.pp-carousel__fullscreen:hover { background: rgba(9, 26, 30, 0.8); scale: 1.06; }
.pp-carousel__nav svg,
.pp-carousel__fullscreen svg { width: 60%; height: 60%; display: block; }
.pp-carousel__nav--prev { left: 1.6svh; top: 50%; transform: translateY(-50%); }
.pp-carousel__nav--next { right: 1.6svh; top: 50%; transform: translateY(-50%); }
.pp-carousel__fullscreen { bottom: 1.6svh; right: 1.6svh; }

/* ─── Fullscreen Carousel Dialog ──────────────────────────────────────── */
.pp-carousel-full {
  position: fixed;
  inset: 0;
  width: 100svw;
  height: 100svh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pp-white);
  overflow: hidden;
}
.pp-carousel-full::backdrop { background: rgba(9, 26, 30, 0.92); }
.pp-carousel-full__image {
  position: absolute;
  top: 4svh;
  left: 4svw;
  width: calc(100svw - 8svw);
  height: calc(100svh - 8svh);
  object-fit: contain;
  display: block;
  /* `object-fit: contain` letterboxes the visible image inside this
     box, but the img element itself still fills the whole box and
     captures clicks. Pass clicks through so the dialog's backdrop
     handler fires anywhere outside the nav/close buttons. */
  pointer-events: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.pp-carousel-full__nav,
.pp-carousel-full__close {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 6svh;
  height: 6svh;
  border-radius: 999px;
  border: 0;
  cursor: pointer;
  /* Match the home-page nav buttons' navy-glass — lighter white
     versions were hard to see against bright photos on mobile. */
  background: rgba(9, 26, 30, 0.55);
  color: var(--pp-white);
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--t-200) var(--ease), scale var(--t-200) var(--ease);
}
.pp-carousel-full__nav:hover,
.pp-carousel-full__close:hover { background: rgba(9, 26, 30, 0.8); scale: 1.06; }
.pp-carousel-full__nav svg,
.pp-carousel-full__close svg { width: 60%; height: 60%; display: block; }
.pp-carousel-full__nav--prev { left: 2svw; top: 50%; transform: translateY(-50%); }
.pp-carousel-full__nav--next { right: 2svw; top: 50%; transform: translateY(-50%); }
.pp-carousel-full__close { top: 2svh; right: 2svw; }

/* ─── Services (Section 03) ─────────────────────────────────────────────── */
/* Navy bg full-bleed via [data-tone="navy"]. Inner padding (13svh top/bottom,
   13svw L/R) lives on .pp-services. Card grid uses 6 equal columns so the
   row layouts (2-up, 3-up, full) hit the mockup's exact svw widths. */

.pp-services {
  position: relative;
  width: 100%;
}
.pp-services__header {
  position: relative;
}
.pp-services__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-white);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;          /* white "1.2" font-height block */
  line-height: 1;
  margin: 0;
  min-height: 6svh;           /* "6" auto-sizing container — font centers inside */
  display: flex;
  align-items: center;
}
.pp-services__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-white);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;                  /* no spacer — title sits directly below the 6svh eyebrow container */
}
.pp-services__grid {
  margin-top: 3svh;
  display: grid;
}

.pp-services__card {
  position: relative;
  display: block;
  border: 0;
  padding: 0;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-card);
  background: transparent;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: box-shadow var(--t-320) var(--ease);
}
.pp-services__card:hover { box-shadow: var(--shadow-card-hover); }

.pp-services__card-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-320) var(--ease);
}
.pp-services__card:hover .pp-services__card-img { transform: scale(1.04); }
/* 50% black overlay over the home-page service card images so the
   white label stays readable. The expanded detail card uses the raw
   image (its hero <img> doesn't have this overlay). */
.pp-services__card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  pointer-events: none;
  z-index: 1;
}

.pp-services__card-label {
  position: absolute;
  inset: 0;
  z-index: 2;                  /* above the dark overlay */
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-white);
  font-size: clamp(18px, 2.6svh, 28px);
  text-align: center;
  pointer-events: none;
  transition: transform var(--t-320) var(--ease);
}
.pp-services__card:hover .pp-services__card-label { transform: translateY(-5px); }

/* ─── Detail overlay (services + reusable for projects/tips) ────────────── */
/* Sheet sits over the section it was triggered from. Header lockup (z:100)
   stays on top per the standing rule; detail surface at z:90.
   Open animation uses a FLIP transform on the hero <img> so the card's image
   appears to expand into the sheet's hero slot. */

.pp-detail {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: auto;
}
.pp-detail[hidden] { display: none; }

/* Backdrop: full-bleed flat #091A1E behind the sheet, in front of page content
   but below the header lockup. Fades 0→1 on open (320ms), 1→0 on close (320ms). */
.pp-detail__backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--pp-navy);             /* #091A1E — flat layer, no scrim/blur/gradient */
  opacity: 0;
  transition: opacity 320ms var(--ease);
  pointer-events: auto;                   /* clicks on backdrop bubble up to close handler */
}
.pp-detail.is-open    .pp-detail__backdrop { opacity: 1; }
.pp-detail.is-closing .pp-detail__backdrop { opacity: 0; transition: opacity 320ms var(--ease); }

.pp-detail__sheet {
  position: absolute;
  z-index: 1;
  background: #18292F;                    /* slightly lighter than the backdrop, gives separation */
  border-radius: 20px;                    /* one-off per spec §22 */
  box-shadow: 0 30px 80px rgba(9, 26, 30, 0.45); /* one-off per spec §32 */
  overflow: hidden;
  overscroll-behavior: contain;           /* keep iOS rubber-band INSIDE the sheet (so it reveals the sheet's own bg, not the body's) */
  display: flex;
  flex-direction: column;
  /* Slide-up open / slide-down close. Applies to every detail card
     (services + tip/project) at every breakpoint — the FLIP open was
     retired in favor of this consistent slide. */
  transform: translateY(100dvh);
  transition: transform 460ms var(--ease);
}
.pp-detail.is-open .pp-detail__sheet {
  transform: translateY(0);
}
.pp-detail.is-closing .pp-detail__sheet {
  transform: translateY(100vh);
  transition: transform 460ms var(--ease);
}

/* Wrap holds the hero image plus its overlay controls (zoom toggle +
   prev/next nav). Sizing rules live on the wrap; the image fills it. */
.pp-detail__hero-wrap {
  position: relative;
  display: block;
  overflow: hidden;
  background: #18292F;
  /* sizing (flex / aspect-ratio) per breakpoint */
}
.pp-detail--tip .pp-detail__hero-wrap { background: var(--pp-blue); }
/* Tip expanded card: no hero image — just the blue sheet bg + copy. */
.pp-detail--type-tip .pp-detail__hero-wrap { display: none; }
.pp-detail__hero {
  display: block;
  width: 100%;
  height: 100%;
  /* Default (closed/unzoomed expanded card): cover the image area so
     the card image fills its frame. Zoomed view switches to `contain`
     so the full image fits inside the card without cropping. */
  object-fit: cover;
}
.pp-detail.pp-detail--zoomed .pp-detail__hero {
  object-fit: contain;
}

/* Multi-image stack — used on mobile when the user zooms a multi-image
   card. Each image fits to width with full height visible; the stack
   scrolls vertically. 3svh gap between images. */
.pp-detail__hero-stack {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 3svh;
  padding: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.pp-detail__hero-stack::-webkit-scrollbar { display: none; }
.pp-detail__hero-stack > img {
  display: block;
  width: 100%;
  height: auto;
  flex: 0 0 auto;
  object-fit: contain;
}
.pp-detail__hero-stack[hidden] { display: none; }

/* Overlay buttons: bottom-right zoom + side prev/next. Pure CSS — JS
   only toggles the .pp-detail--zoomed class on the wrapper and updates
   the heroImg.src for nav. */
.pp-detail__hero-zoom,
.pp-detail__hero-nav {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5svh;
  height: 5svh;
  border-radius: 999px;
  border: 0;
  cursor: pointer;
  background: rgba(9, 26, 30, 0.55);
  color: var(--pp-white);
  padding: 0;
  transition: background-color var(--t-200) var(--ease), scale var(--t-200) var(--ease);
}
.pp-detail__hero-zoom:hover,
.pp-detail__hero-nav:hover { background: rgba(9, 26, 30, 0.8); scale: 1.06; }
.pp-detail__hero-zoom svg,
.pp-detail__hero-nav svg { width: 60%; height: 60%; display: block; }
.pp-detail__hero-zoom {
  bottom: 1.6svh;
  right: 1.6svh;
}
.pp-detail__hero-nav--prev { left: 1.6svh; top: 50%; transform: translateY(-50%); }
.pp-detail__hero-nav--next { right: 1.6svh; top: 50%; transform: translateY(-50%); }
.pp-detail__hero-nav[hidden] { display: none; }
/* Toggle which zoom icon is showing based on aria-pressed. */
.pp-detail__hero-zoom .pp-detail__hero-zoom-icon--minimize { display: none; }
.pp-detail__hero-zoom[aria-pressed="true"] .pp-detail__hero-zoom-icon--expand { display: none; }
.pp-detail__hero-zoom[aria-pressed="true"] .pp-detail__hero-zoom-icon--minimize { display: block; }

/* Zoomed state: hero wrap fills the entire sheet, content area hidden.
   Sizing-per-breakpoint rules (flex / aspect-ratio) are overridden. */
.pp-detail.pp-detail--zoomed .pp-detail__hero-wrap {
  flex: 1 1 100% !important;
  height: 100% !important;
  aspect-ratio: auto !important;
}
.pp-detail.pp-detail--zoomed .pp-detail__content {
  display: none;
}
/* Tip + project zoomed view: swap the wrap (and sheet) bg from blue
   to white so the contain-fit images letterbox cleanly and the 3svh
   gaps in the mobile stack read as page breaks. */
.pp-detail.pp-detail--zoomed.pp-detail--tip .pp-detail__hero-wrap,
.pp-detail.pp-detail--zoomed.pp-detail--tip .pp-detail__sheet {
  background: var(--pp-white);
}
/* Mobile: hide the prev/next nav while zoomed — multi-image cards
   become a scrolling vertical stack instead. Desktop keeps the arrows
   for paging through individual full-fit images. */
@media (max-width: 767.98px) {
  .pp-detail.pp-detail--zoomed .pp-detail__hero-nav { display: none; }
}
/* Pin the zoom button to the bottom-right of the viewport while
   zoomed, so it stays visible while the user scrolls the stack. */
.pp-detail.pp-detail--zoomed .pp-detail__hero-zoom {
  position: fixed;
  bottom: 1.6svh;
  right: 1.6svh;
  z-index: 10;
}

.pp-detail__content {
  position: relative;
  flex: 0 0 auto;
  margin-top: 2svh;
  /* horizontal padding per breakpoint */
}

.pp-detail__close {
  position: absolute;
  top: 0;
  right: 0;
  width: 6svh;
  height: 6svh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0;
}
.pp-detail__close img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform var(--t-200) var(--ease);
}
.pp-detail__close:hover  img { transform: scale(1.06); }
.pp-detail__close:active img { transform: scale(0.94); }

.pp-detail__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-cream-fg);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;           /* "6" auto-sizing container — font centers inside */
  display: flex;
  align-items: center;
}
.pp-detail__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;                  /* no spacer — title sits directly against the 6svh eyebrow container */
  min-height: 7svh;           /* auto-grows when title wraps; 7svh floor otherwise */
}
.pp-detail__copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 2svh;
  line-height: 1.6;
  /* 2svh top — copy starts where the (removed) pricing line used to. */
  margin: 2svh 0 0;
}
.pp-detail__copy p { margin: 0; white-space: pre-line; }
.pp-detail__copy p + p { margin-top: 1em; }

.pp-detail__buttons {
  display: flex;
  margin-top: 5svh;
  /* gap + direction per breakpoint */
}
.pp-detail__buttons .pp-btn {
  height: 7svh;                 /* 7svh button container per chat */
  line-height: 1.6;
}

/* Sheet content (close button, eyebrow, title, copy, action buttons)
   stays visible and slides in / out with the sheet — no fade cascade. */
.pp-detail__close,
.pp-detail__content > :not(.pp-detail__close) {
  opacity: 1;
}

body.pp-carousel-full-locked { overflow: hidden; }
body.pp-detail-locked { overflow: hidden; background: #18292F; }
body.pp-detail-locked:has(.pp-detail--tip) { background: #86C4D7; }

/* Hide scrollbars site-wide and on the expanded card sheet, while keeping
   scroll behavior intact. */
html, body, .pp-detail__sheet { scrollbar-width: none; }
html::-webkit-scrollbar,
body::-webkit-scrollbar,
.pp-detail__sheet::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .pp-detail__hero { transition: none !important; transform: none !important; }
  .pp-detail.is-closing .pp-detail__sheet {
    transform: none !important;
    transition: none !important;
  }
  .pp-detail__backdrop {
    transition: opacity 200ms var(--ease) !important;
  }
  .pp-detail__close,
  .pp-detail__content > :not(.pp-detail__close) {
    transition: opacity 200ms var(--ease) !important;
    transition-delay: 0ms !important;
  }
}

/* ─── Mac-only message-choice dialog (text vs email) ───────────────────── */

.pp-message-choice {
  position: fixed;
  inset: 0;
  margin: auto;
  border: 0;
  border-radius: var(--r-card);
  padding: 5svh 4svw;
  background: var(--pp-cream);
  box-shadow: 0 30px 80px rgba(9, 26, 30, 0.45);
  max-width: 36svw;
  color: var(--pp-navy);
}
.pp-message-choice::backdrop {
  background: rgba(9, 26, 30, 0.5);
}
.pp-message-choice__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 4svh;
  line-height: 1.2;
  margin: 0;
}
.pp-message-choice__buttons {
  display: flex;
  gap: 2svw;
  margin-top: 4svh;
}
.pp-message-choice__buttons .pp-btn {
  flex: 1 1 0;
  height: 7svh;
  font-size: 1.6svh;
}
.pp-message-choice__close {
  position: absolute;
  top: 2svh;
  right: 2svw;
  width: 4svh;
  height: 4svh;
  background: transparent;
  border: 0;
  outline: none;
  -webkit-tap-highlight-color: transparent;
  color: var(--pp-navy);
  font-size: 3svh;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

/* ─── Hidden video player (fullscreen launcher) ─────────────────────────── */
/* Stays in the DOM at 1×1px so requestFullscreen / webkitEnterFullscreen work,
   but is visually invisible until video.js triggers fullscreen on click. */

.pp-video {
  position: fixed;
  bottom: 0;
  right: 0;
  width: 1px;
  height: 1px;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
/* When the video enters fullscreen via requestFullscreen, restore visibility. */
.pp-video:fullscreen,
.pp-video:-webkit-full-screen {
  opacity: 1;
  width: 100vw;
  height: 100vh;
  pointer-events: auto;
}

/* ─── Contact (Section 06) ──────────────────────────────────────────────── */
/* Blue bg full-bleed via [data-tone="blue"]. Section auto-heights. */

.pp-section--contact { min-height: 100svh; }

.pp-contact {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100svh;
}
.pp-contact__row { display: flex; }
.pp-contact__left { display: flex; flex-direction: column; }
.pp-contact__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-white);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-contact__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-white);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}
.pp-contact__copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-white);
  font-size: 2svh;
  line-height: 1.6;
  margin: 0;
}
.pp-contact__link {
  display: flex;
  align-items: center;
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-white);
  text-decoration: none;
  font-size: 2.5svh;
  line-height: 1;
  min-height: 3svh;
  gap: 1.2svw;
}
.pp-contact__drop {
  display: block;
  height: 3svh;
  width: auto;
}

.pp-contact__form {
  background: var(--pp-white);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  padding: 3svh 3svw 5svh;
}
.pp-contact__field { display: flex; flex-direction: column; }
.pp-contact__field-label {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.6svh;
  line-height: 1;
  min-height: 3svh;
  padding-bottom: 1svh;
  display: flex;
  align-items: center;
}
.pp-contact__field input,
.pp-contact__field textarea {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1.6svh;
  color: var(--pp-navy);
  background: var(--pp-white);
  border: 1px solid #E0E2E2;
  border-radius: var(--r-input);
  padding: 0 1svw;
  height: 7svh;
  width: 100%;
}
.pp-contact__field textarea {
  height: auto;
  min-height: 14svh;
  padding: 1.5svh 1svw;
  line-height: 1.6;
  resize: vertical;
}
.pp-contact__field input::placeholder,
.pp-contact__field textarea::placeholder {
  color: rgba(9, 26, 30, 0.35);
}
.pp-contact__submit { height: 7svh; }

/* ─── FAQ (Section 07) ──────────────────────────────────────────────────── */

.pp-section--faq { min-height: 0; }

.pp-faq { display: flex; flex-direction: column; }
.pp-faq__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-faq__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-navy);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}
.pp-faq__divider {
  height: 1px;
  background: #575E59;
  width: 100%;
}
.pp-faq__item { display: flex; flex-direction: column; transform-origin: left center; }
.pp-faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  font-size: 3svh;
  text-align: left;
  min-height: 6svh;
  line-height: 1.2;
}
.pp-faq__q-text { flex: 1 1 auto; }
.pp-faq__icon-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5svw;
  height: 3svh;
  flex: 0 0 auto;
}
.pp-faq__icon {
  display: block;
  height: 100%;
  width: 100%;
  object-fit: contain;
  transition: transform 200ms var(--ease);
}
.pp-faq__item:not(.is-open) .pp-faq__icon {
  transform: rotate(45deg);  /* X → + when closed */
}
.pp-faq__body {
  overflow: hidden;
  max-height: 100vh;
  transition: max-height 320ms var(--ease);
}
.pp-faq__item:not(.is-open) .pp-faq__body { max-height: 0; }
.pp-faq__answer {
  font-family: var(--font-body);
  font-weight: 400;
  color: #575E59;
  font-size: 1.6svh;
  line-height: 1.6;
  margin: 0;
}
/* Per-answer "Read More / Read Less" toggle — blue, inherits the
   answer's font / size so it reads as a sibling line of copy rather
   than a separate UI control. */
.pp-faq__readmore {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1.6svh;
  line-height: 1.6;
  color: var(--pp-blue);
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-align: left;
}
.pp-faq__readmore:hover { opacity: 0.8; }
.pp-faq__answer-extra[hidden] { display: none; }
.pp-faq__list {
  font-family: var(--font-body);
  font-weight: 400;
  color: #575E59;
  font-size: 1.6svh;
  line-height: 1.6;
  margin: 0;
  padding-left: 1.5em;
}
.pp-faq__list li { margin: 0; }

/* ─── Projects (Section 10) + Tips (Section 11) cards (cream sections) ──── */
/* Both sections use white card surfaces on cream bg.
   Cards are buttons so the entire card is clickable to open the detail overlay. */

.pp-section--projects { min-height: 0; }
.pp-section--tips { min-height: 0; }

.pp-projects, .pp-tips { display: flex; flex-direction: column; }

.pp-projects__eyebrow,
.pp-tips__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-projects__title,
.pp-tips__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-navy);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}

/* Shared card surface */
.pp-projects__card,
.pp-tips__card {
  display: flex;
  flex-direction: column;
  background: var(--pp-white);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
  transition: box-shadow var(--t-320) var(--ease);
}
.pp-projects__card:hover,
.pp-tips__card:hover { box-shadow: var(--shadow-card-hover); }

.pp-projects__card-image,
.pp-tips__card-image {
  display: block;
  width: 100%;
  object-fit: cover;
}

.pp-projects__card-body,
.pp-tips__card-body {
  display: flex;
  flex-direction: column;
  padding: 3svh 3svw;
  flex: 1 1 auto;
}
.pp-projects__card-title,
.pp-tips__card-title {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  font-size: 2.4svh;
  line-height: 1.2;
  margin: 0;
}
.pp-projects__card-copy,
.pp-tips__card-copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-navy);
  font-size: 1.6svh;
  line-height: 1.6;
  margin: 0;
}
/* Project cards on the home page show only a 3-line teaser. We use a
   JS-driven truncation (services.js) instead of `-webkit-line-clamp`
   because the JS path can land on a clean word boundary and strip
   trailing punctuation — avoiding artifacts like ",…" at the cutoff. */
.pp-projects__card-copy {
  overflow: hidden;
}
.pp-projects__card-link,
.pp-tips__card-link {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-blue);
  font-size: 1.4svh;
  margin-top: auto;       /* push to bottom of card body */
  display: inline-block;
  align-self: flex-start; /* hug the left edge so scale grows rightward */
  transform-origin: left center;
  transition: scale var(--t-200) var(--ease);
}
.pp-projects__card-link:hover,
.pp-tips__card-link:hover {
  scale: 1.07;
}

/* ─── Tip / Project detail theme variant ──────────────────────────────────
   Reuses .pp-detail HTML but switches color theme via .pp-detail--tip class.
   No pricing line shown. */
.pp-detail--tip .pp-detail__backdrop { background: var(--pp-cream); }
.pp-detail--tip .pp-detail__sheet { background: var(--pp-blue); }
.pp-detail--tip .pp-detail__eyebrow,
.pp-detail--tip .pp-detail__title,
.pp-detail--tip .pp-detail__copy { color: var(--pp-white); }
.pp-detail--tip .pp-btn-cream { background: var(--pp-white); }
.pp-detail--tip .pp-btn-navy { color: var(--pp-white); }

/* ─── Footer (Section 13) ────────────────────────────────────────────────
   Uses 3svw outer padding (not the standard 13svw). */

.pp-footer {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.pp-footer__top {
  display: flex;
  align-items: flex-start;
  gap: 3svw;
}
.pp-footer__logo img {
  display: block;
  height: 7svh;
  width: auto;
}
.pp-footer__col {
  display: flex;
  flex-direction: column;
}
.pp-footer__col-title {
  font-family: var(--font-body);
  font-weight: 500;
  color: #82857B;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 2.1svh;
  line-height: 1;
  margin: 0;
}
.pp-footer__link {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 1.9svh;
  line-height: 1.4;
  text-decoration: none;
}
.pp-footer__link:hover { opacity: 0.8; }

.pp-footer__divider {
  width: 100%;
  height: 2px;
  background: var(--pp-cream-fg);
  opacity: 0.3;
}
.pp-footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.pp-footer__copyright,
.pp-footer__license {
  font-family: var(--font-body);
  font-weight: 400;
  color: #82857B;
  font-size: 1.4svh;
  line-height: 1.4;
  margin: 0;
}

/* ─── CTA (Section 12) ──────────────────────────────────────────────────── */
/* Blue bg via [data-tone="blue"]. Auto-height. Content centered horizontally. */

.pp-section--cta { min-height: 0; }

.pp-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pp-cta__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-white);
  font-size: 9svh;
  line-height: 1.2;
  margin: 0;
  text-align: center;
}
.pp-cta__copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-white);
  font-size: 3svh;
  line-height: 1.4;
  margin: 0;
  text-align: center;
}
.pp-cta__btn {
  height: 7svh;
}
/* CTA-specific button color tweaks (white instead of cream within this section) */
.pp-cta__btn.pp-btn-navy  { color: var(--pp-white); }
.pp-cta__btn.pp-btn-cream { background-color: var(--pp-white); }
.pp-cta__spacer-edge { flex: 0 0 auto; height: 21svh; }
.pp-cta__spacer-paragraph { flex: 0 0 auto; height: 5svh; }

/* ─── About (Section 08) ────────────────────────────────────────────────── */
/* Cream bg full-bleed via [data-tone="cream"]. Section auto-heights. */

.pp-section--about { min-height: 0; }

.pp-about { display: flex; flex-direction: column; }
.pp-about__layout { display: flex; }
.pp-about__content { display: flex; flex-direction: column; }

.pp-about__image-col {
  position: relative;            /* anchor for the absolute video btn */
  border-radius: var(--r-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
/* Watch-video button overlaid dead-center on the Meet Braydon image.
   Centered via inset:0 + margin:auto rather than translate(-50%,-50%)
   so the hover-scale doesn't drift — using transform for centering
   would compose with the hover `scale` and shift the element on grow. */
.pp-about__video-btn {
  position: absolute;
  inset: 0;
  margin: auto;
  width: max-content;
  height: max-content;
}
.pp-about__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Same scroll-parallax pattern as the intro image. */
  transform: translate3d(0, var(--pp-parallax-y, 0), 0) scale(1.08);
  will-change: transform;
}
.pp-about__image-col--mobile { display: none; }

.pp-about__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-about__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-navy);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}
.pp-about__paragraph {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-navy);
  line-height: 1.6;
  margin: 0;
}

.pp-about__checks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3svh;
}
.pp-about__check {
  display: flex;
  align-items: center;
  gap: 1svw;
  min-height: 3svh;
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-navy);
  line-height: 1.4;
}
.pp-about__check-icon {
  display: block;
  height: 1.6svh;
  width: auto;
  flex: 0 0 auto;
  margin-right: 1svw;
}

/* ─── Reviews (Section 09) ──────────────────────────────────────────────── */
/* Navy bg full-bleed via [data-tone="navy"]. Section auto-heights.
   Cards are #122930 per spec §31. */

.pp-section--reviews { min-height: 0; }

.pp-reviews { display: flex; flex-direction: column; }
.pp-reviews__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-cream-fg);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-reviews__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}

.pp-reviews__cards {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
}
.pp-reviews__card {
  display: flex;
  flex-direction: column;
  background: #122930;
  border-radius: var(--r-card);
  padding: 3svh 5svw;
}
.pp-reviews__card-icon {
  display: block;
  height: 3svh;
  width: auto;
  align-self: flex-start;
}
.pp-reviews__card-copy {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 3svh;
  line-height: 1.4;
  margin: 0;
}
.pp-reviews__card-grow {
  flex: 1 1 auto;
  min-height: 2svh;
  order: 99;          /* push grow to the end so name sits 3svh under copy, not at card bottom */
}
.pp-reviews__card-name {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 1.6svh;
  line-height: 1;
  margin: 0;
}

/* ─── Process (Section 05) ──────────────────────────────────────────────── */
/* Navy bg via [data-tone="navy"]. Section auto-heights to its content. */

.pp-section--process { min-height: 0; }

.pp-process { display: flex; flex-direction: column; }
.pp-process__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-cream-fg);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
}
.pp-process__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
}
.pp-process__steps {
  list-style: none;
  margin: 0;
  padding: 0;
}
.pp-process__step {
  display: flex;
  flex-direction: column;
}
.pp-process__step-head { display: flex; flex-direction: column; }
.pp-process__step-head-gap {
  flex: 0 0 auto;
  height: 3svh;
}
.pp-process__step-number {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-blue);
  font-size: 7svh;
  line-height: 1;
  margin: 0;
  min-height: 7svh;
  display: flex;
  align-items: center;
}
.pp-process__step-name {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-cream-fg);
  font-size: 3.4svh;
  line-height: 1;
  margin: 0;
  min-height: 7svh;
  display: flex;
  align-items: center;
}
.pp-process__step-copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-cream-fg);
  line-height: 1.6;
  margin: 0;
}

/* ─── Generic spacer element ────────────────────────────────────────────── */
/* Use a literal <div class="pp-spacer" style="height: Xsvh"> between content
   elements instead of margin/padding-for-spacing. Width auto (collapses to 0
   in centered flex columns); height carries the gap. */
.pp-spacer { flex: 0 0 auto; }

/* ─── Service Area (Section 04) ────────────────────────────────────────── */
/* Cream bg full-bleed via [data-tone="cream"]. Section auto-heights to its
   content (no 100svh floor) per chat instruction. */

.pp-section--service-area { min-height: 0; }

.pp-area {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pp-area__icon {
  display: block;
  height: 7svh;
  width: auto;
}
.pp-area__eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--pp-navy);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 1.2svh;
  line-height: 1;
  margin: 0;
  min-height: 6svh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.pp-area__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pp-navy);
  font-size: 7svh;
  line-height: 1.2;
  margin: 0;
  text-align: center;
}
.pp-area__copy {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--pp-navy);
  line-height: 1.6;
  margin: 0;
  text-align: center;
}
.pp-area__image {
  display: block;
  width: 34svw;
  height: 34svw;
  object-fit: cover;
  border-radius: var(--r-card);
}

/* ─── Body scroll lock ──────────────────────────────────────────────────── */

body.pp-menu-locked { overflow: hidden; }

/* ─── Reduced-motion overrides ──────────────────────────────────────────── */
/* Skip drip + splash + cascade entirely; bg/items/X all fade together (~200ms). */

@media (prefers-reduced-motion: reduce) {
  .pp-header__drop.is-dripping-out,
  .pp-header__drop.is-dripping-in {
    animation: none !important;
    transform: none !important;
  }
  .pp-menu__splash { display: none !important; }

  .pp-menu,
  .pp-menu.is-open,
  .pp-menu.is-closing {
    transition: background-color 200ms var(--ease) !important;
    transition-delay: 0ms !important;
  }
  .pp-header__logo,
  .pp-menu__logo,
  .pp-menu__close,
  .pp-menu__item,
  .pp-menu.is-open    .pp-menu__item,
  .pp-menu.is-closing .pp-menu__item,
  body:has(.pp-menu.is-open)    .pp-header__logo,
  body:has(.pp-menu.is-open)    .pp-menu__logo,
  body:has(.pp-menu.is-open)    .pp-menu__close,
  body:has(.pp-menu.is-closing) .pp-header__logo,
  body:has(.pp-menu.is-closing) .pp-menu__logo,
  body:has(.pp-menu.is-closing) .pp-menu__close {
    transition: opacity 200ms var(--ease) !important;
    transition-delay: 0ms !important;
    transform: none !important;
  }

  html { scroll-behavior: auto; }
}

/* ─── Scroll-reveal (managed by reveal.js) ────────────────────────────────
   Class applied by JS at runtime so a script failure leaves content
   visible, not hidden. .is-revealed is added once when the element's
   section enters the viewport. */
/* ─── Hover scale for interactive elements ────────────────────────────────
   Buttons grow 7% on hover (no color change). Cards grow 3%. Uses the
   `scale` CSS property so the hover scale composes with inline
   `transform` set by reveal.js (slide-ups, group scales) instead of
   overwriting it. Decorative imagery is left alone so hover stays as
   a clickability cue. */
.pp-services__card,
.pp-projects__card,
.pp-tips__card,
.pp-reviews__card {
  transition: scale var(--t-200) var(--ease), box-shadow var(--t-320) var(--ease);
}
.pp-services__card:hover,
.pp-projects__card:hover,
.pp-tips__card:hover,
.pp-reviews__card:hover {
  scale: 1.03;
}
.pp-btn:hover,
.pp-hero__pill:hover {
  scale: 1.035;
}

.pp-reveal {
  opacity: 0;
  transform: translateY(2svh);
  transition: opacity 600ms var(--ease), transform 600ms var(--ease);
  will-change: opacity, transform;
}
.pp-reveal.is-revealed {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .pp-reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
