/* ==========================================================================
   KIRO HERO - shared stylesheet
   Used by: the public site, generated event landing pages, and the ticket page.

   The palette is fixed at six approved colours and nothing else. The reference
   material defining them is not part of this repository, so the values and their
   contrast ratios are recorded here to keep the code self-contained:

     Purple        #8E48FF   4.64 on White   4.52 on Black
     Light Purple  #C6A0FF                   9.87 on Black
     Black         #000000
     White         #FFFFFF
     Dark Grey     #646464   6.79 on White   3.55 on Black
     Light Grey    #959595   3.03 on White   6.92 on Black

   Two rules follow directly from those numbers and are load-bearing here:

     Dark Grey on Black is 3.55:1, BELOW WCAG AA. It is a hairline and divider
     colour only - never body text. Muted text on black uses Light Grey (6.92:1).

     Purple leads, with the two greys used sparingly. So greys carry structure,
     not emphasis.

   No gradients. No tints or translucency, so no rendered colour can drift outside
   the approved six. The ghost and wordmark are never rotated, recoloured,
   distorted or faded, and never appear in lockup with an AWS mark.
   ========================================================================== */

/* ---------- Fonts (self-hosted woff2, no external requests) ----------
   woff2 from the official Kiro brand asset set: 422KB total versus 800KB for the
   equivalent OTF + PNG set. That difference is felt by a hero opening the door
   scanner on venue wifi, which is the worst network this site has to work on. */
@font-face {
  font-family: "AWS Diatype Rounded Semi Mono";
  src: url("assets/fonts/AWSDiatypeRoundedSemi-Mono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "AWS Diatype Rounded Semi Mono";
  src: url("assets/fonts/AWSDiatypeRoundedSemi-Mono-BoldItalic.woff2") format("woff2");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "AWS Diatype";
  src: url("assets/fonts/AWSDiatype-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "AWS Diatype";
  src: url("assets/fonts/AWSDiatype-RegularItalic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* ---------- Tokens: the six approved colours ---------- */
:root {
  --purple: #8e48ff;
  --purple-light: #c6a0ff;
  --black: #000000;
  --white: #ffffff;
  --grey-dark: #646464;
  --grey-light: #959595;

  /* Semantic aliases used throughout. */
  --bg: var(--black);
  --fg: var(--white);
  /* Light Grey, 6.92:1 on black - the accessible muted-text choice. */
  --fg-muted: var(--grey-light);
  /* Dark Grey, 3.55:1 on black - hairlines only, never text. */
  --border: var(--grey-dark);

  /* There is no approved elevated-surface colour: the palette is Black and White,
     not a grey ramp. Panels therefore sit flat on the page and are separated by a
     1px border rather than a lighter fill. */
  --surface: var(--black);

  --display: "AWS Diatype Rounded Semi Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  --body: "AWS Diatype", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  --maxw: 1140px;
  --pad: 24px;
  --radius: 4px;

  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 96px;
  --s7: 140px;
}

/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/*
 * `hidden` must actually hide.
 *
 * The browser hides a [hidden] element with `display: none` from its own stylesheet, which any
 * author rule setting `display` beats - so every panel here that is laid out as a grid or a flex
 * row ignored it and showed itself regardless. The two-step "give up my place" confirmation on a
 * ticket was the visible symptom: both answers sat there from the moment the page loaded, next to
 * the button that was supposed to reveal them. The "Other city" field, the door viewfinder and the
 * empty error paragraphs were doing the same thing more quietly.
 *
 * !important is the whole point rather than a shortcut: this has to outrank a rule that has not
 * been written yet, in a codebase where showing and hiding is done with the attribute.
 */
[hidden] {
  display: none !important;
}
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--body);
  font-size: 16px;
  /* Body: Diatype Regular leads out to 130%, tracked at 0%. */
  line-height: 1.3;
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
img {
  max-width: 100%;
  display: block;
}
a {
  color: inherit;
  text-decoration: none;
}
ul,
ol {
  list-style: none;
}
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}
svg {
  display: block;
}

/* Visible focus everywhere. Keyboard users are not an edge case. */
:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--purple);
  color: var(--fg);
  padding: 12px 18px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
}
.skip-link:focus {
  left: 12px;
  top: 12px;
}

