/* Layout: one poster-width column (max 34rem) on the ground colour. The host view widens to 60rem.
   Colour tokens come from the generated theme.css (the edition's content file); fonts are self-hosted.
   One deliberate dark look (a movie poster); no light theme. */
:root{
  --display:"League Gothic","Avenir Next Condensed","Arial Narrow","Roboto Condensed",Impact,sans-serif;
  --body:"Sofia Sans","Segoe UI",system-ui,-apple-system,sans-serif;
  --cond:"Sofia Sans Extra Condensed","Avenir Next Condensed","Arial Narrow","Roboto Condensed",sans-serif;
  color-scheme:dark;
}
*{box-sizing:border-box}
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--ink);color:var(--snow);font:400 1rem/1.55 var(--body);-webkit-font-smoothing:antialiased}
h1,h2,h3{text-wrap:balance;margin:0}
p{margin:0}
a{color:var(--gold)}
button,input,select,textarea{font:inherit;color:inherit}
:focus{outline:2px solid var(--gold);outline-offset:2px}
:focus:not(:focus-visible){outline:none}
.wrap{max-width:34rem;margin-inline:auto;padding-inline:16px}
.wrap.wide{max-width:60rem}
/* Grid items that can hold a guest's long name may shrink below that word's width, so the word
   breaks (overflow-wrap) instead of widening the column: iOS 15.0-15.3 have only break-word, which
   doesn't shrink a word's min-content width the way anywhere does. */
.poster-inner>*,.section>*,.field>*,.confirm>*,.ticket-main>*,.tk-star>*{min-width:0}
/* While the page holds a ticket in front of the guest (web/app.js keepTicket), it's the only thing
   that adjusts the scroll: the browser's own anchoring, keyed to some other element, is off. */
.holding,.holding body{overflow-anchor:none}
.linkish{border:0;background:none;padding:0;color:var(--gold);text-decoration:underline;text-underline-offset:3px;cursor:pointer;font-size:inherit;text-align:left}

/* ---------- poster ---------- */
.poster{position:relative;overflow:hidden;background:radial-gradient(ellipse 90% 55% at 50% 0%,var(--glow) 0%,transparent 70%),radial-gradient(ellipse 80% 35% at 50% 100%,var(--ember) 0%,transparent 70%),var(--ink)}
.poster::after{content:"";position:absolute;left:0;right:0;bottom:0;height:8px;background:repeating-linear-gradient(-45deg,var(--red) 0 10px,var(--snow) 10px 20px)}
.snow{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.poster-inner{position:relative;text-align:center;padding-block:34px 38px;display:grid;justify-items:center;gap:18px}
.presents{font:600 .9rem/1 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--mist)}
/* A personal link's first name over the title (poster.starLine; web/app.js starName), the poster's
   own billing: "X present / NAME / the title". Its line keeps its room from the first paint on a
   personal link (the Worker writes data-link="personal" on the root element), empty until the
   invite lookup fills it, then the name fades up; a name too wide for the line is set smaller (down
   to half), never on a second line, and past that cut at its end with an ellipsis. The line clips
   only at its padding's edge: .6em of padding above and below, taken back by its margins (so it
   takes the same room on the page as without), keeps a name's marks (an accent over a capital, a
   cedilla under) inside it. It overlaps its neighbours by that much, so it takes no taps. The
   general link has none. */
.star-line{display:none}
[data-link="personal"] .star-line{display:block;box-sizing:content-box;height:2.1rem;max-width:100%;padding:.6em 0;margin:calc(-6px - .6em) 0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;pointer-events:none;font:600 1.75rem/2.1rem var(--cond);letter-spacing:.12em;text-transform:uppercase;color:var(--snow);opacity:0;transform:translateY(6px);transition:opacity .35s ease-out,transform .35s ease-out}
[data-link="personal"] .star-line.named{opacity:1;transform:none}
/* The title sizes with the title block's width (cqi). An engine without container units (iOS 15,
   Chromium before 105) drops each cqi font-size and keeps the font before it, sized from the viewport:
   min(100vw,34rem) - 32px is .wrap's content box, the title block's width, so the two agree. The cqi
   sizes stay in var()-free declarations: one with var() would turn to unset there, not fall back. */
.title-block{position:relative;width:100%;container-type:inline-size}
/* The ghost numeral: a thin outline in the theme's own colour (as the link-preview image draws it),
   sized to sit behind the title and end above the tagline, so it never crosses running text. It
   stands at the title block's right edge, a sequel's number set beside the name, so it stays clear
   of the small centred kicker ("Part 4") at every width, 200% text included. */
.numeral{position:absolute;left:0;right:0;top:.04em;z-index:0;text-align:right;font:400 min((min(100vw,34rem) - 32px)*.8,22rem)/.8 var(--display);font-size:min(80cqi,22rem);color:transparent;-webkit-text-stroke:1.5px var(--holly);pointer-events:none;user-select:none}
.title-block h1{position:relative;z-index:1;display:grid;justify-items:center;font-weight:400}
.t-this{font:400 min((min(100vw,34rem) - 32px)*.13,3.6rem)/1 var(--display);font-size:min(13cqi,3.6rem);letter-spacing:.06em;text-transform:uppercase}
.t-christmas{font:400 min((min(100vw,34rem) - 32px)*.26,8.6rem)/.86 var(--display);font-size:min(26cqi,8.6rem);text-transform:uppercase;letter-spacing:.01em}
.t-kicker{margin-top:14px;font:700 .9rem/1 var(--cond);letter-spacing:.36em;text-transform:uppercase;color:var(--holly)}
.t-sub{margin-top:6px;max-width:100%;font:400 min((min(100vw,34rem) - 32px)*.15,4.8rem)/.95 var(--display);font-size:min(15cqi,4.8rem);text-transform:uppercase;letter-spacing:.015em;background:linear-gradient(180deg,var(--gold-hi) 0%,var(--gold) 48%,var(--gold-deep) 100%);-webkit-background-clip:text;background-clip:text;color:transparent;text-wrap:balance}
.t-sub.long{font-size:min((min(100vw,34rem) - 32px)*.11,3.6rem);font-size:min(11cqi,3.6rem)}
.stamp{position:relative;z-index:1;display:inline-block;margin-top:14px;transform:rotate(-6deg);font:700 .8rem/1 var(--cond);letter-spacing:.22em;text-transform:uppercase;color:var(--holly);border:2px solid var(--holly);border-radius:4px;padding:6px 10px}
.tagline{font-style:italic;font-size:1.1rem;color:var(--snow);max-width:24rem}
.when{display:grid;gap:4px}
.near{font:600 .875rem/1.2 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--mist)}
.date{font:400 clamp(2.2rem,11vw,3.3rem)/1 var(--display);letter-spacing:.03em;text-transform:uppercase}
.where{font:600 1rem/1.2 var(--cond);letter-spacing:.2em;text-transform:uppercase}
.cta,.btn-primary{display:inline-block;border:0;border-radius:6px;cursor:pointer;text-decoration:none;text-align:center;background:linear-gradient(180deg,var(--red-hi),var(--red) 40%,var(--red-deep));color:var(--on-red);font:700 1.05rem/1 var(--cond);letter-spacing:.22em;text-transform:uppercase;padding:15px 34px;box-shadow:0 8px 24px rgba(0,0,0,.35)}
.cta.wide{width:100%}
.cta[disabled]{opacity:.7;cursor:progress}
.by{font-size:.85rem;color:var(--mist);margin-top:-8px}
.billing{max-width:27rem;overflow-wrap:break-word;overflow-wrap:anywhere;font:500 .875rem/1.35 var(--cond);letter-spacing:.05em;text-transform:uppercase;color:var(--mist)}
.billing b{font-weight:700;font-size:1.1rem;color:var(--snow);letter-spacing:.04em}
.rating{display:flex;align-items:stretch;gap:0;border:1.5px solid var(--mist);max-width:21rem;text-align:left}
.rating-mark{display:grid;place-items:center;padding:4px 10px;font:400 2.1rem/1 var(--display);border-right:1.5px solid var(--mist)}
.rating p{padding:6px 10px;font:500 .875rem/1.3 var(--cond);letter-spacing:.04em;text-transform:uppercase;color:var(--mist)}
.rating p b{display:block;color:var(--snow);font-weight:700;letter-spacing:.12em}

