/* HIS STORY (story.html). Pairs with templates/read.html, data/story.json and js/read.js.
   A long read on night, told with the scroll: one column of about 65 characters, set a little left of centre so the
   landscape paintings can reach out to the right. Scroll is native and never held.
   The scroll-told layer, all of it once and none of it on a loop:
   - paragraphs come in sentence by sentence (opacity), the paragraph settles 10px;
   - each chapter opens on a full screen with its numeral large;
   - the dates he names sit in the left margin (above the paragraph on narrow screens);
   - the palm-size first canvas settles to its small size as you pass it (scroll-driven);
   - the Sinai night: a star field that stays with you; two meteors (js/sky.js, canvas) fall as he tells them;
   - rainbow rain in Umbrella Girl's own colours falls as you read under it (scroll-driven);
   - his two display lines rise word by word; the close builds in and his painted O turns upright.
   The paragraphs about his mother and his brother and sister dying are never animated (data/story.json "quiet").
   Hidden start states live under html.rd-on, which js/read.js sets only when motion is allowed: without JS or under
   reduced motion every word, star and drop is simply there. */
.read-page{
  --sky:#0A0A0B;   /* the Sinai night, a step below Night; it meets Night through linear fades, never an edge */
  /* Umbrella Girl's pours, sampled from assets/art/17-umbrella-girl.jpg: red, yellow, green, cyan, blue, violet */
  --r1:#C50826;--r2:#CB9D40;--r3:#53A977;--r4:#0594C4;--r5:#7582C5;--r6:#AD6FB8;
  /* Blue Yellow Pour's pours, sampled from assets/art/11-blue-yellow-pour.jpg: cobalt, yellow, red, white, black */
  --p1:#0B3C8F;--p2:#CDB651;--p3:#8E2F2A;--p4:#D1D3C2;--p5:#0E1317;
  /* the column's left offset, in viewport units so full-bleed pieces inside it can step back out to the screen edge */
  --rdx:clamp(0px,calc(50vw - var(--gut) - 23ch - 7vw),100vw);
  /* layers: rail 20 (under the nav, 40) */
}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.read{padding:0 var(--gut);isolation:isolate}   /* a stacking context: the rain can sit behind the text with z-index -1 */
.rd{font-size:19px;width:min(100%,46ch);margin-left:var(--rdx)}

/* opening */
.rd-head{padding-top:clamp(120px,17vh,170px)}
.rd-head h1{font-family:"Archivo",sans-serif;font-weight:800;font-stretch:112%;font-size:clamp(48px,6vw,88px);line-height:.95;letter-spacing:-.035em;max-width:11ch}
.rd-head h1 .k{display:block;margin-bottom:.35em;font-size:.32em;font-stretch:125%;letter-spacing:.02em;color:var(--ink-2)}
.rd-head .by{margin-top:28px;font-size:18px;line-height:1.55;color:var(--ink-2)}
/* the opening picture: a wide plate under the title, never over it. Cropped with object-fit, never shown past its own
   pixels (--w, --h from the build), no movement. --focus (data) picks what the crop keeps. */
.rd-pic{--out:0px;margin-top:40px;width:min(calc(100% + var(--out)),calc(var(--w) * 1px))}
.rd-pic img{width:100%;height:min(54vh,520px,calc(var(--h) * 1px));object-fit:cover;object-position:var(--focus);background:var(--raised);
  box-shadow:0 40px 60px -30px rgba(0,0,0,.6),0 12px 20px -12px rgba(0,0,0,.45)}
.rd-pic figcaption{margin-top:14px;font-size:14px;line-height:1.5;color:var(--ink-2)}
@media (min-width:1100px){.rd-pic{--out:14vw}}
.toc ol{list-style:none;display:flex;flex-wrap:wrap;gap:4px 28px;margin-top:22px;padding-top:18px;border-top:1px solid var(--line)}
.toc a{display:inline-flex;align-items:center;min-height:44px;font-size:16px;color:var(--ink);text-decoration:underline;text-decoration-color:var(--ctl);text-decoration-thickness:1px;text-underline-offset:4px;transition:text-decoration-color .2s ease}
.toc a:hover{text-decoration-color:var(--ink)}
.toc .num{color:var(--ink-3);margin-right:.35em}   /* the link is a flex box: the space in the markup collapses */

