/* ============================================================
   Wedding site — "Classic Serif Elegance" (design-claude-classic-serif)
   Palette: ivory whites · deep and soft greens · pale blues
   Principle: practical first, personal second
   ============================================================ */

:root{
  --ivory:      #FBFAF6;   /* page background */
  --ivory-2:    #F4F2EA;   /* alternate section wash */
  --ink:        #2F3B33;   /* deep green-charcoal text */
  --ink-soft:   #5C6B60;   /* secondary text */
  --green:      #5A7263;   /* primary accent */
  --green-deep: #33443A;   /* dark band */
  --sage:       #9AAC9F;   /* soft accent, hairlines on dark */
  --sage-lift:  #C7D3C8;   /* sage with the contrast back: the small sans copy
                              on the two deep green bands. Jost Light is a
                              thinner stroke than the italic it replaced, and
                              --sage on --green-deep is only 4.4:1 */
  --blue:       #B9CEDA;   /* pale blue accent */
  --blue-wash:  #E4EDF2;   /* pale blue background wash */
  --forest:     #4A6741;   /* RSVP "attending" selected pill */
  --terracotta: #8A5744;   /* RSVP "unable to attend" selected pill */
  --hairline:   #DDD9CC;   /* fine rules */
  --admin-hand: #A8792F;   /* admin only: an answer we recorded ourselves —
                              deliberately outside the site's greens so a
                              "by hand" mark can't be mistaken for a state
                              the guest chose */
  --admin-hand-wash: #F6EFE0;
  --vermillion: #B54A2E;   /* the torii gate, quietened for print — Back to Japan page */
  --vermillion-deep: #94391F; /* pressed/hovered state of a filled vermillion control */
  --serif: "Cormorant Garamond", Garamond, Georgia, serif;
  --sans:  "Jost", "Segoe UI", system-ui, sans-serif;
  --jp:    "Shippori Mincho", "Yu Mincho", "Hiragino Mincho ProN", serif;
}

* { margin:0; padding:0; box-sizing:border-box; }

html { scroll-behavior:smooth; }
@media (prefers-reduced-motion: reduce){
  html { scroll-behavior:auto; }
  .reveal { opacity:1 !important; transform:none !important; transition:none !important; }
}

body{
  background:var(--ivory);
  color:var(--ink);
  font-family:var(--sans);
  font-weight:300;
  font-size:17px;
  line-height:1.7;
  -webkit-font-smoothing:antialiased;
}

/* ---------- shared utilities ---------- */

.wrap{ max-width:960px; margin:0 auto; padding:0 24px; }
.narrow{ max-width:680px; }

.kicker{
  font-family:var(--sans);
  font-size:12px;
  letter-spacing:.34em;
  text-transform:uppercase;
  color:var(--green);
  font-weight:400;
}

/* ---------- secondary copy ----------
   The sublines, stories, captions and confirmations that sit under a label
   or beside a heading. These were Cormorant Garamond Light Italic, which at
   15–17px left hairline strokes on a small x-height — guests read straight
   past the fold sublines and the gift stories without registering them.
   They are the sans now, upright: the same Jost as the uppercase label
   directly above most of them, and a step smaller, since Jost's x-height is
   much taller than Cormorant's at the same nominal size.

   The display italics are deliberately untouched, and are where the site's
   voice lives: the nav and footer monograms, the hero salutation, venue and
   countdown, the 20px schedule times and the 22–23px opening ledes on both
   pages. Italic means something here again rather than being the default for
   anything small. Per-element sizes and colours stay in each element's own
   rule below; only the shared decision lives here. */
.welcome-caption, .fold-sub, .satnote,
.saved-note, .rsvp-updated, .form-error,
.note-status, .note-clear, .guest-failed,
.japan .mat-caption, .japan .gift .story, .japan .vending .story,
.japan .shelf-saved, .japan .slip-empty, .japan .slip-sent-line,
.japan .pp-amount p,
.japan .bank-sub, .japan .bank-amount, .japan .bank-note,
.japan .sent-ask, .japan .sent-done, .japan .sent-nudge,
.japan .gift-note-sub, .japan .note-saved,
.japan .note-form textarea, .japan .choice-copy .what{
  font-family:var(--sans);
  font-weight:300;
  letter-spacing:.015em;
  line-height:1.6;
}
/* a placeholder inherits nothing, so this one needs saying twice */
.japan .vend-amount input::placeholder{
  font-family:var(--sans); font-weight:300; letter-spacing:.015em;
}

h2{
  font-family:var(--serif);
  font-weight:400;
  font-size:clamp(34px, 5vw, 46px);
  line-height:1.15;
  margin:14px 0 10px;
}

.rule{
  width:56px; height:1px;
  background:var(--green);
  margin:22px auto;
  position:relative;
}
.rule::after{
  content:"";
  position:absolute; left:50%; top:50%;
  width:5px; height:5px;
  background:var(--blue);
  border-radius:50%;
  transform:translate(-50%,-50%);
}

.section-head{ text-align:center; margin-bottom:52px; }
.section-head p.lede{ color:var(--ink-soft); max-width:520px; margin:0 auto; }

section{ padding:96px 0; }

/* The sticky nav sits over whatever an anchor link lands on — hold the
   section's heading clear of it. */
section[id], header[id]{ scroll-margin-top:76px; }

/* Phones: the same rhythm at three quarters of the height. Seven sections
   of 96px top and bottom is most of a screen's worth of empty page. */
@media (max-width:720px){
  section{ padding:64px 0; }
  section[id]{ scroll-margin-top:104px; }  /* the stacked mobile nav is taller */
  .section-head{ margin-bottom:34px; }
}

/* subtle scroll-reveal */
.reveal{ opacity:0; transform:translateY(18px); transition:opacity .8s ease, transform .8s ease; }
.reveal.in{ opacity:1; transform:none; }

/* image placeholder boxes */
.ph{
  border:1px dashed var(--sage);
  background:
    linear-gradient(135deg, rgba(185,206,218,.18), rgba(154,172,159,.14));
  display:grid; place-items:center;
  color:var(--ink-soft);
  font-size:12px; letter-spacing:.22em; text-transform:uppercase;
  text-align:center; padding:12px;
}

/* ---------- sticky navigation ---------- */

nav.site{
  position:sticky; top:0; z-index:50;
  background:rgba(251,250,246,.92);
  backdrop-filter:blur(8px);
  border-bottom:1px solid var(--hairline);
}
.nav-inner{
  max-width:960px; margin:0 auto; padding:14px 24px;
  display:flex; align-items:center; justify-content:space-between; gap:16px;
}
.nav-mono{
  font-family:var(--serif); font-size:20px; letter-spacing:.08em;
  color:var(--ink); text-decoration:none; white-space:nowrap;
}
.nav-mono em{ font-style:italic; color:var(--green); }
.nav-links{
  display:flex; align-items:center; gap:26px;
  list-style:none;
  flex-wrap:wrap; justify-content:center;
}
.nav-links a{
  font-size:12px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-soft); text-decoration:none; white-space:nowrap;
  padding:6px 0; border-bottom:1px solid transparent;
  transition:color .2s, border-color .2s;
}
.nav-links a.active{ color:var(--green); border-color:var(--green); }
/* RSVP is styled exactly like its four neighbours. It used to be a filled
   green pill, on the theory that the one link guests must take should stand
   out; in use the solid shape read as a badge or a status label rather than
   a link, and looked less tappable than the plain words beside it. */

/* Hover marks the link a pointer is over; .active marks the section in view —
   and they wear the same underline. A touch tap leaves :hover applied until
   the next tap lands elsewhere (scrolling doesn't clear it), so on a phone the
   link a guest tapped kept its underline while the scroll-spy carried .active
   on down the page: two underlines at once. Hover is a pointer affordance, so
   it is only offered to a device that has one. A browser too old to know the
   feature loses the hover styling and keeps the scroll-spy — the cheaper way
   to be wrong. Same for .nav-back on the Japan page, below. */
@media (hover: hover){
  .nav-links a:hover{ color:var(--green); border-color:var(--green); }
}

@media (max-width:720px){
  .nav-inner{ flex-direction:column; gap:8px; padding:10px 12px; }
  /* keep all five links on one row: tighter type and gaps, no wrapping */
  .nav-links{ gap:clamp(8px, 2.6vw, 16px); width:100%; justify-content:center; flex-wrap:nowrap; }
  .nav-links a{ font-size:10px; letter-spacing:.12em; }
}
@media (max-width:360px){
  .nav-links a{ letter-spacing:.06em; }
}

/* ---------- hero ---------- */

header.hero{
  min-height:calc(100vh - 64px);
  min-height:calc(100svh - 64px);
  display:grid; place-items:center;
  text-align:center;
  padding:72px 24px 120px; /* bottom reserves clear space for the scroll hint */
  position:relative;
  background:
    radial-gradient(ellipse 90% 55% at 50% -10%, rgba(185,206,218,.28), transparent 60%),
    var(--ivory);
}
@media (max-width:720px){
  /* the stacked logo + links nav is ~92px tall here; subtract it (and use
     svh, which excludes the browser chrome) or the centred hero gains a
     large empty band at the top */
  header.hero{
    min-height:calc(100vh - 110px);
    min-height:calc(100svh - 110px);
    padding:40px 20px 120px;
  }
}
.hero-salutation{
  font-family:var(--serif); font-style:italic;
  font-size:clamp(18px, 2.4vw, 22px);
  color:var(--ink-soft);
  margin-bottom:20px;
}
/* One-time entrance after passing the gate (.hero-arrive via ?welcome=1):
   the rest of the hero matches the gate page, so only the guests' name —
   the one new thing — makes an entrance. */
.hero-arrive .hero-salutation{
  animation:salute 1.3s ease-out .3s both;
}
@keyframes salute{
  from{ opacity:0; transform:translateY(10px); }
  to{ opacity:1; transform:translateY(0); }
}
@media (prefers-reduced-motion: reduce){
  .hero-arrive .hero-salutation{ animation:none; }
}
.hero-date{
  font-size:13px; letter-spacing:.4em; text-transform:uppercase;
  color:var(--green); font-weight:400;
  margin-top:26px;
}
h1{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(56px, 11vw, 118px);
  line-height:1.02; margin:26px 0 8px;
}
h1 .amp{
  display:block;
  font-style:italic; font-weight:300;
  font-size:.42em; line-height:1.3;
  color:var(--blue);
  filter:saturate(1.4) brightness(.82);
}
.hero-venue{
  font-family:var(--serif); font-style:italic;
  font-size:clamp(19px, 2.6vw, 24px);
  color:var(--ink-soft);
  margin-top:14px;
}
/* .btn is no longer used in the hero (the jump buttons are gone — see
   index.html) but is still the shared button face for the JS-off RSVP save
   and the Google Maps link. The playlist's send is .note-btn now — it sits
   on a white card in the RSVP band, where the note's pill is the vocabulary. */
.btn{
  display:inline-block;
  font-family:var(--sans); font-size:12px; font-weight:400;
  letter-spacing:.28em; text-transform:uppercase;
  text-decoration:none;
  padding:15px 36px;
  border-radius:999px;
  transition:all .25s;
  cursor:pointer;
}
.btn-solid{ background:var(--green); color:var(--ivory); border:1px solid var(--green); }
.btn-solid:hover{ background:var(--green-deep); border-color:var(--green-deep); }
.btn-ghost{ background:transparent; color:var(--green); border:1px solid var(--sage); }
.btn-ghost:hover{ border-color:var(--green); }

