/* ============================================================
   ATLAS — PlayXplore homepage visual world
   Layered paper-cut depth on a dark ground. The hero is a
   coverage map: a route traced across PlayXplore's real
   markets, cut from stacked planes of paper.
============================================================ */

:root {
  /* Paper-cut strata, back to front. Each layer sits a step
     lighter than the one behind it and catches a light edge. */
  --bg-0:   #070A0C;
  --lyr-1:  #0C1216;
  --lyr-2:  #15202A;
  --lyr-3:  #20303C;
  --lyr-4:  #2C4050;
  --lyr-5:  #3A5262;
  --edge:   rgba(255,255,255,0.17);
  --edge-soft: rgba(255,255,255,0.06);

  /* Brand, sampled from the PlayXplore logo mark */
  --orange:      #FF6A00;
  --orange-lift: #FF8B33;
  --orange-soft: rgba(255,106,0,0.14);
  --orange-line: rgba(255,106,0,0.42);
  --teal:        #00C9B1;
  --teal-lift:   #4FE0CE;
  --teal-soft:   rgba(0,201,177,0.14);
  --teal-line:   rgba(0,201,177,0.42);

  --tx:     #F2EFE9;
  --tx2:    #A8B0B8;
  --tx3:    #8A939B;
  --line:   rgba(255,255,255,0.09);
  --line-strong: rgba(255,255,255,0.16);

  --font-display: 'Space Grotesk', sans-serif;
  --font-body:    'Inter', sans-serif;
  --font-mono:    'Inter', sans-serif;
  --radius: 4px;
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body.atlas {
  background: var(--bg-0);
  color: var(--tx);
  font-family: var(--font-body);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
.atlas img { display: block; max-width: 100%; height: auto; }
.atlas a { color: inherit; text-decoration: none; }
.atlas h1, .atlas h2, .atlas h3, .atlas h4 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-weight: 700;
  text-wrap: balance;
}
.atlas p { text-wrap: pretty; }
.atlas .mono { font-family: var(--font-mono); }

.px-wrap { position: relative; z-index: 2; max-width: 1180px; margin: 0 auto; padding: 0 2rem; }
@media (max-width: 640px) { .px-wrap { padding: 0 1.25rem; } }

/* ============================================================
   NAV
============================================================ */
.px-nav {
  position: sticky; top: 0; z-index: 400;
  background: rgba(7,10,12,0.86);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.9rem 2rem;
}
.px-nav-mark { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; color: var(--tx); display: flex; align-items: center; }
.px-nav-mark span { color: var(--orange); }
.px-nav .px-nav-logo { height: 28px; width: auto; display: block; }
.px-nav-links { display: none; align-items: center; gap: 1.75rem; }
@media (min-width: 880px) { .px-nav-links { display: flex; } }
.px-nav-link {
  font-family: var(--font-body); font-weight: 500; font-size: 13px; letter-spacing: 0.01em;
  color: var(--tx2); border-bottom: 1px solid transparent; padding-bottom: 2px;
  transition: color .18s, border-color .18s;
}
.px-nav-link:hover, .px-nav-link.active { color: var(--orange); border-color: var(--orange); }
.px-nav-right { display: flex; align-items: center; gap: 0.75rem; flex-shrink: 0; }
.px-btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-weight: 600; font-size: 14px;
  letter-spacing: 0;
  color: #12160F; background: var(--orange);
  padding: 11px 20px; border: none; border-radius: var(--radius);
  transition: background .2s, transform .18s var(--ease-out-expo), box-shadow .2s;
  box-shadow: 0 6px 20px rgba(255,106,0,0.28);
  cursor: pointer;
}
.px-btn:hover { background: var(--orange-lift); transform: translateY(-2px); box-shadow: 0 10px 28px rgba(255,106,0,0.36); }
.px-nav-right .px-btn { display: none; }
@media (min-width: 880px) { .px-nav-right .px-btn { display: inline-flex; } }
.px-ham { display: flex; flex-direction: column; gap: 4px; cursor: pointer; padding: 4px; }
@media (min-width: 880px) { .px-ham { display: none; } }
.px-ham span { width: 20px; height: 2px; background: var(--tx); }
#px-mob-menu { display: none; position: absolute; top: 100%; left: 0; right: 0; background: var(--lyr-1); border-bottom: 1px solid var(--line); z-index: 399; }
#px-mob-menu.open { display: block; }
.px-mob-item { display: block; padding: 1rem 2rem; font-family: var(--font-body); font-weight: 500; font-size: 15px; color: var(--tx); border-top: 1px solid var(--line); }