.container {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
}
.anchor {
  display: block;
  height: 0;
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- Typography ---------- */
.display {
  font-family: var(--display);
  font-weight: 700;
  /* Display: Semi-Mono Bold leads out to 105%, tracked in to -3%. */
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-transform: lowercase;
}
.display .line {
  display: block;
}
.display .line-accent {
  color: var(--purple);
}

.display-hero {
  font-size: clamp(3rem, 11vw, 7.5rem);
}
.display-2 {
  font-size: clamp(2rem, 5.5vw, 3.6rem);
}
.display-3 {
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  text-transform: none;
  letter-spacing: -0.01em;
}

.lead {
  font-size: clamp(1rem, 1.5vw, 1.125rem);
  color: var(--fg-muted);
  max-width: 58ch;
}
.prose-lead {
  color: var(--fg-muted);
  max-width: 60ch;
}
.prose p + p {
  margin-top: var(--s2);
}

.kicker {
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--purple-light);
}
.marker {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  margin-bottom: var(--s3);
}
.marker-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  color: var(--fg-muted);
  letter-spacing: 0.1em;
}
.marker-on-purple .kicker,
.marker-on-purple .marker-num {
  color: var(--fg);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.badge-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--purple);
}

.meta-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.meta-strip li {
  position: relative;
}
.meta-strip li + li::before {
  content: "";
  position: absolute;
  left: calc(var(--s3) / -2);
  top: 50%;
  width: 1px;
  height: 11px;
  transform: translateY(-50%);
  background: var(--border);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 12px 22px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.04em;
  cursor: pointer;
  background: none;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.btn-lg {
  padding: 16px 28px;
  font-size: 14px;
}
.btn-block {
  width: 100%;
}
.btn-primary {
  background: var(--purple);
  color: var(--fg);
}
.btn-primary:hover {
  background: var(--purple-light);
  color: var(--black);
}
.btn-outline {
  border-color: var(--fg-muted);
  color: var(--fg);
}
.btn-outline:hover {
  border-color: var(--purple);
  color: var(--purple-light);
}
.btn-quiet {
  color: var(--fg-muted);
}
.btn-quiet:hover {
  color: var(--fg);
}
.btn-invert {
  background: var(--white);
  color: var(--black);
}
.btn-invert:hover {
  background: var(--bg);
  color: var(--fg);
}
.btn[disabled] {
  cursor: not-allowed;
  /* Dark Grey stays a hairline. A Dark Grey fill under Light Grey text was 1.98:1,
     and p27 forbids pairings not published as accessible. Light Grey on the page
     ground is 7.01:1. */
  background: none;
  color: var(--fg-muted);
  border-color: var(--border);
}
.btn-icon {
  font-size: 15px;
  line-height: 1;
}

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: 1px solid var(--black);
  transition: border-color 0.2s ease;
}
.nav.scrolled {
  border-bottom-color: var(--fg-muted);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  height: 68px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.06em;
}
/* The ghost renders at its natural proportions, unmodified. */
.brand-mark {
  width: 26px;
  height: auto;
}
.brand-text {
  color: var(--fg);
}
.brand-sub {
  color: var(--purple);
}
.brand-lg {
  font-size: 20px;
}
.brand-lg .brand-mark {
  width: 34px;
}

.nav-panel {
  display: flex;
  align-items: center;
  gap: var(--s5);
}
.nav-links {
  display: flex;
  gap: var(--s3);
}
.nav-links a {
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  display: inline-flex;
  gap: 8px;
  align-items: baseline;
}
.nav-links a:hover {
  color: var(--fg);
}
.nav-idx {
  font-size: 10px;
  color: var(--fg-muted);
}
.nav-actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  width: 42px;
  height: 38px;
  cursor: pointer;
}
.burger {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
}
.burger i {
  display: block;
  width: 16px;
  height: 1.5px;
  background: var(--white);
}

