/* ============================================================
   WORK CHAPTERS — scroll-scrubbed, one project per full-screen scene.
   Each .chapter is tall (250vh); .chapter-sticky pins for that scroll
   distance while js/chapters.js drives transforms off scroll progress.
   ============================================================ */

#chapters {
  position: relative;
  background: #000;
}

.chapter {
  position: relative;
  height: 260vh;
}

.chapter-sticky {
  position: sticky;
  top: 0;
  height: 100vh; /* fallback for browsers without svh support */
  height: 100svh;
  overflow: hidden;
  display: flex;
  align-items: center;
}

.chapter-bg {
  position: absolute;
  inset: -10%;
  z-index: 1;
  will-change: transform;
}

.chapter-visual {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, var(--tint-a, #1a1512), var(--tint-b, #0a0908));
  display: flex;
  align-items: center;
  /* flex-end, not center: chapter-content occupies the left ~55% of the
     viewport, so a centered .mock card would sit half-hidden behind the
     text column. Real <img> screenshots ignore this (fill via width/height
     100%, not flex sizing), so this only affects the mockup cards. */
  justify-content: flex-end;
  /* No padding-right here — it used to live on this element, but <img>
     children use width:100% which resolves against the *content* box
     (post-padding), so padding-right was silently shrinking every real
     screenshot too, not just offsetting the mock cards as intended. The
     right-side offset now lives on .mock itself (margin-right, below),
     which only affects the mock cards — <img> always fills 100% of
     .chapter-visual regardless of justify-content or margins on a sibling. */
}

@media (max-width: 900px) {
  .chapter-visual {
    justify-content: center;
  }
}
.chapter-visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  /* 6px → 3px (2026-08-02): the two real screenshots that needed heavy blur
     for privacy (casino-games.jpg, client-sites.jpg) had that sensitive text
     physically cropped out of the source files instead — blur is no longer
     the only thing standing between a visitor and that text, just a mild
     "don't compete with foreground copy" softening now, so it didn't need to
     stay this strong. 6px on a near-full-viewport image looked muddy. */
  filter: blur(3px) saturate(0.85);
  transform: scale(1.02); /* hides the slight edge softening blur() causes */
}
/* Real screenshots here are wide (~2:1) crops. object-fit:cover forced into
   a viewport that's taller than it is wide (aspect-ratio, not a fixed
   width — this covers phones AND tablet portrait, e.g. a 768x1024 iPad,
   which a plain max-width:760px breakpoint missed entirely) blows the
   image up so only a sliver of its width is ever visible. contain shows
   the whole image letterboxed over the tint gradient instead. */
@media (max-aspect-ratio: 1/1) {
  .chapter-visual img {
    object-fit: contain;
    /* height:100% (base rule) stretches the <img>'s own box to the full
       ~120vh-tall .chapter-bg overhang box, and no amount of
       object-position can land a 190-310px-tall contain-fit image inside
       the real safe window — measured at ~45px between where mobile
       chapter-content's text ends and the letterbox-bottom bar begins
       during a chapter's held/pinned phase. Capping the box itself to a
       size that actually fits the safe window, then aligning it with
       flexbox (below) the same way .mock is aligned, is the only way to
       land it somewhere real. */
    height: auto;
    max-height: 24vh;
    align-self: flex-end;
    margin-bottom: 20vh;
    object-position: center;
    /* The shared/desktop blur (base rule above, originally 6px, now 3px —
       see that rule's comment) was tuned for a full-viewport image sitting
       far from a narrow left-hand text column. Here the image is confined
       to a small ~24vh box sitting close to the text, so even the current
       lighter desktop value would read as mushier than needed at this
       scale. 2px keeps just enough softening to stop it competing
       letter-for-letter with real UI text baked into the screenshot, while
       staying recognizable. Checked against the
       existing mobile .chapter-textscrim (bottom of this file, unchanged
       at flat 0.6) via axe-core color-contrast after this change — still
       0 violations, so no extra darkening turned out to be needed. */
    filter: blur(2px) saturate(0.85);
  }
}

/* Constant (not scroll-animated) left-side darkening so chapter-content text
   stays legible over busy real screenshots even while .chapter-scrim (below)
   is in its "lifted"/reveal state mid-scroll. */