/* ============================================================
   HERO — a filmed cinematic paper-cut aircraft crossing a real
   skyline, not a diagram of one. The video carries the atmosphere
   and the motion; the ten-market coverage story lives in a
   compact data card rather than a second on-screen plane, since
   the footage already has its own.
============================================================ */
.px-hero {
  position: relative; overflow: hidden;
  min-height: 100vh;
  display: flex; align-items: flex-end;
  background: var(--bg-0);
}
.px-hero-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.px-hero-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 55% 48%; }

/* Grain: extremely subtle, breaks up the video's own compression
   banding rather than reading as an effect in its own right. */
.px-l-grain {
  position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.035; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Scrim: pulls the footage back under the text column so the
   headline never has to compete with the skyline behind it. */
.px-hero::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(95deg, var(--bg-0) 0%, var(--bg-0) 30%, rgba(7,10,12,0.86) 42%, rgba(7,10,12,0.26) 60%, transparent 78%),
              linear-gradient(0deg, rgba(7,10,12,0.65) 0%, transparent 16%),
              linear-gradient(180deg, rgba(7,10,12,0.5) 0%, transparent 22%);
}

/* ── Coverage card: the ten real markets, as a departure board
   rather than a second plane duplicating the one already in the
   footage. Each cell "flips" like a split-flap board — the whole
   cell rotates edge-on, swaps text at the midpoint, then rotates
   back, which reads as a mechanical flap without needing a true
   per-character flap mechanism. ── */
.px-coverage {
  position: absolute; right: 2rem; bottom: 6.5rem; z-index: 3; width: 300px;
  background: rgba(7,10,12,0.68); border: 1px solid var(--line-strong); border-radius: var(--radius);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  padding: 1.1rem 1.2rem 0.9rem;
}
.px-coverage-head {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-body); font-weight: 500; font-size: 11.5px; color: var(--tx2); letter-spacing: 0.05em;
  text-transform: uppercase; margin-bottom: 0.7rem;
}

.px-board { font-family: ui-monospace, 'SF Mono', 'JetBrains Mono', 'Courier New', monospace; }
.px-board-head, .px-board-row {
  display: grid; grid-template-columns: 46px 1fr 66px; column-gap: 6px;
  padding: 4px 0; border-bottom: 1px solid var(--line);
}
.px-board-head {
  font-size: 8.5px; letter-spacing: 0.08em; color: var(--tx3); font-weight: 400;
  border-bottom-color: var(--line-strong);
}
.px-board-row { font-size: 10.5px; letter-spacing: 0.02em; color: var(--tx2); align-items: center; perspective: 260px; }
.px-board-row:last-child { border-bottom: none; }
.px-board-row .px-flap-code { color: var(--tx3); }
.px-board-row .px-flap-dest { color: var(--tx); }
.px-board-row .px-flap-status { color: var(--teal); text-align: right; }
.px-flap {
  display: block; transform-style: preserve-3d; backface-visibility: hidden;
  transition: transform .15s cubic-bezier(0.5,0,0.5,1);
}
.px-flap.flipping { transform: rotateX(-90deg); }

@media (max-width: 900px) {
  .px-coverage { position: static; width: 100%; margin-top: 2rem; }
}

/* Left-anchored, not centered: .px-wrap centers content for normal
   sections, which is right for readability below the fold. In the
   hero that fights the scrim, whose dark zone is a fixed percentage
   of the viewport from the left edge — on a wide monitor, centered
   text drifts right while the scrim stays put, leaving a dead black
   gap between them. Overriding the inherited margin keeps the text
   at a constant offset from the left edge at any window width, which
   is what the scrim's percentages actually assume. */
.px-hero-inner { position: relative; z-index: 3; margin-left: 0; padding-top: 2.5rem; padding-bottom: 5.5rem; }
.px-hero-meta {
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-size: 11px; color: var(--tx3); letter-spacing: 0.08em;
  margin-bottom: 1.75rem; text-transform: uppercase;
}
.px-live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--teal); box-shadow: 0 0 8px rgba(0,201,177,0.8); flex-shrink: 0; animation: pxPulse 2.4s ease-in-out infinite; }
@keyframes pxPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
.px-headline {
  font-size: clamp(2.5rem, 6vw, 5rem);
  line-height: 1.02;
  margin-bottom: 1.5rem;
  max-width: 620px;
}
.px-headline .accent { color: var(--orange); }
.px-sub { font-size: 1.06rem; color: var(--tx2); max-width: 490px; margin-bottom: 2.25rem; line-height: 1.7; }
.px-cta-row { display: flex; flex-wrap: wrap; align-items: center; gap: 1.5rem; }
.px-btn-lg { font-size: 14px; padding: 15px 30px; }
.px-link {
  font-family: var(--font-mono); font-size: 13px; color: var(--tx);
  border-bottom: 1.5px solid var(--line-strong); padding-bottom: 3px;
  transition: color .18s, border-color .18s;
}
.px-link:hover { color: var(--teal); border-color: var(--teal); }