/* ---------- Hero ---------- */
.hero {
  padding: var(--s6) 0 var(--s7);
  border-bottom: 1px solid var(--border);
}
.hero-copy > * + * {
  margin-top: var(--s3);
}
.hero-copy .display-hero {
  margin-top: var(--s4);
}
.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s4);
}
.hero-copy .meta-strip {
  margin-top: var(--s5);
}

/* ---------- Sections ---------- */
.section {
  padding: var(--s6) 0;
  border-bottom: 1px solid var(--border);
}
.section-head {
  margin-bottom: var(--s5);
}
.section-head-split {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s4);
}
.section-note {
  color: var(--fg-muted);
  font-size: 14px;
  max-width: 34ch;
}

/* ---------- 01 About ---------- */
.about-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--s6);
  align-items: start;
}
.about-defs {
  margin-top: var(--s4);
  display: grid;
  gap: var(--s3);
}
.about-def {
  border-top: 1px solid var(--border);
  padding-top: var(--s2);
}
.about-def dt {
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  margin-bottom: 6px;
  color: var(--fg);
}
.about-def dd {
  color: var(--fg-muted);
  font-size: 15px;
}

.stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
}
.stat {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  padding: var(--s3);
}
.stat-num {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  color: var(--purple);
  line-height: 1;
}
.stat-label {
  display: block;
  margin-top: var(--s1);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

/* ---------- 02 Pillars ---------- */
.pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
}
.pillar {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  padding: var(--s4) var(--s3);
  position: relative;
}
.pillar-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  color: var(--fg-muted);
  letter-spacing: 0.1em;
}
.pillar h3 {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.25rem;
  margin: var(--s3) 0 var(--s2);
}
.pillar p {
  color: var(--fg-muted);
  font-size: 15px;
}
.pillar-meta {
  margin-top: var(--s3);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--purple-light) !important;
}

/* ---------- 03 Events ---------- */
.tabs {
  display: flex;
  gap: var(--s1);
  margin-bottom: var(--s4);
}
.tab {
  border: 1px solid var(--border);
  background: none;
  border-radius: var(--radius);
  padding: 9px 18px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  cursor: pointer;
}
.tab.active {
  background: var(--purple);
  border-color: var(--purple);
  color: var(--fg);
}

.events {
  border-top: 1px solid var(--border);
}
.event {
  display: grid;
  grid-template-columns: 84px 1fr auto;
  gap: var(--s4);
  align-items: center;
  border-bottom: 1px solid var(--border);
  padding: var(--s3) 0;
}
/* A finished event is visibly finished, not merely further down the page.
   Muted text plus a muted title, so somebody scrolling cannot mistake one for something they
   can still sign up to. Not opacity: that would drop the title below AA contrast. */
.event.past {
  color: var(--fg-muted);
}
.event.past h3 {
  color: var(--fg-muted);
}
.event-closed {
  color: var(--fg-muted);
}

/* The line between what is coming and what has been. */
.events-divider {
  margin: var(--s4) 0 0;
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.event-date {
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 0;
}
.event-date b {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--purple);
}
.event.past .event-date b {
  color: var(--fg-muted);
}
.event-date small {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--fg-muted);
}
.event-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  align-items: center;
  margin-bottom: 6px;
}
.event-tag {
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--purple);
  color: var(--purple-light);
  border-radius: 999px;
  padding: 3px 10px;
}
.event-place {
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}
.event-main h3 {
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 700;
}
.event-desc {
  color: var(--fg-muted);
  font-size: 14px;
  margin-top: 4px;
  max-width: 62ch;
}
.event-seats {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-top: var(--s1);
}
.empty-state {
  padding: var(--s5) 0;
  text-align: center;
  color: var(--fg-muted);
  border-bottom: 1px solid var(--border);
}

/* ---------- 04 Hub ---------- */
.hub-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s6);
  align-items: center;
}
.feature-list {
  display: grid;
  gap: var(--s2);
  margin: var(--s4) 0;
}
.feature-list li {
  display: flex;
  gap: var(--s2);
  align-items: baseline;
  color: var(--fg-muted);
  font-size: 15px;
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--s2);
}
.feature-list li::before {
  content: "//";
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  color: var(--purple-light);
}

