/* ==========================================================================
   OREGON — css/about.css
   Phase P9 — Slide 2, the "OREGON" knockout (About Oregon), shortened.

   Tan (P9, verbatim): the old whole-building scroll-pan was "stalling
   people from getting to the next slide without any real reason... too
   long" — he wants the tower already at rest with "the building starts at
   the very top," then just "a fade like this and stop; there's no need
   after this point," and "no need for the additional long photo." So: no
   more scroll-driven pan. At rest the SCG Head Office elevation
   (assets/img/about-tower.{webp,jpg}, portrait 1745×2328) sits with its
   crown at the very top of the viewport (js/about.js's updateRegister
   pins the photo's BUILDING_TOP row to the section's own top edge) and
   its shaft's left edge on the word's left edge, so the letterforms read
   as glass and white bands. The scrub is a short settle only — the
   overlay lifts part-way and the copy column fades/lifts — then the
   section ends.

   Reuses the hero's knockout ladder rather than reinventing it:
   background-clip:text (primary) → SVG <mask> duplicate-image layer
   (fallback) → solid off-white text (last resort). Mode class
   (.about--bgclip / .about--svgmask / .about--solid) is added by
   js/about.js via window.OregonHero.detectKnockoutMode() (exported by
   js/hero.js) so both knockouts agree on technique for a given browser.

   Register (why the letters and the backdrop never drift apart): both the
   backdrop photo and the word's fill share the exact same cover-computed
   size (--about-bg-w / --about-bg-size) and the exact same pan offset
   (--about-pan-y / --about-shift-x — a fixed register value set once per
   resize by js/about.js's updateRegister(), not scrub-driven). The word
   only ever adds its own fixed anchor offset (--about-bg-x / --about-bg-y0)
   on top of that shared pan value — see js/about.js for the derivation.

   P10 item H: --about-drift-x / --about-drift-y are the one scrub-driven
   exception — a small monotonic pan (js/about.js buildScrub) layered on
   top of the fixed register above, read identically by .about__photo,
   .about__mask-photo AND the word's background-position, so the façade
   inside the OREGON letters keeps matching the photo behind it at every
   point of the pin, not just at rest.
   ========================================================================== */

.about {
  padding: 0;
  background: var(--ink);
  overflow: hidden;
}

/* The shared 90rem `.slide__inner` cap is tuned for reading columns
   elsewhere; at 1920+ it would squeeze both the word and the copy column
   into less than half the viewport. Same fix hero.css applies for its own
   oversized single-line headline (see that file's comment). */
.about .slide__inner { max-width: none; }

/* ---------------------------------------------------------------------- */
/* Backdrop — full-bleed photo + dark overlay                              */
/* The photo is NOT object-fit:cover; it's a pre-sized layer (JS sets its
   width in px to the cover-equivalent size) held at a single fixed
   translateY (the register computed once by js/about.js's
   updateRegister()) — transform-only, and the same value the letters'
   fill reads for register.                                             */
/* ---------------------------------------------------------------------- */
.about__backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: linear-gradient(135deg, #1c1e22 0%, #23262b 45%, #1a1c20 100%);
}
.about__backdrop.is-missing .about__photo { opacity: 0; }

.about__photo {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--about-bg-w, 100vw);
  max-width: none;
  height: auto;
  /* P10 item H: --about-drift-x/-y (js/about.js buildScrub) must be read
     here too, not just by the word's background-position and the SVG-mask
     duplicate below — otherwise the real backdrop stays put while the
     knockout content pans, and the two drift out of register the moment
     the overlay lifts and both are visible at once. */
  transform: translateX(-50%) translateX(var(--about-shift-x, 0px)) translateY(var(--about-pan-y, 0px)) translateX(var(--about-drift-x, 0px)) translateY(var(--about-drift-y, 0px));
  will-change: transform;
}

.about__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ink);
  opacity: 0.88; /* rest state — the only place the photo shows is through the letters;
                    the scrub lifts this to ~0.45 and stops there (js/about.js buildScrub) */
  pointer-events: none;
  will-change: opacity;
}

