/* =========================================================================
   Merlot — Pet Travel Planner — core stylesheet
   Sky Postcard design system (approved palette, do not alter hexes).
   Self-hosted, no inline style/script anywhere — CSP hash-allowlist safe.
   Render-blocking <link> — kept lean on purpose (SPEC §4.9). Step 5-8 +
   concierge + results + checklist + hotel live in planner-steps.css,
   loaded with `defer`/media-swap so they never block first paint.
   ========================================================================= */

@font-face { font-family: 'DM Serif Display'; src: url('../assets/fonts/dm-serif-display.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'DM Sans'; src: url('../assets/fonts/dm-sans.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap; }

:root {
  /* Sky Postcard palette — approved, do not alter */
  --color-cloud: #F4FAFF;
  --color-sky:   #7EC8F2;
  --color-sunny: #FFD44D;
  --color-coral: #FF7B54;
  --color-navy:  #1D3A5F;
  --color-white: #FFFFFF;
  /* Text-safe darker coral: #FF7B54 as TEXT on white/cloud measures 2.56:1,
     well under WCAG AA's 4.5:1 for normal text (and under the 3:1 large-text
     floor even for the 28px rate-limit countdown) — confirmed by contrast
     formula. This darker analog measures 5.36:1 on white / 5.1:1 on cloud.
     Button backgrounds keep --color-coral unchanged; only text uses this. */
  --color-coral-text: #B84526;
  /* Fixed, contrast-checked replacement for the old opacity:.6 "muted text"
     trick (navy at 60% opacity over white measures only 3.58:1). round-10:
     the previous #5C718C (5.0:1 white / 4.76:1 cloud) sat close enough to
     WCAG AA's 4.5:1 floor that axe-core's font-swap/antialiasing timing
     could flip it under the line on some paints (qa/regress + a live
     visual-design judge pass both saw this flicker) — darkened for real
     headroom on both backgrounds instead of chasing render-timing fixes.
     Now 7.5:1 on white / 7.1:1 on cloud (computed via the WCAG relative
     luminance formula, qa/regress/checks/static.js contrast helper). */
  --color-navy-muted: #4A5E77;

  --color-cloud-dim:  #E7F2FC;
  --color-sky-soft:   #AEDBF6;
  --color-navy-80:    rgba(29,58,95,0.80);
  --color-navy-10:    rgba(29,58,95,0.10);

  --tint-toy:    #C9E8FB;
  --tint-small:  #9ED4F6;
  --tint-medium: #7EC8F2;
  --tint-large:  #4FA8DE;
  --tint-giant:  #2E7FBE;

  --verdict-ok:   #1F8A5C;
  --verdict-warn: #FFD44D;
  --verdict-no:   #FF7B54;

  --font-display: 'DM Serif Display', Georgia, serif;
  --font-body:    'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;

  --space-1: 4px;  --space-2: 8px;  --space-3: 16px; --space-4: 24px;
  --space-5: 32px; --space-6: 48px; --space-7: 64px; --space-8: 96px;

  --radius-sm: 10px; --radius-md: 18px; --radius-lg: 28px; --radius-pill: 999px;

  --shadow-sm: 0 2px 8px var(--color-navy-10);
  --shadow-md: 0 8px 24px var(--color-navy-10);
  --shadow-lg: 0 16px 48px rgba(29,58,95,0.16);

  --ease-pop: cubic-bezier(.34,1.56,.64,1);
  --ease-standard: cubic-bezier(.25,.1,.25,1);
  --ease-bounce: cubic-bezier(.68,-0.25,.32,1.25);

  --dur-micro: 160ms;
  --dur-standard: 280ms;
  --dur-scene: 480ms;

  --nav-h: 88px;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --ease-pop: var(--ease-standard);
    --ease-bounce: var(--ease-standard);
    --dur-micro: 100ms;
    --dur-standard: 100ms;
    --dur-scene: 1ms;
  }
}

/* ---- Reset-lite ---- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body.ptp {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-navy);
  background: var(--color-cloud);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.ptp h1, .ptp h2, .ptp h3 { font-family: var(--font-display); font-weight: 400; margin: 0 0 var(--space-2); }
.ptp p { margin: 0 0 var(--space-3); }
.ptp img { max-width: 100%; display: block; }
.ptp button { font-family: inherit; }
/* VIS-07 (recurring minor since round 3): every checkbox in this otherwise
   fully-bespoke illustrated flow — Step 3's service-dog checkbox, the
   email-gate consent checkbox, the One-way checkbox, every Step 7
   checklist item — was rendering as bare, unstyled native OS chrome with
   no brand colour anywhere in planner.css/planner-steps.css. One global,
   brand-coloured accent closes that finish gap without touching any
   per-checkbox markup or logic. */
.ptp input[type="checkbox"] { accent-color: var(--color-coral); width: 20px; height: 20px; cursor: pointer; }
.ptp a { color: var(--color-navy); }
.ptp .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.ptp .skip-link { position: absolute; left: -9999px; top: 0; background: var(--color-navy); color: var(--color-white); padding: 12px 18px; border-radius: 0 0 10px 0; z-index: 999; }
.ptp .skip-link:focus { left: 0; }

/* ---- App shell ---- */
.ptp-shell { max-width: 1180px; margin: 0 auto; padding: 0 var(--space-4); }
.ptp-main { padding-top: var(--space-6); padding-bottom: var(--space-8); min-height: 70vh; }

/* ---- Top nav (minimal, matches site nav tone) ---- */
.ptp-nav { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; row-gap: 6px; padding: var(--space-3) var(--space-4); max-width: 1180px; margin: 0 auto; }
.ptp-nav-logo { font-family: var(--font-display); font-size: 20px; color: var(--color-navy); text-decoration: none; flex: none; }
.ptp-nav-back { font-size: 13px; color: var(--color-navy); text-decoration: none; opacity: .75; display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 480px) { .ptp-nav-back { font-size: 12px; } }
.ptp-nav-back:hover { opacity: 1; }

/* ---- Hero / landing ---- */
.ptp-hero { position: relative; padding: var(--space-6) 0 var(--space-7); overflow: hidden; }
.ptp-hero-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: var(--space-6); align-items: center; }
.ptp-hero-eyebrow { display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--color-navy); background: var(--color-sunny); padding: 6px 14px; border-radius: var(--radius-pill); margin-bottom: var(--space-3); }
.ptp-hero h1 { font-size: clamp(32px, 4.4vw, 54px); line-height: 1.08; color: var(--color-navy); max-width: 16ch; }
.ptp-hero-sub { font-size: 18px; max-width: 46ch; color: var(--color-navy); opacity: .82; }
/* Text is navy, not white: white-on-coral measures ~2.6:1 (fails WCAG AA
   4.5:1 for this font-weight/size) — caught by a live Lighthouse a11y run.
   Coral itself stays exactly as approved; only the text color on top of it
   changes, same principle the site's own --sunny CTAs already follow. */
