/*
  საბა & მარიამი, all styling for index.html.

  Mobile-first: base rules target the narrowest phone, the only media queries
  are min-width. 769px hands the screen to the desktop notice, so nothing above
  it needs a layout.

  Blocks: 1 fonts, 2 tokens (day + night), 3 reset / scrollbar, 4 language,
  5 desktop notice, 6 loader, 7 entry, 8 controls (music / language / night),
  9 hero, 10 scroll cue, 11 petals, 12 sheets + reveal, 13 the letter,
  14 countdown, 15 timetable, 16 RSVP, 17 footer, 18 breakpoints,
  19 reduced motion.

  Shape rule, one system for the whole page: sheets and cards 22-28px radius,
  form fields and answer cards 12px, everything tappable that is a control
  (buttons, pills, toggles) fully round.

  Night mode: every colour is a token re-declared under html.is-night, and the
  global transition in block 3 cross-fades them. Any element with its own
  `transition` MUST start it with var(--tx), or it would override the global
  rule and snap (CSS does not merge transition lists).
*/

/* ==========================================================================
   1. FONTS
   font-display: block is only safe because the loader covers the block period
   and waits on document.fonts with a Georgian probe, see script.js.
   ========================================================================== */
@font-face {
    font-family: 'Vardi';
    /* MCh Ma Si Vardi, subset to Georgian and punctuation. Its digits are
       left OUT on purpose (its 3 reads as a 5), so numerals in script text
       fall through to Parisienne's, the next face in --script. */
    src: url('fonts/vardi.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: block;
}

@font-face {
    font-family: 'Parisienne';
    src: url('fonts/parisienne.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: block;
}

@font-face {
    font-family: 'Noto Serif Georgian';
    src: url('fonts/noto-serif-georgian.woff2') format('woff2');
    font-weight: 300 500;
    font-style: normal;
    font-display: block;
}

@font-face {
    font-family: 'Cormorant Garamond';
    src: url('fonts/cormorant.woff2') format('woff2');
    font-weight: 400 600;
    font-style: normal;
    font-display: block;
}

/* ==========================================================================
   2. TOKENS
   Day is sampled off the client's art: --rose is the wax seal, --ink the
   brown of the handwriting in text.jpeg, the papers the envelope stock and
   the blush of the garland's roses. One accent (rose) for the whole page.

   Night is sampled off the client's night art: the papers are the moonlit
   navy of its sky (#0e2038), lifted a step so the sheets still read as paper,
   and the accent warms from rose to the peach-gold of its lamplight.
   ========================================================================== */
:root {
    --vh: 1vh;

    --paper:   #fbf5f0;
    --paper-2: #f7ebe6;
    --paper-3: #f1e0da;
    --card:    #fffaf7;

    --rose:      #b47e74;
    --rose-deep: #94594f;
    --rose-lt:   #e6cdc5;
    --rose-wash: rgba(180, 126, 116, 0.1);

    --ink:      #6e4c43;
    --ink-deep: #573830;
    --ink-soft: #8f7168;

    --btn-bg:  #94594f;
    --btn-ink: #fffaf7;
    --error:   #a4443a;

    /* rgb triplets for the translucent glass and the shadows. */
    --glass:  252, 246, 242;
    --shadow: 110, 70, 60;

    --script: 'Vardi', 'Parisienne', cursive;
    --serif:  'Noto Serif Georgian', 'Cormorant Garamond', Georgia, serif;

    /* The rounded top lip that makes each section read as a sheet laid over
       the one before it. Used as both radius and negative margin. */
    --lip: 28px;

    /* Every element with its own transition list starts it with this. */
    --tx: background-color 0.9s ease, color 0.9s ease, border-color 0.9s ease, box-shadow 0.9s ease;
}

html.is-night {
    --paper:   #13203a;
    --paper-2: #182742;
    --paper-3: #0f1a31;
    --card:    #1d2d4b;

    --rose:      #e6b48f;
    --rose-deep: #f0c5a2;
    --rose-lt:   #344566;
    --rose-wash: rgba(230, 180, 143, 0.12);

    --ink:      #efe2d6;
    --ink-deep: #fbf2e9;
    --ink-soft: #afa6a3;

    --btn-bg:  #e6b48f;
    --btn-ink: #13203a;
    --error:   #f4a898;

    --glass:  16, 30, 54;
    --shadow: 2, 8, 20;
}

/* ==========================================================================
   3. RESET + SCROLLBAR + GLOBAL NIGHT TRANSITION
   ========================================================================== */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    transition: var(--tx);
}

html {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    transition: var(--tx), scrollbar-color 1.2s ease;
}

/* The thumb fades in only once the envelope has handed over. */
html.is-open {
    scrollbar-color: var(--rose) var(--paper-2);
}

html::-webkit-scrollbar {
    width: 6px;
}

html::-webkit-scrollbar-track {
    background-color: var(--paper-2);
}

html::-webkit-scrollbar-thumb {
    border-radius: 6px;
    background-color: transparent;
    transition: background-color 1.2s ease;
}

html.is-open::-webkit-scrollbar-thumb {
    background-color: var(--rose);
}

html,
body {
    overscroll-behavior-y: none;
}

body {
    background-color: var(--paper);
    color: var(--ink);
    font-family: var(--serif);
    font-size: 16px;
    font-weight: 400;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
}

/* Entry/loader scroll lock. NEVER position: fixed or overflow: hidden: that
   freezes Chrome iOS's boot viewport ~110px short. The gesture is eaten in JS. */
html.is-locked,
body.is-locked {
    overscroll-behavior: none;
    touch-action: none;
}

img {
    display: block;
    max-width: 100%;
}

button,
input {
    font: inherit;
    color: inherit;
}

/* ==========================================================================
   4. LANGUAGE
   Both languages are in the markup; html.is-en decides which set shows. Each
   .l-en span also re-points the two font tokens, so English text picks up
   Parisienne / Cormorant even inside an element styled for Georgian.
   ========================================================================== */
html.is-en .l-ka,
html:not(.is-en) .l-en {
    display: none !important;
}

html.is-en {
    --script: 'Parisienne', cursive;
    --serif:  'Cormorant Garamond', Georgia, serif;
}

.l-en {
    --script: 'Parisienne', cursive;
    --serif:  'Cormorant Garamond', Georgia, serif;
}

/* One ampersand for both languages: Parisienne's. */
.amp {
    font-family: 'Parisienne', cursive;
    color: var(--rose);
}

/* ==========================================================================
   5. DESKTOP NOTICE
   ========================================================================== */
.mobile-prompt {
    display: none;
}

/* ==========================================================================
   6. LOADER
   ========================================================================== */
.loader {
    position: fixed;
    inset: 0;
    z-index: 10050;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 20px;
    background-color: var(--paper);
    transition: var(--tx), opacity 0.9s ease;
}

.loader.is-hidden {
    opacity: 0;
    pointer-events: none;
}

.loader.is-removed {
    display: none;
}

.loader__mark {
    font-family: 'Parisienne', cursive;
    font-size: 54px;
    line-height: 1;
    color: var(--ink-deep);
    animation: breathe 3.4s ease-in-out infinite;
}

.loader__amp {
    margin: 0 4px;
    font-size: 30px;
    color: var(--rose);
}

@keyframes breathe {
    0%, 100% { opacity: 0.45; }
    50%      { opacity: 1; }
}

.loader__track {
    position: relative;
    width: 150px;
    height: 1px;
    overflow: hidden;
    background-color: var(--rose-lt);
}

.loader__bar {
    position: absolute;
    inset: 0;
    transform: scaleX(0);
    transform-origin: left center;
    background-color: var(--rose);
    transition: var(--tx), transform 0.2s linear;
}

.loader__pct {
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 14px;
    letter-spacing: 0.2em;
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   7. ENTRY
   Two layers peeled off in one tap: the envelope still (entry.jpg, the video's
   first frame) with the hint over it, and the video underneath.
   ========================================================================== */
.entry {
    position: fixed;
    /* inset: 0 reaches the true bottom on its own once the document is
       JS-locked; a forced --vh / svh height oversizes it under Chrome's bar. */
    inset: 0;
    z-index: 10000;
    overflow: hidden;
    background-color: #f3e9e2;
    cursor: pointer;
    transition: opacity 1.1s ease;
}

.entry.is-gone {
    opacity: 0;
    pointer-events: none;
}

.entry.is-removed {
    display: none;
}

/* Same crop as .hero__still, so the last frame and the hero line up. */
.entry__video,
.entry__still {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
    pointer-events: none;
}

.entry__video {
    z-index: 1;
}

.entry__still {
    z-index: 2;
    transition: opacity 0.55s ease;
}

.entry.is-playing .entry__still {
    opacity: 0;
}

.entry.is-armed .entry__hint {
    opacity: 0;
    animation: none;
}

.entry__tap-cover {
    position: absolute;
    inset: 0;
    z-index: 4;
    background: url('entry.jpg') center center / cover no-repeat;
    opacity: 0;
    pointer-events: none;
    cursor: pointer;
    transition: opacity 0.5s ease;
}

.entry__tap-cover.is-visible {
    opacity: 1;
    pointer-events: auto;
}

/* In the clear band of paper under the wax seal. Frosted, because the paper
   it lands on is nearly the same tone as the glass. Fixed colours: the
   envelope is the same picture in day and night. */
.entry__hint {
    position: absolute;
    left: 50%;
    bottom: 16%;
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 9px 26px 10px;
    border: 1px solid rgba(180, 126, 116, 0.35);
    border-radius: 999px;
    background-color: rgba(252, 246, 242, 0.8);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    box-shadow: 0 4px 22px rgba(110, 70, 60, 0.16);
    transform: translateX(-50%);
    white-space: nowrap;
    color: #573830;
    animation: hint-pulse 3.4s ease-in-out infinite;
    transition: opacity 0.4s ease;
}

.entry__hint-ka {
    font-family: 'Vardi', 'Parisienne', cursive;
    font-size: 25px;
    line-height: 1.3;
}

.entry__hint-en {
    font-family: 'Parisienne', cursive;
    font-size: 17px;
    line-height: 1.2;
    color: #94594f;
}

@keyframes hint-pulse {
    0%, 100% { opacity: 0.8; }
    50%      { opacity: 1; }
}

/* ==========================================================================
   8. CONTROLS
   Fixed so they work from anywhere on the page. Held invisible and untappable
   until .is-open, so none can be hit through the envelope. Music sits alone on
   the left; language and night stack on the right.
   ========================================================================== */
.ctl {
    position: fixed;
    z-index: 60;
    border: 1px solid var(--rose-lt);
    border-radius: 999px;
    background-color: rgba(var(--glass), 0.78);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    box-shadow: 0 2px 16px rgba(var(--shadow), 0.12);
    color: var(--ink-deep);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: var(--tx), opacity 0.9s ease, transform 0.9s ease, visibility 0.9s;
}

.is-open .ctl {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s, 0s, 0s, 0s, 1.4s, 1.4s, 1.4s;
}

.ctl:active {
    transform: scale(0.95);
}

/* ----- Music: four bars that dance while it plays, rest flat when not ----- */
.music {
    top: calc(14px + env(safe-area-inset-top, 0px));
    left: calc(14px + env(safe-area-inset-left, 0px));
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    padding: 0;
}

.music__bars {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 14px;
}

.music__bars span {
    width: 2px;
    height: 100%;
    border-radius: 2px;
    background-color: var(--rose-deep);
    transform: scaleY(0.2);
    transform-origin: bottom center;
    transition: var(--tx), transform 0.4s ease;
}

.music.is-playing .music__bars span {
    animation: bar 1.1s ease-in-out infinite;
}

.music.is-playing .music__bars span:nth-child(2) { animation-delay: -0.35s; }
.music.is-playing .music__bars span:nth-child(3) { animation-delay: -0.7s; }
.music.is-playing .music__bars span:nth-child(4) { animation-delay: -0.2s; }

@keyframes bar {
    0%, 100% { transform: scaleY(0.3); }
    50%      { transform: scaleY(1); }
}

/* ----- Night: sun / moon pill ----- */
.night-toggle {
    top: calc(14px + env(safe-area-inset-top, 0px));
    right: calc(14px + env(safe-area-inset-right, 0px));
    padding: 3px;
}

.night-toggle__track {
    position: relative;
    display: block;
    width: 48px;
    height: 24px;
}

.night-toggle__thumb {
    position: absolute;
    top: 0;
    left: 0;
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background-color: var(--card);
    box-shadow: 0 1px 5px rgba(var(--shadow), 0.2);
    transition: var(--tx), transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

html.is-night .night-toggle__thumb {
    transform: translateX(24px);
}

.night-toggle__icon {
    grid-area: 1 / 1;
    font-size: 13px;
    line-height: 1;
    transition: opacity 0.4s ease;
}

.night-toggle__icon--moon { opacity: 0; }
html.is-night .night-toggle__icon--sun  { opacity: 0; }
html.is-night .night-toggle__icon--moon { opacity: 1; }

/* ----- Language: ქა / EN pill with a sliding thumb behind the live one ----- */
.lang-toggle {
    top: calc(56px + env(safe-area-inset-top, 0px));
    right: calc(14px + env(safe-area-inset-right, 0px));
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    width: 56px;
    height: 30px;
    padding: 3px;
}

.lang-toggle__opt {
    position: relative;
    z-index: 1;
    text-align: center;
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--ink-soft);
    transition: var(--tx);
}

.lang-toggle__opt--ka { font-family: 'Noto Serif Georgian', serif; }
.lang-toggle__opt--en { font-family: 'Cormorant Garamond', serif; font-size: 12.5px; font-weight: 600; }

html:not(.is-en) .lang-toggle__opt--ka,
html.is-en .lang-toggle__opt--en {
    color: var(--btn-ink);
}

.lang-toggle__thumb {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 24px;
    height: 22px;
    border-radius: 999px;
    background-color: var(--btn-bg);
    transition: var(--tx), transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}

html.is-en .lang-toggle__thumb {
    transform: translateX(24px);
}

/* ==========================================================================
   9. HERO
   hero.jpg is the video's last frame, cover-cropped exactly like the video, so
   the overlay fades off a picture the guest is already looking at. No
   ken-burns: any scale on one side of that cross-fade turns it into a lurch.
   ========================================================================== */
.page {
    position: relative;
    overflow: hidden;
}

.hero {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Floors first, then the JS pin. Order matters, the pin must win. */
    min-height: 100vh;
    min-height: 100svh;
    overflow: hidden;
    background-color: var(--paper);
    /* Room for .hero__foot (date, place, cue) pinned to the bottom edge, so
       on a short phone the hero grows instead of running the names into it. */
    padding-bottom: 220px;
}

.vh-lock .hero {
    min-height: calc(var(--vh) * 100);
}

.hero__still {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
}

.hero__still--night {
    z-index: 2;
    opacity: 0;
    transition: opacity 0.9s ease;
}

html.is-night .hero__still--night {
    opacity: 1;
}

/* Lifts the sky band the names sit in. Two layers cross-faded on opacity,
   because a gradient itself cannot transition. */
.hero__veil,
.hero__veil::after {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.hero__veil {
    z-index: 3;
    background-image: radial-gradient(
        ellipse 110% 38% at 50% 14%,
        rgba(252, 246, 242, 0.88) 0%,
        rgba(252, 246, 242, 0.5) 52%,
        rgba(252, 246, 242, 0) 100%
    );
    transition: opacity 0.9s ease;
}

.hero__veil::after {
    content: '';
    background-image: radial-gradient(
        ellipse 110% 40% at 50% 14%,
        rgba(10, 24, 48, 0.72) 0%,
        rgba(10, 24, 48, 0.36) 55%,
        rgba(10, 24, 48, 0) 100%
    );
    opacity: 0;
    transition: opacity 0.9s ease;
}

html.is-night .hero__veil {
    background-image: none;
}

html.is-night .hero__veil::after {
    opacity: 1;
}

.hero__content {
    position: relative;
    z-index: 4;
    width: 100%;
    padding: 0 14px;
    /* The ONE number that places the whole block. The fixed part keeps the
       names below the language pill (which ends ~86px down) on a short phone;
       the --vh part lets them sit higher in the sky on a tall one. */
    margin-top: calc(var(--vh) * 5 + 62px);
    text-align: center;
    --glow: 0 1px 2px rgba(var(--glass), 0.9), 0 2px 16px rgba(var(--glass), 0.95), 0 0 36px rgba(var(--glass), 0.7);
}

/* Held at opacity 0 until .is-open; the entrance then runs THROUGH the
   overlay fading out, so the video ending and the names arriving read as one
   movement. */
.hero__content > *,
.hero__date,
.hero__place {
    opacity: 0;
}

.is-open .hero__greet  { animation: rise 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.1s both; }
.is-open .hero__names  { animation: rise 1.8s cubic-bezier(0.22, 1, 0.36, 1) 0.2s both; }
.is-open .hero__invite { animation: rise 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.65s both; }
.is-open .hero__rule   { animation: rule-in 1.6s cubic-bezier(0.22, 1, 0.36, 1) 1.1s both; }
.is-open .hero__date   { animation: rise 1.6s cubic-bezier(0.22, 1, 0.36, 1) 1.5s both; }
.is-open .hero__place  { animation: rise 1.6s cubic-bezier(0.22, 1, 0.36, 1) 1.75s both; }

@keyframes rise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
}

@keyframes rule-in {
    from { opacity: 0; transform: scaleX(0); }
    to   { opacity: 1; transform: scaleX(1); }
}

.hero__greet {
    margin-bottom: 2px;
    font-family: var(--script);
    font-size: 28px;
    line-height: 1.4;
    color: var(--ink);
    text-shadow: var(--glow);
}

.hero__greet[hidden] {
    display: none;
}

/* One line, nowrap, sized off the screen width: "საბა & მარიამი" measures
   ~4.55em in Vardi and the hero leaves 100vw - 28px, so /5.6 keeps clear air
   on every phone up to the 481px step, where it goes back to a fixed size.
   Re-measure if either name changes. */
.hero__names {
    font-family: var(--script);
    /* Vardi's word space is wide; pulls the names in around the ampersand. */
    word-spacing: -0.12em;
    font-size: calc((100vw - 28px) / 5.6);
    font-weight: normal;
    line-height: 1.25;
    white-space: nowrap;
    color: var(--ink-deep);
    text-shadow: var(--glow);
}

/* Parisienne: "Saba & Mariami" measures ~6.9em. */
.hero__names .l-en {
    font-size: calc((100vw - 28px) / 7.2);
}

/* Client's request: მარიამი sits a touch closer to the ampersand. Vardi's
   მ opens with a wide left bearing, so the gap after "&" read larger than the
   one before it. em-based so it holds at every size; the footer shares it. */
.name-2 {
    margin-left: -0.14em;
}

.hero__names .amp {
    margin: 0 0.05em;
    font-size: 0.7em;
}

.hero__invite {
    margin-top: 4px;
    font-size: 15px;
    letter-spacing: 0.05em;
    line-height: 1.5;
    color: var(--ink-deep);
    text-shadow: var(--glow);
}

.hero__invite .l-en {
    font-size: 18px;
    font-weight: 500;
}

.hero__rule {
    display: block;
    width: 56px;
    height: 1px;
    margin: 14px auto 0;
    background-color: var(--rose);
}

.hero__foot {
    position: absolute;
    left: 0;
    right: 0;
    /* Clear of the next sheet's lip, which rides 28px up over the hero. */
    bottom: calc(var(--lip) + 20px + env(safe-area-inset-bottom, 0px));
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 16px;
    text-align: center;
    --glow: 0 1px 2px rgba(var(--glass), 0.95), 0 2px 14px rgba(var(--glass), 0.95), 0 0 30px rgba(var(--glass), 0.8);
}

.hero__date {
    font-size: 18px;
    font-weight: 500;
    letter-spacing: 0.1em;
    color: var(--ink-deep);
    text-shadow: var(--glow);
}

.hero__date .l-en {
    font-size: 21px;
    font-weight: 600;
}

.hero__place {
    margin-top: 2px;
    font-family: var(--script);
    font-size: 32px;
    line-height: 1.3;
    color: var(--ink-deep);
    text-shadow: var(--glow);
}

/* ==========================================================================
   9b. HERO -> LETTER PARALLAX
   The client asked for a smoother hand-over from the ceremony to the letter.
   As the guest scrolls out of the hero:
   - the picture drifts down at about a third of the scroll speed, so the
     letter sheet visibly slides OVER it like paper laid on a photograph;
   - the names and the date lift away and fade, a beat ahead of the sheet;
   - a paper tint (.hero__dim) softens the picture under the arriving sheet;
   - the top garland floats up into place as the letter comes into view.

   CSS scroll-driven animations, NOT a scroll listener: the CLAUDE.md iOS rule
   bans parallax that reads innerHeight / getBoundingClientRect every scroll
   frame, because Chrome iOS resizes its toolbar mid-scroll and the maths
   lurches. Here the browser drives it, and every range is in scroll pixels or
   the PINNED --vh, never the live viewport, so a toolbar resize changes
   nothing. Browsers without scroll timelines simply get the still, layered
   page; reduced motion gets it too.

   `animation-timeline` must come AFTER the `animation` shorthand, which
   resets it.
   ========================================================================== */
.hero__dim {
    position: absolute;
    inset: 0;
    z-index: 3;
    background-color: var(--paper);
    opacity: 0;
    pointer-events: none;
}

@supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
        .hero__still {
            animation: hero-drift linear both;
            animation-timeline: scroll(root block);
            animation-range: 0 calc(var(--vh) * 100);
        }

        .hero__content,
        .hero__foot {
            animation: hero-lift linear both;
            animation-timeline: scroll(root block);
            animation-range: 0 calc(var(--vh) * 55);
        }

        .hero__dim {
            animation: hero-dim linear both;
            animation-timeline: scroll(root block);
            animation-range: calc(var(--vh) * 15) calc(var(--vh) * 95);
        }

        /* view(): progress follows the garland's own way into the screen. */
        .bloom--top {
            animation: bloom-float linear both;
            animation-timeline: view();
            animation-range: entry 0% cover 35%;
        }
    }
}

