/* Components. They read semantic tokens only, so every one of them follows
   Live's look or the event style it sits inside. */

@layer components {
  /* ---- Type ------------------------------------------------------- */

  .ui-title {
    font-family: var(--font-display);
    font-weight: var(--title-weight);
    font-size: var(--title-size, var(--text-3xl));
    line-height: var(--title-leading);
    letter-spacing: var(--title-tracking);
    text-transform: var(--title-case);
    color: var(--color-heading);
    text-wrap: balance;
    overflow-wrap: anywhere;
  }

  /* DS8: a headline the display face cannot render is set entirely in the
     safe face, never letter by letter. Applied by the display_font filter. */
  .ui-title--safe,
  .ui-heading--safe {
    font-family: var(--font-display-safe);
    font-weight: var(--title-weight-safe);
    letter-spacing: var(--title-tracking-safe);
  }

  .ui-title em,
  .ui-heading em {
    font-style: var(--title-style-accent);
    color: var(--color-brand);
  }

  .ui-title--lg { --title-size: var(--text-4xl); }
  .ui-title--md { --title-size: var(--text-2xl); }

  .ui-heading {
    font-family: var(--heading-font);
    font-weight: var(--heading-weight);
    font-size: var(--heading-size, var(--text-2xl));
    letter-spacing: var(--heading-tracking);
    line-height: 1.1;
    color: var(--color-heading);
  }

  .ui-heading--sm { --heading-size: var(--text-xl); }

  /* Programme sets headings as small mono labels. */
  [data-style="programme"] .ui-heading {
    --heading-size: var(--text-xs);
    text-transform: uppercase;
    color: var(--color-faint);
  }

  .ui-label {
    font-family: var(--label-font);
    font-size: var(--label-size);
    font-weight: var(--label-weight);
    text-transform: var(--label-case);
    letter-spacing: var(--label-tracking);
    color: var(--label-color);
    line-height: 1.3;
  }

  .ui-lede {
    font-size: var(--text-lg);
    line-height: 1.55;
    color: var(--color-text);
    max-width: 38rem;
  }

  .ui-data {
    font-family: var(--font-data);
    font-weight: var(--data-weight);
    font-variant-numeric: var(--data-figures);
    color: var(--color-heading);
  }

  .ui-muted { color: var(--color-muted); }
  .ui-small { font-size: var(--text-sm); }

  /* ---- Buttons ---------------------------------------------------- */

  .ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-2);
    min-height: var(--control-height);
    padding: 0 var(--p-space-5);
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    background: var(--color-surface);
    color: var(--color-heading);
    font-family: var(--font-body);
    font-size: var(--text-base);
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition:
      background-color var(--duration) var(--ease),
      border-color var(--duration) var(--ease),
      box-shadow var(--duration) var(--ease),
      transform var(--p-duration-fast) var(--ease);
  }

  .ui-btn:hover { text-decoration: none; }
  .ui-btn:active { transform: translateY(1px); }

  .ui-btn--primary {
    background: var(--color-brand);
    color: var(--color-on-brand);
    box-shadow: var(--shadow-action);
  }

  .ui-btn--primary:hover {
    background: var(--color-brand-hover);
    color: var(--color-on-brand);
  }

  .ui-btn--secondary {
    border-color: var(--color-line-strong);
  }

  .ui-btn--secondary:hover {
    border-color: var(--color-heading);
    color: var(--color-heading);
  }

  .ui-btn--quiet {
    background: transparent;
    color: var(--color-brand);
    padding-inline: var(--p-space-2);
  }

  .ui-btn--quiet:hover {
    color: var(--color-brand-hover);
    background: var(--color-brand-soft);
  }

  .ui-btn--lg {
    min-height: var(--control-height-lg);
    padding-inline: var(--p-space-7);
    font-size: var(--text-lg);
  }

  .ui-btn--sm {
    min-height: 2.25rem;
    padding-inline: var(--p-space-4);
    font-size: var(--text-sm);
    width: fit-content;
  }

  .ui-btn--danger {
    background: var(--color-bad);
    color: #ffffff;
  }

  .ui-btn--danger:hover { background: color-mix(in srgb, var(--color-bad) 88%, black); color: #ffffff; }

  .ui-btn--block { width: 100%; }

  .ui-btn:disabled,
  .ui-btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
  }

  /* ---- Fields ----------------------------------------------------- */

  .ui-field {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-2);
  }

  .ui-field__label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--color-heading);
  }

  .ui-field__help {
    font-size: var(--text-sm);
    color: var(--color-muted);
  }

  .ui-field__errors {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-1);
  }

  .ui-field__error {
    display: flex;
    align-items: center;
    gap: var(--p-space-2);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--color-bad);
  }

  .ui-field__required { color: var(--color-bad); margin-left: 2px; }
  .ui-field__optional { color: var(--color-faint); font-weight: 400; margin-left: 2px; white-space: nowrap; }

  .ui-field.has-error .ui-input { border-color: var(--color-bad); }

  select.ui-input {
    appearance: none;
    padding-right: 2.5rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236f6a88' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.875rem center;
  }

  input[type="file"].ui-input { padding: 0.45rem 0.8rem; }

  input[type="file"].ui-input::file-selector-button {
    margin-right: var(--p-space-3);
    padding: 0.35rem 0.8rem;
    border: 0;
    border-radius: var(--radius-inner);
    background: var(--color-brand-soft);
    color: var(--color-brand);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
  }

  /* "You are looking at a preview." Sticky rather than scrolled away with the
     page: a preview is meant to look exactly like the thing it previews, so
     this is the only thing standing between a screenshot of a preview ticket
     and somebody trying it at a door. */
  .ui-preview-bar {
    position: sticky;
    top: 0;
    z-index: 70;
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    flex-wrap: wrap;
    margin-bottom: var(--p-space-4);
    padding: var(--p-space-3) var(--p-space-4);
    border-radius: var(--radius-field);
    background: var(--color-heading);
    color: #fff;
    box-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.5);
  }

  .ui-preview-bar__tag {
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    background: #fff;
    color: var(--color-heading);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }

  .ui-preview-bar__what { font-size: var(--text-sm); flex: 1 1 14rem; }

  /* Something a preview shows but will not let you press. `inert` already
     takes it out of reach and out of the tab order; this is so it looks it. */
  .ui-guest [inert] { opacity: 0.55; }
  .ui-preview-bar__jump { width: auto; max-width: 14rem; }
  .ui-preview-bar .ui-btn { color: #fff; border-color: rgba(255, 255, 255, 0.4); }

  /* Radio and checkbox options as clickable cards. */
  .ui-choices { display: grid; gap: var(--p-space-2); }

  /* A scale: a rating or recommendation score, read as a row of numbers
     rather than a column of cards. Eleven points have to fit a phone, so the
     row wraps instead of scrolling sideways - a number off the edge of the
     screen is a number nobody picks. */
  .ui-choices[data-layout="scale"] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--p-space-1);
  }

  .ui-choices[data-layout="scale"] .ui-choice {
    position: relative;
    flex: 1 1 2.5rem;
    min-width: 2.5rem;
    max-width: min(4.5rem, 100%);
    justify-content: center;
    padding: 0.7rem 0.3rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
  }

  /* The card is the control; a visible dot as well says it twice and costs
     the width a two-digit number needs. */
  .ui-choices[data-layout="scale"] .ui-choice input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .ui-scale-ends {
    display: flex;
    justify-content: space-between;
    gap: var(--p-space-4);
    margin-bottom: var(--p-space-1);
    font-size: var(--text-xs);
    color: var(--color-faint);
  }

  .ui-choices[data-scrollable="true"] {
    max-height: 16rem;
    overflow-y: auto;
    padding: var(--p-space-2);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-field);
    background: var(--color-sunken);
  }

  .ui-choices__group {
    padding: var(--p-space-2) var(--p-space-1) 0;
    font-family: var(--label-font);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-faint);
  }

  .ui-choice {
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--color-line-strong);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    cursor: pointer;
    transition: border-color var(--duration) var(--ease), background-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
  }

  .ui-choice:hover { border-color: var(--color-brand-line); }

  .ui-choice:has(input:checked) {
    border-color: var(--color-brand);
    background: var(--color-brand-soft);
    box-shadow: 0 0 0 3px var(--color-brand-soft);
  }

  .ui-choice input,
  .ui-consent input {
    width: 1.125rem;
    height: 1.125rem;
    margin: 0;
    flex-shrink: 0;
    accent-color: var(--color-brand);
    cursor: pointer;
  }

  .ui-choice__label { color: var(--color-heading); font-weight: 500; }

  /* A searchable dropdown selection: an input with a chevron and a popup
     menu that opens beneath it. The value lives in a hidden field, the
     input only filters and shows the picked label. */
  .ui-combobox {
    position: relative;
    display: flex;
    align-items: stretch;
    min-height: var(--control-height);
    border: 1px solid var(--color-line-strong);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
  }

  .ui-combobox:focus-within {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-brand-soft);
  }

  .ui-combobox__input {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    padding: 0.625rem 0.25rem 0.625rem 0.875rem;
    border: 0;
    background: transparent;
    color: var(--color-heading);
    font-size: var(--text-base);
  }

  .ui-combobox__input:focus { outline: none; box-shadow: none; }
  .ui-combobox__input::placeholder { color: var(--color-faint); }
  .ui-combobox__input::-webkit-search-cancel-button { display: none; }

  .ui-combobox__toggle,
  .ui-combobox__clear {
    flex-shrink: 0;
    width: 2.5rem;
    border: 0;
    background: transparent;
    color: var(--color-muted);
    cursor: pointer;
  }

  .ui-combobox__toggle {
    border-left: 1px solid var(--color-line);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236f6a88' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
  }

  .ui-combobox__clear {
    width: 1.75rem;
    font-size: var(--text-lg);
    line-height: 1;
  }

  .ui-combobox__toggle:hover,
  .ui-combobox__clear:hover { color: var(--color-brand); background-color: var(--color-brand-soft); }

  .ui-combobox__menu {
    position: absolute;
    top: calc(100% + 0.375rem);
    left: 0;
    right: 0;
    z-index: 30;
    display: grid;
    gap: var(--p-space-1);
    max-height: 16rem;
    overflow-y: auto;
    padding: var(--p-space-2);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    box-shadow: var(--shadow-raised);
  }

  .ui-combobox__menu[hidden] { display: none; }

  .ui-combobox__option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    padding: 0.625rem 0.75rem;
    border: 0;
    border-radius: var(--radius-field);
    background: transparent;
    text-align: left;
    font: inherit;
    cursor: pointer;
    transition: background-color var(--duration) var(--ease);
  }

  .ui-combobox__option:hover,
  .ui-combobox__option:focus-visible,
  .ui-combobox__option[aria-selected="true"] { background: var(--color-brand-soft); }

  .ui-combobox__option-label { color: var(--color-heading); font-weight: 500; }
  .ui-combobox__option-meta { color: var(--color-muted); font-size: var(--text-sm); }

  .ui-combobox__empty {
    padding: 0.625rem 0.75rem;
    font-size: var(--text-sm);
    color: var(--color-muted);
  }

  /* A single agreement, e.g. terms or consent to be contacted. */
  .ui-consent {
    display: flex;
    align-items: flex-start;
    gap: var(--p-space-3);
    padding: 0.85rem 1rem;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-field);
    background: var(--color-sunken);
  }

  .ui-consent input { margin-top: 0.2rem; }
  .ui-consent label { color: var(--color-heading); cursor: pointer; }

  /* An organiser's section heading or note inside a form. */
  .ui-form-section {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-1);
    padding-top: var(--p-space-5);
    border-top: var(--divider);
  }

  form > .ui-form-section:first-child { padding-top: 0; border-top: 0; }

  .ui-form-note {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-1);
    padding: 0.85rem 1rem;
    border: 1px solid var(--color-brand-line);
    border-radius: var(--radius-inner);
    background: var(--color-brand-soft);
    color: var(--color-text);
    font-size: var(--text-sm);
  }

  .ui-form-note strong { color: var(--color-heading); }

  .ui-input {
    width: 100%;
    min-height: var(--control-height);
    padding: 0.625rem 0.875rem;
    border: 1px solid var(--color-line-strong);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    color: var(--color-heading);
    font-size: var(--text-base);
    transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
  }

  .ui-input::placeholder { color: var(--color-faint); }

  .ui-input:focus {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-brand-soft);
  }

  .ui-input[aria-invalid="true"] {
    border-color: var(--color-bad);
  }

  textarea.ui-input {
    min-height: 7rem;
    resize: vertical;
    line-height: 1.5;
  }

  .ui-check {
    display: flex;
    align-items: flex-start;
    gap: var(--p-space-3);
    font-size: var(--text-base);
    color: var(--color-text);
    cursor: pointer;
  }

  .ui-check input {
    width: 1.25rem;
    height: 1.25rem;
    margin: 0.15rem 0 0;
    accent-color: var(--color-brand);
  }

  /* ---- Cards and rows -------------------------------------------- */

  .ui-card {
    background: var(--color-surface);
    border: var(--border-card);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
  }

  .ui-card--padded { padding: var(--p-space-5); }

  @media (min-width: 48rem) {
    .ui-card--padded { padding: var(--p-space-6); }
  }

  .ui-rows > * + * {
    border-top: var(--divider);
  }

  .ui-row {
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    padding: var(--p-space-4) 0;
  }

  .ui-card .ui-row { padding-inline: var(--p-space-5); }

  .ui-row__body {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
    flex-grow: 1;
  }

  .ui-row__title {
    font-weight: 600;
    color: var(--color-heading);
  }

  .ui-row__meta {
    font-size: var(--text-sm);
    color: var(--color-muted);
  }

  .ui-row__icon {
    color: var(--color-brand);
    align-self: flex-start;
    flex-shrink: 0;
    padding-top: 0.125rem;
  }

  /* ---- Chips ------------------------------------------------------ */

  .ui-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-1);
    padding: 0.25rem 0.625rem;
    border-radius: var(--radius-chip);
    background: var(--color-sunken);
    color: var(--color-text);
    font-family: var(--label-font);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: var(--label-case);
    letter-spacing: var(--label-tracking);
    line-height: 1.4;
    white-space: nowrap;
    /* Keeps its own width inside a column instead of stretching. */
    width: fit-content;
  }

  .ui-chip--brand { background: var(--color-brand-soft); color: var(--color-brand); }
  .ui-chip--ok { background: var(--color-ok-soft); color: var(--color-ok); }
  .ui-chip--warn { background: var(--color-warn-soft); color: var(--color-warn); }
  .ui-chip--bad { background: var(--color-bad-soft); color: var(--color-bad); }

  /* ---- Stats ------------------------------------------------------ */

  .ui-stat {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-1);
  }

  .ui-stat__value {
    font-family: var(--font-display);
    font-weight: var(--title-weight);
    font-size: var(--text-3xl);
    line-height: 1;
    letter-spacing: var(--title-tracking);
    font-variant-numeric: var(--data-figures);
    color: var(--color-heading);
  }

  [data-style="programme"] .ui-stat__value {
    font-family: var(--font-data);
    font-weight: 500;
  }

  /* ---- Tabs ------------------------------------------------------- */

  .ui-tabs {
    display: flex;
    gap: var(--p-space-6);
    border-bottom: 1px solid var(--color-line);
    overflow-x: auto;
    scrollbar-width: none;
  }

  .ui-tab {
    padding: 0 0 var(--p-space-3);
    margin-bottom: -1px;
    border-bottom: 2px solid transparent;
    color: var(--color-muted);
    font-weight: 500;
    white-space: nowrap;
  }

  .ui-tab:hover { color: var(--color-heading); text-decoration: none; }

  .ui-tab[aria-current="page"] {
    color: var(--color-heading);
    font-weight: 600;
    border-bottom-color: var(--color-brand);
  }

  /* ---- Event cover (generated from the title and style) ----------- */

  .ui-cover {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--p-space-4);
    min-height: var(--cover-height, 13rem);
    padding: var(--p-space-5) var(--p-space-6);
    border-radius: var(--cover-radius);
    background-color: var(--cover-bg);
    background-image: var(--cover-pattern);
    background-size: var(--cover-pattern-size);
    color: var(--cover-ink);
    overflow: hidden;
    isolation: isolate;
  }

  .ui-cover__eyebrow {
    font-family: var(--label-font);
    font-size: var(--text-sm);
    font-weight: 500;
    text-transform: var(--label-case);
    letter-spacing: var(--label-tracking);
    color: var(--cover-eyebrow);
  }

  .ui-cover__title {
    font-family: var(--cover-title-font);
    font-weight: var(--cover-title-weight);
    font-style: var(--cover-title-style);
    text-transform: var(--cover-title-case);
    letter-spacing: var(--cover-title-tracking);
    font-size: var(--cover-title-size, clamp(2.75rem, 2rem + 3vw, 4rem));
    line-height: 0.92;
    color: var(--cover-ink);
    text-wrap: balance;
    overflow-wrap: anywhere;
  }

  .ui-cover__title.ui-title--safe {
    font-family: var(--font-display-safe);
    font-weight: var(--cover-title-weight-safe);
  }

  /* Invitation's quiet ornament: two hairline rings. */
  .ui-cover::before,
  .ui-cover::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    border: 1px solid rgba(250, 250, 253, 0.24);
    z-index: -1;
  }

  .ui-cover::before { width: 14rem; height: 14rem; right: -4rem; top: -3.5rem; }
  .ui-cover::after { width: 9rem; height: 9rem; right: -1.5rem; top: -1rem; opacity: 0.7; }

  [data-style="poster"] .ui-cover::before,
  [data-style="poster"] .ui-cover::after,
  [data-style="programme"] .ui-cover::before,
  [data-style="programme"] .ui-cover::after {
    content: none;
  }

  /* Poster's date sticker. */
  .ui-cover__sticker { display: none; }

  [data-style="poster"] .ui-cover__sticker {
    position: absolute;
    top: var(--p-space-7);
    right: var(--p-space-5);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 5.5rem;
    height: 5.5rem;
    border-radius: 50%;
    background: var(--color-accent);
    color: #ffffff;
    font-family: var(--font-display);
    font-weight: 800;
    line-height: 0.95;
    transform: rotate(-8deg);
    animation: ui-stamp 450ms 350ms var(--p-ease-spring) both;
  }

  .ui-cover__sticker b { font-size: 1.75rem; letter-spacing: -0.03em; }
  .ui-cover__sticker span { font-size: 0.75rem; letter-spacing: 0.06em; }

  /* ---- Tickets ---------------------------------------------------- */

  .ui-ticket-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--p-space-4);
    padding: var(--p-space-4) var(--p-space-5);
  }

  .ui-ticket-row__price {
    font-family: var(--font-data);
    font-weight: var(--data-weight);
    font-size: var(--text-lg);
    font-variant-numeric: var(--data-figures);
    color: var(--color-heading);
    white-space: nowrap;
  }

  [data-style="poster"] .ui-ticket-row__price { font-family: var(--font-display); font-size: var(--text-xl); }

  .ui-ticket-row.is-unavailable { opacity: 0.55; }

  /* The tear line between an event's details and its tickets. The notches
     are cut in the page colour; the line itself is the style's. */
  .ui-perforation {
    position: relative;
    height: 1.75rem;
  }

  .ui-perforation::before {
    content: "";
    position: absolute;
    inset: 50% var(--p-space-5) auto;
    border-top: var(--perforation);
  }

  [data-style="programme"] .ui-perforation {
    background:
      radial-gradient(circle at 0 50%, var(--color-page) 0.875rem, transparent 0.9rem),
      radial-gradient(circle at 100% 50%, var(--color-page) 0.875rem, transparent 0.9rem);
  }

  /* ---- Avatar ----------------------------------------------------- */

  .ui-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--avatar-size, 2.25rem);
    height: var(--avatar-size, 2.25rem);
    border-radius: var(--radius-control);
    background: var(--color-inverse);
    color: var(--color-on-inverse);
    font-size: var(--text-xs);
    font-weight: 700;
    flex-shrink: 0;
  }

  /* ---- Empty state ------------------------------------------------ */

  .ui-empty {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--p-space-3);
    padding: var(--p-space-7);
    border: 1px dashed var(--color-line-strong);
    border-radius: var(--radius-card);
  }

  /* ---- Notices ---------------------------------------------------- */

  .ui-notice {
    display: flex;
    align-items: flex-start;
    gap: var(--p-space-3);
    padding: var(--p-space-3) var(--p-space-4);
    border-radius: var(--radius-inner);
    background: var(--color-brand-soft);
    color: var(--color-heading);
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .ui-notice--ok { background: var(--color-ok-soft); color: var(--color-ok); }
  .ui-notice--warn { background: var(--color-warn-soft); color: var(--color-warn); }
  .ui-notice--bad { background: var(--color-bad-soft); color: var(--color-bad); }
  .ui-notice svg { margin-top: 0.1rem; }

  /* ---- Rich text an organiser wrote ------------------------------- */

  .ui-prose {
    color: var(--color-text);
    font-size: var(--text-base);
    line-height: 1.7;
    overflow-wrap: anywhere;
  }

  /* :where keeps this reset weaker than the spacing rule below. */
  .ui-prose :where(p, ul, ol, blockquote) { margin: 0; }
  .ui-prose > * + * { margin-top: var(--p-space-4); }
  .ui-prose h2, .ui-prose h3 { font-family: var(--font-body); font-weight: 700; font-size: var(--text-lg); color: var(--color-heading); }
  .ui-prose ul, .ui-prose ol { padding-left: 1.25rem; }
  .ui-prose li + li { margin-top: var(--p-space-1); }
  .ui-prose a { text-decoration: underline; }
  .ui-prose img, .ui-prose video { border-radius: var(--radius-inner); height: auto; }
  .ui-prose :where(img, video) { display: block; max-width: 100%; }
  .ui-prose audio { display: block; max-width: 100%; }
  .ui-prose figcaption { margin-top: var(--p-space-2); font-size: var(--text-sm); color: var(--color-muted); }
  .ui-prose blockquote { padding-left: var(--p-space-4); border-left: 3px solid var(--color-brand-line); color: var(--color-muted); }

  /* ---- An uploaded event cover ------------------------------------ */

  .ui-cover-image {
    margin: 0;
    overflow: hidden;
    border-radius: var(--cover-radius);
    background: var(--color-sunken);
  }

  .ui-cover-image img {
    width: 100%;
    height: auto;
    max-height: 32rem;
    object-fit: contain;
  }

  /* ---- Map ---------------------------------------------------------- */

  .ui-map {
    overflow: hidden;
    border-radius: var(--radius-inner);
    aspect-ratio: 16 / 10;
    background: var(--color-sunken);
  }

  .ui-map iframe { width: 100%; height: 100%; border: 0; }

  .ui-map-link {
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    padding: var(--p-space-3) var(--p-space-4);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-inner);
    background: var(--color-surface);
    color: var(--color-heading);
  }

  .ui-map-link:hover { border-color: var(--color-brand-line); text-decoration: none; color: var(--color-heading); }
  .ui-map-link > svg:first-child, .ui-map-link > .ev-maps-icon { width: 2.5rem; height: 2.5rem; flex-shrink: 0; }
  .ui-map-link > .ui-icon:last-child { color: var(--color-brand); flex-shrink: 0; }

  /* ---- Event page ---------------------------------------------------
     One column on a phone. From 60rem, two: the flyer and ticket panel on
     the left, held in view, and everything there is to read on the right,
     scrolling on its own so the ticket button never leaves the screen. */

  .ui-event__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-2);
    width: fit-content;
    color: var(--color-brand);
  }

  .ui-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--p-space-2) var(--p-space-5);
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--color-text);
  }

  .ui-facts li {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-2);
  }

  .ui-facts svg { color: var(--color-brand); flex-shrink: 0; }

  .ui-event__layout {
    display: grid;
    gap: var(--p-space-5);
    align-items: start;
  }

  .ui-event__details { min-width: 0; }

  @media (min-width: 60rem) {
    .ui-event__layout { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--p-space-6); }

    .ui-event__aside {
      position: sticky;
      top: var(--p-space-5);
    }

    .ui-event__details {
      max-height: calc(100dvh - 2 * var(--p-space-5));
      overflow-y: auto;
      /* No overscroll containment: reaching the end of the details carries on
         scrolling the page, and a column short enough to fit never traps. */
      scrollbar-width: none;
    }

    .ui-event__details::-webkit-scrollbar { width: 0; height: 0; }
  }

  @media (min-width: 75rem) {
    .ui-event__layout { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); }
  }

  .ui-card__section { padding: var(--p-space-5); }

  @media (min-width: 48rem) {
    .ui-card__section { padding: var(--p-space-6); }
  }

  .ui-card__section > .ui-label:first-child { display: block; margin-bottom: var(--p-space-3); }

  /* The ticket panel: tickets, the key facts, then the way in. */
  .ui-panel > * + * { border-top: var(--divider); }
  .ui-panel .ui-row { padding-block: var(--p-space-3); }
  .ui-panel .ui-rows .ui-row:first-child { padding-top: var(--p-space-4); }
  .ui-panel .ui-rows .ui-row:last-child { padding-bottom: var(--p-space-4); }
  .ui-panel .ui-row__body .ui-label { font-size: var(--text-xs); }

  .ui-row__value { color: var(--color-heading); }

  .ui-price-headline {
    display: flex;
    align-items: baseline;
    gap: var(--p-space-2);
    margin: 0;
  }

  .ui-price-headline .ui-data {
    font-family: var(--font-display);
    font-weight: var(--title-weight);
    font-size: var(--text-3xl);
    line-height: 1;
  }

  [data-style="programme"] .ui-price-headline .ui-data { font-family: var(--font-data); font-weight: 500; }

  /* Places taken against capacity. */
  .ui-meter { display: flex; flex-direction: column; gap: var(--p-space-2); }

  .ui-meter__track {
    height: 0.375rem;
    border-radius: 999px;
    background: var(--color-sunken);
    overflow: hidden;
  }

  .ui-meter__track > span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--color-brand);
  }

  /* A bar that is updated in place (progress polled every couple of seconds)
     slides to its new width instead of stepping. */
  .ui-meter__track > span.is-live { transition: width 600ms ease-out; }

  @media (prefers-reduced-motion: reduce) {
    .ui-meter__track > span.is-live { transition: none; }
  }

  /* ---- Registration -------------------------------------------------
     The form on the left, the event summary on the right from 60rem. */

  .ui-register {
    display: grid;
    gap: var(--p-space-5);
    align-items: start;
  }

  @media (min-width: 60rem) {
    .ui-register { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--p-space-6); }
    .ui-register__summary { position: sticky; top: var(--p-space-5); }
  }

  @media (min-width: 75rem) {
    .ui-register { grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); }
  }

  .ui-register > .ui-stack > .ui-btn { width: fit-content; }

  .ui-register__assurance {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-2);
  }

  /* One ticket type with its quantity box. */
  .ui-pick-row {
    display: flex;
    align-items: center;
    /* A narrow phone drops the picker under the ticket rather than squeezing it. */
    flex-wrap: wrap;
    gap: var(--p-space-4);
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-field);
    background: var(--color-surface);
  }

  .ui-pick-row.is-unavailable { opacity: 0.55; }

  .ui-pick-row__price {
    margin-left: var(--p-space-2);
    font-family: var(--font-data);
    font-weight: var(--data-weight);
    font-variant-numeric: var(--data-figures);
    color: var(--color-brand);
    white-space: nowrap;
  }

  .ui-qty { width: 5.5rem; flex: 0 0 auto; text-align: center; }

  .ui-code {
    width: fit-content;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-chip);
    background: var(--color-surface);
    border: 1px solid var(--color-brand-line);
    font-family: var(--p-face-plex-mono);
    font-size: 0.9em;
    color: var(--color-heading);
  }

  /* ---- Ticket card ----------------------------------------------------
     A physical admission ticket translated into UI (docs/PLAN-ticket-card.md):
     a white sheet inside a thin violet outline, with a notch cut into each
     side. Live's own look rather than the event's - this reads the primitive
     --p-* faces and fixed radii instead of --font-display and --radius-card,
     so an invitation, a poster and a programme all hand out the same ticket.

     The outline is drawn by ui/static/ui/js/ticket.js as one SVG path, and
     the light that traces it once on load is a dash travelling that same
     path. The CSS border below is the fallback: without JavaScript the card
     is still bordered, it simply has straight sides. */

  .ui-ticket {
    --ticket-pad: clamp(1.25rem, 6%, 1.9rem);
    --notch: 15px;
    --notch-y: 47%;
    position: relative;
    /* A ticket is portrait. Without this it stretches to whatever column it
       is in and reads as a wide panel on a desktop. */
    max-width: 25rem;
    margin-inline: auto;
    border: 2px solid var(--p-violet);
    border-radius: 22px;
    background: #fff;
    box-shadow: 0 18px 40px -26px rgb(29 25 51 / 0.45);
    color: var(--p-ink);
    font-family: var(--p-face-franklin);
    text-align: left;
  }

  /* Once the path is drawn it is the border. Until then - and for a browser
     that never runs the script - the CSS border and a ringed disc stand in. */
  .ui-ticket.is-outlined { border-color: transparent; }

  .ui-ticket:not(.is-outlined)::before,
  .ui-ticket:not(.is-outlined)::after { border: 2px solid var(--p-violet); }

  .ui-ticket__sheet {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-4);
    padding: var(--ticket-pad);
  }

  /* The notches punch the card's white out so the page shows through; the
     drawn path supplies the arc. */
  .ui-ticket::before,
  .ui-ticket::after {
    content: "";
    position: absolute;
    top: var(--notch-y);
    width: calc(var(--notch) * 2);
    height: calc(var(--notch) * 2);
    border-radius: 50%;
    background: var(--color-page);
    transform: translateY(-50%);
    z-index: 1;
  }

  .ui-ticket::before { left: calc(var(--notch) * -1 - 2px); clip-path: inset(0 0 0 calc(50% - 3px)); }
  .ui-ticket::after { right: calc(var(--notch) * -1 - 2px); clip-path: inset(0 calc(50% - 3px) 0 0); }

  .ui-ticket__outline {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
    z-index: 2;
  }

  .ui-ticket__edge {
    fill: none;
    stroke: var(--p-violet);
    stroke-width: 2;
  }

  /* A light traced this outline once on load - the motion was right, the
     look never was. It is in the commit before the one that removed it, so
     bringing it back is a revert rather than a rewrite. */

  /* A button that is fetching something says so. The spinner replaces the
     icon's place rather than being added beside it, so the button does not
     change width and shuffle what is next to it. */
  .ui-btn.is-busy {
    cursor: progress;
    pointer-events: none;
  }

  .ui-btn.is-busy > .ui-icon { display: none; }

  .ui-btn.is-busy::before {
    content: "";
    width: 1em;
    height: 1em;
    flex: none;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    opacity: 0.85;
    animation: ui-spin 0.7s linear infinite;
  }

  @keyframes ui-spin { to { transform: rotate(360deg); } }

  @media (prefers-reduced-motion: reduce) {
    .ui-btn.is-busy::before { animation-duration: 2s; }
  }

  /* Header: spacious and white, never a coloured bar. */
  .ui-ticket__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--p-space-3);
  }

  .ui-ticket__logo { max-height: 2.25rem; max-width: 11rem; object-fit: contain; }

  .ui-ticket__organiser {
    font-weight: 700;
    font-size: var(--text-lg);
    letter-spacing: -0.01em;
  }

  .ui-ticket__pill {
    padding: 0.3rem 0.85rem;
    border-radius: 999px;
    background: var(--p-violet-soft, #F1ECFF);
    color: var(--p-violet);
    font-size: var(--text-sm);
    font-weight: 700;
  }

  .ui-ticket__title {
    margin: 0;
    font-family: var(--p-face-bricolage);
    font-weight: 800;
    font-size: clamp(1.6rem, 6vw, 2.1rem);
    line-height: 1.05;
    letter-spacing: -0.03em;
  }

  .ui-ticket__lines { display: flex; flex-direction: column; gap: var(--p-space-2); }

  .ui-ticket__line {
    display: flex;
    align-items: flex-start;
    gap: var(--p-space-3);
    margin: 0;
    font-size: var(--text-base);
  }

  .ui-ticket__line .ui-icon { flex: none; margin-top: 0.15em; color: var(--p-violet); }
  .ui-ticket__address { display: block; margin-top: 0.15rem; color: var(--color-muted); font-size: var(--text-sm); }

  /* The code. Generous padding, plain field, nothing decorative inside. */
  .ui-ticket__code {
    align-self: center;
    width: min(16rem, 74%);
    padding: var(--p-space-4);
    border-radius: 18px;
    background: var(--p-violet-soft, #F4F1FE);
  }

  .ui-ticket__code img { display: block; width: 100%; height: auto; aspect-ratio: 1; }

  /* Two columns, a hairline between them. */
  .ui-ticket__meta {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--p-space-3);
    margin: 0;
    padding-top: var(--p-space-4);
    border-top: 1px solid var(--color-brand-line);
  }

  .ui-ticket__meta:has(div + div) { grid-template-columns: 1fr 1fr; }
  .ui-ticket__meta > div + div { padding-left: var(--p-space-4); border-left: 1px solid var(--color-brand-line); }

  .ui-ticket__meta dt {
    color: var(--color-muted);
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .ui-ticket__meta dd {
    margin: 0.3rem 0 0;
    font-size: var(--text-lg);
    font-weight: 700;
  }

  .ui-ticket__ref {
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-lg);
    letter-spacing: 0.02em;
    user-select: all;
  }

  .ui-ticket__note {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-2);
    margin: 0;
    color: var(--color-muted);
    font-size: var(--text-sm);
    text-align: center;
  }

  .ui-ticket__note .ui-icon { flex: none; }

  /* A refused ticket draws no code, so its line stands where one would be.
     A used ticket keeps its code for re-entry, so its line is a short marker
     above the panel instead - hence the padding split below. */
  .ui-ticket__spent,
  .ui-ticket__voided {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-2);
    margin: 0;
    padding: var(--p-space-6) 0;
    font-weight: 700;
    font-size: var(--text-lg);
  }

  .ui-ticket__spent { padding: 0; color: var(--p-violet); }
  .ui-ticket__voided { color: var(--color-bad, #B3261E); }

  .ui-ticket__foot {
    display: flex;
    justify-content: center;
    padding-top: var(--p-space-4);
    border-top: 1px solid var(--color-brand-line);
  }

  .ui-ticket__foot img { height: 1.4rem; width: auto; opacity: 0.9; }

  /* Home, where the row above already names the event. */
  .ui-ticket--compact { --ticket-pad: var(--p-space-4); }

  /* No notch at this size. The card is short enough that the cut lands
     beside the code and reads as damage rather than as a ticket, and
     Home is a list: the silhouette is doing no work here. */
  .ui-ticket--compact::before,
  .ui-ticket--compact::after { display: none; }
  .ui-ticket--compact .ui-ticket__code { width: min(13rem, 80%); }
  .ui-ticket--compact .ui-ticket__foot { display: none; }

  @media print {
    .ui-ticket { background: #000; break-inside: avoid; }
    .ui-ticket__sheet { background: #fff; }

    /* Printers drop backgrounds by default, and the code's field going with
       them is fine - white behind it is what a scanner wants anyway. */
    .ui-ticket__code { background: #fff !important; border: 1px solid #ddd; }
    .ui-ticket__pill { border: 1px solid #000; }
  }

  /* ---- Confirmation ---------------------------------------------------
     Centred like a receipt; the organiser's own rows keep their alignment. */

  .ui-confirm { text-align: center; }

  .ui-confirm__head,
  .ui-confirm__block {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--p-space-2);
  }

  .ui-confirm__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 4.25rem;
    height: 4.25rem;
    margin-bottom: var(--p-space-2);
    border-radius: 50%;
    background: var(--color-ok-soft);
    color: var(--color-ok);
    box-shadow: 0 0 0 10px color-mix(in srgb, var(--color-ok) 6%, transparent);
    animation: ui-stamp-in 450ms 150ms var(--p-ease-spring) both;
  }

  .ui-confirm__icon--brand { background: var(--color-brand-soft); color: var(--color-brand); box-shadow: 0 0 0 10px color-mix(in srgb, var(--color-brand) 6%, transparent); }
  .ui-confirm__icon--warn { background: var(--color-warn-soft); color: var(--color-warn); box-shadow: 0 0 0 10px color-mix(in srgb, var(--color-warn) 6%, transparent); }
  .ui-confirm__icon--bad { background: var(--color-bad-soft); color: var(--color-bad); box-shadow: 0 0 0 10px color-mix(in srgb, var(--color-bad) 6%, transparent); }
  .ui-confirm__icon--muted { background: var(--color-sunken); color: var(--color-muted); box-shadow: none; }

  .ui-confirm .ui-title { text-wrap: balance; }

  /* A ticket's QR code: dark on white whatever the style, so any scanner
     reads it. */
  .ui-qr {
    width: min(14rem, 70vw);
    aspect-ratio: 1;
    padding: var(--p-space-2);
    border: 1px solid var(--color-line);
    border-radius: var(--radius-inner);
    background: #ffffff;
  }

  .ui-qr svg { display: block; width: 100%; height: 100%; }

  /* An image keeps its square: its width/height attributes (there so the page
     doesn't jump while it loads) must not stretch it to the frame's height. */
  .ui-qr img { display: block; width: 100%; height: auto; aspect-ratio: 1; }

  /* The event link's QR is a whole card (wordmark, code, line underneath):
     it brings its own frame. */
  .ui-qr--poster { padding: 0; border: 0; background: none; }

  /* How many tickets: minus and plus around the number, and a slider when
     there is a range worth dragging (ui/static/ui/js/inputs.js). */
  .ui-qty-field {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    flex: 0 0 auto;
  }

  .ui-qty-control {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-inner);
    background: var(--color-surface);
    overflow: hidden;
  }

  .ui-qty-control .ui-qty,
  .ui-qty-control input[type="number"] {
    width: 3.5rem;
    border: 0;
    border-radius: 0;
    text-align: center;
    -moz-appearance: textfield;
    appearance: textfield;
  }

  .ui-qty-control input[type="number"]::-webkit-outer-spin-button,
  .ui-qty-control input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }

  .ui-qty-step {
    border: 0;
    background: var(--color-surface-sunken, #f5f4f9);
    color: var(--color-ink);
    font-size: var(--text-lg, 1.125rem);
    line-height: 1;
    min-width: 2.5rem;
    cursor: pointer;
  }

  .ui-qty-step:hover:not(:disabled) { background: var(--color-brand-soft); color: var(--color-brand); }
  .ui-qty-step:disabled { opacity: 0.4; cursor: default; }

  .ui-qty-slider {
    display: block;
    width: 100%;
    min-width: 9rem;
    max-width: 12rem;
    margin-top: var(--p-space-2);
    accent-color: var(--color-brand);
  }

  /* A code typed one character per box, the way it arrives by email. */
  .ui-code-boxes {
    display: flex;
    align-items: center;
    gap: var(--p-space-1, 4px);
  }

  /* The boxes narrow to keep one code on one line, however small the screen. */
  .ui-code-box {
    flex: 1 1 2.5rem;
    width: 2.5rem;
    min-width: 0;
    max-width: 3rem;
    height: 3rem;
    padding: 0;
    border: 1px solid var(--color-line);
    border-radius: var(--radius-inner);
    background: var(--color-surface);
    color: var(--color-ink);
    font-size: var(--text-lg, 1.125rem);
    font-weight: 600;
    text-align: center;
    text-transform: uppercase;
  }

  .ui-code-box:focus {
    outline: 2px solid var(--color-brand);
    outline-offset: 1px;
  }

  .ui-code-boxes__dash { color: var(--color-muted); padding-inline: 2px; }

  /* Label and value pairs, one per line: an order and its amounts. */
  .ui-dl {
    display: flex;
    flex-direction: column;
    margin: 0;
    text-align: left;
    border-top: var(--divider);
  }

  .ui-dl > div {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--p-space-4);
    padding: var(--p-space-3) 0;
    border-bottom: var(--divider);
  }

  .ui-dl dt { color: var(--color-muted); }
  .ui-dl dd { margin: 0; color: var(--color-heading); text-align: right; }
  .ui-dl__total dt, .ui-dl__total dd { font-weight: 700; color: var(--color-heading); }

  .ui-ref {
    padding: 0.65rem 1.3rem;
    border-radius: var(--radius-field);
    background: var(--color-brand-soft);
    color: var(--color-brand);
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-xl);
    font-weight: 600;
    letter-spacing: 0.1em;
  }

  .ui-confirm__tickets { justify-content: center; }

  /* A chip may now be a link to its own card; it must not look different for it. */
  a.ui-ticket-chip { text-decoration: none; color: inherit; }
  a.ui-ticket-chip:hover { border-color: var(--color-brand); }

  .ui-ticket-chip {
    display: flex;
    flex-direction: column;
    padding: 0.5rem 1rem;
    border: 1px solid var(--color-brand-line);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    color: var(--color-heading);
  }

  .ui-ticket-chip code {
    font-family: var(--p-face-plex-mono);
    font-weight: 600;
    color: var(--color-brand);
  }

  .ui-confirm__facts { text-align: left; }
  .ui-confirm__facts .ui-row { padding: 0; align-items: flex-start; }

  .ui-confirm__actions { justify-content: center; }
  .ui-confirm__note { text-align: center; }

  /* Rows of blocks an organiser placed on the confirmation page. */
  .ui-blocks { display: grid; gap: var(--p-space-5); text-align: left; }

  .ui-blocks__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--p-space-4) var(--p-space-5);
  }

  .ui-blocks__row--left { justify-content: flex-start; }
  .ui-blocks__row--center { justify-content: center; text-align: center; }
  .ui-blocks__row--right { justify-content: flex-end; text-align: right; }
  .ui-blocks__item { min-width: 0; }
  .ui-blocks__item :where(img, video) { max-width: 100%; height: auto; }
  .ui-blocks__item audio { display: block; max-width: 100%; }

  @keyframes ui-stamp-in {
    from { opacity: 0; transform: scale(0.6); }
    to { opacity: 1; transform: scale(1); }
  }

  /* ---- "or" between two ways of doing the same thing --------------- */

  .ui-divider {
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    color: var(--color-faint);
    font-size: var(--text-sm);
  }

  .ui-divider::before,
  .ui-divider::after {
    content: "";
    flex: 1;
    border-top: var(--divider);
  }

  /* ---- A quiet disclosure: "Use a password instead" ---------------- */

  .ui-details > summary {
    width: fit-content;
    color: var(--color-muted);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  .ui-details > summary:hover { color: var(--color-heading); }
  .ui-details[open] > summary { margin-bottom: var(--p-space-4); }

  /* ---- Door check-in ----------------------------------------------- */

  .ui-checkin {
    width: 100%;
    max-width: 30rem;
    margin-inline: auto;
    text-align: center;
  }

  .ui-checkin__input {
    min-height: var(--control-height-lg);
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-lg);
    letter-spacing: 0.05em;
    text-align: center;
  }

  .ui-checkin-result {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--p-space-1);
    padding: var(--p-space-6) var(--p-space-4);
    border-radius: var(--radius-card);
    background: var(--color-sunken);
    color: var(--color-muted);
    animation: ui-rise var(--p-duration-slow) var(--ease) both;
  }

  .ui-checkin-result__title { font-size: var(--text-lg); font-weight: 700; }
  .ui-checkin-result__name { font-size: var(--text-2xl); font-weight: 600; color: var(--color-heading); }
  .ui-checkin-result__meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 0.35rem; font-size: var(--text-sm); }

  .ui-checkin-result--admitted { background: var(--color-ok-soft); color: var(--color-ok); }
  .ui-checkin-result--already_checked_in { background: var(--color-warn-soft); color: var(--color-warn); }
  .ui-checkin-result--voided { background: var(--color-bad-soft); color: var(--color-bad); }

  /* ---- Organiser pages: headers, search, tables ------------------- */

  .ui-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--p-space-4);
  }

  .ui-back { width: fit-content; margin-left: calc(-1 * var(--p-space-2)); }
  .ui-narrow { max-width: 40rem; }

  .ui-rule {
    width: 100%;
    height: 1px;
    margin: 0;
    border: 0;
    background: var(--color-line);
  }

  .ui-search {
    display: flex;
    flex-wrap: wrap;
    gap: var(--p-space-2);
    max-width: 36rem;
  }

  .ui-search .ui-input { flex: 1 1 14rem; width: auto; }

  /* An input with a fixed prefix welded to its left edge, like a URL bar that
     repeats the site address. The add-on takes the input's own border on its
     right, so the two read as one control. */
  .ui-affix {
    display: flex;
    align-items: stretch;
    width: 100%;
  }

  .ui-affix__addon {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-2);
    flex: 0 0 auto;
    min-height: var(--control-height);
    padding: 0 var(--p-space-3);
    border: 1px solid var(--color-line-strong);
    border-right: 0;
    border-radius: var(--radius-field) 0 0 var(--radius-field);
    background: var(--color-sunken);
    color: var(--color-faint);
    font-size: var(--text-sm);
    white-space: nowrap;
    overflow: hidden;
  }

  .ui-affix .ui-input {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }

  .ui-affix:focus-within .ui-affix__addon {
    border-color: var(--color-brand);
    box-shadow: inset 0 0 0 3px var(--color-brand-soft);
  }

  .ui-table-wrap { overflow-x: auto; }

  .ui-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .ui-table th {
    padding: var(--p-space-3) var(--p-space-4);
    border-bottom: var(--divider);
    font-family: var(--label-font);
    font-size: var(--text-xs);
    font-weight: 600;
    text-align: left;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-faint);
    white-space: nowrap;
  }

  .ui-table td {
    padding: var(--p-space-3) var(--p-space-4);
    border-bottom: var(--divider);
    color: var(--color-text);
    vertical-align: middle;
  }

  .ui-table tbody tr:last-child td { border-bottom: 0; }
  .ui-table tbody tr:hover td { background: color-mix(in srgb, var(--color-sunken) 55%, transparent); }
  .ui-table--compact th, .ui-table--compact td { padding: var(--p-space-2) var(--p-space-3); white-space: nowrap; }
  .ui-table th.is-email { color: var(--color-brand); }
  .ui-table__primary { font-weight: 600; color: var(--color-heading); }
  a.ui-table__primary:hover { color: var(--color-brand); }
  .ui-table__actions { text-align: right; white-space: nowrap; }
  /* A column of numbers: right-aligned, digits the same width so they line up. */
  .ui-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ui-table__actions form { display: inline; }
  .ui-table__note td { background: var(--color-bad-soft); color: var(--color-bad); }

  /* On a phone a stacking table shows each row as a card of label and value. */
  @media (max-width: 47.99rem) {
    .ui-table--stack thead { display: none; }
    .ui-table--stack, .ui-table--stack tbody, .ui-table--stack tr, .ui-table--stack td { display: block; }
    .ui-table--stack tr { padding: var(--p-space-3) var(--p-space-4); border-bottom: var(--divider); }
    .ui-table--stack tbody tr:last-child { border-bottom: 0; }
    .ui-table--stack td {
      display: flex;
      justify-content: space-between;
      gap: var(--p-space-4);
      padding: var(--p-space-1) 0;
      border: 0;
      text-align: right;
      overflow-wrap: anywhere;
    }
    .ui-table--stack td[data-label]::before {
      content: attr(data-label);
      flex-shrink: 0;
      color: var(--color-faint);
      text-align: left;
    }
    .ui-table--stack td:first-child { font-size: var(--text-base); }
    .ui-table--stack .ui-table__actions { justify-content: flex-start; padding-top: var(--p-space-2); }
    .ui-table--stack tbody tr:hover td { background: none; }
    /* A note row belongs to the card above it: one block of left-aligned text. */
    .ui-table--stack tr.has-note { border-bottom: 0; }
    .ui-table--stack tr.ui-table__note { padding-top: 0; }
    .ui-table--stack .ui-table__note td {
      display: block;
      padding: var(--p-space-2) var(--p-space-3);
      border-radius: var(--radius-inner);
      text-align: left;
    }
    .ui-table--stack tbody tr.ui-table__note:hover td { background: var(--color-bad-soft); }
  }

  .ui-btn--danger-quiet { color: var(--color-bad); }
  .ui-btn--danger-quiet:hover { color: var(--color-bad); background: var(--color-bad-soft); }

  .ui-row--link { color: inherit; }
  .ui-row--link:hover { background: var(--color-sunken); text-decoration: none; color: inherit; }
  .ui-card .ui-row--link:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; }
  .ui-row--link > .ui-icon:last-child { color: var(--color-faint); }

  .ui-choice--button { width: 100%; text-align: left; font: inherit; }
  .ui-choice--button[aria-selected="true"] { border-color: var(--color-brand); background: var(--color-brand-soft); }
  .ui-choice--button > .ui-icon { color: var(--color-brand); flex-shrink: 0; }

  .ui-facts-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
    gap: var(--p-space-4);
    margin: 0;
  }

  .ui-facts-grid dt { font-size: var(--text-sm); color: var(--color-muted); }
  .ui-facts-grid dd { margin: 0; font-weight: 600; color: var(--color-heading); overflow-wrap: anywhere; }

  .ui-codeblock {
    margin: 0;
    padding: var(--p-space-3) var(--p-space-4);
    overflow-x: auto;
    border-radius: var(--radius-inner);
    background: var(--color-sunken);
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-sm);
    color: var(--color-heading);
  }

  .ui-list { margin: 0; padding-left: 1.1rem; display: grid; gap: var(--p-space-2); }

  .ui-column-row {
    display: grid;
    gap: var(--p-space-3);
    margin: 0;
    padding: var(--p-space-4) 0;
    border: 0;
  }

  .ui-column-row legend { float: left; width: 100%; padding: 0; margin-bottom: var(--p-space-1); }
  .ui-column-row__fields { display: grid; gap: var(--p-space-3); grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); clear: both; }

  .ui-report .ui-stat__value { font-size: var(--text-2xl); }

  /* ---- Guest page action bar -------------------------------------- */

  .ui-action-bar {
    position: sticky;
    bottom: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--p-space-4);
    padding: var(--p-space-3) var(--space-gutter) calc(var(--p-space-3) + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--color-surface) 94%, transparent);
    border-top: 1px solid var(--color-line);
    backdrop-filter: blur(8px);
  }

  .ui-action-bar .ui-data { white-space: nowrap; }

  @media (min-width: 60rem) {
    .ui-action-bar { position: static; border: 0; background: none; padding-inline: 0; }
    /* On a desktop the ticket panel is always in view and carries the button. */
    .ui-action-bar--phone { display: none; }
  }

  /* ---- Email editor ----------------------------------------------- */

  /* The form first; on a wide screen the preview sits beside it and stays in view. */
  .ui-editor {
    display: grid;
    gap: var(--p-space-5);
    align-items: start;
  }

  .ui-editor__form,
  .ui-editor__aside,
  .ui-editor > * { min-width: 0; }

  @media (min-width: 72rem) {
    .ui-editor { grid-template-columns: minmax(0, 1fr) minmax(22rem, 34rem); }
    .ui-editor__aside { position: sticky; top: var(--p-space-5); }
    /* The review page lists its sections flat; the preview takes the second column. */
    .ui-editor--review { grid-template-rows: auto auto 1fr; }
    .ui-editor--review > * { grid-column: 1; }
    .ui-editor--review > .ui-editor__aside { grid-column: 2; grid-row: 1 / span 3; }
  }

  /* A step that keeps everything in one column, stacked: no side preview. */
  .ui-editor--stacked { grid-template-columns: minmax(0, 1fr); }
  .ui-editor--stacked .ui-editor__aside { display: none; }

  .ui-editor__preview { overflow: hidden; }
  .ui-editor__preview > * + * { border-top: var(--divider); }

  .ui-editor__preview iframe {
    display: block;
    width: 100%;
    height: min(70vh, 44rem);
    border: 0;
    background: #f5f4f8;
  }

  .ui-block { margin: 0; min-width: 0; }
  .ui-block legend { float: left; padding: 0; }
  .ui-block.is-focus { box-shadow: 0 0 0 3px var(--color-brand-soft); border-color: var(--color-brand-line); }
  .ui-block:target { box-shadow: 0 0 0 3px var(--color-brand-soft); }

  .ui-code-input {
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .ui-chip--button {
    border: 1px solid var(--color-line);
    text-transform: none;
    letter-spacing: 0;
    font-family: inherit;
    cursor: pointer;
  }

  .ui-chip--button:hover { border-color: var(--color-brand-line); color: var(--color-heading); }

  .ui-variables > summary { cursor: pointer; }

  /* ---- Motion ----------------------------------------------------- */

  @keyframes ui-rise {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
  }

  @keyframes ui-stamp {
    from { opacity: 0; transform: rotate(-8deg) scale(1.25); }
    to { opacity: 1; transform: rotate(-8deg) scale(1); }
  }

  /* One orchestrated entrance: children rise in sequence. */
  .ui-rise > * {
    animation: ui-rise var(--p-duration-slow) var(--ease) both;
  }

  .ui-rise > :nth-child(2) { animation-delay: 60ms; }
  .ui-rise > :nth-child(3) { animation-delay: 120ms; }
  .ui-rise > :nth-child(4) { animation-delay: 180ms; }
  .ui-rise > :nth-child(n + 5) { animation-delay: 240ms; }
}

