/* invite.hawsar.app — the invitation a guest opens.
   Every colour comes from the --i-* properties the renderer writes on .inv.
   This file is the frame every design shares (the cover's mechanics, the
   bar, the form, the laptop layout) and the look of the six first themes;
   each newer design adds assets/styles/<style>.css, scoped to
   .inv[data-style="<style>"].
   See-through colours come ready-mixed from palette() (--i-line, --i-glass
   and the rest), never mixed here with color-mix, which Safari before 16.2
   drops whole, and iPhones stuck on iOS 15 still open these links.
   Bump ASSET_VERSION in functions/src/invite/render.ts when this changes. */

@font-face {
  font-family: "ArkanEffra";
  src: url("/fonts/ArkanEffra-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html.locked, html.locked body { overflow: hidden; }
body { margin: 0; }

.inv {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--i-bg);
  color: var(--i-ink);
  font-family: var(--i-body);
  font-size: 16px;
  line-height: 1.7;
  text-align: center;
  position: relative;
  -webkit-font-smoothing: antialiased;
}
/* The couple's colours are the design: a browser's forced dark mode must not
   repaint them (renderPage says the same in a color-scheme meta). */
.inv { color-scheme: light only; }
.inv[data-dark="1"] { color-scheme: dark only; }
/* English, left to right (2026-10-03, PLAN-EN D6, D8). The renderer writes lang and dir on .inv itself. An ornament
   that must swap sides puts scaleX(var(--mx)) before its own transform: 1 right to left, so the matrix is today's,
   and -1 left to right, its mirror. Attribute selectors, never :dir(), which iOS 15 Safari lacks. Latin script faces
   have one weight: a 700 asked of them draws the face as it is, never a smeared fake bold or a slanted fake italic. */
.inv { --mx: 1; }
.inv[dir="ltr"] { --mx: -1; }
.inv[lang="en"] { font-synthesis: none; }
.inv [hidden] { display: none !important; }
/* Links keep the colour of the text around them unless a rule of their own
   says otherwise. :where() keeps this the weakest rule there is, so a link
   drawn as a button (the map, the cover's open) takes its ink from .btn, as
   a <button> does, here and on the pages that restyle .btn around a preview. */
.inv :where(a) { color: inherit; }
.inv svg { display: block; }

/* ---------- the cover ---------- */
.inv-cover {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--i-bg);
  color: var(--i-ink);
  overflow: hidden;
  transition: transform .95s cubic-bezier(.76, 0, .24, 1);
}
.inv-cover.gone { transform: translateY(-100%); pointer-events: none; }
/* Only the six first themes slide their cover up. Every newer design plays
   its own opening from .opening (invite.js adds it, then hides the cover
   after the design's data-open-ms), and nothing under it can be tapped. */
.inv:not([data-style="classic"]) .inv-cover { transition: none; }
.inv-cover.opening { pointer-events: none; }
.inv-cover[hidden] { display: none; }
/* The open button is a link to #inv: a page whose invite.js never arrived
   opens the cover this way, and the script cancels the jump to slide it. */
#inv:target .inv-cover { display: none; }
/* The words scroll inside the cover, which stays still, photo and ornaments
   and all: a phone held sideways is shorter than they are, and the open
   button must stay within reach. Auto margins centre them while they fit and
   start them at the top when they do not. */