.chapter-textscrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    rgba(6, 6, 5, 0.97) 0%,
    rgba(6, 6, 5, 0.92) 45%,
    rgba(6, 6, 5, 0.55) 68%,
    rgba(6, 6, 5, 0.1) 88%
  );
}
/* The 0-88% stops above assume text occupies a narrow left column, which only
   holds once .chapter-content hits its max-width:1120px cap and recenters
   with room to spare on the right. Below that (viewport < 1120px, box-sizing
   border-box means the cap engages exactly at 1120px, not 1120+padding),
   .chapter-content has no room budget and stretches close to full width —
   chapter-desc/chapter-title's own max-width (620/780px) then determines the
   right edge in *absolute px*, which lands anywhere from ~73% to ~95% of the
   viewport depending on exact width in this band (worst around 800-900px,
   where the text wraps to nearly fill the available column). The 88%
   falloff above leaves that whole zone almost transparent. Below 761px the
   760px breakpoint's flat 0.6 fill (further down this file) already covers
   it; this fills the gap between there and the 1120px cap with a gradient
   that stays dark far further right, instead of tuning per-width (the danger
   zone moves with viewport width, a single gradient can't chase it exactly,
   so this errs toward staying dark too long rather than fading too early). */
@media (min-width: 761px) and (max-width: 1119px) {
  .chapter-textscrim {
    background: linear-gradient(
      90deg,
      rgba(6, 6, 5, 0.97) 0%,
      rgba(6, 6, 5, 0.94) 60%,
      rgba(6, 6, 5, 0.8) 92%,
      rgba(6, 6, 5, 0.45) 100%
    );
  }
}

.chapter-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.75) 0%,
    rgba(0, 0, 0, 0.35) 40%,
    rgba(0, 0, 0, 0.75) 100%
  );
  will-change: opacity;
}

.chapter-number {
  position: absolute;
  top: 50%;
  right: 4%;
  transform: translateY(-50%);
  font-family: var(--font-display);
  font-size: min(42vw, 520px);
  font-weight: 600;
  color: rgba(243, 237, 226, 0.05);
  line-height: 1;
  z-index: 1;
  pointer-events: none;
  user-select: none;
}

.chapter-content {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 40px;
  will-change: transform, opacity;
}

.chapter-eyebrow {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 20px;
}

.chapter-title {
  font-size: clamp(40px, 7vw, 92px);
  color: var(--ink);
  max-width: 780px;
}

.chapter-sub {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink-soft);
  margin: 14px 0 0;
}

.chapter-desc {
  max-width: 620px;
  margin: 28px 0 0;
  font-size: clamp(15.5px, 1.4vw, 17.5px);
  line-height: 1.75;
  color: var(--ink-soft);
}

.chapter-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 28px 0 0;
}
.chapter-tags .tag {
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  color: var(--ink-soft);
}

.chapter-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 30px;
  font-family: var(--font-sans);
  font-size: 14.5px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
}

/* ============================================================
   Stylized mockups — for the 3 chapters with no honest screenshot
   available (Telegram bots have no web UI; the Mini App shows only an
   auth error without a real Telegram session). Illustrative, not fake
   screenshots — deliberately look like abstractions, not real captures.
   ============================================================ */