/* Compass: an abstract motion cue, not a redraw of the logo —
   eight thin radiating lines and a ring, low-opacity, slowly
   turning in the negative space behind the route. */
.px-compass {
  position: absolute; top: -8%; right: 4%; width: 420px; height: 420px;
  z-index: 1; pointer-events: none; opacity: 0.16;
  animation: pxCompassSpin 140s linear infinite;
}
.px-compass circle { fill: none; stroke: var(--tx2); stroke-width: 1; }
.px-compass line { stroke: var(--tx2); stroke-width: 1; }
.px-compass .px-compass-accent { stroke: var(--orange); }
@keyframes pxCompassSpin { to { transform: rotate(360deg); } }
@media (max-width: 900px) { .px-compass { width: 280px; height: 280px; top: 2%; right: -6%; opacity: 0.12; } }

/* Scroll indicator: a quiet line-and-chevron, not a bounce loop. */
.px-scroll-cue {
  position: absolute; left: 2rem; bottom: 1.75rem; z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  animation: pxScrollFade 3.2s ease-in-out infinite;
}
.px-scroll-cue .px-scroll-line { width: 1px; height: 34px; background: linear-gradient(var(--line-strong), transparent); }
.px-scroll-cue svg { width: 10px; height: 6px; opacity: 0.6; }
@keyframes pxScrollFade { 0%,100% { opacity: 0.55; } 50% { opacity: 1; } }
@media (max-width: 560px) { .px-scroll-cue { display: none; } }

/* Motion toggle: subtle, always reachable, never the visual focus. */
.px-motion-toggle {
  position: absolute; right: 1.75rem; bottom: 1.75rem; z-index: 3;
  width: 38px; height: 38px; border-radius: 50%;
  background: rgba(7,10,12,0.55); border: 1px solid var(--line-strong);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--tx2); display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: color .18s, border-color .18s;
}
.px-motion-toggle:hover, .px-motion-toggle:focus-visible { color: var(--tx); border-color: var(--line-strong); }
.px-motion-toggle svg { width: 14px; height: 14px; }
.px-motion-toggle .px-icon-play { display: none; }
.px-hero.motion-paused .px-motion-toggle .px-icon-pause { display: none; }
.px-hero.motion-paused .px-motion-toggle .px-icon-play { display: block; }

/* Paused: every continuous CSS loop freezes in place; the video
   itself and the two rAF loops are paused directly by JS via the
   same button, since a CSS class alone can't pause a <video>. */
.px-hero.motion-paused .px-live-dot,
.px-hero.motion-paused .px-compass,
.px-hero.motion-paused .px-scroll-cue { animation-play-state: paused; }

/* Below the two-column threshold the footage becomes a fixed
   picture window above the copy, rather than a full-bleed
   backdrop behind it — the coverage card also drops out of its
   corner position and becomes a normal block in the text flow. */
@media (max-width: 900px) {
  .px-hero { display: block; min-height: 0; }
  .px-hero-media { bottom: auto; height: 58vh; }
  .px-hero::after { bottom: auto; height: 58vh; background: linear-gradient(180deg, transparent 0%, rgba(7,10,12,0.5) 60%, var(--bg-0) 92%); }
  .px-hero-inner { padding-top: calc(58vh + 2rem); padding-bottom: 3.5rem; }
  /* The stacked hero (video + text + board) is much taller than
     the viewport here, so pinning these to the hero's own bottom
     edge — correct on desktop, where the video fills the whole
     hero — would land them on top of the board instead of the
     video. Anchor them to the bottom of the video window itself. */
  .px-scroll-cue, .px-motion-toggle { bottom: auto; top: calc(58vh - 3.25rem); }
}
@media (max-width: 600px) {
  .px-hero-media { height: 50vh; }
  .px-hero::after { height: 50vh; }
  .px-hero-inner { padding-top: calc(50vh + 1.5rem); }
  .px-scroll-cue, .px-motion-toggle { top: calc(50vh - 3.25rem); }
}
@media (prefers-reduced-motion: reduce) {
  .px-live-dot, .px-compass, .px-scroll-cue { animation: none; }
}