.hero-count{
  /* keeps the countdown the same distance below the venue line that the
     removed button row used to hold */
  margin-top:38px;
  font-family:var(--serif); font-style:italic;
  font-size:17px; color:var(--ink-soft);
}
.hero-count strong{ color:var(--green); font-weight:500; }

.scroll-hint{
  position:absolute; bottom:26px; left:50%; transform:translateX(-50%);
  font-size:11px; letter-spacing:.3em; text-transform:uppercase;
  color:var(--ink-soft); text-decoration:none;
}
.scroll-hint::after{
  content:"↓"; display:block; text-align:center; margin-top:6px;
  animation:drift 2.2s ease-in-out infinite;
}
@keyframes drift{ 0%,100%{transform:translateY(0)} 50%{transform:translateY(6px)} }
@media (prefers-reduced-motion: reduce){ .scroll-hint::after{ animation:none; } }

/* ---------- welcome strip (framed photo + short note) ---------- */

.welcome{
  background:var(--ivory-2);
  border-top:1px solid var(--hairline);
  border-bottom:1px solid var(--hairline);
}
/* framed print: contained rather than full bleed — no fade, a thin
   keyline, and the caption doing the storytelling */
.welcome-frame{
  /* a touch wider than the 680px text column so the photo still reads
     as a feature, but close enough to sit with the copy */
  max-width:800px; margin:0 auto; padding:0 24px;
}
.welcome-photo{
  /* 1.5:1 (close to the source ratio) so the crop keeps both faces and
     the engagement ring in the lower gesture — a wider frame drops the
     ring, which the "newly engaged" caption depends on */
  aspect-ratio:1.5/1;
  overflow:hidden;
  border:1px solid var(--hairline);
}
.welcome-photo img{
  width:100%; height:100%; display:block;
  object-fit:cover; object-position:50% 30%;
  filter:brightness(1.12) saturate(.94);
}
.welcome-caption{
  text-align:center; margin-top:14px;
  font-size:15.5px; color:var(--ink-soft);
}
.welcome-copy{ text-align:center; margin-top:34px; }
.welcome-copy p{ color:var(--ink-soft); margin-bottom:1em; }
.welcome-copy p.first{
  font-family:var(--serif); font-size:23px; font-style:italic;
  color:var(--ink); line-height:1.5;
}
@media (max-width:760px){
  .welcome-copy{ margin-top:28px; }
}

/* ---------- the day / timeline ----------
   Each entry is its own details/summary (the FAQ idiom, laid out as a
   timeline row) so guests open one description at a time. Times and
   headings are never hidden: that skeleton is what a guest checks on the
   morning itself, and it must not cost a tap. The closed row and the open
   one share a header layout — an entry that re-flowed as it opened would
   throw the reader's eye off the line they just tapped. */

.timeline{ max-width:620px; margin:0 auto; position:relative; }
.timeline::before{
  content:"";
  position:absolute; left:110px; top:8px; bottom:8px;
  width:1px; background:var(--hairline);
}
/* The entry itself stays a block and the SUMMARY carries the timeline grid:
   Chrome wraps a details' non-summary children in a ::details-content box,
   so a grid on .t-item can't place .t-detail in a column of its own — the
   description is aligned with padding instead (see .t-detail below). */
.t-item{ margin-bottom:26px; }
.t-item:last-child{ margin-bottom:0; }
/* The whole row is the control. Most of this undoes the FAQ-tuned global
   summary rule (a serif 21px flex row with padding of its own). Baseline
   alignment: the larger serif time sits low against the caps heading under
   starts-align. */
.t-item > summary{
  display:grid; grid-template-columns:88px 44px 1fr;
  align-items:baseline; gap:0;
  padding:0; font:inherit;
  list-style:none; cursor:pointer;
}
.t-item > summary::-webkit-details-marker{ display:none; }
/* the global summary:hover greens the whole row, times included */
.t-item > summary:hover{ color:inherit; }
.t-item > summary:hover h3{ color:var(--green); }
.t-time{
  font-family:var(--serif); font-style:italic;
  font-size:20px; color:var(--green);
  text-align:right;
}
.t-dot{
  justify-self:center; align-self:start;
  width:11px; height:11px; border-radius:50%;
  background:var(--ivory);
  border:2px solid var(--green);
  margin-top:9px;
  position:relative; z-index:1;
}
.t-item.highlight .t-dot{ background:var(--blue); border-color:var(--green); }
.t-item h3{
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  max-width:400px;  /* keeps the + over the detail column, not out in space */
  font-family:var(--sans); font-weight:400;
  font-size:13px; letter-spacing:.24em; text-transform:uppercase;
  transition:color .2s;
}
/* the FAQ's + mark (rotates to a × when open), sized for a 13px heading */
.t-item h3 .mark{ font-size:17px; }
/* lines up under the heading: the time and dot columns above it */
.t-detail{ padding:9px 0 0 132px; }
.t-detail p{ color:var(--ink-soft); font-size:16px; max-width:400px; }
.t-detail p + p{ margin-top:.7em; }
.t-detail a{ color:var(--green); }

@media (max-width:560px){
  /* nothing between them to wrap under, so time and heading share a line
     and the schedule reads as a genuine list */
  .timeline::before{ left:16px; }
  .t-item{ margin-bottom:17px; }
  .t-item > summary{ grid-template-columns:36px auto 1fr; }
  .t-time{
    grid-column:2; grid-row:1;
    text-align:left; padding-right:14px;
    font-size:18px;
  }
  .t-item h3{ grid-column:3; grid-row:1; }
  .t-dot{ grid-column:1; grid-row:1; margin-top:7px; }
  .t-detail{ padding-left:36px; }
}

/* ---------- the playlist: the RSVP band's second half ----------
   Design: wedding/playlist-in-rsvp-forks.html, Fork 5. It was a section of
   its own between Getting There and Questions, with the form behind a fold
   to keep the page short; the closed face read as a note and a guest never
   found the box behind it. Moving the ask into the band a guest is already
   filling in removes the thing to discover, and pays for itself in length —
   the section's own heading, lede and ivory wash cost more than the open
   form does.

   The rule and the heading are the band saying "that was the reply, this is
   something else", so the card can't read as a fourth thing guests must do.
   Everything below the split is the note card's vocabulary, because the two
   are twins: a white card, boxes at rest, a send that arrives on typing. */

.band-split{
  max-width:560px; margin:34px auto 0; padding-top:30px;
  /* the band's own hairline: --hairline is for ivory, and on the deep green
     it disappears entirely. Sage at low alpha reads as a rule rather than
     as a second card edge. */
  border-top:1px solid rgba(194,208,198,.28);
  text-align:left;
}
.band-split h3{
  font-family:var(--serif); font-weight:400; font-style:italic;
  font-size:27px; color:var(--ivory);
  margin-bottom:8px; text-align:center;
}
/* --sage-lift, not --sage: this is 15.5px Jost Light on --green-deep, which
   is precisely the case that token exists for (4.33:1 against 6.47:1 — see
   its comment at the top of this file). The band's own .lede sits at the
   larger size and keeps --sage. */
.band-split .split-lede{
  color:var(--sage-lift); font-size:15.5px; line-height:1.6;
  text-align:center; margin:0 auto 22px; max-width:440px;
}
.playlist-form{ max-width:560px; margin:0 auto; text-align:left; }
/* The card's own margin-bottom is the gap before the next RSVP card, and
   there is no next card here. */
.playlist-form .rsvp-guest{ margin-bottom:0; }

.song-fields{ display:grid; grid-template-columns:1.5fr 1fr; gap:12px; }
.song-fields .field{ margin-bottom:0; }
@media (max-width:480px){ .song-fields{ grid-template-columns:1fr; gap:10px; } }
.song-more{
  display:inline-block; margin-top:16px;
  background:none; border:none; cursor:pointer;
  /* a button centres its own text, so this one's second line sat centred
     under a left-aligned first — visible on any phone narrow enough to
     wrap it, which is most of them */
  text-align:left;
  font-family:var(--sans); font-weight:400;
  font-size:11px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--green); padding:2px 0;
  border-bottom:1px solid transparent;
  transition:border-color .2s;
}
.song-more:hover{ border-color:var(--green); }
.song-second{ margin-top:16px; padding-top:16px; border-top:1px solid var(--hairline); }
.song-second .second-label{
  font-size:11px; letter-spacing:.26em; text-transform:uppercase;
  color:var(--ink-soft); margin-bottom:12px;
}
/* .song-more sets a display of its own, which would beat the UA's [hidden]
   rule — the band's own [hidden] override (above) is what puts it back, and
   it reaches here because this card lives inside .rsvp-band. */

/* a button that reads as a link (the playlist's undo, the note's take-back,
   the Japan page's undo and save retry). The Japan page re-colours the hover
   in its own accent — .japan .link-btn:hover — and inherits the rest. */
.link-btn{
  background:none; border:0; padding:0; cursor:pointer;
  font:inherit; color:var(--ink-soft);
  border-bottom:1px solid var(--sage);
  transition:color .2s, border-color .2s;
}
.link-btn:hover{ color:var(--green); border-color:var(--green); }

/* ---------- expandable detail blocks (the driving routes) ----------
   One idiom for "there's more here if you want it", shared with the FAQ's
   details/summary: a quiet card that opens in place. Used only where the
   content is genuinely optional for most guests — never for the schedule,
   and no longer for the playlist, whose fold was what hid its form. */

.routes-fold{
  border:1px solid var(--hairline);
  background:var(--ivory);
  text-align:left;
}
.routes-fold{ margin-bottom:36px; }
.routes-fold > summary{
  list-style:none; cursor:pointer;
  display:flex; align-items:center; justify-content:space-between; gap:20px;
  padding:20px 24px;
  font-family:var(--sans); font-size:16px; font-weight:300;
  transition:background .2s;
}
.routes-fold > summary::-webkit-details-marker{ display:none; }
.routes-fold > summary:hover{ background:var(--ivory-2); color:inherit; }
.routes-fold[open] > summary{ border-bottom:1px solid var(--hairline); }
.fold-face{ min-width:0; }
.fold-title{
  display:block;
  font-size:12px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--ink);
}
.fold-sub{
  display:block; margin-top:4px;
  font-size:15px; color:var(--ink-soft);
}
.routes-fold .there-routes{ padding:24px; margin-bottom:0; }
@media (max-width:560px){
  .routes-fold > summary{ padding:18px 20px; }
  .routes-fold .there-routes{ padding:20px; }
}

/* ---------- getting there ---------- */

.there{ background:var(--blue-wash); border-top:1px solid var(--hairline); border-bottom:1px solid var(--hairline); }

.there-map{
  background:var(--ivory);
  border:1px solid var(--hairline);
  margin-bottom:36px;
  overflow:hidden;
}