@layer components {
  /* ---- Home: a person's own events (flow A4) ------------------------ */

  .ui-account__main { max-width: 46rem; }

  /* Someone's own words, quoted: a guest's refund request. */
  .ui-quote {
    margin: 0;
    padding: var(--p-space-3) var(--p-space-4);
    border-left: 3px solid var(--color-brand-line);
    border-radius: var(--radius-inner);
    background: var(--color-sunken);
    color: var(--color-heading);
  }

  .ui-attend { display: flex; flex-direction: column; }

  .ui-attend__head {
    display: flex;
    align-items: flex-start;
    gap: var(--p-space-4);
    padding: var(--p-space-5);
  }

  .ui-attend__date {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex-shrink: 0;
    width: 3.5rem;
    padding: var(--p-space-2) 0;
    border-radius: var(--radius-inner);
    background: var(--color-brand-soft);
    color: var(--color-brand);
    font-family: var(--label-font);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    line-height: 1.2;
  }

  .ui-attend__date strong {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    letter-spacing: 0;
    line-height: 1.1;
  }

  .ui-attend__title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    line-height: 1.3;
    color: var(--color-heading);
    overflow-wrap: anywhere;
  }

  .ui-attend__title a { color: inherit; }

  .ui-attend__section {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-2);
    padding: var(--p-space-4) var(--p-space-5);
    border-top: var(--divider);
  }

  .ui-attend__section .ui-row { padding: 0; align-items: flex-start; }
  .ui-attend__section > .ui-btn { width: fit-content; }

  .ui-attend__ticket {
    border: 1px solid var(--color-line);
    border-radius: var(--radius-field);
  }

  .ui-attend__ticket > summary {
    display: flex;
    align-items: center;
    gap: var(--p-space-3);
    min-height: 3rem;
    padding: var(--p-space-2) var(--p-space-3);
    list-style: none;
    cursor: pointer;
  }

  .ui-attend__ticket > summary::-webkit-details-marker { display: none; }
  .ui-attend__ticket > summary > .ui-small,
  .ui-attend .ui-code,
  .ui-home .ui-dl .ui-code { white-space: nowrap; }
  .ui-attend__ticket[open] > summary .ui-small { visibility: hidden; }

  .ui-attend__qr {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--p-space-2);
    padding: 0 var(--p-space-3) var(--p-space-4);
  }

  .ui-attend__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--p-space-2);
    padding: var(--p-space-2) var(--p-space-3);
    border-top: var(--divider);
  }
}