/* ============================================================
   SECTION SHELL — each section is its own cut plane
============================================================ */
.px-sec { position: relative; z-index: 2; padding: 5rem 0; }
.px-sec-1 { background: var(--lyr-1); }
.px-sec-2 { background: var(--lyr-2); }
.px-sec-0 { background: var(--bg-0); }
/* The lit top edge of a cut plane */
.px-sec-1, .px-sec-2, .px-sec-0 { border-top: 1px solid var(--edge-soft); }

.px-sec-head { margin-bottom: 2.5rem; }
.px-sec-title { font-size: clamp(1.7rem, 3.4vw, 2.5rem); }
.px-sec-title .accent { color: var(--orange); }
.px-sec-sub { font-size: 0.98rem; color: var(--tx2); max-width: 52ch; margin-top: 0.75rem; line-height: 1.7; }

/* ── tension statement ── */
/* ============================================================
   KNOW BEFORE YOU GROW — a single statement, no paragraphs, no
   cards. The line pulls into focus once it enters view: soft,
   blurred, desaturated to sharp — the same visual event as the
   emotional beat it lands (clarity), not decoration on top of it.
============================================================ */
.px-know {
  position: relative; z-index: 2; overflow: hidden;
  min-height: 78vh; display: flex; align-items: center;
  background: var(--bg-0); border-top: 1px solid var(--edge-soft);
}
.px-know-glow {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(480px circle at var(--gx, 50%) var(--gy, 40%), rgba(255,106,0,0.14), transparent 70%);
  opacity: 0; transition: opacity .4s ease;
}
/* JS-driven rather than CSS :hover — :hover matching proved
   unreliable for this full-bleed section during testing (the
   pointer-tracking mousemove listener fired correctly while the
   :hover pseudo-class didn't reliably engage), so mouseenter/
   mouseleave on .px-know toggle this class directly instead. */
.px-know.glow-active .px-know-glow { opacity: 1; }
.px-know-line {
  position: relative; z-index: 1;
  font-size: clamp(2.5rem, 8vw, 7rem); line-height: 1.04; letter-spacing: -0.025em; font-weight: 700;
}
.px-know-line .accent { color: var(--orange); }
/* Ten markets, one pre-made graphic instead of ten hand-positioned
   CSS spans — the span approach needed three rounds of breakpoint
   fixes (collisions turned up at ~800px, ~1024px, and even 1440px
   alone wasn't representative of "desktop") before it was safe at
   every width. A single image sidesteps all of that: it's one box
   that scales as a unit, so nothing inside it can newly collide at
   a width that wasn't tested. `cover` + `center` crops the edges
   at extreme aspect ratios rather than leaving letterbox gaps —
   acceptable here since every label sits well clear of the image's
   own edges (verified against the actual asset, not assumed). */
.px-know-map {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background-image: url('/images/hero/coordinate-background.jpg');
  background-size: cover; background-position: center;
  opacity: 0.6;
}
@media (max-width: 640px) {
  .px-know { min-height: 58vh; }
  /* On a tall narrow viewport, `cover` scales the image to exactly
     match the container's height and crops only left/right — so a
     vertical background-position shift does nothing here (there's
     no vertical crop margin left to move within, only horizontal).
     What actually needed to move was X: the default centered crop
     window includes Bangkok's marker (~40% across the source
     image), which then sits right behind the headline (also
     centered). Shifting the window rightward crops Bangkok out of
     frame instead of through the text. The opacity cut is the
     actual safety net regardless of crop position — whatever ends
     up near the headline should read as background either way. */
  .px-know-map { background-position: 78% center; opacity: 0.3; }
}

/* Focus-pull entrance: visible by default — CSS never hides this
   line. JS only opts it into the pre-reveal (blurred/soft) state
   once it commits to animating it in, same guard pattern used on
   every other animated element on this page. */
[data-focus-reveal] { opacity: 1; filter: none; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  [data-focus-reveal].js-anim {
    opacity: 0; filter: blur(24px) saturate(0.3); transform: scale(1.06);
    transition: opacity .7s var(--ease-out-expo), filter .7s var(--ease-out-expo), transform .7s var(--ease-out-expo);
  }
  [data-focus-reveal].js-anim.in { opacity: 1; filter: blur(0) saturate(1); transform: scale(1); }
}