.aerial{ position:relative; }
.aerial img{ display:block; width:100%; height:auto; }
/* A marker is a zero-size point ON its coordinate: the mark centres on it and
   the label hangs off it. It used to be a flex row carrying both, with
   translate(-50%,-50%) centring the WHOLE assembly — which left every
   side-labelled mark half a label's width from the thing it named (measured
   at 13.7% of image width for Dodmoor House, ~53px at phone size, putting the
   mark in the trees beside the house and the lower Parking mark in the hedge).
   Mobile never showed the fault, because the label was display:none there and
   the assembly WAS the mark; desktop has carried it all along. Keep the label
   positioned off the point rather than laid out beside it — that is the whole
   fix. Mockups: wedding/getting-there-map-g-pointer-forks.html */
.marker{ position:absolute; width:0; height:0; pointer-events:none; }
/* The mark is a crosshair, not a disc: a survey mark rather than a control.
   A filled, haloed circle is the shape of a button, and a guest tapped these
   expecting them to do something — nobody taps a crosshair. Its centre is a
   true intersection, so it also points more precisely than a disc did.
   Two strokes, not a background image, so it scales with one width value. */
.marker .dot{
  position:absolute; left:0; top:0; transform:translate(-50%,-50%);
  width:18px; height:18px;
  filter:drop-shadow(0 0 2px rgba(20,26,22,.95));
}
.marker .dot::before,
.marker .dot::after{ content:""; position:absolute; background:var(--ivory); }
.marker .dot::before{ left:50%; top:0; width:1px; height:100%; transform:translateX(-50%); }
.marker .dot::after{ top:50%; left:0; height:1px; width:100%; transform:translateY(-50%); }
/* No plate behind the label. The plates, not the marks, were what covered the
   photograph — measured by pixel diff, swapping one mark for another moves
   total coverage by 5-8%, while dropping the plates moves it by 63%. The
   shadow is three layers deliberately, and all three earn their place: a tight
   drop for edge definition, a close halo that carries the letterform over pale
   ground, and a wide soft one to seat it against near-black canopy. Two layers
   were enough everywhere except the upper Parking label, which crosses the
   gravel drive — 35% of its footprint is bright at phone size, the worst on
   the photograph, and "KING" went soft on it. Measure before thinning these:
   scratch/verify_all.py reports the ground under every label. */
.marker .lbl{
  position:absolute; top:50%; left:16px; transform:translateY(-50%);
  color:var(--ivory); white-space:nowrap;
  font-size:10.5px; font-weight:400;
  letter-spacing:.18em; text-transform:uppercase;
  text-shadow:0 1px 2px rgba(20,26,22,.95),
              0 0 4px rgba(20,26,22,.85),
              0 0 9px rgba(20,26,22,.7);
}
.marker.flip .lbl{ left:auto; right:16px; }
.marker.above .lbl{ top:auto; bottom:16px; left:50%; transform:translateX(-50%); }

/* compact split: the left column drives the row height, so the photo stays a
   modest crop rather than stretching into a tall sliver of tree canopy */
.there-split{ display:grid; grid-template-columns:1.35fr 1fr; }
.there-txt{ padding:34px 36px 32px; }
.there-txt h3{
  font-family:var(--serif); font-weight:500; font-size:22px; margin-bottom:8px;
}
.there-txt address{
  font-style:normal; font-size:15px; color:var(--ink-soft); line-height:1.7; margin-bottom:18px;
}
.there-txt .map-links{ display:flex; gap:12px; flex-wrap:wrap; align-items:center; }
.there-txt .map-links .btn{ padding:12px 26px; font-size:11px; }
.there-txt .rulebar{ border-top:1px solid var(--hairline); margin:22px 0 16px; }
.there .eyebrow{
  font-size:12px; letter-spacing:.26em; text-transform:uppercase;
  color:var(--green); font-weight:400; margin-bottom:8px;
}
.there-txt .tipnote{ font-size:14.5px; color:var(--ink-soft); }
.there-pic{ position:relative; }
.there-pic img{
  position:absolute; inset:0;
  width:100%; height:100%; object-fit:cover; display:block;
  object-position:50% 74%; /* keep the entrance gates in frame, not the tree above */
}

/* what3words chip — terracotta slashes keep it on-palette */
.w3w{
  display:inline-flex; align-items:center; gap:10px; flex-wrap:wrap; max-width:100%;
  border:1px solid var(--hairline); background:var(--ivory-2);
  border-radius:999px; padding:8px 18px 8px 16px; font-size:14px; margin:0;
  text-decoration:none; transition:border-color .25s;
}
.w3w:hover{ border-color:var(--sage); }
.w3w .w3w-k{ font-size:10px; letter-spacing:.22em; text-transform:uppercase; color:var(--ink-soft); }
.w3w .w3w-v{ letter-spacing:.02em; color:var(--ink); }
.w3w .sl{ color:var(--terracotta); letter-spacing:0; }

/* route cards — one per approach */
.there-routes{ display:grid; grid-template-columns:repeat(3,1fr); gap:24px; margin-bottom:24px; }
.route-card{
  background:var(--ivory); border:1px solid var(--hairline);
  padding:32px 28px 30px; position:relative;
}
.route-card::before{
  content:""; position:absolute; left:28px; right:28px; top:0;
  height:2px; background:var(--green);
}
.route-card .compass{
  font-family:var(--sans); font-size:12px; font-weight:400;
  letter-spacing:.26em; text-transform:uppercase;
  color:var(--green); margin-bottom:2px;
}
.route-card p.body{ font-size:15px; color:var(--ink-soft); }
.chips{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin:12px 0 14px; }
.chips span{
  font-size:10.5px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--green-deep); border:1px solid var(--sage);
  border-radius:3px; padding:4px 9px; background:var(--ivory);
}
.chips i{ font-style:normal; color:var(--sage); font-size:12px; }

