/* ══════════════════════════════════════════════════════════
   SCOOP Client Portal — login page only. Self-contained, same isolation
   reasoning as css/portal.css (client-portal/ has no dependency on the
   internal app's assets/css/theme.css, and this page in turn has no
   dependency on css/portal.css — dashboard.html keeps using that file
   unchanged). Every rule is scoped under body.lp-page and every class is
   "lp-" prefixed so this stylesheet can't bleed onto another page even if
   it were mistakenly linked there.

   Fonts: Material Symbols (icons, same convention as css/portal.css) +
   Space Grotesk (display headline) + IBM Plex Mono (labels/eyebrow/utility
   text — the "HUD" half of the type system) — none of these are loaded
   anywhere else in the repo, so they're pulled in fresh here rather than
   assumed available.
   ══════════════════════════════════════════════════════════ */
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&family=Space+Grotesk:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

body.lp-page {
  /* Matches css/portal.css's --cp-bg (light theme) so this page and
     dashboard.html read as the same product instead of two different
     shades of "white". */
  --lp-bg:        #f4f5f7;
  --lp-surface:   #ffffff;
  --lp-text:      #0A0D12;
  --lp-text-sub:  #5B6472;
  --lp-border:    rgba(10,13,18,0.10);
  --lp-accent:    #981E32;
  --lp-accent-600: #7A1828;
  --lp-accent-wash: rgba(152,30,50,0.08);
  --lp-teal:      #4FE6C7;
  --lp-danger:    #D9403F;
  --lp-shadow:    0 24px 60px rgba(10,13,18,0.09), 0 2px 8px rgba(10,13,18,0.05);
  --lp-radius:    18px;
  --lp-font-display: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
  --lp-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --lp-font-mono: 'IBM Plex Mono', ui-monospace, monospace;

  margin: 0;
  background: var(--lp-bg);
  color: var(--lp-text);
  font-family: var(--lp-font-body);
  -webkit-font-smoothing: antialiased;
}
body.lp-page * { box-sizing: border-box; }
body.lp-page a { color: inherit; }