@keyframes hero-drift {
    to { transform: translateY(calc(var(--vh) * 34)); }
}

@keyframes hero-lift {
    to { transform: translateY(calc(var(--vh) * -8)); opacity: 0; }
}

@keyframes hero-dim {
    to { opacity: 0.45; }
}

@keyframes bloom-float {
    from { transform: translateY(56px); }
    to   { transform: none; }
}

/* ==========================================================================
   10. SCROLL CUE
   The hero looks finished at the fold, so this is what tells a guest there is
   more. Entrance is a transition, not an animation: an animation with `both`
   would pin opacity 1 and fight the .is-scrolled fade.
   ========================================================================== */
.cue {
    display: block;
    margin-top: 16px;
    opacity: 0;
    pointer-events: none;
    transition: var(--tx), opacity 1.1s ease;
}

.is-open .cue { opacity: 1; transition-delay: 0s, 0s, 0s, 0s, 2.6s; }
.is-open.is-scrolled .cue { opacity: 0; transition-delay: 0s; }

.cue__ring {
    position: relative;
    display: block;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background-color: rgba(var(--glass), 0.84);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    box-shadow: 0 2px 16px rgba(var(--shadow), 0.16);
    animation: cue-bob 2.6s ease-in-out infinite;
}

.cue__ring::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 1px solid var(--rose);
    border-radius: 50%;
    animation: cue-ping 2.6s ease-out infinite;
}