/* the one thing everyone should read, whichever road brought them */
.final-band{ background:var(--green-deep); color:#EAEDE4; padding:30px 40px 28px; margin-bottom:36px; }
.final-band .eyebrow{ color:var(--sage); }
.final-band p.big{ font-family:var(--serif); font-size:16.5px; line-height:1.6; max-width:760px; }
.final-band .satnote{ color:var(--sage-lift); }
.satnote{ display:block; margin-top:10px; font-size:13px; }
.satnote::before{
  content:""; display:inline-block; width:14px; height:1px;
  background:var(--sage); vertical-align:middle; margin-right:8px;
}

.there-cards{ display:grid; grid-template-columns:repeat(2,1fr); gap:24px; }
.t-card{
  background:var(--ivory);
  border:1px solid var(--hairline);
  padding:34px 30px;
}
.t-card .glyph{
  font-family:var(--serif); font-size:26px; color:var(--green);
  display:block; margin-bottom:12px;
}
.t-card h4{
  font-family:var(--sans); font-weight:400; font-size:12px;
  letter-spacing:.26em; text-transform:uppercase;
  margin-bottom:10px;
}
.t-card p{ font-size:15px; color:var(--ink-soft); }
/* Links in body copy take the site's green, the way .t-detail and .gifts
   already do. Without this they arrive as browser-default blue — which the
   hotel link in the "Staying overnight" card had been doing all along, and
   the FAQ's answers now have links of their own. */
.t-card p a,
details .answer a,
.section-head .lede a,
.gate-hint a{ color:var(--green); }

@media (max-width:820px){
  .there-split{ grid-template-columns:1fr; }

  /* Narrow screens keep the labels rather than swapping them for a numbered
     key below the photo. The key was a second system to cross-reference, and
     the marks that indexed it were what a guest tapped. Same treatment as
     desktop now — only smaller, and with the entrance label moved. */
  .marker .dot{ width:13px; height:13px; }
  /* 9.5px/.06em is the largest that clears at every width from 320px up, and
     the tracking is what buys the size: at .14em the same label fails from
     412px, where the entrance label takes its road name back. Deliberately
     tighter than the .14-.20em this site sets uppercase elsewhere — these sit
     on a photograph at a size nothing else on the page runs at, and open
     tracking costs both width and word recognition down here.
     10px is the ceiling regardless of tracking: the short labels themselves
     collide at 320px. Past that, wording has to go. Matrix: scratch sweep in
     the PR #240 thread. */
  .marker .lbl{ left:13px; font-size:9.5px; letter-spacing:.06em; }
  .marker.flip .lbl{ left:auto; right:13px; }
  /* Stacked above its mark, "Entrance from Weedon Lane" ran into the lower
     Parking label at every width from 320px to 780px. Side-labelled, nothing
     collides anywhere in that range. Desktop keeps the stack — it has the room,
     and side-labelling it there runs the long label into Parking instead. */
  .marker.above .lbl{
    top:50%; bottom:auto; left:13px; transform:translateY(-50%);
  }
  /* The road name is what a guest needs when the sat nav won't take Weedon
     Lane, so it stays as long as it fits. Swept in 1px steps against the real
     page, demanding 2px of clear air: the full label first clears "The canal"
     at 420px, so it drops to plain "Entrance" at 419 and below. This number
     belongs to the entrance mark's coordinate — it was 411 while the mark sat
     at left:17%, and moving it onto the mouth of the drive at 18.2% pushed the
     label 1.2% of the width further right, reopening a collision at 412-419.
     Re-sweep it if that mark moves again, and don't round it to something
     tidier — 400 was the first guess and it left a window at 400-411px. */
  @media (max-width:419px){
    .lbl-more{ display:none; }
  }
  /* keep the directions button on one line down to 320px */
  .there-txt .btn{ padding:13px 22px; letter-spacing:.18em; }
  /* Stacked, the entrance photo is 240px of scroll that says nothing the
     aerial above hasn't already said. Desktop keeps it. */
  .there-pic{ display:none; }
  .w3w{ font-size:12.5px; padding:7px 14px; gap:8px; }
  .w3w .w3w-k{ font-size:9px; }
  .there-routes{ grid-template-columns:1fr; }
  .final-band{ padding:26px 26px 24px; }
  .there-cards{ grid-template-columns:1fr; }
}

/* ---------- questions ---------- */

.faq{ max-width:680px; margin:0 auto; }
/* Scoped to the FAQ: the page's other details blocks (the routes fold, the
   Japan page's bank card) are cards with borders of their own, and
   :first-of-type would out-specify them. */
.faq details{
  border-bottom:1px solid var(--hairline);
}
.faq details:first-of-type{ border-top:1px solid var(--hairline); }
summary{
  list-style:none; cursor:pointer;
  display:flex; justify-content:space-between; align-items:baseline; gap:20px;
  padding:24px 4px;
  font-family:var(--serif); font-size:21px; font-weight:500;
  transition:color .2s;
}
summary::-webkit-details-marker{ display:none; }
summary:hover{ color:var(--green); }
summary .mark{
  font-family:var(--sans); font-weight:300; font-size:22px;
  color:var(--green); flex-shrink:0;
  transition:transform .25s;
}
details[open] summary .mark{ transform:rotate(45deg); }
details .answer{
  padding:0 4px 26px;
  color:var(--ink-soft); font-size:16px; max-width:600px;
}
details .answer p + p{ margin-top:.8em; }

/* ---------- rsvp band ---------- */

.rsvp-band{
  background:var(--green-deep);
  color:var(--ivory);
  text-align:center;
}
.rsvp-band .kicker{ color:var(--blue); }
.rsvp-band h2{ color:var(--ivory); }
.rsvp-band p.lede{ color:var(--sage); max-width:520px; margin:0 auto 36px; }

/* Several elements in this band toggle with the hidden attribute (server-
   rendered, then flipped by the page's JS) and set a display of their own
   below, which would otherwise beat the UA's [hidden] rule. */
.rsvp-band [hidden]{ display:none !important; }

/* The RSVP form sits directly on the band: one white card per guest
   (name + attending choice), then the party message and save button.
   An unchecked pair of pills IS the "awaiting reply" state. */
.rsvp-form{
  max-width:560px; margin:0 auto;
  text-align:left;
}
.rsvp-guest{
  background:#fff;
  border:1px solid var(--hairline);
  padding:24px 26px;
  margin-bottom:14px;
  color:var(--ink);
  transition:border-color .25s;
}
.rsvp-guest h3{
  font-family:var(--serif); font-weight:500; font-size:23px;
  margin-bottom:14px;
}
.choice-row{ display:flex; gap:10px; flex-wrap:wrap; }
label.choice{ position:relative; cursor:pointer; }
label.choice input{
  /* visually hidden but still focusable, so the pills work by keyboard */
  position:absolute; opacity:0; width:1px; height:1px;
}
label.choice span{
  display:inline-block;
  font-family:var(--sans); font-size:11px; font-weight:400;
  letter-spacing:.18em; text-transform:uppercase;
  padding:10px 20px; border-radius:999px;
  border:1px solid var(--hairline); color:var(--ink-soft);
  white-space:nowrap;
  transition:all .2s;
}
label.choice:hover input:not(:checked) + span{ border-color:var(--green); color:var(--green); }
/* Selected answers must be scannable without reading: green fill = coming,
   terracotta fill = not coming, plus a checkmark so the distinction never
   rests on colour alone. */
label.choice input:checked + span{
  background:var(--forest); border-color:var(--forest); color:var(--ivory);
}
label.choice input[value="no"]:checked + span{
  background:var(--terracotta); border-color:var(--terracotta);
}
label.choice input:checked + span::before{
  content:"\2713"; display:inline-block; margin-right:9px;
  font-size:14px; line-height:1; vertical-align:-1px;
}
label.choice input[value="no"]:checked + span::before{ content:"\2715"; }
label.choice input:focus-visible + span{
  outline:2px solid var(--blue); outline-offset:2px;
}
@media (max-width:400px){
  label.choice span{ padding:9px 15px; letter-spacing:.1em; }
}
/* ---------- saving as you answer ----------
   Design: wedding/rsvp-note-b2-reveal-on-type.html. There is no Save button
   for a guest with JS: tapping a pill IS the save. Watching a guest RSVP on
   a phone, "Happily attending" read as the reply and the button underneath
   as somebody else's business — so the answer now commits on the tap that
   makes it, and the filled pill is the receipt.

   Both controls ship in the markup and are taken away BY the script (which
   puts .js-live on the form), never the other way round: with JS off the
   Save button is the only way to commit, and a note whose send never
   arrived would be a note that could physically never be sent. */
.rsvp-form.js-live .rsvp-actions{ display:none; }

/* A save is silent when it works and speaks only when it fails — on the
   card the guest just touched, carrying its own retry, because with the
   Save button gone there is no standing control to try again with. */
.rsvp-guest.is-failed{ border-color:var(--terracotta); }
.guest-failed{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  margin-top:16px; padding-top:14px; border-top:1px solid var(--terracotta);
  font-size:14px; color:var(--terracotta);
}
.guest-failed button{
  font-family:var(--sans); font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  background:none; border:1px solid var(--terracotta); color:var(--terracotta);
  padding:6px 14px; border-radius:999px; cursor:pointer;
}
.guest-failed button:hover{ background:var(--terracotta); color:#fff; }

/* ---------- the note's send: full width, revealed on typing ----------
   The card at rest is a label and a box and nothing else, so the note stops
   advertising itself as a task and the attending pills get the section's
   only visible controls back. The send arrives on the first keystroke,
   directly under the box the guest is typing into — and everything that
   moves is BELOW that box, so nothing being read or typed into shifts. */
.note-send-slot{
  overflow:hidden; margin-top:14px;
  transition:max-height .26s ease, opacity .2s ease, margin-top .26s ease;
}
.js-live .note-send-slot{ max-height:0; opacity:0; margin-top:0; }
/* Generous enough for the button, a two-line failure and the take-back:
   max-height clips, and a clipped apology is worse than a slack reveal. */
.js-live .note-send-slot.is-shown{ max-height:260px; opacity:1; margin-top:14px; }
@media (prefers-reduced-motion: reduce){ .note-send-slot{ transition:none; } }
/* Its own button rather than .btn-solid: that one is ivory-on-green for the
   dark band, and this one sits on a white card.

   It reads as a full-width sibling of the pills above it and moves through
   the same two states they do. AT REST it is --blue-wash on a --blue
   hairline — the site's one non-green accent, already carrying the Getting
   There section, and the only face tried that is findable at this width
   without borrowing an answer's colour. What it must not look like is a
   choice already made: a green fill wore exactly that, and the warm
   off-white it replaces (--ivory-2 on --hairline) was a shade off the card
   and read as disabled next to the pills' crisp edges. Blue says "this is
   the other kind of control" without saying "done". SENT it still takes the
   accepted pill's forest green and tick, because that is what the pills
   already teach "this is recorded" looks like — so the send now changes
   colour AND wording on the way, which the off-white barely did.

   Amber and a saturated light green were the other two candidates; the
   four-panel comparisons behind the choice are output_samples/. */
.note-btn{
  display:block; width:100%;
  font-family:var(--sans); font-size:11.5px; font-weight:400;
  letter-spacing:.24em; text-transform:uppercase;
  padding:15px 20px; border-radius:999px; cursor:pointer;
  background:var(--blue-wash); color:var(--ink); border:1px solid var(--blue);
  transition:background .25s, border-color .25s, color .25s;
}
/* The pills deepen their EDGE on hover because they have no fill to deepen;
   this one has, so it deepens the fill instead — one step down the same
   wash, kept local since nothing else on the site needs the tone. Reaching
   for --green here instead would put a green hairline round a blue face. */
.note-btn:hover{ background:#D6E5EE; border-color:#A6C3D3; }
.note-btn:disabled{ opacity:.6; cursor:default; }
.note-btn.is-sent{
  background:var(--forest); border-color:var(--forest); color:var(--ivory);
}
.note-btn.is-sent:hover{ background:var(--green-deep); border-color:var(--green-deep); color:var(--ivory); }
/* Failures only — the button carries every other state itself. */
.note-status{
  display:block; margin-top:12px; text-align:center;
  font-size:14px; color:var(--terracotta);
}
.note-clear{ margin-top:14px; text-align:center; font-size:14.5px; }

/* Save confirmation sits under the button — where the guest is looking —
   and fades in so it registers as new rather than as static page copy.
   JS-off only now: .rsvp-actions goes with the Save button it belongs to. */
.saved-note{
  font-size:15.5px; color:var(--blue);
  margin-top:20px;
  animation:note-in .6s ease both;
}
@keyframes note-in{
  from{ opacity:0; transform:translateY(4px); }
  to{ opacity:1; transform:none; }
}
@media (prefers-reduced-motion: reduce){ .saved-note{ animation:none; } }
.rsvp-updated{
  margin-top:14px;
  font-size:14.5px; color:var(--sage-lift);
}
.rsvp-band .btn-solid{ background:var(--ivory-2); color:var(--green-deep); border-color:var(--ivory-2); }
.rsvp-band .btn-solid:hover{ background:#fff; border-color:#fff; }
.rsvp-band .btn-solid.btn-saved{ background:var(--forest); color:var(--ivory); border-color:var(--forest); }

/* ---------- gifts (the monogram ticket) ----------
   Design: wedding/gifts-cta-n-monogram-chopped.html, "Monogram,
   Washed and Chopped". The note itself stays as quiet as it ever
   was; what changed is the link out to the fund, which was a line
   of body text and is now a single travel ticket — the whole
   object a link.

   It sits square, in a vermillion outline, with the action line
   set as a solid pill. Two rounds of quieter tuning (a resting
   lift, then a press state and a heavier CTA) did not fix the
   thing actually wrong with it: at a glance the card read as an
   illustration of a ticket printed beside the note, and a guest
   who never hovered never learned otherwise. The tilt was most
   of why — a straight card is a component, a tilted one is a
   picture of an object — and the underlined-blue-link job was
   being done by eleven vermillion capitals in the corner. A
   filled pill is the one shape on this page that is only ever
   pressed, so it carries the affordance on its own, before any
   pointer arrives and on touch, where no pointer ever does.

   The section takes an ivory-2 band, hairline top and bottom, so
   the note stops sharing a surface with the footer. The band colour matters twice over: the
   perforation notches are discs of it, so they only read as holes
   punched through the ticket because the ground behind is
   ivory-2. `.gifts a{color:var(--green)}` is deliberately gone —
   it out-specified the ticket's own `color:inherit` and turned
   the whole slab green. */

.gifts{
  text-align:center;
  padding:80px 0;
  background:var(--ivory-2);
  border-top:1px solid var(--hairline);
  border-bottom:1px solid var(--hairline);
}
.gifts h2{ font-size:30px; }
.gifts p{ color:var(--ink-soft); max-width:520px; margin:0 auto; }

/* The object itself. The vermillion outline is the ticket's own
   edge — it is what makes the card a printed object at a glance
   rather than a pale panel on a pale band, and it ties the whole
   thing to the one accent the fund page is built in. 2px, not 1:
   at a hairline the colour reads as a tint on the background. */
.ticket{
  position:relative;
  display:grid; grid-template-columns:1fr 86px;
  max-width:420px;
  margin:34px auto 0;
  background:#fff;
  border:2px solid var(--vermillion);
  /* Resting lift. A touchscreen has no hover to discover the object
     with, so the card has to read as raised while nothing is
     happening to it. Still well under the hover shadow, which has to
     stay visibly the bigger move. */
  box-shadow:0 3px 14px rgba(47,59,51,.10);
  text-decoration:none; color:inherit; text-align:left;
  transition:transform .22s ease, box-shadow .22s ease;
}
/* Hover and press. The card lifts and the pill darkens: with the
   tilt gone the straighten went with it, and a lift is the gesture
   a thing you can press makes. Keep both states cheap — transform
   and shadow only, no layout.

   :active carries it to touch, which is the one moment a guest is
   asking "did that do anything?"; it presses back down rather than
   up, so a finger gets the button behaviour it expects. */
.ticket:hover,
.ticket:focus-visible{
  transform:translateY(-3px);
  box-shadow:0 10px 26px rgba(47,59,51,.17);
}
.ticket:active{
  transform:translateY(0);
  box-shadow:0 2px 8px rgba(47,59,51,.14);
}
.ticket:hover .t-act,
.ticket:focus-visible .t-act,
.ticket:active .t-act{ background:var(--vermillion-deep); }
.ticket:focus-visible{ outline:2px solid var(--green); outline-offset:4px; }
/* The lift is decoration; the pill's colour change is not, so it
   stays. */
@media (prefers-reduced-motion: reduce){
  .ticket{ transition:box-shadow .22s ease; }
  .ticket:hover, .ticket:focus-visible, .ticket:active{ transform:none; }
  .ticket:hover .t-act .arrow, .ticket:active .t-act .arrow{ transform:none; }
}

/* Perforation. Half-discs in the band colour, 18px across and
   9px deep, centred on the dashed divide (right = stub width −
   radius) and seated on the ticket's own edge, so the outline
   runs along the top, dips through a semicircle and carries on.

   Half-discs, not full circles with the outward border dropped:
   a circle's border corners fall on the diagonals, so a
   transparent top leaves the side borders standing 45° proud of
   the edge — two red horns above the card, which at a hairline
   was a detail and at 2px vermillion is a wart. The 2px of
   height above the edge is the card's own border, covered by the
   notch's background so the outline is genuinely interrupted
   rather than drawn across the bite.

   box-sizing is restated here on purpose: the reset's bare `*`
   does not match pseudo-elements, so without it the border sits
   outside the 18px and the notches land off the dashed line. */
.ticket::before,
.ticket::after{
  content:"";
  box-sizing:border-box;
  position:absolute; right:77px; z-index:1;
  width:18px; height:11px;
  background:var(--ivory-2);
  border:2px solid var(--vermillion);
}
.ticket::before{ top:-2px; border-top:0; border-radius:0 0 9px 9px; }
.ticket::after{ bottom:-2px; border-bottom:0; border-radius:9px 9px 0 0; }

.t-main{ padding:20px; min-width:0; }
/* Route line: two place names with a hairline run between them,
   drawn rather than typed — a dash character at .2em tracking
   sits at the wrong height and breaks at the wrong widths. */
.t-route{
  display:flex; align-items:center; gap:8px;
  font-family:var(--sans); font-weight:400;
  font-size:10.5px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--ink-soft);
  white-space:nowrap;
}
.t-route i{ flex:1 1 auto; min-width:12px; height:1px; background:var(--hairline); }
.t-title{
  display:block;
  font-family:var(--serif); font-weight:400;
  font-size:24px; line-height:1.2;
  margin:9px 0 4px;
}
.t-sub{
  display:block;
  font-family:var(--sans); font-weight:300;
  font-size:14px; line-height:1.45;
  color:var(--ink-soft);
}
/* The action line, as a filled pill. It was vermillion capitals on
   white, which is a caption of a button — the same treatment the
   route line and ADMIT TWO use, so it took its turn in the reading
   order behind the 48px chop instead of being the thing on the card
   that is obviously pressed. Filled, it is the only element in the
   section with a solid ground, and nothing else on the page looks
   like it.

   inline-flex, not block: the pill has to end where the words do.
   Stretched to the panel it becomes a banner across the ticket and
   the card stops looking like a ticket. 500 is a real Jost weight in
   base.html's request, so no synthesised bold on ivory. */
.t-act{
  display:inline-flex; align-items:center; gap:10px;
  margin-top:14px;
  padding:11px 22px;
  border-radius:999px;
  background:var(--vermillion);
  font-family:var(--sans); font-weight:500;
  font-size:11px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--ivory);
  transition:background .22s ease;
}
.t-act .arrow{ transition:transform .22s ease; display:inline-block; }
.ticket:hover .t-act .arrow,
.ticket:active .t-act .arrow{ transform:translateX(3px); }

/* The stub, washed. 6% vermillion is enough that the tear-off
   reads as a separate piece of card and not enough to become a
   second colour block on an ivory page; it is also what the
   chop's interior is filled with, so ring and stub share one
   tint where they overlap.

   The bottom padding is not spacing, it is the chop's seat: the
   monogram centres in what's left above it. The card grew when
   the action line became a pill, and a stub centred in the whole
   height put ADMIT TWO straight through the ring. */
.t-stub{
  border-left:1px dashed var(--hairline);
  background:rgba(181,74,46,.06);
  display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:7px;
  padding:16px 8px 42px;
}
/* The footer's monogram at stub scale: upright M and T in green at
   500, the ampersand italic at 400 and a size down in ink-soft, so
   it sits between the initials as a join rather than as a third
   letter. Cormorant's italic ampersand is a calligraphic form, not
   a slanted upright, which is what makes the mark read as a
   monogram and not as three characters. Naming the travellers is
   the point of the stub: ADMIT TWO under a destination is a
   caption, under the monogram it is a statement of fact. */
.t-stub .mono{
  font-family:var(--serif); font-weight:500;
  font-size:21px; line-height:1;
  letter-spacing:.04em;
  color:var(--green);
  white-space:nowrap;
  user-select:none;
}
.t-stub .mono em{
  font-style:italic; font-weight:400;
  font-size:.82em;
  color:var(--ink-soft);
  letter-spacing:0;
}
.t-stub .admit{
  font-family:var(--sans); font-weight:400;
  font-size:8px; letter-spacing:.16em; text-transform:uppercase;
  line-height:1.4; text-align:center; white-space:nowrap;
  color:var(--ink-soft);
}

/* THE CHOP. An outlined ring of vermillion with 検 inside it, not
   a filled disc: a gate stamp is a ring of ink, and hollow is what
   keeps it clear of the solid 済 seal on the fund page. The 6%
   wash inside the ring is what makes it read as ink into paper
   rather than a decal, and it is why the dashed perforation stays
   visible through it. pointer-events:none so the whole slab is
   still the tap target; aria-hidden in the markup so it is not
   announced.

   It is a child of .ticket, not of .t-main, deliberately: it is
   positioned against the ticket's right edge so it holds the same
   relationship to the stub at every width (the stub is a fixed
   column; the main panel is the one that flexes), and only a child
   of .ticket can cross the divider at all. z-index 2 puts it over
   the stub's wash and the dashed border; the perforation notches
   sit at z-index 1 and the chop clears them.

   It hangs off the BOTTOM edge, not the top: the card's height is
   now set by the pill in the main panel, so anything measured from
   the top drifts the moment the title or the sub wraps at some
   width in between. Measured up from the bottom, the ring keeps
   its seat in the stub whatever happens above it.

   Desktop: the ticket is 420×179 and the ring sits with 21px of it
   on the stub — 10.8px clear of ADMIT TWO, 13px clear of the lower
   notch and 22px inside the bottom edge. Close numbers; don't move
   them without re-rendering. */
.t-chop{
  position:absolute; z-index:2;
  right:65px; bottom:22px;
  width:48px; height:48px;
  border:2px solid var(--vermillion);
  border-radius:50%;
  background:rgba(181,74,46,.06);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--jp); font-weight:400;
  font-size:21px; line-height:1;
  color:var(--vermillion);
  transform:rotate(-12deg);
  opacity:.85;
  pointer-events:none;
  user-select:none;
}

/* Narrow phones. 312px of column at 360px: the stub gives back
   14px and the main panel 8px, which is what the route line
   needs to stay on one line and the title needs to stay on one
   line. The notches follow the stub in (right = 72 − 9). The stub
   block comes down with it — the monogram and ADMIT TWO both stay
   on one line inside the 60px of usable stub, and stay the same
   width as each other, which is what holds the composition. */
@media (max-width:400px){
  .ticket{ grid-template-columns:1fr 72px; }
  .ticket::before, .ticket::after{ right:63px; }
  .t-main{ padding:18px 16px 18px; }
  .t-route{ font-size:8.8px; letter-spacing:.12em; gap:6px; }
  .t-title{ font-size:22px; margin:8px 0 4px; }
  .t-sub{ font-size:13px; }
  .t-act{ font-size:10.5px; letter-spacing:.18em; padding:10px 18px; gap:8px; }
  .t-stub{ padding:14px 5px 34px; gap:6px; }
  .t-stub .mono{ font-size:19px; letter-spacing:.03em; }
  .t-stub .admit{ font-size:7.6px; letter-spacing:.13em; }
  /* The chop steps down with everything else, still seated on the
     bottom edge. Both 390 and 360 take this branch and measure the
     same on the stub side: 20px of the ring on the stub, 11.2px
     clear of ADMIT TWO, 10px clear of the lower notch. */
  .t-chop{ width:42px; height:42px; font-size:18px; right:52px; bottom:19px; }
}

/* ============================================================
   Back to Japan — honeymoon fund page (design: gifts option D,
   "The Hybrid" — wedding/backtojapan-gifts-d-hybrid.html, with
   the opening from photo option C, "Matted Portrait" — wedding/
   backtojapan-photo-c-matted-portrait.html: the story leads and
   the photo is a white-matted 4:5 print beside it).
   The gifts are a shelf of museum-label cards acting as a
   basket: stamping a card presses a vermillion hanko seal over
   its chop mark, amounts sum into one PayPal.Me link, and a
   fixed checkout bar carries the running total. The vending-
   machine fund (custom amount) adds to the basket. Everything
   here is scoped under .japan so the vermillion accents never
   leak into the main page.
   ============================================================ */

.japan .kicker{ color:var(--vermillion); }

/* The give section toggles its states with the hidden attribute (server-
   rendered, then flipped by the page's JS). Several of those elements set
   a display of their own below, which would otherwise beat the UA's
   [hidden] rule and leave an empty total or a stray button on screen. */
.japan [hidden]{ display:none !important; }

.nav-back{
  font-size:12px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-soft); text-decoration:none; white-space:nowrap;
  padding:6px 0; border-bottom:1px solid transparent;
  transition:color .2s, border-color .2s;
}
@media (hover: hover){
  .nav-back:hover{ color:var(--green); border-color:var(--green); }
}