.mock {
  font-family: var(--font-sans);
  width: min(420px, 84%);
  border-radius: 16px;
  background: rgba(10, 9, 8, 0.55);
  border: 1px solid var(--line-strong);
  box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.6);
  padding: 22px;
  /* Replaces the old .chapter-visual padding-right approach (which also
     shrank sibling <img> elements). Same overhang math: .chapter-bg is 10%
     oversized (inset:-10%) for the scroll-scrub scale effect, so this has
     to compensate for that plus normal breathing room from the true edge. */
  margin-right: 16%;
}
@media (max-width: 900px) {
  .mock {
    margin-right: 0;
    /* Mobile text uses the full width and sits vertically centered
       (.chapter-sticky's align-items:center), so a centered mock card
       collides directly with it. Push the card to the bottom of the
       frame instead — pairs with the lightened lower band in the mobile
       .chapter-textscrim gradient below, so it lands somewhere actually
       visible rather than fighting the paragraph for the same space. */
    align-self: flex-end;
    /* vh, not %: margin-bottom percentages resolve against the
       container's *width* per spec, but the overhang this needs to
       clear (.chapter-bg's inset:-10%) is relative to its *height* —
       on a narrow, tall mobile viewport those are very different
       numbers. A %-based margin here left the card pushed entirely
       below the visible frame. 10vh cancels the overhang exactly;
       18vh clears it with real breathing room. */
    margin-bottom: 20vh;
    /* Real safe gap between chapter-content's bottom edge and the
       letterbox bar is only ~45-140px on mobile (measured per chapter —
       shorter descriptions leave more room). The un-shrunk mock cards
       (up to 279px tall for the chat mockup) were taller than that whole
       gap, so they necessarily reached up into the text column — with
       both the mock's own readable UI-style text and the real paragraph
       legible at similar darkening levels, it read as two competing
       blocks of text rather than a card behind text. Shrinking padding
       and type here (this rule + the per-variant overrides below) is
       what actually fixes the collision, not just repositioning. */
    padding: 14px 16px;
    max-width: 78vw;
    /* A hard 60%->85% reveal (tried first) erred too far the other way:
       it hid so much of mock-chat and mock-terminal that there often
       wasn't enough left on screen to register as a card at all — user
       feedback was explicitly that those two had become invisible. The
       chat mockup's *original*, unmasked design (full card, top portion
       legitimately overlapping the description text) had already been
       called out as the one that read well, so some overlap with real
       text is evidently fine as long as most of the card is intact and
       legible — it's a *fully hidden* card that reads as broken, not a
       partially-overlapping one. Only fade the very top sliver (the
       single worst collision point, right where the card starts) and
       leave the rest fully visible. */
    mask-image: linear-gradient(to bottom, transparent 0%, black 20%);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 20%);
  }

  /* Per-variant trims: same "shrink it so it actually fits the safe gap"
     reasoning as the shared .mock rule above, applied to whichever part of
     each mockup was contributing the most unnecessary height/clutter. */

  /* mock-chat (LeadCopilot): the incoming-message bubble was the single
     biggest height contributor (~90px) and, being the least essential line
     (the "AI drafts, human approves" story is told by the draft bubble +
     send button + stage line, not by the customer's inbound message), is
     dropped entirely on mobile rather than just shrunk. aria-hidden="true"
     already sits on the whole .mock-chat wrapper, so nothing is lost from
     assistive tech either. */
  .mock-msg-in {
    display: none;
  }
  .mock-msg {
    padding: 8px 12px;
    font-size: 12.5px;
    line-height: 1.4;
  }
  .mock-chat {
    gap: 6px;
  }

  /* mock-terminal (Payment Reconciliation): 5 lines at line-height:2 was
     the tallest content of the three mockups after mock-chat. Tightening
     line-height and type size keeps every line (the log is short enough to
     stay meaningful) while cutting overall height substantially. */
  .mock-terminal {
    font-size: 11px;
    line-height: 1.6;
  }

  /* mock-app (Casino Platform): width:min(480px,90%) (base rule) resolves
     the 90% against .chapter-bg's oversized inset:-10% box, not the real
     viewport — on mobile that computed to ~435px, wider than the 390px
     viewport itself, pushing the card off both edges and clipping "Profile"
     at the right edge. The shared .mock max-width:78vw above already
     clamps this back onto the real viewport; padding is also tightened
     here so the card reads as compact rather than needing that extra
     width in the first place. */
  .mock-app-head {
    padding: 12px 14px;
  }
  .mock-app-nav {
    padding: 10px 14px;
    font-size: 10px;
  }
}

/* Chat mockup (LeadCopilot) */
.mock-chat { display: flex; flex-direction: column; gap: 10px; }
.mock-msg {
  max-width: 88%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.5;
}
.mock-msg-in {
  align-self: flex-start;
  background: rgba(255, 255, 255, 0.08);
  color: var(--ink-soft);
  border-bottom-left-radius: 4px;
}
.mock-msg-draft {
  align-self: flex-end;
  background: rgba(201, 151, 95, 0.16);
  border: 1px dashed var(--accent);
  color: var(--ink);
  border-bottom-right-radius: 4px;
}
.mock-draft-label {
  display: block;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 6px;
}
.mock-send {
  display: block;
  margin-top: 10px;
  margin-left: auto;
  font: 600 12px var(--font-sans);
  color: #14110d;
  background: var(--accent);
  border: none;
  border-radius: 999px;
  padding: 6px 14px;
  cursor: default;
}
.mock-stage {
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--ink-faint);
  text-align: center;
}
.mock-stage b { color: var(--ink-soft); }

