/* ═══════════════════════════════════════════
   the-programme: the wedding day, hour by hour
   pages.md "the-programme" · screens.md "### 5. the-programme"

   Two modes, decided purely here:
   - standalone: this page draws its own copy of the wedding art (backdrop, lift,
     havan + lantern halos, petals, sparkles, butterflies).
   - in the wedding set, i.e. exactly when
       #sv-card[data-sv-last-event="wedding"][data-sv-programme="on"]
     the section is pulled up one page over the second half of the-functions'
     [data-sv-set="wedding"] wrapper, and this page's own art and loops are hidden:
     only the words (and their lift) move over the shared sticky stack.

   Lifecycle (app.js writes the attributes on #the-programme-page):
     pre-entrance  [data-sv-state="pending"], or "entering" before [data-sv-ready]
     entrance      [data-sv-state="entering"][data-sv-ready]
     end state     [data-sv-state="done"] (or no state at all): no animation
     loops         run only under [data-sv-view="on"]
   ═══════════════════════════════════════════ */

/* Hidden when the programme is empty (setProgramme). Wins over any display main.css gives .screen. */
#screen-the-programme[hidden] {
    display: none !important;
}

/* ───── The page element: one 100dvh snap area ───── */
.the-programme-page {
    position: relative;
    width: 100%;
    height: 100dvh;
    overflow: clip;
    isolation: isolate;
    scroll-snap-align: start;
    scroll-snap-stop: always;
}

/* ───── Standalone art (layer 1 + halos). Parallax 0.22, halos ride with it. ───── */
.the-programme-art {
    position: absolute;
    inset: 0;
    z-index: 0;
    container-type: size;
    pointer-events: none;
    transform: translate3d(0, calc(var(--d, 0) * var(--px-backdrop, 0.22) * -1px), 0);
}

/* The plate is the backdrop's own box: object-fit: cover, anchored 50% 100%, for a
   960 x 1440 (2:3) image. Anything inside positioned in % sits on the painted art at
   that image fraction, at every card aspect ratio. */
.the-programme-plate {
    position: absolute;
    left: 50%;
    bottom: 0;
    width: max(100cqw, 66.6667cqh);
    height: max(100cqh, 150cqw);
    margin-left: calc(max(100cqw, 66.6667cqh) / -2);
    background: var(--wash-wedding);
    transform-origin: 50% 70%;
}

.the-programme-backdrop {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 100%;
}

/* Glow halos over the painted light sources, measured from scene-wedding.webp
   (glass / flame centroids, as fractions of the 960 x 1440 image). Sizes are % of the
   plate's width (12% = 60px, 8% = 40px on a 360 x 740 card); margins are % of the same
   width, so -w/2 centres the disc on both axes. */
.the-programme-halo {
    position: absolute;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(255, 214, 140, 0.55), rgba(255, 214, 140, 0));
    pointer-events: none;
    animation: the-programme-flicker-lantern 2.2s var(--ease-breath) infinite;
    animation-play-state: paused;
}
.the-programme-halo--havan {          /* the havan fire, (479, 1206) px */
    left: 49.9%;
    top: 83.7%;
    width: 12%;
    margin: -6% 0 0 -6%;
    animation-name: the-programme-flicker-havan;
    animation-duration: 1.5s;
}
.the-programme-halo--lantern {
    width: 8%;
    margin: -4% 0 0 -4%;
}
.the-programme-halo--lantern-lu {     /* left cascade, upper lantern (112, 144) px */
    left: 11.7%;
    top: 10%;
    animation-duration: 2s;
    animation-delay: -0.4s;
}
.the-programme-halo--lantern-ll {     /* left cascade, lower lantern (77, 254) px */
    left: 8%;
    top: 17.6%;
    animation-duration: 2.4s;
    animation-delay: -1.1s;
}
.the-programme-halo--lantern-ru {     /* right cascade, upper lantern (850, 144) px */
    left: 88.5%;
    top: 10%;
    animation-duration: 2.2s;
    animation-delay: -0.8s;
}
.the-programme-halo--lantern-rl {     /* right cascade, lower lantern (885, 258) px */
    left: 92.2%;
    top: 17.9%;
    animation-duration: 2.1s;
    animation-delay: -1.6s;
}