.ptp-hero-cta { display: inline-flex; align-items: center; gap: 8px; background: var(--color-coral); color: var(--color-navy); font-weight: 700; font-size: 17px; padding: 16px 28px; border-radius: var(--radius-pill); text-decoration: none; border: none; cursor: pointer; box-shadow: var(--shadow-md); transition: transform var(--dur-micro) var(--ease-pop), box-shadow var(--dur-standard) var(--ease-standard); }
.ptp-hero-cta:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.ptp-hero-art { position: relative; aspect-ratio: 3/4; border-radius: var(--radius-lg); overflow: hidden; background: linear-gradient(180deg, var(--color-sky-soft), var(--color-cloud)); }
.ptp-hero-art img { width: 100%; height: 100%; object-fit: cover; }
.ptp-hero-trust { margin-top: var(--space-4); display: flex; gap: var(--space-4); flex-wrap: wrap; font-size: 13px; opacity: .7; }
@media (max-width: 860px) {
  .ptp-hero-grid { grid-template-columns: 1fr; }
  .ptp-hero-art { order: -1; aspect-ratio: 16/10; }
}

/* ---- Step counter + trail chips (always-present non-animated progress cue) ---- */
.ptp-progress { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); flex-wrap: wrap; }
.ptp-step-counter { font-size: 13px; font-weight: 700; color: var(--color-navy); opacity: .7; white-space: nowrap; }
.ptp-trail { display: flex; flex-wrap: wrap; gap: var(--space-2); list-style: none; margin: 0; padding: 0; }
.ptp-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--color-cloud-dim); border: 1px solid var(--color-navy-10); color: var(--color-navy); font-size: 13px; padding: 6px 12px; border-radius: var(--radius-pill); cursor: pointer; }
.ptp-chip:hover { background: var(--color-sky-soft); }
.ptp-chip[aria-pressed="true"], .ptp-chip.is-filter-active { background: var(--color-navy); color: var(--color-white); }

/* ---- Merlot guide: INLINE avatar + speech bubble (architecture change,
   this round) ----
   Replaces the old position:fixed bottom-left overlay (.ptp-guide /
   #ptpGuideFixed) plus its entire app.js collision-avoidance subsystem
   (setupGuideCollision(): a ResizeObserver + scroll/resize listeners + a
   ~100ms rAF loop, all just to shrink/hide the avatar whenever a
   [data-guide-watch] element happened to sit under its fixed rect) and the
   separate mobile-only .ptp-guide-mobile duplicate. Every prior judge round
   found a NEW place that fixed overlay covered (Step1 choice cards, Step2
   weight readout, Step3 Continue button, Step6 flight cards, Step7
   checklist cards, Step8 concierge strip...) because a fixed-position
   element over scrolling content is structurally never fully coverable by
   per-element watch-lists. One inline element, always in normal document
   flow at the top of the step app, same markup/ids at every width — it
   cannot cover anything below it, by construction, so there is no
   collision case left to detect. The media query below only scales the
   portrait up and adds the idle "breathe" motion at >=1024px; it never
   changes `position`, `display` or source order. */
.ptp-guide-inline { display: flex; align-items: center; gap: var(--space-3); background: var(--color-white); border-radius: var(--radius-md); padding: var(--space-3); margin-bottom: var(--space-4); box-shadow: var(--shadow-sm); }
.ptp-guide-portrait { border-radius: 50%; overflow: hidden; flex: none; display: block; }
.ptp-guide-portrait img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; object-position: 50% 50%; display: block; }
/* VIS-16 fix (round 17): object-fit:cover with no object-position defaults
   to a dead-center crop, which is wrong whenever a scene composes Merlot
   off-center next to a large prop (M-01's signpost silhouette, M-06's
   airport sign banner) -- the prop, not Merlot's face, lands in the middle
   of the circle. app.js's setGuide() sets data-scene="M-0X" on #ptpGuideImg
   on every step change, so a per-scene override here always matches
   whichever image is currently loaded. Values were picked by pixel-
   simulating the exact live object-fit:cover crop window offline against
   the real deployed asset bytes (same method VIS-15 used), not guessed --
   see qa/identity/../round17_guide_crop_sim.py. M-06's mobile portrait
   (512x768, used below the 1024px breakpoint per the <picture> markup) is
   the one confirmed broken: a 50/50 crop put ~25% of the frame's height as
   a flat navy sign bar above Merlot's head; shifting toward the bottom of
   the frame keeps his whole face in view. M-06's desktop composition
   (768x512, >=1024px) already crops cleanly at center, so only the mobile
   breakpoint below needs the override. M-01 crops acceptably at center on
   both orientations already, but 62%/46% centers his face a little better
   in the circle (less of the dark silhouette, none of him lost) -- a safe
   polish pass, not a correction of a cut-off like M-06. */
#ptpGuideImg[data-scene="M-01"] { object-position: 62% 46%; }
.ptp-guide-bubble { margin: 0; font-size: 14px; font-weight: 600; color: var(--color-navy); }
@media (max-width: 1023.98px) {
  #ptpGuideImg[data-scene="M-06"] { object-position: 50% 85%; }
}
@media (min-width: 1024px) {
  .ptp-guide-inline { padding: var(--space-4); gap: var(--space-4); }
  .ptp-guide-portrait img { width: 72px; height: 72px; border: 3px solid var(--color-white); box-shadow: var(--shadow-md); background: var(--color-sky-soft); animation: ptp-breathe 8s ease-in-out infinite; }
  .ptp-guide-bubble { font-size: 15px; }
}
@keyframes ptp-breathe { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .ptp-guide-portrait img { animation: none; } }
/* One-shot head-tilt (app.js triggerGuideHeadTilt()) for a reactive guide
   line, e.g. Step 4's same-origin-airport catch. */
@keyframes ptp-guide-tilt { 0% { transform: rotate(0deg); } 30% { transform: rotate(-9deg); } 60% { transform: rotate(5deg); } 100% { transform: rotate(0deg); } }
.ptp-guide-head-tilt { animation: ptp-guide-tilt 520ms var(--ease-standard); }
@media (prefers-reduced-motion: reduce) { .ptp-guide-head-tilt { animation: none; } }

/* ---- Step panel shell ---- */
.ptp-step { display: none; }
.ptp-step.is-active { display: block; animation: ptp-step-in var(--dur-scene) var(--ease-standard); }
@keyframes ptp-step-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .ptp-step.is-active { animation: none; } }
.ptp-step-head { margin-bottom: var(--space-5); }
.ptp-step-head h2 { font-size: clamp(24px, 3vw, 34px); }

/* ---- Choice cards (Step 1) — base rules; scene-specific image rules reuse assets/icons/choice/choice-cards.css ---- */
.ptp-choice-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 680px) { .ptp-choice-grid { grid-template-columns: 1fr; } }
.ptp-choice-card { position: relative; border: none; padding: 0; background: var(--color-white); border-radius: var(--radius-lg); overflow: hidden; cursor: pointer; text-align: left; box-shadow: var(--shadow-sm); transition: transform var(--dur-micro) var(--ease-pop), box-shadow var(--dur-standard); min-height: 320px; }
.ptp-choice-card:hover, .ptp-choice-card:focus-visible { transform: translateY(-6px); box-shadow: var(--shadow-lg); }
/* Navy, not coral: coral-on-cloud measured ~2.57:1 (fails WCAG 1.4.11's 3:1
   non-text-contrast minimum for a focus indicator) — found via a live
   keyboard pass + getComputedStyle contrast math, not caught by Lighthouse
   (it doesn't audit focus-ring contrast). Navy-on-cloud measures ~8:1+. */