/* compact opening: story left, mounted print right */
.japan .opening{
  display:grid; grid-template-columns:minmax(0, 58fr) minmax(0, 42fr);
  gap:64px; align-items:center;
  max-width:880px; margin:0 auto; padding:84px 24px 92px;
  position:relative;
}
.japan .opening-copy{ position:relative; }
.japan h1{
  font-weight:400;
  font-size:clamp(42px, 5.4vw, 58px); line-height:1.06;
  margin:16px 0 0;
}
.japan .v-rule{
  width:56px; height:2px; background:var(--vermillion);
  margin:26px 0;
}
.japan .opening-copy .first{
  font-family:var(--serif); font-size:22px; font-style:italic;
  line-height:1.55; margin-bottom:18px;
}
.japan .opening-copy p{ color:var(--ink-soft); margin-bottom:1em; max-width:44ch; }
/* mounted print: white mat, hairline frame, caption beneath */
.japan .mat-frame{ position:relative; margin:0; }
.japan .mat{
  background:#fff;
  border:1px solid var(--hairline);
  padding:14px;
  box-shadow:0 1px 0 rgba(47,59,51,.04);
}
.japan .mat-photo{
  aspect-ratio:4/5;
  overflow:hidden;
}
.japan .mat-photo img{
  width:100%; height:100%; display:block;
  object-fit:cover; object-position:42% 35%;
  filter:brightness(1.03) saturate(.98);
}
.japan .mat-caption{
  text-align:center; margin-top:13px;
  font-size:15px; color:var(--ink-soft);
}
/* the vertical lettering — "to Japan, once again" —
   hung outside the print's right edge */