/* ---------------------------------------------------------------------- */
/* Content grid — word (left) / about copy (right)                         */
/* ---------------------------------------------------------------------- */
.about__content {
  position: relative;
  z-index: 3;
  width: 100%;
  /* Block padding deliberately tighter than the other slides' var(--space-6):
     this slide is pinned at exactly one viewport tall (see js/about.js's
     updateRegister, which sizes the pan against window.innerHeight, not
     this section's own possibly-taller box) and the two-column content
     below has to fit inside that one viewport with the full-size copy
     column Tan asked to be bigger — the standard --space-6 padding plus
     the old #scale slide's spacing pushed the footnote below the fold for
     the entire pin (only visible in a brief unpinned instant afterwards).
     Trimmed here, and in .about__right's gap / .about__stats / a
     .about__footnote below, rather than shrinking any type size. */
  padding: var(--space-2) var(--margin);
  /* P8: but never under the fixed logo (mobile stacks the word first, so
     the eyebrow/word top must clear it; desktop's right column sits well
     right of the logo and only needs the small pad). */
  padding-top: max(var(--space-2), var(--chrome-top));
}
@media (min-width: 900px) {
  .about__content { padding-top: var(--space-2); }
  /* "ARCHITECTURAL" is ≈11em at wdth 125; the right column is ≈45vw, so
     between 900 and ~1400px the --fs-h2 token overruns the column and the
     slide's overflow:hidden clips the last letters. Cap by viewport width
     there; min() leaves the token alone from ~1440 up, where it fits. */
  .about__headline { font-size: min(var(--fs-h2), 4vw); }
}
.about__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gutter);
  align-items: center;
  width: 100%;
}
.about__left {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-width: 0;
}
.about__right {
  display: flex;
  flex-direction: column;
  gap: 0.5rem; /* tightened from --space-3 — see .about__content's comment */
  min-width: 0;
}
@media (max-width: 899.98px) {
  /* Stacked word-above-copy is inherently taller than the two-column
     desktop layout, so the same one-viewport budget needs a second round
     of trimming here on top of the shared cuts above. */
  .about__grid { grid-template-columns: 1fr; gap: 0.25rem; }
  .about__left { justify-content: center; }
  .about__content { padding-top: var(--chrome-top); padding-bottom: 0.375rem; }
  /* The footnote sentence is repeated verbatim on the why-3/why-5 slides;
     on a phone the pinned one-viewport budget can't hold it under the logo. */
  .about__footnote { display: none; }
  .about__right { gap: 0.1rem; }
  .about__body { line-height: 1.16; }
  .about__headline { line-height: 0.94; }
  .about__stats { gap: 0.15rem var(--gutter); }
  .about__footnote { padding-top: 0.25rem; }

  /* P10 item I: Tan — "on mobile I also get cut off at the end" (the
     stats row runs off the bottom of the pinned one-viewport budget at
     ~390px wide). The desktop word/headline/stat sizes above are display
     type, not the 18px body floor (that constraint is 1920/1440-only —
     see GOAL.md/_common.md), so every one of these can shrink further on
     a phone without breaking the floor; tightened here rather than
     touching any copy (still every word of it, just smaller/tighter).
     Word: was clamp(2.5rem,14vw,5rem) ≈ 52-55px at 375-390 — one of the
     single biggest boxes in the stack for a word that's decorative
     (knockout art, not read as text). Headline: --fs-h2's own mobile
     floor (2.1rem ≈ 33.6px) was wrapping the longest headline to 4 lines;
     a dedicated mobile size lands it closer to 3. Stat values: site.css's
     shared .stat__value clamp (~27-28px here) is sized for the full-width
     stats band elsewhere on the page, not this cramped 2-up mobile grid —
     scoped down to match the now-smaller headline. The decorative hr
     divider (no text) is dropped outright; it was drawing its own gap
     interval for ~1px of rule. */
  .about__headline { font-size: clamp(1.4rem, 5.6vw, 1.8rem); }
  .about .about__stats .stat__value:not(.stat__value--compact) {
    font-size: clamp(1.1rem, 4vw, 1.5rem);
  }
  .about .about__stats .stat__value--compact { font-size: clamp(0.95rem, 3.6vw, 1.3rem); }
  .about .about__stats .stat__label { margin-top: 0.2rem; }
  .about__right > .hr { display: none; }

  /* P10 (item C1): Tan — "the next section has very poor contrast and
     text visibility" (phone, OREGON + copy over the bright building).
     The photo is only ever the OREGON word's fill and a dim backdrop
     above the copy — this real bed sits behind the whole stacked copy
     column so body/eyebrow/stat text never reads directly off the
     façade, at rest or at any point of the scrub (the scrub only moves
     the overlay/photo brightness — js/about.js's buildScrub — not this
     panel, so contrast holds independent of scroll position). Full-bleed
     via the same left:50%/100vw/translateX(-50%) technique .about__photo
     uses above, clipped by .about's own overflow:hidden. Soft top edge
     (a gradient, not a hard box line) through the small gap above the
     eyebrow; ≥0.85 ink by the time the eyebrow starts (measured via
     p10/contrast.mjs), settling ≥0.94 under the stat row at the bottom. */
  .about__right { position: relative; z-index: 0; }
  .about__right::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: 50%;
    width: 100vw;
    transform: translateX(-50%);
    top: -1.25rem;
    bottom: -1.5rem;
    background: linear-gradient(
      to bottom,
      rgba(14, 15, 17, 0) 0%,
      rgba(14, 15, 17, 0.92) 12%,
      rgba(14, 15, 17, 0.95) 100%
    );
    pointer-events: none;
  }
}