@layer components {
  /* ---- Browse and organiser profiles (flows A1, A6) ------------------- */

  .ui-browse__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
    gap: var(--p-space-4);
  }

  .ui-browse-card {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-3);
    padding: var(--p-space-3);
    color: inherit;
    transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
  }

  .ui-browse-card:hover { border-color: var(--color-brand-line); text-decoration: none; color: inherit; }
  .ui-browse-card .ui-cover { --cover-height: 9.5rem; padding: var(--p-space-4); }
  .ui-browse-card .ui-cover-image img { aspect-ratio: 16 / 9; max-height: none; object-fit: cover; }
  .ui-browse-card__body { padding: 0 var(--p-space-1) var(--p-space-1); flex-grow: 1; }
  .ui-browse-card__title { font-weight: 600; font-size: var(--text-lg); line-height: 1.3; color: var(--color-heading); overflow-wrap: anywhere; }
  .ui-browse-card__foot { margin-top: auto; padding-top: var(--p-space-2); gap: var(--p-space-2); }
  .ui-browse-card__foot strong { white-space: nowrap; }
  .ui-browse-card__org { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

  .ui-browse__city { width: auto; min-width: 12rem; flex: 1 1 12rem; }
  .ui-browse__date { width: auto; }
  .ui-chip--button.is-on { background: var(--color-brand); border-color: var(--color-brand); color: #ffffff; }
  .ui-chip--button:has(input:focus-visible) { outline: 2px solid var(--color-brand); outline-offset: 2px; }

  /* ---- Live's front door (discovery/landing.html, flows §1.1) ------------ */

  /* ---- Hero: the words, and an event in motion beside them ---------- */

  .ui-landing__hero {
    display: grid;
    gap: var(--p-space-7);
    align-items: center;
    padding-top: var(--p-space-6);
  }

  .ui-landing__hero-text { max-width: 44rem; }

  @media (min-width: 60rem) {
    .ui-landing__hero { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
  }

  /* The art is a container sized by its width; everything inside is in em
     of a font size set from that width, so the scene scales like a picture. */
  .ui-landing__art {
    position: relative;
    width: 100%;
    max-width: 32rem;
    justify-self: center;
    aspect-ratio: 100 / 86;
    container-type: inline-size;
  }

  /* cqi measures the nearest container *above* an element, never the element
     itself, so the size is set on a scene inside the container. */
  .ui-landing__scene { position: absolute; inset: 0; font-size: calc(100cqi / 28); }
  @supports not (font-size: 1cqi) { .ui-landing__scene { font-size: 14px; } }
  .ui-landing__scene > * { position: absolute; }

  .ui-landing__trail { inset: 0; width: 100%; height: 100%; overflow: visible; }
  .ui-landing__trail path {
    fill: none;
    stroke: var(--color-brand);
    stroke-width: 0.5;
    stroke-dasharray: 1.2 1.6;
    stroke-linecap: round;
    opacity: 0.5;
    vector-effect: non-scaling-stroke;
  }
  .ui-landing__trail path { stroke-width: 2px; stroke-dasharray: 2px 6px; }

  .ui-landing__invite {
    left: 3%;
    top: 10%;
    width: 62%;
    min-height: 0;
    padding: 1.2em 1.4em;
    gap: 0.6em;
    --cover-title-size: 2.5em;
    rotate: -4deg;
    box-shadow: 0 1.6em 3em -1.4em rgba(87, 22, 194, 0.55);
  }

  .ui-landing__invite .ui-cover__eyebrow { font-size: 0.8em; }
  .ui-landing__invite-when { font-size: 0.8em; color: var(--cover-eyebrow); }

  .ui-landing__toast {
    right: 0;
    top: 3%;
    width: 50%;
    display: flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.6em 0.8em;
    border: var(--border-card);
    border-radius: 1em;
    background: var(--color-surface);
    box-shadow: 0 1em 2em -1em rgba(29, 25, 51, 0.3);
    font-size: 0.8em;
    line-height: 1.3;
  }

  .ui-landing__toast .ui-avatar { --avatar-size: 2.2em; font-size: 0.75em; }
  .ui-landing__toast > span:last-child { display: flex; flex-direction: column; min-width: 0; color: var(--color-heading); }

  .ui-landing__count {
    left: 1%;
    bottom: 11%;
    display: flex;
    align-items: center;
    gap: 0.45em;
    padding: 0.5em 0.9em;
    border: var(--border-card);
    border-radius: 999px;
    background: var(--color-surface);
    box-shadow: 0 1em 2em -1em rgba(29, 25, 51, 0.3);
    font-size: 0.85em;
    color: var(--color-heading);
    white-space: nowrap;
  }

  .ui-landing__count b { font-variant-numeric: tabular-nums; }

  .ui-landing__live-dot {
    position: relative;
    width: 0.55em;
    height: 0.55em;
    border-radius: 50%;
    background: var(--color-ok);
  }

  .ui-landing__live-dot::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--color-ok);
    animation: ui-landing-ping 2.4s var(--ease) infinite;
  }

  /* The ticket: its details, a perforation, and a code that really scans. */
  .ui-landing__art-ticket {
    right: 1%;
    bottom: 4%;
    width: 60%;
    display: grid;
    grid-template-columns: 1fr auto;
    border: var(--border-card);
    border-radius: 0.9em;
    background: var(--color-surface);
    box-shadow: 0 1.4em 2.6em -1.2em rgba(29, 25, 51, 0.35);
    rotate: 3deg;
    overflow: hidden;
  }

  .ui-landing__art-ticket-info {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.15em;
    padding: 0.9em 1em;
    border-right: 1px dashed var(--color-line-strong);
    font-size: 0.8em;
    line-height: 1.3;
    min-width: 0;
  }

  .ui-landing__art-ticket-info strong { color: var(--color-heading); font-size: 1.15em; }
  .ui-landing__art-ticket-label { font-size: 0.8em; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-brand); }

  .ui-landing__qr { position: relative; width: 6.4em; padding: 0.55em; overflow: hidden; }
  .ui-landing__qr svg { display: block; width: 100%; height: auto; fill: #1d1933; }
  .ui-landing__qr-bg { fill: #ffffff; }

  /* A scan line passing over the code, as at the door. */
  .ui-landing__qr-scan {
    position: absolute;
    left: 0.4em;
    right: 0.4em;
    top: 0.55em;
    height: 2px;
    border-radius: 2px;
    background: var(--color-accent);
    box-shadow: 0 0 0.6em var(--color-accent);
    opacity: 0;
  }

  .ui-landing__confetti { width: 0.6em; height: 0.6em; border-radius: 50%; background: var(--color-brand); opacity: 0.35; }
  .ui-landing__confetti--a { left: 70%; top: 36%; background: var(--color-accent); }
  .ui-landing__confetti--b { left: 12%; top: 2%; width: 0.4em; height: 0.4em; }
  .ui-landing__confetti--c { left: 46%; bottom: 2%; width: 0.45em; height: 0.45em; background: var(--color-accent); }
  .ui-landing__confetti--plus { right: 4%; top: 42%; width: auto; height: auto; background: none; color: var(--color-brand); font-weight: 700; font-size: 1.3em; line-height: 1; }

  /* Arrival, in the order an event happens; then everything drifts gently. */
  .ui-landing__scene > .ui-landing__invite,
  .ui-landing__scene > .ui-landing__toast,
  .ui-landing__scene > .ui-landing__count,
  .ui-landing__scene > .ui-landing__art-ticket {
    transition: opacity 0.7s var(--ease), translate 0.7s var(--ease);
    animation: ui-landing-float 7s ease-in-out infinite;
  }

  .ui-landing__scene > .ui-landing__toast { transition-delay: 0.5s; animation-delay: -2s; }
  .ui-landing__scene > .ui-landing__count { transition-delay: 0.9s; animation-delay: -4s; }
  .ui-landing__scene > .ui-landing__art-ticket { transition-delay: 1.3s; animation-delay: -5.5s; }

  .ui-landing.is-animated .ui-landing__art:not(.is-in) .ui-landing__scene > .ui-landing__invite { opacity: 0; translate: 0 1em; }
  .ui-landing.is-animated .ui-landing__art:not(.is-in) .ui-landing__scene > .ui-landing__toast { opacity: 0; translate: -1.2em 0; }
  .ui-landing.is-animated .ui-landing__art:not(.is-in) .ui-landing__scene > .ui-landing__count { opacity: 0; translate: 0 0.8em; }
  .ui-landing.is-animated .ui-landing__art:not(.is-in) .ui-landing__scene > .ui-landing__art-ticket { opacity: 0; translate: 0 1.6em; }

  .ui-landing.is-animated .ui-landing__art.is-in .ui-landing__qr-scan { animation: ui-landing-scan 3.2s ease-in-out 2.2s infinite; }

  @keyframes ui-landing-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-4px); }
  }

  @keyframes ui-landing-ping {
    0% { transform: scale(1); opacity: 0.6; }
    80%, 100% { transform: scale(2.6); opacity: 0; }
  }

  @keyframes ui-landing-scan {
    0% { top: 0.55em; opacity: 0; }
    15% { opacity: 0.9; }
    60% { top: calc(100% - 0.55em); opacity: 0.9; }
    75%, 100% { top: calc(100% - 0.55em); opacity: 0; }
  }


  /* The journey reads in order, so it only ever splits evenly: four across,
     two by two, or one column on a phone. Never three and an orphan, which
     auto-fit would give at tablet widths. Each stage is a numbered node over
     its card; a line joins the nodes where the order runs in one direction
     (across on a wide screen, down on a phone) and draws itself on arrival. */
  .ui-landing__steps {
    --steps-gap: var(--p-space-4);
    --node: 2.25rem;
    position: relative;
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--p-space-6) var(--steps-gap);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  @media (min-width: 40rem) { .ui-landing__steps { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 75rem) { .ui-landing__steps { grid-template-columns: repeat(4, 1fr); } }

  .ui-landing__steps::before {
    content: "";
    position: absolute;
    z-index: 0;
    background: linear-gradient(90deg, var(--color-brand-line), var(--color-brand), var(--color-accent));
    transition: transform 1.4s var(--ease);
  }

  /* Down the middle on a phone: it shows between the cards, hidden behind them. */
  @media (max-width: 39.99rem) {
    .ui-landing__steps::before {
      top: calc(var(--node) / 2);
      bottom: 3rem;
      left: calc(50% - 1px);
      width: 2px;
      background: linear-gradient(180deg, var(--color-brand-line), var(--color-brand), var(--color-accent));
      transform-origin: top;
    }
    .ui-landing.is-animated .ui-landing__steps:not(.is-in)::before { transform: scaleY(0); }
  }

  /* Two by two has no single direction to draw, so the nodes stand alone. */
  @media (min-width: 40rem) and (max-width: 74.99rem) {
    .ui-landing__steps::before { display: none; }
  }

  /* Across, from the centre of the first node to the centre of the last. */
  @media (min-width: 75rem) {
    .ui-landing__steps::before {
      top: calc(var(--node) / 2 - 1px);
      left: calc((100% - 3 * var(--steps-gap)) / 8);
      right: calc((100% - 3 * var(--steps-gap)) / 8);
      height: 2px;
      transform-origin: left;
    }
    .ui-landing.is-animated .ui-landing__steps:not(.is-in)::before { transform: scaleX(0); }
  }

  .ui-landing__stage {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--p-space-3);
    transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
    transition-delay: calc(var(--i, 0) * 140ms + 200ms);
  }

  .ui-landing.is-animated .ui-landing__steps:not(.is-in) .ui-landing__stage { opacity: 0; transform: translateY(12px); }

  .ui-landing__stage > .ui-card {
    align-self: stretch;
    flex-grow: 1;
    transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease), border-color var(--duration) var(--ease);
  }

  .ui-landing__node {
    display: grid;
    place-items: center;
    width: var(--node);
    height: var(--node);
    border-radius: 50%;
    border: 1px solid var(--color-brand-line);
    background: var(--color-surface);
    color: var(--color-brand);
    font-family: var(--p-face-plex-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    box-shadow: 0 0 0 4px var(--color-page);
    transition: background-color var(--duration) var(--ease), color var(--duration) var(--ease), transform var(--duration) var(--ease);
  }

  /* Pointing at a stage lights its node and lifts its card. */
  @media (hover: hover) {
    .ui-landing__stage:hover > .ui-card { transform: translateY(-3px); border-color: var(--color-brand-line); box-shadow: var(--shadow-card-hover, 0 12px 28px -14px rgba(29, 25, 51, 0.25)); }
    .ui-landing__stage:hover .ui-landing__node { background: var(--color-brand); color: var(--color-on-brand); transform: scale(1.08); }
  }

  .ui-landing__step {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-2);
    color: var(--color-brand);
    font-family: var(--label-font);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }

  .ui-landing__step-title { margin: 0; font-size: var(--text-lg); font-weight: 600; line-height: 1.3; color: var(--color-heading); }

  /* A small picture of each step, drawn with the real components (cover,
     chips, avatars, the door's result) so it restyles along with them.
     Decorative: aria-hidden in the template, and nothing in it responds. */
  .ui-landing__preview {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--p-space-2);
    height: 13rem;
    padding: var(--p-space-3);
    border-radius: calc(var(--radius-card) - 0.25rem);
    background: var(--color-sunken);
    font-size: var(--text-xs);
    line-height: 1.4;
    overflow: hidden;
    pointer-events: none;
    user-select: none;
  }

  .ui-landing__cover {
    --cover-height: 6.25rem;
    --cover-title-size: 1.6rem;
    padding: var(--p-space-3) var(--p-space-4);
    gap: var(--p-space-2);
  }

  .ui-landing__cover .ui-cover__eyebrow { font-size: var(--text-xs); }

  .ui-landing__ticket {
    padding: var(--p-space-2) var(--p-space-3);
    border: var(--border-card);
    border-radius: var(--p-radius-sm);
    background: var(--color-surface);
  }

  .ui-landing__ticket-name { display: flex; flex-direction: column; }

  .ui-landing__mail {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: var(--p-space-2) var(--p-space-3);
    border: var(--border-card);
    border-radius: var(--p-radius-sm);
    background: var(--color-surface);
  }

  .ui-landing__mail strong { color: var(--color-heading); }
  .ui-landing__mail--next { flex-direction: row; align-items: center; gap: var(--p-space-2); opacity: 0.75; }

  .ui-landing__code {
    width: fit-content;
    margin-top: 0.15rem;
    padding: 0.1rem 0.45rem;
    border: 1px dashed var(--color-line-strong);
    border-radius: var(--radius-chip);
    font-family: var(--p-face-plex-mono);
    color: var(--color-heading);
  }

  .ui-landing__guest {
    --avatar-size: 1.75rem;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--p-space-2);
    padding: 0.35rem var(--p-space-2);
    border-radius: var(--p-radius-sm);
    background: var(--color-surface);
  }

  .ui-landing__guest > span:nth-child(2) { display: flex; flex-direction: column; color: var(--color-heading); font-weight: 600; min-width: 0; }
  .ui-landing__guest small { font-weight: 400; }
  .ui-landing__guest .ui-avatar { font-size: 0.625rem; }

  .ui-landing__door { height: 100%; justify-content: center; padding: var(--p-space-3); animation: none; }
  .ui-landing__door .ui-checkin-result__name { font-size: var(--text-xl); }

  /* The guest list is the part competitors don't keep, so it gets its own band. */
  .ui-landing__audience {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
    gap: var(--p-space-6);
    align-items: center;
    background: var(--color-brand-soft);
    border-color: var(--color-brand-line);
  }

  .ui-landing__audience-points li::marker { color: var(--color-brand); }

  /* Past events' guests gathering into one audience. The lines are drawn by
     animating a dash along pathLength="1": no measuring in script. */
  .ui-landing__net-wrap { width: 100%; max-width: 30rem; justify-self: center; }
  .ui-landing__net { display: block; width: 100%; height: auto; overflow: visible; font-family: var(--font-body, inherit); }

  .ui-landing__net-guest circle { stroke: var(--color-brand-soft); stroke-width: 3; }
  .ui-landing__net-guest text { fill: #ffffff; font-size: 10px; font-weight: 700; text-anchor: middle; }
  .ui-landing__net-guest--a circle { fill: var(--color-inverse); }
  .ui-landing__net-guest--b circle { fill: var(--color-brand); }
  .ui-landing__net-guest--c circle { fill: var(--color-accent); }
  .ui-landing__net-label { fill: var(--color-muted); font-size: 13px; }

  .ui-landing__net-line {
    fill: none;
    stroke: var(--color-brand);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 0;
    opacity: 0.55;
    transition: stroke-dashoffset 0.9s var(--ease);
    transition-delay: calc(0.5s + var(--i, 0) * 0.15s);
  }

  .ui-landing__net-line--out { stroke: var(--color-accent); transition-delay: calc(1.3s + (var(--i, 0) - 3) * 0.15s); }

  .ui-landing__net-hub rect { fill: var(--color-brand); }
  .ui-landing__net-hub text { fill: var(--color-on-brand); font-size: 14px; font-weight: 700; text-anchor: middle; }
  .ui-landing__net-end rect { fill: var(--color-surface); stroke: var(--color-brand-line); stroke-width: 1.5; }
  .ui-landing__net-end text { fill: var(--color-heading); font-size: 13px; font-weight: 600; text-anchor: middle; }

  .ui-landing__net-guest,
  .ui-landing__net-hub,
  .ui-landing__net-end {
    transform-box: fill-box;
    transform-origin: center;
    transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  }

  .ui-landing__net-guest { transition-delay: calc(var(--i, 0) * 50ms); }
  .ui-landing__net-hub { transition-delay: 1.1s; }
  .ui-landing__net-end { transition-delay: calc(1.8s + var(--i, 0) * 0.15s); }

  .ui-landing.is-animated .ui-landing__net-wrap:not(.is-in) .ui-landing__net-guest { opacity: 0; transform: scale(0.6); }
  .ui-landing.is-animated .ui-landing__net-wrap:not(.is-in) .ui-landing__net-line { stroke-dashoffset: 1; }
  .ui-landing.is-animated .ui-landing__net-wrap:not(.is-in) .ui-landing__net-hub { opacity: 0; transform: scale(0.85); }
  .ui-landing.is-animated .ui-landing__net-wrap:not(.is-in) .ui-landing__net-end { opacity: 0; transform: translateX(-6px); }

  /* ---- Real events: the first one featured, all of them alive to the pointer */

  /* A card's cover title is set for a full-width event page; sized to the
     card here, so long words don't break mid-word. */
  .ui-landing__events .ui-cover { --cover-title-size: clamp(1.5rem, 1.1rem + 0.9vw, 2rem); }

  /* Fixed columns (one, two, three) rather than Browse's fit-as-many, so the
     five cards with the first spanning two fill whole rows at every width. */
  .ui-landing__events { grid-template-columns: 1fr; }
  @media (min-width: 48rem) { .ui-landing__events { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 64rem) { .ui-landing__events { grid-template-columns: repeat(3, 1fr); } }

  /* The date is already above the title; only the featured cover has room
     for the poster style's round date sticker too. */
  .ui-landing__events .ui-cover__sticker { display: none; }

  /* The first of three or more spans two columns with a taller cover. */
  @media (min-width: 48rem) {
    .ui-landing__events--featured > :first-child { grid-column: span 2; }
    [data-style="poster"].ui-browse-card:first-child .ui-cover__sticker { display: flex; }
    .ui-landing__events--featured > :first-child .ui-cover { --cover-height: 17rem; --cover-title-size: clamp(2.25rem, 1.6rem + 1.6vw, 3.25rem); }
    .ui-landing__events--featured > :first-child .ui-cover-image img { aspect-ratio: 21 / 9; }
    .ui-landing__events--featured > :first-child .ui-browse-card__title { font-size: var(--text-xl); }
  }

  .ui-landing__events .ui-browse-card { transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease), border-color var(--duration) var(--ease); }
  .ui-landing__events .ui-cover,
  .ui-landing__events .ui-cover-image { overflow: hidden; }
  .ui-landing__events .ui-cover-image img,
  .ui-landing__events .ui-cover__title { transition: transform 0.6s var(--ease); }

  @media (hover: hover) {
    .ui-landing__events .ui-browse-card:hover { transform: translateY(-3px); box-shadow: 0 14px 30px -16px rgba(29, 25, 51, 0.3); }
    .ui-landing__events .ui-browse-card:hover .ui-cover-image img { transform: scale(1.04); }
    .ui-landing__events .ui-browse-card:hover .ui-cover__title { transform: scale(1.03); transform-origin: left bottom; }
  }

  /* "See all events": the arrow leans the way it goes. */
  .ui-landing__go .ui-icon { transition: transform var(--duration) var(--ease); }
  .ui-landing__go:hover .ui-icon { transform: translateX(3px); }

  /* ---- The moments people gather: a mosaic, photographs where there are any */
  /* Spans are chosen so the tiles fill whole rows with no holes: 16 cells in
     four columns, 12 in two (Conference and Social wide, the rest single). */
  .ui-landing__moments {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 8.5rem;
    grid-auto-flow: dense;
    gap: var(--p-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .ui-landing__moment[data-kind="conference"],
  .ui-landing__moment[data-kind="social"] { grid-column: span 2; }

  @media (min-width: 40rem) {
    .ui-landing__moments { grid-auto-rows: 10.5rem; }
    .ui-landing__moment[data-kind="conference"] { grid-row: span 2; }
    .ui-landing__moment[data-kind="music"],
    .ui-landing__moment[data-kind="community"] { grid-row: span 2; }
  }

  @media (min-width: 64rem) {
    .ui-landing__moments { grid-template-columns: repeat(4, 1fr); grid-auto-rows: 11rem; }
  }

  .ui-landing__moment > a {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
    padding: var(--p-space-4);
    border: var(--border-card);
    border-radius: var(--radius-card);
    background-color: var(--color-surface);
    /* A faint dot grid: the quiet tile reads as a drawing, not an empty box. */
    background-image: radial-gradient(var(--color-brand-line) 1px, transparent 1.2px);
    background-size: 14px 14px;
    color: var(--color-heading);
    text-decoration: none;
    overflow: hidden;
    isolation: isolate;
    transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease), border-color var(--duration) var(--ease);
  }

  .ui-landing__moment > a:hover { transform: translateY(-3px); border-color: var(--color-brand-line); box-shadow: 0 14px 30px -16px rgba(29, 25, 51, 0.3); color: var(--color-heading); text-decoration: none; }
  .ui-landing__moment > a:focus-visible { outline: 3px solid var(--color-brand); outline-offset: 3px; }

  /* Without a photograph: the kind's line icon, large, in a soft ring. */
  .ui-landing__moment-art {
    position: absolute;
    top: var(--p-space-4);
    right: var(--p-space-4);
    display: grid;
    place-items: center;
    width: 4.75rem;
    height: 4.75rem;
    border-radius: 50%;
    background: var(--color-brand-soft);
    box-shadow: 0 0 0 8px color-mix(in srgb, var(--color-brand-soft) 45%, transparent);
    color: var(--color-brand);
    transition: transform 0.5s var(--ease);
  }

  .ui-landing__moment > a:hover .ui-landing__moment-art { transform: scale(1.06) rotate(-4deg); }
  .ui-landing__moment-art svg { width: 55%; height: 55%; }

  /* Phone tiles are shorter: a smaller ring keeps clear of the words. */
  @media (max-width: 39.99rem) {
    .ui-landing__moment-art { top: var(--p-space-3); right: var(--p-space-3); width: 3rem; height: 3rem; box-shadow: 0 0 0 5px color-mix(in srgb, var(--color-brand-soft) 45%, transparent); }
  }

  .ui-landing__moment-text { position: relative; display: flex; flex-direction: column; gap: 0.2rem; }

  .ui-landing__moment-label {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-2);
    font-family: var(--font-display);
    font-size: var(--text-xl);
    line-height: 1.1;
  }

  .ui-landing__moment-icon { display: none; }
  .ui-landing__moment-go { display: inline-flex; align-items: center; gap: 0.3rem; font-size: var(--text-sm); font-weight: 600; color: var(--color-brand); }
  .ui-landing__moment-go .ui-icon { transition: transform var(--duration) var(--ease); }
  .ui-landing__moment > a:hover .ui-landing__moment-go .ui-icon { transform: translateX(3px); }

  /* With a photograph: the picture fills the tile, darkened at the foot for the words. */
  .ui-landing__moment.has-photo > a { background: var(--color-inverse); border-color: transparent; color: #ffffff; }
  .ui-landing__moment.has-photo img {
    position: absolute;
    inset: 0;
    z-index: -2;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.7s var(--ease);
  }
  .ui-landing__moment.has-photo > a::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(to top, rgba(20, 16, 40, 0.78), rgba(20, 16, 40, 0.15) 55%, transparent);
  }
  .ui-landing__moment.has-photo > a:hover img { transform: scale(1.04); }
  .ui-landing__moment.has-photo .ui-landing__moment-icon {
    display: inline-grid;
    place-items: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.18);
    border: 1px solid rgba(255, 255, 255, 0.35);
    backdrop-filter: blur(6px);
  }
  .ui-landing__moment.has-photo .ui-landing__moment-go { color: rgba(255, 255, 255, 0.92); }

  /* ---- Confetti, sparingly: beside the journey and behind the last call ---- */

  .ui-landing__journey-head { position: relative; max-width: 40rem; }

  .ui-landing__sparkles { display: none; }

  @media (min-width: 60rem) {
    .ui-landing__sparkles { display: block; position: absolute; top: 0.25rem; left: calc(100% + var(--p-space-6)); width: 12rem; height: 4.5rem; }
    .ui-landing__sparkles .ui-landing__spark:nth-child(1) { left: 0; top: 1.5rem; }
    .ui-landing__sparkles .ui-landing__spark:nth-child(2) { left: 3.5rem; top: 0.25rem; }
    .ui-landing__sparkles .ui-landing__spark:nth-child(3) { left: 7.5rem; top: 2rem; }
    .ui-landing__sparkles .ui-landing__spark:nth-child(4) { left: 10.5rem; top: 0.5rem; }
  }

  .ui-landing__spark {
    position: absolute;
    display: block;
    font-style: normal;
    animation: ui-landing-float 6s ease-in-out infinite;
  }

  .ui-landing__spark:nth-child(2n) { animation-delay: -3s; }

  .ui-landing__spark--dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--color-brand); opacity: 0.45; }
  .ui-landing__spark--pink { background: var(--color-accent); width: 0.45rem; height: 0.45rem; }
  .ui-landing__spark--plus { color: var(--color-accent); font-weight: 700; font-size: 1.25rem; line-height: 1; opacity: 0.7; }

  /* A ticket's outline: a rounded box with a notch bitten from each side. */
  .ui-landing__spark--ticket {
    width: 1.8rem;
    height: 1.1rem;
    border: 1.5px solid var(--color-brand);
    border-radius: 0.3rem;
    opacity: 0.5;
    rotate: -12deg;
    -webkit-mask: radial-gradient(circle 0.3rem at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat,
                  radial-gradient(circle 0.3rem at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat;
            mask: radial-gradient(circle 0.3rem at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat,
                  radial-gradient(circle 0.3rem at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat;
  }

  /* ---- The last call to action, with the next events drifting behind it --- */

  .ui-landing__cta { position: relative; overflow: hidden; isolation: isolate; }
  .ui-landing__cta-text { position: relative; z-index: 1; align-items: flex-start; }
  .ui-landing__cta-art { display: none; }

  @media (min-width: 60rem) {
    .ui-landing__cta { min-height: 15rem; display: flex; align-items: center; }
    .ui-landing__cta-text { max-width: 58%; }
    .ui-landing__cta-art { display: block; position: absolute; top: 0; right: 0; bottom: 0; width: 38%; }
  }

  .ui-landing__mini {
    position: absolute;
    width: 11rem;
    min-height: 0;
    padding: var(--p-space-3) var(--p-space-4);
    gap: var(--p-space-2);
    --cover-title-size: 1.45rem;
    box-shadow: 0 1rem 2rem -1rem rgba(29, 25, 51, 0.35);
    transition: opacity 0.7s var(--ease), translate 0.7s var(--ease);
    animation: ui-landing-float 7s ease-in-out infinite;
  }

  .ui-landing__mini .ui-cover__eyebrow { font-size: var(--text-xs); }
  .ui-landing__mini--a { --cover-bg: #0f766e; top: 12%; right: 44%; rotate: -8deg; animation-delay: -1s; }
  .ui-landing__mini--b { --cover-bg: #b45309; top: 50%; right: 34%; rotate: 5deg; transition-delay: 0.2s; animation-delay: -3.5s; }
  .ui-landing__mini--c { top: 22%; right: 6%; rotate: -2deg; transition-delay: 0.4s; animation-delay: -5s; }

  .ui-landing__cta-art .ui-landing__spark--dot { top: 10%; right: 8%; }
  .ui-landing__cta-art .ui-landing__spark--plus { bottom: 14%; right: 12%; }
  .ui-landing__cta-art .ui-landing__spark--ticket { bottom: 10%; right: 60%; }

  .ui-landing.is-animated .ui-landing__cta-art:not(.is-in) .ui-landing__mini { opacity: 0; translate: 0 1.25rem; }

  .ui-profile__head { display: flex; align-items: center; gap: var(--p-space-4); }
  .ui-profile__logo { width: 4.5rem; height: 4.5rem; border-radius: var(--radius-card); object-fit: cover; background: var(--color-sunken); flex-shrink: 0; }
}

@layer utilities {
  /* The first thing a keyboard reaches: jumps past the header to #main.
     Off-screen until focused, then shown over the page's top-left corner. */
  .ui-skip {
    position: absolute;
    top: var(--p-space-2);
    left: var(--p-space-2);
    z-index: 1000;
    padding: var(--p-space-2) var(--p-space-4);
    border-radius: var(--p-radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: 0 0 0 2px var(--color-brand);
    transform: translateY(calc(-100% - var(--p-space-4)));
  }

  .ui-skip:focus { transform: none; }

  /* Read by screen readers, not shown. */
  .ui-visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* ---- Pop-ups the scripts open (events/js/modal-shim.js), used by the confirm dialog and the builders' pickers ---------------------------- */

  .ev-modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: var(--p-space-4);
    background: color-mix(in srgb, var(--color-heading) 45%, transparent);
  }

  .ev-modal .modal-dialog { width: min(32rem, 100%); }

  .ev-modal .modal-content {
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-card, 1rem);
    background: var(--color-surface);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
  }

  .ev-modal .modal-header,
  .ev-modal .modal-body,
  .ev-modal .modal-footer { padding: var(--p-space-4) var(--p-space-5); }

  .ev-modal .modal-header { display: flex; align-items: center; justify-content: space-between; border-bottom: var(--divider); }
  .ev-modal .modal-title { margin: 0; font-size: var(--text-lg); color: var(--color-heading); }
  .ev-modal .modal-body { display: flex; flex-direction: column; }
  .ev-modal .modal-footer { display: flex; justify-content: flex-end; gap: var(--p-space-2); border-top: var(--divider); }

  .ev-modal .btn-close { width: 2rem; height: 2rem; border: 0; border-radius: 50%; background: transparent; color: var(--color-muted); font-size: 1.3rem; cursor: pointer; }
  .ev-modal .btn-close::before { content: "×"; }
  .ev-modal .btn-close:hover { background: var(--color-sunken); color: var(--color-heading); }

  /* ---- Live's own footer (ui/_footer.html) ------------------------------------ */

  .ui-foot {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-5);
    margin-top: auto;
    padding: var(--p-space-7) var(--space-gutter) var(--p-space-6);
    border-top: 1px solid var(--color-line);
    background: var(--color-surface-sunken, var(--color-surface));
    font-size: var(--text-sm);
  }

  .ui-foot a { color: var(--color-muted); text-decoration: none; }
  .ui-foot a:hover { color: var(--color-heading); text-decoration: underline; }

  .ui-foot__grid {
    display: grid;
    gap: var(--p-space-5);
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    width: 100%;
    max-width: 72rem;
    margin-inline: auto;
  }

  .ui-foot__about { grid-column: span 2; max-width: 24rem; }

  .ui-foot__brand { display: flex; align-items: center; gap: var(--p-space-2); color: var(--color-heading); }
  .ui-foot__name { font-size: var(--text-base); }
  .ui-foot__mark { border-radius: 0.35rem; }

  .ui-foot__social {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--color-line);
    border-radius: 50%;
  }

  .ui-foot__base {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--p-space-2);
    width: 100%;
    max-width: 72rem;
    margin-inline: auto;
    padding-top: var(--p-space-4);
    border-top: 1px solid var(--color-line);
    color: var(--color-muted);
  }

  /* Signed in: one quiet row that never competes with the page. */
  .ui-foot--quiet {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--p-space-3);
    padding-block: var(--p-space-4);
    color: var(--color-muted);
  }

  @media (max-width: 47.99rem) {
    .ui-foot__about { grid-column: 1 / -1; }
  }
}