/* ---------- previously on ---------- */
.section{padding-block:34px 8px;display:grid;gap:16px}
.section.rsvp{padding-bottom:48px}
.eyebrow-h{font:400 1.9rem/1 var(--display);letter-spacing:.02em;text-transform:uppercase;color:var(--snow)}
.parts{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.parts>li{display:grid;grid-template-columns:auto 1fr;gap:14px;align-items:start;padding-bottom:12px;border-bottom:1px solid var(--ink-3)}
.parts>li:last-child{border-bottom:0}
.part-n{font:400 3rem/.8 var(--display);color:transparent;-webkit-text-stroke:1.5px var(--holly);min-width:1.6rem;text-align:center}
.parts h3{font:400 1.6rem/1.05 var(--display);letter-spacing:.02em;text-transform:uppercase}
.part-date{font-size:.9rem;line-height:1.4;color:var(--mist);margin-top:2px}
.parts .part-body>p:last-of-type{margin-top:4px;color:var(--snow)}

/* ---------- the recap photos (web/app.js), only once the guest's own link or answer opens them ----------
   One idea: the parts are an episode guide, and the photos are prints and film from those nights.
   The latest part gets one print tucked beside its text (the peek); a part with photos gets a button
   that opens its roll of film, edge to edge, one frame per photo at its own shape, ending on "Next on".
   All in the theme's own colours: the film is the page's ink, darkened; the prints and the sprocket
   holes are the ticket's paper. Captions keep the guest-facing sentence case; only the edge numbers
   are film print. Nothing here moves by itself. */
.recap-status:empty,.roll-note:empty{position:absolute;width:1px;height:1px;overflow:hidden}
/* A roll slow to come, or one that didn't load (its button then says Try again), says so just above
   the button; so does the whole section, with its own Try again under the line. */
.roll-note{margin-top:6px;font-size:.9rem;line-height:1.4;color:var(--mist)}
.recap-retry{margin-top:8px}
.parts>li.has-roll{row-gap:2px}
.parts>li>.roll{grid-column:1 / -1}
/* A part's text never widens the page: a word too long for its column breaks (a long title at large text). */
.parts>li>.part-body,.parts>li>.roll{min-width:0}
.parts h3{overflow-wrap:break-word;overflow-wrap:anywhere}
/* The peek floats at the start of the part's text: lines wrap beside it, and a line that doesn't fit
   there goes below it. The roll button ends that text, so it sits beside the print too where the
   text is short; the text's box (a grid item) holds the float, and the roll opens under both. */
.peek{float:right;display:block;margin:2px 4px 10px 14px;padding:0;border:0;border-radius:2px;background:none;cursor:pointer}
.peek-print{display:block;padding:5px 5px 13px;background:var(--paper);box-shadow:0 8px 18px rgba(0,0,0,.5);transform:rotate(3deg)}
.peek img{display:block;background:var(--ink-2)}
/* On the narrowest phones the print is smaller, so the part's text keeps its room. */
@media (max-width:359px){.peek img{width:64px;height:auto}}
.peek:focus{outline-offset:6px}
/* While its own roll is open, the strip starts with the same photo: the print hides there, keeping
   its place, so nothing beside it moves; it's back when the roll shuts. */
.parts>li.open .peek{visibility:hidden}
.roll-btn,.sroll-btn{display:inline-flex;align-items:center;gap:9px;min-height:44px;margin-top:2px;vertical-align:top;padding:0;border:0;background:none;color:var(--gold);font:600 1rem/1.25 var(--body);text-align:left;cursor:pointer}
.roll-btn::after,.sroll-btn::after{content:"";flex:none;width:7px;height:7px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:translateY(-2px) rotate(45deg)}
.roll-btn[aria-expanded="true"]::after,.sroll-btn[aria-expanded="true"]::after{transform:translateY(2px) rotate(-135deg)}
.roll{position:relative;margin-inline:-16px;padding-top:4px}
/* Earlier and Later at the strip's ends, for a mouse (which has no swipe): only for a pointer that
   hovers and points finely, only while there's more of the strip that way, quiet until pointed at. */
.roll-step{display:none;position:absolute;top:50%;z-index:1;place-items:center;width:44px;height:44px;margin-top:-20px;padding:0;border:0;border-radius:50%;background:rgba(0,0,0,.62);box-shadow:0 0 0 1px rgba(255,255,255,.55),0 2px 10px rgba(0,0,0,.5);color:var(--paper);opacity:.8;cursor:pointer}
.roll-step:hover,.roll-step:focus{opacity:1}
.roll-step .vw-icon{width:20px;height:20px}
.roll-back{left:8px}
.roll-on{right:8px}
@media (hover:hover) and (pointer:fine){.roll.can-back .roll-back,.roll.can-on .roll-on{display:grid}}
.roll-scroll{overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scroll-snap-type:x proximity;scroll-padding-inline:16px}
.film{--fh:120px;position:relative;display:flex;align-items:center;gap:8px;width:max-content;min-width:100%;padding:30px 16px;background:linear-gradient(rgba(0,0,0,.62),rgba(0,0,0,.62)),var(--ink)}
@media (min-width:560px){.film{--fh:136px}}
.film::before,.film::after{content:"";position:absolute;left:0;right:0;height:8px;background:linear-gradient(90deg,transparent 5px,var(--paper) 5px 12px,transparent 12px) 0 0/18px 8px repeat-x;opacity:.5}
.film::before{top:7px}
.film::after{bottom:7px}
.film-leader{flex:none;width:20px;align-self:stretch}
.frames{list-style:none;margin:0;padding:0;display:flex;gap:8px}
.frame{position:relative;flex:none;height:var(--fh)}
.frame-btn{display:block;height:100%;padding:0;border:0;border-radius:2px;background:var(--ink-2);cursor:pointer;scroll-snap-align:start;overflow:hidden}
.frame-btn img{display:block;width:100%;height:100%;object-fit:cover}
.frame-btn.failed img{visibility:hidden}
.frame-edge{position:absolute;left:3px;bottom:-14px;font:700 .62rem/1 var(--cond);letter-spacing:.14em;color:var(--gold);opacity:.7;pointer-events:none}
/* "Next on" points back to the poster without being another one: the edition's title a step below
   the poster's own sizes, close to its kicker and date; the red action stays the strongest thing. */
.end-card{display:grid;align-content:center;justify-items:start;gap:2px}
.end-kicker{font:600 .8rem/1.2 var(--body);color:var(--mist)}
.end-title{font:400 1rem/1 var(--display);letter-spacing:.02em;text-transform:uppercase;color:var(--snow);overflow-wrap:break-word;overflow-wrap:anywhere}
.end-when{font:400 1.25rem/1 var(--display);letter-spacing:.03em;text-transform:uppercase;color:var(--gold)}
.end-go{min-height:44px;margin-top:4px;padding:0 16px;border:0;border-radius:6px;background:linear-gradient(180deg,var(--red-hi),var(--red) 40%,var(--red-deep));color:var(--on-red);font:700 .85rem/1 var(--cond);letter-spacing:.18em;text-transform:uppercase;cursor:pointer}
.roll-end{flex:none;width:184px;min-height:var(--fh);padding:6px 12px;border-radius:2px;background:var(--ink-2)}

/* The print viewer: a modal dialog over the whole screen, a cinema's dark. Its controls stay clear of
   the notch and the home bar; the stage takes swipes, and a pinch is still the browser's to zoom. */
.viewing,.viewing body{overflow:hidden}
.viewer{position:fixed;inset:0;z-index:50;width:auto;height:auto;max-width:none;max-height:none;margin:0;padding:0;border:0;overflow:hidden;background:radial-gradient(ellipse 80% 60% at 50% 42%,var(--ink-2),#000 78%);color:var(--snow);overscroll-behavior:contain;touch-action:pinch-zoom}
.viewer.is-open{display:block}
.viewer::backdrop{background:#000}
.vw{position:absolute;inset:0;display:grid;grid-template-rows:auto 1fr auto;-webkit-user-select:none;user-select:none}
.vw-top{display:flex;align-items:center;gap:10px;padding:calc(8px + env(safe-area-inset-top,0px)) calc(10px + env(safe-area-inset-right,0px)) 6px calc(16px + env(safe-area-inset-left,0px))}
.vw-head{flex:1;min-width:0;display:grid;gap:2px}
.vw-title{font:400 1.35rem/1.05 var(--display);letter-spacing:.02em;text-transform:uppercase;overflow-wrap:break-word;overflow-wrap:anywhere}
.vw-pos{font-size:.9rem;color:var(--mist);font-variant-numeric:tabular-nums}
.vw-btn{display:grid;place-items:center;flex:none;width:44px;height:44px;padding:0;border:0;border-radius:50%;background:rgba(255,255,255,.1);color:var(--snow);cursor:pointer}
.vw-btn[aria-disabled="true"]{opacity:.35;cursor:default}
.vw-icon{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.vw-stage{position:relative;min-height:0;overflow:hidden;touch-action:pinch-zoom}
.vw-track{position:absolute;inset:0;display:flex;transform:translateX(-100%)}
.vw-slide{position:relative;flex:0 0 100%;width:100%;height:100%;display:grid;place-items:center;padding:12px 16px;overflow:hidden}
.vw-bottom{display:flex;align-items:center;gap:10px;padding:6px calc(16px + env(safe-area-inset-right,0px)) calc(12px + env(safe-area-inset-bottom,0px)) calc(16px + env(safe-area-inset-left,0px))}
.vw-cap{flex:1;min-width:0;text-align:center;font-size:.95rem;line-height:1.35;overflow-wrap:break-word;overflow-wrap:anywhere}
.print-fig{margin:0}
.print{padding:clamp(8px,2.6vw,16px);background:var(--paper);box-shadow:0 20px 44px rgba(0,0,0,.6)}
.print img{display:block;max-width:100%;background:var(--ink-2) center/cover no-repeat}
.print.failed{display:grid;justify-items:center;gap:12px;min-width:min(18rem,70vw);padding:28px 20px;color:var(--paper-ink);text-align:center}
.print.failed .btn-ghost{color:var(--snow)}
.vw-end{max-width:22rem;justify-items:center;gap:4px;text-align:center}
.vw-end .end-kicker{font-size:1rem}
.vw-end .end-title{font-size:2rem;line-height:.95}
.vw-end .end-go{margin-top:8px;min-height:48px;padding:0 26px;font-size:1rem}

/* ---------- RSVP ---------- */
.rsvp h2{font:400 2.6rem/1 var(--display);text-transform:uppercase;letter-spacing:.02em;scroll-margin-top:calc(env(safe-area-inset-top,0px) + 12px)}
/* A personal link not yet answered: "<First name>, you're invited", set like the poster's tagline. */
.greeting{margin-bottom:-8px;font-style:italic;font-size:1.15rem;line-height:1.3;color:var(--gold);overflow-wrap:break-word;overflow-wrap:anywhere}
.lede{color:var(--mist);margin-top:-8px}
.cast-status{color:var(--mist)}
/* The guest's own place (web/app.js renderOwn): a line of its own above the public list, never a mark
   on one of its rows. Their credit in the display face, the label quiet beside it. */
.cast-self{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px}
.cast-self-label{color:var(--mist)}
.cast-self-name{min-width:0;font:400 1.45rem/1.1 var(--display);letter-spacing:.03em;text-transform:uppercase;color:var(--gold);overflow-wrap:break-word;overflow-wrap:anywhere}
.cast-self-note{color:var(--mist)}
.cast-status:empty{position:absolute;width:1px;height:1px;overflow:hidden}
.cast-names{list-style:none;margin:0;padding:0;columns:2 9rem;column-gap:20px}
.cast-names li{break-inside:avoid;padding:5px 0;border-bottom:1px solid var(--ink-3);font-weight:600;overflow-wrap:break-word;overflow-wrap:anywhere}
/* While the list is "updating" (web/app.js renderCast), the space it keeps shows plain bars where its
   rows sat, each row's rule under a short bar: no names, no count and nothing that moves, all drawn
   as the list's background (it holds no rows then, and is hidden from screen readers). Two columns,
   or one (.held-one) where the rows had one. A row is 36 px, as a name's row is. */
.cast-names.held{--ph-w:calc((100% - 20px) / 2);position:relative;background:linear-gradient(transparent 35px,var(--ink-3) 35px) 0 0/var(--ph-w) 36px space repeat}
.cast-names.held-one{--ph-w:100%}
.cast-names.held::before,.cast-names.held::after{content:"";position:absolute;top:0;bottom:0;left:0;width:calc(var(--ph-w) * .58);background:linear-gradient(transparent 12px,var(--ink-2) 12px 23px,transparent 23px) 0 0/100% 36px repeat-y}
.cast-names.held::after{left:calc(var(--ph-w) + 20px);width:calc(var(--ph-w) * .42)}
.cast-names.held-one::after{display:none}
.cast-notice{margin-top:6px}
/* A long list's "See all {count}" (web/app.js renderCast), under the dozen it shows: a text button,
   about a row of the list tall, taking a tap 46 px tall (a clear layer, as the title chips do). While
   the list is "updating" it keeps its place, unseen, so nothing under the list moves. */
.cast-more{position:relative;justify-self:start;margin-top:-6px;font-weight:600}
.cast-more::after{content:"";position:absolute;left:0;right:0;top:50%;height:max(100%,46px);transform:translateY(-50%)}
.cast-more.held{visibility:hidden}
.not-you{font-size:.9rem;color:var(--mist);overflow-wrap:break-word}
/* Until web/app.js has wired up the form (JavaScript off, or a browser too old for the script), the
   form can't send anything, so it's hidden and the .nojs line shows instead: no button that does
   nothing. The line waits a moment first, so a script that's only slow to arrive doesn't flash it. */
html:not(.js) #rsvp-form{display:none}
.js .nojs{display:none}
.nojs{animation:nojs-wait 0s 2.5s both}
@keyframes nojs-wait{from{visibility:hidden}to{visibility:visible}}
.status{font-size:.95rem;color:var(--mist)}
form{display:grid;gap:22px}
fieldset{border:0;margin:0;padding:0;min-width:0}
.field{display:grid;gap:8px;min-width:0}
legend,.field>label,.lbl{font-weight:700;font-size:1rem;padding:0;margin-bottom:8px}
.field>label,.fold-body>label{margin-bottom:0;font-weight:700}
.opt{font-weight:400;color:var(--mist);font-size:.85rem}
input[type="text"],input[type="password"],textarea,select{width:100%;background:var(--ink-2);border:1px solid var(--ink-3);border-radius:10px;padding:12px 14px;font-size:1rem}
textarea{resize:vertical;min-height:3.2rem}
input::placeholder,textarea::placeholder{color:var(--mist);opacity:.7}
/* Title and Full name share a row while both fit: Title near 7.5rem, Full name the rest. When Full
   name would get less than 12ch (a narrow screen, or large text), it wraps to a row of its own and
   both go full width. Flex, not a container query: iOS 15 has none. */
.name-row{display:flex;flex-wrap:wrap;gap:10px}
.name-row>div{display:grid;gap:6px;align-content:start;min-width:0}
.nr-title{flex:1 1 7.5rem}
.nr-name{flex:999 1 12ch}
.name-row label{font-size:.85rem;color:var(--mist);font-weight:600}
@media (max-width:360px){.name-row>div{flex-basis:100%}}
.hon-chips{display:flex;flex-wrap:wrap;gap:13px 6px;margin-top:10px}
.hon-chips button{position:relative;min-width:44px;border:1px solid var(--ink-3);background:var(--ink-2);border-radius:999px;padding:5px 11px;font-size:.875rem;color:var(--snow);cursor:pointer}
.hon-chips button:hover{border-color:var(--gold)}
.hon-chips button[aria-pressed="true"]{background:var(--gold);border-color:var(--gold);color:var(--ink);font-weight:700}
/* Touch targets: the title chips and a folded field's "Add a note..." button stay small to look at,
   but take a tap at least 44 px tall (WCAG 2.5.5): a clear 46 px layer reaches past the drawn button,
   and wrapped rows of chips sit 13 px apart so their layers don't overlap. Chips are 44 px wide at
   least. Links inside running text are left alone (WCAG 2.5.8's inline exception). */
.linkish[data-expand]{position:relative}
.hon-chips button::after,.linkish[data-expand]::after{content:"";position:absolute;left:0;right:0;top:50%;height:max(100%,46px);transform:translateY(-50%)}
.credit{margin-top:10px;font:600 .8rem/1.4 var(--cond);letter-spacing:.18em;text-transform:uppercase;color:var(--mist)}
.credit b{font:400 1.45rem/1.1 var(--display);letter-spacing:.03em;color:var(--snow);display:block;margin-top:2px;overflow-wrap:break-word;overflow-wrap:anywhere}
.hint{font-size:.85rem;color:var(--mist)}
.saved-note{border-left:3px solid var(--gold);padding:4px 0 4px 12px}
.err{font-size:.9rem;color:var(--holly);font-weight:600}
.has-err input[type="text"],.has-err textarea{border-color:var(--holly);box-shadow:0 0 0 1px var(--holly)}
.has-err .choice label{border-color:var(--holly)}
.sr{position:absolute;opacity:0;width:1px;height:1px;overflow:hidden;pointer-events:none}
.hp{position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden}
.choice{position:relative;display:flex;flex-wrap:wrap;gap:8px}
.choice label{border:1px solid var(--ink-3);background:var(--ink-2);border-radius:10px;padding:11px 16px;cursor:pointer;font-weight:600;min-width:0;-webkit-tap-highlight-color:transparent}
.choice.two{display:grid;grid-template-columns:1fr 1fr}
.choice.two label{text-align:center;padding-block:14px;font-size:1.05rem}
.choice input:checked+label{background:var(--red);border-color:var(--red);color:var(--on-red)}
.choice input:focus+label{outline:2px solid var(--gold);outline-offset:2px}
.choice input:focus:not(:focus-visible)+label{outline:none}
.songs{display:grid;gap:12px}
.song-prompt{display:grid;gap:6px}
.song-prompt label{font-size:.9rem;font-weight:600;color:var(--snow)}
.check-row{display:flex;align-items:flex-start;gap:10px;font-weight:600;cursor:pointer}
.check-row input{width:1.2rem;height:1.2rem;margin:.15rem 0 0;accent-color:var(--red)}
.folded{gap:10px}
.fold-body{display:grid;gap:8px}
.plusone{border-left:3px solid var(--red);background:var(--ink-2);padding:12px 14px;border-radius:0 10px 10px 0;font-size:.95rem}
.plusone b{color:var(--holly)}

/* ---------- confirmation ticket ---------- */
/* The guest is the ticket's star: "Starring", then their credit name in the display face, the
   largest type on it. The event is the small two-line lockup under it. Names wrap; nothing is cut. */
.confirm{display:grid;gap:14px;scroll-margin-top:calc(env(safe-area-inset-top,0px) + 16px)}
.ticket-slot{position:relative}
/* While it prints (web/app.js perform()), the slot clips only below itself: the ticket rises out of
   its bottom edge top first, so "Starring" and the name come out before anything under them. What
   sits below the slot (the line under the ticket, its actions) is never covered: the clip also keeps
   the rising ticket from taking their taps. .tk-mouth is the slot's dark line. */
.ticket-slot.printing{clip-path:inset(-48px -48px 0 -48px)}
.tk-mouth{position:absolute;z-index:2;left:-6px;right:-6px;bottom:-1px;height:3px;border-radius:2px;background:#000;opacity:0;pointer-events:none}
.ticket{position:relative;display:grid;grid-template-columns:1fr auto;background:var(--paper);color:var(--paper-ink);border-radius:10px;overflow:hidden;box-shadow:0 14px 34px rgba(0,0,0,.45)}
.ticket-main{padding:18px 18px 16px;display:grid;gap:6px;min-width:0}
.admit{font:700 .8rem/1 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--red-deep)}
.tk-star{display:grid;gap:2px;margin-top:6px;font-weight:400}
.tk-star:focus{outline:2px solid var(--red-deep);outline-offset:4px}
.tk-star:focus:not(:focus-visible){outline:none}
.tk-kicker{font:600 1rem/1.2 var(--body);color:var(--red-deep)}
.tk-name{display:block;min-width:0;font:400 2.6rem/.92 var(--display);text-transform:uppercase;letter-spacing:.01em;overflow-wrap:break-word;overflow-wrap:anywhere}
.tk-show{display:grid;border-top:1px dashed var(--paper-rule);padding-top:10px;margin-top:6px;font-family:var(--display);text-transform:uppercase}
.tk-series{font-size:1.05rem;line-height:1.1;letter-spacing:.06em}
.tk-sub{font-size:1.5rem;line-height:1;letter-spacing:.02em;color:var(--red-deep)}
.t-line{font:600 .85rem/1.3 var(--cond);letter-spacing:.14em;text-transform:uppercase}
.t-small{font-size:.9rem;display:grid;gap:4px}
.t-small:not(:empty){border-top:1px dashed var(--paper-rule);padding-top:8px;margin-top:4px}
.t-venue{border-top:1px dashed var(--paper-rule);padding-top:8px;margin-top:4px;font-size:.95rem;display:grid;gap:2px}
.ticket-stub{width:2.75rem;border-left:2px dashed var(--paper-rule);padding-block:14px;display:grid;place-items:center}
.ticket-stub span{writing-mode:vertical-rl;transform:rotate(180deg);font:700 .8rem/1 var(--cond);letter-spacing:.24em;text-transform:uppercase;white-space:nowrap}
.ticket::before,.ticket::after{content:"";position:absolute;right:calc(2.75rem - 10px);width:18px;height:18px;border-radius:50%;background:var(--ink)}
.ticket::before{top:-9px}
.ticket::after{bottom:-9px}
.row-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.confirm-actions{display:grid;justify-items:start;gap:2px}
.confirm-actions .btn-primary{margin-bottom:4px;padding:14px 26px}
/* "Add to calendar" and, beside it, the cue to the photos below (web/app.js syncCue). The cue's room
   is kept from the answer's first frame, unseen until the photo sections are on the page, then it
   fades in. Where the row is narrow it takes two lines beside the button, or goes under it. */
.cal-row{display:flex;flex-wrap:wrap;align-items:center;gap:4px 18px;justify-self:stretch;margin-bottom:4px}
.cal-row .btn-primary{margin-bottom:0}
.photo-cue{flex:1 1 0;display:inline-flex;align-items:center;gap:9px;min-height:44px;color:var(--gold);font:600 1rem/1.25 var(--body);text-decoration:none;transition:opacity .2s ease-out}
.photo-cue::after{content:"";flex:none;width:7px;height:7px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:translateY(-2px) rotate(45deg)}
.photo-cue.cue-wait{visibility:hidden;opacity:0}
.cal-alt{color:var(--mist)}
/* Text links that stand alone take a 44 px tall tap, with no box drawn round them. */
.cal-alt a,.edit-line .linkish,.save-actions .linkish{display:inline-block;padding-block:10px}
.edit-line .linkish{font-weight:600}
/* After an answer "Not you?" sits under the actions and the Getting in card (web/app.js
   placeNotYou), its link a 44 px tap. */
.confirm>.not-you a{display:inline-block;padding-block:11px}
/* The way in (the venue's entry line), off the ticket: a plain block under the actions, on the
   page's own ink, with a heading of its own in sentence case. */
.getting-in{display:grid;gap:4px;border-top:1px solid var(--ink-3);padding-top:14px}
.getting-in h3{font:700 1rem/1.3 var(--body);color:var(--snow)}
.getting-in p{overflow-wrap:break-word;overflow-wrap:anywhere}
.btn-ghost{display:inline-block;border:1px solid var(--ink-3);background:var(--ink-2);border-radius:8px;padding:10px 14px;font-weight:600;cursor:pointer;color:var(--snow);text-decoration:none}
.btn-ghost:hover{border-color:var(--gold)}
.btn-ghost.small{padding:5px 10px;font-size:.85rem}
.save-link{display:grid;gap:8px;border-top:1px solid var(--ink-3);padding-top:14px}
.save-actions{column-gap:16px}
.save-url{font-size:.9rem;word-break:break-all}
/* Bonus features: a section of its own after the photo sections, while there's an answer. */
.section.bonus{gap:12px;padding-block:28px 48px}
.bonus h2{font:400 1.6rem/1 var(--display);letter-spacing:.02em;text-transform:uppercase;color:var(--gold)}

/* ---------- host view ---------- */
.host-head{padding-block:28px 6px;display:grid;gap:8px}
.host-head h1,.login h1,.notice h1{font:400 2.8rem/1 var(--display);text-transform:uppercase;letter-spacing:.02em}
.host-title{font-weight:700;color:var(--gold)}
.logout{display:block}
.h-nav{display:flex;flex-wrap:wrap;gap:6px 14px;padding-block:10px;font-size:.9rem}
.h-sec{padding-block:20px 10px;display:grid;gap:10px;border-bottom:1px solid var(--ink-3)}
/* A grid item is as wide as its widest content unless told otherwise: without this, a table whose
   scrolling .tbl-wrap sits inside another box (the song tables) widened the whole page on a phone. */
.h-sec>*{min-width:0}
/* A guest's name can be one long word: host sections break it rather than widen the page. */
.h-sec{overflow-wrap:break-word;overflow-wrap:anywhere}
.h-sec .tbl-wrap{overflow-wrap:normal}
.h-sec:last-child{border-bottom:0;padding-bottom:48px}
.h-sec h2{font:700 .9rem/1.2 var(--cond);letter-spacing:.24em;text-transform:uppercase;color:var(--holly)}
.sub-h{font:700 .8rem/1.2 var(--cond);letter-spacing:.2em;text-transform:uppercase;color:var(--gold);margin-top:10px}
.tally{font-size:1.05rem}
.tally b{font-variant-numeric:tabular-nums}
.notes{margin:0;padding-left:1.1rem;font-size:.92rem;color:var(--mist)}
.tbl-wrap{overflow-x:auto;border:1px solid var(--ink-3);border-radius:10px}
table{border-collapse:collapse;width:100%;font-size:.9rem}
th,td{padding:9px 11px;text-align:left;vertical-align:top;border-bottom:1px solid var(--ink-3)}
tbody tr:last-child td{border-bottom:0}
th{font:600 .78rem/1.2 var(--cond);letter-spacing:.12em;text-transform:uppercase;color:var(--mist);white-space:nowrap;background:var(--ink-2)}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
td.nowrap{white-space:nowrap}
td.pre{white-space:pre-line;min-width:12rem}
td.sub{padding-left:28px;color:var(--mist)}
.badge{display:inline-block;font:700 .68rem/1 var(--cond);letter-spacing:.1em;text-transform:uppercase;border:1px solid currentColor;border-radius:4px;padding:3px 6px;margin:0 6px 2px 0;vertical-align:1px}
.b-yes{color:var(--pine)}
.b-no{color:var(--mist)}
.b-warn,.b-plus{color:var(--gold)}
.missing{margin:0;padding-left:1.1rem}
.missing li{padding-block:2px}
.remind{display:grid;gap:8px;background:var(--ink-2);border:1px solid var(--ink-3);border-radius:10px;padding:12px 14px}
.remind-text{font-size:.95rem}
.plus-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:12px;align-items:end}
.plus-form>div{display:grid;gap:6px;min-width:0}
.plus-form label,.import-form label{font-size:.85rem;font-weight:600;color:var(--mist)}
.import-form{gap:10px}
.cols{margin:0;padding-left:1.6rem;columns:2 14rem;column-gap:24px;font-size:.92rem}
.cols li{break-inside:avoid;padding-block:2px}
.exports{margin:0;padding-left:1.1rem;display:grid;gap:8px}
.card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:10px;padding:12px 14px;display:grid;gap:6px}
.card-list{display:grid;gap:10px}
.kit-grid{display:grid;gap:10px}
.kit-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.kit-row b{min-width:7rem}
/* The hosts' photo library: thumbnails with their roll number and a Hide switch. */
.h-photo-grid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:10px}
.h-photo-grid li{display:grid;gap:4px;align-content:start;font-size:.85rem}
.h-photo-grid img{width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;border-radius:4px;background:var(--ink-3)}
.h-photo-grid li.is-hidden img{opacity:.35;filter:grayscale(1)}
.h-photo-n{display:flex;gap:6px;align-items:center;font-family:var(--cond)}
.h-photo-hide{display:flex;gap:6px;align-items:center;min-height:44px}
details summary{cursor:pointer;color:var(--gold)}
.guest-flags{display:flex;flex-wrap:wrap;gap:8px 14px}
.guest-flags label{display:flex;gap:6px;align-items:center;font-size:.85rem}
.guest-edit{display:grid;gap:8px;margin-top:8px}
.mono{font-family:ui-monospace,"SFMono-Regular",Consolas,monospace;font-size:.82rem}

.name-cell input{min-width:9rem}
.name-cell .badge,.name-cell .btn-ghost{margin-top:6px}

/* ---------- login and notices ---------- */
.login,.notice{padding-block:48px;display:grid;gap:16px}
.login-form{gap:12px}
.login-form label{font-weight:700}

/* ---------- tonight (guest page, when the content file switches it on) ---------- */
.tonight{display:grid;gap:10px;border:1.5px solid var(--gold);border-radius:14px;padding:16px;background:var(--ink-2)}
.tonight>h3{font:700 .9rem/1.2 var(--cond);letter-spacing:.24em;text-transform:uppercase;color:var(--gold)}
.tonight-body{display:grid;gap:10px}
.tonight-lede{color:var(--snow)}
.tonight-h{font:700 .8rem/1.2 var(--cond);letter-spacing:.2em;text-transform:uppercase;color:var(--holly);margin-top:4px}
.ros{list-style:none;margin:0;padding:0;display:grid;gap:6px}
.ros li{display:grid;grid-template-columns:5.2rem 1fr;gap:10px;align-items:baseline}
.ros-text:only-child{grid-column:1/-1}
.ros-time{font:700 .95rem/1.2 var(--cond);letter-spacing:.06em;text-transform:uppercase;color:var(--gold)}
.tonight-home{margin:0;padding-left:1.1rem;display:grid;gap:4px}
.confirm>.tonight{margin-bottom:4px}

/* ---------- end credits (/host/credits): a TV or a laptop ----------
   Sizes follow --u, about 1% of the screen's height (capped by its width on a phone), so the same
   roll fits a 1920x1080 TV and a laptop. Without script it's a plain list under the setup panel. */
.credits-body{min-height:100vh}
.cr-setup{padding-block:28px 40px;display:grid;gap:14px}
.cr-setup h1{font:400 2.8rem/1 var(--display);text-transform:uppercase;letter-spacing:.02em}
.cr-warn li{color:var(--gold)}
.cr-form{gap:18px}
.cr-stage{position:relative;overflow:hidden;color:var(--snow);background:radial-gradient(ellipse 80% 45% at 50% 0%,var(--glow) 0%,transparent 70%),var(--ink)}
.cr-roll{--u:min(1vh,1.9vw);position:relative;display:grid;justify-items:center;padding:6vh 4vw 12vh;text-align:center;font-family:var(--cond)}
.js .cr-stage{position:fixed;inset:0;z-index:5;visibility:hidden;cursor:pointer;touch-action:manipulation;-webkit-user-select:none;user-select:none}
.js .credits:not([data-state="setup"]) .cr-stage{visibility:visible}
.js .cr-roll{position:absolute;left:0;right:0;top:0;padding:0 4vw;will-change:transform;transform:translate3d(0,100vh,0)}
.cr-block{width:100%;max-width:72rem;margin:0}
.cr-off{visibility:hidden}
.cr-open{display:grid;justify-items:center;gap:calc(var(--u)*1.4);padding-bottom:calc(var(--u)*4)}
.cr-presents{font:600 calc(var(--u)*2.2)/1 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--mist)}
.cr-t-top{font:400 calc(var(--u)*5)/1 var(--display);letter-spacing:.06em;text-transform:uppercase}
.cr-t-main{font:400 calc(var(--u)*14)/.88 var(--display);letter-spacing:.01em;text-transform:uppercase}
.cr-kicker{margin-top:calc(var(--u)*1.2);font:700 calc(var(--u)*2.2)/1 var(--cond);letter-spacing:.36em;text-transform:uppercase;color:var(--holly)}
.cr-sub,.cr-next{font:400 calc(var(--u)*7.5)/.95 var(--display);text-transform:uppercase;letter-spacing:.015em;background:linear-gradient(180deg,var(--gold-hi) 0%,var(--gold) 48%,var(--gold-deep) 100%);-webkit-background-clip:text;background-clip:text;color:transparent;text-wrap:balance}
.cr-h{margin-top:calc(var(--u)*9);margin-bottom:calc(var(--u)*3);font:700 calc(var(--u)*2.6)/1.1 var(--cond);letter-spacing:.32em;text-transform:uppercase;color:var(--gold)}
.cr-star{display:grid;grid-template-columns:1fr 1fr;column-gap:calc(var(--u)*2.4);align-items:baseline;padding-block:calc(var(--u)*.55)}
.cr-role{text-align:right;font:500 calc(var(--u)*2.7)/1.15 var(--cond);letter-spacing:.14em;text-transform:uppercase;color:var(--mist);overflow-wrap:break-word;overflow-wrap:anywhere}
.cr-name{text-align:left;font:700 calc(var(--u)*4)/1.1 var(--cond);letter-spacing:.06em;text-transform:uppercase;overflow-wrap:break-word;overflow-wrap:anywhere}
.cr-song{display:grid;gap:calc(var(--u)*.6);padding-block:calc(var(--u)*1.3)}
.cr-song-name{font:700 calc(var(--u)*3.5)/1.12 var(--cond);letter-spacing:.05em;text-transform:uppercase;overflow-wrap:break-word;overflow-wrap:anywhere}
.cr-song-name::before{content:"\201C"}
.cr-song-name::after{content:"\201D"}
.cr-by{font:500 calc(var(--u)*2.2)/1.35 var(--cond);letter-spacing:.12em;text-transform:uppercase;color:var(--mist)}
.cr-vote{display:grid;justify-items:center;gap:calc(var(--u)*1)}
.cr-winner{font:400 calc(var(--u)*9)/1 var(--display);text-transform:uppercase;letter-spacing:.02em}
.cr-tie{font:700 calc(var(--u)*2.2)/1 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--holly)}
.cr-host{font:700 calc(var(--u)*4.4)/1.15 var(--cond);letter-spacing:.08em;text-transform:uppercase;padding-block:calc(var(--u)*.4)}
.cr-end{margin-top:75vh;display:grid;justify-items:center;gap:calc(var(--u)*2.2);padding-bottom:calc(var(--u)*4)}
.cr-lead{font:700 calc(var(--u)*2.6)/1 var(--cond);letter-spacing:.34em;text-transform:uppercase;color:var(--holly)}
.cr-next{font-size:calc(var(--u)*11)}
.cr-bar{position:fixed;left:50%;bottom:max(16px,env(safe-area-inset-bottom));transform:translateX(-50%);z-index:6;display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:8px;width:max-content;max-width:calc(100vw - 32px);padding:10px 12px;background:rgba(0,0,0,.72);border:1px solid var(--ink-3);border-radius:12px;transition:opacity .3s}
.cr-speed-label{min-width:3.2em;text-align:center;font-weight:700;font-variant-numeric:tabular-nums}
.credits.idle .cr-bar{opacity:0;pointer-events:none}
.credits.idle .cr-stage{cursor:none}
@media (max-width:640px){
  .cr-star{grid-template-columns:1fr;row-gap:calc(var(--u)*.2)}
  .cr-role,.cr-name{text-align:center}
  .cr-role:empty{display:none}
}