/* ───── The lift (--scrim = --lift-sky on data-scene="wedding"). Rate 0, with the text. ───── */
.the-programme-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: var(--scrim);
    pointer-events: none;
}

/* ───── Loops: petals, sparkles, butterflies. Outer bands / below 58% only. ───── */
.the-programme-loops {
    position: absolute;
    inset: 0;
    z-index: 2;
    overflow: hidden;
    pointer-events: none;
}

/* Rose petals: CSS ellipses with a 2-stop fill, falling from the top corners down the
   outer bands with a ±4% sway. */
.the-programme-petal {
    position: absolute;
    top: -4%;
    width: 12px;
    height: 8px;
    margin-left: -6px;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    opacity: 0;
    animation: the-programme-fall 11s var(--ease-drift) infinite;
    animation-play-state: paused;
}
.the-programme-petal--crimson {
    background: radial-gradient(ellipse at 35% 35%, rgba(214, 72, 92, 1) 0%, rgba(177, 30, 53, 1) 70%);
}
.the-programme-petal--blush {
    background: radial-gradient(ellipse at 35% 35%, rgba(250, 222, 220, 1) 0%, rgba(242, 184, 181, 1) 70%);
}
.the-programme-petal--cream {
    background: radial-gradient(ellipse at 35% 35%, rgba(255, 252, 248, 1) 0%, rgba(255, 241, 230, 1) 70%);
}
.the-programme-petal:nth-child(1) { width: 12px; height: 8px; animation-duration: 9s;    animation-delay: -1s; }
.the-programme-petal:nth-child(2) { width: 10px; height: 7px; animation-duration: 12.5s; animation-delay: -7s; }
.the-programme-petal:nth-child(3) { width: 14px; height: 9px; animation-duration: 11s;   animation-delay: -4s; }
.the-programme-petal:nth-child(4) { width: 9px;  height: 6px; animation-duration: 14s;   animation-delay: -11s; }
.the-programme-petal:nth-child(5) { width: 13px; height: 8px; animation-duration: 10s;   animation-delay: -3s; }
.the-programme-petal:nth-child(6) { width: 11px; height: 7px; animation-duration: 13s;   animation-delay: -9s; }
.the-programme-petal:nth-child(7) { width: 9px;  height: 6px; animation-duration: 9.5s;  animation-delay: -6s; }
.the-programme-petal:nth-child(8) { width: 12px; height: 8px; animation-duration: 12s;   animation-delay: -0.5s; }

/* Sparkles: _shared/effects/sparkles.css light variant (.sparkle-layer--light). Only
   their timing and play state are set here. */
.the-programme-sparkles {
    position: absolute;
    inset: 0;
}
.the-programme-sparkles .the-programme-sparkle {
    animation-play-state: paused;
}
.the-programme-sparkles .the-programme-sparkle:nth-child(1) { animation-duration: 3s; }
.the-programme-sparkles .the-programme-sparkle:nth-child(2) { animation-duration: 3.6s; }
.the-programme-sparkles .the-programme-sparkle:nth-child(3) { animation-duration: 4.4s; }
.the-programme-sparkles .the-programme-sparkle:nth-child(4) { animation-duration: 3.3s; }
.the-programme-sparkles .the-programme-sparkle:nth-child(5) { animation-duration: 4s; }
.the-programme-sparkles .the-programme-sparkle:nth-child(6) { animation-duration: 3.8s; }

/* Butterflies over the lawn: the flight wrapper follows a waypoint path, the image
   beats its wings (scaleX 1 -> 0.32 -> 1). */
.the-programme-flight {
    position: absolute;
    left: 12%;
    top: 90%;
    width: 26px;
    height: 26px;
    animation: the-programme-flight-a 12s var(--ease-drift) infinite;
    animation-play-state: paused;
}
.the-programme-flight--b {
    left: 80%;
    top: 91.5%;
    width: 23px;
    height: 23px;
    animation-name: the-programme-flight-b;
    animation-duration: 14s;
    animation-delay: -5s;
}
.the-programme-wing {
    display: block;
    width: 100%;
    height: 100%;
    animation: the-programme-wing 320ms var(--ease-breath) infinite;
    animation-play-state: paused;
}
.the-programme-flight--b .the-programme-wing {
    animation-duration: 290ms;
}