.japan .tategaki{
  position:absolute; top:2px; left:calc(100% + 22px);
  writing-mode:vertical-rl;
  font-family:var(--jp);
  font-size:19px; letter-spacing:.42em;
  color:var(--sage);
  user-select:none;
  white-space:nowrap;
}

/* ---------- the gift shelf: museum-label cards as a basket ---------- */
.japan .gift-shelf{
  padding:88px 24px 84px;
  border-top:1px solid var(--hairline);
  text-align:center;
}
.japan .gift-shelf h2{
  font-size:clamp(30px, 4.4vw, 38px);
  margin:14px 0 10px;
}
.japan .rule{ background:var(--vermillion); }
.japan .gift-shelf .lede{ color:var(--ink-soft); max-width:540px; margin:0 auto 52px; }
.japan .shelf{
  display:grid; grid-template-columns:repeat(3, 1fr); gap:18px;
  max-width:1020px; margin:0 auto; text-align:left;
}
.japan .gift{
  position:relative;
  display:flex; flex-direction:column;
  background:#fff;
  border:1px solid var(--hairline);
  padding:26px 24px 22px;
  cursor:pointer;
  transition:border-color .18s ease, box-shadow .18s ease;
}
.japan .gift:hover{ border-color:var(--sage); }
.japan .gift:focus-within{ outline:2px solid var(--green); outline-offset:2px; }
/* the real checkbox, visually absorbed into the card */
.japan .gift-check{ position:absolute; opacity:0; pointer-events:none; }
.japan .gift .kanji{
  position:absolute; top:16px; right:18px;
  font-family:var(--jp); font-size:30px; line-height:1;
  color:var(--sage); opacity:.5;
  transition:opacity .16s ease;
  pointer-events:none;
}
.japan .gift .gift-title{
  display:block;
  font-size:12px; letter-spacing:.24em; text-transform:uppercase;
  font-weight:400; margin:0 34px 7px 0;
}
.japan .gift .story{
  display:block;
  font-size:15px; color:var(--ink-soft);
  flex-grow:1; margin:0 0 20px; max-width:30ch;
}
.japan .gift .price{
  font-family:var(--serif); font-size:25px; font-weight:500;
  line-height:1;
  transition:color .18s ease;
}
/* the hakubun seal — a solid disc pressed over the chop mark */
.japan .seal{
  position:absolute; top:5px; right:7px;
  width:54px; height:54px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--jp); font-size:25px; line-height:1;
  background:var(--vermillion); color:var(--ivory);
  transform:rotate(-8deg) scale(1.4);
  opacity:0;
  pointer-events:none;
  transition:opacity .16s ease-out, transform .16s ease-out;
}
.japan .gift-check:checked ~ .kanji{ opacity:0; }
.japan .gift-check:checked ~ .seal{
  opacity:.94;
  transform:rotate(-8deg) scale(1);
}
.japan .gift-check:checked ~ .price{ color:var(--vermillion); }
/* the stamped card's border needs the parent — :has() everywhere that
   matters; a browser without it just misses the border, the seal (sibling
   selectors above) still shows */
.japan .gift:has(.gift-check:checked){
  border-color:var(--vermillion);
  box-shadow:inset 0 0 0 1px var(--vermillion);
}

/* the vending machine fund — a deep-green panel, additive */
.japan .vending{
  grid-column:1 / -1;
  display:grid; grid-template-columns:1fr auto;
  align-items:center; gap:28px;
  background:var(--green-deep); color:var(--ivory);
  border:1px solid var(--green-deep);
  padding:28px 34px;
  position:relative; overflow:hidden;
  text-align:left;
}
.japan .vending .yen{
  position:absolute; right:-14px; top:50%;
  transform:translateY(-50%);
  font-family:var(--jp); font-size:130px; line-height:1;
  color:#fff; opacity:.05;
  user-select:none; pointer-events:none;
}
.japan .vending-title{
  display:block;
  font-size:12px; letter-spacing:.26em; text-transform:uppercase;
  font-weight:400; margin-bottom:6px;
}
.japan .vending .story{
  font-size:15.5px; color:var(--sage-lift);
  max-width:46ch; margin:0;
}
.japan .vend-panel{
  display:flex; flex-direction:column; align-items:center; gap:10px;
  position:relative; z-index:1;
}
.japan .coin-slot{
  width:34px; height:5px; border-radius:3px;
  background:var(--ivory); opacity:.5;
  transition:background .18s ease, opacity .18s ease;
}
.japan .vend-amount{
  display:flex; align-items:baseline; gap:6px;
  border:1px solid rgba(251,250,246,.35);
  padding:8px 16px;
  transition:border-color .18s ease;
}
.japan .vend-amount label{
  font-family:var(--serif); font-size:22px; font-weight:500;
}
.japan .vend-amount input{
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:19px;
  color:var(--ivory); width:110px;
  background:transparent; border:0;
  padding:0 2px;
}
.japan .vend-amount input:focus{ outline:none; }
.japan .vend-amount input::placeholder{
  font-size:15px;
  color:var(--sage-lift);
}
.japan .vending.active .vend-amount{ border-color:var(--vermillion); }
.japan .vending.active .coin-slot{ background:var(--vermillion); opacity:1; }
.japan .vend-note{
  font-size:12px; letter-spacing:.06em; color:var(--sage);
}

/* JS-off: the shelf saves with a plain form post */
.japan .shelf-save{ grid-column:1 / -1; text-align:center; padding-top:12px; }
.japan .shelf-save button{ cursor:pointer; font-family:var(--sans); }
.japan .shelf-saved{
  margin-top:16px;
  font-size:15px; color:var(--ink-soft);
}

/* ---------- at the till: the slip, then how to send it ----------
   The section's job is NOT to be a second checkout (the bar below only
   walks the guest down here). It prints an itemised slip of what was
   stamped — the itemisation lives on the page, where it's true, since a
   PayPal.Me link can only ever carry one figure — and then offers the two
   ways to send it: PayPal loud, bank transfer visibly quieter. */
.japan .fund{
  padding:84px 24px 96px;
  text-align:center;
  border-top:1px solid var(--hairline);
}
.japan .fund h2{
  font-size:clamp(30px, 4.4vw, 38px);
  margin:14px 0 10px;
}
.japan .fund .lede{ color:var(--ink-soft); max-width:500px; margin:0 auto 34px; }

/* the slip itself — a shop receipt in the page's own hand */
.japan .slip{
  position:relative;
  max-width:520px; margin:0 auto;
  background:#fff;
  border:1px solid var(--hairline);
  padding:26px 30px 24px;
  text-align:left;
  transition:border-color .2s ease;
}
.japan .slip-head{
  font-size:11px; letter-spacing:.3em; text-transform:uppercase;
  color:var(--ink-soft);
  padding-bottom:12px; border-bottom:1px solid var(--hairline);
}
.japan .slip-line{
  display:flex; align-items:baseline; gap:12px;
  padding:12px 0;
  border-bottom:1px dotted var(--hairline);
}
/* the total's own rule closes the list — no doubled line above it */
.japan .slip-line:last-child{ border-bottom:0; }
.japan .slip-mark{
  font-family:var(--jp); font-size:17px; line-height:1;
  color:var(--sage);
  flex-shrink:0; width:20px;
}
.japan .slip-what{
  flex:1; min-width:0;
  font-size:11.5px; letter-spacing:.16em; text-transform:uppercase;
  line-height:1.5;
}
.japan .slip-amount{
  flex-shrink:0;
  font-family:var(--serif); font-size:19px; font-weight:500;
  color:var(--ink-soft);
}
.japan .slip-empty{
  padding:20px 0 4px;
  font-size:15.5px; color:var(--ink-soft);
}
.japan .slip-total{
  display:flex; align-items:baseline; gap:12px;
  margin-top:14px; padding-top:14px;
  border-top:1px solid var(--ink);
}
.japan .slip-total .slip-what{ font-weight:400; }
.japan .slip-total .slip-amount{
  font-size:27px; color:var(--vermillion);
}
/* The undo, kept where the second thoughts happen. Quiet enough that it
   never competes with the total above it or the PayPal button below, but a
   real control rather than an instruction to go back up and re-tap a card.
   Right-aligned under the total, clear of the 済 chop's corner. */
.japan .slip-clear{
  margin-top:12px; text-align:right;
  font-size:13px; letter-spacing:.02em;
}
.japan .slip-clear .link-btn{ font-size:inherit; }
.japan .slip-empty:focus{ outline:none; }
.japan .slip-empty:focus-visible{
  outline:2px solid var(--sage); outline-offset:4px;
}
/* the 済 chop — pressed on the slip when the guest tells us it's away.
   Same gesture as the cards' hanko seal, squared off the way a document
   chop is; the sent line beneath keeps its right edge clear. */
.japan .slip-chop{
  position:absolute; right:26px; bottom:16px;
  width:54px; height:54px;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--jp); font-size:28px; line-height:1;
  color:var(--vermillion);
  border:2px solid var(--vermillion); border-radius:4px;
  transform:rotate(-11deg) scale(1.35);
  opacity:0; pointer-events:none;
  transition:opacity .2s ease-out, transform .2s ease-out;
}
/* room at the foot for the chop, so it lands in clear space beside the
   sent line rather than clipping the total */
.japan .slip-sent{ border-color:var(--vermillion); padding-bottom:40px; }
.japan .slip-sent .slip-chop{ opacity:.85; transform:rotate(-11deg) scale(1); }
.japan .slip-sent-line{
  margin-top:16px; padding-right:78px;
  font-size:14.5px; color:var(--ink-soft);
}
@media (prefers-reduced-motion: reduce){
  .japan .slip-chop{ transition:none; }
}