body.lp-page :focus-visible {
  outline: 2px solid var(--lp-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.lp-wrap {
  max-width: 1180px;
  margin: 0 auto;
  padding: 72px 24px 0;
}

/* ── Blur-in stagger entrance — title/subtitle/card each carry their own
   --lp-delay custom property (set inline in the HTML) so one keyframe rule
   drives all three at different offsets instead of three near-duplicate
   animation rules. ── */
.lp-blur-in {
  opacity: 0;
  animation: lpBlurIn 0.7s cubic-bezier(0.16,1,0.3,1) both;
  animation-delay: var(--lp-delay, 0s);
}
@keyframes lpBlurIn {
  from { opacity: 0; filter: blur(10px); transform: translateY(16px); }
  to   { opacity: 1; filter: blur(0);    transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .lp-blur-in { animation: none; opacity: 1; filter: none; transform: none; }
}

/* ── 1. Title ─────────────────────────────────────────── */
.lp-hero { text-align: center; max-width: 760px; margin: 0 auto; }
.lp-hero__title {
  margin: 0;
  font-family: var(--lp-font-display);
  font-weight: 700;
  font-size: clamp(32px, 4.6vw, 54px);
  line-height: 1.16;
  letter-spacing: -0.02em;
}
.lp-accent { color: var(--lp-accent); }
.lp-hero__sub {
  margin: 14px 0 0;
  font-size: 16px;
  color: var(--lp-text-sub);
}

/* ── 2. Login fields — deliberately not a bordered/shadowed "card" (see
   login.html's header comment): just the fields/button sitting directly on
   the page background. ── */
.lp-card-section {
  display: flex; justify-content: center;
  margin: 48px 0 0;
}
.lp-card {
  width: 100%; max-width: 400px;
  padding: 8px 4px;
}

.lp-field { margin-bottom: 16px; }
.lp-field label {
  display: block; margin-bottom: 7px;
  font-family: var(--lp-font-mono);
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--lp-text-sub);
}
.lp-field-wrap { position: relative; display: flex; align-items: center; }
.lp-field-icon {
  position: absolute; left: 14px;
  color: var(--lp-text-sub); font-size: 18px;
  pointer-events: none; z-index: 1;
}
.lp-field-wrap:focus-within .lp-field-icon { color: var(--lp-accent); }
.lp-field-input {
  width: 100%; height: 46px;
  padding: 0 14px 0 44px;
  border-radius: 10px;
  border: 1px solid var(--lp-border);
  background: #fff;
  color: var(--lp-text);
  font-size: 15px;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.lp-field-input:focus { border-color: var(--lp-accent); box-shadow: 0 0 0 3px var(--lp-accent-wash); }
.lp-has-toggle .lp-field-input { padding-right: 42px; }
.lp-pw-toggle {
  position: absolute; right: 12px;
  background: none; border: none; cursor: pointer;
  color: var(--lp-text-sub); padding: 4px;
  display: flex; align-items: center; justify-content: center;
  z-index: 1;
}
.lp-pw-toggle:hover { color: var(--lp-text); }
.lp-pw-toggle .material-symbols-outlined { font-size: 18px; }

.lp-login-msg {
  min-height: 18px; margin: 12px 0 0;
  font-size: 13px; color: var(--lp-danger);
}

.lp-login-btn {
  width: 100%; height: 48px; margin-top: 8px;
  border: none; border-radius: 10px; cursor: pointer;
  background: var(--lp-accent); color: #fff;
  font-family: var(--lp-font-body);
  font-size: 15px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: background 0.2s, transform 0.2s;
}
.lp-login-btn:hover { background: var(--lp-accent-600); transform: translateY(-1px); }
.lp-login-btn:disabled { opacity: 0.6; cursor: default; transform: none; }
/* Hidden by default until the username field passes checkEmailDomain() —
   see js/login.js. */
.lp-login-btn-hidden { display: none; }
.lp-btn-arrow {
  width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255,255,255,0.22);
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; transition: transform 0.2s;
}
.lp-login-btn:hover .lp-btn-arrow { transform: translateX(3px); }

/* ── 3. Trusted-by strip — single-row infinite marquee, same DOM
   group+duplicate-group technique as the internal app's own logo walls
   (mask-fade at the edges, animation-duration-based loop). The internal
   app's own version forces every logo to solid white via
   brightness(0) invert(1) (needed there since it sits over a dark photo
   backdrop) — this page inverts that same trick to solid dark/black via
   brightness(0) alone, since a plain grayscale(1) doesn't touch lightness
   and left several of these PNGs' own near-white artwork just as invisible
   against this white background as it would've been unfiltered. ── */
.lp-trusted { margin: 72px 0 0; }
.lp-trusted__label {
  text-align: center; margin: 0 0 20px;
  font-family: var(--lp-font-mono);
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--lp-text-sub);
}
.lp-trusted__wall {
  overflow: hidden;
  /* Vertical breathing room so a tile scaled up on hover isn't clipped by
     this same overflow:hidden (which still needs to stay on, to keep the
     horizontal mask-fade below working). */
  padding: 14px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
/* Deliberately never paused (not even on hover/focus, unlike the internal
   app's own logo walls) — the loop stays infinite regardless of what the
   cursor is doing; only the individual tile being hovered reacts, via the
   scale below. */
.lp-trusted__track { display: flex; width: max-content; animation: lpTrustedScroll 70s linear infinite; }
@keyframes lpTrustedScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.lp-trusted__group { display: flex; align-items: center; flex-shrink: 0; }
.lp-trusted__tile {
  height: 40px; width: auto; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  margin-right: 48px;
  position: relative; z-index: 0;
  transition: transform 0.25s, z-index 0.25s;
}
/* z-index lift so the enlarged tile draws over its neighbors instead of
   being clipped/overlapped by them — plain CSS, so this works identically
   on touch (tap-and-hold triggers :hover in most mobile browsers) and
   desktop, no JS involved. */
.lp-trusted__tile:hover,
.lp-trusted__tile:focus-within {
  transform: scale(1.35);
  z-index: 1;
}
.lp-trusted__tile img {
  height: 100%; width: auto; max-width: 160px;
  object-fit: contain; display: block;
  filter: brightness(0); opacity: 0.6;
  transition: filter 0.25s, opacity 0.25s;
}
.lp-trusted__tile:hover img { filter: none; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .lp-trusted__track { animation: none; }
}

/* ── 4. Animated scroll gallery ───────────────────────────
   .lp-scroll-region (tall) > .lp-gallery-sticky (pinned, 100vh, clips the
   tilted/scaled content) > .lp-gallery (the 3-column grid that actually
   gets tilted/scaled) > .lp-gcol (each gets its own parallax translateY).
   All scroll-driven values are computed in js/login.js; the CSS below only
   supplies the *initial* transform (matching p=0 exactly) so there's no
   flash of an untransformed gallery before the first rAF tick, plus the
   static per-column layout (grid, aspect-ratio, masonry offset). ── */
.lp-scroll-region {
  position: relative;
  height: 320vh;
  margin-top: 96px;
}
.lp-gallery-sticky {
  position: sticky; top: 0;
  height: 100vh;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  /* A larger perspective than the "usual" ~800-1000px keeps the tilt
     reading as a clearly-visible grid immediately on load instead of an
     almost-edge-on sliver — 1000px made the far (top) rows compress into
     something close to invisible before any scrolling happened. */
  perspective: 2400px;
  perspective-origin: center top;
  background: var(--lp-bg);
}
.lp-gallery {
  /* Narrower than the sticky viewport on purpose (not 100%) — at scale(1.08)
     (see below) a 100%-wide grid would grow past the viewport edge and get
     sliced by .lp-gallery-sticky's overflow:hidden, cropping the outer
     columns' cards instead of showing them whole. 88% leaves enough margin
     that the scaled-up grid still lands fully inside the viewport. */
  width: min(88%, 1040px);
  margin: 0 auto;
  padding: 0 24px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  transform-style: preserve-3d;
  transform-origin: center top;
  will-change: transform;
  /* Matches p=0 exactly (rotateX 50deg, scale 1.08) — see the gallery spec
     comment in js/login.js for the full p-to-transform mapping. Scale
     starts more modestly than the task spec's literal 1.2 (down to 1.08)
     specifically to reduce upscale artifacting on top of the perspective
     transform — a real, reproducible source of the "pixelated" look on
     actual GPU-accelerated browsers. rotateX also starts more modestly than
     the task spec's literal 75deg (down to 50deg): rotateX only rotates
     around the X axis, so it compresses an image's screen-projected height
     while leaving its width untouched — the steeper the angle, the more
     every card reads as artificially squashed/stretched-wide regardless of
     its true aspect ratio, which is exactly what a lower starting angle
     avoids while still keeping a clearly-tilted "receding" look. */
  transform: rotateX(50deg) scale(1.08);
}
.lp-gcol {
  display: flex; flex-direction: column; gap: 20px;
  will-change: transform;
}
/* Masonry feel: the "odd" columns (2nd, and 4th once the 5-column wide
   layout below reveals it) start higher than the others so their images
   interlock instead of lining up in flat rows — a static counterpart to
   those same columns' own parallax range (see colRanges in js/login.js),
   not itself scroll-driven. */
.lp-gcol:nth-child(2) { margin-top: clamp(-120px, -9vw, -40px); }

/* Only 3 columns render below the wide breakpoint — the 4th/5th column
   slots exist in the DOM (js/login.js only ever populates them once the
   breakpoint is actually crossed) but stay hidden and empty otherwise. */
.lp-gcol:nth-child(4),
.lp-gcol:nth-child(5) { display: none; }

/* ── Wide-screen gallery: 5 columns × 4 rows (20 cards) instead of 3 × 4
   (12) — js/login.js's buildCards() reads the same 1400px breakpoint via
   matchMedia() to know how many card slots to actually create/populate.
   Widens .lp-wrap itself (title/card/trusted-by come along with it, not
   just the gallery) rather than the usual "100vw + negative margin"
   full-bleed trick — that trick measures against the viewport including
   its scrollbar, which is reliably ~15-17px wider than the actual visible
   area once a vertical scrollbar is showing (this page always has one, it's
   taller than any screen), so it left a permanent few-pixel horizontal
   scrollbar. Widening the existing centered container has no such gotcha. */
@media (min-width: 1400px) {
  .lp-wrap { max-width: 1700px; }
  .lp-gallery {
    width: min(94%, 1600px);
    grid-template-columns: repeat(5, 1fr);
  }
  .lp-gcol:nth-child(4),
  .lp-gcol:nth-child(5) { display: flex; }
  .lp-gcol:nth-child(4) { margin-top: clamp(-120px, -9vw, -40px); }
}

/* No overflow:hidden here (deliberately) — a clipped descendant of a
   transform-style:preserve-3d ancestor (.lp-gallery, constantly re-rotated
   inline by js/login.js) is a known Chromium/GPU-compositing trap: on some
   GPU/driver combinations the clipped content simply never composites and
   the item renders blank once the parent's rotation moves through certain
   angles — reproducible in real browsers even though it can look fine in a
   software-rendered headless check. Rounding + shadow live on the *image*
   itself instead, which needs no clipping ancestor to achieve the same look. */
.lp-gitem {
  background: #eee;
}
.lp-gitem img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(10,13,18,0.10);
  /* Round-robin cycling (js/login.js) fades opacity to 0, swaps src, then
     fades back to 1 once the new image has loaded — this is what makes
     that swap read as a smooth crossfade-style transition instead of an
     instant pop. */
  transition: opacity 0.4s ease;
}

/* A real button (click → scroll to top + focus the username field, see
   js/login.js) rather than a passive label, so it needs the usual
   appearance/font/cursor resets on top of its own look. Brand-colored
   (solid accent fill + white text) instead of the neutral glass pill the
   task spec originally called for, matching the same red/white the login
   button itself uses — this cue is a real CTA now, not a passive hint. */
.lp-scroll-cue {
  position: absolute; left: 50%; bottom: 28px; z-index: 1;
  transform: translateX(-50%);
  margin: 0; padding: 10px 20px;
  -webkit-appearance: none; appearance: none;
  font-family: var(--lp-font-mono);
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: #fff;
  background: var(--lp-accent);
  border: 1px solid var(--lp-accent);
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(152,30,50,0.28);
  animation: lpScrollCueBob 2s ease-in-out infinite;
  transition: background 0.2s, transform 0.2s, box-shadow 0.2s;
}
.lp-scroll-cue:hover {
  background: var(--lp-accent-600); border-color: var(--lp-accent-600);
  box-shadow: 0 10px 26px rgba(152,30,50,0.36);
}
@keyframes lpScrollCueBob {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(6px); }
}