/* chapter openers: a screen of their own. The numeral is set large on its own line; the heading still reads
   "I. The History" to a screen reader. */
#read{outline:none}
.ch{scroll-margin-top:0}
.ch-head{min-height:100vh;min-height:100svh;display:flex;flex-direction:column;justify-content:center;padding:112px 0 72px}
.ch-head h2{font-family:"Archivo",sans-serif;font-weight:800;font-stretch:112%;font-size:clamp(44px,5vw,72px);line-height:.95;letter-spacing:-.035em}
.ch-head .num{display:block;overflow:hidden;padding-bottom:.06em;font-size:clamp(120px,17vw,240px);font-weight:200;font-stretch:100%;line-height:.82;letter-spacing:.02em;color:var(--ink-2)}   /* hairline numerals: at this size a heavy I is a bar */
.ch-head .num>span{display:block}
.ch-head .t{display:block;margin-top:.45em}
.ch-sub{margin-top:18px;font:600 22px/1.3 "Archivo",sans-serif;font-stretch:112%;letter-spacing:-.01em;color:var(--ink-2);max-width:28ch}
.ch p:not(.ch-sub){font-size:1em;line-height:1.7;color:var(--ink-2);margin-top:1.15em;text-wrap:pretty}
.ch-head+p{margin-top:0!important}

/* his one-line paragraphs: a typographic moment, wider than the column, with a screen's worth of air */
.ch p.line{margin:0;padding:.5em 0;font:800 clamp(44px,6.4vw,104px)/.98 "Archivo",sans-serif;font-stretch:112%;letter-spacing:-.035em;color:var(--ink);
  width:min(calc(100% + 14vw),calc(100vw - 2 * var(--gut)));max-width:12ch;text-wrap:balance}
.ch p.line+p{margin-top:0}
.line .w{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.1em;margin-bottom:-.1em}
.line .w>span{display:inline-block}

/* dates he names: a quiet marker, the hairline joins it to its sentence */
.dated{position:relative}
.dated::before{content:attr(data-when);content:attr(data-when) / "";display:block;margin-bottom:12px;font:500 14px/1.2 "Geist",sans-serif;font-variant-numeric:tabular-nums;letter-spacing:.01em;color:var(--ink)}
@media (min-width:1100px){
  .dated::before{position:absolute;right:calc(100% + 44px);top:.42em;margin:0;white-space:nowrap}
  .dated::after{content:"";position:absolute;right:calc(100% + 14px);top:.82em;width:20px;height:1px;background:var(--ctl);transform-origin:right}
}

/* the first canvas: no larger than a palm. A primed square with the Om, small beside the paragraph. */
.palm-row{position:relative;margin-top:1.15em}
.palm-row>p{margin-top:0!important}
.palm{display:grid;place-items:center;width:64px;height:64px;background:var(--board);color:var(--board-ink);
  font:400 34px/1 "Kohinoor Devanagari","Devanagari Sangam MN","Noto Sans Devanagari","Nirmala UI",serif;
  box-shadow:0 16px 20px -12px rgba(0,0,0,.7),0 4px 8px -4px rgba(0,0,0,.5);transform-origin:left bottom}
.palm>span{translate:0 .04em}
@media (max-width:1099px){.palm{float:right;margin:.35em 0 10px 18px;transform-origin:right top}}   /* the text wraps beside it: no gap */
@media (min-width:1100px){.palm{position:absolute;right:calc(100% + 44px);top:.3em;margin:0;transform-origin:right top}}

/* ---------- BEATS (js/beats.js): drawn beside their paragraph, never taking room in the flow ----------
   From 1100px they live in the margins beside the column; below it at the screen edges, or dimmed behind the text. */
[data-beat]{position:relative}
.bt{position:absolute;z-index:-1;pointer-events:none}
.bt svg{display:block;overflow:visible}

