/* ==========================================================================
   East Star Hotel — motion layer
   Scroll-driven components + animation primitives.
   Everything here degrades to a static, fully readable page:
     · without JS   → the `.js` guard means nothing is hidden
     · reduced motion → transforms are neutralised at the bottom of this file
   ========================================================================== */

/* ---------- Film grain ---------------------------------------------------
   A fixed, non-interactive overlay. Gives flat gradients some tooth.        */
.grain {
  position: fixed; inset: 0; z-index: 900; pointer-events: none;
  opacity: .5; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
}

/* ---------- Scroll progress rail ---------- */
.progress {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 950;
  block-size: 2px; pointer-events: none;
}
.progress__bar {
  block-size: 100%; inline-size: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--terracotta), var(--brass) 70%, var(--brass-lt));
}

/* ---------- Word-mask text reveal ----------
   JS wraps each word as <span class="w"><i>word</i></span>. The mask sits on
   .w; the inner <i> slides up out of it. Padding/negative-margin keeps
   descenders from being clipped by the mask.                                */
.js .words .w {
  display: inline-block; overflow: hidden; vertical-align: bottom;
  padding-block-end: .14em; margin-block-end: -.14em;
}
.js .words .w > i {
  display: inline-block; font-style: inherit; font-weight: inherit;
  transform: translateY(110%);
  transition: transform 1s var(--ease-out);
}
.js .words.is-in .w > i { transform: none; }

/* ---------- Generic scroll reveals ---------- */
.js [data-anim] { transition: opacity .9s var(--ease-out), transform 1s var(--ease-out); }
.js [data-anim="up"]    { opacity: 0; transform: translateY(42px); }
.js [data-anim="fade"]  { opacity: 0; }
.js [data-anim="left"]  { opacity: 0; transform: translateX(-48px); }
.js [data-anim="right"] { opacity: 0; transform: translateX(48px); }
.js [data-anim="scale"] { opacity: 0; transform: scale(.92); }
.js [data-anim].is-in { opacity: 1; transform: none; }

/* Clip-wipe reveal for media blocks */
.js [data-anim="clip"] {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.3s var(--ease-io);
}
.js [data-anim="clip"].is-in { clip-path: inset(0 0 0 0); }

/* Slow scroll-linked push-in. The wrapper crops; the inner element (which
   carries the background) is oversized and eases back to natural size. */
.zoom { overflow: hidden; display: block; }
.zoom > * { inline-size: 100%; block-size: 100%; will-change: transform; }

