/* ==========================================================================
   style.css — PHOTOGRAPHY page components.
   Fonts, :root tokens and the reset now live in css/base.css: Barba keeps
   <head> across a navigation, so both pages load base.css + style.css +
   wheel.css + ui.css and each of these files may only own its own selectors.
   ========================================================================== */

/* Gradient rectangle — Figma node 14320:10762. 26 x 90px, centred on
   x = 50% + 144px, y = 549 of the 1080 frame. Gradient stops are Figma's:
   rgba(195,195,195,0) -> #c3c3c3, "to top". */
.tick{
  position: absolute;
  left: calc(50% + 144px);
  top: 50.83%;              /* 549 / 1080 */
  transform: translateX(-50%);
  width: 26px;
  height: 90px;
  background: linear-gradient(to top, rgba(195,195,195,0) 0%, var(--c-fade) 100%);
  z-index: 15;
}

/* ==========================================================================
   HERO — 1:1 port of Figma node 14320:10757
   ========================================================================== */
.hero{
  position: relative;
  width: 100%;
  min-height: 100svh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(to bottom, var(--c-white) 0%, var(--c-fog) 162.59%);
}

.hero__title{
  position: relative;
  z-index: 5;          /* sits behind the sphere */
  margin: 0;
  text-align: center;
  font-weight: 400;
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: -0.04em; /* -5.12px at the design's 128px size */
  font-size: clamp(2.75rem, 6.7vw, 8rem);

  /* line-height 0.5 is load-bearing, not a typo: it makes the text BOX far
     shorter than the glyphs, so the glyphs overflow it. The gradient below is
     sized to that short box, which is what lets line 2 sit in the visible
     upper part of the ramp instead of falling past the transparent stop.
     Loosening this makes "CRAP!" disappear entirely. */
  line-height: 0.5;

  /* PP Supply Sans has a 1.2em content box (hhea 990/-210 over 1000upm).
     Against line-height 0.5 that means glyphs overflow each line box by
     exactly 0.35em top and bottom. Padding by that amount makes the
     PADDING box — which is what the background is painted over — equal the
     real glyph extent. That's the box Figma measured its stop percentages
     against, so the values below can be used verbatim and land correctly.
     Without this the stops are measured against a box ~35% too short and
     line 2 falls past the transparent stop and vanishes. */
  padding-top: 0.35em;
  padding-bottom: 0.35em;

  /* Gradient fill. Colors and direction are Figma's verbatim
     (rgba(195,195,195,0) -> #c3c3c3, "to top"). The STOP POSITIONS are
     re-anchored: Figma measures its 44.172% / 117.18% against its own text
     layer box, which sits differently against the glyphs than a CSS line
     box does, so pasting those numbers renders line 2 invisible. These
     stops put the same ramp over the padded glyph-extent box above and
     reproduce the design: line 1 near-solid, line 2 lighter and dissolving
     toward its baseline. Verified against the Figma render. */
  background: linear-gradient(to top, rgba(195,195,195,0) 10%, var(--c-fade) 82%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;

  /* Tuned by eye against the sphere sitting over it. The wheel page's
     .stage__title is deliberately left at 1 — it has no sphere in front of
     it. Match them if the two pages ever need to read identically.

     Expressed as the value the reveal LANDS on rather than as a static
     opacity: the headline now carries .anim-line, whose keyframes would
     otherwise finish at opacity 1 and undo this. */
  --in-opacity: .7;
}
.hero__title > span{
  display: block;
}
.hero__title > span:first-child{
  margin-bottom: 0.267em; /* 34.2px at the design's 128px size */
}

/* ==========================================================================
   SPHERE GALLERY — GSAP-driven CSS 3D, layered over the headline in the
   same first screen. Cards are tangent to the sphere surface; the placement
   math lives in js/sphere-gallery.js.
   ========================================================================== */
.sphere-viewport{
  z-index: 20;
  position: absolute;
  inset: 0;
  perspective: 1400px;
  perspective-origin: 50% 50%;
  touch-action: none;
  cursor: grab;

  /* The blend lives HERE, on the whole sphere layer, not on the individual
     cards. Two reasons: a card's blend would be isolated inside the 3D
     stacking context (so it'd blend against nothing), and mix-blend-mode on
     a child of a preserve-3d parent forces the group to flatten, which
     collapses the sphere. Blending the layer as a group keeps the 3D intact
     and lets the headline read through the images. */
  mix-blend-mode: multiply;
}
.sphere-viewport.is-dragging{ cursor: grabbing; }

.sphere-stage{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  will-change: transform;
}

.sphere-item{
  position: absolute;
  top: 0;
  left: 0;

  /* CRITICAL — do not remove.
     The browser applies transforms as T(origin) . M . T(-origin). With the
     default 50% 50% origin, the trailing translate(-50%,-50%) in the card's
     transform composes with that and leaves every card offset by
     (w/2, h/2) in STAGE space. That offset rotates with the sphere, so the
     whole cloud orbits the pivot at a radius of half a card diagonal — the
     sphere visibly slides from A to B while you drag, even though the pivot
     itself never moves. Anchoring the origin at the top-left makes the
     transform chain mean what it says: card centres land exactly on the
     sphere, concentric with the pivot. */
  transform-origin: 0 0;
  width: var(--item-w, 132px);
  height: var(--item-h, 176px);
  overflow: hidden;
  /* Cards are tangent to the sphere, so far-side cards legitimately face
     away — backfaces stay visible so the sphere reads as a full volume
     rather than a hollow front hemisphere. */
}
/* ---- halftone echo layer -------------------------------------------------
   A second sphere stacked over the real one, running on its rotation from
   htDelayMs ago. Every dial arrives as a custom property set on this
   container by applyHalftoneStyle(), so the 65 cards inherit rather than
   each needing a write. It must never take the pointer — the drag belongs
   to the real sphere underneath. */
.sphere-viewport--ghost{
  z-index: 21;
  pointer-events: none;
  mix-blend-mode: var(--ht-blend, normal);
  opacity: var(--ht-opacity, 0.85);
}

.sphere-item--ghost img{
  filter: var(--ht-filter, none);

  /* The dot screen. The mask tile is an inline SVG built in JS, so the
     shape (circle / square / cross) and its size within the cell are fully
     controllable. It masks the IMAGE, not the card, so the dots ride along
     with the card's 3D transform and scale with it as you zoom. */
  -webkit-mask-image: var(--ht-mask);
          mask-image: var(--ht-mask);
  -webkit-mask-size: var(--ht-size, 6px) var(--ht-size, 6px);
          mask-size: var(--ht-size, 6px) var(--ht-size, 6px);
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
}

.sphere-item img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;

  /* object-fit: cover is what lets landscape source photos fill the
     vertical card — they crop rather than letterbox. */
}