/* ── capabilities: an unequal, borderless, hairline-divided grid.
   No card chrome — structure comes only from rules between cells,
   never around them. One accent color (the numeral, orange), one
   cell (Market Intelligence) carrying more visual weight than the
   rest since it's the page's actual thesis, not just item one of
   six. Hover behaves as focus, not highlight: the whole grid dims
   except the cell under the cursor, so attention moves by
   subtraction rather than decoration. ── */
.px-caps {
  display: grid; grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}
.px-cap {
  padding: 2.75rem 2.25rem;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transition: opacity .35s ease;
}
.px-cap:nth-child(3n) { border-right: none; }
.px-cap:nth-child(4), .px-cap:nth-child(5), .px-cap:nth-child(6) { border-bottom: none; }
/* JS-driven (mouseenter/mouseleave), not CSS :hover — ancestor
   :hover bubbling proved unreliable in testing (individual-cell
   :hover matched correctly, but the parent .px-caps:hover the dim
   rule depends on didn't reliably engage), the same failure mode
   already hit and fixed on the "Know before you grow" glow.

   The [data-reveal] attribute is included in these selectors not
   for meaning but for specificity: [data-reveal].js-anim.in (the
   scroll-reveal system every .px-cap also carries) resolves to the
   same specificity as a plain three-class selector here, and being
   later in the file it would silently win the cascade and cancel
   the dim entirely — confirmed by testing, not a guess. */
@media (hover: hover) {
  .px-caps.is-focusing .px-cap[data-reveal] { opacity: 0.42; }
  .px-caps.is-focusing .px-cap[data-reveal].is-focused { opacity: 1; }
}
.px-cap-n {
  font-family: var(--font-mono); font-weight: 400;
  font-size: clamp(2.75rem, 4vw, 4rem); color: var(--orange);
  line-height: 1; letter-spacing: -0.01em; margin-bottom: 1.1rem;
}
.px-cap-lead .px-cap-n { font-size: clamp(3.5rem, 5.6vw, 5.5rem); }
.px-cap-h { font-family: var(--font-display); font-weight: 700; font-size: 1.18rem; margin-bottom: 0.6rem; }
.px-cap-p { font-size: 0.92rem; color: var(--tx2); line-height: 1.65; max-width: 32ch; }

/* Reveal order reads rather than renders: a hand-tuned, slightly
   uneven stagger rather than a metronomic per-cell interval. */
.px-caps .px-cap:nth-child(1) { transition-delay: 0s; }
.px-caps .px-cap:nth-child(2) { transition-delay: .09s; }
.px-caps .px-cap:nth-child(3) { transition-delay: .15s; }
.px-caps .px-cap:nth-child(4) { transition-delay: .22s; }
.px-caps .px-cap:nth-child(5) { transition-delay: .27s; }
.px-caps .px-cap:nth-child(6) { transition-delay: .34s; }

@media (max-width: 760px) {
  .px-caps { grid-template-columns: 1fr; }
  .px-cap { border-right: none !important; border-bottom: 1px solid var(--line) !important; padding: 2.25rem 0; }
  .px-caps .px-cap:last-child { border-bottom: none !important; }
  .px-cap-lead .px-cap-n { font-size: clamp(3rem, 12vw, 4rem); }
}

/* ── gallery: real activation photography, editorial masonry.
   CSS multi-column (not CSS grid) gives genuine masonry for free —
   each item keeps its native aspect ratio and the column simply
   flows to the next shortest, which is what produces the
   asymmetrical rhythm rather than a forced crop grid.
   column-span:all on .px-gitem-wide is the one full-bleed break. ── */
.px-gallery { columns: 3; column-gap: 1.1rem; margin-top: 0.5rem; }
.px-gitem {
  display: block; position: relative; break-inside: avoid;
  margin: 0 0 1.1rem; border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--line-strong); background: var(--lyr-2);
}
.px-gitem-wide { column-span: all; }
.px-gitem img {
  display: block; width: 100%; height: auto;
  transition: transform 1s cubic-bezier(.16,.8,.24,1);
}
/* A portrait photo sitting at full native height in an otherwise
   landscape-heavy masonry reads as an outlier, not an intentional
   asymmetry — square-crop it instead. Desktop only: this is placed
   before the mobile media query so mobile's own aspect-ratio/
   object-fit rules below win on cascade order at that width. */