/* ---------- Oversized section index ---------- */
.chapter {
  display: flex; align-items: flex-start; gap: clamp(1rem, 3vw, 2.5rem);
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
/* Outlined numeral. The stroke colour must be named explicitly — `currentColor`
   would resolve to the `transparent` fill and render nothing. */
.chapter__num {
  font-family: var(--ff-display); font-size: var(--step-2);
  line-height: 1;
  color: transparent; -webkit-text-stroke: 1px var(--brass);
  flex: none; padding-top: .15em;
}
@supports not (-webkit-text-stroke: 1px red) {
  .chapter__num { color: var(--brass); }
}
.chapter__label { flex: 1; }

/* Ghost numeral sitting behind a section */
.ghost-num {
  position: absolute; z-index: 0; pointer-events: none; user-select: none;
  font-family: var(--ff-display); font-size: var(--step-5); line-height: .8;
  color: transparent; -webkit-text-stroke: 1px var(--line);
  inset-block-start: -.15em; inset-inline-end: -.05em;
}
.section--ink .ghost-num { -webkit-text-stroke-color: rgba(255, 255, 255, .09); }

/* ---------- Hero, layered ---------- */
.hero { isolation: isolate; }
.hero__layer {
  position: absolute; inset-inline: -6%; z-index: 1;
  pointer-events: none; will-change: transform;
}
/* The drawn planes carry the hero while it is a gradient. Behind a real
   photograph they would read as a second, competing horizon — so --tint
   (set by photos.css) fades them out. They come back automatically if a
   photo is ever removed. */
.hero__layer--hills {
  inset-block-end: 8%; block-size: clamp(120px, 22vh, 240px);
  background: url("../img/hills.svg") repeat-x bottom center / auto 100%;
  opacity: calc(.5 * (1 - var(--tint, 0)));
}
.hero__layer--town {
  inset-block-end: 0; block-size: clamp(150px, 28vh, 300px);
  background: url("../img/skyline.svg") repeat-x bottom center / auto 100%;
  opacity: calc(.72 * (1 - var(--tint, 0)));
}
.hero__stars {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: calc(.55 * (1 - var(--tint, 0)));
}

.hero__inner { z-index: 3; }
.hero__title { font-size: var(--step-4); line-height: .95; letter-spacing: -.03em; }

/* Hero content parks itself as you scroll past */
.js .hero__inner { will-change: transform, opacity; }

/* ---------- Pinned horizontal gallery ----------
   JS sets --shift (px) and the section height on resize; the scroll handler
   only writes a transform. Below 900px the whole mechanism is switched off
   and the track becomes a native snap carousel.                             */
.pinh { position: relative; }
.pinh__sticky {
  position: sticky; inset-block-start: 0;
  block-size: 100svh; display: grid; align-content: center;
  overflow: hidden;
}
.pinh__head {
  padding-inline: var(--gutter); margin-bottom: clamp(1.25rem, 3vw, 2.25rem);
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
}
.pinh__track {
  display: flex; gap: clamp(1rem, 2vw, 1.75rem);
  padding-inline: var(--gutter);
  will-change: transform;
}
.pinh__card {
  flex: none; inline-size: min(78vw, 420px);
  display: grid; grid-template-rows: auto 1fr;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
}
.section--ink .pinh__card { background: rgba(255, 255, 255, .04); border-color: var(--line-ink); }
.pinh__card .media { aspect-ratio: 4 / 3; }
.pinh__body { padding: 1.25rem; display: grid; gap: .55rem; align-content: start; }
.pinh__index {
  font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
  font-weight: 700; color: var(--brass);
}
.pinh__rail {
  margin-top: clamp(1.25rem, 3vw, 2rem); margin-inline: var(--gutter);
  block-size: 2px; background: var(--line-ink); position: relative;
}
.section--sand .pinh__rail { background: var(--line); }
.pinh__rail i {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  inline-size: 100%; background: var(--brass);
  transform: scaleX(0); transform-origin: 0 50%;
}

/* Carousel fallback (small screens, and no-JS everywhere) */
.pinh.is-static .pinh__sticky { position: static; block-size: auto; overflow: visible; }
.pinh.is-static .pinh__track {
  overflow-x: auto; scroll-snap-type: x mandatory;
  padding-block-end: 1rem; transform: none !important;
  scrollbar-width: thin;
}
.pinh.is-static .pinh__card { scroll-snap-align: start; }
.pinh.is-static .pinh__rail { display: none; }

/* ---------- Stacking cards ----------
   Block flow, not grid: a sticky *grid item* is trapped inside its own grid
   area and would unstick after one row. In normal flow every card shares the
   same containing block, so they genuinely pile on top of one another.
   Each card's stick point steps down a little so the stack's edges stay
   visible, and transform-origin is the top edge so a shrinking card tucks
   under the one arriving over it.                                           */
.stack-scene { display: block; }
.stack-card {
  --stick: max(84px, 11vh);
  position: sticky; inset-block-start: var(--stick);
  transform-origin: 50% 0;
  border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 4vw, 3rem);
  background: var(--ink-2); color: var(--text-onink);
  border: 1px solid var(--line-ink);
  box-shadow: 0 -20px 60px -30px rgba(0, 0, 0, .95);
  will-change: transform;
  margin-bottom: clamp(1.25rem, 2.5vw, 2rem);
}
.stack-card:nth-child(1) { inset-block-start: calc(var(--stick) + 0px); }
.stack-card:nth-child(2) { inset-block-start: calc(var(--stick) + 14px); }
.stack-card:nth-child(3) { inset-block-start: calc(var(--stick) + 28px); }
.stack-card:nth-child(4) { inset-block-start: calc(var(--stick) + 42px); }
.stack-card:nth-child(5) { inset-block-start: calc(var(--stick) + 56px); }
.stack-card:nth-child(even) { background: var(--ink-3); }
.stack-card__top {
  display: flex; align-items: baseline; gap: 1rem;
  margin-bottom: 1rem; padding-bottom: 1rem;
  border-bottom: 1px solid var(--line-ink);
}
.stack-card__no {
  font-family: var(--ff-display); font-size: 1.4rem; color: var(--brass);
}
.stack-card__grid {
  display: grid; gap: clamp(1rem, 3vw, 2.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 780px) { .stack-card__grid { grid-template-columns: 1.1fr 1fr; } }
.stack-card h3 { font-size: var(--step-2); }

/* ---------- Drawn railway line ---------- */
.railmap { position: relative; }
.railmap svg { inline-size: 100%; block-size: auto; overflow: visible; }
.railmap .rail-track { stroke: var(--line-ink); stroke-width: 2; fill: none; }
.railmap .rail-live {
  stroke: var(--brass); stroke-width: 2.5; fill: none; stroke-linecap: round;
}
.railmap .stop circle { fill: var(--ink); stroke: var(--brass); stroke-width: 2; }
.railmap .stop text {
  fill: var(--text-onink-soft); font-family: var(--ff-body);
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 600;
}
.railmap .stop { opacity: .25; transition: opacity .5s var(--ease); }
.railmap .stop.is-lit { opacity: 1; }
.railmap .stop.is-here circle { fill: var(--brass); r: 8; }
.railmap .stop.is-here text { fill: #fff; font-size: 13px; }

/* ---------- Counters ---------- */
.count { font-variant-numeric: tabular-nums; }

/* ---------- Editorial extras ---------- */
.overlap { position: relative; z-index: 2; margin-block-start: clamp(-6rem, -8vw, -3rem); }
.rule-heavy { block-size: 6px; background: var(--ink); border-radius: 3px; }
.section--ink .rule-heavy { background: var(--brass); }

.figure-note {
  font-size: var(--step--1); color: var(--text-soft);
  display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: start;
}
.figure-note::before { content: "↑"; color: var(--terracotta); }

/* Big pull quote */
.pull {
  font-family: var(--ff-display); font-size: var(--step-3);
  line-height: 1.05; letter-spacing: -.02em; max-width: 20ch;
}

/* Link with a wiping underline */
.wipe { position: relative; text-decoration: none; display: inline-block; }
.wipe::after {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: -2px;
  block-size: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform .5s var(--ease-out);
}
.wipe:hover::after { transform: scaleX(1); transform-origin: 0 50%; }

/* ---------- Reduced motion: strip every transform, keep every pixel ---------- */
@media (prefers-reduced-motion: reduce) {
  .grain { display: none; }
  .js .words .w { overflow: visible; }
  .js .words .w > i,
  .js [data-anim],
  .js [data-anim].is-in { transform: none !important; opacity: 1 !important; transition: none !important; }
  .js [data-anim="clip"] { clip-path: none !important; }
  .hero__layer, .zoom > * { transform: none !important; }
  .pinh__sticky { position: static; block-size: auto; overflow: visible; }
  .pinh__track { transform: none !important; overflow-x: auto; scroll-snap-type: x mandatory; }
  .stack-card { position: static; transform: none !important; }
  .railmap .stop { opacity: 1; }
}
