/* The bridal shower uploader, wearing the registry's clothes.
 *
 * This is the design system from C:\Users\Michael\Projects\bridal-shower,
 * narrowed to one look and to the handful of components two pages actually
 * need. The token block and the two dark-mode mapping blocks below are copied
 * out of that project's styles.css verbatim; everything after them is this
 * app's own, built only out of those tokens.
 *
 * THE ONE ROOT RULE holds here as it does there: exactly one :root, ever. The
 * registry carries seven looks and scopes six of them to [data-theme]; this app
 * carries one, so the look lives on :root and nothing scopes it. Swapping to a
 * different look is a replacement of that single block — the other six are at
 * bridal-shower/public/styles.css:133-388.
 *
 * The rules the registry's DESIGN.md sets out are not decoration and are worth
 * restating, because two of them are load-bearing for an uploader specifically:
 *
 *   THE PINK IS NEVER GONE RULE — --accent is the invitation's voice. A failed
 *   upload is --danger. The accent must never be the colour that means "gone".
 *
 *   THE NEVER COLOUR ALONE RULE — a queue row states its status in words as
 *   well as in colour. "Done — thank you!" is not a decoration on the green.
 *
 *   THE TWO CORALS RULE — --accent-2 is legal on the 26px script line and
 *   nowhere smaller; below 24px the same hue is --accent-2-ink.
 *
 *   THE MONTSERRAT CONSTANT RULE — only the display and script faces belong to
 *   a look. Every label is Montserrat.
 *
 *   THE 375 FIRST RULE — 375px is the target, not an afterthought. Every
 *   component below has a phone rule at the end of this file.
 */

:root {
  /* Native widgets — the select popup, the scrollbars, the autofill wash —
     are painted by the browser, and without this they are painted for a light
     page whatever the page is actually wearing. It is one word, and it is the
     only way to tell it. */
  color-scheme:light;
  /* SAVE THE DATE — the default, and the bride's own.
     Lifted from her actual invitation: magenta serif, coral script, gold flower
     centres, blush wash, warm cream card. Neutrals are biased warm toward the
     accent rather than being plain grey. */
  --bg:#FFFCF9; --panel:#FFFFFF; --ink:#3A2430; --soft:#7E5F6B; --line:#F6E3E7;
  --accent:#E01B76; --accent-2:#E37522; --accent-2-ink:#B45D1B; --accent-3:#F3B23F;
  --accent-soft:#FDEFF4; --on-accent:#FFFFFF;
  /* Two coral tokens, not one. --accent-2 is the invitation's actual coral and
     belongs to the 26px script line, where 3:1 is the bar it has to clear.
     --accent-2-ink is the same hue taken down far enough to be read at 11.5px
     on a button, where the bar is 4.5:1. One value cannot do both jobs without
     either dulling her invitation or shipping a label nobody can read. */
  /* Destructive actions. Kept out of the accent ramp on purpose: the pink is
     the invitation's voice and must never be the colour that means "gone". */
  --danger:#B3261E; --danger-line:#E8B8B4;
  /* The edge of a control, as distinct from --line, which is the rule between
     two blocks of content. A divider may be a whisper; a button's border is the
     only thing saying where the target is, and --line drew it at 1.06:1 against
     the page — invisible, which is how a row of buttons became a row of words. */
  --edge:#D9C2CA;
  --shadow:0 1px 2px rgba(58,36,48,.04), 0 8px 24px -16px rgba(224,27,118,.20);
  --shadow-lift:0 2px 4px rgba(58,36,48,.05), 0 18px 40px -22px rgba(224,27,118,.32);
  /* The four corner washes behind the page. Full colours rather than hues,
     because a gradient stop cannot take a bare channel triple from a token. */
  --wash-1:rgba(240,124,36,.17); --wash-2:rgba(224,27,118,.13);
  --wash-3:rgba(243,178,63,.15); --wash-4:rgba(224,27,118,.12);
  /* Decoration. A look changes more than hue and lettering: the ornament
     between sections, the corner language, the shape of the dot and the weight
     of the rule all belong to it. Kept as tokens so a look is still one block. */
  --ornament:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%3E%3Cpath%20fill%3D'black'%20d%3D'M12%2021C12%2021%203.4%2014.9%203.4%209.4A4.9%204.9%200%200%201%2012%206.5a4.9%204.9%200%200%201%208.6%202.9C20.6%2014.9%2012%2021%2012%2021Z'%2F%3E%3C%2Fsvg%3E"); --radius:6px; --dot-radius:50%;
  --rule-h:1px; --script-caps:none; --script-track:0;
  --display:'Playfair Display',Georgia,serif;
  --script:'Great Vibes',cursive;

  /* ---- the same look after dark ----
     Declared here, applied by the two mapping blocks further down. Naming the
     dark values rather than applying them is what lets a media query switch
     the whole page before the first frame is painted — see the note above them. */

  /* Dark keeps the same hues, lifted for contrast on a warm near-black. */
  --d-bg:#1C1319; --d-panel:#251A21; --d-ink:#F9EFF3; --d-soft:#C4A3AF; --d-line:#3C2B34; --d-edge:#5B4550;
  --d-accent:#FF74AC; --d-accent-2:#FF9E52; --d-accent-2-ink:#FFA968; --d-accent-3:#F5C266;
  --d-accent-soft:#2C1D25; --d-on-accent:#1C1319;
  --d-danger:#FF8A80; --d-danger-line:#5A2E2C;
  --d-shadow:none; --d-shadow-lift:0 18px 40px -24px rgba(0,0,0,.8);
  --d-wash-1:rgba(240,124,36,.14); --d-wash-2:rgba(224,27,118,.12);
  --d-wash-3:rgba(243,178,63,.11); --d-wash-4:rgba(224,27,118,.10);
}