.cue__chev {
    position: absolute;
    left: 50%;
    top: 11px;
    width: 9px;
    height: 9px;
    margin-left: -4.5px;
    border-right: 1.5px solid var(--ink-deep);
    border-bottom: 1.5px solid var(--ink-deep);
    transform: rotate(45deg);
    animation: chev-fade 2.6s ease-in-out infinite;
}

.cue__chev--2 {
    top: 17px;
    opacity: 0.45;
    animation-delay: 0.32s;
}

@keyframes cue-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(6px); }
}

@keyframes cue-ping {
    0%   { opacity: 0.5; transform: scale(1); }
    70%  { opacity: 0; transform: scale(1.8); }
    100% { opacity: 0; transform: scale(1.8); }
}

@keyframes chev-fade {
    0%, 100% { opacity: 0.35; }
    45%      { opacity: 1; }
}

/* ==========================================================================
   11. PETALS
   Each petal is parked at a fixed document offset by script.js and falls
   ~110vh from there on a loop, so the drift is spread down the sheets.
   ========================================================================== */
.petals {
    position: absolute;
    inset: 0;
    z-index: 20;
    overflow: hidden;
    pointer-events: none;
    visibility: hidden;
}

.petals.is-visible {
    visibility: visible;
}

.petal {
    position: absolute;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    will-change: transform, opacity;
}

