/* ===========================================================================
   Wedding Express — the site under the opening sequence.
   Morning palette: the night ends when the guest taps to board.
   =========================================================================== */

:root{
  --ink:#241d16; --paper:#f8f2e4; --deep:#05070d; --red:#b23a2f;
  --brass:#a9832f; --brass-lo:#c2a96d;
  --rail:#123a63; --rail-lo:#1b4c86; --rail-hi:#2a6fb0;
  --sky1:#ffe8c4; --sky2:#ffd7a8; --cream:#fdf8ee;
  --line:rgba(36,29,22,.16);
  --mono:'Share Tech Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --script:'Great Vibes',cursive;
  --serif:'Cormorant Garamond',Georgia,serif;
  --tamil:'Noto Sans Tamil','Latha',sans-serif;

  /* Where the top of the station box sits, and how much air the names keep
     above it. Both the gate and the intro overlay read these, so the two
     scenes stay identical — and because the names are anchored UPWARD from
     the roofline rather than downward from the top of the screen, they can
     never land on the building no matter how the window is shaped. Tuning
     the layout means changing these two numbers, nothing else. */
  --roofline:51%;
  --namegap:20px;
}
@media (max-width:700px){
  :root{--roofline:49%;--namegap:42px}
}
*{box-sizing:border-box}
/* Any author display rule outranks the UA sheet's [hidden]{display:none},
   so `hidden` silently stops working on flex/grid elements. Make it win. */
[hidden]{display:none !important}

/* Every layer of the arrival scene is decoration — none of it contains
   anything you can click, and several sit above the ticket in the stacking
   order. Without this they still hit-test while invisible and swallow
   TAP TO BOARD. Opacity does not disable hit-testing; pointer-events is the
   only thing that does.

   This lives up here in the global block ON PURPOSE. It used to sit down
   among the gate rules and a section-level edit spliced it away twice —
   silently, because the layers look identical either way and only the click
   stops working. Anything decorative added to the scene belongs in this list. */
.skyfest,.skystars,.skyglow,.fireworks,.station,.forecourt,.egsign,.railnames,
.gate .g-scroll,.gate .g-chev{pointer-events:none}
html{scroll-behavior:smooth;scroll-padding-top:56px}
/* Cormorant Garamond ships OLD-STYLE figures by default: at 60px the digits
   measure 1=24, 3=42, 0=25, 2=25, 6=41 — an 18px spread — so "13 NOV 2026"
   came out with some numerals half the height of others. The face does
   carry lining figures, they just have to be asked for. Set once here so
   every number on the page gets them. */
body{font-variant-numeric:lining-nums;font-feature-settings:"lnum" 1;
  margin:0;background:var(--cream);color:var(--ink);
  font-family:var(--serif);-webkit-font-smoothing:antialiased;
  overflow-x:hidden}
body.lang-ta{font-family:var(--tamil),var(--serif)}
body.lang-ta .mono{font-family:var(--tamil),var(--mono)}
/* Tamil is written with conjuncts; the wide tracking that suits the English
   mono headings pulls those apart and makes words hard to read. */
body.lang-ta .sec-head h2,body.lang-ta .mono,body.lang-ta .hero .kicker,
body.lang-ta .hero .when,body.lang-ta .ntes-body,body.lang-ta .chip,
body.lang-ta .pill,body.lang-ta .btn,body.lang-ta .field label,
body.lang-ta .foot .sub,body.lang-ta .coach .when{letter-spacing:.01em}
img{max-width:100%;display:block}
button{font:inherit}
a{color:inherit}

.mono{font-family:var(--mono);letter-spacing:.12em}
.wrap{width:min(1080px,100% - 32px);margin-inline:auto}
.sec{padding-block:clamp(48px,8vw,88px);position:relative}
.sec-head{text-align:center;margin-bottom:clamp(24px,4vw,40px)}
.sec-head h2{font-family:var(--mono);font-size:clamp(15px,3.4vw,20px);letter-spacing:.26em;
  margin:0 0 8px;color:var(--rail)}
.sec-head p{margin:0;font-size:clamp(15px,3.4vw,18px);color:#6d6154;font-style:italic}
.sec-head::after{content:"";display:block;width:64px;height:2px;margin:14px auto 0;
  background:repeating-linear-gradient(90deg,var(--brass) 0 8px,transparent 8px 14px)}

/* scroll-reveal: every section drifts up into place once */
.rv{opacity:0;transform:translateY(26px);transition:opacity .7s ease,transform .7s cubic-bezier(.2,.8,.3,1)}
.rv.in{opacity:1;transform:none}
.rv.d1{transition-delay:.08s} .rv.d2{transition-delay:.16s} .rv.d3{transition-delay:.24s}
.rv.d4{transition-delay:.32s} .rv.d5{transition-delay:.40s}

/* ---------------------------------------------------------------- top bar */
/* fixed, not sticky. A sticky bar sits IN the flow and pushes the gate down by
   its own height, which knocked the gate's names 47px below the overlay's and
   put a visible jump in the handover. Floating it keeps the gate flush to the
   top of the page, exactly where the overlay is. */
.topbar{position:fixed;left:0;right:0;top:0;z-index:40;display:flex;align-items:center;
  gap:12px;padding:9px 16px;background:rgba(253,248,238,.9);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line)}