/* Loops run only while the page intersects the viewport. */
.the-programme-page[data-sv-view="on"] .the-programme-halo,
.the-programme-page[data-sv-view="on"] .the-programme-petal,
.the-programme-page[data-sv-view="on"] .the-programme-flight,
.the-programme-page[data-sv-view="on"] .the-programme-wing,
.the-programme-page[data-sv-view="on"] .the-programme-sparkles .the-programme-sparkle {
    animation-play-state: running;
}

/* More than one page away: no loop layer, and the art skips rendering. */
.the-programme-page[data-sv-view="far"] .the-programme-loops {
    display: none;
}
.the-programme-page[data-sv-view="far"] .the-programme-art {
    content-visibility: auto;
}

/* ───── Text block: --col-scene, from 9% down to <= 55% (JS fits it). ─────
   --the-programme-fit (1 -> 0.88) is written by the module's fit pass. */
.the-programme-text {
    position: absolute;
    top: 9%;
    left: 0;
    right: 0;
    z-index: 3;
    width: var(--col-scene);
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.the-programme-title {
    margin: 0;
    max-width: 100%;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: calc(var(--type-title-sm) * var(--the-programme-fit, 1));
    line-height: 1.15;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--ink-title);
    text-shadow: var(--halo);
    overflow-wrap: anywhere;
}

.the-programme-script {
    margin: calc(6px * var(--the-programme-fit, 1)) 0 0;
    padding: 0 0.3em;
    font-family: var(--font-script);
    font-weight: 400;
    font-size: calc(var(--type-script) * var(--the-programme-fit, 1));
    line-height: var(--leading-script);
    color: var(--ink-script);
    text-shadow: var(--halo);
}

/* 1px rule, --rule-width long, with 5px diamond ends. */
.the-programme-divider {
    position: relative;
    flex: none;
    width: var(--rule-width);
    height: 1px;
    margin: calc(12px * var(--the-programme-fit, 1)) 0 calc(14px * var(--the-programme-fit, 1));
    background: var(--rule);
}
.the-programme-divider::before,
.the-programme-divider::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    width: 5px;
    height: 5px;
    background: var(--rule);
    transform: translate(-50%, -50%) rotate(45deg);
}
.the-programme-divider::after {
    left: 100%;
}

/* ───── Item groups ───── */
.the-programme-items {
    width: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: calc(14px * var(--the-programme-fit, 1));
}

.the-programme-item {
    min-width: 0;
    margin: 0;
    padding: 0;
}

.the-programme-item-title {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: calc(1.125rem * var(--the-programme-fit, 1));       /* 18px */
    line-height: 1.2;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--ink-title);
    text-shadow: var(--halo);
    overflow-wrap: anywhere;
}

.the-programme-item-detail {
    margin: calc(2px * var(--the-programme-fit, 1)) 0 0;
}
.the-programme-item-detail[hidden] {
    display: none;
}

.the-programme-item-time {
    display: block;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: calc(var(--type-detail-caps) * var(--the-programme-fit, 1));   /* 17px */
    line-height: 1.3;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--ink);
    text-shadow: var(--halo);
    overflow-wrap: anywhere;
}

/* Buyer lines wrap (an unbreakable token or a URL breaks anywhere) and are never cut:
   the fit pass shrinks the block instead. The setter's --the-programme-place-lines
   (2 with <= 3 items, 1 with 4-5) applies only under the last-resort clamp below. */
.the-programme-item-place {
    display: block;
    overflow-wrap: anywhere;
    font-family: var(--font-body);
    font-weight: 600;
    font-size: calc(var(--type-detail) * var(--the-programme-fit, 1));       /* 18px */
    line-height: var(--leading-detail);
    color: var(--ink);
    text-shadow: var(--halo);
}

/* A blank title, time or place removes its line. */
.the-programme-item-title:empty,
.the-programme-item-time:empty,
.the-programme-item-place:empty {
    display: none;
}

/* Compact (the fit pass's second stage, when the block overflows at the 0.88 floor):
   time and place share one wrapping line, "6:30 PM · The lakeside mandap", rows set
   tighter, and the groups close up to 6px. Nothing is clipped. */