.px-gitem-portrait img { aspect-ratio: 1 / 1; object-fit: cover; }
.px-gcap {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  padding: 1.1rem 1.1rem 0.65rem;
  background: linear-gradient(to top, rgba(7,10,12,0.94) 0%, rgba(7,10,12,0.6) 55%, transparent 100%);
  display: flex; flex-direction: column; gap: 0.15rem;
}
.px-gcountry { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--orange); }
.px-gcat { font-family: var(--font-display); font-weight: 700; font-size: 1rem; color: #fff; }
.px-gind { font-size: 0.82rem; color: var(--tx2); }

/* Caption stays visible at rest — the showcase page's whole point is
   scannability, so a title you only see after hovering was working
   against the page rather than for it. Zoom-on-hover is still a
   desktop-only flourish since there's no hover state to trigger it
   on touch. */
@media (hover: hover) {
  .px-gitem:hover img { transform: scale(1.045); }
}
@media (max-width: 900px) { .px-gallery { columns: 2; } }
/* Mobile stacks in one column, so masonry's asymmetry has nothing
   left to do — mixed portrait/landscape/square natives just look
   uneven rather than editorial. A single uniform box (4:5) reads as
   neat and deliberate instead. But a hard object-fit:cover crop on
   that box was cutting people out of wide group/landscape photos —
   so the box stays uniform, and the photo itself now shows in full
   (object-fit:contain, letterboxed) over a blurred, scaled-up copy
   of the same image filling the rest of the box. The blurred copy
   is injected by JS as `.px-gbackdrop` — see atlas.js. */
@media (max-width: 640px) {
  .px-gallery { columns: 1; }
  .px-gitem-wide { column-span: initial; }
  .px-gitem img {
    position: relative; z-index: 1;
    aspect-ratio: 4 / 5; object-fit: contain; background: transparent;
  }
  .px-gbackdrop {
    position: absolute; inset: 0; z-index: 0;
    background-size: cover; background-position: center;
    filter: blur(28px) brightness(0.55);
    transform: scale(1.2); /* hides the blur's own soft edge from view */
  }
  /* Anything that isn't landscape (portrait or square) pillar/
     letterboxes under contain — bars down the sides or top and
     bottom, which reads as broken rather than deliberate at this
     box shape. Cropping one mostly costs headroom, legroom, or a
     sliver of background, not the people in frame, so cover is the
     better trade specifically for these — landscape group shots
     keep contain, since that's the crop that was cutting people
     out in the first place. */
  .px-gitem-fill img { object-fit: cover; }
}

/* ── expanding cards: homepage's curated 7-photo teaser (the full
   17-photo set lives on /showcase/, which keeps the masonry grid
   above — different job, different pattern). One card wide, the
   rest collapsed to slivers; hover/focus/click swaps which one is
   wide. flex-grow is what's animated — cheap, no layout thrash,
   and (unlike width/flex-basis in %) doesn't need a media query to
   recompute anything when the container resizes. ── */
.px-xcards { display: flex; gap: 0.6rem; height: 460px; margin-top: 0.5rem; }
.px-xcard {
  position: relative; flex: 1 1 0%; min-width: 0;
  border: none; padding: 0; margin: 0; cursor: pointer;
  border-radius: var(--radius); overflow: hidden;
  background: var(--lyr-2);
  transition: flex-grow .65s cubic-bezier(.16,.8,.24,1);
}
.px-xcard.is-active { flex-grow: 7; }
.px-xcard img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; pointer-events: none;
}
.px-xcard::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(7,10,12,0.92) 0%, rgba(7,10,12,0.32) 45%, rgba(7,10,12,0.12) 100%);
}
.px-xcard-label {
  position: absolute; left: 1rem; bottom: 1.1rem; z-index: 2;
  font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: #fff;
  writing-mode: vertical-rl; transform: rotate(180deg);
  white-space: nowrap; opacity: 1; transition: opacity .3s ease;
}
.px-xcard.is-active .px-xcard-label { opacity: 0; }
.px-xcard-content {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  padding: 1.25rem 1.4rem;
  display: flex; flex-direction: column; gap: 0.2rem; text-align: left;
  opacity: 0; transform: translateY(10px);
  transition: opacity .4s ease .08s, transform .4s ease .08s;
}
.px-xcard.is-active .px-xcard-content { opacity: 1; transform: translateY(0); }
.px-xcard-content .px-gcountry { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--orange); }
.px-xcard-content .px-gcat { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: #fff; }
.px-xcard-content .px-gind { font-size: 0.82rem; color: var(--tx2); }

@media (max-width: 760px) {
  .px-xcards { flex-direction: column; height: 600px; gap: 0.5rem; }
  .px-xcard:not(.is-active) { min-height: 44px; }
  .px-xcard-label { writing-mode: horizontal-tb; transform: none; left: 1.1rem; bottom: 0.7rem; }
}
@media (prefers-reduced-motion: reduce) {
  .px-xcard, .px-xcard-label, .px-xcard-content { transition: none; }
}

/* ── showcase: category filter + clickable gallery + lightbox.
   Reuses .px-gallery/.px-gitem/.px-gcap as-is — only what's new
   for this page is the filter bar, the click affordance, and the
   lightbox itself. ── */
.px-sfilter { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.75rem; }
.px-fbtn {
  font-family: var(--font-body); font-weight: 500; font-size: 12.5px;
  padding: 8px 16px; border-radius: 100px; border: 1px solid var(--line-strong);
  background: transparent; color: var(--tx2); cursor: pointer;
  transition: border-color .18s ease, color .18s ease, background .18s ease;
}
.px-fbtn:hover { color: var(--tx); border-color: var(--tx2); }
.px-fbtn.active { background: var(--orange); border-color: var(--orange); color: #12160F; }

#px-showcase-gallery.is-filtering .px-gitem { transition: opacity .22s ease; }
#px-showcase-gallery .px-gitem.is-out { opacity: 0; }

.px-gitem[data-cat] { cursor: zoom-in; }
.px-gitem[data-cat]:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

/* `.px-gextra` never renders in the grid — it's a plain data
   container the lightbox JS reads a cluster's other photos and
   write-up from. The "N photos" count is exposed to screen readers
   via each cluster's aria-label instead of a visible badge. */
.px-gextra { display: none; }

.px-lightbox {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(4,6,8,0.94);
  display: flex; align-items: center; justify-content: center; padding: 4rem 1.5rem;
  opacity: 0; visibility: hidden; transition: opacity .28s ease;
}
.px-lightbox.is-open { opacity: 1; visibility: visible; }
.px-lightbox figure { position: relative; max-width: min(1100px, 92vw); max-height: 88vh; display: flex; flex-direction: column; gap: 0.6rem; }

/* One photo: strip just holds a single centered image, no scroll
   needed. Multiple: a scroll-snap strip — swipe on touch, drag or
   the prev/next buttons on desktop, no carousel library required. */
.px-lightbox-strip {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  gap: 0; border-radius: var(--radius); max-height: 74vh;
  scrollbar-width: none;
}
.px-lightbox-strip::-webkit-scrollbar { display: none; }
.px-lightbox-strip img {
  flex: 0 0 100%; scroll-snap-align: center;
  max-height: 74vh; width: 100%; object-fit: contain;
}
.px-lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 40px; height: 40px; border-radius: 100px;
  border: 1px solid var(--line-strong); background: rgba(7,10,12,0.7); color: var(--tx);
  font-size: 22px; line-height: 1; cursor: pointer;
}
.px-lightbox-nav:hover { border-color: var(--tx2); }
.px-lightbox-prev { left: -1rem; }
.px-lightbox-next { right: -1rem; }
@media (max-width: 640px) { .px-lightbox-nav { display: none; } }
.px-lightbox-count {
  position: absolute; top: 0.75rem; left: 50%; transform: translateX(-50%); z-index: 2;
  font-size: 10.5px; color: var(--tx2); background: rgba(7,10,12,0.7);
  border: 1px solid var(--line-strong); border-radius: 100px; padding: 4px 10px;
}
.px-lightbox figcaption { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; justify-content: center; }
.px-lightbox-write { font-size: 0.92rem; color: var(--tx2); line-height: 1.6; text-align: center; max-width: 62ch; margin: 0 auto; }
.px-lightbox-close {
  position: absolute; top: 1.5rem; right: 1.75rem; z-index: 3;
  width: 40px; height: 40px; border-radius: 100px;
  border: 1px solid var(--line-strong); background: rgba(7,10,12,0.7); color: var(--tx);
  font-size: 20px; line-height: 1; cursor: pointer;
}
.px-lightbox-close:hover { border-color: var(--tx2); }
@media (prefers-reduced-motion: reduce) { .px-lightbox, .px-lightbox-strip { transition: none; scroll-behavior: auto; } }

/* ── live signal: a bonus freshness layer, not load-bearing. Hidden
   by default via the HTML `hidden` attribute; JS only reveals it on
   a successful pull from the same feed the Intelligence page runs
   on. If that call fails, this stays hidden and the data band below
   — evergreen, properly cited — still stands on its own. ── */
.px-signal {
  display: flex; align-items: baseline; gap: 0.65rem; flex-wrap: wrap;
  padding: 0.9rem 1.1rem; margin-bottom: 1.75rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--lyr-1);
  perspective: 300px;
}
.px-signal-label { font-weight: 600; font-size: 10.5px; letter-spacing: 0.08em; color: var(--teal); text-transform: uppercase; flex-shrink: 0; }
/* A scoped flip, not a reuse of the departure board's .px-flap —
   same rotateX-and-swap mechanic, kept independent so nothing here
   can regress the board, which is already tested and working. */