.mock {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.mock-bar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  border-bottom: 1px solid var(--border);
  padding: 12px var(--s3);
}
.mock-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--purple);
}
.mock-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--fg-muted);
  flex: 1;
}
.mock-badge {
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.1em;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 9px;
  color: var(--fg-muted);
}
.mock-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--border);
}
.mock-tile {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  padding: var(--s3);
}
.mock-tile b {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.4rem;
  color: var(--purple);
}
.mock-tile small {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.mock-foot {
  padding: 12px var(--s3);
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--purple-light);
}

/* ---------- 05 Join / apply ---------- */
.join-panel {
  border: 1px solid var(--purple);
  border-radius: var(--radius);
  padding: var(--s5);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s6);
  align-items: start;
  position: relative;
  overflow: hidden;
}
/* No faded ghost watermark anywhere.
   Brand guidelines forbid effects on the ghost, and reducing its opacity is an
   effect - so the mark appears only at full opacity, unmodified, as the brand
   lockup in the nav and footer. Panel emphasis comes from the purple border
   instead. */
.join-copy {
  position: relative;
}

/* ---------- Forms ---------- */
.form-hint {
  font-size: 13px;
  color: var(--fg-muted);
  margin-bottom: var(--s3);
}
.field {
  margin-bottom: var(--s3);
}
.field label {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: var(--s1);
}
.field input,
.field select,
.field textarea {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  color: var(--fg);
}
.field textarea {
  min-height: 120px;
  resize: vertical;
  font-family: var(--body);
}
.field input::placeholder,
.field textarea::placeholder {
  color: var(--fg-muted);
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--purple);
}
.field input.invalid,
.field textarea.invalid,
.field select.invalid {
  border-color: var(--purple-light);
}

/* A field you cannot change should not look like one you can.
   Nothing styled these, so a read-only email sat in a full-brightness box with a live border and
   read as editable - the explanation underneath was doing all the work. Now it is plainly inert:
   muted text, a dashed edge, and no focus colour. Text stays selectable, because the one thing
   people want from an address they cannot edit is to copy it. */
.field input[readonly],
.field textarea[readonly],
.field input:disabled,
.field select:disabled,
.field textarea:disabled {
  /* The VALUE stays at full brightness and the frame is what dims.
     Muted text in an outlined box reads as a placeholder in an empty field - which is worse than
     the original problem, because now the address looks like it has not been filled in. */
  color: var(--fg);
  border-style: dashed;
  border-color: var(--border);
  cursor: default;
}
.field input[readonly]:focus,
.field textarea[readonly]:focus {
  border-color: var(--border);
}
.field-err {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--purple-light);
}
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s2);
}

.field-consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s2);
  align-items: start;
}
.field-consent input {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  accent-color: var(--purple);
}
.field-consent label {
  text-transform: none;
  letter-spacing: 0;
  font-family: var(--body);
  font-size: 14px;
  color: var(--fg);
}
.field-consent label span {
  display: block;
  color: var(--fg-muted);
  font-size: 13px;
}
.field-consent .field-err {
  grid-column: 1 / -1;
}

.consent-notice {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s2);
  font-size: 12px;
  color: var(--fg-muted);
  margin-bottom: var(--s3);
}
.consent-notice strong {
  color: var(--fg);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}


.form-note {
  margin-top: var(--s2);
  font-size: 14px;
  color: var(--purple-light);
}
.form-note.error {
  color: var(--fg);
  border-left: 2px solid var(--purple);
  padding-left: var(--s2);
}