/* ---------- name badges (/host/badges): page size and label grid come from badges.css ---------- */
.badges-body{background:var(--ink)}
.bd-tools{padding-block:24px 16px;display:grid;gap:10px}
.bd-tools h1{font:400 2.8rem/1 var(--display);text-transform:uppercase;letter-spacing:.02em}
.bd-sheets{overflow-x:auto;padding:0 16px 48px}
.bd-sheet{display:grid;align-content:start;margin:0 auto 24px;background:#fff;color:var(--paper-ink);box-shadow:0 10px 30px rgba(0,0,0,.45)}
.bd-label{position:relative;overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.05in;padding:.3in .2in .3in;text-align:center;outline:1px dashed #c4c4c4;outline-offset:-1px}
.bd-label::before{content:"";position:absolute;left:0;right:0;top:0;height:.16in;background:repeating-linear-gradient(-45deg,var(--red) 0 .09in,#fff .09in .18in)}
.bd-kicker{font:700 8.5pt/1 var(--cond);letter-spacing:.3em;text-transform:uppercase;color:var(--red-deep)}
.bd-title{max-width:100%;font:400 18pt/1 var(--display);letter-spacing:.04em;text-transform:uppercase;color:var(--red-deep);overflow-wrap:break-word;overflow-wrap:anywhere}
.bd-title.t-m{font-size:15pt}
.bd-title.t-s{font-size:12pt}
.bd-title.t-xs{font-size:10pt}
.bd-first{max-width:100%;font:400 44pt/.95 var(--display);letter-spacing:.02em;text-transform:uppercase;color:var(--paper-ink);overflow-wrap:break-word;overflow-wrap:anywhere}
.bd-first.f-l{font-size:38pt}
.bd-first.f-m{font-size:30pt}
.bd-first.f-s{font-size:22pt}
.bd-foot{position:absolute;left:.2in;right:.2in;bottom:.13in;font:600 7pt/1.2 var(--cond);letter-spacing:.14em;text-transform:uppercase;color:#6b6b6b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bd-line{width:78%;height:.38in;border-bottom:1px solid #9a9a9a}
.bd-line-title{width:52%;height:.22in}
@media print{
  .badges-body{background:#fff}
  .bd-tools{display:none}
  .bd-sheets{overflow:visible;padding:0}
  .bd-sheet{margin:0;box-shadow:none;break-after:page;page-break-after:always}
  .bd-sheet:last-child{break-after:auto;page-break-after:auto}
  .bd-label{outline:none}
  .badges-body,.badges-body *{-webkit-print-color-adjust:exact;print-color-adjust:exact}
}

/* The photo sections under the ticket (web/app.js, "Deleted scenes" and "Your scenes"): quieter than
   the ticket, in the edition's own ink, cream and gold; no motion of their own. The grids are contact
   sheets of square cells, each sized before its photo loads, so nothing under them jumps; the viewer
   shows each photo whole. */
.scenes{padding-top:28px}
/* The first photo section on the page follows the answer at one gap, the RSVP section's own 48 px,
   not that and its 28 px as well (Your scenes, or Deleted scenes when Your scenes isn't on). */
.section.rsvp+.scenes,.section.rsvp+.scenes[hidden]+.scenes{padding-top:0}
/* The cue to the photos brings these headings to the top of the screen, clear of the notch. */
.scenes .eyebrow-h{font-size:1.6rem;scroll-margin-top:calc(env(safe-area-inset-top,0px) + 12px)}
.rolls{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.srow{display:grid;gap:2px;padding-bottom:10px;border-bottom:1px solid var(--ink-3)}
.srow:last-child{border-bottom:0}
.srow-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:center}
.srow-head h3{font:400 1.25rem/1.05 var(--display);letter-spacing:.02em;text-transform:uppercase;overflow-wrap:break-word;overflow-wrap:anywhere;min-width:0}
.srow .part-n{font-size:2.2rem}
.sgrid-wrap{display:grid;gap:12px;margin-top:6px;min-width:0}
.sgrid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:6px}
.scell{display:grid;gap:2px;min-width:0}
.sthumb{display:block;width:100%;aspect-ratio:1/1;min-height:44px;padding:0;border:0;border-radius:2px;background:var(--ink-2);cursor:pointer;overflow:hidden}
.sthumb img{display:block;width:100%;height:100%;object-fit:cover;color:transparent}
.sthumb.failed img{visibility:hidden}
.scell-n{font:400 .8rem/1 var(--cond);letter-spacing:.08em;color:var(--mist);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.scenes-body{display:grid;gap:12px;min-width:0}
.yours-more{justify-self:start;min-height:44px}
.sactions{display:grid;gap:8px}
.sactions .btn-ghost{min-height:44px}
/* A long roll's keeping buttons at its start: the same buttons, smaller, so the roll leads. */
.sactions-top .btn-ghost{font-size:.85rem;padding-left:12px;padding-right:12px}
.yours-retry{justify-self:start;min-height:44px}
.job{display:grid;gap:6px}
.job-status{font-size:.9rem;line-height:1.4;color:var(--snow)}
.job-status:empty{display:none}
.job-buttons a.btn-ghost{min-height:44px;display:inline-flex;align-items:center}
.job-buttons .linkish{min-height:44px}
.takedown{margin-top:4px}
/* The viewer's tools for a shared photo: its original to open or download, and Save on a phone. */
.vw-tools{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:4px 16px;padding:0 calc(16px + env(safe-area-inset-right,0px)) calc(12px + env(safe-area-inset-bottom,0px)) calc(16px + env(safe-area-inset-left,0px))}
.vw-tool{display:inline-flex;align-items:center;min-height:44px;padding:0 4px;border:0;background:none;color:var(--gold);font:600 .95rem/1.2 var(--body);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.vw-tool[disabled]{opacity:.5;cursor:default}
.vw-tool-status{flex-basis:100%;text-align:center;font-size:.9rem;color:var(--snow)}
.vw-tool-status:empty{display:none}

/* The wall (design lane "the wall", web/app.js buildWide/buildPile/starWall): the latest part's
   highlights as prints on paper. Decoration over photos the page offers elsewhere: hidden from
   screen readers, out of the Tab order. Out of the flow everywhere, so it never moves the ticket
   or the form. Each print sits under a veil of the ground colour, so it reads as backdrop and the
   column stays the page's subject; a pointer over one lifts the veil. */
#guest{position:relative}
.wall{position:absolute;inset:0;z-index:1;overflow:hidden;pointer-events:none}
.wall-side{position:absolute;top:0;bottom:0;width:calc((100% - 34rem) / 2 - 44px);max-width:30rem}
.wall-l{left:22px}
.wall-r{right:22px}
.wprint{position:absolute;display:block;margin:0;padding:6px 6px 20px;border:0;border-radius:1px;background:var(--paper);box-shadow:0 12px 28px rgba(0,0,0,.6);transform:rotate(var(--r,0deg));cursor:pointer;pointer-events:auto;transition:opacity .8s ease,transform .25s ease}
.wprint img{display:block;width:100%;height:auto;background:var(--ink-2);color:transparent}
.wprint.failed img{visibility:hidden}
.wprint::after{content:"";position:absolute;inset:0;background:var(--ink);opacity:.42;transition:opacity .25s ease;pointer-events:none}
@media (hover:hover) and (pointer:fine){
  .wprint:hover{z-index:3;transform:rotate(var(--r,0deg)) translateY(-4px) scale(1.04)}
  .wprint:hover::after{opacity:0}
}
/* The peek is the wall's first print, already on the wall or the pile, so it steps out of sight. */
.wall-wide .peek,.wall-pile .peek{display:none}
/* On a wide screen "Previously on" is the caption to the wall either side: centred. */
.wall-wide .recap{text-align:center;justify-items:center}
.wall-wide .recap .parts>li{grid-template-columns:1fr;justify-items:center;border-bottom:0}
.wall-wide .recap .part-n{font-size:2.2rem;min-width:0}
.wall-wide .recap .parts>li>.roll{justify-self:stretch;text-align:left}
/* The pile, on narrower screens: five prints fanned across the column under the heading. */
.pile{position:relative;height:clamp(112px,30vw,180px);margin:4px 0 0}
.pile-print{width:34%;max-width:180px;box-shadow:0 10px 22px rgba(0,0,0,.6)}
.pile-print::after{opacity:.12}
/* "Previously on" as the heading and the wheel only (content previously.showItems false; web/app.js
   buildWheel): the height is kept before the prints come, so nothing under them moves when they do.
   The box clips the arc's ends, so it never widens the page; an upright drag stays the page's scroll. */
.recap-bare{--wheel-h:clamp(128px,38vw,150px)}
@media (min-width:600px){.recap-bare{--wheel-h:190px}}
.recap-bare .parts{display:block;width:100%;min-height:var(--wheel-h)}
.wall-wide .recap-bare .parts{justify-self:stretch}
.wheel{position:relative;height:var(--wheel-h);margin-inline:-16px;overflow:hidden;cursor:default;cursor:grab;touch-action:pan-y;touch-action:pan-y pinch-zoom;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}
.wheel.turning,.wheel.turning .wprint{cursor:default;cursor:grabbing}
.wprint.wheel-print{left:50%;top:50%;padding:5px 5px 16px;box-shadow:0 6px 14px rgba(0,0,0,.55);transition:none;touch-action:pan-y;touch-action:pan-y pinch-zoom}
.wheel-print::after{opacity:var(--veil,0);transition:none}
.wheel-print img{width:100%;height:100%;object-fit:cover;pointer-events:none}
.wheel-print.mine{box-shadow:0 6px 14px rgba(0,0,0,.55),0 0 0 2px var(--gold)}
/* The phone's poster wall: prints half off the poster's edges, behind its words and the snow, under
   a veil that is heaviest down the middle where the title and the date are. */
.poster-wall{position:absolute;inset:0;overflow:hidden;pointer-events:none}
.poster-wall::after{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 46% 60% at 50% 45%,rgba(0,0,0,.55),transparent 100%),linear-gradient(90deg,transparent 0%,var(--ink) 30%,var(--ink) 70%,transparent 100%);opacity:.72}
.wprint.poster-print{width:40%;max-width:220px;padding:5px 5px 15px;pointer-events:none;cursor:default}
.wprint.poster-print::after{opacity:.38}
/* The poster's smaller words keep a shadow while prints sit behind them (the title's gradient letters need none). */
.wall-pile .poster :is(.presents,.tagline,.near,.date,.where,.by,.billing,.rating p){text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 10px rgba(0,0,0,.6)}
/* "Starring you": the guest's own photos gathered beside the ticket, the rest of the wall dimmed. */
.wall.starring .wprint:not(.starred){opacity:.22}
.wall.starring .wprint.cleared{opacity:0;pointer-events:none}
.wprint.starred{z-index:2;padding:8px 8px 26px;box-shadow:0 18px 40px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.08)}
.wprint.starred::after{opacity:0}
.wall .wprint.starred img{aspect-ratio:5/4;object-fit:cover}
.star-pile{position:relative;margin:18px 0 4px}
/* The phone's tuck: prints behind the ticket's top corner (the ticket, a later positioned sibling, paints over them). */
.star-tuck{position:absolute;z-index:0;right:10px;bottom:calc(100% - 46px);height:0}
.wprint.tuck-print{bottom:0;width:100%;padding:5px 5px 16px}
.tuck-print img{aspect-ratio:4/5;object-fit:cover}
.star-pile .wall-star-label{position:relative;display:inline-block;left:0;margin:0 0 -6px 6px;font-size:1.3rem}
.star-pile .pile-print{padding:6px 6px 20px}
.wall-star-label{position:absolute;left:4%;z-index:4;padding:4px 12px;background:var(--gold);color:var(--ink);font:400 1.5rem/1.1 var(--display);letter-spacing:.06em;text-transform:uppercase;transform:rotate(-3deg);box-shadow:0 6px 14px rgba(0,0,0,.5);white-space:nowrap}
/* Your scenes on every screen: the guest's photos as prints, each a little turned. A yes just given
   deals them in one after another (.deal); nothing moves with reduced motion. */
#yours .sthumb{padding:4px 4px 12px;background:var(--paper);border-radius:1px;box-shadow:0 6px 14px rgba(0,0,0,.45);transform:rotate(var(--t,0deg))}
#yours .scell:nth-child(3n+1){--t:-2deg}
#yours .scell:nth-child(3n+2){--t:1.5deg}
#yours .scell:nth-child(3n){--t:-.8deg}
.sgrid.deal .scell{animation:deal .7s cubic-bezier(.2,.7,.2,1) both;animation-delay:calc(var(--k,0) * 110ms + 150ms)}
@keyframes deal{from{opacity:0;transform:translateY(60px) rotate(-8deg) scale(.85)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.wprint,.wprint::after{transition:none}.sgrid.deal .scell{animation:none}}

@media (prefers-reduced-motion:reduce){*{scroll-behavior:auto!important}.cr-bar,.photo-cue{transition:none}[data-link="personal"] .star-line{transition:none;transform:none}}
