/* Street: the Print poster on black stock, over a block face at night where people walk by.
   The street is chalk: a dim off-white façade, the city at night rather than a sign.
   Red is kept for what is alive: the people walking, Accept, the count, your own name. */
:root {
  --black: #000;         /* stock: true black; the grain is the only thing that lifts it */
  --chalk: #F1ECE2;      /* type and form fields: warm chalk, not screen white */
  --chalk-soft: #C1BBB0; /* notes, helpers */
  --red: #E8192E;        /* the one accent: a near-carmine tuned up from #E0162B to stay vivid on black */

  --face: "Bricolage Grotesque", system-ui, sans-serif;

  /* motion: named for the physical thing */
  --ease-press: cubic-bezier(0.22, 0.9, 0.3, 1);   /* a platen lifting off: fast release, long settle */
  --dur-street: 700ms;   /* the street coming up out of the dark as you arrive */
  --ease-lamp: cubic-bezier(0.45, 0, 0.55, 1); /* a filament warming and cooling: soft at both ends */
  --dur-press: 120ms;    /* a button pressed into the stock */
  --dur-release: 260ms;  /* lifting off, slower than the press */

  --gutter: max(20px, env(safe-area-inset-left));
  --gutter-r: max(20px, env(safe-area-inset-right));
}

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%; text-size-adjust: 100%;
  background: var(--black);
  overflow-x: clip;
}
body {
  position: relative;
  min-height: 100vh;
  margin: 0;
  background: var(--black);
  color: var(--chalk);
  font: 400 17px/1.45 var(--face);
  font-variation-settings: "wdth" 100;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}

/* The one texture layer: fine grain screened over everything, so black reads as stock, not a screen.
   Absolute, not fixed, so it covers the whole sheet in a full-page capture and on iOS. */
body::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  mix-blend-mode: screen; opacity: 0.3;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' seed='5' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 2.4 -1.45'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E") 0 0 / 200px 200px;
}

a { color: inherit; text-decoration-thickness: 1.5px; text-underline-offset: 0.2em; }
:focus-visible { outline: 3px solid var(--chalk); outline-offset: 3px; }
p { margin: 0; }

/* The street: you stand across the road at night, looking at one block face. The façade is still;
   the only motion is people walking along the sidewalk, drawn on a canvas by people.js.
   Both use the same cover fit, anchored bottom centre, so feet land on the sidewalk at any size. */
.street {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  opacity: calc(var(--q, 1) * var(--fade, 1));  /* --fade: set by people.js as the list scrolls up */
  /* The street rises out of the dark: one long eased gradient, no plateau, no step. The top of the first
     screen is nearly pure black, the façade emerges as the eye moves down, and the sidewalk is at full
     strength. The stops follow roughly (y / 90%)^2.2, so the brightness climbs late and smoothly. */
  -webkit-mask-image: var(--rise);
          mask-image: var(--rise);
  --rise: linear-gradient(to bottom,
    rgb(0 0 0 / 0.02) 0%, rgb(0 0 0 / 0.04) 18%, rgb(0 0 0 / 0.09) 32%, rgb(0 0 0 / 0.16) 44%,
    rgb(0 0 0 / 0.26) 54%, rgb(0 0 0 / 0.39) 63%, rgb(0 0 0 / 0.55) 71%, rgb(0 0 0 / 0.73) 78%,
    rgb(0 0 0 / 0.9) 85%, #000 91%);
  animation: street-up var(--dur-street) var(--ease-lamp) both;
}
@keyframes street-up { from { opacity: 0; } }
.street .facade, .street .car, .street canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* without script the pictures still cover the screen; people.js then sets their exact box on the scene fit */
.street .facade, .street .car { object-fit: cover; object-position: 50% 100%; max-width: none; }

/* the form, the confirmation and the full list: the same street at 35%, because text runs everywhere */
.quiet { --q: 0.35; }

/* On the invite, the first screen is the invitation over the street. The list starts below it, in a pool
   of dark that has no edge: it deepens over the first 220px of the list and dissolves at the sides,
   so the street fades away behind the names rather than stopping at a plate. */
.invite .lead { min-height: calc(100svh - 52px); }
.invite .party-wrap { position: relative; }
.invite .party-wrap::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  top: -60px; bottom: -72px; left: calc(-1 * var(--gutter)); right: calc(-1 * var(--gutter-r));
  background: linear-gradient(to bottom, rgb(0 0 0 / 0) 0, rgb(0 0 0 / 0.35) 110px, rgb(0 0 0 / 0.8) 280px, rgb(0 0 0 / 0.8) calc(100% - 40px), rgb(0 0 0 / 0));
}