/* ---- light or dark ----------------------------------------------------
 *
 * Copied from the registry, minus its .lookdark specimen selectors, which
 * belong to a look picker this app does not have.
 *
 * Naming the dark values in :root and mapping them here — rather than writing
 * a second palette inline — is what lets the SAME declarations be switched
 * both by a class a script adds and by a media query that needs no script at
 * all. THE TWO BLOCKS MUST DECLARE THE SAME PROPERTIES; CSS has no way to
 * reuse a declaration block across a media boundary, so this is real
 * duplication and it has to be kept in step by hand.
 *
 * Both are @media screen. With neither applying, every token falls back to its
 * light value, so anything printed is the look on paper rather than near-white
 * ink on a white sheet.
 */
@media screen {
body.dark {
  color-scheme:dark;
  --bg:var(--d-bg);
  --panel:var(--d-panel);
  --ink:var(--d-ink);
  --soft:var(--d-soft);
  --line:var(--d-line);
  --edge:var(--d-edge);
  --accent:var(--d-accent);
  --accent-2:var(--d-accent-2);
  --accent-2-ink:var(--d-accent-2-ink);
  --accent-3:var(--d-accent-3);
  --accent-soft:var(--d-accent-soft);
  --on-accent:var(--d-on-accent);
  --danger:var(--d-danger);
  --danger-line:var(--d-danger-line);
  --shadow:var(--d-shadow);
  --shadow-lift:var(--d-shadow-lift);
  --wash-1:var(--d-wash-1);
  --wash-2:var(--d-wash-2);
  --wash-3:var(--d-wash-3);
  --wash-4:var(--d-wash-4);
}
}

@media screen and (prefers-color-scheme: dark) {
  /* Not :not(.dark) as well — a page that is dark by choice and dark by device
     wants the same answer either way, and listing both would only make the two
     blocks harder to keep identical. */
  body:not(.light) {
    color-scheme:dark;
    --bg:var(--d-bg);
    --panel:var(--d-panel);
    --ink:var(--d-ink);
    --soft:var(--d-soft);
    --line:var(--d-line);
    --edge:var(--d-edge);
    --accent:var(--d-accent);
    --accent-2:var(--d-accent-2);
    --accent-2-ink:var(--d-accent-2-ink);
    --accent-3:var(--d-accent-3);
    --accent-soft:var(--d-accent-soft);
    --on-accent:var(--d-on-accent);
    --danger:var(--d-danger);
    --danger-line:var(--d-danger-line);
    --shadow:var(--d-shadow);
    --shadow-lift:var(--d-shadow-lift);
    --wash-1:var(--d-wash-1);
    --wash-2:var(--d-wash-2);
    --wash-3:var(--d-wash-3);
    --wash-4:var(--d-wash-4);
  }
  body:not(.light) .err { color:#E9A08C; }
  body:not(.light)::before { opacity:.45; }
}


/* ---- ground ----------------------------------------------------------- */

*,*::before,*::after { box-sizing:border-box; }

html { scrollbar-gutter:stable; }

body {
  margin:0;
  background:var(--bg);
  color:var(--ink);
  min-height:100vh;
  font-family:'Montserrat',system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing:antialiased;
  transition:background .3s ease, color .3s ease;
}

button { font-family:inherit; cursor:pointer; }
a { color:inherit; }
[hidden] { display:none !important; }

:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:4px; }
::selection { background:var(--accent); color:var(--on-accent); }

