/* --- COMBINED INVITATION.CSS --- */
    @font-face{font-family:Calligraphy;src:url('assets/fonts/greatvibes.ttf') format('truetype');font-display:swap}
    @font-face{font-family:Italiana;src:url('assets/fonts/italiana-regular.ttf') format('truetype');font-display:swap}
    @font-face{font-family:Manrope;src:url('assets/fonts/manrope-regular.ttf') format('truetype');font-display:swap}
    :root{--paper:#f7f2e8;--light:#fffcf5;--gold:#ac8b51;--ink:#58452c;--muted:#776247;--action:#796035;--line:#cab991;--shadow:0 16px 45px #58452c17;scroll-behavior:smooth;scroll-padding-top:85px;color-scheme:light}
    *{box-sizing:border-box}body{margin:0;color:var(--ink);background:var(--paper);font:15px/1.8 Manrope,sans-serif}body::after{content:'';position:fixed;inset:12px;border:1px solid #ac8b5155;pointer-events:none;z-index:20}::selection{background:#dbc699;color:#44341d}html{scrollbar-color:var(--gold) var(--paper)}button,input,textarea,select{font:inherit;color:inherit}input,textarea{caret-color:var(--action)}a{color:inherit;text-underline-offset:5px}button,a{-webkit-tap-highlight-color:#ac8b5122}button{cursor:pointer}button:disabled{cursor:default;opacity:.55}:focus-visible{outline:2px solid var(--action);outline-offset:5px}button,a,input,textarea,select{touch-action:manipulation}button,select,input,textarea{min-height:44px}button{border:0;background:none}img{max-width:100%;display:block}h1,h2,h3,p,figure{margin:0}h1,h2,h3{font-weight:400;text-wrap:balance}h2{font:400 clamp(44px,5vw,66px)/1.2 Calligraphy,cursive;color:var(--action)}h3{font:400 30px/1.3 Italiana,serif}p{max-width:68ch;margin-inline:auto}svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.4}.symbols{position:absolute;width:0;height:0}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}[hidden]{display:none!important}.skip-link{position:fixed;top:8px;left:20px;transform:translateY(-200%);z-index:50;background:var(--light);padding:10px}.skip-link:focus{transform:none}
    .masthead{position:absolute;top:0;left:0;width:100%;height:84px;display:flex;align-items:center;justify-content:flex-end;padding:0 6%;z-index:10;pointer-events:none}.sound-toggle{position:relative}
    .welcome{min-height:850px;min-height:100svh;position:relative;display:grid;place-items:center;background:var(--paper) url('assets/magnolia-suite/garden.webp') center/cover;padding:120px 24px 70px;isolation:isolate}.welcome::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at center,#fffaf0e0 0%,#fffaf081 28%,transparent 60%);z-index:-1}.welcome-frame{position:absolute;inset:92px 23%;border:1px solid #9f81485c;border-radius:48% 48% 0 0;pointer-events:none}.welcome-content{text-align:center;position:relative}.welcome-wording{font:22px Italiana,serif;margin-bottom:28px}.welcome h1{font:400 clamp(72px,8vw,112px)/.9 Calligraphy,cursive;color:#806336;text-shadow:0 2px 12px #fff8e9}.welcome h1 .given{display:block;width:fit-content;margin-inline:auto}.welcome h1 .amp{display:block;font-size:43px;line-height:1.3}.flourish{width:110px;height:44px;color:var(--gold);stroke-width:.8;display:block;margin:0 auto 22px}.scroll-cue{display:flex;flex-direction:column;align-items:center;text-decoration:none;font-size:11px;margin-top:46px;gap:12px}.scroll-cue span{height:35px;width:1px;background:var(--gold);transform-origin:top;animation:invite-scroll 2.4s ease-in-out infinite}@keyframes invite-scroll{0%,100%{transform:scaleY(.65)}50%{transform:scaleY(1)}}
    :root{--sync:0s}
    .section{position:relative;text-align:center;padding:95px 7%;max-width:1600px;margin:auto}.section>h2+p{margin-top:12px}.section:not(.invitation)::after{content:'';position:absolute;bottom:0;left:calc(50% - 70px);width:140px;height:1px;background:var(--line)}.invitation{padding-top:75px;padding-bottom:65px}.photo-invitation{position:relative;display:grid;place-items:center}.invitation-letter{background:#fffcf5ed;max-width:565px;width:100%;padding:45px 36px;outline:1px solid var(--line);outline-offset:-13px;box-shadow:var(--shadow)}.invitation-letter>p{font-size:13px}.invitation-letter h2{margin:18px 0;font-size:52px}.invitation-letter .couple-names{font:40px/1.5 Calligraphy,cursive;margin:16px 0;color:var(--action)}.couple-names span{font-size:28px}.invitation-letter .signature{font:21px/1.6 Italiana,serif}.disclosure{font-size:11px;line-height:1.7;color:var(--muted);margin-top:18px;max-width:560px}blockquote{font:30px/1.5 Italiana,serif;max-width:660px;margin:38px auto 0;text-wrap:balance}
    .scratch-card{width:min(100%,430px);height:270px;margin:35px auto 0;position:relative;background:var(--light);box-shadow:var(--shadow);isolation:isolate;overflow:hidden}.date-reveal{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;border:1px solid var(--line);margin:0 10px}.date-reveal>span{text-transform:uppercase;letter-spacing:.16em;font-size:11px}.date-reveal time{display:flex;flex-direction:column;font:24px/1.15 Italiana,serif}.date-reveal b{font-weight:400;font-size:84px}.date-reveal p{font-size:11px;margin-top:14px}#scratch{position:absolute;inset:0;width:100%;height:100%;touch-action:none;cursor:crosshair;transition:opacity .6s}.scratch-label{position:absolute;inset:0;pointer-events:none;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#49371d;transition:opacity .3s}.scratch-label span{font:44px Italiana,serif}.scratch-label p{font-size:12px;margin-top:20px}.scratch-card.revealed #scratch,.scratch-card.revealed .scratch-label{opacity:0;pointer-events:none}.scratch-card.scratching .scratch-label{opacity:0}.text-button{display:inline-flex;align-items:center;justify-content:center;min-height:44px;font-size:12px;text-decoration:underline;text-underline-offset:5px;padding:10px 12px}.text-button:hover{color:#39280e}.calendar-actions{display:flex;justify-content:center;gap:12px;flex-wrap:wrap;margin-top:25px}.button{display:inline-flex;gap:18px;align-items:center;justify-content:center;min-height:50px;padding:12px 23px;background:var(--action);color:var(--light);text-decoration:none;font-size:12px;border:1px solid var(--action);transition:background .15s}.button:hover{background:#604923}.button.secondary{color:var(--ink);background:transparent}.button.secondary:hover{background:#ede1cb}.countdown-title{margin:55px 0 24px;font:38px Calligraphy,cursive;color:var(--action)}.countdown{display:grid;grid-template-columns:repeat(4,1fr);max-width:520px;margin:auto}.countdown>div+div{border-left:1px solid var(--line)}.countdown b{display:block;font:48px/1.2 Italiana,serif;font-variant-numeric:tabular-nums}.countdown span{font-size:11px;letter-spacing:.08em}
    .timeline{list-style:none;max-width:750px;margin:45px auto 0;padding:0;text-align:left}.timeline li{display:grid;grid-template-columns:180px 1fr;gap:55px;padding:0 0 42px;position:relative}.timeline li::before{content:'';position:absolute;width:1px;top:14px;bottom:0;left:207px;background:var(--line)}.timeline li:last-child::before{display:none}.timeline-mark{position:absolute;left:203px;top:10px;width:9px;height:9px;background:var(--gold);transform:rotate(45deg)}.timeline time{font-size:12px;text-align:right;padding-top:5px}.timeline h3{font-size:29px}.timeline p{font-size:13px;margin-top:9px}.timeline li:last-child{padding-bottom:0}
    .architecture{position:relative;width:min(100%,1050px);aspect-ratio:1100/709;margin:35px auto 0}.architecture img,.architecture canvas{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;transition:opacity .6s}.venue-finished{opacity:1}#venue-canvas{opacity:0}.architecture.is-drawing .venue-finished{opacity:0}.architecture.is-drawing #venue-canvas{opacity:1}.venue-photograph{opacity:0}.architecture.show-photo .venue-photograph{opacity:1}.architecture.show-photo #venue-canvas,.architecture.show-photo .venue-finished{opacity:0}.drawing-actions{display:flex;justify-content:center;gap:12px;margin-top:5px}.venue address{font:22px/1.6 Italiana,serif;margin:25px auto}.map-place{display:flex;width:fit-content;margin:14px auto 0}
    .reply-card{max-width:580px;margin:40px auto 0;padding:40px 45px;text-align:left;background:var(--light);box-shadow:var(--shadow);outline:1px solid var(--line);outline-offset:-10px}.reply-card h3{text-align:center;font:44px Calligraphy,cursive;margin-bottom:25px;color:var(--action)}.reply-card label,.reply-card legend{display:block;font-size:12px}.reply-card label>span{color:var(--muted)}input:not([type=radio]),select,textarea{display:block;width:100%;background:transparent;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:10px 0;font-size:16px;margin:3px 0 22px}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:var(--muted);opacity:1}fieldset{border:0;margin:0 0 20px;padding:0}.reply-card .choice{display:flex;align-items:center;gap:10px;min-height:44px}.choice input{margin:0;width:18px;height:18px;min-height:18px;accent-color:var(--action)}.reply-card>.button{width:100%;margin-top:5px}.reply-card .disclosure{text-align:center}#reply-status{font-size:13px;margin-top:14px}#reply-text{white-space:pre-wrap;font-size:13px;margin:14px 0}#prepared{border-top:1px solid var(--line);margin-top:20px}.closing{text-align:center;padding:65px 24px 45px}.closing-names{font:40px/1.4 Calligraphy,cursive;color:var(--action)}.closing>p:not(.closing-names){font-size:12px}.closing a{font-size:11px;display:inline-block;padding:15px;margin-top:15px}.noscript-note{text-align:center;padding:24px;background:var(--light)}
    @media(min-width:1600px){.welcome-frame{inset:100px 29%}.welcome{min-height:100vh}}
    @media(max-width:700px){body::after{inset:6px}.masthead{height:65px;padding:0 24px}.welcome{background-image:url('assets/magnolia-suite/portrait.webp');min-height:780px;min-height:100svh;padding:100px 22px 60px}.welcome-frame{inset:85px 13% 45px;border-radius:170px 170px 0 0}.welcome-wording{font-size:19px;margin-bottom:28px}.welcome h1{font-size:clamp(68px,19vw,96px)}.welcome h1 .amp{font-size:35px}.scroll-cue{margin-top:28px}.section{padding:65px 24px}h2{font-size:44px}.section>h2+p{font-size:13px}.invitation-letter{padding:30px 22px}.invitation-letter h2{font-size:42px}.invitation-letter>p{font-size:12px}.invitation-letter .couple-names{font-size:34px;line-height:1.2}.invitation-letter .signature{font-size:20px}.invitation-letter .flourish{margin-bottom:15px}blockquote{font-size:25px;margin-top:30px}.disclosure{font-size:10px}.scratch-card{height:250px}.calendar-actions{display:grid;max-width:430px;margin-inline:auto}.countdown b{font-size:38px}.countdown-title{font-size:34px}.timeline{margin-top:38px}.timeline li{grid-template-columns:1fr;gap:7px;padding:0 0 30px 28px}.timeline li::before{left:4px}.timeline-mark{left:0}.timeline time{text-align:left;font-size:10px;padding:0}.timeline h3{font-size:27px}.timeline p{font-size:12px;margin-top:7px}.architecture{width:calc(100% + 28px);max-width:100vw;margin:25px -14px 0}.drawing-actions{gap:0;flex-wrap:wrap}.drawing-actions button{font-size:11px}.reply-card{padding:32px 25px}.closing-names{font-size:34px}}
    @media(max-height:650px) and (min-width:701px){.welcome{min-height:760px}}
    .invitation-letter{background:#fffcf5fa}
/* The entrance is a small stage set, not one flat picture. The camera looks into a real space: the
   wall stands at the front with the doorway cut out of it, the two leaves hinge on it and swing
   inwards, and the garden stands well behind. Because each is at its own distance, walking forward
   moves them past one another the way real things do - the wall and the leaves sweep by, the garden
   comes on slowly. That is the whole difference between a door opening and a picture growing.
   Every distance is a share of --dh, the height of the set, so the scene is the same on any screen. */
body.intro-locked{overflow:hidden}
/* will-change:opacity is essential, not an optimisation. The dissolve is added with .opening, so
   without it the entrance is promoted to its own layer at that exact moment and the first frame
   renders un-rasterised — you see the page straight through the closed doors before they swing. */
.door-intro{position:fixed;inset:0;z-index:100;overflow:hidden;isolation:isolate;will-change:opacity;background:var(--paper) url('assets/magnolia-suite/garden.webp') center/cover}
.door-intro::before{content:'';position:absolute;inset:0;background:#f7f2e8c9}
/* The camera is the fixed frame the set is seen through: it holds the lens (perspective) and never
   moves. The eye is level with the middle of the doorway, so the doorway is what everything recedes
   towards. What moves is the world inside it. */
.door-camera{--dh:100vh;--lt:calc(var(--dh)*.018);position:absolute;top:50%;left:50%;height:var(--dh);width:calc(var(--dh)*.666667);transform:translate(-50%,-50%);perspective:calc(var(--dh)*1.1);perspective-origin:50% 58%}
@supports(height:1svh){.door-camera{--dh:100svh}}
/* will-change is set up front, not on .opening: promoting these layers at the moment the animation
   starts costs the first few frames, which is exactly where the hitch was visible. */
.door-world{position:absolute;inset:0;transform-style:preserve-3d;transform:translateZ(0);will-change:transform}
/* The vista - the garden through the next arch - stands seven tenths of the set's height behind the
   wall, enlarged so that from the threshold it fills the doorway. It is drawn and on its own layer
   from the first frame, under the closed doors, so it is ready the instant they part. As the camera
   nears the doorway it sees past the picture's edges, and must not find a cut there: the marble
   floor carries on below the picture, and its sides are feathered away to the hall behind it. */
.door-room{position:absolute;inset:0 0 -30%;transform-origin:50% 44.6154%;transform:translateZ(calc(var(--dh)*-.7)) scale(1.211);will-change:transform;background:url('assets/magnolia-suite/portrait.webp') top/100% auto no-repeat,linear-gradient(#f4ebdc00 74%,#f4ebdc 77%);-webkit-mask-image:linear-gradient(90deg,#0000,#000 7%,#000 93%,#0000);mask-image:linear-gradient(90deg,#0000,#000 7%,#000 93%,#0000)}
/* Sunlit air between the doorway and the garden. It blooms as the leaves part and then settles, and
   because it hangs in the middle of the space the camera walks into it. */
.door-haze{position:absolute;left:5%;width:90%;top:18%;height:80%;transform:translateZ(calc(var(--dh)*-.3));opacity:0;will-change:transform,opacity;background:radial-gradient(closest-side,#fffbf2e6,#fffaf080 50%,#fffaf000)}
/* The wall has thickness: the underside of the lintel and the stone sill run back from the edges of
   the opening. Slivers from the threshold, they widen as the camera comes through. */
.door-reveal{position:absolute;left:21.45%;width:57.1%;height:calc(var(--dh)*.05);will-change:transform}
.reveal-head{top:30.34%;transform-origin:50% 0;transform:rotateX(-90deg);background:linear-gradient(#b7a179,#d8c7a4)}
.reveal-sill{bottom:14.41%;transform-origin:50% 100%;transform:rotateX(90deg);background:linear-gradient(#fbf5e8,#eadfc8)}
/* The wall is the photograph with the doorway cut clean out of it, a hair inside the leaves' own
   outline so that no seam shows while they are shut. */
.door-wall{position:absolute;inset:0;transform:translateZ(0);will-change:transform;background:url('assets/magnolia-suite/doors.webp') center/100% 100%;-webkit-clip-path:polygon(evenodd,0 0,100% 0,100% 100%,0 100%,0 0,21.6% 30.5%,21.6% 85.45%,78.4% 85.45%,78.4% 30.5%,21.6% 30.5%);clip-path:polygon(evenodd,0 0,100% 0,100% 100%,0 100%,0 0,21.6% 30.5%,21.6% 85.45%,78.4% 85.45%,78.4% 30.5%,21.6% 30.5%)}
/* The two leaves are cut from the same photograph and share its coordinates, so shut they complete
   it exactly. transform:translateZ(1px) rotateY(0) declares the rotation up front so a leaf never
   takes its first transform matrix on the same class change that starts the hinge; both layers are
   built and rastered while the visitor is still looking at the closed doors instead of at press
   time. The opaque background-color is a backstop: a leaf that loses only its image still reads as
   a pale panel rather than as an opening. */
.door-leaf{position:absolute;top:30.34%;width:28.55%;height:55.25%;background-color:#f2ebdd;background-image:url('assets/magnolia-suite/doors.webp');background-size:350.262697% 180.995475%;background-repeat:no-repeat;transform:translateZ(1px) rotateY(0deg);transform-style:preserve-3d;backface-visibility:hidden;will-change:transform}
.leaf-left{left:21.45%;background-position:30.020994% 67.798883%;transform-origin:left center}
.leaf-right{left:50%;background-position:69.979006% 67.798883%;transform-origin:right center}
/* Each leaf is a solid slab, not a sheet: a carved front, a carved back set one thickness (--lt)
   behind it, and a gilded stile closing the free edge between them. Swinging towards the guest, the
   stile is the face that catches the light, and past eighty degrees it is the back of the leaf that
   shows. Without the back and the stile a leaf thins to nothing as it turns, which is what made the
   doors read as flat cards. A leaf's top and bottom are never seen from the threshold. */
.leaf-back{position:absolute;inset:0;background:inherit;background-color:#efe6d4;transform:rotateY(180deg) translateZ(var(--lt));backface-visibility:hidden;-webkit-backface-visibility:hidden;box-shadow:inset 0 0 0 100vmax #6b4e2224,inset 0 0 calc(var(--dh)*.03) #4a321252}
.door-leaf::after{content:'';position:absolute;top:0;bottom:0;width:var(--lt);background:linear-gradient(90deg,#fffaf0,#f1e6cd 20%,#b98f45 27%,#d9b56c 31%,#f4ead4 37%,#e6d9bc 63%,#b98f45 69%,#d9b56c 73%,#d8c8a4 80%,#a8905f)}
.leaf-left::after{left:100%;transform-origin:0 50%;transform:rotateY(90deg)}
.leaf-right::after{right:100%;transform-origin:100% 50%;transform:rotateY(-90deg)}
/* The shadow a standing leaf lays on the marble behind it. It lies flat on the floor and turns
   with the leaf, so it is hidden under the doors while they are shut. */
.leaf-shadow{position:absolute;left:0;right:0;bottom:0;height:calc(var(--dh)*.07);transform-origin:50% 100%;transform:rotateX(90deg);background:linear-gradient(0deg,#3a2a1270,#3a2a1226 45%,#3a2a1200)}
/* A shading plane darkens each front as it turns side-on to the light, deepest at the hinge. */
.door-leaf::before{content:'';position:absolute;inset:0;opacity:0;will-change:opacity;backface-visibility:hidden;-webkit-backface-visibility:hidden;background:linear-gradient(90deg,#4a3212b8,#5c401940 60%,#5c40190a)}
.leaf-right::before{background:linear-gradient(270deg,#4a3212b8,#5c401940 60%,#5c40190a)}
/* Daylight falling out across the floor in front of the doorway, widening with the gap. */
.door-spill{position:absolute;left:8%;width:84%;top:85.59%;height:14.41%;transform-origin:50% 0;transform:translateZ(2px) scaleX(.05);opacity:0;will-change:transform,opacity;background:radial-gradient(ellipse 62% 105% at 50% 0,#fffef8f0,#fff6dc8c 50%,#fff6dc00);-webkit-clip-path:polygon(16% 0,84% 0,100% 100%,0 100%);clip-path:polygon(16% 0,84% 0,100% 100%,0 100%)}
/* The seal is the only affordance: its halves ride the leaves and part as the doors swing. */
.door-leaf .seal-half{position:absolute;top:44%;height:23%;aspect-ratio:1/2;background:url('assets/materials/brass-seal-monogram-512.webp') no-repeat;background-size:200% 100%;filter:drop-shadow(0 4px 7px #3a2a1245)}
.leaf-left .seal-half{right:0;background-position:left center}
.leaf-right .seal-half{left:0;background-position:right center}
.seal-press{position:absolute;top:61%;left:50%;translate:-50% -50%;z-index:5;width:clamp(68px,13svh,112px);aspect-ratio:1;min-height:0;padding:0;border-radius:50%;background:none}
.seal-press::after{content:'';position:absolute;inset:-12%;border-radius:50%;border:1px solid #ab853d7a}
.seal-press:hover::after{border-color:#8a672ab5}
/* The caption is the one line on the doors, so it is set in Italiana at reading size rather than as
   a 10px tracked label, which the pale marble swallowed. The ivory halo lifts it off the veining. */
.seal-word{position:absolute;left:50%;translate:-50% 0;bottom:9%;z-index:5;width:max-content;max-width:calc(100% - 32px);text-align:center;font:400 clamp(16px,2.3svh,21px)/1.3 Italiana,serif;letter-spacing:.05em;color:#3f2c12;text-shadow:0 0 16px #fffaf0,0 0 6px #fffaf0,0 1px 0 #fffaf0}
/* The seal reacts on touch-down, so the press reads as instant even while the art finishes decoding. */
.seal-press{transition:scale .15s ease}
.seal-press:active,.pressed .seal-press{scale:.93}
.pressed .seal-word{opacity:.45;transition:opacity .3s}
.opening .seal-press,.opening .seal-word{opacity:0;pointer-events:none;transition:opacity .3s}
/* Two strictly sequential stages, because the doors must finish opening before the camera moves:
   closed, then the swing, then the open scene coming towards you. Stage one, 0-1.8s: the camera is
   locked at scale 1 and only the two leaves turn. Stage two, 1.8-4.8s: the leaves are out of the
   scene entirely and the camera advances through the open arch as the entrance dissolves. The two
   stages previously overlapped, which read as one muddled move; keeping them apart also keeps the
   leaves out of the camera's 4.8x scale, so they are never rastered at seventeen times their size.
   Both stages are cut to the score, which starts on the same press: at 100bpm a beat is 0.6s and a
   bar 2.4s, so the swing runs three beats, the walk-through the next five, and the entrance ends at
   4.8s — the two-bar mark, which is the exact point the recording's own introduction gives way to
   the full arrangement. The doors land on the downbeat rather than near it.
   --sync is however far the music has already travelled by the time the class lands, a tenth of a
   second or so of decode and paint. Every stage here starts that much further in, so the sequence
   finishes on the recording's own downbeat instead of trailing the press's overhead behind it. */
.opening .leaf-left{animation:hinge-left 1.8s cubic-bezier(.34,0,.24,1) calc(-1 * var(--sync,0s)) both}
.opening .leaf-right{animation:hinge-right 1.8s cubic-bezier(.34,0,.24,1) calc(-1 * var(--sync,0s)) both}
.opening .door-leaf::before{animation:leaf-shade 1.8s cubic-bezier(.34,0,.24,1) calc(-1 * var(--sync,0s)) both}
.opening .door-haze{animation:haze-bloom 1.8s ease-out calc(-1 * var(--sync,0s)) both}
.opening .door-spill{animation:light-spill 1.8s cubic-bezier(.34,0,.24,1) calc(-1 * var(--sync,0s)) both}
.opening .door-world{animation:walk-through 3s cubic-bezier(.42,0,.32,1) calc(1.8s - var(--sync,0s)) both}
.opening.door-intro{animation:entrance-dissolve 3s linear calc(1.8s - var(--sync,0s)) both}
/* The document is NOT hidden behind the entrance. It used to be visibility:hidden until the doors
   opened, which meant the whole page laid out and painted on frame one of the swing — the stall
   people saw before the doors moved. The entrance is opaque and covers the viewport, so the page
   can paint underneath it at load instead, off the interaction path. `inert` still holds a11y. */
.intro-opening .masthead{animation:names-after-entry 4.8s ease calc(-1 * var(--sync,0s)) both}
/* Hold shut for 6% (108ms) so the pressed seal registers, then swing only outwards, towards the
   guest, to 81deg. The leaves stay where they come to rest, standing open - the camera then walks
   out between them, past the stiles and the backs of the leaves. */
@keyframes hinge-left{0%,6%{transform:translateZ(1px) rotateY(0)}90%,100%{transform:translateZ(1px) rotateY(-81deg)}}
@keyframes hinge-right{0%,6%{transform:translateZ(1px) rotateY(0)}90%,100%{transform:translateZ(1px) rotateY(81deg)}}
@keyframes leaf-shade{0%,6%{opacity:0}88%,100%{opacity:1}}
@keyframes haze-bloom{0%,6%{opacity:0}55%{opacity:.85}100%{opacity:.4}}
@keyframes light-spill{0%,6%{opacity:0;transform:translateZ(2px) scaleX(.05)}88%,100%{opacity:1;transform:translateZ(2px) scaleX(1)}}
/* The open leaves stand about a fifth of --dh in front of the wall and the lens is 1.1 away, so
   the camera stops at .85: nothing is ever asked to pass through the lens, and by then the
   entrance has dissolved. */
@keyframes walk-through{0%{transform:translateZ(0)}100%{transform:translateZ(calc(var(--dh)*.85))}}
@keyframes entrance-dissolve{0%,45%{opacity:1}90%,100%{opacity:0}}
@keyframes names-after-entry{0%,75%{opacity:0;transform:translateY(12px)}100%{opacity:1;transform:translateY(0)}}
.intro-replay{display:block;margin:8px auto;font-size:12px;text-decoration:underline;text-underline-offset:5px;padding:10px 15px}
/* The score's control. Ghosted into the masthead at the same weight as the navigation, so it reads
   as part of the header rather than a player bolted onto it. Hidden until the score can actually
   play, so it never offers a control that would do nothing. */
.sound-toggle{display:flex;align-items:center;justify-content:center;width:44px;height:44px;margin-right:-10px;flex:none;background:none;border:0;padding:0;color:var(--ink);border-radius:50%;transition:color .15s ease,background .15s ease}
.sound-toggle:hover{color:var(--action);background:#ac8b5116}
.sound-icon{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}
/* Muted is the quieter state visually as well: the icon steps back rather than shouting. */
.sound-toggle[aria-pressed=true]{color:var(--muted)}
.sound-toggle[aria-pressed=true]:hover{color:var(--ink)}
/* One bar of the score, so the ring pulses in time with what is playing. Paused, it simply rests. */
.sound-toggle::after{content:'';position:absolute;width:34px;height:34px;border-radius:50%;border:1px solid currentColor;opacity:0;pointer-events:none}
.motion-ready .sound-toggle[data-playing]::after{animation:sound-pulse 2.4s cubic-bezier(.4,0,.5,1) infinite}
@keyframes sound-pulse{0%{opacity:.34;scale:.72}70%,100%{opacity:0;scale:1.18}}
h1[tabindex="-1"]:focus,h2[tabindex="-1"]:focus{outline:none}
.masthead{position:fixed;height:68px}
.section>h2{max-width:900px;margin-inline:auto}
.reply-card input:focus,.reply-card textarea:focus,.reply-card select:focus{border-bottom-color:var(--action)}
.reply-card input:user-invalid{border-bottom-color:#984f35}
@media(max-width:700px){.door-camera{--dh:max(100vh,150vw)}@supports(height:1svh){.door-camera{--dh:max(100svh,150vw)}}.masthead{height:64px;padding-inline:22px}.disclosure{font-size:11px}.timeline time{font-size:12px}.timeline p{font-size:14px}}
@media(max-width:360px){.masthead{padding-inline:18px}}
/* Asked for less motion, the page keeps still - except the doors, which every guest sees open. */
@media(prefers-reduced-motion:reduce){:root{scroll-behavior:auto}*:not(#door-intro,#door-intro *,.intro-opening .masthead),*:not(#door-intro,#door-intro *)::before,*:not(#door-intro,#door-intro *)::after{animation:none!important;transition:none!important}}

/* ---------- Phones ---------- */
/* Safari and Chrome on a phone enlarge any text block they judge too small, and do it again whenever
   the phone is turned sideways. Every size here is already chosen for a phone, so that inflation
   only ever pushes the script lines and the programme out of shape. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
/* The invitation is ivory stationery and has no dark version. Samsung Internet and Android's
   forced-dark setting would otherwise invert it: brown paper, murky gold. */
:root{color-scheme:only light}
/* viewport-fit=cover runs the page edge to edge, which on a phone turned sideways puts the Dynamic
   Island or the notch over the right or left margin. The music control and the frame line are held
   clear of it. In portrait both insets are zero and nothing moves. */
.masthead{padding-right:max(6%,calc(env(safe-area-inset-right) + 12px))}
body::after{left:max(12px,env(safe-area-inset-left));right:max(12px,env(safe-area-inset-right))}
@media(max-width:700px){
 .masthead{padding-right:max(22px,calc(env(safe-area-inset-right) + 12px))}
 body::after{left:max(6px,env(safe-area-inset-left));right:max(6px,env(safe-area-inset-right))}
}
@media(max-width:360px){.masthead{padding-right:max(18px,calc(env(safe-area-inset-right) + 12px))}}
/* Older iOS only honours the prefixed form, and without it a turning leaf shows its mirrored back. */
.door-leaf{-webkit-backface-visibility:hidden}
/* The seal is the way in. Where the translate property is missing (Chrome before 104, iOS before
   14.5) it is placed with transform instead, or it would sit off-centre on an old phone. */
@supports not (translate:0 0){.seal-press{transform:translate(-50%,-50%)}.seal-word{transform:translateX(-50%)}}