.the-programme-text[data-the-programme-compact] .the-programme-items {
    gap: calc(6px * var(--the-programme-fit, 1));
}
.the-programme-text[data-the-programme-compact] .the-programme-item-title {
    line-height: 1.08;
}
.the-programme-text[data-the-programme-compact] .the-programme-item-detail {
    margin-top: 0;
    line-height: 1.15;
}
.the-programme-text[data-the-programme-compact] .the-programme-item-detail[hidden] {
    display: none;
}
.the-programme-text[data-the-programme-compact] .the-programme-item-time:not(:empty),
.the-programme-text[data-the-programme-compact] .the-programme-item-place:not(:empty) {
    display: inline;
    line-height: inherit;
}
.the-programme-text[data-the-programme-compact] .the-programme-item-time:not(:empty) + .the-programme-item-place:not(:empty)::before {
    content: '\00B7';
    margin: 0 0.45em;
    color: var(--ink);
}

/* ───── Pre-entrance (pending, or settled but not yet ready) ───── */
.the-programme-page[data-sv-state="pending"] .the-programme-plate,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-plate {
    transform: scale(1.04);
}
.the-programme-page[data-sv-state="pending"] .the-programme-title,
.the-programme-page[data-sv-state="pending"] .the-programme-script,
.the-programme-page[data-sv-state="pending"] .the-programme-divider,
.the-programme-page[data-sv-state="pending"] .the-programme-item,
.the-programme-page[data-sv-state="pending"] .the-programme-loops,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-title,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-script,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-divider,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-item,
.the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-loops {
    opacity: 0;
}

/* ───── Entrance (on settle, once). Times are ms from settle. ─────
   1.    0 ms, 1400 ms, expo-out: backdrop 1.04 -> 1 (standalone; hidden in a set)
   2.  200 ms,  500 ms, expo-out: title, fade + scale
   3.  550 ms,  900 ms, silk:     script, write-on
   4. 1050 ms,  500 ms, expo-out: divider, from the centre
   5. 1300 ms,  450 ms each, expo-out, stagger 250 ms: item groups, fade-up
      Loops fade in from 1400 ms. */
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-plate {
    animation: the-programme-settle 1400ms var(--ease-expo-out) both;
}
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-title {
    animation: the-programme-rise-scale var(--dur-title) var(--ease-expo-out) 200ms both;
}
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-script {
    animation: the-programme-write var(--dur-write) var(--ease-silk) 550ms both;
}
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-divider {
    animation: the-programme-rule var(--dur-rule) var(--ease-expo-out) 1050ms both;
}
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item {
    animation: the-programme-up 450ms var(--ease-expo-out) 1300ms both;
}
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(2) { animation-delay: 1550ms; }
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(3) { animation-delay: 1800ms; }
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(4) { animation-delay: 2050ms; }
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(5) { animation-delay: 2300ms; }
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(6) { animation-delay: 2550ms; }
.the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-loops {
    animation: the-programme-fade 600ms var(--ease-silk) 1400ms both;
}

/* ───── In the wedding set ─────
   The-functions' last event is a wedding and the programme is on: its
   [data-sv-set="wedding"] wrapper is 200dvh with the art sticky. Pull this section up
   one page so this page lies over the wrapper's second half, and show only the words
   (and their lift) over the shared stack. No backdrop settle: the art is already there. */
#sv-card[data-sv-last-event="wedding"][data-sv-programme="on"] #screen-the-programme {
    margin-top: -100dvh;
    background: transparent;
}
#sv-card[data-sv-last-event="wedding"][data-sv-programme="on"] .the-programme-art,
#sv-card[data-sv-last-event="wedding"][data-sv-programme="on"] .the-programme-loops {
    display: none;
}

/* ───── Reduced motion: text fades in (400 ms, 150 ms stagger); no settle, no loops;
   halos static at 0.8. ───── */
