/* ============================================================
   ARITSU — shared house contract
   Loaded by every page BEFORE the page's own <style>.
   Tsudio runs the ink scale. Conceptual Haven runs the paper scale.
   ============================================================ */

:root {
  /* ---- paper scale (Conceptual Haven, and the splash) ---- */
  --paper:      #FBFAF8;
  --paper-2:    #F2F0EC;
  --paper-3:    #E7E4DE;
  --paper-rule: #DAD7D1;
  --paper-ink:  #111114;
  --paper-mid:  #5E5F62;
  --paper-lo:   #8A8B8E;

  /* ---- ink scale (Tsudio), evenly stepped in perceptual lightness ---- */
  --ink-50:  #0D0D10;
  --ink-100: #161619;
  --ink-200: #1F1F22;
  --ink-300: #2B2B2E;
  --ink-400: #424245;
  --txt-hi:  #F5F5F5;
  --txt-mid: #ABABAB;
  --txt-lo:  #7A7A7A;

  /* ---- one accent, used almost never ---- */
  --accent:      #EC9D53;
  --accent-deep: #B36A24;   /* AA-safe on paper */

  --grotesk: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --serif:   "Bodoni Moda", Didot, "Times New Roman", serif;
  --mono:    "JetBrains Mono", ui-monospace, "SF Mono", monospace;

  --gut: clamp(20px, 3.6vw, 52px);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-io:  cubic-bezier(0.76, 0, 0.24, 1);
}

*, *::before, *::after { box-sizing: border-box; }
h1, h2, h3, h4, h5 { margin: 0; font-size: inherit; font-weight: inherit; }
p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }

html { scroll-behavior: smooth; }
body { margin: 0; font-family: var(--grotesk); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.skip-link {
  position: absolute; left: var(--gut); top: -60px; z-index: 100;
  padding: 10px 18px; background: var(--accent); color: #111114;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
  transition: top 0.25s var(--ease-out);
}
.skip-link:focus { top: 14px; }

/* ---------- type helpers ---------- */
.mono { font-family: var(--mono); font-size: 10px; letter-spacing: 0.24em; text-transform: uppercase; }
.serif { font-family: var(--serif); font-weight: 400; }
.display {
  font-variation-settings: "wdth" 100, "wght" 640;
  letter-spacing: -0.035em; line-height: 0.92; text-transform: uppercase; text-wrap: balance;
}
.body-copy { font-size: 16px; line-height: 1.68; text-wrap: pretty; }

/* ---------- film grain, whisper level ---------- */
.grain::after {
  content: ""; position: absolute; inset: 0; z-index: 8; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.72' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 168px; opacity: 0.05; mix-blend-mode: overlay;
}

/* ---------- image frame ---------- */
.shot { position: relative; overflow: hidden; }
.shot img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- entry reveal: resting state is VISIBLE, motion is additive ----------
   NOTE: never put clip-path on the observed element itself. A collapsed
   clip-path zeroes the element's intersection rect, so IntersectionObserver
   can never fire and the content stays invisible forever. The curtain wipe
   therefore lives on the inner image, which is never the observed node.     */
.reveal { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    opacity: 0; transform: translateY(20px);
    transition: opacity .8s var(--ease-out), transform .9s var(--ease-out);
    transition-delay: var(--d, 0ms);
  }
  .reveal.in { opacity: 1; transform: none; }

  .reveal .shot img {
    clip-path: inset(0 0 100% 0); transform: scale(1.05);
    transition: clip-path .9s var(--ease-io), transform 1.2s var(--ease-out);
    transition-delay: var(--d, 0ms);
  }
  .reveal.in .shot img { clip-path: inset(0 0 0 0); transform: scale(1); }

  /* Rail items live inside a horizontally clipping scroller, so most of them
     never intersect the viewport until the rail is dragged. Reveal-on-enter
     is the wrong tool there: it would leave the bulk of a gallery invisible.
     Rails are always visible; their motion comes from the scroll itself.   */
  .rail .reveal, .rail .reveal.in { opacity: 1; transform: none; }
  .rail .reveal .shot img { clip-path: none; transform: none; transition: transform .6s var(--ease-out); }
}

/* ============================================================
   RAIL — the shared horizontal scroll-snap gallery.
   Native overflow + scroll-snap. No wheel hijacking, ever.
   Markup contract:
   <div class="rail" data-rail>
     <div class="rail-track">
       <figure class="rail-item"> <div class="shot"><picture>…</picture></div>
         <figcaption class="rail-cap mono">…</figcaption> </figure>
       …
     </div>
   </div>
   ============================================================ */
.rail {
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  padding-bottom: 14px;
  cursor: grab;
}
.rail.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.rail.is-dragging * { pointer-events: none; }
.rail-track { display: flex; gap: clamp(10px, 1.4vw, 22px); width: max-content; padding-inline: var(--gut); }
.rail-item { scroll-snap-align: center; flex: 0 0 auto; margin: 0; }
.rail-item .shot { height: clamp(300px, 56vh, 620px); width: auto; }
.rail-item .shot img { height: 100%; width: auto; max-width: none; }
.rail-cap { margin-top: 10px; display: flex; gap: 12px; }
@media (max-width: 760px) {
  .rail-item .shot { height: clamp(260px, 46vh, 420px); }
  .rail-track { gap: 10px; }
}

/* rail progress bar — set --p from JS 0..1 */
.rail-progress { height: 1px; position: relative; overflow: hidden; }
.rail-progress::after {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: 100%; transform: scaleX(var(--p, 0)); transform-origin: left;
  transition: transform .18s linear;
}

/* ---------- marquee: exactly one per page ---------- */
.marquee { overflow: hidden; white-space: nowrap; }
.marquee-track { display: inline-flex; width: max-content; animation: marquee 58s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---------- reduced motion: real feature gate ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .marquee-track { animation: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---------- cross-document view transition ---------- */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: 480ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