/* The four-corner watercolour wash. No image — four radial gradients on a
   fixed pseudo-element behind everything, which is why it costs nothing and
   why it re-tints itself the moment the tokens change. */
body::before {
  content:"";
  position:fixed;
  inset:0;
  z-index:-1;
  pointer-events:none;
  background:
    radial-gradient(58% 42% at 100% 0%,   var(--wash-1), transparent 70%),
    radial-gradient(52% 38% at 0% 0%,     var(--wash-2), transparent 70%),
    radial-gradient(62% 44% at 0% 100%,   var(--wash-3), transparent 70%),
    radial-gradient(58% 40% at 100% 100%, var(--wash-4), transparent 70%);
  transition:background .45s ease;
}
body.dark::before { opacity:.45; }

/* The registry's guest page is 1140px because it holds a grid of gifts. An
   uploader is a single column holding one decision, so it gets 720px — same
   gutters, same tail, same rhythm. The gallery opts back up to the full width
   with .wrap.wide, because a wall of photographs is the one thing here that
   genuinely wants the room. */
.wrap { max-width:720px; margin:0 auto; padding:0 24px 100px; }
.wrap.wide { max-width:1140px; }


/* ---- header ----------------------------------------------------------- */

/* The negative margin is not a nicety. The bar is sticky and it sits inside
   .wrap, so its background stops where the gutter starts — and with a hero
   photograph passing underneath, that left two slivers of the hero image
   scrolling up either side of the header. Bleeding it out by exactly the
   gutter, and putting the same figure back as padding, keeps the name aligned
   with the column below while the background covers the full width. Both
   numbers move with .wrap's padding, here and in the phone rule. */
.top {
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap:10px;
  margin:0 -24px;
  padding:26px 24px 0;
  position:sticky;
  top:0;
  z-index:30;
  background:var(--bg);
}
.mark {
  font-family:'Montserrat',sans-serif;
  font-size:10.5px;
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--soft);
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.topbtns { display:flex; align-items:center; gap:8px; flex:0 0 auto; }

.iconbtn {
  background:none;
  border:1px solid var(--edge);
  color:var(--soft);
  white-space:nowrap;
  border-radius:999px;
  padding:8px 15px;
  font-size:12px;
  letter-spacing:.05em;
  display:inline-flex;
  align-items:center;
  gap:7px;
  text-decoration:none;
  transition:all .18s ease;
}
.iconbtn:hover { color:var(--ink); border-color:var(--ink); }


/* ---- the light/dark switch --------------------------------------------
 *
 * Geometry copied from the registry so that this is the same control, not a
 * second answer to a question already answered. It shows the STATE, not the
 * action: both symbols ride the track, the knob covers the one you are in,
 * and the uncovered one is where pressing it takes you.
 */

.modeswitch {
  --sw-w:60px; --sw-h:32px;
  position:relative;
  flex:0 0 auto;
  display:inline-flex;
  align-items:center;
  width:var(--sw-w);
  height:var(--sw-h);
  padding:0;
  background:var(--panel);
  border:1px solid var(--edge);
  border-radius:999px;
  transition:border-color .18s ease, background-color .18s ease;
}
.modeswitch:hover { border-color:var(--accent); }
.modeknob {
  position:absolute;
  top:2px; bottom:2px; left:2px;
  z-index:1;
  width:calc(var(--sw-h) - 4px);
  display:flex;
  align-items:center;
  justify-content:center;
  border-radius:50%;
  background:var(--accent);
  color:var(--on-accent);
  transition:transform .24s cubic-bezier(.4,.06,.2,1);
}
.modeswitch[aria-checked="true"] .modeknob {
  transform:translateX(calc(var(--sw-w) - var(--sw-h)));
}
.moderail { position:absolute; inset:2px; display:flex; align-items:center; }
.moderailhalf { flex:1; display:flex; align-items:center; justify-content:center; }
.modeicon { width:15px; height:15px; }
.modeghost { width:14px; height:14px; color:var(--soft); opacity:.55; }


/* ---- hero ------------------------------------------------------------- */

.hero { padding:76px 0 46px; }

/* Script line, accent dot, then a gilt rule fading out to nothing. The whole
   look, in three elements. */