/* prefers-reduced-motion: no tilt/parallax, no pin — the region collapses
   to a plain static grid of images at their natural flow height. The scroll
   cue stays visible (it's a real "jump back to the form" shortcut now, not
   just a decorative hint about the scroll-driven effect below) but drops
   the bob animation. js/login.js also skips attaching the scroll listener
   in this case, so it never fights these rules. */
@media (prefers-reduced-motion: reduce) {
  .lp-scroll-region { height: auto; margin-bottom: 64px; }
  .lp-gallery-sticky {
    position: static; height: auto; overflow: visible; perspective: none;
    padding: 32px 0;
  }
  .lp-gallery { transform: none !important; }
  .lp-gcol { transform: none !important; }
  .lp-gcol:nth-child(2), .lp-gcol:nth-child(4) { margin-top: 0; }
  /* Flow-positioned instead of absolute/bottom-pinned — .lp-gallery-sticky
     is no longer a fixed 100vh box to pin against above, so it just follows
     the flattened grid in normal document flow instead. */
  .lp-scroll-cue {
    position: static; display: block; width: fit-content;
    margin: 24px auto 0; transform: none; animation: none;
  }
}

/* ── Responsive ───────────────────────────────────────── */
@media (max-width: 720px) {
  .lp-wrap { padding: 48px 18px 0; }
  .lp-trusted { margin-top: 56px; }
  .lp-gallery { gap: 12px; padding: 0 16px; }
  .lp-gcol { gap: 12px; }
  .lp-gcol:nth-child(2) { margin-top: clamp(-70px, -18vw, -30px); }
  .lp-scroll-region { height: 260vh; margin-top: 64px; }
}
@media (max-width: 480px) {
  .lp-hero__sub { font-size: 14px; }
  .lp-trusted__tile { height: 32px; margin-right: 34px; }
}