/* ---------- Footer ---------- */
.footer {
  padding: var(--s5) 0 var(--s4);
}
.footer-top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--s5);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--border);
}
.footer-tag {
  margin-top: var(--s2);
  font-family: var(--body);
  font-size: 13px;
  color: var(--fg-muted);
}
.footer-col-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--fg-muted);
  margin-bottom: var(--s2);
}
.footer-nav,
.footer-col {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.footer-nav a,
.footer-col a {
  color: var(--fg-muted);
  font-size: 14px;
}
.footer-nav a:hover,
.footer-col a:hover {
  color: var(--purple-light);
}
.footer-legal {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding-top: var(--s3);
  flex-wrap: wrap;
}
/* The wordmark alone. Never in lockup with an AWS mark. */
.footer-mark {
  width: 84px;
  height: auto;
}
.footer-legal p {
  font-size: 12px;
  color: var(--fg-muted);
}

/* ==========================================================================
   Generated event landing pages
   ========================================================================== */
/* ---------- Cancelled event notice ---------- */
/* Above the fold and unmissable. A cancelled event keeps its page rather than 404ing
   (D-003), which only helps if the cancellation is the first thing read - otherwise
   the page still looks like an invitation. Light Purple on Black is 6.6:1, and the
   left rule matches the notice pattern used elsewhere rather than inventing one. */
.ev-cancelled {
  border-bottom: 1px solid var(--border);
  border-left: 2px solid var(--purple-light);
  padding: var(--s4) 0;
  background: var(--surface);
}
.ev-cancelled-tag {
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--purple-light);
  margin-bottom: var(--s1);
}
.ev-cancelled-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(24px, 4vw, 34px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin-bottom: var(--s2);
}
.ev-cancelled-reason {
  font-size: 16px;
  line-height: 1.5;
  max-width: 60ch;
  margin-bottom: var(--s2);
}
.ev-cancelled-note {
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg-muted);
  max-width: 60ch;
}

.ev-hero {
  padding: var(--s5) 0;
  border-bottom: 1px solid var(--border);
}
.ev-hero-inner {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: var(--s4);
  align-items: start;
}
.ev-datebox {
  border: 1px solid var(--purple);
  border-radius: var(--radius);
  text-align: center;
  padding: var(--s2) 0;
}
.ev-datebox b {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 2.4rem;
  line-height: 1;
  color: var(--purple);
}
.ev-datebox small {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--fg-muted);
}
.ev-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  align-items: center;
  margin-bottom: var(--s2);
}
.ev-headline .display-2 {
  margin-bottom: var(--s3);
  text-transform: none;
}
.ev-headline .meta-strip {
  margin-top: var(--s3);
}

.ev-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: var(--s6);
  align-items: start;
}
.ev-body h2 {
  margin-bottom: var(--s2);
}
.ev-body h2 + * {
  margin-bottom: var(--s4);
}
.ev-agenda {
  display: grid;
  gap: var(--s1);
}
.ev-agenda li {
  display: flex;
  gap: var(--s2);
  align-items: baseline;
  color: var(--fg-muted);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--s1);
}
.ev-agenda li::before {
  content: "//";
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  color: var(--purple-light);
  flex: none;
}
.ev-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s3);
}
.ev-card-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--purple-light);
  margin-bottom: var(--s2);
}
.ev-dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s1) var(--s3);
  font-size: 14px;
}
.ev-dl dt {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.ev-dl dd {
  color: var(--fg);
}
.ev-seats {
  margin-top: var(--s3);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--purple-light);
}

.section-register {
  border-bottom: none;
}
.reg-panel {
  border: 1px solid var(--purple);
  border-radius: var(--radius);
  padding: var(--s5);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s6);
  align-items: start;
  position: relative;
  overflow: hidden;
}
.reg-copy {
  position: relative;
}
.reg-form {
  position: relative;
}

/* ==========================================================================
   Ticket page (/r/)
   ========================================================================== */