.japan .pay-hero{
  max-width:520px; margin:26px auto 0;
}
.japan .btn-vermillion{
  display:inline-block;
  font-size:12px; letter-spacing:.28em; text-transform:uppercase;
  color:var(--ivory); text-decoration:none;
  padding:14px 32px; border-radius:999px;
  background:var(--vermillion); border:1px solid var(--vermillion);
  transition:background .25s;
}
.japan .btn-vermillion:hover{ background:#9E3F26; }
.japan .btn-vermillion:disabled{ opacity:.6; cursor:default; }
/* saved state settles into the site's green — "done" rather than "act" */
.japan .btn-vermillion.btn-saved{
  background:var(--green); border-color:var(--green);
}
/* The amount fallback for phones whose PayPal app eats the figure in the
   link. Quieter than the button but directly above it — it carries a number
   the guest may have to retype, and a caveat under the button is read by
   someone who has already left. Suppressed only where the device has no
   touch pointer at all (see the media query below); anything ambiguous errs
   towards showing it. */
.japan .pp-amount{ margin-bottom:20px; }
.japan .pp-amount p{
  font-size:14.5px; color:var(--ink-soft);
}
.japan .pp-figure{
  font-style:normal; font-weight:500; color:var(--vermillion);
  white-space:nowrap;
}
/* The copy action is the amount itself, inline in the sentence — a pill
   beneath it made the guest read a number, then look elsewhere to take it.
   Underlined and in the accent so it reads as a control mid-sentence.

   The underline is text-decoration, NOT a border-bottom, so that the
   generous vertical padding below can grow the hit area without dragging
   the line away from the words (a border sits on the padding edge; an
   underline stays on the baseline). It also survives a wrap: a border draws
   one line under the whole inline box, text-decoration underlines each row.

   ~11px top and bottom takes the tap target past 44px on a 15px line. This
   is the only control in the give section besides the button itself and it
   renders on touch alone, so it can afford the room — the padding overflows
   the line box without disturbing the paragraph, and the 20px below
   .pp-amount keeps it clear of "Continue to PayPal". */
.japan .pp-copy{
  display:inline; cursor:pointer;
  font:inherit; color:var(--vermillion);
  background:transparent; border:0; padding:11px 0;
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:3px;
  transition:color .2s, opacity .2s;
}
.japan .pp-copy:hover{ opacity:.72; }
/* Done: the underline goes with the invitation to press. */
.japan .pp-copy.copied{ color:var(--green); text-decoration:none; }
.japan .pp-copy.copied .pp-figure{ color:inherit; }
/* Touch only, and nothing takes its place on a desktop: the app handoff is
   the only thing this line warns about, and a pointer-only browser opens
   paypal.me itself with the figure already in it. The fee hint that used to
   sit here went with it — paypal.me between personal accounts doesn't in
   practice put that choice in front of anyone, so it was instructions for a
   screen that never arrives, and the give section is better for saying one
   true thing than two hedged ones.

   `pointer`/`hover` describe the PRIMARY pointer, which an iPad with a
   trackpad satisfies as squarely as a desktop — and iPadOS hands paypal.me
   to the PayPal app like any other tablet, so keying off those would hide
   the fallback in exactly the case it exists for. `any-pointer` instead
   asks whether the device has a touch pointer at all, and every OS that can
   pass the link to an installed app does. A browser that doesn't know the
   feature fails the query and keeps the line — the right way to be wrong,
   since the cost is one quiet caveat that doesn't apply. */
@media not all and (any-pointer: coarse){
  .japan .pp-amount{ display:none; }
}
/* bank transfer: the quiet option, but unmistakably an option — a muted
   card on the alternate wash rather than a line of text hoping to be
   noticed. Folded shut, so it never competes with the PayPal button. */
.japan details.bank-card{
  max-width:520px; margin:22px auto 0;
  background:var(--ivory-2);
  border:1px solid var(--hairline);
  text-align:left;
}
.japan details.bank-card summary{
  list-style:none; cursor:pointer;
  /* the FAQ's summary rule is global and lays its children out in a row —
     this card stacks its two lines instead */
  display:block;
  position:relative;
  padding:16px 52px 16px 22px;
  transition:background .2s;
}
.japan details.bank-card summary::-webkit-details-marker{ display:none; }
.japan details.bank-card summary:hover{ background:#EDEBE1; }
.japan details.bank-card summary::after{
  content:'+';
  position:absolute; right:22px; top:50%; transform:translateY(-50%);
  font-family:var(--serif); font-size:22px; line-height:1;
  color:var(--ink-soft);
}
.japan details.bank-card[open] summary::after{ content:'\2013'; }
.japan .bank-face{
  display:block;
  font-size:12px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink);
}
.japan .bank-sub{
  display:block; margin-top:3px;
  font-size:14.5px; color:var(--ink-soft);
}
.japan .bank-body{
  margin:0 22px; padding:18px 0 22px;
  border-top:1px solid var(--hairline);
}
.japan .bank-amount{
  margin:0 0 14px;
  font-size:15.5px; color:var(--ink);
}
.japan .bank-amount strong{ font-style:normal; font-weight:500; color:var(--vermillion); }
.japan .bank-note{
  margin-top:14px;
  font-size:14.5px; color:var(--ink-soft);
}

/* "I've sent it" — the guest's own word, never ours. Revealed only
   after they set off for PayPal or opened the bank card. */
.japan .sent-box{
  max-width:520px; margin:28px auto 0;
}
.japan .sent-ask{
  margin-bottom:16px;
  font-size:15px; color:var(--ink-soft);
}
.japan .btn-quiet{
  display:inline-block; cursor:pointer;
  font-family:var(--sans);
  font-size:11.5px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--ink-soft); background:transparent;
  padding:12px 28px; border-radius:999px;
  border:1px solid var(--hairline);
  transition:color .2s, border-color .2s;
}
.japan .btn-quiet:hover{ color:var(--vermillion); border-color:var(--vermillion); }
/* Disabled while a basket save is still in flight — see giftSentSync in
   backtojapan.html. Not just inert: the label swaps to say why. */
.japan .btn-quiet:disabled{ opacity:.55; cursor:default; }
.japan .btn-quiet:disabled:hover{ color:var(--ink-soft); border-color:var(--hairline); }
.japan .sent-done{
  font-size:15.5px; color:var(--ink);
}
.japan .sent-nudge{
  margin-top:8px;
  font-size:14.5px; color:var(--ink-soft);
}
/* a submit button that reads as a link (the undo, the save retry) — the
   box comes from the global .link-btn; this page only changes the accent. */
.japan .link-btn:hover{ color:var(--vermillion); border-color:var(--vermillion); }
/* leave-a-note: quietly beneath the fold */
.japan .gift-note{
  max-width:560px; margin:72px auto 0;
}
.japan .gift-note h3{
  font-size:12px; letter-spacing:.26em; text-transform:uppercase;
  font-weight:400; margin-bottom:6px;
}
.japan .gift-note-sub{
  color:var(--ink-soft); font-size:15px; margin-bottom:24px;
}
.japan .note-form textarea{
  width:100%; min-height:110px; resize:vertical;
  background:#fff; border:1px solid var(--hairline);
  padding:16px 18px;
  font-size:15.5px;
  color:var(--ink);
}
.japan .note-form textarea:focus{
  outline:none; border-color:var(--sage);
}
.japan .note-form button{
  margin-top:20px; cursor:pointer; font-family:var(--sans);
}
.japan .note-saved{
  margin-top:20px;
  font-size:15px; color:var(--ink-soft);
  animation:note-in .6s ease both;
}
@media (prefers-reduced-motion: reduce){ .japan .note-saved{ animation:none; } }
.japan dl.bank{
  text-align:left;
  border-top:1px solid var(--hairline);
}
.japan dl.bank div{
  display:flex; justify-content:space-between; gap:16px;
  padding:10px 2px; border-bottom:1px solid var(--hairline);
}
.japan dl.bank dt{
  font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--ink-soft); padding-top:3px;
}
.japan dl.bank dd{
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:14px; color:var(--ink);
}
/* ---------- the running-total bar: the way down to the slip ---------- */
.japan .choice-bar{
  position:fixed; left:0; right:0; bottom:0; z-index:60;
  background:rgba(251,250,246,.96); backdrop-filter:blur(8px);
  border-top:1px solid var(--hairline);
  transform:translateY(100%);
  transition:transform .25s ease;
}
.japan .choice-bar.show{ transform:translateY(0); }
.japan .choice-inner{
  max-width:960px; margin:0 auto; padding:11px 24px;
  display:flex; align-items:center; gap:18px;
}
.japan .choice-copy{ flex:1; min-width:0; }
.japan .choice-copy .what{
  display:block;
  font-size:15.5px; color:var(--ink); line-height:1.35;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.japan .choice-copy .what strong{
  font-style:normal; font-weight:500; color:var(--vermillion);
}
/* the save state: the page tells guests their choices are kept with their
   invitation, so it has to say when a save is in flight or has failed —
   for a bank transfer the slip is the only record of what the money meant */
.japan .choice-copy .save-state{
  display:block;
  font-size:11.5px; letter-spacing:.04em; color:var(--ink-soft);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.japan .choice-copy .save-state.saved{ color:var(--green); }
/* The failed state carries an actionable "Try again" button, not just
   status text — nowrap+ellipsis would let it clip out of reach on narrow
   screens (the bar's "Review your gift" button already eats width there).
   Let this one state wrap instead of truncating. */
.japan .choice-copy .save-state.failed{
  color:var(--vermillion);
  overflow:visible; text-overflow:clip; white-space:normal;
}
.japan .choice-bar .btn-vermillion{
  flex-shrink:0;
  padding:12px 26px; font-size:11px;
}
/* keep the footer readable above the fixed bar */
.japan.bar-open{ padding-bottom:72px; }

@media (max-width:1080px){
  .japan .tategaki{ display:none; }
}
@media (max-width:900px){
  .japan .shelf{ grid-template-columns:1fr 1fr; }
}
@media (max-width:860px){
  /* One column — and the print moves up into the copy, between the opening
     line and the rest. "This is us a few days later in Kyoto, still
     grinning" is a caption for the photograph; stacked, the photograph sat
     below two more paragraphs, so the two were never on screen together and
     the sentence had nothing to point at.

     Done by flattening .opening-copy with display:contents so its children
     become grid items alongside .mat-frame, then ordering: everything up to
     and including .first keeps DOM order at 0, the print follows (last of
     the 0s), and .opening-rest is pushed after it. The gap goes to 0 and the
     print carries its own margins — a grid gap would otherwise open 44px
     between the kicker, the h1 and the rule, which are one block. Desktop is
     untouched: above this breakpoint .opening-copy is still a real box in
     column one. */
  .japan .opening{ grid-template-columns:1fr; gap:0; padding:48px 24px 64px; }
  .japan .opening-copy{ display:contents; }
  .japan .opening-rest{ order:1; }
  .japan .mat-frame{ max-width:400px; margin:32px auto 38px; width:100%; }
  .japan .opening-copy .first{ margin-bottom:0; }
}
@media (max-width:600px){
  .japan .shelf{ grid-template-columns:1fr; }
  .japan .vending{ grid-template-columns:1fr; gap:18px; }
  .japan .vend-panel{ align-items:flex-start; }
  .japan .slip{ padding:22px 20px 20px; }
  .japan .slip-chop{ right:18px; bottom:16px; width:50px; height:50px; font-size:24px; }
  .japan .slip-sent-line{ padding-right:64px; }
  .japan .choice-inner{ gap:12px; padding:10px 20px; }
  .japan .choice-bar .btn-vermillion{ padding:11px 18px; letter-spacing:.18em; }
}

/* ---------- footer ---------- */

footer{
  border-top:1px solid var(--hairline);
  text-align:center;
  padding:56px 24px 64px;
}
/* The gifts band closes itself with a hairline and the footer opens
   itself with one, in the same colour, in the same place. The band's
   is the meaningful rule, so the footer stands its own down when it
   follows the band. Parties with hide_gifts get no gifts section, the
   selector doesn't match, and the footer keeps its rule. */
.gifts + footer{ border-top:0; }
footer .mono{
  font-family:var(--serif); font-size:26px; letter-spacing:.1em; margin-bottom:8px;
}
footer .mono em{ font-style:italic; color:var(--green); }
footer p{ font-size:13px; color:var(--ink-soft); letter-spacing:.08em; }
footer p + p{ margin-top:6px; }
footer a{ color:var(--ink-soft); }
footer a:hover{ color:var(--green); }

/* ============================================================
   Gate — invitation-code entry (the site's front door)
   ============================================================ */

.gate{
  min-height:100vh;
  display:grid; place-items:center;
  text-align:center;
  padding:48px 24px;
  background:
    radial-gradient(ellipse 90% 55% at 50% -10%, rgba(185,206,218,.28), transparent 60%),
    var(--ivory);
}
.gate h1{ font-size:clamp(48px, 9vw, 96px); }
.gate .lede{
  color:var(--ink-soft); max-width:420px; margin:18px auto 34px;
}
.code-form{
  display:flex; gap:12px; justify-content:center; flex-wrap:wrap;
}
.code-form input{
  font-family:var(--sans); font-size:15px; letter-spacing:.28em;
  text-align:center; text-transform:uppercase;
  background:#fff;
  border:1px solid var(--sage);
  color:var(--ink);
  padding:14px 22px; border-radius:999px;
  width:290px;
}
.code-form input::placeholder{ color:var(--sage); letter-spacing:.28em; }
.code-form input:focus{ outline:none; border-color:var(--green); }
.form-error{
  color:#8A4B3D; font-size:15px;
  max-width:380px; margin:22px auto 0;
}
.gate-hint{
  margin-top:34px;
  font-size:13px; color:var(--ink-soft); letter-spacing:.04em;
  max-width:360px; margin-left:auto; margin-right:auto;
}

/* ============================================================
   Form fields (shared by the RSVP band form and the admin panel)
   ============================================================ */

.field{ margin-bottom:14px; }
.field:last-child{ margin-bottom:0; }
.field label{
  display:block;
  font-size:11px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-soft);
  margin-bottom:7px;
}
.field input[type=text], .field textarea{
  width:100%;
  padding:12px 14px;
  border:1px solid var(--hairline);
  background:var(--ivory);
  font-family:var(--sans); font-weight:300; font-size:15px;
  color:var(--ink);
}
.field input[type=text]:focus, .field textarea:focus{
  outline:none; border-color:var(--sage);
}
.field textarea{ min-height:96px; resize:vertical; }

.rsvp-actions{ text-align:center; margin-top:28px; }

/* ============================================================
   Admin dashboard (internal, Basic-Auth protected)
   ============================================================ */

.admin{ max-width:1280px; margin:0 auto; padding:48px 24px 96px; }
.admin h1{ font-size:44px; margin:0 0 6px; }
.admin .sub{ color:var(--ink-soft); font-size:14px; margin-bottom:30px; }
.admin h2{ font-size:28px; margin-top:44px; }

.stat-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));
  gap:16px; margin:24px 0;
}
.stat{
  background:#fff; border:1px solid var(--hairline);
  padding:18px 12px; text-align:center;
}
.stat .num{ font-family:var(--serif); font-size:40px; line-height:1.1; color:var(--green); }
.stat .lbl{ font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--ink-soft); }