/* ---------------------------------------------------------------------- */
/* The word — LEFT column                                                  */
/* Sized against the measured glyph width (not assumed) so it fills the    */
/* ~46vw left column at 1920 without touching the gutter; see HANDOVER/    */
/* report for the measured px widths at each verified viewport.            */
/* ---------------------------------------------------------------------- */
.about__word {
  position: relative;
  display: inline-block;
  margin: 0;
  font-family: var(--font-display);
  font-variation-settings: "wdth" 125;
  font-weight: 700;
  line-height: 0.86;
  letter-spacing: 0;
  text-transform: uppercase;
  transform-origin: 0% 50%;
  will-change: transform;
  font-size: clamp(3rem, 8vw, 10.5rem);
  /* Base / no-JS / solid-fallback: plain, always-legible off-white type.
     This must never render as invisible text — the fills below only turn
     it transparent once JS has confirmed a knockout technique is live. */
  color: var(--offwhite);
  -webkit-text-fill-color: var(--offwhite);
}
@media (max-width: 899.98px) {
  /* Stacked layout: word sits above the copy, so it can be more generous
     without competing with a column — but still capped well inside the
     375/390px viewport width minus margins (measured, not assumed — the
     old `.close__word` shipped a 14vw clamp that overflowed at this exact
     breakpoint range).
     P10 item I: shrunk again (was clamp(2.5rem,14vw,5rem) ≈ 52-55px at
     375-390) — the word is decorative knockout art, not read as text, and
     was one of the single biggest boxes in the mobile stack that ran the
     stats row off the bottom of the pinned viewport. */
  .about__word { font-size: clamp(1.9rem, 9.2vw, 3rem); }
}
/* P10 (PM): the 9.2vw cap exists for the 375×667 fit; taller phones
   (390×844 measured 138px spare, 375×812 100px) can afford the word back
   at the P9 weight without pushing the stats off the bottom. */
@media (max-width: 899.98px) and (min-height: 800px) {
  .about__word { font-size: clamp(2.2rem, 12.5vw, 3.6rem); }
}