.ticket-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--s4) var(--pad);
}
.ticket {
  width: 100%;
  max-width: 420px;
  border: 1px solid var(--purple);
  border-radius: var(--radius);
  overflow: hidden;
}
.ticket-head {
  padding: var(--s3);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
}
.ticket-body {
  padding: var(--s3);
  text-align: center;
}
.ticket-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.4rem;
  margin-bottom: 4px;
}
.ticket-event {
  color: var(--fg-muted);
  font-size: 14px;
  margin-bottom: var(--s3);
}
/* Pure white plate behind the QR: contrast is functional, not decorative. */
.ticket-qr {
  background: var(--white);
  border-radius: var(--radius);
  padding: var(--s2);
  display: inline-block;
  line-height: 0;
}
.ticket-qr svg {
  width: 100%;
  max-width: 260px;
  height: auto;
}
.ticket-meta {
  margin-top: var(--s3);
  display: grid;
  gap: 4px;
  font-size: 13px;
  color: var(--fg-muted);
  text-align: left;
}
.ticket-meta dt {
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.ticket-status {
  margin-top: var(--s3);
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.ticket-status.in {
  border-color: var(--purple);
  color: var(--purple-light);
}
.ticket-note {
  margin-top: var(--s3);
  font-size: 12px;
  color: var(--fg-muted);
}

/* ==========================================================================
   Error page
   ========================================================================== */
.error-wrap {
  min-height: 80vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: var(--s5) var(--pad);
}
.error-code {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(4rem, 16vw, 9rem);
  color: var(--purple);
  line-height: 1;
}
.error-lead {
  margin: var(--s3) auto 0;
}
.cta-center {
  justify-content: center;
}

/* ==========================================================================
   Scroll reveal. Content is visible by default and only hidden once JS
   confirms it can reveal it, so a JS failure can never leave the page blank.
   ========================================================================== */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.js-reveal .reveal.in {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 900px) {
  .about-grid,
  .hub-inner,
  .ev-grid,
  .join-panel,
  .reg-panel {
    grid-template-columns: 1fr;
    gap: var(--s4);
  }
  .pillars {
    grid-template-columns: 1fr;
  }
  .footer-top {
    grid-template-columns: 1fr 1fr;
    gap: var(--s4);
  }
  .section-note {
    text-align: left;
  }
  .join-panel,
  .reg-panel {
    padding: var(--s3);
  }

  .nav-toggle {
    display: grid;
    place-items: center;
  }
  .nav-panel {
    position: absolute;
    top: 68px;
    left: 0;
    right: 0;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    flex-direction: column;
    align-items: stretch;
    gap: var(--s3);
    padding: var(--s3) var(--pad);
    display: none;
  }
  .nav.open .nav-panel {
    display: flex;
    /* Above the page, and a hard bottom edge. Without these the panel sat in the flow
       and the hero copy showed through underneath it, so the menu appeared to cut off
       mid-sentence. */
    z-index: 60;
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.9);
  }
  /* Dims the page behind an open menu, so the panel reads as a layer rather than as
     part of the content it is covering. Pointer events fall through so the click lands
     on the page, where app.js listens for it and closes the menu. */
  .nav.open::after {
    content: '';
    position: fixed;
    inset: 68px 0 0;
    background: rgba(0, 0, 0, 0.72);
    z-index: 50;
    pointer-events: none;
  }
  .nav-links {
    flex-direction: column;
    gap: var(--s2);
  }
  .nav-links a {
    font-size: 15px;
  }
  .nav-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .nav-actions .btn {
    width: 100%;
  }
}

@media (max-width: 640px) {
  :root {
    --pad: 18px;
  }
  .hero {
    padding: var(--s4) 0 var(--s5);
  }
  .section {
    padding: var(--s5) 0;
  }
  .event {
    grid-template-columns: 64px 1fr;
    gap: var(--s2) var(--s3);
  }
  .event-action {
    grid-column: 2;
  }
  .ev-hero-inner {
    grid-template-columns: 1fr;
  }
  .ev-datebox {
    width: 92px;
  }
  .stats,
  .mock-tiles {
    grid-template-columns: 1fr;
  }
  .field-row {
    grid-template-columns: 1fr;
  }
  .footer-top {
    grid-template-columns: 1fr;
  }
  .cta-row .btn {
    width: 100%;
  }
}

/* ==========================================================================
   Motion preferences
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .js-reveal .reveal {
    opacity: 1;
    transform: none;
  }
}

/* ==========================================================================
   Print - a hero may print a door list, an attendee may print a ticket.
   ========================================================================== */
@media print {
  /* On a white ground the roles invert: Light Grey drops to 3.00:1 and Dark Grey
     rises to 5.92:1. Remapping the tokens fixes every muted string at once rather
     than patching individual selectors. */
  :root {
    --fg: var(--black);
    --fg-muted: var(--grey-dark);
    --border: var(--grey-light);
    --bg: var(--white);
  }
  body {
    background: var(--white);
    color: var(--black);
  }
  /* The ghost ships as supplied: a white body with black eyes, which disappears on
     paper. Adding an outline ourselves would be modifying the mark, and that is not
     allowed - so the ghost is omitted from print and the KIRO HERO wordtype carries
     the branding instead. */
  .brand-mark,
  .footer-mark {
    display: none !important;
  }
  .nav,
  .footer,
  .skip-link,
  .btn {
    display: none !important;
  }
  .ticket {
    border-color: var(--black);
  }
  .ticket-name,
  .ticket-event,
  .ticket-meta dd {
    color: var(--black);
  }
}

/* ---------- Post-event feedback ---------- */
/* Sits under the ticket, clearly a separate ask rather than part of the pass. */
.feedback {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
}
.feedback-form {
  display: grid;
  gap: var(--s2);
  margin-top: var(--s2);
}
.feedback-scale {
  display: flex;
  gap: var(--s1);
}
/* Numbers, not stars: a star is ambiguous about whether it is filled, and would need an
   image or an icon font the site does not otherwise load. 44px keeps the tap target
   usable on a phone, which is where this will be opened. */
.feedback-star {
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--fg);
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  cursor: pointer;
}
.feedback-star:hover {
  border-color: var(--purple-light);
}
.feedback-star.on {
  background: var(--purple);
  border-color: var(--purple);
  color: var(--white);
}
.feedback-star:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}
.feedback-comment {
  width: 100%;
  min-height: 84px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--fg);
  font: inherit;
  resize: vertical;
}