@media (prefers-reduced-motion: reduce) {
    .the-programme-page[data-sv-state="pending"] .the-programme-plate,
    .the-programme-page[data-sv-state="entering"]:not([data-sv-ready]) .the-programme-plate {
        transform: none;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-plate {
        animation: none;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-title {
        animation: the-programme-fade 400ms var(--ease-silk) 0ms both;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-script {
        animation: the-programme-fade 400ms var(--ease-silk) 150ms both;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-divider {
        animation: the-programme-fade 400ms var(--ease-silk) 300ms both;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item {
        animation: the-programme-fade 400ms var(--ease-silk) 450ms both;
    }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(2) { animation-delay: 600ms; }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(3) { animation-delay: 750ms; }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(4) { animation-delay: 900ms; }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(5) { animation-delay: 1050ms; }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-item:nth-child(6) { animation-delay: 1200ms; }
    .the-programme-page[data-sv-state="entering"][data-sv-ready] .the-programme-loops {
        animation: none;
    }

    .the-programme-petal,
    .the-programme-flight,
    .the-programme-sparkles {
        display: none;
    }
    .the-programme-halo {
        animation: none;
        opacity: 0.8;
    }
}

/* ───── Keyframes ───── */
@keyframes the-programme-settle {
    from { transform: scale(1.04); }
    to   { transform: scale(1); }
}

@keyframes the-programme-rise-scale {      /* fade + scale */
    from { opacity: 0; transform: scale(0.92); }
    to   { opacity: 1; transform: scale(1); }
}

@keyframes the-programme-write {           /* write-on: left-to-right wipe, opacity over the first 150 ms of 900 */
    0%      { opacity: 0; clip-path: inset(-0.5em 100% -0.5em 0); }
    16.667% { opacity: 1; }
    100%    { opacity: 1; clip-path: inset(-0.5em 0 -0.5em 0); }
}

@keyframes the-programme-rule {            /* divider draws from the centre */
    from { opacity: 0; transform: scaleX(0); }
    20%  { opacity: 1; }
    to   { opacity: 1; transform: scaleX(1); }
}

@keyframes the-programme-up {              /* fade-up 10px */
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes the-programme-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes the-programme-flicker-havan {   /* 0.5 <-> 1, irregular */
    0%, 100% { opacity: 1; }
    14%      { opacity: 0.62; }
    27%      { opacity: 0.88; }
    41%      { opacity: 0.5; }
    58%      { opacity: 0.95; }
    73%      { opacity: 0.66; }
    86%      { opacity: 0.84; }
}

@keyframes the-programme-flicker-lantern { /* 0.6 <-> 1, irregular */
    0%, 100% { opacity: 0.92; }
    22%      { opacity: 0.64; }
    40%      { opacity: 1; }
    63%      { opacity: 0.6; }
    81%      { opacity: 0.86; }
}

@keyframes the-programme-fall {            /* top corners -> bottom, ±4% sway */
    0%   { opacity: 0; transform: translate3d(0, 0, 0) rotate(0deg); }
    6%   { opacity: 1; }
    25%  { transform: translate3d(4cqi, 27dvh, 0) rotate(80deg); }
    50%  { transform: translate3d(-3cqi, 54dvh, 0) rotate(170deg); }
    75%  { transform: translate3d(4cqi, 81dvh, 0) rotate(255deg); }
    92%  { opacity: 1; }
    100% { opacity: 0; transform: translate3d(0, 108dvh, 0) rotate(340deg); }
}

@keyframes the-programme-flight-a {        /* over the lawn, left */
    0%, 100% { transform: translate3d(0, 0, 0); }
    20%      { transform: translate3d(9cqi, -3cqi, 0); }
    45%      { transform: translate3d(17cqi, 1cqi, 0); }
    70%      { transform: translate3d(9cqi, 3.5cqi, 0); }
    85%      { transform: translate3d(3cqi, 2cqi, 0); }
}

@keyframes the-programme-flight-b {        /* over the lawn, right */
    0%, 100% { transform: translate3d(0, 0, 0); }
    25%      { transform: translate3d(-8cqi, 2.5cqi, 0); }
    50%      { transform: translate3d(-15cqi, -1.5cqi, 0); }
    75%      { transform: translate3d(-7cqi, -3cqi, 0); }
}

@keyframes the-programme-wing {            /* wing beat */
    0%, 100% { transform: scaleX(1); }
    50%      { transform: scaleX(0.32); }
}