.px-signal-text {
  display: inline-block; font-size: 0.92rem; color: var(--tx2);
  transform-style: preserve-3d; backface-visibility: hidden;
  transition: transform .18s cubic-bezier(0.5,0,0.5,1);
}
.px-signal-text.flipping { transform: rotateX(-90deg); }

/* ── data band ── */
.px-data { border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--lyr-2); padding: 2.25rem; }
.px-data-big { font-size: clamp(3.2rem, 8vw, 5.5rem); font-weight: 700; color: var(--orange); letter-spacing: -0.03em; line-height: 1; margin-bottom: 0.75rem; }
.px-data-cap { font-size: 0.98rem; color: var(--tx2); max-width: 52ch; line-height: 1.7; }
.px-data-rows { margin-top: 1.75rem; border-top: 1px solid var(--line); }
.px-data-row { display: flex; justify-content: space-between; gap: 1rem; padding: 0.8rem 0; border-bottom: 1px solid var(--line); font-size: 0.9rem; color: var(--tx2); }
.px-data-row span:last-child { font-family: var(--font-mono); color: var(--teal); }

/* ── banner ── */
.px-banner { border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--lyr-2); padding: 2rem 2.25rem; display: flex; justify-content: space-between; align-items: center; gap: 1.75rem; flex-wrap: wrap; }
.px-banner-h { font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; margin-bottom: 0.4rem; }
.px-banner-p { font-size: 0.9rem; color: var(--tx2); max-width: 44ch; }