/* Terminal mockup (Payment Reconciliation) */
.mock-terminal {
  font-family: "SF Mono", Consolas, monospace;
  font-size: 12.5px;
  line-height: 2;
  color: var(--ink-soft);
}
.mock-term-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mock-term-line .ok { color: #7fc99a; }
.mock-term-line .hl { color: var(--accent); font-weight: 600; }

/* Mini App mockup (Casino Platform) */
.mock-app { padding: 0; overflow: hidden; width: min(480px, 90%); }
.mock-app-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 16px 18px;
  font-weight: 600;
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.mock-app-bal { font-size: 13px; color: var(--accent); font-weight: 600; }
.mock-app-nav {
  display: flex;
  justify-content: space-between;
  padding: 14px 16px;
  font-size: 11px;
  color: var(--ink-faint);
  white-space: nowrap;
}
.mock-app-nav .active { color: var(--accent); font-weight: 600; }

@media (max-width: 760px) {
  /* Letterbox bars (js/chapters.js, MAX_LETTERBOX_VH=8) eat up to 8vh off
     both the top and bottom of the pinned 100svh chapter frame — 16vh total.
     The comment block below already measured that mobile chapter-content's
     text-bottom edge can reach 86.7% of viewport height on the longer
     descriptions, just ~45px above where the bottom bar starts (92%) — on a
     shorter phone, or with the browser's own UI chrome further shrinking
     the real viewport, that's not a safe margin, it's already clipping
     into the bar. Desktop has vertical space to spare for the cinematic
     framing; mobile doesn't — and mobile already drops the progress rail
     for the same reason (`.progress-rail` hidden below 900px in
     cinema.css): space is the constraint here, not aesthetics. max-height,
     not display:none — clamps the JS-driven inline `style.height` without
     needing to touch chapters.js. */
  .letterbox { max-height: 0; }
  .chapter { height: 220vh; }
  .chapter-content { padding: 0 24px; }
  .chapter-number { font-size: min(60vw, 320px); opacity: 0.6; }
  /* Reported bug (2026-08-07): on a real phone, `.chapter-link` ("View
     live ↗" on Vitrea, "Watch the 3D table deal ↗" on Casino Games & RGS —
     the only two chapters that have one) wasn't visible at all.
     `.chapter-sticky` is `overflow:hidden` with the content vertically
     centered (align-items:center); on the two chapters with a link, the
     stacked content (eyebrow + title + sub + desc + tags + link) is taller
     than most phone viewports once browser chrome eats into 100svh, so the
     centered block overflows and the excess — including the link, the very
     last item — gets clipped rather than shown. Freeing the letterbox space
     above helps but isn't enough on its own for these two specifically.
     Tightening the vertical rhythm here claws back real height across every
     chapter; dropping the tag pills claws back the most for the least
     loss — the tech stack they list is either already named in the prose
     description or covered in the Skills section further down the page, so
     it's the one piece of content mobile can afford to drop entirely. */
  .chapter-eyebrow { margin-bottom: 12px; }
  .chapter-sub { margin-top: 8px; }
  .chapter-desc { margin-top: 16px; line-height: 1.55; }
  .chapter-tags { display: none; }
  .chapter-link { margin-top: 16px; }
  /* (object-fit:contain for real screenshots is handled by the
     max-aspect-ratio:1/1 query above — phones are always portrait, so
     that query already covers them; no need to duplicate it here.) */
  /* On mobile, chapter-content text uses the full width (no separate
     column like desktop) but sits vertically centered within the sticky
     viewport (.chapter-sticky's align-items:center) — it doesn't occupy
     the full height. A flat dark fill across the *whole* frame (the old
     approach) kept text legible but also crushed the image/mock in the
     top and bottom bands where there's no text at all to protect —
     exactly the space the visual needed to actually read as visible.
     Vertical gradient: darkest through the text's vertical band, lighter
     in the top/bottom margins so the real screenshot or mock card shows
     through in the space text isn't using. */
  /* A vertical gradient tuned to "dark behind text, light elsewhere" was
     tried here first, but measurement showed it doesn't work on mobile:
     .chapter-content's text-bottom edge ranges from 75.7% to 86.7% of
     viewport height depending on how long each chapter's description is,
     while the letterbox-bottom bar starts at a fixed 92% — so the truly
     "text-free" gap is as narrow as ~45px on the longest chapters, far
     too little room for a gradient transition to actually reach a light
     value before running into the letterbox. A flat, moderate opacity
     gives the image/mock real visible presence across its whole height
     instead of chasing a gap that barely exists — verified against
     axe-core afterward to confirm text contrast still holds. */
  .chapter-textscrim {
    background: rgba(6, 6, 5, 0.6);
  }

  /* .chapter-scrim's own edge-darkening (0.75 in the base rule above) was
     tuned for desktop, where the image fills the whole frame and nothing
     needs to be visible right at the top/bottom edge. On mobile the real
     screenshot / mock card is deliberately anchored into that exact edge
     band (see .chapter-visual img's object-position and .mock's
     align-self below) — stacked with .chapter-textscrim's own edge
     darkening above, the combined effect was still crushing it to near-
     black even after both were individually loosened. Only the gradient
     is overridden here; opacity keeps coming from JS (chapters.js) per
     scroll position, unchanged. */
  .chapter-scrim {
    background: linear-gradient(
      180deg,
      rgba(0, 0, 0, 0.35) 0%,
      rgba(0, 0, 0, 0.2) 40%,
      rgba(0, 0, 0, 0.35) 100%
    );
  }
}

@media (prefers-reduced-motion: reduce) {
  .chapter { height: auto; padding: 100px 0; }
  .chapter-sticky { position: relative; height: auto; padding: 60px 0; }
  .chapter-bg { position: absolute; inset: 0; }
}