/* A half-transparent white petal over the night navy reads as a grey smudge;
   screened, it reads as a petal catching the lamplight instead. */
html.is-night .petal {
    mix-blend-mode: screen;
}

@keyframes petalFallA {
    0%   { transform: translateY(-100px) translateX(0) rotate(0deg);    opacity: 0; }
    12%  { opacity: 0.6; }
    30%  { transform: translateY(26vh) translateX(28px) rotate(50deg); }
    55%  { transform: translateY(54vh) translateX(-14px) rotate(96deg); }
    80%  { transform: translateY(80vh) translateX(24px) rotate(148deg); opacity: 0.5; }
    93%  { opacity: 0; }
    100% { transform: translateY(110vh) translateX(-4px) rotate(183deg); opacity: 0; }
}

@keyframes petalFallB {
    0%   { transform: translateY(-100px) translateX(0) rotate(16deg);   opacity: 0; }
    12%  { opacity: 0.55; }
    32%  { transform: translateY(28vh) translateX(-26px) rotate(-44deg); }
    58%  { transform: translateY(58vh) translateX(18px) rotate(-90deg); }
    82%  { transform: translateY(82vh) translateX(-10px) rotate(-145deg); opacity: 0.45; }
    93%  { opacity: 0; }
    100% { transform: translateY(110vh) translateX(6px) rotate(-176deg); opacity: 0; }
}