.topbar .names{font-family:var(--script);font-size:22px;line-height:1.7;color:var(--rail);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar .spacer{flex:1}
.pill{border:1px solid var(--line);background:#fff;border-radius:999px;padding:6px 12px;
  font-family:var(--mono);font-size:11px;letter-spacing:.1em;cursor:pointer;color:var(--rail);
  display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.pill:hover{background:#fffdf7;border-color:var(--brass-lo)}
.pill.on{background:var(--rail);color:#fff;border-color:var(--rail)}
/* The sky-show switch. Same cream pill, navy ink and brass edge as its
   neighbour -- only tighter, because it carries a glyph and not a word. */
.sndpill{padding:5px 9px;font-size:14px;line-height:1}
/* Sound is wanted but the browser has not seen a gesture yet. A slow brass
   breath says "there is something here to turn on" without ever becoming a
   banner across the page. */
.sndpill.needtap{border-color:var(--brass);animation:sndwait 2.6s ease-out infinite}
@keyframes sndwait{
  0%{box-shadow:0 0 0 0 rgba(169,131,47,.42)}
  70%{box-shadow:0 0 0 9px rgba(169,131,47,0)}
  100%{box-shadow:0 0 0 0 rgba(169,131,47,0)}}
@media (prefers-reduced-motion: reduce){
  .sndpill.needtap{animation:none;box-shadow:0 0 0 2px rgba(169,131,47,.3)}}

/* =================== the gate: where the intro lands =====================
   The first screen of the site, built to be pixel-identical to the final
   frame of the arrival sequence — same sky, same station, same names in the
   same place — so the overlay just dissolves on top of it and there is no cut.

   The backdrop is Chennai Egmore itself rather than trackside scenery, and
   the sky is a festive dusk rather than a muted one.

   Every value below is shared with #opening in opening.css. Move one, move
   both, or the handover will visibly jump.
   ====================================================================== */
.gate{position:relative;height:100svh;min-height:520px;overflow:hidden}

/* the sky, and the same stack the overlay ends on */
.skyfest{position:absolute;inset:0;
  background:linear-gradient(#1b2a63 0%,#4b3a86 18%,#8e4a86 38%,#d1557a 56%,
                             #f07d4e 74%,#ffb659 88%,#ffe6a8 100%)}
.skyglow{position:absolute;left:50%;bottom:26%;width:min(620px,120vw);aspect-ratio:1;
  translate:-50% 0;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,rgba(255,226,150,.62) 0%,rgba(255,150,90,.28) 38%,
                             rgba(255,120,80,0) 68%)}
/* a few warm stars still out at dusk */
.skystars{position:absolute;inset:0;pointer-events:none}
.skystars i{position:absolute;width:2px;height:2px;border-radius:50%;background:#fff3d2;
  animation:twf 4s ease-in-out infinite}
@keyframes twf{0%,100%{opacity:.15}50%{opacity:.75}}

/* Fireworks over the station. Each burst is a ring of particles thrown out
   from one point; the keyframe cycle is long and the burst occupies only the
   first quarter of it, so a burst fires once per cycle instead of pulsing
   continuously. Staggered delays keep them from going off in unison. */
.fireworks{position:absolute;left:0;right:0;top:0;height:56%;overflow:hidden;z-index:3}
.fw{position:absolute;width:0;height:0}
.fw i{position:absolute;left:0;top:0;width:4px;height:4px;border-radius:50%;
  background:var(--c);box-shadow:0 0 9px 2px var(--c);opacity:0;
  animation:fwpop var(--dur,6.5s) cubic-bezier(.12,.62,.3,1) infinite;
  animation-delay:var(--dl,0s)}
/* The cycle is now the length of the soundtrack (17s) rather than ~7s, so
   these percentages are scaled to match: a burst still takes about 2.1s on
   screen, it simply occupies a twelfth of the cycle instead of a third. The
   long tail is the wait for this burst's next bang. */
@keyframes fwpop{
  0%    {transform:rotate(var(--a)) translateY(0) scale(.4);opacity:0}
  0.8%  {opacity:1}
  6.6%  {opacity:1}
  12.3% {transform:rotate(var(--a)) translateY(calc(-1 * var(--d))) scale(.35);opacity:0}
  100%  {transform:rotate(var(--a)) translateY(calc(-1 * var(--d))) scale(.35);opacity:0}
}
/* the flash at the moment of the burst */
.fw b{position:absolute;left:-26px;top:-26px;width:52px;height:52px;border-radius:50%;
  background:radial-gradient(circle,var(--c),transparent 68%);opacity:0;
  animation:fwflash var(--dur,6.5s) ease-out infinite;animation-delay:var(--dl,0s)}
@keyframes fwflash{0%{opacity:0;transform:scale(.3)}0.8%{opacity:.85}
  4.9%{opacity:0;transform:scale(1.5)}100%{opacity:0}}
/* One frame with no animation-name is what restarts them. opening.js adds
   this class the instant the recording starts over, which is what keeps the
   sky and the soundtrack on the same clock however long the page is open. */
.fireworks.resync .fw i,.fireworks.resync .fw b{animation-name:none}

/* the station */
/* Lower than before, and only half the frame tall: the names need clear sky
   above the roofline rather than sitting on top of the building. */
/* top + bottom, not height: the roofline is the anchor the names measure
   against, so it has to be a position rather than a derived value. Because
   "slice" scales to cover, a box shorter than the artwork's aspect means the
   scale is driven by height — the full building height always shows and it is
   the sides that crop. */
/* min-height is what stops the domes being sliced off.

   "slice" scales to COVER the box, so whenever the box is proportionally
   shorter than the artwork (3200/700 = 4.57:1) the excess is cropped off the
   top — and the top is exactly where the chhatris and the great dome's finial
   live. On a wide, short window (1900x880) the box came out at 5.22:1 and took
   87 units off, decapitating all three towers.

   Holding the box to at least width/4.57 means the scale is never
   width-driven, so nothing is ever cropped vertically. The box then runs a
   little past `bottom` on wide screens and the base of the building tucks
   behind the road, which is what you would see from a street anyway. */
.station{position:absolute;left:0;right:0;top:var(--roofline);bottom:6%;
  min-height:calc(100vw / 4.5);z-index:1}
.station .eg{width:100%;height:100%;display:block;
  filter:drop-shadow(0 -6px 40px rgba(255,170,90,.28))}
/* warm light washing up the frontage from the forecourt */
.station::after{content:"";position:absolute;left:0;right:0;bottom:0;height:46%;
  pointer-events:none;
  background:linear-gradient(0deg,rgba(255,176,92,.42),rgba(255,176,92,0))}

/* Ends of the frontage that only fit on a wide screen. See the note in
   _art_egmore.html — below this width "slice" would cut them in half. */
@media (max-width:1000px){ .eg-wide{display:none} }

/* ---- the road in front of the frontage.
   Egmore's facade faces a street, not a running line, so the track that made
   sense trackside is wrong here. The overlay fades its track out and this in
   at the same moment, so both scenes still end on the same picture. ---- */
.forecourt{position:absolute;left:0;right:0;bottom:0;height:6.5%;z-index:2;
  background:linear-gradient(#4a4e57,#2a2d34)}
.forecourt::before{content:"";position:absolute;left:0;right:0;top:0;height:5px;
  background:#8b8f98;opacity:.5}
.forecourt::after{content:"";position:absolute;left:0;right:0;top:55%;height:4px;
  background:repeating-linear-gradient(90deg,#e8e3d4 0 42px,transparent 42px 90px);opacity:.75}

/* The parapet board lives out near the end of the frontage, so "slice"
   crops it away on a phone. Below 1000px the station name stands on a
   post by the road instead, centred where it always fits. Above that the
   painted one on the roofline does the job and this stays hidden. */
.egsign{display:none}
@media (max-width:1000px){
  .egsign{display:block;position:absolute;left:50%;translate:-50% 0;bottom:10%;
    z-index:3;width:min(272px,70vw);pointer-events:none}
}
.egsign-board{background:#F4F8F0;border:3px solid #1F6B3A;border-radius:4px;
  padding:5px 8px;text-align:center;font-family:Arial,Helvetica,sans-serif;
  font-weight:700;font-size:clamp(11px,3.1vw,14px);letter-spacing:.06em;
  color:#1F6B3A;box-shadow:0 7px 16px rgba(40,10,0,.4)}
.egsign-posts{display:flex;justify-content:space-between;padding:0 22%}
.egsign-posts i{width:5px;height:20px;
  background:linear-gradient(90deg,#5c646f,#a3acb7 42%,#4b525b)}

/* ---- the names. Shared with the overlay: same class, same rules, so the two
   are guaranteed to sit in the same place at the same size ---- */
/* Above the station, not over it. The frontage top edge lands at 40% from the
   top of the frame, so this block has to finish before that. */
.railnames{position:absolute;left:0;right:0;
  bottom:calc(100% - var(--roofline) + var(--namegap));
  text-align:center;padding:0 20px;z-index:6}
/* small mono caps over a red wall: a wide soft shadow swallows them at phone
   size, so these get a tight dark rim instead of a glow */
.railnames .rbrand{font-family:var(--mono);font-size:clamp(9.5px,2.3vw,11px);letter-spacing:.34em;
  color:#fff3d8;margin-bottom:clamp(12px,2vw,18px);
  text-shadow:0 1px 1px rgba(40,8,0,.95),0 0 6px rgba(40,8,0,.9)}
/* Glossy, and readable against a bright sky. The fill is a gradient clipped
   to the glyphs — highlight across the upper third, warm gold rolling into
   the descenders — which is what reads as polished rather than flat. The
   separation comes from two drop-shadows on the painted result: one tight for
   a crisp edge, one wide and soft to lift it off the sky. A text-shadow can't
   do this job once the fill is a clipped gradient, but a filter can. */
/* line-height and padding are load-bearing here. With background-clip:text the
   paint comes from the element's background box, so any part of a glyph that
   sticks out of that box gets no fill at all and simply vanishes. Great Vibes
   has tall swashes and deep descenders that overflow a tight line box, which
   is what was slicing the tops off the capitals. */
.railnames .rn{font-family:var(--script);
  /* The vh term is what keeps the block inside a short window. Anchored from
     the roofline the names grow UPWARD, so on a 620px-tall laptop an 11vh cap
     pushed their top off the screen. 9vh fits every shape tested. */
  font-size:clamp(34px,min(9.5vw,9vh),82px);line-height:1.16;padding:.06em 0;
  color:#fff6e2;
  background-image:linear-gradient(176deg,#ffffff 0%,#fffaf0 30%,#ffeaba 52%,
                                   #f0bd6e 70%,#fff4d8 88%,#ffe3ab 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;
  filter:drop-shadow(0 2px 2px rgba(56,14,4,.6))
         drop-shadow(0 10px 30px rgba(40,8,0,.55))}
.railnames .ramp{display:block;font-size:clamp(18px,min(4vw,4vh),38px);color:#ffc069;
  font-family:var(--script);line-height:1.1;margin:0;
  text-shadow:0 2px 14px rgba(50,10,0,.65)}
.railnames .rrule{display:flex;align-items:center;justify-content:center;gap:12px;
  margin:10px 0 8px;color:#ffd9a8;opacity:.85;
  filter:drop-shadow(0 2px 8px rgba(50,10,0,.6))}
.railnames .rrule i{width:clamp(30px,10vw,78px);height:1px;background:currentColor}
.railnames .rrule span{font-size:13px}
.railnames .rexp{font-family:var(--mono);font-size:clamp(10.5px,2.7vw,13px);letter-spacing:.3em;
  color:#fff3d8;text-shadow:0 1px 1px rgba(40,8,0,.95),0 0 6px rgba(40,8,0,.9)}
/* No glow behind the names. It muddied the sky whichever way it was tuned —
   warm or dark — and the drop-shadows on the type already do the separating. */

.gate .g-scroll{position:absolute;left:0;right:0;bottom:3.5%;text-align:center;z-index:6;
  font-family:var(--mono);font-size:9.5px;letter-spacing:.24em;color:#ffdcae;opacity:.85;
  text-shadow:0 2px 8px rgba(0,0,0,.8)}
.gate .g-chev{display:block;width:9px;height:9px;margin:9px auto 0;rotate:45deg;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  animation:gbob 1.9s ease-in-out infinite}
@keyframes gbob{0%,100%{transform:translateY(-3px);opacity:.45}50%{transform:translateY(3px);opacity:1}}

/* the top bar must not pop in at the handover — it arrives softly, after */
.topbar{opacity:1;transition:opacity .7s ease .35s}
body.intro .topbar{opacity:0;transition:none}

/* ------------------------------------------------------- NTES status board */
.board-wrap{position:relative;z-index:2}
/* The live status board. It used to be a green CRT terminal, which was the
   one thing on the page that did not belong to the wedding palette — this is
   the same information on an enamelled brass departure board: deep navy
   ground, brass rules, cream type, amber lamps. */
.ntes{background:linear-gradient(180deg,#16233c,#0d1626 55%,#091120);
  border:1px solid rgba(201,169,97,.32);border-radius:14px;overflow:hidden;
  box-shadow:0 26px 60px rgba(9,17,32,.42),inset 0 1px 0 rgba(255,255,255,.07);
  position:relative}
.ntes::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:.16;
  background:repeating-linear-gradient(180deg,rgba(0,0,0,.5) 0 1px,transparent 1px 3px)}
.ntes-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:11px 16px;background:rgba(201,169,97,.09);
  border-bottom:1px solid rgba(201,169,97,.26)}
.ntes-top .tag{font-family:var(--mono);font-size:10px;letter-spacing:.22em;color:#e0b45c}
.ntes-top .live{margin-left:auto;display:inline-flex;align-items:center;gap:6px;
  font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:#f0dcae}
.ntes-top .dot{width:7px;height:7px;border-radius:50%;background:#ffc864;
  box-shadow:0 0 10px #ffc864;animation:blink 1.6s ease-in-out infinite}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.25}}
.ntes-body{padding:16px;color:#f0dcae;font-family:var(--mono)}
.ntes-name{font-size:clamp(16px,4.6vw,26px);letter-spacing:.18em;color:#ffe9b8;
  text-shadow:0 0 16px rgba(232,201,138,.35)}
.ntes-no{font-size:12px;letter-spacing:.3em;color:#c2a96d;margin-top:2px}
.ntes-state{display:inline-block;margin-top:10px;padding:4px 12px;
  border:1px solid rgba(134,199,154,.5);border-radius:4px;
  font-size:12px;letter-spacing:.2em;color:#9fd9b4;background:rgba(134,199,154,.08)}
.ntes-updated{font-size:10px;letter-spacing:.14em;color:#9a8458;margin-top:10px}

/* split-flap countdown */
.flaps{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-top:16px}
.flap{font-family:var(--mono);min-width:66px;border-radius:8px;
  background:linear-gradient(180deg,#1d2c47,#121c30);
  border:1px solid rgba(201,169,97,.28);
  padding:9px 6px 7px;text-align:center;position:relative;overflow:hidden}
.flap::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:rgba(0,0,0,.45)}
.flap b{display:block;font-size:clamp(20px,6vw,30px);line-height:1;color:#fff3d4;
  letter-spacing:.04em;font-weight:400}
.flap span{display:block;font-size:8.5px;letter-spacing:.2em;color:#c2a96d;margin-top:6px}
/* The old flip rotated the digit to -88deg, i.e. almost edge-on. The seconds
   cell changes every second and the animation ran for .38s of it, so for over
   a third of every second the number was an unreadable sliver. A short drop
   and squash reads as a falling flap and never stops being legible. */
.flap.tick b{animation:flip .26s cubic-bezier(.3,.9,.4,1)}
@keyframes flip{0%{transform:translateY(-42%) scaleY(.55);opacity:.4}
                100%{transform:none;opacity:1}}
.ntes-line{font-size:11px;letter-spacing:.14em;color:#e0b45c;margin-top:14px;text-align:center}

/* route strip inside the board */
/* The timeline scrolls sideways when the stops do not fit. The edges fade
   via a mask rather than a gradient overlay, so the fade matches the board's
   own background exactly instead of being a guessed colour — and the widths
   are driven by variables the script sets, so a side only fades when there
   is actually more timeline that way. */
.route{--fl:0px;--fr:0px;
  display:flex;align-items:flex-start;gap:0;margin-top:10px;overflow-x:auto;
  overflow-y:hidden;padding-bottom:8px;
  /* No scroll-behavior:smooth and no touch-action override here. Smooth
     would fight the per-frame scrollLeft the script writes, and a
     touch-action rule would take away either the page's vertical scroll or
     the timeline's horizontal one — the default lets the browser decide per
     gesture, which is exactly what is wanted. */
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;scrollbar-color:rgba(201,169,97,.65) rgba(201,169,97,.14);
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 var(--fl),
    #000 calc(100% - var(--fr)),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 var(--fl),
    #000 calc(100% - var(--fr)),transparent 100%)}
.route::-webkit-scrollbar{height:6px}
.route::-webkit-scrollbar-track{background:rgba(201,169,97,.14);border-radius:3px}
.route::-webkit-scrollbar-thumb{background:rgba(201,169,97,.65);border-radius:3px}
.route:focus-visible{outline:2px solid rgba(201,169,97,.7);outline-offset:3px}
.route-hint{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;
  color:#9a8458;margin-top:14px;text-align:center}
.route-hint[hidden]{display:none}
.route .stop{flex:1 0 96px;text-align:center;position:relative;padding-top:20px}
.route .stop::before{content:"";position:absolute;top:7px;left:0;right:0;height:2px;
  background:rgba(201,169,97,.3)}
.route .stop:first-child::before{left:50%} .route .stop:last-child::before{right:50%}
.route .pip{position:absolute;top:2px;left:50%;translate:-50% 0;width:11px;height:11px;
  border-radius:50%;background:#0d1626;border:2px solid rgba(201,169,97,.55)}
.route .stop.done .pip{background:#c9a961;border-color:#e8c98a;
  box-shadow:0 0 10px rgba(232,201,138,.65)}
/* the destination reads as a cleared signal: green, and the only pip that
   blinks, so it stands apart from the gold ones already passed */
.route .stop.last .pip{background:#2fd65f;border-color:#9cf0b8;
  box-shadow:0 0 12px rgba(47,214,95,.85);animation:blink 1.8s ease-in-out infinite}
.route .code{font-size:11px;letter-spacing:.14em;color:#c2a96d}
.route .nm{font-size:11px;color:#ffe9b8;margin-top:3px;line-height:1.3;
  letter-spacing:.06em}
.route .milestone{font-size:9px;color:#9a8458;margin-top:2px;line-height:1.3;
  font-style:italic;letter-spacing:.02em}
@media (max-width:560px){
  /* 96px could not hold "Ponnu Paakuradhu" without cramping it, so each stop
     gets a wider fixed track and the row scrolls instead. Names still wrap
     freely onto as many lines as they need. */
  .route .stop{flex:0 0 132px;padding-left:4px;padding-right:4px}
  .route .code{font-size:12px}
  .route .nm{font-size:12.5px}
  .route .milestone{font-size:10px}
}

/* ------------------------------------------------------ window-seat parallax */
.window-sec{background:linear-gradient(180deg,#0e2c50,#123a63)}
.window-sec .sec-head h2{color:#8fc7ff}
.window-sec .sec-head p{color:#a9c6e4}
.window-sec .sec-head::after{background:repeating-linear-gradient(90deg,#8fc7ff 0 8px,transparent 8px 14px)}
.coachwall{position:relative;border-radius:20px;padding:clamp(14px,3vw,26px);
  background:linear-gradient(180deg,#2a6fb0,#1b4c86 42%,#0e2c50);
  box-shadow:inset 0 2px 0 rgba(255,255,255,.18),0 30px 60px rgba(0,0,0,.35)}
.win{position:relative;height:clamp(230px,44vw,340px);border-radius:14px;overflow:hidden;
  border:7px solid #0a2540;background:#bfe0ff;
  box-shadow:inset 0 0 0 3px rgba(255,255,255,.25),inset 0 22px 44px rgba(0,0,0,.18)}
.win .w-sky{position:absolute;inset:0;
  background:linear-gradient(#a7d8ff 0%,#d8ecff 52%,#ffe7bd 100%)}
.win .w-sun{position:absolute;top:14%;right:12%;width:56px;height:56px;border-radius:50%;
  background:radial-gradient(circle,#fff6cf,#ffd066 60%,rgba(255,208,102,0) 72%)}
.win .w-layer{position:absolute;left:0;bottom:0;width:300%;height:100%;will-change:transform}
.win .w-layer svg{width:33.333%;height:100%;display:block;float:left}
.win .w-hills{bottom:18%;height:44%;opacity:.5;filter:hue-rotate(-14deg) saturate(.8)}
.win .w-palms{bottom:16%;height:34%;opacity:.9}
.win .w-ground{position:absolute;left:0;right:0;bottom:0;height:17%;
  background:linear-gradient(#6f8a4a,#40542c)}
.win .w-poles{position:absolute;left:0;right:0;bottom:16%;height:40%;
  background-repeat:repeat-x;background-size:220px 100%;opacity:.55;will-change:background-position;
  background-image:linear-gradient(90deg,transparent 0 6px,#123 6px 9px,transparent 9px 220px)}
/* the photo strip sliding past the glass */
.win .w-strip{position:absolute;top:0;bottom:0;left:0;display:flex;
  width:max-content;will-change:transform}
.win .w-strip picture{display:block;height:100%}
.win .w-strip img{display:block;height:100%;width:auto}

/* Light falls from the right-hand side: 242deg is 118deg mirrored about the
   vertical axis (360 - 118), so the band keeps its slant but the bright stop
   — which sits at the START of the gradient line — lands in the top-right
   corner instead of the top-left. */
.win .w-glare{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(242deg,rgba(255,255,255,.34) 0 14%,transparent 30% 100%)}
.win .w-bars{position:absolute;inset:0;pointer-events:none}
.win .w-bars i{position:absolute;top:0;bottom:0;width:6px;background:rgba(10,37,64,.85);
  box-shadow:0 0 0 1px rgba(255,255,255,.15)}

.storycard{margin-top:18px;background:var(--paper);border-radius:12px;padding:16px 18px;
  box-shadow:0 14px 34px rgba(0,0,0,.3);position:relative}
.storycard .st-code{position:absolute;top:-11px;left:18px;background:var(--rail);color:#fff;
  font-family:var(--mono);font-size:10px;letter-spacing:.16em;padding:3px 9px;border-radius:4px}
.storycard .st-when{font-family:var(--mono);font-size:10px;letter-spacing:.18em;color:#8a7850}
.storycard h3{margin:4px 0 6px;font-size:clamp(20px,5vw,27px);color:var(--rail)}
.storycard p{margin:0;font-size:clamp(15px,3.6vw,17px);line-height:1.5;color:#4e453a}
.wphoto{margin:18px 0 0;background:var(--paper);padding:11px 11px 0;border-radius:10px;
  box-shadow:0 16px 38px rgba(0,0,0,.36);rotate:-.7deg}
.wphoto img{width:100%;height:auto;border-radius:5px;display:block;background:#123a63}
.wphoto figcaption{font-family:var(--script);font-size:24px;color:#4e453a;
  text-align:center;padding:8px 4px 12px;line-height:1.1}

.storynav{display:flex;gap:8px;justify-content:center;margin-top:14px;flex-wrap:wrap}
.storynav button{width:11px;height:11px;padding:0;border-radius:50%;cursor:pointer;
  border:2px solid rgba(255,255,255,.55);background:transparent}
.storynav button.on{background:#ffd166;border-color:#ffd166}

/* ------------------------------------------------------------- coach events */
.rake{display:flex;gap:14px;overflow-x:auto;padding:8px 4px 22px;scroll-snap-type:x mandatory;
  scrollbar-width:thin}
.rake::-webkit-scrollbar{height:7px}
.rake::-webkit-scrollbar-thumb{background:var(--brass-lo);border-radius:9px}
.coach{flex:0 0 min(300px,82vw);scroll-snap-align:center;cursor:pointer;border:0;padding:0;
  background:none;text-align:left;transition:transform .3s cubic-bezier(.2,.8,.3,1)}
.coach:hover,.coach:focus-visible{transform:translateY(-6px)}
.coach .shell{border-radius:14px 14px 8px 8px;padding:14px 16px 16px;color:#eaf2ff;position:relative;
  background:linear-gradient(180deg,#2a6fb0,#1b4c86 45%,#0e2c50);
  box-shadow:inset 0 2px 0 rgba(255,255,255,.22),0 16px 34px rgba(18,58,99,.28)}
.coach[data-kind="loco"] .shell{background:linear-gradient(180deg,#b8392c,#8e2620 45%,#511513);
  box-shadow:inset 0 2px 0 rgba(255,255,255,.22),0 16px 34px rgba(142,38,32,.3)}
.coach .code{position:absolute;top:10px;right:12px;font-family:var(--mono);font-size:10px;
  letter-spacing:.2em;background:rgba(0,0,0,.3);padding:3px 8px;border-radius:4px}
.coach .windows{display:flex;gap:6px;margin-bottom:12px}
.coach .windows i{flex:1;height:26px;border-radius:4px;
  background:linear-gradient(#2b4460,#16293f);box-shadow:inset 0 1px 0 rgba(255,255,255,.2)}
.coach h3{margin:0;font-size:clamp(19px,4.6vw,24px)}
.coach .when{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;opacity:.85;margin-top:5px}
.coach .bogies{display:flex;justify-content:space-between;padding:0 24px;margin-top:-4px}
.coach .bogies i{width:44px;height:13px;border-radius:0 0 6px 6px;background:#1a1d22}
.coach .wheels{display:flex;justify-content:space-between;padding:0 26px}
.coach .wheels i{width:17px;height:17px;border-radius:50%;background:#20242b;
  border:3px solid #444c57;animation:roll 2.4s linear infinite}
@keyframes roll{to{transform:rotate(360deg)}}

/* event detail sheet */
.sheet{position:fixed;inset:0;z-index:60;display:none;place-items:center;padding:18px;
  background:rgba(6,14,24,.68);backdrop-filter:blur(4px)}
.sheet.open{display:grid}
.sheet .card{background:var(--paper);border-radius:16px;padding:24px 22px;width:min(440px,100%);
  box-shadow:0 30px 70px rgba(0,0,0,.5);animation:pop .32s cubic-bezier(.2,.9,.3,1)}
@keyframes pop{from{transform:translateY(18px) scale(.96);opacity:0}to{transform:none;opacity:1}}
.sheet .code{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:#8a7850}
.sheet h3{margin:6px 0 4px;font-size:29px;color:var(--rail)}
.sheet .when{font-family:var(--mono);font-size:12px;letter-spacing:.1em;color:var(--red)}
.sheet p{margin:12px 0 0;line-height:1.55;color:#4e453a}
.sheet .acts{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap}

/* -------------------------------------------------------------- the venue */
.venue-sec{background:linear-gradient(180deg,#fdf8ee,#f4ead6)}
/* ================= the platform ==========================================
   The venue isn't on a card, it's on the station name board — so the whole
   section is built as a lit platform at dusk: festoon lights strung overhead,
   two lamp posts, the yellow IR name board on its posts, and the platform
   floor with its warning line. The scratch foil sits inside the board. */
/* .pform, not .platform: the opening overlay already owns that class for the
   station platform the train pulls into, and an unscoped rule here would
   reach into it. overflow:hidden keeps the festoon's -4% bleed and the
   floor's negative margins from widening the page. */
/* =========================================================== the platform ==
   The supplied illustration, used as the asset. Nothing here redraws it: the
   only thing on top is the yellow board, placed by percentage of the picture
   so it stays locked to the painted sign at any width.

   Those four numbers were measured off the artwork, not guessed — the sign's
   yellow field sits at x 551-1214, y 380-615 of the 1774x755 crop.
   ========================================================================= */
.pform{position:relative;overflow:hidden;border-radius:16px;isolation:isolate;
  box-shadow:0 26px 56px rgba(40,26,10,.3);
  /* A rub that starts on the artwork must not become a native image drag or
     a text selection — either one steals the pointer mid-stroke and leaves
     the scratch half done. */
  -webkit-user-select:none;user-select:none}
/* the illustration is scenery: it never needs to receive a pointer, and with
   pointer-events off the browser has nothing to start a drag from */
.pform .ps-art,.pform .ps-art img{-webkit-user-drag:none;user-drag:none;
  pointer-events:none}
.pform .ps-air{pointer-events:none}
/* image and board live in one stage so they can be zoomed together — the
   board is a percentage of the stage, so any scaling keeps it locked to the
   painted sign */
.ps-stage{position:relative;width:100%}
.ps-art{display:block}
.ps-art img{display:block;width:100%;height:auto}
.festoon{display:none}

/* ---- ambience over the illustration ------------------------------------
   The picture is untouched; these are three translucent layers on top of it
   so the platform feels lit and alive rather than flat. All of it pauses for
   prefers-reduced-motion. */
.ps-air{position:absolute;inset:0;pointer-events:none;z-index:2;overflow:hidden}
.ps-air i,.ps-air span{position:absolute}

/* morning light coming down the platform from the far end */
.ps-rays{inset:0;mix-blend-mode:screen;opacity:.5;
  background:
    linear-gradient(197deg,rgba(255,228,170,0) 42%,rgba(255,228,170,.5) 50%,
                    rgba(255,228,170,0) 57%),
    linear-gradient(203deg,rgba(255,236,190,0) 56%,rgba(255,236,190,.32) 62%,
                    rgba(255,236,190,0) 68%),
    radial-gradient(42% 60% at 72% 36%,rgba(255,221,150,.5),rgba(255,221,150,0) 70%);
  animation:psbreathe 9s ease-in-out infinite}
@keyframes psbreathe{0%,100%{opacity:.46}50%{opacity:.62}}

/* depth at the edges */
.ps-vig{inset:0;
  background:radial-gradient(118% 92% at 50% 42%,
             rgba(38,22,6,0) 52%,rgba(38,22,6,.3) 100%)}

/* dust catching the light */
.ps-dust{inset:0}
.ps-dust i{width:3px;height:3px;border-radius:50%;
  background:rgba(255,241,205,.85);box-shadow:0 0 5px rgba(255,226,160,.8);
  animation:psdrift linear infinite}
@keyframes psdrift{
  0%{transform:translate3d(0,22px,0);opacity:0}
  14%{opacity:.85}
  82%{opacity:.6}
  100%{transform:translate3d(16px,-78px,0);opacity:0}}
.ps-dust i:nth-child(1){left:18%;top:62%;animation-duration:15s;animation-delay:0s}
.ps-dust i:nth-child(2){left:31%;top:70%;animation-duration:19s;animation-delay:2.4s}
.ps-dust i:nth-child(3){left:44%;top:58%;animation-duration:13s;animation-delay:5.1s}
.ps-dust i:nth-child(4){left:57%;top:73%;animation-duration:21s;animation-delay:1.3s}
.ps-dust i:nth-child(5){left:66%;top:60%;animation-duration:17s;animation-delay:7.2s}
.ps-dust i:nth-child(6){left:76%;top:68%;animation-duration:14s;animation-delay:3.8s}
.ps-dust i:nth-child(7){left:86%;top:56%;animation-duration:20s;animation-delay:6.4s}
.ps-dust i:nth-child(8){left:9%;top:66%;animation-duration:16s;animation-delay:9s}

/* a phone has less to gain and more to lose from eight animated motes */
@media (max-width:620px){
  .ps-dust{display:none}
  .ps-rays{opacity:.36;animation:none}
}
@media (prefers-reduced-motion:reduce){
  .ps-rays{animation:none}
  .ps-dust i{animation:none;opacity:.5}
}

/* the scratchable sign, aligned to the painted one */
/* The painted sign, measured edge to edge. Profiling a row shows, going in
   from the left post: post 505-539, yellow 540-547, KEYLINE 548-550, then the
   main field 551-1214, KEYLINE 1215-1218, yellow 1219-1224, right post 1225+.
   Covering only the main field left those two keylines showing as black
   vertical lines beside the posts — the same mistake as the top and bottom
   lines, which I had fixed on one axis only. This spans 540-1223: the whole
   yellow including both keylines, stopping 1px short of each post. */
.board-holder{position:absolute;left:30.440%;top:31.523%;
  width:38.557%;height:33.775%;z-index:3;container-type:inline-size}

/* the enamel face. It covers the venue text painted into the artwork so the
   live HTML underneath is what guests actually read — crisp at any zoom, and
   it follows the Tamil toggle. */
/* The board is rectangular: its yellow field holds a constant x 551-1214 for
   effectively its whole height. An earlier reading mistook the corner radius
   at the extreme top and bottom rows for perspective, and clipping to a
   trapezoid gave the overlay slanted edges that did not match the painted
   sign. A rectangle with the same corner softening is what fits.
   The sign's own posts sit at 28.5-30.4% and 69.1-70.9% of the artwork, so
   this window clears both. */
.scratch{position:relative;width:100%;height:100%;overflow:hidden;
  background:#fabb07;border-radius:4px}
.scratch::before{content:"";position:absolute;inset:0;pointer-events:none;z-index:1;
  background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,0) 46%,
                             rgba(92,62,6,.04))}
.scratch .under{position:absolute;inset:0;display:grid;align-content:center;
  justify-items:center;text-align:center;padding:2% 4%;gap:0}
/* The second declaration wins where container units are supported; the clamp
   before it is the fallback. Sizing off the viewport drifted away from the
   board — the board is a share of the picture, not of the window — which
   clipped the Tamil line and pushed the location off the sign entirely. */
.scratch .under .vn-ta{font-family:var(--tamil);font-weight:700;color:#120c04;
  font-size:clamp(9px,2vw,24px);font-size:5.5cqw;line-height:1.18}
.scratch .under .vn{font-family:system-ui,"Segoe UI",Arial,sans-serif;font-weight:800;
  font-size:clamp(11px,2.3vw,29px);font-size:7.8cqw;letter-spacing:.005em;color:#0b0801;
  line-height:1.02;text-transform:uppercase}
.scratch .under .va{font-family:system-ui,"Segoe UI",Arial,sans-serif;font-weight:700;
  font-size:clamp(7px,1.4vw,16px);font-size:3.8cqw;letter-spacing:.06em;color:#241a04;
  text-transform:uppercase;line-height:1.2}
/* touch-action:none lives HERE and nowhere else -- on the foil itself, not
   on the board, the section or the page. A finger that lands on the foil is
   handed to the script immediately, so a rub is never stolen by a page pan
   that has already begun (no browser lets you cancel one of those). Every
   other pixel of the page, the artwork around the board included, scrolls
   exactly as before; and once `.done` sets pointer-events:none below, the
   canvas leaves hit testing entirely and the revealed board scrolls too. */
.scratch canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;z-index:2;
  touch-action:none}
.scratch.done canvas{opacity:0;transition:opacity .6s ease;pointer-events:none}
/* the reveal glints once, like sun catching the enamel */
.scratch.done::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:5;
  background:linear-gradient(112deg,transparent 40%,rgba(255,255,255,.75) 50%,transparent 60%);
  /* forwards matters: without it the sweep snaps back to translateX(0) when
     the animation ends and leaves a permanent white band across the board */
  animation:glint .9s ease-out forwards}
@keyframes glint{from{transform:translateX(-120%)}to{transform:translateX(120%)}}

@media (max-width:620px){
  /* At phone width the painted sign is only about 128px across, too small for
     three lines. Zooming the stage and letting .pform crop the edges keeps the
     sign readable; the board rides along because it is positioned in percent. */
  .ps-stage{width:152%;margin-left:-26%}
}

.scratch-cap{text-align:center;font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;
  color:#8a7850;margin-top:26px}
.venue-acts{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:18px}
.btn{display:inline-flex;align-items:center;gap:8px;cursor:pointer;border-radius:999px;
  text-decoration:none;
  font-family:var(--mono);font-size:12px;letter-spacing:.14em;padding:12px 22px;border:0;
  background:linear-gradient(#f3d668,#d9a939);color:#2a1e07;
  box-shadow:0 10px 24px rgba(169,131,47,.34);transition:transform .2s ease,box-shadow .2s ease}
.btn:hover{transform:translateY(-2px);box-shadow:0 14px 30px rgba(169,131,47,.42)}
.btn.ghost{background:transparent;color:var(--rail);border:1px solid var(--line);box-shadow:none}
.btn.ghost:hover{background:#fff;border-color:var(--brass-lo)}
.btn[disabled]{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none}

/* ------------------------------------------------------------- contacts */
/* ------------------------------------------------- the enquiry booth ---
   A station enquiry window: a navy board with a lit lamp across the top of a
   cream counter, and the contacts below it as brass-edged plaques. Five of
   them, so the grid is told to fit as many 210px tracks as it can rather
   than being given a column count that would strand one card on its own row
   at some widths. */
.booth{position:relative;border-radius:16px;padding:clamp(10px,2vw,16px);
  background:linear-gradient(180deg,#fffdf6,#f3ead6);
  border:1px solid rgba(169,131,47,.4);
  box-shadow:0 18px 44px rgba(60,40,15,.16),inset 0 1px 0 #fff}
/* the counter's wooden lip */
.booth::after{content:"";position:absolute;left:0;right:0;bottom:0;height:7px;
  border-radius:0 0 16px 16px;
  background:repeating-linear-gradient(90deg,#8b5e34 0 9px,#7a5230 9px 18px);
  opacity:.5}
.booth-board{display:flex;align-items:center;justify-content:center;gap:10px;
  background:linear-gradient(180deg,var(--rail-lo),var(--rail));
  color:#ffe9b8;font-family:var(--mono);letter-spacing:.24em;
  font-size:clamp(10px,2.2vw,12px);padding:8px 12px;border-radius:8px;
  margin-bottom:clamp(10px,1.8vw,14px);text-align:center;
  box-shadow:0 8px 20px rgba(10,37,64,.3),inset 0 0 0 1px rgba(201,169,97,.5)}
.booth-lamp{width:9px;height:9px;border-radius:50%;flex:none;background:#ffd166;
  box-shadow:0 0 10px rgba(255,209,102,.9);animation:blink 2.4s ease-in-out infinite}

/* Five cards, so an auto-fit grid strands the fifth on a row of its own at
   most desktop widths. Flex with a third-width basis gives 3 + 2 instead,
   and `justify-content:center` keeps that short last row balanced under the
   first. The min-width is what drops it to 2-up and then 1-up: once a third
   of the row is narrower than a phone number needs, the items stop fitting
   three abreast on their own. */
.contacts{display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(8px,1.3vw,11px)}
.contact{flex:0 1 calc(33.333% - 7.34px);min-width:250px;max-width:100%}
/* one per row once three no longer fit, filling the width rather than
   sitting narrow and centred with the booth empty either side */
@media (max-width:760px){ .contact{flex:1 1 100%;min-width:0} }
/* The plaque reads as two columns: the text stacks down the left and the
   Call button sits beside it, spanning whatever rows the text happens to
   need — which is three for the card that carries a name and two for the
   rest. Stacking the button underneath instead made every card half again
   as tall for no extra information. */
.contact{background:linear-gradient(180deg,#fff,#fdf8ec);
  border:1px solid rgba(169,131,47,.45);border-radius:10px;
  padding:9px 11px 10px 14px;
  display:grid;grid-template-columns:1fr auto;grid-auto-rows:min-content;
  align-items:center;column-gap:10px;
  position:relative;overflow:hidden;
  box-shadow:0 4px 12px rgba(60,40,15,.1),inset 0 1px 0 #fff}
.contact .role,.contact .nm,.contact .tel{grid-column:1}
.contact .btn.call{grid-column:2;grid-row:1/-1;align-self:center}
/* the plaque's painted edge — navy for the groom's side, burgundy for the
   bride's, matching the two colours used on the signals elsewhere */
.contact::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;
  background:var(--rail-hi)}
.contact[data-side="bride"]::before{background:var(--red)}
.contact .role{font-family:var(--mono);font-size:9px;letter-spacing:.16em;
  color:#8a7850;text-transform:uppercase}
.contact .nm{font-size:17px;color:var(--rail);line-height:1.2;font-weight:600}
.contact .tel{font-family:var(--mono);font-size:16px;
  letter-spacing:.05em;color:var(--ink);text-decoration:none;margin-top:1px;
  display:block;white-space:nowrap}
.contact .tel:hover{color:var(--rail)}
.contact .btn.call{padding:8px 15px;font-size:11px;gap:6px;min-height:38px;
  box-shadow:0 5px 14px rgba(169,131,47,.3)}

/* ------------------------------------------------------------------ RSVP */
.rsvp-sec{background:linear-gradient(180deg,#123a63,#0e2c50)}
.rsvp-sec .sec-head h2{color:#8fc7ff}
.rsvp-sec .sec-head p{color:#a9c6e4}
.rsvp-sec .sec-head::after{background:repeating-linear-gradient(90deg,#8fc7ff 0 8px,transparent 8px 14px)}
.slip{--r:8px;background:var(--paper);border-radius:12px;padding:22px;width:min(560px,100%);
  margin-inline:auto;filter:drop-shadow(0 22px 44px rgba(0,0,0,.4));
  -webkit-mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 26px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 26px repeat-y;
  -webkit-mask-composite:source-in;
  mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 26px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 26px repeat-y;
  mask-composite:intersect}
.field{margin-bottom:13px}
.field label{display:block;font-family:var(--mono);font-size:9.5px;letter-spacing:.17em;
  color:#8a7850;margin-bottom:5px}
.field input,.field select,.field textarea{width:100%;border:1px solid var(--line);
  border-radius:8px;padding:11px 12px;font:inherit;font-size:16px;background:#fffdf7;color:var(--ink)}
.field textarea{resize:vertical;min-height:72px}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--brass-lo);
  outline-offset:1px;border-color:transparent}
.two{display:grid;gap:13px;grid-template-columns:1fr 1fr}
.rsvp-done{text-align:center;padding:10px 0;position:relative}
.rsvp-done .tick{font-size:42px;line-height:1;color:#2a7f4c}
.rsvp-done p{margin:8px 0 14px;color:#4e453a}
/* the inked counter stamp, struck slightly off-square the way a real one is */
.rsvp-done .stamp{position:absolute;right:2px;top:-4px;rotate:-13deg;
  font-family:var(--mono);font-size:12px;letter-spacing:.18em;color:#9c3326;
  border:2px solid #9c3326;border-radius:5px;padding:5px 9px;opacity:.6;
  pointer-events:none}

/* --------------------------------------------- the reservation ticket ---
   Shared furniture for both slips: a printed head rule, a route strip and a
   line of counter detail. It lives outside the <form> so the ticket still
   reads as a ticket once the form is swapped for the confirmation. */
.tkt-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:10px;flex-wrap:wrap;
  border-bottom:2px solid var(--rail);padding-bottom:7px;margin-bottom:10px}
.tkt-brand{font-family:var(--mono);font-size:clamp(9px,2vw,10.5px);
  letter-spacing:.2em;color:var(--rail)}
.tkt-no{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;color:#8a7850}
.tkt-no b{color:var(--ink);margin-left:5px;letter-spacing:.1em}

.tkt-route{display:flex;align-items:center;gap:clamp(8px,2vw,14px);
  padding:9px 0 10px;border-bottom:1px dashed rgba(36,29,22,.3);margin-bottom:10px}
.tkt-stn{display:flex;flex-direction:column;line-height:1.15;
  font-family:var(--mono);font-size:9px;letter-spacing:.16em;color:#8a7850}
.tkt-stn b{font-family:var(--serif);font-size:clamp(17px,3.6vw,21px);
  letter-spacing:.04em;color:var(--rail);font-weight:600}
.tkt-stn.to{text-align:right;margin-left:auto}
/* the arrow is drawn rather than typed so it keeps its weight at any size */
.tkt-arrow{flex:1;height:2px;position:relative;min-width:30px;
  background:repeating-linear-gradient(90deg,var(--brass-lo) 0 7px,transparent 7px 12px)}
.tkt-arrow::after{content:"";position:absolute;right:0;top:50%;translate:0 -50%;
  border:5px solid transparent;border-left-color:var(--brass)}

.tkt-meta{display:flex;align-items:center;justify-content:space-between;
  gap:10px;flex-wrap:wrap;margin-bottom:14px;
  font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;color:var(--ink)}
.tkt-meta i{font-style:normal;color:#8a7850;margin-right:6px;letter-spacing:.16em}
.tkt-cls{border:1px solid var(--brass-lo);border-radius:4px;padding:3px 8px;
  color:var(--rail);background:rgba(201,169,97,.12)}

/* the register's head rule is the same furniture in a warmer key */
.slip.reg{margin-bottom:22px}
.slip.reg .tkt-head{border-bottom-color:var(--brass)}
.slip.reg .tkt-brand{color:#7a4a2a}
/* ruled paper under the wish, so writing on it reads as pen on a register */
.slip.reg textarea{min-height:92px;line-height:26px;padding-top:9px;
  background-image:repeating-linear-gradient(180deg,
    transparent 0 25px,rgba(36,29,22,.12) 25px 26px);
  background-attachment:local}

/* ------------------------------------------------------- passenger log */
.log-sec{background:
  linear-gradient(rgba(120,80,40,.06),rgba(120,80,40,.06)),
  repeating-linear-gradient(46deg,#c8a476 0 3px,#c09a6a 3px 6px)}
/* the notice board the stubs hang on: a brass rail across the top, so the
   tickets read as hung from it rather than floating on the panel */
.board-cork{position:relative;background:rgba(255,253,246,.14);border-radius:16px;
  padding:clamp(22px,3vw,28px) clamp(12px,2vw,18px) clamp(14px,2vw,18px);
  box-shadow:inset 0 0 0 2px rgba(90,58,25,.3),0 14px 34px rgba(60,35,10,.18)}
.cork-rail{position:absolute;left:clamp(12px,2vw,18px);right:clamp(12px,2vw,18px);
  top:11px;height:3px;border-radius:3px;
  background:linear-gradient(180deg,#e2c489,#a9832f);
  box-shadow:0 1px 3px rgba(0,0,0,.35)}
/* The wishes run sideways rather than down the page: with a hundred of them
   a vertical board put several screens of scrolling between the form and the
   photo booth below. A horizontal strip keeps the section a fixed height
   however many wishes arrive, and `grid-auto-flow:column` fills three rows
   top to bottom before starting the next column, so one swipe brings three
   new wishes into view instead of one.

   `overflow-x:auto` alone does not fight the page: a vertical swipe still
   scrolls the document, because the browser only claims the gesture for this
   element once it is predominantly horizontal. So no touch-action override
   is needed here, and adding one would break vertical scrolling over the
   strip. */
.stubs{display:grid;grid-auto-flow:column;grid-template-rows:repeat(3,auto);
  grid-auto-columns:clamp(228px,74vw,286px);
  gap:14px;overflow-x:auto;overflow-y:hidden;
  padding:6px 2px 16px;scroll-snap-type:x proximity;scroll-padding-left:2px;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;scrollbar-color:rgba(169,131,47,.8) rgba(90,58,25,.18)}
.stubs::-webkit-scrollbar{height:8px}
.stubs::-webkit-scrollbar-track{background:rgba(90,58,25,.18);border-radius:4px}
.stubs::-webkit-scrollbar-thumb{background:rgba(169,131,47,.8);border-radius:4px}
.stubs::-webkit-scrollbar-thumb:hover{background:var(--brass)}
.stub{--r:7px;scroll-snap-align:start;
  background:var(--paper);padding:14px 16px 16px;
  border-radius:8px;position:relative;filter:drop-shadow(0 10px 20px rgba(60,35,10,.32));
  -webkit-mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 22px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 22px repeat-y;
  -webkit-mask-composite:source-in;
  mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 22px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 22px repeat-y;
  mask-composite:intersect;
  animation:pin .5s cubic-bezier(.2,.9,.3,1) both}
@keyframes pin{from{opacity:0;transform:translateY(-10px) rotate(-3deg)}to{opacity:1;transform:none}}
/* each stub is a ticket with its counterfoil torn down the left: the dashed
   rule is the tear line, and the foil carries the coupon number and date */
.stub{display:flex;align-items:stretch;padding:0}
.stub-foil{flex:none;width:34px;display:flex;flex-direction:column;
  align-items:center;justify-content:space-between;gap:8px;
  padding:13px 0;background:rgba(169,131,47,.11);
  border-right:1px dashed rgba(36,29,22,.35)}
.stub-no,.stub-date{font-family:var(--mono);font-size:9px;letter-spacing:.12em;
  color:#8a7850;writing-mode:vertical-rl;rotate:180deg;white-space:nowrap}
.stub-no{color:#7a4a2a}
.stub-body{flex:1;min-width:0;padding:14px 16px 15px}
.stub .msg{font-size:16px;line-height:1.5;color:#3f382f;overflow-wrap:break-word}
.stub .by{margin-top:10px;font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:#8a7850;
  border-top:1px dashed rgba(0,0,0,.2);padding-top:8px;overflow-wrap:break-word}
.log-empty{text-align:center;color:#4a2f14;font-style:italic;padding:22px 20px;
  font-size:17px}

/* ------------------------------------------------------------ the gallery */
.gal-sec{background:linear-gradient(180deg,#0e2c50,#07131f)}
.gal-sec .sec-head h2{color:#8fc7ff}
.gal-sec .sec-head p{color:#a9c6e4}
.gal-sec .sec-head::after{background:repeating-linear-gradient(90deg,#8fc7ff 0 8px,transparent 8px 14px)}
/* ======================= the luggage coach ==============================
   A parcel van standing at the platform: navy body, a vintage maroon header,
   gold lining and lettering, two sliding doors across the middle and bogies
   underneath. All of it is CSS over the palette this section already uses --
   no new artwork and no photograph.

   The doors are what hides the gallery. While they are shut the notice and
   the countdown are painted ON them; when the clock runs out they slide out
   to either side and the photos are simply there behind them. */
.coach{position:relative;margin:0 auto;max-width:980px;
  filter:drop-shadow(0 24px 44px rgba(0,0,0,.45))}
.coach-roof{height:clamp(10px,2.4vw,16px);margin:0 clamp(6px,1.6vw,14px);
  border-radius:16px 16px 3px 3px;background:linear-gradient(180deg,#36414f,#222b36);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22)}
.coach-body{position:relative;border-radius:5px 5px 9px 9px;
  padding:clamp(10px,2.2vw,16px);background:linear-gradient(180deg,#154371,#0c2744);
  border:1px solid rgba(0,0,0,.55);
  box-shadow:inset 0 0 0 1px rgba(194,169,109,.34),inset 0 2px 0 rgba(255,255,255,.09)}

.coach-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:7px clamp(8px,2vw,13px);border-radius:3px;
  margin-bottom:clamp(10px,2.2vw,14px);
  background:linear-gradient(180deg,#8e2620,#5c1714);
  box-shadow:inset 0 0 0 1px rgba(194,169,109,.45),0 2px 6px rgba(0,0,0,.35)}
/* the van's own number plate, enamelled the way the station board is */
.coach-plate{display:inline-flex;align-items:center;gap:7px;padding:3px 9px;
  border:1px solid #a9832f;border-radius:3px;color:#123a63;
  background:linear-gradient(180deg,#fdf8ee,#e7dcc4);
  font-family:var(--mono);font-size:10px;letter-spacing:.14em}
.coach-plate i{font-style:normal;font-weight:700;color:#8e2620;
  border-left:1px solid rgba(169,131,47,.55);padding-left:7px}
.coach-brand{flex:1;text-align:center;font-family:var(--mono);color:#e8c98a;
  font-size:clamp(8.5px,1.7vw,11px);letter-spacing:.24em;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 0 rgba(0,0,0,.5)}
/* the coach-status lamp: red while the doors are shut, green once they open */
.coach-state{display:inline-flex;align-items:center;gap:7px;padding:4px 10px;
  border:1px solid rgba(253,248,238,.28);border-radius:999px;
  background:rgba(0,0,0,.3);color:#ffd9b0;
  font-family:var(--mono);font-size:9px;letter-spacing:.16em}
.coach-state i{width:8px;height:8px;border-radius:50%;background:#e2563f;
  box-shadow:0 0 0 2px rgba(226,86,63,.2),0 0 9px rgba(226,86,63,.9);
  animation:lamp 2.2s ease-in-out infinite}
.coach-state.open{color:#c9f3d4}
.coach-state.open i{background:#49d07d;
  box-shadow:0 0 0 2px rgba(73,208,125,.2),0 0 9px rgba(73,208,125,.9)}
@keyframes lamp{0%,100%{opacity:1}50%{opacity:.42}}

.doorway{position:relative;overflow:hidden;border-radius:4px;
  background:linear-gradient(180deg,#0a1a28,#071219);
  box-shadow:inset 0 0 0 2px rgba(0,0,0,.6),inset 0 0 0 3px rgba(194,169,109,.24),
             inset 0 14px 26px rgba(0,0,0,.5)}
/* While the doors are shut there is nothing to lay out behind them, so the
   doorway takes its height from the notice itself. No min-height to guess at,
   and nothing to clip on a narrow phone. */
.doorway:not(.open) .van-inside{display:none}
/* Padding only while shut. The doors are absolutely positioned against the
   padding box, so they still fill the opening edge to edge -- it is the
   notice that is inset, which leaves a clear band of bare door top and
   bottom for the grab rails. The padding goes when the doors do. */
.doorway:not(.open){padding:clamp(28px,6.5vw,44px) 0}
.van-inside{padding:clamp(10px,2.4vw,16px)}
.van-inside .shot{box-shadow:0 0 0 1px rgba(194,169,109,.3)}

.door{position:absolute;top:0;bottom:0;width:50.4%;z-index:2;
  background:repeating-linear-gradient(90deg,rgba(0,0,0,.17) 0 2px,transparent 2px 15px),
             linear-gradient(180deg,#9c2f26,#6d2019 58%,#4a1210);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55),inset 0 0 0 2px rgba(194,169,109,.4),
             inset 0 18px 30px rgba(255,255,255,.05),0 0 22px rgba(0,0,0,.45);
  transition:transform .95s cubic-bezier(.76,0,.24,1)}
.door.l{left:0}
.door.r{right:0}
.doorway.open .door.l{transform:translate3d(-100%,0,0)}
.doorway.open .door.r{transform:translate3d(100%,0,0)}
.door .grip{position:absolute;bottom:clamp(12px,3vw,18px);height:7px;
  width:clamp(40px,11vw,62px);border-radius:4px;
  background:linear-gradient(180deg,#e8c98a,#a9832f);
  box-shadow:0 2px 5px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.5)}
.door.l .grip{right:14px}
.door.r .grip{left:14px}

/* the notice rides on the doors: in flow, so it gives the doorway its height,
   and above them, so the doors never cover it */
.locked{position:relative;z-index:3;text-align:center;color:#ffe9c9;
  padding:clamp(20px,5vw,34px) clamp(12px,4vw,24px)}
/* Cream lettering over ribbed maroon is a fight neither side wins, so the
   notice gets its own plate. z-index:-1 keeps it behind its own text while
   staying inside .locked's stacking context, i.e. still above the doors. */
.locked::before{content:"";position:absolute;z-index:-1;
  inset:0 clamp(6px,2vw,14px);border-radius:6px;
  background:linear-gradient(180deg,rgba(9,20,33,.82),rgba(6,14,24,.88));
  box-shadow:inset 0 0 0 1px rgba(194,169,109,.38),0 10px 26px rgba(0,0,0,.5)}
.locked .padlock{display:inline-block;font-size:clamp(24px,6vw,32px);
  filter:drop-shadow(0 3px 6px rgba(0,0,0,.6))}
.locked h3{margin:10px 0 6px;font-family:var(--serif);font-weight:700;
  font-size:clamp(19px,5vw,27px);color:#ffd166}
.locked p{margin:0;font-size:clamp(13px,3.4vw,15.5px);color:#f0dcc0}
.locked .cd-label{margin-top:clamp(14px,3.4vw,20px);font-family:var(--mono);
  font-size:9px;letter-spacing:.22em;color:#e8c98a}
.locked .flaps{margin-top:9px}
.locked .opens-on{margin-top:12px;font-family:var(--mono);font-size:10px;
  letter-spacing:.14em;color:#cfb68d}
/* Once the doors part the notice leaves the flow at once -- fading it in
   place would hold the doorway open at its old height and the gallery would
   drop in underneath it. */
.doorway.open .locked{position:absolute;inset:0;opacity:0;transform:scale(.97);
  pointer-events:none;transition:opacity .5s ease,transform .5s ease}

.coach-foot{display:flex;align-items:center;justify-content:center;
  margin-top:clamp(10px,2.2vw,14px)}
/* the tie-on luggage label, as it would hang off the door rail */
.coach-tag{position:relative;display:inline-flex;align-items:center;gap:9px;
  padding:6px 13px 6px 23px;border-radius:3px;color:#5c1714;
  background:linear-gradient(180deg,#f4e6c6,#dbc698);border:1px solid #a9832f;
  box-shadow:0 4px 10px rgba(0,0,0,.42)}
.coach-tag .eye{position:absolute;left:9px;top:50%;margin-top:-4px;
  width:8px;height:8px;border-radius:50%;background:#0c2744;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.7)}
.coach-tag b{font-family:var(--mono);font-size:11px;letter-spacing:.16em}
.coach-tag em{font-style:normal;font-family:var(--mono);font-size:8.5px;
  letter-spacing:.14em;color:#8e2620;padding-left:9px;
  border-left:1px solid rgba(169,131,47,.6)}
.coach-under{display:flex;justify-content:space-between;
  margin:0 clamp(18px,7vw,74px);padding-top:3px}
.bogie{display:flex;gap:clamp(6px,1.6vw,10px)}
.bogie i{display:block;border-radius:50%;
  width:clamp(16px,4.2vw,24px);height:clamp(16px,4.2vw,24px);
  background:radial-gradient(circle at 36% 32%,#4b5663,#1b222b 62%,#0b0f14);
  box-shadow:0 0 0 2px rgba(194,169,109,.32),0 4px 7px rgba(0,0,0,.6)}

@media (max-width:480px){
  .coach-head{justify-content:center;gap:8px}
  .coach-brand{flex:1 1 100%;order:3;letter-spacing:.18em}
  .coach-foot{justify-content:center}
}
@media (prefers-reduced-motion: reduce){
  .door{transition:none}
  .coach-state i{animation:none}
  .doorway.open .locked{transition:none}
}

.van-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px}
.van-top .grow{flex:1}
.chip{border:1px solid rgba(255,255,255,.22);background:rgba(255,255,255,.06);color:#dfe8f4;
  border-radius:999px;padding:7px 13px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  cursor:pointer}
.chip.on{background:#ffd166;color:#2a1e07;border-color:#ffd166}

.grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
/* margin:0 matters — a <figure> carries a 40px left/right UA margin that
   otherwise shrinks every tile to a third of its grid cell */
.shot{margin:0;position:relative;border-radius:10px;overflow:hidden;background:#11161d;
  aspect-ratio:1;cursor:pointer;animation:pop .35s ease both}
.shot img{width:100%;height:100%;object-fit:cover;transition:transform .5s cubic-bezier(.2,.8,.3,1)}
.shot:hover img{transform:scale(1.06)}
.shot .meta{position:absolute;left:0;right:0;bottom:0;padding:22px 9px 7px;
  background:linear-gradient(transparent,rgba(0,0,0,.78));display:flex;align-items:flex-end;gap:6px}
.shot .who{flex:1;font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;color:#dfe8f4;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.like{display:inline-flex;align-items:center;gap:4px;border:0;background:rgba(0,0,0,.4);
  color:#fff;border-radius:999px;padding:4px 9px;font-family:var(--mono);font-size:10.5px;
  cursor:pointer;line-height:1}
.like .h{transition:transform .28s cubic-bezier(.2,1.7,.4,1)}
.like.on{background:rgba(226,58,80,.92)}
.like.on .h{transform:scale(1.28)}
.shot .rm{position:absolute;top:6px;right:6px;border:0;border-radius:999px;cursor:pointer;
  background:rgba(0,0,0,.55);color:#fff;font-family:var(--mono);font-size:9.5px;padding:4px 8px;
  opacity:0;transition:opacity .2s ease}
.shot:hover .rm,.shot:focus-within .rm{opacity:1}
.gal-empty{text-align:center;color:#a9c6e4;font-style:italic;padding:28px}

/* train-shaped upload progress */
.loader{margin-top:14px;display:none}
.loader.on{display:block}
.loader .rails{position:relative;height:56px;border-radius:8px;overflow:hidden;
  background:linear-gradient(180deg,#141a23,#0d1219);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.08)}
.loader .rails::before{content:"";position:absolute;left:0;right:0;bottom:9px;height:2px;
  background:#4d5764;z-index:2}
.loader .rails::after{content:"";position:absolute;left:0;right:0;bottom:2px;height:7px;
  background:repeating-linear-gradient(90deg,#39414d 0 8px,transparent 8px 22px);z-index:2}
/* The train's position IS the progress: parked off the LEFT edge when nothing
   has uploaded, arriving at the right when everything has, which leaves the
   engine at the right end with coaches filling the track behind it. The rake
   is mirrored so the locomotive leads in the direction of travel rather than
   running in reverse. The script writes the transform; the transition is what
   makes each completed file a glide rather than a jump. */
.loader .ltrain{position:absolute;left:0;bottom:8px;width:max-content;
  will-change:transform;transform:translate3d(-100%,0,0);
  transition:transform .75s cubic-bezier(.25,.8,.3,1)}
.loader .ltrain svg{display:block;height:44px;width:auto;transform:scaleX(-1)}
.loader.done .cap{color:#9fe3b4}
.loader.failed .cap{color:#f0b4a8}
.loader .cap{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;color:#a9c6e4;
  margin-top:8px;display:flex;gap:10px}
.loader .cap .grow{flex:1}

/* lightbox */
.lb{position:fixed;inset:0;z-index:70;display:none;background:rgba(3,7,12,.94)}
.lb.open{display:grid;place-items:center}
.lb img{max-width:96vw;max-height:82vh;border-radius:8px;
  box-shadow:0 30px 70px rgba(0,0,0,.7);animation:pop .3s ease}
.lb .lbbar{position:absolute;left:0;right:0;bottom:0;padding:16px;display:flex;gap:12px;
  align-items:center;justify-content:center;color:#dfe8f4;font-family:var(--mono);font-size:11px}
.lb .x{position:absolute;top:14px;right:14px;border:0;background:rgba(255,255,255,.12);
  color:#fff;border-radius:999px;width:38px;height:38px;font-size:17px;cursor:pointer}
/* Paging slides the incoming photo in from the side you came from. Only the
   arriving image is animated — cross-fading two layers buys very little here
   and doubles the work on a phone mid-swipe. */
@keyframes lbfromR{from{opacity:0;transform:translateX(8%) scale(.985)}
                   to{opacity:1;transform:none}}
@keyframes lbfromL{from{opacity:0;transform:translateX(-8%) scale(.985)}
                   to{opacity:1;transform:none}}
.lb img.fromR{animation:lbfromR .3s cubic-bezier(.22,.8,.3,1)}
.lb img.fromL{animation:lbfromL .3s cubic-bezier(.22,.8,.3,1)}
.lb .lbbar .grow{flex:1}
.lb .lbbar .count{letter-spacing:.12em;opacity:.75}
/* the like inside the viewer, sharing its look with the one on the tile */
.lb .like{display:inline-flex;align-items:center;gap:6px;border:0;cursor:pointer;
  background:rgba(255,255,255,.14);color:#fff;border-radius:999px;
  padding:9px 15px;font:inherit;font-size:12px;min-height:38px}
.lb .like .h{opacity:.6;transition:opacity .15s ease,transform .15s ease}
.lb .like.on{background:rgba(214,51,80,.9)}
.lb .like.on .h{opacity:1;transform:scale(1.15)}
/* paging arrows: big targets pinned to the sides, clear of the caption bar */
.lb .nav{position:absolute;top:50%;translate:0 -50%;border:0;cursor:pointer;
  background:rgba(255,255,255,.12);color:#fff;border-radius:999px;
  width:46px;height:46px;font-size:26px;line-height:1;display:grid;place-items:center}
.lb .nav:hover{background:rgba(255,255,255,.22)}
.lb .nav.prev{left:12px} .lb .nav.next{right:12px}
.lb .nav[disabled]{opacity:.25;cursor:default}
@media (max-width:600px){
  /* on a phone the arrows would sit on top of the photo, and swiping is the
     natural gesture there anyway — so shrink them and tuck them to the edge */
  .lb .nav{width:38px;height:38px;font-size:21px;background:rgba(255,255,255,.1)}
  .lb .nav.prev{left:4px} .lb .nav.next{right:4px}
  .lb img{max-width:92vw}
}

/* ----------------------------------------------- "whose ticket?" ask --
   A keepsake ticket rather than a form: the same cream paper, punched edges
   and brass rules as the real one, asking for the name it will carry. */
.ask{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:18px;
  background:rgba(3,7,12,.82)}
.ask[hidden]{display:none}
/* The punched edge is a mask, and a mask clips a box-shadow — so the lift
   comes from a drop-shadow filter, which follows the masked silhouette. */
.ask-card{--r:9px;width:min(400px,100%);background:var(--paper);
  padding:clamp(20px,5vw,26px) clamp(18px,5vw,26px);
  filter:drop-shadow(0 26px 56px rgba(0,0,0,.55));
  animation:pop .28s ease;
  -webkit-mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 26px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 26px repeat-y;
  -webkit-mask-composite:source-in;
  mask:radial-gradient(var(--r) at left,#0000 98%,#000) left/100% 26px repeat-y,
    radial-gradient(var(--r) at right,#0000 98%,#000) right/100% 26px repeat-y;
  mask-composite:intersect;border-radius:12px}

.ask-head{display:flex;align-items:center;justify-content:flex-end;
  padding-bottom:8px;margin-bottom:12px;
  border-bottom:1px dashed rgba(36,29,22,.32)}
.ask-no{font-family:var(--mono);font-size:9.5px;letter-spacing:.16em;color:#8a7850}
.ask-no b{color:var(--ink);letter-spacing:.1em;margin-left:3px}

.ask-card h3{margin:0 0 5px;font-family:var(--serif);font-weight:600;
  font-size:clamp(21px,5vw,25px);line-height:1.25;color:var(--rail);
  /* keeps the line from breaking with "it ¶1" stranded on its own */
  text-wrap:balance}
.ask-card > p{margin:0 0 14px;font-family:var(--serif);font-style:italic;
  font-size:clamp(14px,3.4vw,15.5px);color:#6b6253}
.ask-card input{width:100%;border:1px solid var(--line);border-radius:8px;
  padding:12px 13px;font:inherit;font-size:16px;background:#fffdf7;color:var(--ink)}
.ask-card input::placeholder{color:#a79a84;font-style:italic}
.ask-card input:focus{outline:2px solid var(--brass-lo);outline-offset:1px;
  border-color:var(--brass-lo)}
.ask-err{margin:9px 0 0;font-family:var(--serif);font-style:italic;font-size:14px;
  color:var(--red)}
.ask-err[hidden]{display:none}

.ask-row{display:flex;gap:10px;align-items:center;justify-content:flex-end;
  margin-top:18px;flex-wrap:wrap}
.ask-row .btn{flex:0 1 auto;min-height:44px}
/* the primary button warms up rather than just lifting */
.ask-row .btn.glow{transition:transform .2s ease,box-shadow .25s ease}
.ask-row .btn.glow:hover,.ask-row .btn.glow:focus-visible{
  box-shadow:0 0 0 3px rgba(242,214,104,.3),0 14px 34px rgba(201,169,97,.55),
    0 0 26px rgba(255,214,110,.5)}
@media (max-width:420px){
  /* full-width stacked buttons are the easier target on a small phone */
  .ask-row{flex-direction:column-reverse;align-items:stretch}
  .ask-row .btn{justify-content:center;width:100%}
}

/* ------------------------------------------------------- chai vendor */
.vendor{position:fixed;bottom:16px;left:-160px;z-index:45;display:flex;align-items:flex-end;gap:8px;
  cursor:pointer;pointer-events:auto}
.vendor.walk{animation:walkby 22s linear}
@keyframes walkby{from{left:-160px}to{left:104vw}}
.vendor .guy{position:relative;font-size:34px;line-height:1;
  animation:bob .5s ease-in-out infinite alternate;
  filter:drop-shadow(0 6px 8px rgba(0,0,0,.3))}
.vendor .guy .cup{position:absolute;right:-9px;bottom:2px;font-size:17px}
@keyframes bob{from{transform:translateY(0)}to{transform:translateY(-4px)}}
.vendor .say{max-width:min(230px,58vw);background:var(--paper);color:var(--ink);border-radius:12px;
  padding:9px 12px;font-size:13.5px;line-height:1.35;box-shadow:0 10px 24px rgba(0,0,0,.3);
  opacity:0;transform:translateY(6px);transition:opacity .25s ease,transform .25s ease}
.vendor.say-on .say{opacity:1;transform:none}

/* -------------------------------------------------------------- footer */
.foot{background:#07131f;color:#9fb6cd;text-align:center;padding:34px 16px 46px}
.foot .sig{font-family:var(--script);font-size:34px;color:#ffd166}
.foot .sub{font-family:var(--mono);font-size:10px;letter-spacing:.2em;margin-top:8px}
.foot .acts{display:flex;gap:10px;justify-content:center;margin-top:18px;flex-wrap:wrap}

/* toast */
.toast{position:fixed;left:50%;bottom:24px;translate:-50% 0;z-index:90;max-width:min(420px,92vw);
  background:#1a232e;color:#eaf2ff;border:1px solid rgba(255,255,255,.14);border-radius:12px;
  padding:13px 17px;font-size:14.5px;line-height:1.4;box-shadow:0 18px 40px rgba(0,0,0,.5);
  opacity:0;transform:translateY(14px);transition:opacity .3s ease,transform .3s ease;
  pointer-events:none}
.toast.on{opacity:1;transform:none}
.toast.warn{border-color:#d9a939;background:#2a2113}

/* ------------------------------------------------------------- responsive */
@media (max-width:620px){
  .two{grid-template-columns:1fr}
  .topbar .names{font-size:18px}
  .grid{grid-template-columns:repeat(auto-fill,minmax(108px,1fr))}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .loader .ltrain{transition:none}
  .rv{opacity:1;transform:none;transition:none}
  .hero .sunball,.hero .bird,.hero .hero-track::after,.coach .wheels i,
  .vendor,.vendor .guy,.stub,.shot,.festoon .bulb,.lamp .head::after,
  .lb img,
  .scratch.done::after{animation:none !important}
}