.ornament { display:flex; align-items:center; gap:14px; margin-bottom:26px; }
.ornament .txt {
  font-family:var(--script);
  font-size:26px;
  line-height:1;
  letter-spacing:var(--script-track);
  text-transform:var(--script-caps);
  color:var(--accent-2);
  padding-bottom:4px;
}
.ornament .dot {
  width:6px; height:6px;
  border-radius:var(--dot-radius);
  background:var(--accent);
  flex:0 0 auto;
}
.ornament .rule {
  height:var(--rule-h);
  flex:1;
  max-width:120px;
  background:linear-gradient(to right, var(--accent-3), transparent);
}

h1 {
  font-family:var(--display);
  font-weight:500;
  font-size:clamp(44px,7.6vw,84px);
  line-height:1.02;
  letter-spacing:-.015em;
  margin:0 0 26px;
  max-width:14ch;
}
.heroname {
  display:block;
  font-size:clamp(56px,10.2vw,104px);
  line-height:.96;
  letter-spacing:-.03em;
}
.herorest {
  display:block;
  font-family:'Montserrat',sans-serif;
  font-weight:500;
  font-size:clamp(12.5px,1.35vw,15px);
  letter-spacing:.24em;
  text-transform:uppercase;
  color:var(--soft);
  line-height:1.5;
  margin-top:16px;
}
.hero p { max-width:50ch; color:var(--soft); font-size:15.5px; line-height:1.7; margin:0; }

/* The date, in the letterspaced label voice, tucked under the name it belongs
   to rather than floating between the hero and the welcome. */
.hero .count { margin:-10px 0 20px; }

/* The optional photograph, when settings supply one. It sits ABOVE the
   typographic hero rather than replacing it, because the name and the script
   line are the look and a photo cannot carry them. */
.heropic {
  display:block;
  width:100%;
  aspect-ratio:16/10;
  object-fit:cover;
  border:1px solid var(--line);
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  margin:0 0 34px;
  background:var(--accent-soft);
}

/* The look's ornament, drawn as a mask so it takes the look's colour rather
   than carrying one of its own. It must always be a real image: setting
   --ornament to none paints a solid coloured square. */
.ornmark {
  width:15px;
  height:15px;
  margin:0 auto 30px;
  background-color:var(--accent-3);
  -webkit-mask:var(--ornament) center/contain no-repeat;
  mask:var(--ornament) center/contain no-repeat;
}
@supports not ((-webkit-mask: url("")) or (mask: url(""))) {
  .ornmark { display:none; }
}


/* ---- controls --------------------------------------------------------- */

.btn {
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  border:1px solid var(--accent);
  background:var(--accent);
  color:var(--on-accent);
  border-radius:4px;
  padding:10px 14px;
  font-size:13px;
  font-weight:500;
  text-align:center;
  text-decoration:none;
  transition:opacity .18s ease, transform .18s ease, border-color .18s ease;
}
.btn:hover { opacity:.87; }
.btn:active { transform:scale(.985); }
.btn.ghost { background:none; color:var(--ink); border-color:var(--line); }
.btn.ghost:hover { border-color:var(--ink); opacity:1; }
.btn[disabled] { opacity:.4; cursor:not-allowed; }
.btn.block { display:flex; width:100%; padding:15px 18px; font-size:14.5px; }

.linkbtn {
  background:none;
  border:none;
  color:var(--soft);
  font-size:12px;
  text-decoration:underline;
  text-underline-offset:2px;
  padding:4px;
}
.linkbtn:hover { color:var(--ink); }

label.f { display:block; margin-bottom:16px; }
label.f span {
  display:block;
  font-family:'Montserrat',sans-serif;
  font-size:9.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--soft);
  margin-bottom:7px;
}
input, textarea, select {
  width:100%;
  background:var(--bg);
  border:1px solid var(--line);
  color:var(--ink);
  border-radius:4px;
  padding:11px 12px;
  font-size:14px;
  font-family:inherit;
  transition:border-color .18s ease;
}
input:focus, textarea:focus, select:focus { border-color:var(--accent); outline:none; }
input::placeholder { color:var(--soft); opacity:.75; }