.ptp-choice-card:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-choice-card img { width: 100%; height: 240px; object-fit: cover; }
.ptp-choice-card-label { padding: var(--space-4); font-family: var(--font-display); font-size: 24px; }
.ptp-choice-card.is-deselected { opacity: .4; transform: scale(.97); }

/* Reselectable chip version of a deselected Step-1 card, shown in the trail */
.ptp-reselect-chip { background: var(--color-sunny); color: var(--color-navy); }

/* ---- Step 2: breed search + grid ---- */
/* NEW-02: this and every other `:focus`/`:focus-visible` outline below that
   used to say `var(--color-sky)` measured ~1.75:1 against the cloud/white
   page background -- the UX-02 fix only ever touched the catch-all
   `.ptp :focus-visible` rule at the bottom of this file; these more
   specific selectors (higher CSS specificity, so they always won) were
   never brought in line with it. All now navy, same >=3:1 WCAG 1.4.11 fix,
   one consistent focus-ring color site-wide. */
.ptp-search-input { width: 100%; padding: 14px 18px; font-size: 16px; border-radius: var(--radius-pill); border: 1.5px solid var(--color-navy-10); background: var(--color-white); color: var(--color-navy); margin-bottom: var(--space-4); }
.ptp-search-input:focus { outline: 3px solid var(--color-navy); outline-offset: 1px; }
/* round 8 visual-design BLOCKER: hard-clipped row 3 mid-circle/mid-label with
   no bottom fade and no scroll affordance (scrollHeight 2804 vs clientHeight
   420), then the breed descriptor rendered flush against that exact clipped
   edge, reading as one broken overlapping layout. The fade-mask (not an exact
   row-height max-height, since avatar size itself changes at the 681px
   breakpoint) makes the cut read as an intentional scrollable panel at any
   size; ptp-breed-grid-wrap + the JS-driven "Scroll for more" label (see
   renderGrid() in app.js) add an explicit affordance on top of the visual
   cue, and the wrapper's own margin-bottom keeps the descriptor below it
   clearly separated instead of touching the grid's edge. */
.ptp-breed-grid-wrap { position: relative; margin-bottom: var(--space-2); }
.ptp-breed-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--space-3); max-height: 420px; overflow-y: auto; padding: 4px; mask-image: linear-gradient(to bottom, #000 calc(100% - 32px), transparent); -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 32px), transparent); }
.ptp-breed-scroll-hint { position: absolute; left: 50%; bottom: -2px; transform: translateX(-50%); font-size: 12px; font-weight: 700; color: var(--color-navy); background: var(--color-white); padding: 3px 10px; border-radius: var(--radius-pill); box-shadow: var(--shadow-sm); pointer-events: none; }
.ptp-breed-scroll-hint[hidden] { display: none; }
.ptp-breed-tile { display: flex; flex-direction: column; align-items: center; gap: 6px; background: transparent; border: 2px solid transparent; border-radius: var(--radius-md); padding: var(--space-2); cursor: pointer; text-align: center; }
.ptp-breed-tile:hover { background: var(--color-cloud-dim); }
.ptp-breed-tile[aria-selected="true"] { border-color: var(--color-coral); background: var(--color-white); }
/* Keyboard-active option under the grid's aria-activedescendant pattern
   (the grid itself holds the one real Tab stop — see app.js renderStep2) —
   real :focus never lands on the tile itself, so this class is the only
   visible focus indicator a keyboard user gets. */
.ptp-breed-tile.is-active { background: var(--color-cloud-dim); outline: 3px solid var(--color-navy); outline-offset: 1px; }
.ptp-breed-avatar { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; background: var(--color-sky-soft); }
@media (min-width: 681px) { .ptp-breed-avatar { width: 96px; height: 96px; } }
.ptp-breed-avatar.is-silhouette { display: flex; align-items: center; justify-content: center; color: var(--color-navy); font-weight: 700; font-size: 11px; text-transform: uppercase; }
.ptp-breed-avatar.is-silhouette svg { width: 52%; height: 52%; fill: currentColor; }
.ptp-breed-name { font-size: 12px; font-weight: 600; line-height: 1.2; }
.ptp-breed-descriptor { margin-top: var(--space-3); background: var(--color-white); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); font-weight: 600; box-shadow: var(--shadow-sm); animation: ptp-settle 220ms var(--ease-pop); }
@keyframes ptp-settle { from { transform: scale(.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* ---- Step 2b: weight slider ---- */
.ptp-weight-panel { background: var(--color-white); border-radius: var(--radius-lg); padding: var(--space-5); margin-top: var(--space-5); box-shadow: var(--shadow-sm); }
.ptp-weight-readout { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.ptp-weight-value { font-family: var(--font-display); font-size: 40px; color: var(--color-navy); }
.ptp-size-badge { display: inline-flex; padding: 6px 16px; border-radius: var(--radius-pill); font-weight: 700; font-size: 13px; color: var(--color-navy); background: var(--tint-medium); text-transform: uppercase; letter-spacing: .03em; transition: background var(--dur-standard); }
.ptp-slider-track { position: relative; height: 12px; border-radius: var(--radius-pill); background: var(--color-cloud-dim); margin: var(--space-5) 4px; }
/* Invisible enlarged hit-zone: the track's real painted height (12px) is
   well under the spec's own 48px touch-target minimum (§4.7). A ::before
   with no fill is still hit-tested as part of the track, so this widens
   what a tap/click actually has to land on without changing the visual
   pill's thinness — found at 390px via a live measured touch-target audit. */
.ptp-slider-track::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 48px; min-height: 100%; transform: translateY(-50%); }
.ptp-slider-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--radius-pill); background: var(--tint-medium); transition: background var(--dur-standard); }
.ptp-slider-thumb { position: absolute; top: 50%; width: 32px; height: 32px; margin-left: -16px; border-radius: 50%; background: var(--color-white); border: 4px solid var(--color-coral); transform: translateY(-50%); cursor: grab; box-shadow: var(--shadow-sm); touch-action: none; }
/* UX-12: the track's own ::before already widens ITS hit zone to 48px, but
   the thumb itself — the one a user actually has to see and grab, SPEC
   §4.7's 48px touch-target minimum — was still painted at a 32px diameter
   with no matching invisible hit-area of its own. An invisible ::before
   centered on the thumb (48x48, same technique as .ptp-slider-track::before
   above) extends what a tap/click actually has to land on without touching
   the deliberately slim 32px visual design. */
.ptp-slider-thumb::before { content: ""; position: absolute; top: 50%; left: 50%; width: 48px; height: 48px; transform: translate(-50%, -50%); }
.ptp-slider-thumb:active { cursor: grabbing; }
.ptp-slider-thumb:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 3px; }
.ptp-weight-hint { font-size: 15px; font-weight: 600; }