.page {
  position: relative; z-index: 1;
  max-width: 1180px; margin: 0 auto;
  padding: max(28px, env(safe-area-inset-top)) var(--gutter-r) max(48px, env(safe-area-inset-bottom)) var(--gutter);
}
.narrow { max-width: 660px; }

/* Printed type: title and names get rough edges from an inline SVG filter (#press) in each page */
.title, .party .name a, h1.h { filter: url(#press); }

/* The invitation */
.kicker { font-weight: 650; font-size: 19px; letter-spacing: -0.01em; line-height: 1.3; text-wrap: balance; max-width: 20em; }
.title {
  margin: 10px 0 0;
  font-weight: 800; font-variation-settings: "wdth" 75;
  font-size: clamp(58px, 21vw, 132px);
  line-height: 0.82; letter-spacing: -0.025em;
}
.when { margin-top: 18px; font-size: 18px; font-weight: 500; line-height: 1.35; text-wrap: balance; max-width: 21em; }
.places { margin-top: 12px; font-size: 15px; font-variant-numeric: tabular-nums; color: var(--chalk-soft); }
/* the count is red, so it is set large and bold (3:1 rule for large text; see README) */
.places b { color: var(--red); font-weight: 800; font-size: 24px; line-height: 1; letter-spacing: -0.01em; font-variation-settings: "wdth" 80; }
.details { list-style: none; margin: 24px 0 0; padding: 0; font-size: 18px; }
.details li { padding: 1px 0; }
.nb { white-space: nowrap; }

/* The held line: chalk text over a chalk hairline, so red stays with the people and Accept */
.held-line {
  position: relative; display: inline-block;
  margin-top: 18px; padding-bottom: 8px;
  font-size: 16px; font-weight: 600;
}
.held-line::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--chalk);
}

.answer { margin-top: 16px; }
.regrets { margin-top: 10px; font-size: 16px; }
.regrets a, .party .name a { display: inline-block; padding-block: 10px; margin-block: -10px; }

/* Buttons: a red slab with a chalk trap showing top-left, as if the red sat a hair out of register */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 54px; padding: 0 28px;
  border: 0; border-radius: 0;
  background: var(--red); color: #fff;
  font: 750 19px/1 var(--face); font-variation-settings: "wdth" 85; letter-spacing: -0.005em;
  text-decoration: none; cursor: pointer;
  box-shadow: -2px -2px 0 0 var(--chalk);
  transition: transform var(--dur-release) var(--ease-press), box-shadow var(--dur-release) var(--ease-press);
}
.btn:active { transform: translate(-2px, -2px); box-shadow: 0 0 0 0 var(--chalk); transition-duration: var(--dur-press); }
.btn.wide { width: 100%; }
.btn.plain { background: transparent; color: var(--chalk); box-shadow: inset 0 0 0 2px var(--chalk); }
.linkbtn {
  background: none; border: 0; padding: 0; min-height: 44px;
  font: inherit; color: var(--chalk); text-decoration: underline; text-decoration-thickness: 1.5px;
  text-underline-offset: 0.2em; cursor: pointer;
}