@keyframes petalFallC {
    0%   { transform: translateY(-100px) translateX(0) rotate(-8deg);   opacity: 0; }
    14%  { opacity: 0.52; }
    28%  { transform: translateY(24vh) translateX(34px) rotate(36deg); }
    54%  { transform: translateY(54vh) translateX(4px) rotate(80deg); }
    80%  { transform: translateY(80vh) translateX(30px) rotate(126deg); opacity: 0.44; }
    93%  { opacity: 0; }
    100% { transform: translateY(110vh) translateX(12px) rotate(158deg); opacity: 0; }
}

/* ==========================================================================
   12. SHEETS
   Every section below the hero is a sheet of cotton paper laid over the one
   before it: rounded lip, upward shadow, negative margin. DOM order is paint
   order, which is exactly what the overlap needs.

   The paper grain (paper.jpg, the lemo-opuscar watercolour paper) is a grey
   tile MULTIPLIED over the sheet's background-color, so the tint stays a
   plain colour that night mode can transition.
   ========================================================================== */
.sheet {
    position: relative;
    z-index: 2;
    margin-top: calc(var(--lip) * -1);
    padding: 52px 22px 58px;
    border-top-left-radius: var(--lip);
    border-top-right-radius: var(--lip);
    background-color: var(--paper);
    background-image: url('paper.jpg');
    background-size: 512px 512px;
    background-blend-mode: multiply;
    box-shadow: 0 -14px 30px rgba(var(--shadow), 0.13);
}

/* Catches the light along the lip, so the fold reads where two sheets are
   nearly the same tone. */
.sheet::before {
    content: '';
    position: absolute;
    top: 0;
    left: 12%;
    right: 12%;
    height: 1px;
    background-color: rgba(255, 255, 255, 0.75);
}

html.is-night .sheet::before {
    background-color: rgba(255, 255, 255, 0.08);
}

.sheet--alt { background-color: var(--paper-2); }

.sheet__inner {
    width: 100%;
    max-width: 360px;
    margin: 0 auto;
    text-align: center;
}

.sheet__title {
    font-family: var(--script);
    text-wrap: balance;
    font-size: 44px;
    font-weight: normal;
    line-height: 1.45;
    color: var(--ink-deep);
}

.sheet__title .l-en {
    font-size: 40px;
}

/* The client's wording is long. Broken by hand (<br> in index.html) rather
   than balanced: see the iOS repaint note on .write. */
.sheet--rsvp .sheet__title       { font-size: 34px; text-wrap: wrap; }
.sheet--rsvp .sheet__title .l-en { font-size: 36px; }

/* Grows out of the centre once the heading lands, tying it to the title. */
.sheet__rule {
    position: relative;
    display: block;
    width: 72px;
    height: 1px;
    margin: 14px auto 0;
    background-color: var(--rose-lt);
}

.sheet__rule::after {
    content: '';
    position: absolute;
    top: -3px;
    left: 50%;
    width: 7px;
    height: 7px;
    margin-left: -3.5px;
    border: 1px solid var(--rose);
    background-color: var(--paper);
    transform: rotate(45deg);
}

.sheet--alt .sheet__rule::after { background-color: var(--paper-2); }

/* ===== Scroll reveal ===== */
.reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: var(--tx), opacity 1.2s ease, transform 1.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal.is-in {
    opacity: 1;
    transform: none;
}

/* The rule scales rather than rises; its in-state is re-stated at matching
   specificity or .reveal would pin it shut. */
.sheet__rule.reveal       { transform: scaleX(0); }
.sheet__rule.reveal.is-in { transform: scaleX(1); transition-delay: 0s, 0s, 0s, 0s, 0.2s, 0.2s; }

/* ==========================================================================
   13. THE LETTER
   The client's text, laid out as text.jpeg has it, between the garland and
   its mirror image.

   Two lemo-opuscar watercolour moves, both motivated by the content being a
   hand-written letter on painted paper:
   - .bloom: the garland is painted in behind a ragged wet edge
     (brush-mask.png, three panels: opaque | edge | clear, slid from 100% to
     0% so the edge travels left to right across the flowers).
   - .write: each line is uncovered left to right like ink leaving a pen.
     script.js writes them in order as they scroll into view, faster when
     the guest scrolls ahead of the pen (see writeLetter). The padding /
     negative-margin pair gives the clip room for the script's loops, which
     overhang the line box.
   ========================================================================== */
.sheet--letter {
    padding-top: 26px;
    padding-bottom: 40px;
}

.bloom {
    position: relative;
    display: block;
    width: 116%;
    max-width: none;
    margin-left: -8%;
    -webkit-mask-image: url('brush-mask.png');
    mask-image: url('brush-mask.png');
    -webkit-mask-size: 300% 100%;
    mask-size: 300% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: 100% 0;
    mask-position: 100% 0;
    transition: -webkit-mask-position 2.4s cubic-bezier(0.45, 0.05, 0.4, 1), mask-position 2.4s cubic-bezier(0.45, 0.05, 0.4, 1);
}

.bloom.is-in {
    -webkit-mask-position: 0 0;
    mask-position: 0 0;
}

.bloom__img {
    position: relative;
    width: 100%;
    height: auto;
}

/* Lamplight pooled behind the flowers, like the glow round the hero's arch.
   One warm colour in both modes; only its strength changes (opacity is what
   transitions, a gradient cannot). */
.bloom::before {
    content: '';
    position: absolute;
    /* Inside the box only: .bloom is masked (the brush reveal), so anything
       drawn past its edge is cut off flat and shows as a line. */
    inset: 2% 0 8%;
    /* closest-side: reaches zero exactly at the box edge, so the pool of
       light never shows the rectangle it is drawn in. */
    background-image: radial-gradient(closest-side, rgba(255, 214, 160, 0.5) 0%, rgba(255, 214, 160, 0.18) 55%, rgba(255, 214, 160, 0) 100%);
    opacity: 0.55;
    transition: opacity 0.9s ease;
    pointer-events: none;
}

html.is-night .bloom::before {
    opacity: 1;
}

/* Fairy lights woven through the garland (positions in script.js). A warm
   point with a soft halo, breathing on its own phase so no two pulse
   together. Gentle by day, where the paper is already light; the real glow
   is at night. */
.twinkle {
    position: absolute;
    width: var(--s, 10px);
    height: var(--s, 10px);
    margin: calc(var(--s, 10px) / -2) 0 0 calc(var(--s, 10px) / -2);
    border-radius: 50%;
    background-image: radial-gradient(circle, rgba(255, 252, 240, 1) 0%, rgba(255, 226, 170, 0.85) 22%, rgba(255, 200, 130, 0.25) 50%, rgba(255, 200, 130, 0) 72%);
    opacity: 0.55;
    animation: twinkle var(--d, 3.6s) ease-in-out var(--delay, 0s) infinite;
    pointer-events: none;
}

html.is-night .twinkle {
    mix-blend-mode: screen;
}