/* ---- Step 3: service-animal radio cards ---- */
.ptp-radio-cards { display: grid; gap: var(--space-3); }
.ptp-radio-card { display: block; background: var(--color-white); border: 2px solid var(--color-navy-10); border-radius: var(--radius-md); padding: var(--space-4); cursor: pointer; }
.ptp-radio-card:hover { border-color: var(--color-sky); }
.ptp-radio-card[aria-checked="true"] { border-color: var(--color-coral); background: var(--color-cloud-dim); }
.ptp-radio-card-title { font-weight: 700; font-size: 16px; margin-bottom: 4px; }
.ptp-radio-card-sub { font-size: 14px; opacity: .8; margin: 0; }
/* UX-31: .ptp-sd-confirm is now a sibling grid item of .ptp-radio-card inside
   .ptp-radio-cards (see app.js renderStep3), not a nested child -- the grid's
   own `gap` provides the spacing above/below it, so no margin-top here. */
.ptp-sd-confirm { display: flex; gap: 10px; align-items: flex-start; font-size: 14px; background: var(--color-cloud-dim); padding: var(--space-3); border-radius: var(--radius-sm); }
.ptp-info-toggle { background: none; border: none; color: var(--color-navy); text-decoration: underline; cursor: pointer; font-size: 14px; padding: var(--space-2) 0; }
.ptp-info-panel { background: var(--color-cloud-dim); border-radius: var(--radius-sm); padding: var(--space-3); font-size: 14px; margin-top: var(--space-2); }
.ptp-info-panel[hidden] { display: none; }

/* ---- Step 4: route/date/pax ---- */
/* Round-7 visual-design CRITICAL/Medium fix: Step 4 was the one step in the
   journey with no Merlot scene at all — a bare white form beside empty
   cloud-blue space, right before the email-gate conversion moment. Mirrors
   the M-05 "Merlot + signpost" illustration already shipped as an asset:
   top banner crop on mobile (order:-1, before the form), background-right
   column on desktop. */
.ptp-route-layout { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
.ptp-route-scene { order: -1; border-radius: var(--radius-lg); overflow: hidden; background: linear-gradient(180deg, var(--color-sky-soft) 0%, var(--color-cloud) 100%); box-shadow: var(--shadow-sm); }
/* round 8 visual-design minor: default center crop sliced straight through
   the OLD M-05's forehead/ear line on the 390px top-banner crop — shifting
   the focal point up (object-position Y closer to 0%) kept the full
   head/ears in frame for that composition.
   round-10 fix (first attempt, superseded below): the round-9
   identity-rebuilt M-05-mobile has a completely different composition
   (signpost occupying roughly the top third, Merlot's face only starting
   around the vertical midpoint) — the old 15% anchor, tuned for the
   previous asset, showed only the blank signpost boards with Merlot
   cropped out of frame entirely at 390px. Moved to 50% — but a pixel-level
   simulation of the ACTUAL object-fit:cover crop window at the real 150px
   height (rendering the shipped M-05-mobile@2x.webp through the exact
   cover-crop math and visually inspecting the result, not just eyeballing
   the full image) showed 50%/150px still only captures Merlot's forehead
   and eyes — the nose, open mouth and smile (the part that actually reads
   as "Merlot", not just a slice of head) were still cropped out. This is
   the owner-override identity note's "fix mobile crops that cut Merlot out"
   item for this exact element.
   round-11 fix: taller crop (210px, was 150px) + focal point moved further
   down (76%, was 50%) — re-simulated the same way and confirmed this keeps
   nose+open-mouth+smile fully in frame with only the very top of the
   signpost visible above. qa/regress/checks/layout.js's
   checkRouteSceneCrop asserts both the height and the Y offset so a future
   change can't silently regress this; re-tune both together (and re-run
   the offline simulation) if this scene's source image composition ever
   changes again. */
.ptp-route-scene img { width: 100%; height: 210px; object-fit: cover; object-position: center 76%; display: block; }
@media (min-width: 1024px) {
  .ptp-route-layout { grid-template-columns: 1fr 280px; align-items: start; gap: var(--space-5); }
  .ptp-route-scene { order: 0; position: sticky; top: var(--space-4); }
  .ptp-route-scene img { height: 380px; }
}
.ptp-date-quickpicks { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin: var(--space-3) 0; }
/* Same opacity-on-navy-text contrast failure as .ptp-fineprint/
   .ptp-hotel-fallback-note (axe color-contrast, serious — navy at 65%
   opacity over white measures ~4.1:1, under WCAG AA's 4.5:1). */
.ptp-date-quickpicks-label { font-size: 13px; color: var(--color-navy-muted); }
.ptp-route-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
@media (max-width: 680px) { .ptp-route-grid { grid-template-columns: 1fr; } }
.ptp-field { position: relative; }
.ptp-field label { display: block; font-size: 13px; font-weight: 700; margin-bottom: 6px; opacity: .75; }
.ptp-field input, .ptp-field select { width: 100%; padding: 14px 16px; font-size: 16px; border-radius: var(--radius-md); border: 1.5px solid var(--color-navy-10); background: var(--color-white); color: var(--color-navy); }
.ptp-field input:focus, .ptp-field select:focus { outline: 3px solid var(--color-navy); outline-offset: 1px; }
/* The concierge modal's "Travel month" field (ptpCMonth) is the one
   remaining native <input type="month"> in the planner — Webkit/Blink let
   us hide its native calendar-icon glyph and draw our own icon-calendar
   symbol (already in assets/icons/ui/ui-sprite.svg) in its place; browsers
   without that pseudo-element (Firefox) keep their own small native icon
   untouched rather than risk a duplicate. Step 4's Depart/Return used to be
   native <input type="date"> styled the same way (VIS-06) — now a real
   custom calendar popover (.ptp-date-trigger button below), which needs no
   such per-browser icon dance since there's no native control to coordinate
   with in any browser. */
.ptp-field-date-wrap { position: relative; }
.ptp-field-date-wrap input[type="month"] { padding-right: 44px; color-scheme: light; width: 100%; }
/* Base rule MUST come before the @supports override below: equal
   specificity + later source order would otherwise always win and the
   icon would never show (caught live — a headless-Chrome screenshot
   diff showed a 0x0 <svg>, not the calendar icon). */
.ptp-field-date-icon { display: none; position: absolute; top: 50%; right: 16px; transform: translateY(-50%); width: 20px; height: 20px; color: var(--color-navy); opacity: .55; pointer-events: none; }
.ptp-date-trigger ~ .ptp-field-date-icon { display: block; }
@supports selector(::-webkit-calendar-picker-indicator) {
  .ptp-field-date-wrap input[type="month"]::-webkit-calendar-picker-indicator { opacity: 0; position: absolute; right: 0; width: 44px; height: 100%; cursor: pointer; margin: 0; }
  .ptp-field-date-icon { display: block; }
}
/* ---- VIS-06: custom calendar date picker (replaces native <input
   type="date"> on Step 4's Depart/Return) ---- */
.ptp-date-trigger { display: block; width: 100%; padding: 14px 44px 14px 16px; font-family: inherit; font-size: 16px; text-align: left; border-radius: var(--radius-md); border: 1.5px solid var(--color-navy-10); background: var(--color-white); color: var(--color-navy); cursor: pointer; }
.ptp-date-trigger:hover { border-color: var(--color-sky); }
.ptp-datepicker-panel { position: absolute; z-index: 20; left: 0; top: calc(100% + 4px); min-width: 280px; background: var(--color-white); border-radius: var(--radius-md); box-shadow: var(--shadow-md); padding: var(--space-3); }
.ptp-datepicker-panel[hidden] { display: none; }
.ptp-datepicker-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); font-weight: 700; }
.ptp-datepicker-nav { width: 32px; height: 32px; border-radius: 50%; border: none; background: var(--color-cloud-dim); color: var(--color-navy); font-size: 16px; cursor: pointer; }
.ptp-datepicker-nav:hover { background: var(--color-sky-soft); }
.ptp-datepicker-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-size: 11px; font-weight: 700; color: var(--color-navy-muted); margin-bottom: 4px; }
.ptp-datepicker-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.ptp-datepicker-day { aspect-ratio: 1; min-width: 36px; min-height: 36px; border: none; border-radius: 50%; background: transparent; color: var(--color-navy); font-size: 14px; cursor: pointer; }
.ptp-datepicker-day:hover:not(:disabled) { background: var(--color-cloud-dim); }
.ptp-datepicker-day:disabled { opacity: .3; cursor: not-allowed; }
.ptp-datepicker-day.is-today { font-weight: 700; box-shadow: inset 0 0 0 1.5px var(--color-sky); }
.ptp-datepicker-day.is-selected { background: var(--color-coral); color: var(--color-white); font-weight: 700; }
.ptp-datepicker-day:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-autocomplete-list { position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px); background: var(--color-white); border-radius: var(--radius-sm); box-shadow: var(--shadow-md); max-height: 260px; overflow-y: auto; list-style: none; margin: 0; padding: 6px; }
.ptp-autocomplete-list[hidden] { display: none; }
.ptp-autocomplete-row { padding: 10px 12px; border-radius: 8px; cursor: pointer; font-size: 14px; display: flex; justify-content: space-between; gap: 8px; }
.ptp-autocomplete-row:hover, .ptp-autocomplete-row[aria-selected="true"], .ptp-autocomplete-row.is-active { background: var(--color-cloud-dim); }
.ptp-autocomplete-row.is-active { outline: 2px solid var(--color-navy); outline-offset: -2px; }
.ptp-autocomplete-list { scroll-margin: 8px; }
.ptp-field-hint { font-size: 13px; margin-top: 6px; opacity: .75; }
.ptp-pax-stepper { display: inline-flex; align-items: center; gap: var(--space-3); background: var(--color-white); border: 1.5px solid var(--color-navy-10); border-radius: var(--radius-pill); padding: 8px 8px 8px 16px; }
.ptp-pax-btn { width: 34px; height: 34px; border-radius: 50%; border: none; background: var(--color-cloud-dim); color: var(--color-navy); font-size: 18px; font-weight: 700; cursor: pointer; }
.ptp-pax-btn:disabled { opacity: .35; cursor: not-allowed; }