/* ---------- Door screen ---------- */
/* No navigation and no chrome: a volunteer needs the list, not the site. Generous tap
   targets because this is used one-handed, standing up, often in poor light. */
.door-page {
  background: var(--bg);
}
.door-wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--s3) var(--pad) var(--s5);
}
.door-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: var(--s3);
}
.door-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(22px, 5vw, 30px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin-bottom: 4px;
}
.door-meta,
.door-count {
  font-size: 14px;
  color: var(--fg-muted);
}
.door-count {
  margin-bottom: var(--s3);
  font-variant-numeric: tabular-nums;
}
.door-status {
  border-left: 2px solid var(--purple);
  background: var(--surface);
  padding: 12px 14px;
  margin-bottom: var(--s3);
  font-size: 14px;
}
.door-status.error {
  border-left-color: var(--purple-light);
}
.door-list {
  border-top: 1px solid var(--border);
}
.door-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  border-bottom: 1px solid var(--border);
  padding: 12px 0;
}
.door-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 16px;
}
.door-row.in .door-name {
  color: var(--fg-muted);
  text-decoration: line-through;
}
.door-tag {
  display: inline-block;
  margin-left: 8px;
  font-family: var(--display);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--purple-light);
}
.btn-sm {
  padding: 10px 16px;
  font-size: 13px;
  flex: none;
}
/* Folded away by default. Most volunteers will use the list; the few with a scanner app
   should not have to scroll past a box they do not need. */
.door-paste {
  margin-bottom: var(--s3);
  font-size: 14px;
  color: var(--fg-muted);
}
.door-paste summary {
  cursor: pointer;
  padding: 8px 0;
}
.door-paste input {
  width: 100%;
  margin-bottom: var(--s1);
}

/* ---------- The three doors on the join form ----------
   Radio cards rather than a dropdown: the choice changes which fields appear, so it has to be
   visible without a click - and on a phone a dropdown puts the most important decision on the
   form behind a native picker.

   EVERY RULE HERE IS SCOPED UNDER .intent-choice, AND THAT MATTERS.
   These options are <label> elements inside .field, so `.field label` (0,1,1) outranks a bare
   `.intent-option` (0,1,0) and won. It forced display:block, which meant the grid never formed
   and the title sat on top of the description, plus 11px uppercase muted monospace inherited from
   the form-label style. The cards rendered as illegible overlapping text. Anything added below
   needs the same scoping, or it will lose the same argument. */
.intent-choice {
  border: 0;
  padding: 0;
  margin: 0 0 var(--s3);
  display: grid;
  gap: var(--s1);
}
.intent-choice legend {
  /* Spans the grid so it sits above the cards rather than being placed as the first one. */
  grid-column: 1 / -1;
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
  padding: 0;
  margin-bottom: var(--s1);
}