@keyframes twinkle {
    0%, 100% { transform: scale(0.7); opacity: 0.25; }
    50%      { transform: scale(1.15); opacity: 0.95; }
}

/* The mirror at the foot: stems reach up toward the letter. */
.bloom--bottom {
    margin-top: 6px;
    transform: scaleY(-1);
}

html.is-night .bloom__img {
    filter: brightness(0.86) saturate(0.9);
}

.letter {
    position: relative;
    z-index: 1;
    margin-top: -8px;
    font-family: var(--script);
    color: var(--ink);
}

/* Set like a written card: a large salutation, a small rose diamond under
   it, three short balanced paragraphs in a narrow measure, then the
   sign-off on its own with the names as the second-largest line.
   "ძვირფასო სტუმრებო" measures ~6.1em in Vardi, so 46px (~280px) holds one
   line on a 320px phone. */
.letter__title {
    font-size: 46px;
    font-weight: normal;
    line-height: 1.35;
    color: var(--ink-deep);
}

.letter__rule {
    position: relative;
    display: block;
    width: 64px;
    height: 1px;
    margin: 8px auto 4px;
    background-color: var(--rose-lt);
}

.letter__rule::after {
    content: '';
    position: absolute;
    top: -3px;
    left: 50%;
    width: 7px;
    height: 7px;
    margin-left: -3.5px;
    border: 1px solid var(--rose);
    background-color: var(--paper);
    transform: rotate(45deg);
}

/* Balanced so no paragraph ends on a lone word; the measure is held narrower
   than the sheet so the lines read as a card, not a page. Vardi's small
   x-height wants the size and the deeper ink. */
.letter__para {
    max-width: 330px;
    margin: 22px auto 0;
    font-size: 26px;
    line-height: 1.55;
    color: var(--ink-deep);
    text-wrap: balance;
}

.letter__sign {
    margin-top: 34px;
    line-height: 1.4;
}

.letter__sign .write:first-child {
    font-size: 26px;
    color: var(--rose-deep);
}

.letter__sign .write:last-child {
    margin-top: 2px;
    font-size: 40px;
    color: var(--ink-deep);
}

.l-en.letter .letter__title { font-size: 46px; }
.l-en.letter .letter__para  { font-size: 25px; line-height: 1.5; }
.l-en.letter .letter__sign .write:first-child { font-size: 27px; }
.l-en.letter .letter__sign .write:last-child  { font-size: 38px; }

.write {
    display: block;
    padding: 0.6em 0.6em 0.7em;
    margin: -0.6em -0.6em -0.7em;
    -webkit-clip-path: inset(0 100% 0 0);
    clip-path: inset(0 100% 0 0);
    transition: var(--tx), -webkit-clip-path 1s linear, clip-path 1s linear;
}

.write.is-in {
    -webkit-clip-path: inset(0 0 0 0);
    clip-path: inset(0 0 0 0);
}

/* Each .write is exactly one rendered line (script.js measured it), so it
   must not inherit the paragraph's text-wrap: balance. Older iOS Safari
   leaves balanced text painted in its old colour when night mode swaps the
   tokens: the letter stayed dark brown on navy for the client. */
.write {
    text-wrap: wrap;
}

/* Night colours set ON these elements, not only inherited through the token
   change, so iOS always repaints them; and Vardi's hairlines thickened a
   touch, because light script on navy reads thinner than dark on cream. */
html.is-night .letter__title,
html.is-night .letter__para,
html.is-night .letter__sign .write:last-child,
html.is-night .sheet--rsvp .sheet__title {
    color: #fbf2e9;
    -webkit-text-stroke: 0.35px #fbf2e9;
}

/* After a language switch the new letter appears whole, never re-written. */
.write.is-instant {
    transition: var(--tx);
}

/* Once a line is written the clip goes entirely, so no swash of the script
   can ever be cut by the line's own box. */
.write.is-done {
    -webkit-clip-path: none;
    clip-path: none;
}

/* ----- How the letter arrives -----
   The client wanted a more interesting entrance for the text section. The
   envelope opened at the start, so the letter now arrives like a sheet being
   laid down onto the scene: it rides up tilted back a few degrees and a touch
   smaller, and settles flat and full width as it covers the hero. Then a pale
   blush wash blooms behind the salutation (the watercolour "wash" move), just
   before the pen starts writing.

   Text section only: nothing here touches the hero's own rules. Same engine
   as the hero parallax (CSS scroll timelines, no scroll listener; view()
   tracks the sheet itself, so no viewport maths), and the same fallback: no
   support or reduced motion leaves the plain layered sheet. Only transform
   and opacity animate. */
.letter__wash {
    position: absolute;
    top: -30px;
    left: 50%;
    z-index: 0;
    width: 120%;
    height: 210px;
    margin-left: -60%;
    background-image: radial-gradient(closest-side, rgba(230, 196, 186, 0.55) 0%, rgba(230, 196, 186, 0.22) 55%, rgba(230, 196, 186, 0) 100%);
    -webkit-mask-image: url('brush-mask.png');
    mask-image: url('brush-mask.png');
    -webkit-mask-size: 300% 100%;
    mask-size: 300% 100%;
    -webkit-mask-position: 0 0;
    mask-position: 0 0;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    pointer-events: none;
    transition: opacity 0.9s ease;
}

/* Gradients cannot cross-fade, so night is a second layer faded in. */
.letter__wash::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: radial-gradient(closest-side, rgba(230, 180, 143, 0.22) 0%, rgba(230, 180, 143, 0.08) 55%, rgba(230, 180, 143, 0) 100%);
    opacity: 0;
    transition: opacity 0.9s ease;
}

html.is-night .letter__wash::after { opacity: 1; }
html.is-night .letter__wash { background-image: none; }

.letter__title,
.letter__rule {
    position: relative;
    z-index: 1;
}

@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        .sheet--letter {
            transform-origin: 50% 0;
            animation: letter-lay-down linear both;
            animation-timeline: view();
            animation-range: entry 0% entry 60%;
        }

        .letter__wash {
            animation: letter-wash ease-out both;
            animation-timeline: view();
            animation-range: entry 20% cover 30%;
        }
    }
}

@keyframes letter-lay-down {
    from { transform: perspective(1100px) rotateX(14deg) scale(0.93); }
    to   { transform: none; }
}