/* ---- Step nav controls ---- */
.ptp-step-actions { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-5); flex-wrap: wrap; }
/* 2026-10-22 — button label centring contract (owner: "text sits shifted
   left/right on some buttons"). Root cause, measured at 360-1440: these
   controls centred their label only via inline-flex + align-items, never
   justify-content/text-align, so the label hugged the left edge whenever
   the control was wider than its text (width:100% on mobile, flex-grow) or
   the label wrapped onto two lines (the wrapped anonymous flex item keeps
   text-align:start). One shared rule instead of per-button fixes; no
   `display` here so each control's own show/hide rules stay in charge. */
.ptp-btn, .ptp-hero-cta, .ptp a.ptp-skyscanner-cta, .ptp-concierge-cta, .ptp-chip, .ptp-filter-chip,
.ptp-codeshare-chip, .ptp-x-stop-chip, .ptp-x-filter-toggle, .ptp-x-reset, .ptp-noaccept-btn,
.ptp-pax-btn, .ptp-species-opt, .ptp-search-skip {
  justify-content: center;
  text-align: center;
}
.ptp-btn { font-family: var(--font-body); font-weight: 700; font-size: 16px; border-radius: var(--radius-pill); padding: 14px 26px; border: none; cursor: pointer; transition: transform var(--dur-micro) var(--ease-pop), box-shadow var(--dur-standard); }
.ptp-btn-primary { background: var(--color-coral); color: var(--color-navy); box-shadow: var(--shadow-sm); }
.ptp-btn-primary:hover:not(:disabled) { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.ptp-btn-primary:disabled { background: var(--color-sky-soft); color: var(--color-navy); opacity: .7; cursor: not-allowed; transform: none; }
.ptp-btn-ghost { background: transparent; color: var(--color-navy); text-decoration: underline; padding: 14px 10px; }
.ptp-btn-secondary { background: var(--color-white); color: var(--color-navy); border: 1.5px solid var(--color-navy-10); }
/* round-15 ux-interaction: .ptp-btn-primary:disabled above has always dimmed
   correctly; this sibling class never got the same treatment, so disabled
   stub buttons (#ptpEmailChecklist, #ptpEmailResult) rendered with IDENTICAL
   computed style to a fully-working secondary button beside them -- the only
   cue was a tiny "COMING SOON" pill + a title tooltip that never surfaces on
   mobile taps. Mirrors the primary's muted/opacity/cursor pattern. */
.ptp-btn-secondary:disabled { opacity: .55; cursor: not-allowed; background: var(--color-cloud-dim); }
.ptp-inline-validation { font-size: 14px; color: var(--color-navy); font-weight: 600; display: flex; align-items: center; gap: 6px; }
/* round 8 visual-design: now only ever holds the distinct inline *error*
   copy (app.js validate()) — an empty/whitespace state means there's nothing
   to say here right now (the guide bubble is carrying the line instead), so
   it visually collapses instead of leaving a dead gap in the form rhythm. */
.ptp-inline-validation:empty { display: none; }

/* ---- Email gate modal ---- */
.ptp-modal-overlay { position: fixed; inset: 0; background: rgba(29,58,95,0.45); display: none; align-items: center; justify-content: center; z-index: 100; padding: var(--space-3); }
.ptp-modal-overlay.is-open { display: flex; }
.ptp-modal { background: var(--color-cloud); border-radius: var(--radius-lg); max-width: 440px; width: 100%; padding: var(--space-5); box-shadow: var(--shadow-lg); position: relative; max-height: 90vh; overflow-y: auto; animation: ptp-modal-in var(--dur-standard) var(--ease-standard); }
@keyframes ptp-modal-in { from { opacity: 0; transform: scale(.96) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
.ptp-modal-close { position: absolute; top: 14px; right: 14px; width: 36px; height: 36px; border-radius: 50%; border: none; background: var(--color-white); cursor: pointer; font-size: 18px; }
.ptp-modal-art { width: 88px; height: 88px; border-radius: 50%; object-fit: cover; margin-bottom: var(--space-3); }
.ptp-modal h2 { font-size: 22px; }
.ptp-consent-row { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; margin: var(--space-3) 0; }
.ptp-consent-row input { margin-top: 3px; }
/* 2026-10-22: "I'm not a robot" = one full-row <button role="checkbox">
   (see index.html / app.js ensureAltcha()). States via [data-state]. */
.ptp-altcha { width: 100%; display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 10px 14px; margin: 0 0 var(--space-3); border: 1.5px solid var(--color-navy-10); border-radius: var(--radius-sm); background: var(--color-white); color: var(--color-navy); font: 400 15px/1.3 var(--font-body); text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.ptp-altcha:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-altcha-box { flex: none; width: 26px; height: 26px; box-sizing: border-box; border: 2px solid var(--color-navy); border-radius: 6px; background: var(--color-white); display: inline-flex; align-items: center; justify-content: center; font: 800 16px/1 var(--font-body); }
.ptp-altcha[data-state="solving"] .ptp-altcha-box { border-color: var(--color-navy-10); border-top-color: var(--color-navy); border-radius: 50%; animation: ptp-altcha-spin .8s linear infinite; }
.ptp-altcha[data-state="solved"] .ptp-altcha-box { background: var(--verdict-ok); border-color: var(--verdict-ok); }
.ptp-altcha[data-state="solved"] .ptp-altcha-box::after { content: "\2713"; color: #fff; }
.ptp-altcha[data-state="error"] { border-color: var(--color-coral-text); }
.ptp-altcha[data-state="error"] .ptp-altcha-box { border-color: var(--color-coral-text); }
.ptp-altcha[data-state="error"] .ptp-altcha-box::after { content: "!"; color: var(--color-coral-text); }
.ptp-altcha-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ptp-altcha-name { font-weight: 700; }
.ptp-altcha-state { font-size: 13px; color: var(--color-navy-muted); }
.ptp-altcha[data-state="solved"] .ptp-altcha-state { color: var(--verdict-ok); font-weight: 700; }
.ptp-altcha[data-state="error"] .ptp-altcha-state { color: var(--color-coral-text); font-weight: 700; }
.ptp-altcha.is-pulse .ptp-altcha-box { animation: ptp-altcha-pulse .35s ease; }
@keyframes ptp-altcha-spin { to { transform: rotate(360deg); } }
@keyframes ptp-altcha-pulse { 50% { transform: scale(1.15); } }
@media (prefers-reduced-motion: reduce) {
  .ptp-altcha[data-state="solving"] .ptp-altcha-box { animation-duration: 2.4s; }
  .ptp-altcha.is-pulse .ptp-altcha-box { animation: none; }
}
.ptp-field-error { color: var(--color-coral-text); font-size: 13px; font-weight: 700; margin-top: 4px; }
/* NEW-03 (axe color-contrast, serious): opacity:.65 on navy text measured
   below WCAG AA on both white and cloud backgrounds -- and since CSS
   opacity applies to descendants too, it also dragged the privacy-policy
   <a> inside this exact element (the ACC-03/ACC-13 affiliate-disclosure
   sentence) below contrast along with it. A solid pre-mixed muted color
   (--color-navy-muted, ~5:1 on white/cloud) reads the same "quieter than
   body text" visually without opacity's compounding effect on children. */
.ptp-fineprint { font-size: 12px; color: var(--color-navy-muted); margin-top: var(--space-3); margin-bottom: 0; }

/* ---- Focus ring, global ---- */
/* Navy, not coral — see the identical fix note on .ptp-choice-card above;
   this catch-all rule is the focus indicator for nearly every interactive
   element in the planner, so the coral/cloud contrast failure applied
   almost everywhere keyboard focus landed. */
.ptp :focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }

/* ---- Skyscanner outbound CTA — lives here (critical-path, not the
   deferred planner-steps.css) and with `.ptp a.ptp-skyscanner-cta`
   specificity (0,2,1) so it beats `.ptp a { color: navy }` above (0,1,1)
   unconditionally, regardless of stylesheet load order. Previously this
   rule lived only in planner-steps.css, which is appended to <head> by
   defer-css.js AFTER first paint — on a slow connection (or Lighthouse's
   simulated throttling) a `.ptp-skyscanner-cta` link could render and even
   be screenshotted before that stylesheet arrived, showing invisible
   navy-on-navy text. Moving the ~6-line rule into the always-loaded
   critical-path sheet removes the race entirely, for every instance of
   this class: the per-flight-card CTA, the empty-state "widen search" CTA,
   and the Step 8 hotel CTA all share this one class and are all fixed by
   this one rule. ---- */
.ptp a.ptp-skyscanner-cta { display: inline-flex; align-items: center; gap: 6px; background: var(--color-navy); color: var(--color-white); font-weight: 700; font-size: 14px; padding: 12px 20px; border-radius: var(--radius-pill); text-decoration: none; }
.ptp a.ptp-skyscanner-cta:hover { background: #142c49; }

/* ---- Skeleton (secondary loaders, reduced-motion aware via loaders/skeleton.css) ---- */
.ptp-skeleton { background: var(--color-cloud-dim); border-radius: var(--radius-md); }

/* 2026-10-22 print: Step 7's "Print checklist" prints ONLY the dedicated
   #ptpPrintSheet (built by app.js buildPrintSheet()), never the screen UI.
   The old rule (visibility:hidden on body.ptp * then visible on the print
   area) lost on specificity and printed a blank page in every browser. */
.ptp-print-sheet { display: none; }
@media print {
  .ptp-guide-inline, .ptp-nav, .ptp-hero, .ptp-progress, .ptp-concierge-strip { display: none !important; }
  @page { margin: 14mm 14mm 16mm; }
  body.ptp.ptp-print-checklist { background: #fff !important; }
  body.ptp.ptp-print-checklist > :not(.ptp-print-sheet) { display: none !important; }
  body.ptp.ptp-print-checklist > .ptp-print-sheet { display: block !important; }
  .ptp-print-sheet { color: #111; background: #fff; font: 10.5pt/1.45 var(--font-body); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .ptp-print-sheet h1 { font: 400 19pt/1.2 var(--font-display); margin: 2pt 0 8pt; color: #1D3A5F; }
  .ptp-print-sheet h2 { font: 700 12pt/1.3 var(--font-body); margin: 12pt 0 5pt; padding-bottom: 3pt; border-bottom: 1px solid #c9d3df; color: #1D3A5F; break-after: avoid; }
  .ptp-ps-brand { font: 400 13pt/1 var(--font-display); color: #1D3A5F; margin: 0; }
  .ptp-ps-brand span { font: 600 9pt var(--font-body); color: #4A5E77; }
  .ptp-ps-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 4pt 16pt; margin: 0 0 8pt; padding: 8pt 10pt; border: 1px solid #c9d3df; border-radius: 6pt; }
  .ptp-ps-meta div { display: flex; gap: 6pt; }
  .ptp-ps-meta dt { font-weight: 700; min-width: 62pt; white-space: nowrap; }
  .ptp-ps-meta dd { margin: 0; }
  .ptp-ps-summary { margin: 0 0 4pt; }
  .ptp-ps-list { list-style: none; margin: 0; padding: 0; }
  .ptp-ps-item { display: flex; gap: 9pt; align-items: flex-start; padding: 5pt 0; border-bottom: 1px dotted #c9d3df; break-inside: avoid; }
  .ptp-ps-item p { margin: 2pt 0 0; }
  .ptp-ps-box { flex: none; width: 11pt; height: 11pt; margin-top: 2pt; border: 1.3pt solid #1D3A5F; border-radius: 2pt; display: inline-flex; align-items: center; justify-content: center; font: 800 9pt/1 var(--font-body); }
  .ptp-ps-src, .ptp-ps-sources span { color: #4A5E77; font-size: 8.5pt; word-break: break-all; }
  .ptp-ps-facts { margin: 0; padding-left: 14pt; }
  .ptp-ps-facts li { margin: 0 0 4pt; break-inside: avoid; }
  .ptp-ps-sources { margin: 0; padding-left: 16pt; }
  .ptp-ps-sources li { margin: 0 0 4pt; break-inside: avoid; }
  .ptp-ps-foot { margin-top: 12pt; font-size: 8.5pt; color: #4A5E77; }
}

/* ---- Utility classes (replace any would-be inline style="" — CSP hash-
   allowlist has no exception for dynamically-templated inline styles, see
   audit/AUDIT.md's CSP trap note) ---- */
.ptp-hp-hide { position: absolute; left: -9999px; }
.ptp-u-block { width: 100%; }
.ptp-u-text-sm { font-size: 13px; }
.ptp-u-text-xs { font-size: 12px; }
.ptp-u-muted { opacity: .7; }
.ptp-u-mt-2 { margin-top: var(--space-2); }
.ptp-u-mt-3 { margin-top: var(--space-3); }
.ptp-u-mt-4 { margin-top: var(--space-4); }
.ptp-u-weight-700 { font-weight: 700; }
.ptp-u-inline-flex { display: flex; align-items: center; gap: var(--space-2); }
.ptp-u-label { display: block; font-size: 13px; font-weight: 700; opacity: .75; margin: var(--space-2) 0 6px; }
.ptp-u-input { width: 100%; padding: 10px 12px; border-radius: var(--radius-sm); border: 1.5px solid var(--color-navy-10); background: var(--color-white); color: var(--color-navy); font-family: var(--font-body); }
.ptp-u-hidden { display: none; }
.ptp-u-btn-inline { padding: 0; background: none; border: none; text-decoration: underline; cursor: pointer; color: var(--color-navy); font-size: 13px; }

.ptp-tint-toy    { background: var(--tint-toy); }
.ptp-tint-small  { background: var(--tint-small); }
.ptp-tint-medium { background: var(--tint-medium); }
.ptp-tint-large  { background: var(--tint-large); }
.ptp-tint-giant  { background: var(--tint-giant); }

/* =====================================================================
   FIX-2 (2026-10-21): 3-screen flow — species toggle + breed portraits,
   weight + role on one screen, progress dots.
   ===================================================================== */
.ptp-progress-dots { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; align-items: center; }
.ptp-progress-dots li { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--color-navy-muted); }
.ptp-progress-dots li + li::before { content: ""; width: 18px; height: 2px; background: var(--color-navy-10); border-radius: 2px; }
.ptp-progress-dot { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: var(--color-cloud-dim); color: var(--color-navy); font-size: 12px; font-weight: 800; }
.ptp-progress-dots li.is-current .ptp-progress-dot { background: var(--color-coral); color: var(--color-navy); }
.ptp-progress-dots li.is-current { color: var(--color-navy); }
.ptp-progress-dots li.is-done .ptp-progress-dot { background: var(--color-navy); color: var(--color-white); }
@media (max-width: 480px) { .ptp-progress-lbl { display: none; } .ptp-progress-dots li.is-current .ptp-progress-lbl { display: inline; } }

.ptp-species-toggle { display: inline-grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); background: var(--color-white); padding: 6px; border-radius: var(--radius-pill); box-shadow: var(--shadow-sm); margin-bottom: var(--space-3); }
.ptp-species-opt { display: inline-flex; align-items: center; gap: 8px; min-height: 48px; padding: 4px 14px; border: 2px solid transparent; border-radius: var(--radius-pill); background: transparent; color: var(--color-navy); font: 700 16px/1 var(--font-body); cursor: pointer; }
.ptp-species-opt img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: var(--color-sky-soft); }
.ptp-species-opt[aria-checked="true"] { background: var(--color-navy); color: var(--color-white); }
.ptp-species-opt:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-breed-instruction { font-size: 14px; color: var(--color-navy-muted); margin: var(--space-2) 0 var(--space-3); }
.ptp-breed-grid--portraits { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); max-height: 560px; }
.ptp-breed-portrait { background: linear-gradient(180deg, #CFEAFB, #F4FAFF); }
.ptp-breed-grid--portraits .ptp-breed-tile { min-height: 44px; cursor: pointer; }
@media (max-width: 680px) {
  .ptp-breed-grid--portraits { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); max-height: none; overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .ptp-breed-grid--portraits .ptp-breed-avatar { width: 76px; height: 76px; }
}

.ptp-weight-label { font-weight: 800; font-size: 15px; }
.ptp-weight-input-wrap { display: inline-flex; align-items: center; gap: 6px; }
.ptp-weight-input { width: 88px; min-height: 48px; text-align: center; font: 400 28px/1 var(--font-display); color: var(--color-navy); border: 1.5px solid var(--color-navy-10); border-radius: var(--radius-sm); background: var(--color-cloud); }
.ptp-weight-input:focus { outline: 3px solid var(--color-navy); outline-offset: 1px; }
.ptp-weight-unit { font-weight: 700; }
.ptp-weight-step { width: 44px; height: 44px; border-radius: 50%; border: none; background: var(--color-cloud-dim); color: var(--color-navy); font-size: 22px; font-weight: 800; cursor: pointer; }
.ptp-weight-step:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-weight-readout { flex-wrap: wrap; }
.ptp-weight-prefill-note { font-size: 13px; color: var(--color-navy-muted); margin: var(--space-2) 0 0; }
.ptp-step .ptp-weight-panel { margin-top: 0; padding: var(--space-4); }
.ptp-role-q { font-family: var(--font-display); font-size: 22px; margin: var(--space-4) 0 var(--space-3); color: var(--color-navy); }
.ptp-radio-cards--compact .ptp-radio-card { padding: 14px 18px; cursor: pointer; }
.ptp-radio-cards--compact .ptp-radio-card-sub { font-size: 13px; }

/* FIX-2b (owner): travel type = 3 picture cards, minimal words, auto-advance. */
.ptp-travel-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.ptp-travel-card { display: flex; flex-direction: column; align-items: stretch; padding: 0 0 12px; overflow: hidden; text-align: center; cursor: pointer; min-height: 44px; }
.ptp-travel-card img { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: contain; background: linear-gradient(180deg, #CFEAFB, #F4FAFF); }
.ptp-travel-card .ptp-radio-card-title { margin: 10px 8px 2px; font-size: 16px; line-height: 1.2; }
.ptp-travel-card .ptp-radio-card-sub { margin: 0 8px; font-size: 13px; line-height: 1.3; }
.ptp-travel-card:focus-visible { outline: 3px solid var(--color-navy); outline-offset: 2px; }
.ptp-sd-confirm--compact { margin-top: var(--space-3); padding: 10px 14px; font-weight: 700; align-items: center; }
.ptp-sd-confirm--compact input { width: 24px; height: 24px; flex: none; }
.ptp-info-toggle--small { font-size: 13px; text-decoration: underline; margin-top: var(--space-2); min-height: 44px; }
.ptp-info-sheet { list-style: none; margin: 0 0 var(--space-3); padding: 12px 16px; font-size: 13px; line-height: 1.45; display: grid; gap: 4px; }
.ptp-info-sheet[hidden] { display: none; }
.ptp-weight-label-sub { display: block; font-size: 12px; font-weight: 600; color: var(--color-navy-muted); }
.ptp-step .ptp-weight-panel .ptp-weight-hint { font-size: 14px; margin: 8px 0 0; }
.ptp-step .ptp-weight-panel .ptp-weight-readout { margin-bottom: var(--space-3); }
@media (max-width: 680px) {
  .ptp-step .ptp-weight-panel { padding: 14px 16px; border-radius: var(--radius-md); }
  .ptp-step .ptp-weight-panel .ptp-weight-readout { gap: 10px; margin-bottom: 12px; }
  .ptp-weight-input { width: 72px; min-height: 44px; font-size: 24px; }
  #step2 .ptp-step-head h2 { font-size: 24px; }
  #step2 .ptp-role-q { font-size: 19px; margin: 14px 0 10px; }
  .ptp-travel-cards { gap: 8px; }
  .ptp-travel-card { padding-bottom: 8px; border-radius: var(--radius-md); }
  .ptp-travel-card .ptp-radio-card-title { font-size: 14px; margin: 6px 4px 2px; }
  .ptp-travel-card .ptp-radio-card-sub { font-size: 11.5px; margin: 0 4px; }
}
@media (max-width: 680px) {
  /* dots already name the screen; counter stays as the aria-live announcer */
  .ptp-progress .ptp-step-counter { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
  .ptp-progress { margin-bottom: 10px; gap: 8px; }
  .ptp-step .ptp-weight-panel .ptp-weight-readout { flex-wrap: nowrap; }
  .ptp-weight-label-sub { display: none; }
  .ptp-weight-step { width: 40px; height: 40px; font-size: 20px; }
  .ptp-weight-input { width: 60px; }
  .ptp-step .ptp-weight-panel .ptp-size-badge { padding: 5px 10px; font-size: 11px; }
  .ptp-step .ptp-weight-panel .ptp-weight-hint { font-size: 13px; margin-top: 6px; }
  #step2 .ptp-step-head { margin-bottom: 10px; }
  #step2 .ptp-step-head h2 { font-size: 22px; line-height: 1.2; }
}
.ptp-sd-confirm[hidden] { display: none !important; }
@media (max-width: 400px) { .ptp-step .ptp-weight-panel .ptp-size-badge { display: none; } }

/* =====================================================================
   FIX-2W (2026-10-21, wide framing; owner feedback after FIX-1): "it should have been a
   wider angle, from further away" — Merlot's head looked huge. Rule now:
   show (almost) the full frame, head ~25-35% of the visible frame height,
   face centred and fully visible. Close-up sources (M-01, M-05, M-09,
   M-10, M-12, M-13) got outpainted *-wide@{1x,2x}.webp siblings (Nano
   Banana zoom-out around the ORIGINAL pixels, which are kept identical in
   the centre; originals untouched). M-16 is never altered.
   Supersedes FIX-1's tight crops (removed).
   ===================================================================== */

/* Landing hero (M-16 mobile): 4:3 window = middle half of the portrait
   art -> head ~26% of the frame, whole sitting Merlot visible. */
@media (max-width: 860px) {
  .ptp-hero-art { aspect-ratio: 4/3; }
  .ptp-hero-art img { object-position: 53% 70%; }
}

/* Step 4 scene: now M-05-wide (both breakpoints), a light cover crop. */
.ptp-route-scene img { object-position: 50% 20%; }
@media (max-width: 1023.98px) { .ptp-route-scene img { height: 240px; } }

/* Guide avatar: plain cover crop of a WIDE frame (no zoom). Mobile
   circle grows 48 -> 56px so a ~30% head is still readable. Mobile
   sources are 2:3 (*-mobile) except M-01/M-05/M-13 which use the 3:2
   *-wide frame at both breakpoints (see GUIDE_WIDE in app.js); desktop
   sources are 3:2. Focal x/y below = Merlot's face in each source. */
@media (max-width: 1023.98px) {
  .ptp-guide-portrait img { width: 56px; height: 56px; }
  #ptpGuideImg[data-scene="M-01"] { object-position: 74% 50%; }
  #ptpGuideImg[data-scene="M-03"] { object-position: 50% 0%; }
  #ptpGuideImg[data-scene="M-04"] { object-position: 50% 2%; }
  #ptpGuideImg[data-scene="M-05"] { object-position: 67% 50%; }
  #ptpGuideImg[data-scene="M-06"] { object-position: 50% 62%; }
  #ptpGuideImg[data-scene="M-08"] { object-position: 50% 4%; }
  #ptpGuideImg[data-scene="M-13"] { object-position: 74% 50%; }
  #ptpGuideImg[data-scene="M-14"] { object-position: 50% 12%; }
}
@media (min-width: 1024px) {
  #ptpGuideImg[data-scene="M-01"] { object-position: 74% 50%; }
  #ptpGuideImg[data-scene="M-03"] { object-position: 86% 50%; }
  #ptpGuideImg[data-scene="M-04"] { object-position: 20% 50%; }
  #ptpGuideImg[data-scene="M-05"] { object-position: 67% 50%; }
  #ptpGuideImg[data-scene="M-06"] { object-position: 38% 50%; }
  #ptpGuideImg[data-scene="M-08"] { object-position: 56% 50%; }
  #ptpGuideImg[data-scene="M-13"] { object-position: 74% 50%; }
  #ptpGuideImg[data-scene="M-14"] { object-position: 92% 50%; }
}

/* Email-gate art (88px circle): sizes="264px" in index.html -> natural
   264x176 CSS px; object-fit:none shows head + body, head ~30%. */
.ptp-modal-art { object-fit: none; object-position: 50% 30%; }

/* FIX-2c: keep the 3 travel-type picture cards compact on desktop. */
@media (min-width: 681px) {
  .ptp-travel-cards { grid-template-columns: repeat(3, minmax(0, 200px)); }
  .ptp-travel-card .ptp-radio-card-title { font-size: 17px; }
}