.about--bgclip .about__word {
  background-image: url("../assets/img/about-tower.jpg");
  background-image: image-set(
    url("../assets/img/about-tower.webp") type("image/webp") 1x,
    url("../assets/img/about-tower.jpg") type("image/jpeg") 1x
  );
  background-repeat: no-repeat;
  background-size: var(--about-bg-size, cover);
  background-position:
    calc(var(--about-bg-x, 0px) + var(--about-drift-x, 0px))
    calc(var(--about-bg-y0, 0px) + var(--about-pan-y, 0px) + var(--about-drift-y, 0px));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: brightness(1.15) contrast(1.08) saturate(1.15);
}

.about--svgmask .about__word {
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* ---------------------------------------------------------------------- */
/* SVG mask fallback — a masked duplicate image layer, register-matched to */
/* the same pan/size as the backdrop (mirrors hero.js's buildSvgMask).      */
/* ---------------------------------------------------------------------- */
.about__mask-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.about__mask-media {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  opacity: 1; /* visible at rest under no-JS-timeline / reduced-motion too */
  -webkit-mask-image: url(#about-knockout-mask);
  mask-image: url(#about-knockout-mask);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  will-change: opacity, transform;
  transform-origin: 0% 50%;
}

.about__mask-photo {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--about-bg-w, 100vw);
  max-width: none;
  height: auto;
  transform: translateX(-50%) translateX(var(--about-shift-x, 0px)) translateY(var(--about-pan-y, 0px)) translateX(var(--about-drift-x, 0px)) translateY(var(--about-drift-y, 0px));
  filter: brightness(1.15) contrast(1.08) saturate(1.15);
}

/* ---------------------------------------------------------------------- */
/* Right column — about copy                                               */
/* ---------------------------------------------------------------------- */
.about__headline { margin: 0; }

.about__body {
  font-family: var(--font-body);
  font-size: var(--fs-body); /* ~27px at 1920 — clears the 26px floor asked for */
  color: var(--offwhite-60);
  max-width: 58ch; /* wide enough to use the column, short enough to stay readable — fewer wrapped lines than a tight 46ch, which matters for the one-viewport height budget */
  line-height: 1.5;
}

/* Stat row — reuses .stat / .stat__value / .stat__label / .stats__footnote
   from site.css (same markup as the old #scale slide) plus the divider +
   compact-value treatment that used to live scoped to `#scale` in
   projects.css (now dead there since the id moved — replicated here since
   this file, not projects.css, owns the new section). */
.about__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--gutter); /* row-gap tightened from --space-4 */
  /* No margin-top here — .about__right's own flex `gap` already spaces this
     from the body copy above it; an extra margin on top of that gap was
     pure double-spacing (a leftover from the old #scale slide's non-flex
     layout, where it was load-bearing). */
}
.about__stats .stat { position: relative; }
.about__stats .stat:nth-child(even)::before {
  content: "";
  position: absolute;
  top: 0.15em;
  bottom: 0.15em;
  left: calc(var(--gutter) * -0.5);
  width: 1px;
  background: var(--steel-20);
}
.about__stats .stat__value--compact {
  font-size: clamp(1.05rem, 1.1vw + 0.7rem, 1.55rem);
  letter-spacing: 0.02em;
}
.about__footnote {
  /* No margin-top — see .about__stats's comment; the flex gap already
     separates this from the stat row above it. */
  padding-top: var(--space-2);
  border-top: 1px solid var(--steel-20);
  max-width: 42em;
}

/* ---------------------------------------------------------------------- */
/* Reduced motion / no-engine — belt & braces. js/about.js already skips   */
/* the pin/scrub timeline in this case (the knockout itself, and its       */
/* resting pan/overlay values, are still built so the letters read as      */
/* windows over the darkened photo) — this just guarantees a clean static  */
/* frame if a class were ever left stuck mid-scrub.                        */
/* ---------------------------------------------------------------------- */
.reduced-motion .about__word,
.reduced-motion .about__mask-media { transform: none !important; opacity: 1 !important; }
.reduced-motion .about__right { opacity: 1 !important; transform: none !important; }