@keyframes letter-wash {
    from { opacity: 0; transform: scale(0.6); }
    to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   14. COUNTDOWN
   Plain numbers on the paper, divided by hairlines rather than boxed: the
   only thing a guest needs here is the figure.
   ========================================================================== */
.cd {
    display: flex;
    justify-content: center;
    margin-top: 26px;
}

.cd__unit {
    flex: 1 1 0;
    min-width: 0;
    padding: 4px 2px;
}

.cd__unit + .cd__unit {
    border-left: 1px solid var(--rose-lt);
}

.cd__num {
    display: block;
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 40px;
    font-weight: 500;
    line-height: 1.05;
    color: var(--ink-deep);
    /* Without tabular figures the seconds jitter the row every tick. */
    font-variant-numeric: lining-nums tabular-nums;
}

.cd__label {
    display: block;
    margin-top: 4px;
    font-size: 12.5px;
    letter-spacing: 0.06em;
    color: var(--ink-soft);
}

.cd__label .l-en {
    font-size: 14px;
    letter-spacing: 0.08em;
}

.cd__date {
    margin-top: 24px;
    font-family: var(--script);
    font-size: 30px;
    line-height: 1.35;
    color: var(--rose-deep);
}

.cd__date .l-en {
    font-size: 27px;
}

/* ==========================================================================
   15. TIMETABLE
   Three cards, each one link to the venue on Google Maps. The painting runs
   to the card's edges and dissolves into it (a mask, not a gradient overlay,
   so it dissolves into whatever the card colour is, day or night). The
   painting is also revealed behind the wet brush edge, like the garland.
   ========================================================================== */
.tt {
    display: flex;
    flex-direction: column;
    gap: 24px;
    margin-top: 30px;
}

.tt__card {
    position: relative;
    display: block;
    overflow: hidden;
    padding-bottom: 26px;
    border: 1px solid var(--rose-lt);
    border-radius: 24px;
    background-color: var(--card);
    box-shadow: 0 6px 22px rgba(var(--shadow), 0.09);
    color: var(--ink);
    text-align: center;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    /* Lit by the IntersectionObserver in script.js as each card arrives. */
    opacity: 0;
    transform: translateY(22px);
    transition: var(--tx), opacity 1.1s ease, transform 1.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.tt__card.is-lit {
    opacity: 1;
    transform: none;
}

.tt__card.is-lit:active {
    transform: scale(0.985);
    transition: var(--tx), transform 0.15s ease;
}

.tt__art {
    position: relative;
    display: block;
    aspect-ratio: 5 / 4;
    -webkit-mask-image: url('brush-mask.png');
    mask-image: url('brush-mask.png');
    -webkit-mask-size: 300% 100%;
    mask-size: 300% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: 100% 0;
    mask-position: 100% 0;
    transition: -webkit-mask-position 2s cubic-bezier(0.45, 0.05, 0.4, 1) 0.25s, mask-position 2s cubic-bezier(0.45, 0.05, 0.4, 1) 0.25s;
}

.tt__card.is-lit .tt__art {
    -webkit-mask-position: 0 0;
    mask-position: 0 0;
}

.tt__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 40%;
    -webkit-mask-image: linear-gradient(to bottom, #000 70%, rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(to bottom, #000 70%, rgba(0, 0, 0, 0) 100%);
}

.tt__img--night {
    opacity: 0;
    transition: opacity 0.9s ease;
}

html.is-night .tt__img--night {
    opacity: 1;
}

/* When. The single largest thing in the card, so a guest scrolling for the
   hour finds it without reading anything else. */
.tt__time {
    position: relative;
    display: block;
    margin-top: -14px;
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 44px;
    font-weight: 500;
    line-height: 1;
    color: var(--ink-deep);
    font-variant-numeric: lining-nums tabular-nums;
}

/* What. In the letter's hand, in the accent. */
/* The script's loops (ჯ, წ, ძ) climb well above and below the line box,
   hence the loose line-height wherever it is set. */
.tt__label {
    display: block;
    margin-top: 6px;
    padding: 0 18px;
    font-family: var(--script);
    font-size: 32px;
    line-height: 1.65;
    color: var(--rose-deep);
}

.tt__label .l-en {
    font-size: 30px;
}

/* Where. */
.tt__venue {
    display: block;
    margin-top: 4px;
    padding: 0 18px;
    font-size: 17px;
    font-weight: 500;
    letter-spacing: 0.03em;
    line-height: 1.45;
    color: var(--ink-deep);
}

.tt__venue .l-en {
    font-size: 20px;
    font-weight: 600;
}

/* The visible affordance for the whole-card link. Reads as a button without
   competing with the venue name. */
.tt__map {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    padding: 8px 18px 9px;
    border: 1px solid var(--rose-lt);
    border-radius: 999px;
    background-color: var(--rose-wash);
    font-size: 13.5px;
    letter-spacing: 0.06em;
    color: var(--ink-deep);
}

.tt__map .l-en {
    font-size: 16px;
    font-weight: 500;
}

.tt__map::after {
    content: '\2197';
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 15px;
    line-height: 1;
    color: var(--rose-deep);
}

/* ==========================================================================
   16. RSVP
   Labels sit ABOVE every field; nothing relies on a placeholder. The answer
   cards and the count pills are real radio inputs, visually hidden but still
   focusable, so the keyboard and screen readers get a normal form.
   ========================================================================== */
.rsvp {
    margin-top: 28px;
    text-align: left;
}

.field + .field {
    margin-top: 16px;
}

.field__label {
    display: block;
    margin-bottom: 7px;
    padding: 0;
    font-size: 14px;
    letter-spacing: 0.03em;
    color: var(--ink-soft);
}

.field__label .l-en {
    font-size: 16px;
    font-weight: 500;
}

.field__input {
    display: block;
    width: 100%;
    height: 50px;
    padding: 0 16px;
    border: 1px solid var(--rose-lt);
    border-radius: 12px;
    background-color: var(--card);
    /* Georgian first: names are typed in either script, and Noto carries the
       digits so a mixed entry does not switch faces mid-word. */
    font-family: 'Noto Serif Georgian', 'Cormorant Garamond', Georgia, serif;
    /* 16px+ or iOS zooms the page on focus. */
    font-size: 17px;
    color: var(--ink-deep);
    outline: none;
    -webkit-appearance: none;
    appearance: none;
}

.field__input:focus {
    border-color: var(--rose);
    box-shadow: 0 0 0 3px var(--rose-wash);
}

.field__input.is-invalid {
    border-color: var(--error);
}

fieldset {
    min-width: 0;
    border: 0;
}

.choice,
.count {
    margin-top: 22px;
}

.count[hidden],
.companions[hidden] {
    display: none;
}

/* Visually hidden, still focusable and announced. */
.choice__opt input,
.pill input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
    clip-path: inset(50%);
}

.choice__opt {
    display: block;
    cursor: pointer;
}

.choice__opt + .choice__opt {
    margin-top: 10px;
}

.choice__card {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 58px;
    padding: 12px 16px;
    border: 1px solid var(--rose-lt);
    border-radius: 12px;
    background-color: var(--card);
    font-family: var(--script);
    font-size: 25px;
    line-height: 1.6;
    color: var(--ink-deep);
    transition: var(--tx), transform 0.15s ease;
}

.choice__card .l-en {
    font-size: 25px;
}

/* The radio dot, drawn on the card so the whole card is the target. */
.choice__card::before {
    content: '';
    flex: none;
    width: 18px;
    height: 18px;
    border: 1px solid var(--rose);
    border-radius: 50%;
    background-color: var(--card);
    box-shadow: inset 0 0 0 4px var(--card);
}

.choice__opt input:checked + .choice__card {
    border-color: var(--rose);
    background-color: var(--rose-wash);
}

.choice__opt input:checked + .choice__card::before {
    background-color: var(--rose-deep);
}

.choice__opt input:focus-visible + .choice__card,
.pill input:focus-visible + span {
    outline: 2px solid var(--rose);
    outline-offset: 2px;
}

.choice__opt:active .choice__card {
    transform: scale(0.99);
}

/* Which question the count answers follows the yes / no above it. */
.count__q { display: none; }
.rsvp.is-yes .count__q--yes,
.rsvp.is-no .count__q--no { display: inline; }

.count__pills {
    display: flex;
    gap: 10px;
}

.pill {
    flex: 1 1 0;
    cursor: pointer;
}

.pill span {
    display: grid;
    place-items: center;
    height: 48px;
    border: 1px solid var(--rose-lt);
    border-radius: 999px;
    background-color: var(--card);
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 22px;
    font-weight: 500;
    color: var(--ink-deep);
    font-variant-numeric: lining-nums;
    transition: var(--tx), transform 0.15s ease;
}

.pill input:checked + span {
    border-color: var(--btn-bg);
    background-color: var(--btn-bg);
    color: var(--btn-ink);
}

.pill:active span {
    transform: scale(0.96);
}

.companions {
    margin-top: 20px;
    padding: 16px 16px 18px;
    border: 1px dashed var(--rose-lt);
    border-radius: 12px;
}

.companions__note {
    margin-bottom: 12px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink-soft);
}