/* three warm lights, side by side: crisp points with a few pixels of falloff, the picture light's colour */
.three{display:flex;gap:12px;right:calc(100% + 40px);top:.5em}
.three i{width:12px;height:12px;border-radius:50%;background:radial-gradient(circle,#FFF6E6 0 1.6px,rgba(var(--light),.5) 2.6px,rgba(var(--light),0) 6px)}
@media (max-width:1099px){.three{right:auto;left:-4px;top:-15px;gap:8px}}
.rd-on .three:not(.go) i{opacity:0;transform:scale(.6)}
.rd-on .three i{transition:opacity .7s cubic-bezier(.23,1,.32,1) calc(var(--i) * .55s),transform .7s cubic-bezier(.23,1,.32,1) calc(var(--i) * .55s)}

/* the pour: the stream is uncovered from the top and the bead rides its leading edge, both with the scroll */
.pour{left:-200px;top:.55em}
@media (max-width:1099px){.pour{left:-17px;top:.3em}}
.pour .drizzle{fill:none;stroke:var(--p4);stroke-width:1.1;stroke-linejoin:round;opacity:.85}
@supports (animation-timeline:view()){
  .rd-on [data-beat=pour]{view-timeline-name:--pour}
  .rd-on .pour .flow{animation:pour-flow linear both;animation-timeline:--pour;animation-range:cover 30% cover 85%}
  .rd-on .pour .bead{animation:pour-bead linear both;animation-timeline:--pour;animation-range:cover 30% cover 85%}
}
@keyframes pour-flow{from{clip-path:inset(0 0 100% 0)}to{clip-path:inset(0 0 0 0)}}
@keyframes pour-bead{from{transform:translateY(calc(var(--h) * -1));opacity:0}6%{opacity:1}to{transform:none}}   /* no bead before the paint runs */

/* the journey south and back: 2.5s, the line draws, the waypoints light as it passes, the cut, the way home */
.route{left:calc(100% + 56px);top:-.2em}
.route .leg{fill:none;stroke:var(--ink-2);stroke-width:1.2;stroke-linecap:round}
.route .leg.r{stroke:var(--ink-3)}
.route .cut{fill:none;stroke:var(--ink);stroke-width:1.4;stroke-linecap:round}
.route .home,.route .wp{fill:var(--ink)}
.route .label{font:400 10px/1 "Geist Mono",monospace;letter-spacing:.04em;fill:var(--ink-3)}
@media (max-width:1099px){.route{left:auto;right:-6px;top:0;opacity:.35}.route svg{width:110px;height:169px}}
.rd-on .route .leg{stroke-dasharray:1;stroke-dashoffset:1}
.rd-on .route .wp,.rd-on .route .cut{opacity:0}
.rd-on .route.go .leg.s{stroke-dashoffset:0;transition:stroke-dashoffset 1.5s cubic-bezier(.45,0,.2,1)}
.rd-on .route.go .wp{opacity:1;transition:opacity .3s ease-out calc(.4s + var(--i) * .38s)}
.rd-on .route.go .cut{opacity:1;transition:opacity .15s ease-out 1.55s}
.rd-on .route.go .leg.r{stroke-dashoffset:0;transition:stroke-dashoffset .7s cubic-bezier(.23,1,.32,1) 1.8s}

/* splashes: land with a quick impact, the drops thrown out from the centre, then settle */
.splash{inset:0}
.splash .sp{position:absolute;top:calc(var(--y) - 40px)}
.splash .sp.l{right:calc(100% + var(--x) - 60px)}
.splash .sp.r{left:calc(100% + var(--x) - 60px)}
@media (max-width:1099px){.splash .sp{opacity:.5;transform:scale(.5)}.splash .sp.l{right:auto;left:-95px}.splash .sp.r{left:auto;right:-95px}}   /* mostly past the screen edge: only their rims show */
.splash .body,.splash .drop{transform-box:fill-box;transform-origin:center}
.rd-on .splash:not(.go) .sp{visibility:hidden}
.rd-on .splash.go .body{animation:splat .45s cubic-bezier(.2,.8,.2,1) calc(var(--i) * .13s) backwards}
.rd-on .splash.go .drop{animation:fling .4s cubic-bezier(.23,1,.32,1) calc(var(--i) * .13s + .04s) backwards}
@keyframes splat{0%{transform:scale(.35);opacity:0}30%{transform:scale(1.1);opacity:1}100%{transform:none}}
@keyframes fling{from{transform:translate(var(--dx),var(--dy)) scale(.4);opacity:0}to{transform:none;opacity:1}}

/* paintings: square corners, the plate shadow, never past 1 CSS px per photo px or 78% of the screen height */
.fig{--out:0px;margin:72px 0;width:min(calc(100% + var(--out)),calc(var(--w) * 1px),calc(78vh * var(--ar)))}
.fig img{width:100%;height:auto;box-shadow:0 40px 60px -30px rgba(0,0,0,.6),0 12px 20px -12px rgba(0,0,0,.45)}
.fig figcaption{margin-top:16px;font-size:14px;line-height:1.5;color:var(--ink-2)}
.fig cite{font-style:normal;color:var(--ink)}
.fig a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--ctl);text-underline-offset:3px;transition:text-decoration-color .2s ease}
.fig a:hover{text-decoration-color:var(--ink)}
@media (min-width:1100px){.fig,.rain{--out:14vw}}