.hint {
  display:block;
  font-family:'Montserrat',sans-serif;
  font-size:11.5px;
  color:var(--soft);
  text-transform:none;
  letter-spacing:0;
  line-height:1.6;
  margin:10px 0 0;
}
.err { color:#A8402C; font-size:12.5px; line-height:1.5; margin:7px 0 0; }
body.dark .err { color:#E9A08C; }

/* Reserved rather than hidden. The name hint appears at the moment a guest
   taps "Add photos" without having typed one, and a message that pushes the
   button down as they reach for it is how a second tap lands on nothing. */
.err.reserve { visibility:hidden; }
.err.reserve.show { visibility:visible; }

/* A note, not an alarm: the accent at a whisper. */
.note {
  font-size:12.5px;
  color:var(--soft);
  line-height:1.65;
  background:var(--accent-soft);
  border-radius:5px;
  padding:14px 16px;
  margin:18px 0 0;
}

.empty {
  border:1px dashed var(--line);
  border-radius:6px;
  padding:44px;
  text-align:center;
  color:var(--soft);
  font-size:14px;
}

/* The letterspaced count line. Tabular numerals so a climbing number does not
   shuffle the words on either side of it. */
.count {
  display:block;
  font-size:10.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--soft);
  font-variant-numeric:tabular-nums;
  margin:0;
}
.count .live {
  display:inline-block;
  width:6px; height:6px;
  border-radius:50%;
  background:var(--accent);
  margin-right:.6em;
  vertical-align:1px;
}


/* ---- the upload form -------------------------------------------------- */

.give { margin-top:8px; }

/* The drop target is the primary button, full width, and it is also a drag
   zone. Dragging over it borrows the focus colour rather than inventing a
   third state: the message is "this is live", which is what the accent already
   means everywhere else on the page. */
#drop.over { border-color:var(--accent-3); box-shadow:0 0 0 3px var(--accent-soft); }

.dropnote { color:var(--soft); font-size:11.5px; line-height:1.6; margin:12px 0 0; }

#queue { list-style:none; margin:26px 0 0; padding:0; }

.item {
  background:var(--panel);
  border:1px solid var(--line);
  border-radius:6px;
  padding:13px 15px;
  margin-bottom:10px;
  box-shadow:var(--shadow);
}
.itemtop { display:flex; justify-content:space-between; align-items:baseline; gap:12px; }
.itemname {
  font-size:13px;
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.itemsize {
  color:var(--soft);
  font-size:10.5px;
  letter-spacing:.1em;
  text-transform:uppercase;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
  flex:0 0 auto;
}

/* The registry's meter, at 3px rather than 2px. The hero meter there is a
   fact being reported once; this one is the only thing a guest watches while
   a video climbs, and two pixels of feedback on a phone in daylight is not
   enough of it. */
.track {
  height:3px;
  background:var(--line);
  border-radius:999px;
  position:relative;
  overflow:hidden;
  margin:11px 0 8px;
}
.fill {
  position:absolute;
  inset:0 auto 0 0;
  width:0;
  border-radius:999px;
  background:linear-gradient(to right, var(--accent), var(--accent-2));
  transition:width .3s cubic-bezier(.4,0,.2,1);
}
.itemstatus {
  font-size:10.5px;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--soft);
  font-variant-numeric:tabular-nums;
}

/* Finished and failed both say so in words — the colour is the second telling,
   never the only one. And a failure is --danger, never the accent: the pink is
   the invitation's voice and must not become the colour that means "gone". */
.item.done .fill { background:var(--accent); }
.item.done .itemstatus { color:var(--accent-2-ink); }
.item.failed { border-color:var(--danger-line); }
.item.failed .fill { background:var(--danger); }
.item.failed .itemstatus { color:var(--danger); }
.item.failed { cursor:pointer; }


/* ---- the gallery wall ------------------------------------------------- */

.filters {
  display:flex;
  flex-wrap:wrap;
  gap:8px;
  margin:0 0 26px;
}
.chip {
  border:1px solid var(--line);
  background:none;
  color:var(--soft);
  border-radius:999px;
  padding:7px 15px;
  font-size:13px;
  transition:all .18s ease;
  white-space:nowrap;
  flex:0 0 auto;
}
.chip:hover { border-color:var(--ink); color:var(--ink); }
.chip[aria-pressed="true"] { background:var(--accent); border-color:var(--accent); color:var(--on-accent); }

.grid {
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
  gap:18px;
}
.card {
  background:var(--panel);
  border:1px solid var(--line);
  border-radius:var(--radius);
  overflow:hidden;
  padding:0;
  display:block;
  width:100%;
  text-align:left;
  box-shadow:var(--shadow);
  transition:transform .28s cubic-bezier(.4,0,.2,1), box-shadow .28s ease, border-color .28s ease;
  opacity:0;
  transform:translateY(10px);
  animation:rise .5s cubic-bezier(.4,0,.2,1) forwards;
}
@keyframes rise { to { opacity:1; transform:none; } }
.card:hover { transform:translateY(-3px); box-shadow:var(--shadow-lift); border-color:transparent; }

.thumb {
  aspect-ratio:1;
  background:var(--accent-soft);
  display:flex;
  align-items:center;
  justify-content:center;
  overflow:hidden;
  position:relative;
  color:var(--accent);
}
.thumb img {
  width:100%;
  height:100%;
  object-fit:cover;
  transition:transform .5s cubic-bezier(.4,0,.2,1);
}
.card:hover .thumb img { transform:scale(1.05); }

/* A video whose thumbnail never arrived. Drawn rather than written, and drawn
   out of the look's own tokens so it reads as a missing frame rather than as
   something broken. */
.thumb.noframe {
  background:repeating-linear-gradient(
    90deg, var(--accent-soft) 0 14px, var(--line) 14px 20px
  );
}
.play {
  position:absolute;
  top:50%; left:50%;
  transform:translate(-50%,-50%);
  width:44px; height:44px;
  border-radius:50%;
  background:var(--panel);
  border:1px solid var(--line);
  box-shadow:var(--shadow);
  pointer-events:none;
}
.play::after {
  content:"";
  position:absolute;
  top:50%; left:56%;
  transform:translate(-50%,-50%);
  border-style:solid;
  border-width:7px 0 7px 12px;
  border-color:transparent transparent transparent var(--accent);
}

.cbody {
  padding:12px 14px 14px;
  display:flex;
  flex-direction:column;
  gap:4px;
}
.who { font-family:var(--display); font-weight:400; font-size:16px; line-height:1.25; letter-spacing:-.01em; }
.when {
  font-size:9.5px;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--soft);
  font-variant-numeric:tabular-nums;
}