/* Also joining you: the names are the poster */
.section-title { margin: 0 0 16px; font-size: 16px; font-weight: 600; }
.party { list-style: none; margin: 0; padding: 0; }
.party li { padding: 0 0 20px; }
.party .name { font-size: 17px; line-height: 1.3; }
.party .name a {
  font-weight: 800; font-variation-settings: "wdth" 75;
  font-size: clamp(36px, 10.6vw, 60px); line-height: 0.92; letter-spacing: -0.025em;
  text-decoration: none;
}
.party .name a:hover { text-decoration: underline; text-decoration-thickness: 3px; }
.party .role { font-weight: 500; font-variation-settings: "wdth" 100; font-size: 17px; letter-spacing: 0; color: var(--chalk-soft); }
.party .note { margin-top: 7px; font-size: 16px; line-height: 1.42; max-width: 32em; color: var(--chalk-soft); }

/* Your own entry on the confirmation: the name printed in red */
.party li.held .name a { color: var(--red); }

.echo { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding-top: 14px; font-size: 16px; font-weight: 600; }
.echo .btn { min-height: 50px; padding: 0 24px; }
.coda { margin-top: 30px; font-size: 15px; color: var(--chalk-soft); }

.invite .party-wrap { margin-top: 52px; }
.back { display: inline-flex; align-items: center; min-height: 44px; font-size: 16px; }