.cover-scroll {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  padding: max(32px, env(safe-area-inset-top)) 24px max(32px, env(safe-area-inset-bottom));
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.cover-photo { position: absolute; inset: 0; background-size: cover; background-position: 50% var(--i-focus-y, 40%); }
.cover-photo::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(10, 8, 7, .12) 25%, rgba(10, 8, 7, .68)); }
.inv-cover.has-photo { color: #fff; }
.cover-in { position: relative; display: grid; gap: 18px; justify-items: center; width: 100%; max-width: 26rem; margin: auto; }
.cover-kick { margin: 0; font-size: .95rem; color: var(--i-muted); }
.has-photo .cover-kick, .has-photo .cover-date { color: rgba(255, 255, 255, .88); }
.mono {
  width: 124px; height: 124px; border-radius: 50%;
  border: 1.5px solid var(--i-accent);
  display: flex; align-items: center; justify-content: center; gap: .22em;
  font-family: var(--i-font); font-size: 2.35rem; font-weight: 700; line-height: 1;
  color: var(--i-accent-ink); position: relative;
}
.mono::before { content: ""; position: absolute; inset: 6px; border-radius: 50%; border: 1px solid var(--i-accent); opacity: .45; }
.mono i { font-style: normal; font-family: var(--i-body); font-weight: 600; font-size: .46em; opacity: .8; }
.has-photo .mono { color: #fff; border-color: rgba(255, 255, 255, .85); }
.has-photo .mono::before { border-color: rgba(255, 255, 255, .6); }
.cover-names { margin: 0; font-family: var(--i-font); font-weight: 700; font-size: clamp(2.2rem, 10vw, 3rem); line-height: 1.35; }
.cover-names em { font-style: normal; font-family: var(--i-body); font-weight: 600; color: var(--i-accent-ink); font-size: .5em; margin-inline: .35em; }
.has-photo .cover-names em { color: #fff; }
.cover-date { margin: 0; font-size: .95rem; color: var(--i-muted); }
.has-photo .btn { background: #fff; color: #1a1614; }

/* ---------- the bar ---------- */
.inv-bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  max-width: 34rem; margin: 0 auto;
  padding: calc(env(safe-area-inset-top) + 10px) 14px 6px;
  pointer-events: none;
}
.inv-bar > * { pointer-events: auto; }
.lang-switch, .bar-btn {
  display: inline-flex; align-items: center; gap: 2px;
  background: var(--i-glass);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border: 1px solid var(--i-line);
  border-radius: 999px; padding: 3px;
}
.lang-switch a { text-decoration: none; font-size: .86rem; font-weight: 600; padding: 5px 12px; border-radius: 999px; color: var(--i-muted); min-height: 32px; display: inline-flex; align-items: center; }
.lang-switch a[aria-current] { background: var(--i-btn); color: var(--i-btn-ink); }
.bar-btn { font: inherit; font-size: .86rem; font-weight: 600; color: var(--i-ink); padding: 6px 13px; gap: 6px; cursor: pointer; min-height: 38px; }
.bar-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- the page ---------- */
.inv-page { position: relative; max-width: 34rem; margin: 0 auto; padding-bottom: env(safe-area-inset-bottom); }
.inv-sec { position: relative; display: grid; gap: 12px; justify-items: center; padding: 18px 24px; }
.inv-sec.hero { padding-top: 22px; padding-bottom: 28px; gap: 14px; }

.orn-rings { width: 64px; height: 34px; }
.orn-sprig { width: 150px; height: 34px; }
.orn-diamond { width: 64px; height: 34px; }
.orn-rule { display: block; width: 56px; height: 1px; background: var(--i-accent); opacity: .6; }
.band { display: block; width: 100%; height: 26px; }

.bism { margin: 0; font-family: "Amiri", "Noto Naskh Arabic", serif; font-size: 1.12rem; color: var(--i-muted); }
.lead { margin: 0; max-width: 30ch; font-size: 1.04rem; line-height: 1.95; color: var(--i-muted); text-wrap: balance; }
.names { margin: 2px 0 0; display: grid; font-family: var(--i-font); font-weight: 700; font-size: clamp(2.9rem, 14vw, 4.1rem); line-height: 1.22; text-wrap: balance; }
.names em { font-style: normal; font-family: var(--i-body); font-weight: 600; color: var(--i-accent-ink); font-size: .42em; line-height: 1.2; }
.bless { margin: 0; max-width: 28ch; font-size: 1.06rem; line-height: 1.9; text-wrap: balance; }

.kick { margin: 0; font-size: .92rem; font-weight: 700; color: var(--i-accent-ink); }
.card {
  width: 100%; display: grid; gap: 14px; justify-items: center;
  padding: 24px 18px; border-radius: 24px;
  background: var(--i-surface);
  border: 1px solid var(--i-accent-line);
  box-shadow: 0 24px 48px -36px var(--i-shadow);
}
.cd { width: 100%; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.cd div { display: grid; gap: 2px; }
.cd strong { font-size: clamp(1.6rem, 8vw, 2.1rem); font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums; }
.cd span { font-size: .8rem; color: var(--i-muted); }
.when-date { margin: 0; font-weight: 700; font-size: 1.1rem; line-height: 1.6; text-wrap: balance; }
.pill { display: inline-block; padding: 6px 16px; border-radius: 999px; font-weight: 700; font-size: .95rem; color: var(--i-accent-ink); background: var(--i-soft); }

.hall-photo { display: block; width: 100%; max-width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: 20px; background: var(--i-surface); border: 1px solid var(--i-line); }
.venue { margin: 0; font-family: var(--i-font); font-weight: 700; font-size: clamp(1.7rem, 8vw, 2.1rem); line-height: 1.4; text-wrap: balance; }
.addr { margin: 0; color: var(--i-muted); font-size: .98rem; }
.where-actions { display: grid; gap: 12px; justify-items: center; }

.note { margin: 0; max-width: 32ch; padding: 14px 18px; border-radius: 16px; font-size: 1rem; line-height: 1.9; white-space: pre-line; border: 1px dashed var(--i-ink-line); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 12px 24px; border: 0; border-radius: 999px;
  background: var(--i-btn); color: var(--i-btn-ink);
  font: inherit; font-weight: 700; font-size: 1rem; line-height: 1.3;
  text-decoration: none; cursor: pointer;
  box-shadow: 0 14px 28px -16px var(--i-btn);
  transition: transform .2s ease, box-shadow .2s ease;
}
.btn:active { transform: scale(.98); }
.btn svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.btn.soft { background: transparent; color: var(--i-ink); box-shadow: none; border: 1px solid var(--i-ink-line); }
.inv :focus-visible { outline: 2px solid var(--i-accent-ink); outline-offset: 3px; }

/* ---------- replies ---------- */
.rsvp-q { margin: 0; font-family: var(--i-font); font-weight: 700; font-size: 1.7rem; line-height: 1.4; }
.rsvp-by { margin: 0; color: var(--i-muted); font-size: .95rem; }
.rsvp { width: 100%; display: grid; gap: 12px; }
.rsvp label { display: grid; gap: 6px; text-align: start; font-size: .9rem; font-weight: 600; color: var(--i-muted); }
.rsvp input, .rsvp select {
  width: 100%; min-height: 50px; padding: 11px 14px; border-radius: 14px;
  border: 1px solid var(--i-ink-line);
  background: var(--i-bg); color: var(--i-ink);
  font: inherit; font-size: 16px;
}
.rsvp-actions { display: grid; grid-template-columns: 1fr; gap: 10px; margin-top: 4px; }
.rsvp-actions .btn { padding-inline: 12px; font-size: .95rem; }
.rsvp-msg { margin: 0; min-height: 1.4em; font-size: .9rem; color: var(--i-accent-ink); }
.rsvp-done, .rsvp-note { margin: 0; font-weight: 700; font-size: 1.05rem; color: var(--i-accent-ink); line-height: 1.8; }
.rsvp-after { display: grid; gap: 6px; justify-items: center; }
/* Under the thanks: the way back to the form, to change the answer. A link
   on the page a phone without script gets, a button where invite.js runs. */
.inv .rsvp-change {
  font: inherit; font-size: .92rem; font-weight: 700; color: var(--i-muted);
  background: none; border: 0; padding: 8px 12px; min-height: 40px; cursor: pointer;
  text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px;
}
.hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.rsvp.sending .btn { opacity: .6; pointer-events: none; }

/* ---------- the evening, the people, the memories ----------
   The shared markup every design draws (parts.ts: programmeHtml and the
   rest): the base look, in the classic manner and in the page's own colours,
   which a design's sheet restyles as it likes. */
.programme { list-style: none; margin: 0; padding: 0; width: 100%; max-width: 22rem; display: grid; }
.programme li { display: flex; align-items: baseline; gap: 14px; padding: 11px 4px; border-bottom: 1px dashed var(--i-ink-line); }
.programme li:last-child { border-bottom: 0; }
.programme time { flex: none; min-width: 3.4em; text-align: start; font-weight: 700; font-size: 1.05rem; color: var(--i-accent-ink); font-variant-numeric: tabular-nums; }
.programme span { flex: 1; text-align: start; font-weight: 600; line-height: 1.6; }

.reach { gap: 26px; }
.reach-item { width: 100%; display: grid; gap: 10px; justify-items: center; }
.contact-btn {
  width: 100%; max-width: 22rem; min-height: 60px;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 20px;
  border: 1px solid var(--i-ink-line); background: var(--i-surface);
  text-decoration: none; text-align: start;
}
.contact-btn svg {
  flex: none; width: 42px; height: 42px; padding: 10px; border-radius: 50%;
  background: var(--i-btn); color: var(--i-btn-ink);
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round;
}
.contact-who { flex: 1; min-width: 0; display: grid; line-height: 1.45; }
.contact-name { font-weight: 700; overflow-wrap: anywhere; }
/* A number reads left to right; start still puts it under the name. */
.contact-num { justify-self: start; font-size: .88rem; color: var(--i-muted); font-variant-numeric: tabular-nums; }
.contact-call { flex: none; padding: 6px 14px; border-radius: 999px; font-weight: 700; font-size: .9rem; color: var(--i-accent-ink); background: var(--i-soft); }
.calendar { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.calendar a {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; min-width: 7.5rem; padding: 10px 20px; border-radius: 999px;
  border: 1px solid var(--i-ink-line); font-weight: 700; font-size: .95rem; text-decoration: none;
}
.hashtag { margin: 0; font-family: var(--i-font); font-weight: 700; font-size: 1.4rem; line-height: 1.5; color: var(--i-accent-ink); overflow-wrap: anywhere; }

/* Inside the invitation only: the landing and the maker link this stylesheet, and their own .gallery strip of designs
   would take two collapsing columns and a cream ground behind every picture of every cover in it. */
.inv .gallery { width: 100%; max-width: 640px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.inv .gallery img { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; border-radius: 16px; background: var(--i-surface); border: 1px solid var(--i-line); }
.inv .gallery[data-count="1"] { grid-template-columns: minmax(0, 1fr); }
.inv .gallery[data-count="1"] img { aspect-ratio: 4 / 3; }
/* An odd count opens on one wide photo, so the pairs under it come out even. */
.inv .gallery[data-count="3"] img:first-child,
.inv .gallery[data-count="5"] img:first-child { grid-column: 1 / -1; aspect-ratio: 16 / 10; }

/* Where a design's ink does not read on its page (Seal's and Tewn's linen), the
   classic style lays these sections on a paper of its surface, and renderPage
   writes the colours that read there on the section itself (sectionColours). The
   attribute and the doubled class outrank a design that clears .inv-sec's
   own background on a laptop. */
.inv .inv-page .inv-sec.on-paper[style] {
  width: auto; margin: 10px 16px; padding: 26px 20px; border-radius: 24px;
  background: var(--i-surface); color: var(--i-ink);
  box-shadow: 0 22px 44px -30px var(--i-shadow);
}
.on-paper .wish-form { padding: 0; border: 0; box-shadow: none; background: transparent; }

.wishes { width: 100%; }
.wish-form {
  width: 100%; max-width: 32rem; display: grid; gap: 12px;
  padding: 22px 18px; border-radius: 24px;
  background: var(--i-surface); border: 1px solid var(--i-accent-line);
  box-shadow: 0 24px 48px -36px var(--i-shadow);
}
.wish-form label { display: grid; gap: 6px; text-align: start; font-size: .9rem; font-weight: 600; color: var(--i-muted); }
.wish-form input, .wish-form textarea {
  width: 100%; min-height: 50px; padding: 11px 14px; border-radius: 14px;
  border: 1px solid var(--i-ink-line); background: var(--i-bg); color: var(--i-ink);
  font: inherit; font-size: 16px;
}
.wish-form textarea { min-height: 104px; line-height: 1.7; resize: vertical; }
.wish-form.sending .btn { opacity: .6; pointer-events: none; }
.wish-msg { margin: 0; min-height: 1.4em; font-size: .9rem; color: var(--i-accent-ink); }
.wish-msg:empty { min-height: 0; }
.wish-thanks { margin: 0; font-weight: 700; font-size: 1.02rem; color: var(--i-accent-ink); line-height: 1.8; }
.wish-list { list-style: none; margin: 4px 0 0; padding: 0; width: 100%; max-width: 32rem; display: grid; gap: 10px; }
.wish {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 12px; row-gap: 2px; align-items: start;
  padding: 14px 16px; border-radius: 20px; text-align: start;
  background: var(--i-surface); border: 1px solid var(--i-line);
}
.wish-i {
  grid-row: span 2; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--i-font); font-weight: 700; font-size: 1.05rem; line-height: 1;
  color: var(--i-accent-ink); background: var(--i-soft);
}
.wish b { font-weight: 700; line-height: 1.5; overflow-wrap: anywhere; }
.wish p { margin: 0; line-height: 1.8; white-space: pre-line; overflow-wrap: anywhere; }
.wish-empty { margin: 0; color: var(--i-muted); font-size: .95rem; }
.wish.fresh { animation: rise .6s cubic-bezier(.16, 1, .3, 1) backwards; }

/* ---------- the footer: the way into Hawsar ---------- */
.inv-foot { display: grid; gap: 10px; justify-items: center; margin: 22px 24px 0; padding: 26px 0 34px; border-top: 1px solid var(--i-line); }
.foot-mark { color: var(--i-ink); opacity: .82; }
.foot-mark .mark { height: 22px; width: auto; }
.inv-foot p { margin: 0; font-size: .86rem; color: var(--i-muted); }
.foot-cta { font-size: .95rem; font-weight: 700; color: var(--i-accent-ink); text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 1px; }
/* The invitation's last line (parts.ts makeYours): «make your own», a quiet pill outlined in the footer's own colour,
   so it reads on every design's ground, light or dark, and is never mistaken for the link into the app above it. */
.foot-make { display: inline-flex; justify-content: center; color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.foot-make span { display: inline-flex; align-items: center; min-height: 38px; padding: 6px 18px; border: 1px solid currentColor; border-radius: 999px; font-size: .86rem; font-weight: 700; line-height: 1.5; opacity: .82; }
.foot-make:hover span, .foot-make:focus-visible span { opacity: 1; }

/* ---------- themes ---------- */
.inv[data-deco="stars"] {
  background-image:
    radial-gradient(1.4px 1.4px at 12% 18%, var(--i-accent) 60%, transparent 62%),
    radial-gradient(1px 1px at 82% 9%, var(--i-ink) 60%, transparent 62%),
    radial-gradient(1.6px 1.6px at 70% 30%, var(--i-accent) 60%, transparent 62%),
    radial-gradient(1px 1px at 30% 44%, var(--i-ink) 60%, transparent 62%),
    radial-gradient(1.3px 1.3px at 90% 58%, var(--i-accent) 60%, transparent 62%),
    radial-gradient(1px 1px at 8% 72%, var(--i-ink) 60%, transparent 62%),
    radial-gradient(1.5px 1.5px at 55% 86%, var(--i-accent) 60%, transparent 62%),
    radial-gradient(1px 1px at 40% 8%, var(--i-accent) 60%, transparent 62%);
  background-size: 100% 560px;
}
.inv[data-deco="stars"] .inv-cover:not(.has-photo) { background-image: inherit; background-size: inherit; }
.inv[data-deco="frame"] .inv-page::before,
.inv[data-deco="frame"] .inv-cover:not(.has-photo)::before {
  content: ""; position: absolute; inset: 12px; pointer-events: none;
  border: 1px solid var(--i-accent); outline: 1px solid var(--i-accent); outline-offset: -6px;
  border-radius: 24px; opacity: .7;
}
.inv[data-deco="paper"] .inv-cover:not(.has-photo)::before { content: ""; position: absolute; inset: 16px; border: 1px solid var(--i-ink); opacity: .16; pointer-events: none; }
.inv[data-deco="rings"] .inv-cover:not(.has-photo)::after {
  content: ""; position: absolute; width: 150vmax; height: 150vmax; left: 50%; top: 76%;
  transform: translate(-50%, 0); border-radius: 50%;
  border: 1px solid var(--i-ring);
  box-shadow: 0 0 0 28px var(--i-halo);
  pointer-events: none;
}
.inv-cover .band { position: absolute; left: 0; right: 0; }
.inv-cover .band-top { top: 0; }
.inv-cover .band-bottom { bottom: 0; }

/* ---------- the opening ---------- */
.inv.play .rv { animation: rise .8s cubic-bezier(.16, 1, .3, 1) backwards; }
.inv.play .hero .rv:nth-child(1) { animation-delay: .35s; }
.inv.play .hero .rv:nth-child(2) { animation-delay: .45s; }
.inv.play .hero .rv:nth-child(3) { animation-delay: .55s; }
.inv.play .hero .rv:nth-child(4) { animation-delay: .65s; }
.inv.play .hero .rv:nth-child(5) { animation-delay: .75s; }
.inv.play .when .rv, .inv.play .when { animation-delay: .85s; }
@keyframes rise { from { opacity: 0; transform: translateY(18px); } }

/* ---------- not found ---------- */
.inv.lost { display: grid; place-items: center; padding: 32px 24px; }
.lost-in { display: grid; gap: 18px; justify-items: center; max-width: 24rem; }
.lost-in h1 { margin: 0; font-family: var(--i-font); font-size: 1.7rem; line-height: 1.5; }
.lost-in .orn-rings { width: 96px; height: 52px; }
.lost-emb { width: 110px; height: 85px; color: var(--i-ink); }
.lost-mark { color: var(--i-ink); opacity: .7; }
.lost-mark .mark { height: 20px; width: auto; }

.toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom) + 22px); z-index: 60;
  transform: translate(-50%, 16px); opacity: 0; pointer-events: none;
  background: var(--i-ink); color: var(--i-bg);
  padding: 10px 18px; border-radius: 999px; font-weight: 600; font-size: .92rem;
  transition: opacity .25s, transform .25s;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ---------- the music (Premium) ---------- */
/* The round button that pauses the couple's track and plays it again
   (invite.js adds it once the page shows). It is a .bar-btn as well, so each
   design's look for the bar's buttons carries over: these rules only place
   and shape it. The note is struck through while the music is paused, and a
   thin arc turns round it while the track is still on its way. */
#inv .inv-music {
  position: fixed;
  z-index: 30;
  inset-inline-start: 14px;
  bottom: calc(env(safe-area-inset-bottom) + 14px);
  width: 44px;
  height: 44px;
  min-height: 0;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
  box-shadow: 0 8px 20px -10px var(--i-shadow);
}
#inv .inv-music svg { width: 20px; height: 20px; overflow: visible; }
#inv .inv-music .note { fill: currentColor; stroke: none; }
#inv .inv-music .off { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
#inv .inv-music[aria-pressed="true"] .off { display: none; }
#inv .inv-music[aria-pressed="false"] .note { opacity: .5; }
#inv .inv-music[aria-pressed="true"]:not(.loading) svg { animation: inv-beat 1.6s ease-in-out infinite; }
#inv .inv-music.loading::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid transparent; border-top-color: var(--i-accent);
  animation: inv-spin .9s linear infinite;
}
@keyframes inv-beat { 50% { transform: scale(1.12); } }
@keyframes inv-spin { to { transform: rotate(1turn); } }

@media (max-width: 350px) {
  .inv-sec { padding-inline: 18px; }
}
/* A phone held sideways, or a small one zoomed in: the monogram goes and the
   cover tightens, so the names, the date and the button fit without
   scrolling on nearly all of them. The kilim bands keep their room. */
@media (max-height: 480px) {
  .cover-scroll { padding-block: max(12px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-bottom)); }
  .inv[data-deco="kilim"] .inv-cover:not(.has-photo) .cover-scroll {
    padding-block: max(34px, env(safe-area-inset-top)) max(34px, env(safe-area-inset-bottom));
  }
  .cover-in { gap: 10px; }
  .mono { display: none; }
}
/* ---------- a laptop ---------- */
/* From 1024px the invitation is a card on its design's backdrop: the sheet
   (the couple and the day) on the right, the hall and the replies beside it
   on the left, each a smaller card. Relatives abroad open the link on a
   laptop; a phone column in an empty window reads as a mistake. */
@media (min-width: 1024px) {
  .inv { background: var(--i-backdrop); }
  .inv-bar { max-width: 1180px; padding-inline: 32px; }
  .inv-page {
    max-width: 1180px;
    display: grid;
    grid-template-columns: minmax(0, 520px) minmax(0, 480px);
    justify-content: center;
    align-items: start;
    column-gap: 56px;
    padding: 12px 32px 72px;
  }
  .inv-page > :not(.inv-sheet):not(.inv-side) { grid-column: 1 / -1; }
  .inv-sheet {
    position: relative;
    display: flex; flex-direction: column; justify-content: center;
    min-height: 640px;
    padding: 40px 36px 32px;
    border-radius: 28px;
    background: var(--i-surface);
    box-shadow: 0 40px 80px -40px var(--i-shadow);
  }
  .inv-sheet .card { background: transparent; border: 0; box-shadow: none; padding-inline: 0; }
  .inv-side { display: grid; gap: 22px; padding-top: 12px; }
  .inv-side > .inv-sec {
    padding: 28px;
    border-radius: 24px;
    background: var(--i-surface);
    box-shadow: 0 30px 60px -44px var(--i-shadow);
  }
  .inv-side > .inv-sec .card { background: transparent; border: 0; box-shadow: none; padding: 0; }
  .inv-side > .wishes .wish-form { padding: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
  .inv-side > .wishes .wish { background: var(--i-bg); }
  .inv-side .inv-foot { margin: 6px 0 0; }
  .inv[data-deco="frame"] .inv-page::before { content: none; }
  .inv[data-deco="frame"] .inv-sheet::before {
    content: ""; position: absolute; inset: 12px; pointer-events: none;
    border: 1px solid var(--i-accent); outline: 1px solid var(--i-accent); outline-offset: -6px;
    border-radius: 20px; opacity: .7;
  }
  .cover-in { max-width: 34rem; gap: 22px; }
  .cover-names { font-size: clamp(3rem, 4.4vw, 4.6rem); }
  .mono { width: 150px; height: 150px; font-size: 2.8rem; }
}

/* A phone saving data or on 2G (invite.js adds .calm): the same short fade as reduced motion. */
html.calm .inv-cover.opening *, html.calm .inv-cover.opening *::before, html.calm .inv-cover.opening *::after { animation: none !important; transition: none !important; }
html.calm .inv-cover.opening { animation: none !important; transition: opacity .2s !important; opacity: 0; }
html.calm .inv-cover *, html.calm .inv-cover *::before, html.calm .inv-cover *::after { animation-iteration-count: 1 !important; }
html.calm #inv .inv-music svg { animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  .inv-cover { transition-duration: .01ms; }
  .inv.play .rv { animation: none; }
  .wish.fresh { animation: none; }
  /* The music's note keeps still; its loading arc stays, still too. */
  #inv .inv-music svg, #inv .inv-music.loading::after { animation: none !important; }
  /* Every design's opening becomes a short fade. */
  .inv-cover.opening *, .inv-cover.opening *::before, .inv-cover.opening *::after { animation: none !important; transition: none !important; }
  .inv-cover.opening { animation: none !important; transition: opacity .2s !important; opacity: 0; }
}

/* ---------- a demo page (/d/door …): the bar that sells the design ----------
   render.ts demoBar: the design's name, its tier and price, «make this design for me» and WhatsApp, on a card of
   the design's own surface floating over the bottom of the screen, clear of the phone's home bar. It waits while
   the cover is up and while it opens (html.locked, .opening), then rises; without script it is simply there. The
   music button, the door's cue and the toast move up out of its way, and the page ends above it. */
.demo-bar {
  position: fixed; z-index: 45; left: 10px; right: 10px; bottom: calc(10px + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 10px; max-width: 560px; margin: 0 auto;
  padding: 9px; padding-inline-start: 16px; border-radius: 22px;
  background: var(--i-glass); color: var(--i-ink); font-family: var(--i-body);
  -webkit-backdrop-filter: blur(14px) saturate(1.2); backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 0 0 1px var(--i-line), 0 16px 34px -14px rgba(0, 0, 0, .5);
  transition: transform .55s cubic-bezier(.2, .9, .3, 1), opacity .4s;
}
html.locked .demo-bar, .inv-cover.opening:not([hidden]) ~ .demo-bar {
  transform: translateY(calc(100% + 24px)); opacity: 0; pointer-events: none;
}
.demo-what { flex: 1 1 auto; min-width: 0; display: grid; gap: 1px; line-height: 1.35; }
.demo-what b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 1.02rem; }
.demo-what > span { white-space: nowrap; font-size: .8rem; color: var(--i-muted); }
.demo-what .num { font-weight: 700; color: var(--i-ink); }
.demo-make {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; min-height: 46px;
  padding: 7px 18px; border-radius: 999px; background: var(--i-btn); color: var(--i-btn-ink);
  font-weight: 700; font-size: .92rem; line-height: 1.3; white-space: nowrap; text-decoration: none;
  box-shadow: 0 8px 18px -10px var(--i-shadow);
}
.demo-wa {
  flex: none; display: grid; place-items: center; width: 46px; height: 46px; border-radius: 50%;
  background: #25D366; color: #fff; box-shadow: 0 8px 18px -10px rgba(0, 0, 0, .45);
}
.demo-wa svg { width: 25px; height: 25px; fill: currentColor; }
.demo-make:focus-visible, .demo-wa:focus-visible { outline: 2px solid var(--i-ink); outline-offset: 3px; }
/* A phone: the button keeps its one line; the price stays and the tier's name gives way. */
@media (max-width: 430px) {
  .demo-tier { display: none; }
  .demo-bar { gap: 8px; padding-inline-start: 13px; }
  .demo-make { padding-inline: 14px; font-size: .86rem; }
}
/* Under 390 px (375: an SE, a mini, an 8) the button is a step smaller, so «دەست لە دەست» keeps its last word; a name
   that still would not fit breaks onto a second line rather than lose its end to «…». */
@media (max-width: 389px) {
  .demo-make { padding-inline: 11px; font-size: .8rem; }
  .demo-what b { overflow: visible; white-space: normal; text-wrap: balance; line-height: 1.25; }
}
/* The narrowest phones (320): a two-word name («مۆری شیلان», «هەبوو نەبوو») breaks onto a second line rather than
   being cut to «مۆری …». */
@media (max-width: 340px) {
  .demo-what b { overflow: visible; white-space: normal; text-wrap: balance; font-size: .96rem; line-height: 1.25; }
}
#inv[data-demo="1"] .inv-page { padding-bottom: calc(92px + env(safe-area-inset-bottom)); }
@media (min-width: 1024px) { #inv[data-demo="1"] .inv-page { padding-bottom: 40px; margin-bottom: 112px; } }
#inv[data-demo="1"] .inv-music { bottom: calc(env(safe-area-inset-bottom) + 86px); }
.inv[data-demo="1"] ~ .toast { bottom: calc(env(safe-area-inset-bottom) + 96px); }
/* The demo's note under a reply or a wish: nothing is sent. */
.demo-note { margin: 12px 0 0; font-size: .84rem; color: var(--i-muted); text-align: center; }
@media (prefers-reduced-motion: reduce) { .demo-bar { transition: none; } }

/* ---------- English (2026-10-03, PLAN-EN D6, D8): Latin type on the frame every design shares ----------
   Every rule is scoped to .inv[lang="en"] or .inv[dir="ltr"], so a Kurdish or Arabic page never meets one: Arabic
   script is never letter-spaced or uppercased. The faces are palette(design, "en")'s: --i-font the names, --i-and the
   "&", --i-label kickers and labels, --i-num the figures that count or line up, --i-body the rest; --i-names-lh and
   --i-names-pad give a script's swash capitals room (each with a fallback: palette() writes them on English pages only).
   A design's own sheet repeats what its more specific rules win back (cinema.css, door.css, envelope.css, film.css). */
/* Latin reads loose at Arabic script's 1.8 to 2: 1.55 for running text, its figures old-style. */
.inv[lang="en"] { line-height: 1.55; font-variant-numeric: oldstyle-nums proportional-nums; }
.inv[lang="en"] .lead, .inv[lang="en"] .bless, .inv[lang="en"] .note, .inv[lang="en"] .rsvp-done, .inv[lang="en"] .rsvp-note,
.inv[lang="en"] .wish p, .inv[lang="en"] .wish-thanks { line-height: 1.55; }
/* The names: never uppercased (McDonald, al-Hashimi), broken only at their own space or hyphen, with a script's room. */
.inv[lang="en"] .names, .inv[lang="en"] .cover-names { line-height: var(--i-names-lh, 1.2); padding-inline: var(--i-names-pad, 0); overflow-wrap: normal; word-break: normal; -webkit-hyphens: manual; hyphens: manual; }
/* The "&" in its own face, italic where the face has one (a script has none: font-synthesis keeps it upright). */
.inv[lang="en"] .names em, .inv[lang="en"] .cover-names em, .inv[lang="en"] .mono i { font-family: var(--i-and, var(--i-font)); font-style: italic; }
/* Figures that count or line up: lining and tabular, in the design's numeral face, so the countdown never jitters. */
.inv[lang="en"] .cd strong, .inv[lang="en"] .programme time, .inv[lang="en"] .contact-num, .inv[lang="en"] .rsvp select,
.inv[lang="en"] .demo-what .num { font-family: var(--i-num, var(--i-body)); font-variant-numeric: lining-nums tabular-nums; }
/* The classic frame's kickers and its cover's lines as labels: the label face, uppercase and tracked, 0.82 of the size. */
.inv[lang="en"] .kick, .inv[lang="en"] .cover-in .cover-kick, .inv[lang="en"] .cover-in .cover-date {
  font-family: var(--i-label, var(--i-body)); font-weight: 500; text-transform: uppercase; letter-spacing: .15em; font-variant-numeric: lining-nums;
}
/* The switch's Kurdish and Arabic names in the phone's own Arabic-script face: two words never cost an English page a
   web face's Arabic letters (about 90 KB of Noto Naskh Arabic). */
.inv[lang="en"] .lang-switch a:not([lang="en"]) { font-family: system-ui, -apple-system, "Segoe UI", Tahoma, sans-serif; font-weight: 500; }
/* And the other way: on a Kurdish or Arabic page "English" fell from the Arabic web face to a heavy Latin serif,
   larger and darker than «کوردی» and «عربي» beside it. It reads in the phone's own sans at their weight. */
.inv[lang="ckb"] .lang-switch a[lang="en"], .inv[lang="ar"] .lang-switch a[lang="en"] { font-family: system-ui, -apple-system, "Segoe UI", Tahoma, sans-serif; font-weight: 500; }
.inv[lang="en"] .kick { font-size: .755rem; }
/* The countdown's units as labels too (PLAN-EN D9): DAYS, HOURS, MINUTES, SECONDS, uppercase and tracked in the label
   face, as the films' are; small enough that SECONDS fits its quarter on a 320 px phone. */
.inv[lang="en"] .cd span { font-family: var(--i-label, var(--i-body)); text-transform: uppercase; letter-spacing: .12em; font-size: .64rem; }
.inv[lang="en"] .cover-in .cover-kick, .inv[lang="en"] .cover-in .cover-date { font-size: .78rem; }
/* The demo bar on a phone: an English design's name runs longer than its Kurdish one ("The Engagement Tray", "Wild
   Rose Seal"), so it breaks onto a second line, never «…», and the button gives it a little room. */
@media (max-width: 430px) {
  .inv[lang="en"] .demo-what b { overflow: visible; white-space: normal; text-wrap: balance; font-size: .9rem; line-height: 1.2; }
  .inv[lang="en"] .demo-make { padding-inline: 10px; font-size: .8rem; }
}
/* Under 390 px the button's words break over two lines, so a three-word name keeps two lines at most ("The
   Engagement" / "Tray"), never three beside a one-line button; on the narrowest (320) the name and the button are a
   step smaller still. */
@media (max-width: 389px) { .inv[lang="en"] .demo-make { white-space: normal; text-wrap: balance; text-align: center; max-width: 8.6em; line-height: 1.2; } }
@media (max-width: 340px) {
  .inv[lang="en"] .demo-bar { gap: 6px; padding-inline-start: 11px; }
  .inv[lang="en"] .demo-what b { font-size: .84rem; }
  .inv[lang="en"] .demo-make { padding-inline: 8px; }
}
/* What a screen reader says where the page shows something else (the basmala's meaning under its calligraphy). */
.inv .inv-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