/* ── clients ── */
.px-clients { display: flex; flex-wrap: wrap; align-items: center; gap: 3rem; }
.px-clients img { height: 30px; width: auto; filter: grayscale(1) brightness(2.4); opacity: 0.72; transition: opacity .2s; }
.px-clients img:hover { opacity: 1; }

/* ── final ── */
.px-final { text-align: center; padding: 6rem 0; }
.px-final-h { font-size: clamp(2.1rem, 5vw, 3.4rem); margin-bottom: 1.25rem; }
.px-final-p { font-size: 1.02rem; color: var(--tx2); max-width: 52ch; margin: 0 auto 2.5rem; }
.px-final-ctas { display: flex; justify-content: center; gap: 1.5rem; flex-wrap: wrap; align-items: center; }

/* ============================================================
   FOOTER
============================================================ */
.px-footer { border-top: 1px solid var(--line); padding: 3.5rem 0 2rem; background: var(--bg-0); position: relative; z-index: 2; }
.px-footer-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 2.5rem; margin-bottom: 2.5rem; }
@media (max-width: 780px) { .px-footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .px-footer-grid { grid-template-columns: 1fr; } }
.px-footer-mark { font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; margin-bottom: 0.85rem; }
.px-footer-desc { font-size: 0.88rem; color: var(--tx2); line-height: 1.65; max-width: 34ch; }
.px-footer-h5 { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.09em; color: var(--tx3); margin-bottom: 1rem; }
.px-footer-links { display: flex; flex-direction: column; gap: 0.6rem; }
.px-footer-link { font-size: 0.88rem; color: var(--tx2); transition: color .18s; }
.px-footer-link:hover { color: var(--orange); }
.px-footer-bottom { border-top: 1px solid var(--line); padding-top: 1.5rem; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.75rem; font-family: var(--font-mono); font-size: 10.5px; color: var(--tx3); }

/* ── scroll reveal: visible by default, JS opts in ── */
[data-reveal] { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  [data-reveal].js-anim { opacity: 0; transform: translateY(16px); transition: opacity .7s var(--ease-out-expo), transform .7s var(--ease-out-expo); }
  [data-reveal].js-anim.in { opacity: 1; transform: translateY(0); }
}

.px-top {
  position: fixed; bottom: 1.5rem; right: 1.5rem; width: 40px; height: 40px;
  background: var(--lyr-3); border: 1px solid var(--line-strong); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center; color: var(--tx2);
  font-family: var(--font-mono); cursor: pointer; z-index: 300;
  opacity: 0; transform: translateY(8px); transition: opacity .2s, transform .2s, color .2s, border-color .2s;
}
.px-top.show { opacity: 1; transform: translateY(0); }
.px-top:hover { color: var(--orange); border-color: var(--orange-line); }