.intent-choice .intent-option {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--s2);
  row-gap: 3px;
  align-items: start;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;

  /* Undoing `.field label`. Without these the card is tiny, uppercase, monospace and grey. */
  font-family: var(--body);
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg);
  margin-bottom: 0;
}
.intent-choice .intent-option:hover {
  border-color: var(--purple-light);
}
/* :has keeps the selected state on the card without a class toggle in JS, so what is drawn and
   what is actually checked cannot drift apart. */
.intent-choice .intent-option:has(input:checked) {
  border-color: var(--purple);
}
.intent-choice .intent-option:has(input:focus-visible) {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}

.intent-choice .intent-option input {
  /* Placed explicitly rather than left to auto-placement, which is what put the description on
     top of the title once display:block was overridden. */
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: start;
  margin: 2px 0 0;
  width: 18px;
  height: 18px;
  accent-color: var(--purple);
}
.intent-choice .intent-label {
  grid-column: 2;
  grid-row: 1;
  font-weight: 700;
  line-height: 1.25;
}

.intent-choice .intent-hint {
  grid-column: 2;
  grid-row: 2;
  font-size: 13px;
  line-height: 1.35;
  color: var(--fg-muted);
}

/* ---------- Sharing an event ----------
   Plain links, so no third-party script is ever loaded onto a page that shows
   community-submitted text. Quiet by design: sharing is useful, not the point of the page. */
/* Its own row, set apart from the meta strip above it, so it reads as a set of controls rather
   than as more text. The links were btn-quiet - no border at all - and were being missed
   entirely: from a metre away they looked like a sentence. */
.ev-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1);
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
}
/* A share target that is no longer available. Rendered as a span rather than an anchor, so it is
   genuinely not followable - a disabled-looking <a> with an href is still reachable by keyboard and
   still copyable from the context menu. Matches .btn[disabled] so the two read the same. */
.ev-share [data-disabled] {
  cursor: not-allowed;
  background: none;
  color: var(--fg-muted);
  border-color: var(--border);
}

.ev-share-label {
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-right: 4px;
}

/* ---------- Registration closed ----------
   Replaces the form rather than disabling it. A disabled dark-on-dark input is
   indistinguishable from an empty one, so the old treatment read as "fill this in" and put the
   explanation below the button - people typed their details in before finding out. */
.reg-closed {
  display: grid;
  gap: var(--s2);
  justify-items: start;
  padding: var(--s4);
  border: 1px solid var(--purple);
  border-radius: var(--radius);
}
.reg-closed-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.reg-closed-note {
  color: var(--fg-muted);
  font-size: 14px;
  max-width: 46ch;
}

/* ---------- Giving a place back ----------
   The ticket body is centred, but a .btn is `display: flex` with default justification, so a
   button in a centred block still hugs the left edge and the confirm pair sat off to one side of
   everything above it. Centred explicitly here rather than relying on text-align, which does not
   reach a flex child. */
.release {
  display: grid;
  justify-items: center;
  gap: var(--s2);
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
}
.release .btn {
  justify-content: center;
}
.release-confirm {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s2);
}

/* ---------- The door screen ----------
   Built for one hand, a queue behind, and whatever light the venue has. Everything is full width
   and finger-sized; nothing depends on hovering. */
.door-scan {
  margin-bottom: var(--s2);
}
.door-viewfinder {
  display: grid;
  gap: var(--s1);
  justify-items: center;
  margin-top: var(--s2);
}
.door-viewfinder video {
  width: 100%;
  max-width: 420px;
  border: 1px solid var(--purple);
  border-radius: var(--radius);
  background: var(--black);
}
.door-walkin {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
}
.door-walkin summary {
  cursor: pointer;
  padding: 8px 0;
  color: var(--fg-muted);
}
.door-walkin .field {
  margin-bottom: var(--s2);
}
.door-walkin input[type='text'],
.door-walkin input[type='email'] {
  width: 100%;
}
.door-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--s1);
  margin-bottom: var(--s2);
  font-size: 14px;
}
.door-consent input {
  margin-top: 3px;
  accent-color: var(--purple);
}