.companions__note .l-en {
    font-size: 16px;
}

.rsvp__error {
    margin-top: 16px;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--error);
}

.rsvp__error .l-en {
    font-size: 16.5px;
}

.rsvp__error[hidden] {
    display: none;
}

.rsvp__send {
    display: block;
    width: 100%;
    height: 54px;
    margin-top: 24px;
    border: 0;
    border-radius: 999px;
    background-color: var(--btn-bg);
    box-shadow: 0 6px 18px rgba(var(--shadow), 0.16);
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 0.06em;
    color: var(--btn-ink);
    cursor: pointer;
    transition: var(--tx), transform 0.15s ease, opacity 0.3s ease;
}

.rsvp__send .l-en {
    font-size: 19px;
    font-weight: 600;
}

.rsvp__send:active {
    transform: translateY(1px) scale(0.99);
}

.rsvp__send[disabled] {
    opacity: 0.6;
    cursor: default;
}

.rsvp-done,
.rsvp-closed {
    margin-top: 26px;
}

.rsvp-done[hidden],
.rsvp-closed[hidden] {
    display: none;
}

.rsvp-done__title {
    font-family: var(--script);
    font-size: 38px;
    line-height: 1.35;
    color: var(--ink-deep);
}

.rsvp-done__text {
    display: none;
    margin-top: 10px;
    font-size: 16px;
    line-height: 1.6;
    color: var(--ink);
}

.rsvp-done__text .l-en {
    font-size: 19px;
}

.rsvp-done.is-yes .rsvp-done__text--yes,
.rsvp-done.is-no .rsvp-done__text--no {
    display: block;
}

.rsvp-closed {
    font-size: 16px;
    color: var(--ink-soft);
}

/* ==========================================================================
   17. FOOTER
   ========================================================================== */
.sheet--foot {
    padding-top: 44px;
    padding-bottom: calc(30px + env(safe-area-inset-bottom, 0px));
    background-color: var(--paper-3);
    text-align: center;
}

.foot__names {
    font-family: var(--script);
    word-spacing: -0.12em;
    font-size: 44px;
    line-height: 1.3;
    color: var(--ink-deep);
}

.foot__names .amp {
    font-size: 0.75em;
}

.foot__date {
    margin-top: 4px;
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.3em;
    color: var(--ink-soft);
}

.credit {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 26px;
    font-family: 'Cormorant Garamond', Georgia, serif;
    font-size: 13px;
    color: var(--ink-soft);
    text-decoration: none;
    opacity: 0.3;
    transition: var(--tx), opacity 0.3s ease;
}

.credit:hover,
.credit:focus-visible {
    opacity: 0.75;
}

.credit__logo {
    width: 16px;
    height: auto;
}

html.is-night .credit__logo {
    filter: brightness(2.2);
}

/* ==========================================================================
   18. BREAKPOINTS
   481px is the large-phone step; 769px hands the screen to the desktop notice.
   ========================================================================== */
@media (min-width: 481px) {
    .hero__names        { font-size: 76px; }
    .hero__names .l-en  { font-size: 62px; }
    .hero__invite       { font-size: 16px; }
    .sheet__inner       { max-width: 400px; }
    .sheet__title       { font-size: 48px; }
    .letter__para       { max-width: 360px; font-size: 27px; }
    .l-en.letter .letter__para { font-size: 26px; }
    .cd__num            { font-size: 46px; }
    .tt__time           { font-size: 48px; }
}

@media (min-width: 769px) {
    .mobile-prompt {
        position: fixed;
        inset: 0;
        z-index: 20000;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        padding: 30px;
        background-color: var(--paper);
        text-align: center;
    }

    .mobile-prompt__icon {
        width: 52px;
        height: auto;
        margin-bottom: 10px;
        opacity: 0.8;
    }

    .mobile-prompt p {
        font-size: 17px;
        letter-spacing: 0.04em;
        color: var(--ink-soft);
    }

    .mobile-prompt__en {
        font-family: 'Cormorant Garamond', Georgia, serif;
        font-size: 19px !important;
    }
}

/* ==========================================================================
   19. REDUCED MOTION
   Ambient loops go entirely; reveals become instant rather than disabled, so
   nothing is left stuck hidden.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    .loader__mark,
    .entry__hint,
    .cue__ring,
    .cue__chev,
    .music__bars span,
    .twinkle {
        animation: none !important;
    }

    .cue__ring::after,
    .petals {
        display: none;
    }

    .is-open .hero__content > *,
    .is-open .hero__date,
    .is-open .hero__place {
        animation: none !important;
        opacity: 1;
        transform: none;
    }

    .reveal,
    .tt__card,
    .tt__art,
    .bloom,
    .write {
        transition-duration: 0.01ms !important;
        transition-delay: 0s !important;
    }
}