/* Forms: the fields are chalk boxes cut out of the black, so you write on the stock */
h1.h {
  margin: 6px 0 12px;
  font-weight: 800; font-variation-settings: "wdth" 76;
  font-size: clamp(52px, 15.5vw, 96px); line-height: 0.88; letter-spacing: -0.02em;
  text-wrap: balance;
}
h2.h2 { margin: 0 0 8px; font-weight: 800; font-variation-settings: "wdth" 80; font-size: 28px; line-height: 1.05; text-wrap: balance; }
.intro { max-width: 30em; }
form { margin: 0; }
legend { padding: 0; }
fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.field { margin-top: 24px; }
.field label, .legend { display: block; font-weight: 600; font-size: 16px; line-height: 1.35; }
.optional { font-weight: 400; color: var(--chalk-soft); }
.field input, .field textarea {
  display: block; width: 100%;
  margin-top: 8px; padding: 12px 12px;
  border: 0; border-radius: 0;
  background: var(--chalk); color: #000;
  font: 400 17px/1.4 var(--face);
  min-height: 50px;
}
.field textarea { resize: vertical; min-height: 84px; }
.field input:focus, .field textarea:focus { outline: 3px solid var(--red); outline-offset: 2px; }
.help { margin-top: 7px; font-size: 15px; color: var(--chalk-soft); }
.choices { margin-top: 30px; }
.choice { display: flex; align-items: center; gap: 12px; min-height: 44px; font-size: 17px; cursor: pointer; }
.choice input { width: 22px; height: 22px; margin: 0; accent-color: var(--red); }
.guest { margin-top: 26px; }
.actions { margin-top: 32px; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.split { margin-top: 64px; }
.block { margin-top: 48px; }
.cal { list-style: none; margin: 16px 0 0; padding: 0; }
.cal li a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.block .btn { margin-top: 18px; }

/* Short phones (375x667 and below): "Send your regrets" sits about 71% down, right over the lit shopfronts,
   so the rise starts later, as on wide screens. Walls at full strength still begin at the pavement. */
@media (max-width: 959px) and (max-height: 700px) {
  .street { --rise: linear-gradient(to bottom,
    rgb(0 0 0 / 0.02) 0%, rgb(0 0 0 / 0.04) 25%, rgb(0 0 0 / 0.09) 40%, rgb(0 0 0 / 0.16) 52%,
    rgb(0 0 0 / 0.25) 62%, rgb(0 0 0 / 0.36) 70%, rgb(0 0 0 / 0.47) 77%, rgb(0 0 0 / 0.68) 83%,
    rgb(0 0 0 / 0.88) 88%, #000 93%); }
}

@media (min-width: 960px) {
  .invite .lead { min-height: 0; }
  /* wide screens put the sidewalk lower (about 92% down), so the rise starts later and climbs past the
     bottom of the invitation's last line at under 50% */
  .street { --rise: linear-gradient(to bottom,
    rgb(0 0 0 / 0.02) 0%, rgb(0 0 0 / 0.04) 25%, rgb(0 0 0 / 0.09) 40%, rgb(0 0 0 / 0.16) 52%,
    rgb(0 0 0 / 0.25) 62%, rgb(0 0 0 / 0.36) 70%, rgb(0 0 0 / 0.47) 77%, rgb(0 0 0 / 0.68) 83%,
    rgb(0 0 0 / 0.88) 88%, #000 93%); }
  /* side by side, the pool sits behind the right column only, softened at its left and right edges */
  .invite .party-wrap::before {
    left: -72px; right: -72px; top: -80px;
    background: linear-gradient(to bottom, rgb(0 0 0 / 0.55) 0, rgb(0 0 0 / 0.82) 160px, rgb(0 0 0 / 0.82) calc(100% - 40px), rgb(0 0 0 / 0));
    -webkit-mask-image: linear-gradient(to right, transparent, #000 72px, #000 calc(100% - 72px), transparent);
            mask-image: linear-gradient(to right, transparent, #000 72px, #000 calc(100% - 72px), transparent);
  }
    body { font-size: 18px; }
  .page { padding: 72px 56px 72px; }
  .invite { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); column-gap: 80px; align-items: start; }
  .invite .lead { position: sticky; top: 64px; }
  .invite .party-wrap { margin-top: 8px; }
  .invite .title { font-size: 116px; }
  .answer .btn.wide { width: auto; min-width: 240px; }
  .party .name a { font-size: 58px; }
}

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

/* ---------- The app: rules the mockup did not need ---------- */

/* Honeypot: hidden from people, visible to naive bots. */
.hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
/* Keep the JS-hidden guest fields and share button hidden whatever else applies. */
[hidden] { display: none !important; }

/* Radios: the input sits beside its label (not inside it) so the label keeps a for= */
.choice label { flex: 1; display: flex; align-items: center; min-height: 44px; cursor: pointer; }

/* Errors: red is too dim for small text on black, so the message is chalk on a red rule */
.error, .form-error {
  display: block; margin-top: 8px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--red);
  font-size: 15px; font-weight: 600; line-height: 1.4; color: var(--chalk);
}
.form-error { margin: 0 0 20px; font-size: 16px; }
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { outline: 3px solid var(--red); outline-offset: 2px; }
.choices .error { margin-top: 10px; }

/* Read-only boxes (the share message, the private link): outlined, not filled, so they read as text to copy */
.field input[readonly], .field textarea[readonly] {
  background: transparent; color: var(--chalk-soft);
  box-shadow: inset 0 0 0 1px var(--chalk-soft); font-size: 16px;
}
.field textarea[readonly] { resize: none; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
.block .row .btn { margin-top: 0; }

/* The full line and the you've-replied line on the invite share the held line's hairline */
.held-line.full { max-width: 24em; line-height: 1.4; }
.replied .btn { margin-top: 16px; }
.empty { margin-top: 4px; font-size: 17px; color: var(--chalk-soft); }

/* Quieter actions under a form */
.leave { margin-top: 40px; }
.leave .linkbtn, .actions .linkbtn { color: var(--chalk-soft); }
.notice { margin-top: 18px; max-width: 30em; }

/* ---------- Admin: the same ground and type, a plain table, no street ---------- */
.admin .page { max-width: none; }
.admin h1.h { font-size: clamp(44px, 10vw, 72px); }
.admin h2.h2 { margin-top: 48px; }
.counts { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); margin: 20px 0 0; padding: 1px 0 0 1px; }
/* each cell draws its own hairline, so a short last row leaves no filled gap */
.counts div { margin: -1px 0 0 -1px; padding: 12px 14px; border: 1px solid #2a2926; background: var(--black); }
.counts dt { font-size: 14px; color: var(--chalk-soft); }
.counts dd { margin: 2px 0 0; font-weight: 800; font-size: 28px; font-variant-numeric: tabular-nums; font-variation-settings: "wdth" 80; }
.downloads { display: flex; flex-wrap: wrap; gap: 4px 20px; margin-top: 16px; }
.downloads a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.table-wrap { margin-top: 16px; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.admin table { border-collapse: collapse; width: 100%; font-size: 15px; line-height: 1.35; }
.admin th, .admin td { padding: 10px 12px 10px 0; border-bottom: 1px solid #2a2926; text-align: left; vertical-align: top; }
.admin th { font-weight: 600; color: var(--chalk-soft); white-space: nowrap; }
.admin td form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 2px 0; }
.admin td button, .add-invitees button {
  min-height: 36px; padding: 0 12px; border: 0; border-radius: 0;
  background: transparent; color: var(--chalk); box-shadow: inset 0 0 0 1.5px var(--chalk-soft);
  font: 600 14px/1 var(--face); cursor: pointer;
}
.admin td select { min-height: 36px; max-width: 16em; border: 0; border-radius: 0; background: var(--chalk); color: #000; font: 400 15px var(--face); }
.admin td label { font-size: 14px; color: var(--chalk-soft); }
.admin .marker, .admin .match { display: inline-block; white-space: nowrap; margin-left: 4px; padding: 1px 6px; font-size: 12px; font-weight: 600; color: var(--black); background: var(--chalk-soft); }
.admin td a { overflow-wrap: anywhere; }
.add-invitees { max-width: 520px; margin-top: 28px; }
.add-invitees button { min-height: 50px; padding: 0 24px; margin-top: 20px; }

/* ---------- attending.html / admin.html ---------- */
/* The count line sits between the heading and the list, as the mockup's inline margin did */
.places-list { margin: 0 0 28px; }
.echo .held-line { margin: 0; }
.page-attending .held-line.full { margin-top: 14px; }
/* Admin section headings are plain <h2> (tests slice on "<h2>All replies</h2>") */
.admin h2 { margin: 48px 0 0; font-weight: 800; font-variation-settings: "wdth" 80; font-size: 28px; line-height: 1.05; }
/* Tables keep readable columns and scroll inside .table-wrap on a phone, never the page */
.table-wrap table { min-width: 720px; }
/* All replies has ten columns: give it room to scroll, and the link and note columns a readable width */
.table-wrap table:not(.roster) { min-width: 1180px; }
.table-wrap table:not(.roster) td:nth-child(4) { min-width: 14em; }
.table-wrap table:not(.roster) td:nth-child(5) { min-width: 18em; }
.admin td button { min-height: 44px; }
.admin td select { min-height: 44px; }

/* ---------- me.html / open.html ---------- */
/* .share and .private-link keep their own class names (tests anchor on them), spaced like .block */
.page-me .share, .page-me .private-link { margin-top: 48px; }
.page-me .share .field, .page-me .private-link .field { margin-top: 20px; }
.page-me .invite-url { margin-top: 8px; font-size: 15px; color: var(--chalk-soft); overflow-wrap: anywhere; }
.page-me .field input[readonly] { text-overflow: ellipsis; }
.page-me .guest legend.h2 { margin-bottom: 4px; }
.page-me .decline { margin-top: 6px; }
.page-me .decline .linkbtn { color: var(--chalk-soft); }
.page-me .see-list { margin-top: 32px; }
.page-me .see-list a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.page-me .intro a { display: inline-block; padding-block: 10px; margin-block: -10px; }
.page-open .actions { margin-top: 28px; }
.page-open .notice { padding-left: 12px; border-left: 3px solid var(--red); }
@media (min-width: 960px) { .page-open .btn.wide { width: auto; min-width: 240px; } }