/* ---- the lightbox ----------------------------------------------------- */

dialog {
  border:0;
  padding:0;
  background:var(--panel);
  color:var(--ink);
  border-radius:8px;
  max-width:min(96vw,60rem);
  box-shadow:0 30px 70px -30px rgba(0,0,0,.5);
}
dialog::backdrop { background:rgba(20,18,20,.5); backdrop-filter:blur(3px); }
dialog img, dialog video { display:block; max-width:100%; max-height:82vh; }

.stage { position:relative; background:var(--ink); line-height:0; }

.lightnav {
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  width:42px; height:42px;
  border:1px solid var(--line);
  border-radius:50%;
  background:var(--panel);
  color:var(--ink);
  font:inherit;
  font-size:1.4rem;
  line-height:1;
  z-index:1;
  box-shadow:var(--shadow);
  transition:border-color .18s ease;
}
.lightnav:hover:not(:disabled) { border-color:var(--accent); }
.lightnav:disabled { opacity:.25; cursor:default; }
.lightnav.prev { left:10px; }
.lightnav.next { right:10px; }

.lightbar {
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap:14px;
  padding:12px 14px;
  font-size:11px;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--soft);
  font-variant-numeric:tabular-nums;
}
.lightbar .acts { display:flex; gap:8px; flex:0 0 auto; }


/* ---- motion ----------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,*::before,*::after { animation:none !important; transition:none !important; }
  .card { opacity:1; transform:none; }
}


/* ---- the phone -------------------------------------------------------- */

@media (max-width: 760px) {
  .wrap { padding:0 18px 60px; }
  .hero { padding:34px 0 24px; }
  .ornament { margin-bottom:16px; gap:10px; }
  .ornament .txt { font-size:21px; }
  h1 { margin-bottom:20px; }
  .herorest { margin-top:12px; }
  .hero p { font-size:14.5px; }
  .heropic { margin-bottom:24px; }
  .ornmark { margin-bottom:24px; }
  .top { margin:0 -18px; padding:18px 18px 0; }
  .modeswitch { --sw-w:54px; --sw-h:29px; }
  .iconbtn { padding:7px 11px; font-size:11px; }
  .grid { grid-template-columns:repeat(2,1fr); gap:12px; }
  .cbody { padding:10px 11px 12px; }
  .who { font-size:14px; }
  .empty { padding:30px 18px; }
  .btn.block { padding:14px 16px; }
  .lightnav { width:38px; height:38px; }
  .lightbar { font-size:10px; letter-spacing:.08em; padding:10px 12px; }
}

@media (max-width: 420px) {
  .grid { gap:10px; }
  .lightbar { flex-direction:column; align-items:flex-start; gap:8px; }
}