/* the Sinai night. The field is sticky inside the section, so the sky stays with you while the paragraphs pass over
   it; it steps out of the column to the screen edges (--rdx). The section fades in and out of Night (linear only). */
/* the sky never takes room from the text: its paragraphs keep the normal rhythm, one figure-sized breath on the way in */
.sky{position:relative;isolation:isolate;margin:0;padding-bottom:1px}
.sky::before{content:"";position:absolute;z-index:-2;top:0;bottom:0;left:calc(-1 * (var(--gut) + var(--rdx)));width:100vw;
  background:linear-gradient(var(--night),var(--sky) 24vh,var(--sky) calc(100% - 24vh),var(--night))}   /* stays inside the section: past it, it would paint over the paragraphs around it */
.sky-field{position:sticky;top:0;z-index:-1;width:100vw;height:100vh;height:100svh;margin-left:calc(-1 * (var(--gut) + var(--rdx)));
  -webkit-mask-image:linear-gradient(transparent,#000 14%,#000 86%,transparent);mask-image:linear-gradient(transparent,#000 14%,#000 86%,transparent)}
/* from 1100px the stars keep clear of the reading column (its edges from --gut, --rdx and its 46ch width) */
@media (min-width:1100px){
  .sky-field .st{--c0:calc(var(--gut) + var(--rdx) - 40px);--c1:calc(var(--gut) + var(--rdx) + 46ch + 40px);
    -webkit-mask-image:linear-gradient(90deg,#000 var(--c0),transparent calc(var(--c0) + 40px),transparent calc(var(--c1) - 40px),#000 var(--c1));
    mask-image:linear-gradient(90deg,#000 var(--c0),transparent calc(var(--c0) + 40px),transparent calc(var(--c1) - 40px),#000 var(--c1))}
}
.sky-field svg{position:absolute;inset:0;width:100%;height:100%}
.sky .stars{fill:var(--ink)}
.sky .mw{opacity:.06}   /* the Milky Way: a soft linear band, kept out of the column with the stars */
/* still (no JS, no canvas, reduced motion): two faint streaks where the meteors end. js/sky.js hides them (.sky.cv)
   and draws the meteors on a canvas instead. */
.sky .shoot{opacity:.3}
.sky .shoot polygon{transform:translateX(var(--d))}
.sky.cv .fx{display:none}
.sky-field canvas{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
/* the text pulls up over the field with its own margin, not the field's: a negative margin on a sticky element lets it
   ride a whole screen past the end of its section */
.sky>.dated:first-of-type{margin-top:calc(72px - 100vh);margin-top:calc(72px - 100svh)}
.sky p{position:relative}
@media (max-width:1099px){.sky .stars{opacity:.75}.sky .mw{opacity:.035}}   /* the column runs over the whole sky here: the sky steps back */
/* on a phone the text fills the width: the meteors fall behind it, dimmed so the words stay first */
@media (max-width:1099px){.sky-field canvas{opacity:.9}.sky .fx{opacity:.55}}

/* rainbow rain under Umbrella Girl: drops in its own colours. Still drops by default; they fall with the scroll
   where the browser runs scroll-driven animation (each drop over its own stretch, set by the build). */
/* it takes no room: pulled up under the caption and over the next paragraphs, behind the text (z-index -1, .read) */
.rain{--out:0px;--rh:clamp(200px,34vh,360px);position:relative;z-index:-1;height:var(--rh);margin:-56px 0 calc(40px - var(--rh));opacity:.6;width:min(calc(100% + var(--out)),calc(var(--w) * 1px),calc(78vh * var(--ar)));overflow:hidden}
.rain i{position:absolute;top:0;left:var(--x);width:4px;height:100%;transform:translateY(var(--y))}
.rain i::before{content:"";display:block;width:4px;height:var(--l);border-radius:0 0 2px 2px;background:linear-gradient(transparent,var(--c))}

/* the close: his name, his line, and DO ART. set with his painted O */
.end{margin-top:clamp(140px,24vh,260px);padding-bottom:clamp(120px,22vh,220px)}
.end p:not(.last){font:700 clamp(26px,2.6vw,36px)/1.2 "Archivo",sans-serif;font-stretch:112%;letter-spacing:-.02em;color:var(--ink);max-width:22ch}
.end p:not(.last)+p:not(.last){margin-top:.5em}
.end .last{display:flex;margin-top:48px;font-size:clamp(72px,11vw,168px);color:var(--ink)}
.end .last .sp{margin-left:.2em}   /* his space: DO ART */
.end .btn{margin-top:56px}

/* chapter rail: where you are, three numerals, each a link. Fills with its chapter where the browser runs
   scroll-driven animation; otherwise it only marks the current one. Shown inside the chapters only. */
.rail{position:fixed;z-index:20;right:20px;top:50%;translate:0 -50%;opacity:0;visibility:hidden;transition:opacity .3s ease,visibility 0s linear .3s}
.rail.on{opacity:1;visibility:visible;transition:opacity .3s ease}
.rail ol{list-style:none;display:flex;flex-direction:column;gap:2px;padding:4px;background:var(--raised);border:1px solid var(--line);border-radius:999px}
.rail a{position:relative;display:grid;place-items:center;width:44px;height:44px;border-radius:50%;cursor:pointer;text-decoration:none;
  font:800 14px/1 "Archivo",sans-serif;font-stretch:112%;color:var(--ink-2);transition:color .2s ease,background-color .2s ease}
.rail a[aria-current]{background:var(--ink);color:var(--btn-ink)}
.rail i{position:absolute;left:14px;right:14px;bottom:9px;height:2px;border-radius:1px;background:currentColor;transform:scaleX(0);transform-origin:left;opacity:.5}
@media (hover:hover) and (pointer:fine){.rail a:not([aria-current]):hover{color:var(--ink)}}
.rail a:active{transform:scale(.96)}
@supports (timeline-scope:--a){
  body{timeline-scope:--c-history,--c-journey,--c-story}
  .rail i{animation:rail-fill linear both;animation-range:contain}
}
@keyframes rail-fill{to{transform:scaleX(1)}}

@media (max-width:767px){
  .rd{font-size:17px}
  .rd-head{padding-top:116px}
  .rd-head .by{font-size:16px}
  .rd-pic{margin-top:28px}
  .rd-pic img{height:min(40svh,360px)}
  .ch-head{padding:96px 0 56px}
  .ch-sub{font-size:19px}
  .ch p.line{font-size:clamp(40px,12vw,56px)}
  .fig{margin:56px 0}
  .end .last{font-size:min(16vw,88px)}
  /* the rail goes to the bottom right, in a row, clear of the home indicator */
  .rail{top:auto;right:12px;bottom:max(12px,env(safe-area-inset-bottom));translate:none}
  .rail ol{flex-direction:row}
}

/* ---------- the motion, only under html.rd-on (js/read.js, motion allowed) ---------- */
/* paragraphs: each sentence fades in after the one before, the paragraph settles 10px, once */
.rd-on [data-rv]{translate:0 10px;transition:translate .9s var(--ease)}
.rd-on [data-rv] .s{opacity:0;transition:opacity .7s var(--ease) calc(var(--i) * 90ms)}
.rd-on [data-rv].in{translate:none}
.rd-on [data-rv].in .s{opacity:1}
.rd-on .dated::before,.rd-on .dated::after{opacity:0;transition:opacity .7s var(--ease) .15s,transform .9s var(--ease) .15s}
.rd-on .dated::after{transform:scaleX(0)}
.rd-on .dated.in::before,.rd-on .dated.in::after{opacity:1;transform:none}

/* chapter openers: the numeral rises out of its own line, then the title and the subtitle */
.rd-on .ch-head .num>span{transform:translateY(100%);transition:transform 1.1s var(--ease)}
.rd-on .ch-head .t,.rd-on .ch-head .ch-sub{opacity:0;translate:0 16px;transition:opacity .9s var(--ease),translate .9s var(--ease)}
.rd-on .ch-head .t{transition-delay:.18s}
.rd-on .ch-head .ch-sub{transition-delay:.32s}
.rd-on .ch-head.in .num>span{transform:none}
.rd-on .ch-head.in .t,.rd-on .ch-head.in .ch-sub{opacity:1;translate:none}

/* paintings fade up 16px once */
.rd-on .fig[data-reveal]{opacity:0;translate:0 16px;transition:opacity .8s var(--ease),translate .8s var(--ease)}
.rd-on .fig[data-reveal].in{opacity:1;translate:none}

/* his display lines: word by word out of their own line */
.rd-on .line .w>span{transform:translateY(105%);transition:transform .9s var(--ease) calc(var(--i) * 70ms)}
.rd-on .line.in .w>span{transform:none}

/* a few stars breathe, very slowly; motion allowed only */
.rd-on .sky .tw{animation:twinkle 7s ease-in-out infinite alternate}
@keyframes twinkle{from{opacity:var(--o)}to{opacity:.08}}

/* the first canvas settles from large to palm size as it passes, and the rain falls with the scroll */
@supports (animation-timeline:view()){
  .rd-on .palm{animation:palm linear both;animation-timeline:view();animation-range:entry 0% cover 45%}
  .rd-on .rain{view-timeline-name:--rain}
  .rd-on .rain i{animation:fall linear both;animation-timeline:--rain}
}
@keyframes palm{from{transform:scale(2.2);opacity:0}35%{opacity:1}to{transform:none;opacity:1}}
@keyframes fall{from{transform:translateY(-15%);opacity:0}12%{opacity:1}75%{opacity:1}to{transform:translateY(85%);opacity:0}}

/* the close builds in: his name, his line, then D, his painted O turning upright from its painted angle, ART. */
.rd-on .end p:not(.last),.rd-on .end .btn{opacity:0;translate:0 14px;transition:opacity .9s var(--ease),translate .9s var(--ease)}
.rd-on .end p:not(.last)+p:not(.last){transition-delay:.5s}
.rd-on .end .last>*{opacity:0;translate:0 .25em;transition:opacity .9s var(--ease),translate .9s var(--ease)}
.rd-on .end .last .o{translate:none;transform:rotate(-56deg) scale(.92);transition:opacity .7s var(--ease),transform 1.2s var(--ease-cam)}
.rd-on .end .last .l1{transition-delay:1.1s}
.rd-on .end .last .o{transition-delay:1.25s}
.rd-on .end .last .sp{transition-delay:1.45s}
.rd-on .end .btn{transition-delay:2.1s}
.rd-on .end.in p:not(.last),.rd-on .end.in .btn,.rd-on .end.in .last>*{opacity:1;translate:none}
.rd-on .end.in .last .o{transform:none}

@media (prefers-reduced-motion:reduce){
  .toc a,.fig a,.rail,.rail a{transition:none}
  .rail a:active{transform:none}
  .rail i{animation:none}
}