/* The code column holds an editable field now, so the row needs more width
   than a phone has — the table scrolls inside its own box rather than
   dragging the whole page sideways. */
.table-scroll{ overflow-x:auto; }
table.parties{
  width:100%; min-width:900px; border-collapse:collapse;
  font-size:14px; background:#fff;
  border:1px solid var(--hairline);
}
table.parties td:first-child{ min-width:220px; }
table.parties th, table.parties td{
  padding:10px; text-align:left; vertical-align:top;
  border-bottom:1px solid var(--hairline);
}
table.parties th{
  font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  font-weight:400; color:var(--ink-soft); background:var(--ivory-2);
}
.invcode{
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:13px;
  background:var(--ivory-2);
  padding:2px 8px; border-radius:6px;
  white-space:nowrap;
}
/* status chips — admin-only now the guest RSVP moved to the form pills */
.chip{
  font-family:var(--sans); font-size:10px; font-weight:400;
  letter-spacing:.2em; text-transform:uppercase;
  padding:5px 14px; border-radius:999px; white-space:nowrap;
  border:1px solid var(--sage); color:var(--ink-soft);
}
.chip.yes{ background:var(--blue); color:var(--green-deep); border-color:var(--blue); }
.chip.no{ color:#8A4B3D; border-color:#C9A79B; }

.admin form.inline{ display:inline; }
/* Row actions stack: side by side they pushed the widened code column off the
   end of the table. */
table.parties td:last-child form.inline{ display:block; }
table.parties td:last-child form.inline + form.inline{ margin-top:6px; }
.btn-mini{
  font-family:var(--sans); font-size:11px; letter-spacing:.12em; text-transform:uppercase;
  padding:6px 14px; border-radius:999px;
  background:transparent; border:1px solid var(--sage); color:var(--green);
  cursor:pointer; text-decoration:none; display:inline-block;
  white-space:nowrap;
}
.btn-mini:hover{ border-color:var(--green); }
.btn-mini.danger{ color:#8A4B3D; border-color:#C9A79B; }
/* Links column: three per-party downloads. Side by side they widened the
   table past what the code column already costs it, so they stack too. */
table.parties td.links .btn-mini{ display:block; width:fit-content; }
table.parties td.links .btn-mini + .btn-mini{ margin-top:6px; }

.admin-panel{
  background:#fff; border:1px solid var(--hairline);
  padding:26px; margin-top:20px; max-width:560px;
}
.admin-panel .hint{ font-size:13px; color:var(--ink-soft); margin-bottom:14px; }

/* The two whole-guest-list downloads. Wider than the add-invitation panel:
   its copy is what stops the print PDF and the guest images being confused
   for each other, so it gets a readable measure. */
.print-panel{ max-width:640px; }
.print-actions{ display:flex; flex-wrap:wrap; gap:12px; margin:0; }
.print-panel .print-warning{ margin:14px 0 0; }
.admin > .hint{
  font-size:13px; color:var(--ink-soft); line-height:1.7;
  max-width:640px; margin:10px 0 18px;
}

/* Per-party invitation code: an editable field, not a label — the codes are
   assigned by hand before the invitations go to print. */
.code-edit{ display:flex; gap:6px; align-items:center; }
.code-edit input{
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:13px; text-transform:uppercase;
  background:var(--ivory-2); color:var(--ink);
  border:1px solid var(--hairline); border-radius:6px;
  padding:6px 10px; width:150px;
}
.code-edit input:focus{ outline:none; border-color:var(--green); background:#fff; }
/* Guest names: the party label is derived from them, so this — not a label
   field — is where a misspelt guest is corrected. */
.guest-line input[type=text]{
  font-family:var(--sans); font-size:14px;
  background:var(--ivory-2); color:var(--ink);
  border:1px solid var(--hairline); border-radius:6px;
  padding:6px 10px; width:150px;
}
.guest-line input[type=text]:focus{ outline:none; border-color:var(--green); background:#fff; }
.guest-line input[type=text]::placeholder{ color:var(--ink-soft); font-style:italic; }
.guest-line{ display:flex; gap:8px; align-items:center; margin-bottom:6px; }
.guest-edit .btn-mini{ margin-top:2px; }
/* The party label is a readout of those names, not an input. */
.party-name{ font-size:15px; }
.code-msg{ font-size:12px; line-height:1.5; margin-top:6px; max-width:220px; }
.code-msg.ok{ color:var(--green); }
.code-msg.error{ color:#8A4B3D; }

.dietary-list{ list-style:none; margin:14px 0 0; }
.dietary-list li{
  padding:8px 4px; border-bottom:1px solid var(--hairline);
  font-size:14px;
}
.dietary-list .who{ font-weight:400; }
.dietary-list .what{ color:var(--ink-soft); font-style:italic; }

/* ------------------------------------------------------------------
   Answers given off the website (admin only)

   Two surfaces, one endpoint: the chase list of everyone still to
   answer, and the same three-way control beside every guest in the
   table. An answer we recorded ourselves wears the .by-hand mark, so
   it never reads as a reply the guest made.
   ------------------------------------------------------------------ */
.stat .stat-sub{
  font-size:11px; color:var(--ink-soft);
  letter-spacing:0; text-transform:none; margin-top:2px;
}
.stat.hand .num{ color:var(--admin-hand); }

/* the control: three submit buttons, no JS — a press is a page load */
.seg{
  display:inline-flex; border:1px solid var(--hairline);
  border-radius:999px; overflow:hidden; background:var(--ivory-2);
}
.seg button{
  font-family:var(--sans); font-size:10px; font-weight:300;
  letter-spacing:.14em; text-transform:uppercase; line-height:1.6;
  padding:5px 11px; border:0; background:transparent;
  color:var(--ink-soft); cursor:pointer;
}
.seg button + button{ border-left:1px solid var(--hairline); }
.seg button:hover{ color:var(--ink); background:#fff; }
.seg button[aria-pressed="true"]{ background:var(--blue); color:var(--green-deep); }
.seg button.no[aria-pressed="true"]{ background:#EADCD6; color:#8A4B3D; }
.seg button.awa[aria-pressed="true"]{
  background:#fff; color:var(--ink-soft); box-shadow:inset 0 0 0 1px var(--hairline);
}
.by-hand{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--admin-hand); background:var(--admin-hand-wash);
  border:1px solid #E4D3B2; border-radius:999px;
  padding:3px 9px; white-space:nowrap; cursor:help;
}
/* In the table the control is a form of its own sitting inside the guest's
   line, beside the name box (see the admin template on why). */
.guest-line .seg{ margin:0; }

/* the chase list */
.chase{
  background:#fff; border:1px solid var(--hairline);
  border-top:3px solid var(--green); margin:26px 0 10px;
}
.chase-head{ display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; padding:16px 20px 4px; }
.chase-head h2{ font-size:26px; margin:0; }
.chase-count{ font-size:13px; color:var(--green); }
.admin .chase-lede{ margin:0; padding:0 20px 12px; max-width:720px; }
.chase-list{ list-style:none; margin:0; }
/* A grid, not a flex row: a party of two would otherwise start its controls
   wherever the row above ran out of name, and the column of Coming/Can't
   pairs is the thing the eye runs down. */
.chase-list li{
  display:grid; grid-template-columns:230px 1fr auto;
  align-items:center; gap:16px;
  padding:11px 20px; border-top:1px solid var(--hairline);
}
.chase-party{ font-size:15px; }
.chase-party small{ display:block; font-size:11.5px; color:var(--ink-soft); }
.chase-guests{ display:flex; flex-wrap:wrap; gap:10px 20px; }
.chase-guest{ display:flex; align-items:center; gap:8px; min-width:236px; }
.chase-guest .nm{ font-size:13px; color:var(--ink-soft); width:118px; }
.chase-chased{ justify-self:end; }
.chase-more summary{
  display:flex; align-items:center; gap:10px; cursor:pointer;
  list-style:none; padding:12px 20px;
  border-top:1px solid var(--hairline);
  font-size:12px; letter-spacing:.16em; text-transform:uppercase; color:var(--green);
}
.chase-more summary::-webkit-details-marker{ display:none; }
.chase-more summary .mark{ font-size:18px; }
.chase-more .chase-list li:first-child{ border-top-color:var(--hairline); }
.btn-mini.chased{ color:var(--green); border-color:var(--green); }
.admin .chase-foot{
  margin:0; padding:12px 20px; max-width:none;
  border-top:1px solid var(--hairline); background:var(--ivory-2);
}
.chase-done{ border-top-color:var(--forest); padding:18px 20px; font-size:15px; color:var(--ink-soft); }

@media (max-width:760px){
  .chase-list li{ grid-template-columns:1fr; gap:10px; }
  .chase-chased{ justify-self:start; }
}
