/*
 * The organisation console.
 *
 * Loaded only on console pages, after attendyn.css, and it adds no tokens of
 * its own beyond the two it derives. Everything here is built from the ramp
 * that file establishes, so the console and the desks are visibly one product
 * rather than two that happen to share a logo.
 *
 * On the per-organisation accent
 * ------------------------------
 * A console page carries `style="--ad-accent: #xxxxxx"` from the template, and
 * the two further stops attendyn.css expects are mixed from it here rather than
 * being stored on the model. One stored colour cannot drift from itself; three
 * can, and the pair that would drift are exactly the two that carry contrast.
 *
 *   --ad-accent-deep  type, and the ground of a filled button. Mixed toward
 *                     black in light mode and toward white in dark, which is
 *                     the same direction attendyn.css moves it by hand -- the
 *                     stop means "the legible one", not "the darker one".
 *   --ad-accent-wash  pale ground for chips and tiles.
 *
 * The mixes assume the input is neither a pastel nor nearly black, which is
 * enforced where the colour is chosen rather than hoped for here --
 * tenancy.forms.OrganisationForm checks its relative luminance.
 *
 * Because .btn-primary, the focus rings and the brand mark in attendyn.css are
 * already written in terms of these tokens, none of them needs a console
 * variant: an organisation's colour reaches them by cascade.
 */

.ad-consolebar,
.ad-console-page,
.ad-orgcard {
  --ad-accent-deep: color-mix(in srgb, var(--ad-accent) 70%, #000);
  --ad-accent-wash: color-mix(in srgb, var(--ad-accent) 12%, #fff);
}

:root[data-bs-theme="dark"] .ad-consolebar,
:root[data-bs-theme="dark"] .ad-console-page,
:root[data-bs-theme="dark"] .ad-orgcard {
  --ad-accent-deep: color-mix(in srgb, var(--ad-accent) 55%, #fff);
  --ad-accent-wash: color-mix(in srgb, var(--ad-accent) 22%, #000);
}

/* Links and focus rings in the organisation's colour too. Bootstrap draws a
   link from --bs-link-color-rgb, which is resolved once at :root -- so with
   nothing here, buttons and tabs took an organisation's blue while every
   plain link stayed the house teal, two brand colours on one page. :where()
   keeps this at element specificity, so any link with a class of its own
   (tabs, crumbs) still sets its own colour. */
:where(.ad-console-page, .ad-consolebar) a:where(:not(.btn)) {
  color: var(--ad-accent-deep);
}

:where(.ad-console-page, .ad-consolebar) a:where(:not(.btn)):hover {
  color: color-mix(in srgb, var(--ad-accent-deep) 78%, var(--ad-ink));
}

.ad-console-page .form-control:focus,
.ad-console-page .form-select:focus,
.ad-console-page .form-check-input:focus {
  border-color: var(--ad-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ad-accent) 18%, transparent);
}

/* A faint wash of the organisation's colour behind the top of every console
   page, so each customer's console is recognisably theirs before a word is
   read -- and the page has some depth instead of one flat grey. Clipped at
   the viewport so the bleed never adds a horizontal scrollbar. */
body.ad-console { overflow-x: clip; }

.ad-console-page { position: relative; isolation: isolate; }

.ad-console-page::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: -1.5rem;
  left: 50%;
  width: 100vw;
  height: 22rem;
  transform: translateX(-50%);
  background:
    radial-gradient(55% 100% at 50% 0%, color-mix(in srgb, var(--ad-accent) 10%, transparent), transparent 70%);
  pointer-events: none;
}

/* Arrival. Cards and rows rise into place in sequence when a page opens --
   short, and only once; nothing moves after that except what the person
   touches. Rows carry their position as --i from the template. */
@keyframes ad-rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@keyframes ad-grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.ad-figure,
.ad-eventrow,
.ad-listcard,
.ad-setup-block,
.ad-importcard {
  animation: ad-rise 0.42s cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 55ms);
}

.ad-figure:nth-child(2) { --i: 1; }
.ad-figure:nth-child(3) { --i: 2; }
.ad-figure:nth-child(4) { --i: 3; }
/* "backwards", not "both": holding the last frame would pin transform and
   override the hover lift for good. */
.ad-figures ~ .ad-eventlist .ad-eventrow { animation-delay: calc((var(--i, 0) + 3) * 55ms); }

@media (prefers-reduced-motion: reduce) {
  .ad-figure, .ad-eventrow, .ad-listcard, .ad-setup-block, .ad-importcard,
  .ad-turnout-fill { animation: none !important; }
  .ad-eventrow:hover, .ad-figure:hover { transform: none !important; }
}

/* ==========================================================================
   The bar: whose data this is, and which section
   ========================================================================== */

/* Sticky for the same reason the deskbar is: the one thing a console page must
   never let you lose track of while scrolling is which organisation you are
   about to change something in. */
.ad-consolebar {
  position: sticky;
  top: 0;
  z-index: 1020;
  background: var(--ad-surface);
  border-bottom: 1px solid var(--ad-line);
  box-shadow: var(--ad-shadow-sm);
}

/* No vertical padding on the row itself: the tabs run its full height so the
   active one's underline can sit on the bar's bottom edge, and the org chip
   centres itself with padding of its own. */
.ad-consolebar-inner {
  display: flex;
  align-items: stretch;
  gap: 0 1rem;
  flex-wrap: wrap;
  min-height: 3.6rem;
}

.ad-orgchip,
.ad-orgswitch { align-self: center; }

.ad-orgchip { padding-block: 0.55rem; }

.ad-orgchip {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  color: var(--ad-ink);
  min-width: 0;
}

.ad-orgchip:hover { color: var(--ad-ink); }

/* The monogram tile, which is the console's one piece of per-customer
   identity: initials standing in for a logo, so set as a logo would be --
   the sans, heavy and tight -- rather than in the face kept for serials. */
.ad-orgchip-mark,
.ad-orgcard-mark {
  display: grid;
  place-items: center;
  flex: none;
  background: var(--ad-accent-deep);
  color: #fff;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.ad-orgchip-mark {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--ad-radius-sm);
  font-size: 0.78rem;
}

.ad-orgchip-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

.ad-orgchip-text b {
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ad-orgswitch {
  font-size: 0.78rem;
  color: var(--ad-mute);
  text-decoration: none;
  border: 1px solid var(--ad-line-strong);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  flex: none;
}

.ad-orgswitch:hover {
  color: var(--ad-ink);
  border-color: var(--ad-ink);
}

/* Reuses .ad-desk-tab from attendyn.css, so the console's sections and an
   event's screens are the same control in two places. */
.ad-console-tabs {
  display: flex;
  gap: 0.15rem;
  margin-left: auto;
  overflow-x: auto;
  scrollbar-width: none;
}

.ad-console-tabs::-webkit-scrollbar { display: none; }

/* An underline rather than the desk's segmented pill. The desks switch between
   modes of one screen, which is what a segmented control says; these are the
   sections of a workspace, which is what a tab rail says -- and the rail lets
   the bar stay one quiet line. */
.ad-console-tabs .ad-desk-tab {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0.75rem 0.15rem;
  margin: 0 0.55rem;
  border-radius: 0;
  font-size: 0.9rem;
}

/* Five tabs on a 390px phone: a little less air between them keeps the last
   one on screen rather than half-hidden behind the scroll edge. */
@media (max-width: 420px) {
  .ad-console-tabs .ad-desk-tab { margin: 0 0.32rem; font-size: 0.86rem; }
}

.ad-console-tabs .ad-desk-tab[aria-current="page"] {
  background: none;
  box-shadow: none;
  color: var(--ad-ink);
}

.ad-console-tabs .ad-desk-tab[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--ad-accent);
}

/* ==========================================================================
   Figures
   ========================================================================== */

.ad-figures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.85rem;
}

@media (min-width: 48rem) {
  .ad-figures { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.ad-figures-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 1.25rem; }

.ad-figure {
  position: relative;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-sm);
  padding: 1rem 1.15rem 0.95rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  overflow: hidden;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

/* A hairline of the accent along the top: enough to tie the figures to the
   organisation, not enough to make four coloured boxes. */
.ad-figure::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--ad-accent), color-mix(in srgb, var(--ad-accent) 25%, transparent));
  opacity: 0.85;
}

.ad-figure:hover { transform: translateY(-2px); box-shadow: var(--ad-shadow-md); }

.ad-figure b {
  font-family: var(--ad-figures);
  font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.15rem);
  font-weight: 620;
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.ad-figure-ok { color: var(--ad-ok); }

.ad-figure-note {
  font-size: 0.78rem;
  color: var(--ad-mute);
}

.ad-figure-note a { color: var(--ad-accent-deep); }

/* ==========================================================================
   The event list: each event a ticket
   ========================================================================== */

/* The list is a size container, so a row lays itself out by the width it
   actually has rather than by the screen's. Keyed to the screen, a row at a
   middling width gave the buttons all the room they wanted and squeezed the
   event's name and date into a sliver that wrapped a word per line under
   them. */
.ad-eventlist {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  container-type: inline-size;
  container-name: eventlist;
}

/* Narrow first: stub on the left, the event beside it, the actions below the
   event. Wide (see the container query) the actions move up beside it. */
.ad-eventrow {
  position: relative;
  display: grid;
  grid-template-columns: 5.25rem minmax(0, 1fr);
  grid-template-areas:
    "stub main"
    "stub links";
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-sm);
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.ad-eventrow:hover {
  transform: translateY(-2px);
  border-color: var(--ad-line-strong);
  box-shadow: var(--ad-shadow-md);
}

@container eventlist (min-width: 54rem) {
  .ad-eventrow {
    grid-template-columns: 5.75rem minmax(0, 1fr) auto;
    grid-template-areas: "stub main links";
    align-items: center;
  }
  .ad-eventrow-links {
    justify-content: flex-end;
    padding: 1rem 1.25rem 1rem 0.5rem;
  }
}

/* The stub: the date, on a soft tint of the organisation's colour. Tinted
   rather than filled, because a block of a strong brand colour down the side
   of every row -- navy, scarlet -- was the loudest thing on the page. */
.ad-eventstub {
  grid-area: stub;
  align-self: stretch;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0.9rem 0.4rem;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--ad-accent) 11%, var(--ad-surface)),
    color-mix(in srgb, var(--ad-accent) 5%, var(--ad-surface))
  );
  color: var(--ad-accent-deep);
  border-right: 1.5px dashed color-mix(in srgb, var(--ad-accent) 30%, var(--ad-line));
  line-height: 1;
}

/* The notches either end of the tear line, cut in the page's own colour. */
.ad-eventstub::before,
.ad-eventstub::after {
  content: "";
  position: absolute;
  right: -0.48rem;
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: var(--ad-paper);
}

.ad-eventstub::before { top: -0.48rem; }
.ad-eventstub::after { bottom: -0.48rem; }

.ad-eventstub-month,
.ad-eventstub-dow {
  font-size: 0.66rem;
  font-weight: 650;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.ad-eventstub-day {
  font-size: 2rem;
  font-weight: 650;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  margin: 0.15rem 0 0.2rem;
  color: var(--ad-ink);
}

.ad-eventstub-dow { opacity: 0.8; }

/* Today's event: an outline and a glow in the organisation's colour, and a
   filled "Today" pill. Marked, not shouted. */
.ad-eventrow.is-today {
  border-color: color-mix(in srgb, var(--ad-accent) 45%, var(--ad-line));
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--ad-accent) 10%, transparent),
    0 10px 28px -12px color-mix(in srgb, var(--ad-accent) 45%, transparent);
}

.ad-eventrow.is-today .ad-eventstub {
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--ad-accent) 20%, var(--ad-surface)),
    color-mix(in srgb, var(--ad-accent) 9%, var(--ad-surface))
  );
}

.ad-eventrow.is-today .ad-eventstub-day { color: var(--ad-accent-deep); }

/* An archived event stays legible rather than being greyed to the edge of
   readable: it is still the thing being reconciled and exported for weeks
   after the gates close. */
.ad-eventrow.is-archived { background: var(--ad-surface-sunk); box-shadow: none; }

.ad-eventrow.is-archived .ad-eventstub {
  background: transparent;
  color: var(--ad-mute);
  border-right-color: var(--ad-line-strong);
}

.ad-eventrow.is-archived .ad-eventstub-day { color: var(--ad-mute); }

.ad-eventrow-main {
  grid-area: main;
  min-width: 0;
  padding: 1rem 1.25rem 0.75rem;
}

.ad-eventrow-head {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
}

.ad-eventrow-head h3 {
  font-size: 1.08rem;
  font-weight: 620;
  letter-spacing: -0.018em;
  margin: 0 0.2rem 0 0;
}

.ad-eventrow-when {
  color: var(--ad-mute);
  font-size: 0.84rem;
  margin: 0.25rem 0 0.7rem;
}

.ad-eventrow-links {
  grid-area: links;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.2rem;
  padding: 0 1rem 1rem 1.1rem;
}

.ad-eventrow-links .btn-primary { margin-right: 0.35rem; }

@container eventlist (max-width: 30rem) {
  .ad-eventrow { grid-template-columns: 4.25rem minmax(0, 1fr); }
  .ad-eventstub-day { font-size: 1.6rem; }
  .ad-eventrow-links .ad-btn-quiet { --bs-btn-padding-x: 0.55rem; }
}

/* The quiet button: a secondary action that reads as one without competing
   with the filled one beside it. Text, a hover wash, no border. */
.ad-btn-quiet {
  --bs-btn-color: var(--ad-ink);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--ad-ink);
  --bs-btn-hover-bg: var(--ad-surface-sunk);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--ad-ink);
  --bs-btn-active-bg: var(--ad-line);
  --bs-btn-active-border-color: transparent;
  font-weight: 560;
}

.ad-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: var(--ad-surface-sunk);
  color: var(--ad-mute);
  box-shadow: inset 0 0 0 1px var(--ad-line);
  white-space: nowrap;
  line-height: 1.2;
}

.ad-tag.is-open,
.ad-tag.is-archived,
.ad-tag.is-live { box-shadow: none; }

.ad-tag.is-open { background: var(--ad-ok-wash); color: var(--ad-ok); }
.ad-tag.is-archived { background: var(--ad-warn-wash); color: var(--ad-warn); }
.ad-tag.is-live { background: var(--ad-accent-deep); color: #fff; }

:root[data-bs-theme="dark"] .ad-tag.is-live { background: var(--ad-accent); color: #04161a; }

.ad-tag.is-live i {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: currentColor;
  animation: ad-live 2s ease-out infinite;
}

@keyframes ad-live {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 60%, transparent); }
  70%, 100% { box-shadow: 0 0 0 0.35rem transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .ad-tag.is-live i { animation: none; }
}

/* Turnout. The bar is the figure somebody scanning this list is looking for;
   the count beside it is for the one who needs the actual number. */
.ad-turnout {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.ad-turnout-track {
  flex: 1 1 auto;
  max-width: 22rem;
  height: 0.4rem;
  background: var(--ad-surface-sunk);
  border-radius: 999px;
  overflow: hidden;
  min-width: 4rem;
}

.ad-eventrow.is-archived .ad-turnout-track { background: var(--ad-line); }

.ad-turnout-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, color-mix(in srgb, var(--ad-accent) 70%, var(--ad-surface)), var(--ad-accent));
  border-radius: 999px;
  transform-origin: left center;
  animation: ad-grow 0.9s cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 55ms + 150ms);
}

.ad-turnout-count {
  font-size: 0.8rem;
  color: var(--ad-mute);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.ad-turnout-count b { color: var(--ad-ink); font-weight: 620; }

/* ==========================================================================
   The switcher: an organisation drawn as a credential
   ========================================================================== */

.ad-switcher { max-width: 46rem; margin: 0 auto; }

.ad-orgcards {
  display: grid;
  gap: 0.8rem;
}

.ad-orgcard { margin: 0; }

/* A button, not a link, because choosing an organisation is a POST -- it
   changes what the session is pointed at. Stripped back to the card so that
   the whole credential is the hit area. */
.ad-orgcard-hit {
  display: flex;
  align-items: stretch;
  gap: 0;
  width: 100%;
  padding: 0;
  text-align: left;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, transform 0.06s ease;
}

.ad-orgcard-hit:hover {
  border-color: var(--ad-accent);
  box-shadow: var(--ad-shadow-md);
}

.ad-orgcard-hit:active { transform: translateY(1px); }

.ad-orgcard-mark {
  width: 4.2rem;
  font-size: 1.15rem;
  border-radius: 0;
}

.ad-orgcard-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.1rem;
  padding: 1rem 1.15rem;
  flex: 1 1 auto;
  min-width: 0;
}

.ad-orgcard-body b {
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: -0.014em;
}

.ad-orgcard-meta { font-size: 0.8rem; color: var(--ad-mute); }

/* The perforation, borrowed from the attendee's ticket stub in attendyn.css.
   It is the one piece of ornament in the console and it is doing a job: this
   screen is where somebody picks up a pass, and the product's own vocabulary
   for "a pass" is a tearable stub. */
.ad-orgcard-stub {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  padding: 0 1.1rem;
  border-left: 1px dashed var(--ad-line-strong);
  background: var(--ad-surface-sunk);
}

.ad-orgcard-stub::before,
.ad-orgcard-stub::after {
  content: "";
  position: absolute;
  left: -0.42rem;
  width: 0.84rem;
  height: 0.84rem;
  border-radius: 50%;
  background: var(--ad-paper);
}

.ad-orgcard-stub::before { top: -0.42rem; }
.ad-orgcard-stub::after { bottom: -0.42rem; }

.ad-switcher-foot { margin-top: 1.5rem; }

/* ==========================================================================
   Cards, forms and the shared furniture
   ========================================================================== */

.ad-narrow { max-width: 34rem; margin: 0 auto; }

.ad-card {
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  padding: 1.4rem 1.5rem;
}

/* Not .ad-field. That name is on EasyList's generic cosmetic rules -- it is
   an advertising container on enough sites that blockers hide it everywhere --
   and this project has already had a registration page come up blank because
   of it. See event/tests/test_ad_blocker_safety.py, which fails if it returns.
   Form rows and page chrome stay outside the ad- prefix for that reason. */
.field-row { margin-bottom: 1.1rem; }

.field-row > label,
.field-row > .field-label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}

.field-row small {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.78rem;
  color: var(--ad-mute);
}

.ad-error {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  color: var(--ad-stop);
}

.ad-slug-preview {
  font-family: var(--ad-mono);
  font-size: 0.85rem;
  color: var(--ad-mute);
  margin-bottom: 0.35rem;
}

.ad-slug-preview b { color: var(--ad-ink); }

.ad-colour-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* A native colour input is a text box the width of the page unless told
   otherwise, which looks like a mistake next to the other fields. */
.ad-colour-input {
  width: 3.2rem;
  height: 2.4rem;
  padding: 0.2rem;
  flex: none;
}

.ad-settings,
.ad-people {
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  align-items: start;
}

@media (max-width: 55rem) {
  .ad-settings,
  .ad-people { grid-template-columns: minmax(0, 1fr); }
}

.ad-plan h3,
.ad-invite h3 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0.2rem 0 0.9rem;
}

.ad-plan dl,
.ad-rolekey dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.9rem;
  margin: 0 0 0.9rem;
  font-size: 0.82rem;
}

.ad-plan dt,
.ad-rolekey dt { color: var(--ad-mute); }

.ad-plan dd,
.ad-rolekey dd { margin: 0; }

.ad-plan p,
.ad-rolekey { font-size: 0.78rem; color: var(--ad-mute); }

.ad-rolekey {
  margin-top: 1.2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--ad-line);
}

.ad-rolekey dl { grid-template-columns: 1fr; gap: 0.15rem; }
.ad-rolekey dt { color: var(--ad-ink); font-weight: 600; margin-top: 0.45rem; }

/* ==========================================================================
   The member list
   ========================================================================== */

.ad-memberlist {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.ad-memberlist th {
  text-align: left;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ad-mute);
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--ad-line);
}

.ad-memberlist td {
  padding: 0.7rem 0.5rem 0.7rem 0;
  border-bottom: 1px solid var(--ad-line);
  vertical-align: top;
}

.ad-memberlist tr:last-child td { border-bottom: 0; }

.ad-col-right { text-align: right; }

/* Revoked, not removed. The row stays because "who used to have access" is a
   question an audit asks, and deleting the membership would throw away the
   answer along with who granted it. */
.ad-memberlist tr.is-revoked { opacity: 0.55; }

.ad-member-email {
  display: block;
  font-size: 0.76rem;
  color: var(--ad-mute);
}

.ad-role { font-weight: 600; }

.ad-member-locked {
  display: block;
  font-size: 0.76rem;
  color: var(--ad-mute);
}

.ad-rolechange {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.45rem;
  flex-wrap: wrap;
}

/* Sized to its content, with a floor: the longest label is "Gate steward",
   and a select that shrinks to the shortest one puts the chevron through the
   text of the others. */
.ad-rolechange .form-select {
  width: auto;
  min-width: 10.5rem;
}

.ad-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.76rem;
  color: var(--ad-mute);
  margin: 0;
  white-space: nowrap;
}

/* ==========================================================================
   Empty states
   ========================================================================== */

.ad-empty {
  background: var(--ad-surface);
  border: 1px dashed var(--ad-line-strong);
  border-radius: var(--ad-radius-lg);
  padding: 2.2rem 1.5rem;
  text-align: center;
}

.ad-empty h3 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 0.4rem;
}

.ad-empty p {
  color: var(--ad-mute);
  font-size: 0.88rem;
  max-width: 28rem;
  margin: 0 auto 1.1rem;
}

/* The invite form's confirmation step. Amber rather than red: nothing has
   gone wrong, the form is asking a question whose wrong answer is expensive.
   See tenancy.forms.MemberInviteForm. */
.ad-confirm {
  margin: -0.4rem 0 1rem;
  padding: 0.5rem 0.7rem;
  font-size: 0.8rem;
  font-weight: 560;
  color: var(--ad-warn);
  background: var(--ad-warn-wash);
  border-radius: var(--ad-radius-sm);
}

/* ---------------------------------------------------------------------------
   One event's pages: a header with the event's own tabs.
   --------------------------------------------------------------------------- */

.ad-eventhead { margin-bottom: 1.5rem; }

.ad-crumb {
  display: inline-flex;
  gap: 0.35rem;
  font-size: 0.84rem;
  font-weight: 550;
  color: var(--ad-mute);
  text-decoration: none;
  margin-bottom: 0.6rem;
}

.ad-crumb:hover { color: var(--ad-ink); }

.ad-eventhead-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.ad-eventhead-title { min-width: 0; }

.ad-eventhead h1 {
  font-size: clamp(1.7rem, 1.3rem + 1.5vw, 2.35rem);
  font-weight: 650;
  letter-spacing: -0.035em;
  margin: 0 0 0.35rem;
}

.ad-eventhead-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  color: var(--ad-mute);
  font-size: 0.9rem;
  margin: 0;
}

.ad-eventhead-public { margin-top: 0.35rem; }

.ad-eventtabs {
  display: flex;
  gap: 1.4rem;
  margin-top: 1.25rem;
  border-bottom: 1px solid var(--ad-line);
  overflow-x: auto;
  scrollbar-width: none;
}

.ad-eventtabs::-webkit-scrollbar { display: none; }

.ad-eventtabs a {
  position: relative;
  padding: 0.6rem 0.05rem 0.7rem;
  font-size: 0.92rem;
  font-weight: 560;
  color: var(--ad-mute);
  text-decoration: none;
  white-space: nowrap;
}

.ad-eventtabs a:hover { color: var(--ad-ink); }

.ad-eventtabs a[aria-current="page"] { color: var(--ad-ink); }

.ad-eventtabs a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--ad-accent);
}

.ad-section-title {
  font-size: 1.15rem;
  font-weight: 620;
  margin: 0 0 1rem;
}

/* ---------------------------------------------------------------------------
   Signing in.

   A split card: the house identity on the left, the form on the right. The
   console's other screens carry an organisation's accent; this one cannot,
   because which organisation is exactly what is not known until somebody has
   signed in -- so the panel uses the front page's own dark teal, and the two
   doors into the product look like one building. A phone gets the form alone.

   `field-row` rather than an `ad-`prefixed name on the wrappers: EasyList
   hides .ad-field on every site, which once took the inputs off a form while
   leaving the labels behind.
   --------------------------------------------------------------------------- */

.ad-signin {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  max-width: 56rem;
  margin: clamp(1.5rem, 6vh, 4rem) auto;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: calc(var(--ad-radius-lg) + 4px);
  box-shadow: var(--ad-shadow-lg);
  overflow: hidden;
}

.ad-signin-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
  padding: 2.25rem;
  color: #e6f5f3;
  background:
    radial-gradient(120% 80% at 100% 0%, rgba(45, 212, 191, 0.32), transparent 60%),
    linear-gradient(160deg, #0b3b39 0%, #072523 55%, #04161a 100%);
  overflow: hidden;
}

.ad-signin-mark { width: 3rem; height: auto; }
.ad-signin-mark .ad-mark-a { fill: rgba(94, 234, 212, 0.55); }
.ad-signin-mark .ad-mark-b { fill: #2dd4bf; }

.ad-signin-claim {
  font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2rem);
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -0.03em;
  max-width: 14ch;
  margin: 0;
  color: #fff;
}

/* A pass, drawn in outline: stub, perforation, lines of type. */
.ad-signin-pass {
  display: flex;
  width: 15rem;
  height: 5.5rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 0.8rem;
  transform: rotate(-6deg) translateX(1rem);
  transform-origin: left bottom;
}

.ad-signin-pass-stub {
  width: 4.25rem;
  border-right: 1px dashed rgba(255, 255, 255, 0.35);
  background:
    linear-gradient(rgba(94, 234, 212, 0.55) 0 0) 50% 50% / 1.6rem 1.6rem no-repeat;
  border-radius: 0.8rem 0 0 0.8rem;
}

.ad-signin-pass-lines {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.5rem;
  padding: 0 1rem;
  flex: 1;
}

.ad-signin-pass-lines i {
  display: block;
  height: 0.35rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.3);
}

.ad-signin-pass-lines i:nth-child(1) { width: 80%; background: rgba(255, 255, 255, 0.55); }
.ad-signin-pass-lines i:nth-child(2) { width: 55%; }
.ad-signin-pass-lines i:nth-child(3) { width: 65%; }

.ad-signin-body { padding: clamp(1.75rem, 4vw, 2.75rem); }

.ad-signin .ad-page-head {
  display: block;
  margin-bottom: 1.5rem;
}

.ad-signin .ad-page-head h1 {
  font-size: 1.85rem;
  font-weight: 650;
  letter-spacing: -0.035em;
  margin-bottom: 0.3rem;
}

.ad-signin .ad-page-head p { margin-bottom: 0; }

.ad-signin-form .field-row + .field-row { margin-top: 1rem; }

.ad-signin-form .btn-primary { margin-top: 1.5rem; padding-block: 0.7rem; }

.ad-signin-aside {
  margin-top: 1.5rem;
  margin-bottom: 0;
  font-size: 0.84rem;
}

@media (max-width: 46rem) {
  .ad-signin {
    grid-template-columns: 1fr;
    max-width: 26rem;
  }
  .ad-signin-panel { display: none; }
}

/* ---------------------------------------------------------------------------
   Setting an event up.

   Laid out as settings pages are: each group's name and purpose on the left,
   its fields in a card on the right. The groups are EventAdmin's own
   fieldsets, which were the right grouping all along -- only the gate in front
   of them was wrong.

   Days and questions are tables, not stacked cards, and take the full width:
   rows of the same shape, read against each other.
   --------------------------------------------------------------------------- */

.ad-setup-block {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.9rem 2.5rem;
  padding: 1.6rem 0;
  border-top: 1px solid var(--ad-line);
}

.ad-setup-block:first-of-type { border-top: 0; padding-top: 0.25rem; }

@media (min-width: 62rem) {
  .ad-setup-block:not(.is-wide) { grid-template-columns: 16rem minmax(0, 1fr); }
}

.ad-setup-intro h2 {
  margin: 0 0 0.3rem;
  font-size: 1.02rem;
  font-weight: 620;
  letter-spacing: -0.015em;
}

.ad-setup-intro p {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ad-mute);
  max-width: 34rem;
}

.ad-setup-fields {
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-sm);
  padding: 1.25rem 1.35rem;
  min-width: 0;
}

.ad-setup-fields > .field-row:last-child,
.ad-setup-fields .ad-setup-pair .field-row { margin-bottom: 0; }

.ad-setup-fields .form-text { margin: 0.35rem 0 0; }

/* Two abreast where there is room, stacked on a phone. The pairs are the
   fields that are read together -- a window has two ends, and stock has two
   kinds. */
.ad-setup-pair {
  display: grid;
  gap: 1rem 1.25rem;
  grid-template-columns: 1fr;
}

.ad-setup-fields > .field-row + .ad-setup-pair { margin-top: 1.1rem; }

@media (min-width: 42rem) {
  .ad-setup-pair { grid-template-columns: 1fr 1fr; }
}

/* A switch with its explanation underneath, set apart from the inputs by a
   rule when it shares a card with them. */
.ad-switchrow {
  padding-left: 2.9em;
  margin: 0;
}

.ad-switchrow .form-check-input {
  width: 2.3em;
  height: 1.25em;
  margin-left: -2.9em;
  margin-top: 0.1em;
  cursor: pointer;
}

.ad-switchrow .form-check-label { font-weight: 580; cursor: pointer; }

.ad-switchrow + .ad-setup-pair,
.ad-setup-pair + .ad-switchrow {
  margin-top: 1.15rem;
  padding-top: 1.15rem;
  border-top: 1px solid var(--ad-line);
}

.ad-setup-pair + .ad-switchrow { padding-left: 2.9em; }

.ad-setup-tablecard { padding: 0.35rem 0.6rem; }

.ad-setup-table {
  margin: 0;
  font-size: 0.9rem;
  --bs-table-bg: transparent;
}

.ad-setup-table th {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--ad-mute);
  white-space: nowrap;
  border-bottom-color: var(--ad-line);
  padding-top: 0.75rem;
}

.ad-setup-table td {
  vertical-align: top;
  border-bottom-color: var(--ad-line);
}

.ad-setup-table tbody tr:last-child td { border-bottom: 0; }

/* Wide enough that a label and a comma-separated option list are both
   readable, narrow enough that five columns still fit on a laptop. */
.ad-setup-table .form-control,
.ad-setup-table .form-select {
  min-width: 7rem;
  padding: 0.45rem 0.65rem;
  font-size: 0.92rem;
}

.ad-setup-table .form-select { padding-right: 2rem; }

.ad-setup-table textarea.form-control { min-width: 11rem; }

.ad-setup-table input[type="number"] { max-width: 6rem; }

.ad-setup-table .form-check-input { margin-top: 0.7rem; }

.ad-setup-remove {
  width: 1%;
  text-align: center;
}

.ad-setup .errorlist,
.ad-importcard .errorlist {
  list-style: none;
  padding: 0;
  margin: 0.35rem 0 0;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ad-stop);
}

/* Stuck to the bottom of the window: the form is long, and the button that
   ends it should not be a scroll away from the field just changed. */
.ad-setup > .ad-setup-actions {
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin: 0.5rem -1rem 0;
  padding: 0.85rem 1rem;
  background: color-mix(in srgb, var(--ad-paper) 86%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--ad-line);
}

.ad-setup-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  padding-top: 1.25rem;
}

/* ---------------------------------------------------------------------------
   The guest list.

   Three figures first, because how many are coming, here, and still expected
   is the question somebody opens this page with. Then one card: the search
   and the actions as its toolbar, the people as its body.
   --------------------------------------------------------------------------- */

.ad-listcard {
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-sm);
  overflow: hidden;
}

.ad-listbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.9rem 1rem;
  border-bottom: 1px solid var(--ad-line);
}

.ad-attendee-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  flex: 1 1 28rem;
}

.ad-attendee-tools .form-control,
.ad-attendee-tools .form-select,
.ad-attendee-tools .btn { padding-block: 0.48rem; font-size: 0.92rem; }

.ad-searchbox {
  flex: 1 1 14rem;
  padding-left: 2.2rem !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23556274' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 0.75rem 50%;
  background-size: 1rem;
}

.ad-attendee-tools .form-select { flex: 0 1 10rem; }

.ad-attendee-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
}

.ad-listcount {
  margin: 0;
  padding: 0.75rem 1rem 0;
  font-size: 0.8rem;
  color: var(--ad-mute);
}

.ad-attendee-table {
  margin: 0;
  font-size: 0.92rem;
  --bs-table-bg: transparent;
}

.ad-attendee-table th {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--ad-mute);
  padding: 0.7rem 1rem;
  border-bottom-color: var(--ad-line);
}

.ad-attendee-table td {
  vertical-align: middle;
  padding: 0.6rem 1rem;
  border-bottom-color: var(--ad-line);
}

.ad-attendee-table tbody tr:last-child td { border-bottom: 0; }

.ad-attendee-table tbody tr:hover td { background: color-mix(in srgb, var(--ad-surface-sunk) 60%, transparent); }

.ad-person {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
}

.ad-initials {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  background: var(--ad-surface-sunk);
  color: var(--ad-mute);
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.02em;
}

.ad-person-name { font-weight: 560; }

.ad-muted-cell { color: var(--ad-mute); }

/* A phone has room for who and whether, not for the address as well. */
@media (max-width: 40rem) {
  .ad-col-wide { display: none; }
  .ad-figures-3 .ad-figure { padding: 0.8rem 0.85rem; }
}

.ad-state {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  color: var(--ad-mute);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.ad-state::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 1.5px solid var(--ad-line-strong);
}

.ad-state.is-in { color: var(--ad-ok); font-weight: 560; }

.ad-state.is-in::before { background: var(--ad-ok); border-color: var(--ad-ok); }

.ad-listcard .ad-empty { border: 0; border-radius: 0; box-shadow: none; }

.ad-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 0.9rem;
  border-top: 1px solid var(--ad-line);
  font-size: 0.85rem;
  color: var(--ad-mute);
}

/* ---------------------------------------------------------------------------
   Importing a list: the column guide and the upload, side by side.
   --------------------------------------------------------------------------- */

.ad-import {
  display: grid;
  gap: 1.25rem;
  align-items: start;
}

@media (min-width: 62rem) {
  .ad-import { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
}

.ad-importcard {
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-sm);
  padding: 1.25rem 1.35rem;
}

.ad-importcard > h2 {
  font-size: 1rem;
  font-weight: 620;
  margin: 0 0 0.35rem;
}

.ad-importcard .table { --bs-table-bg: transparent; margin: 0.75rem 0; }

.import-columns code {
  white-space: nowrap;
}

/* The owner block on the new-organisation form: set apart by a rule, because
   it is the question that decides who can read the organisation's data. */
.ad-owner {
  margin: 1.5rem 0 1.25rem;
  padding: 1.25rem 0 0;
  border: 0;
  border-top: 1px solid var(--ad-line);
}

.ad-owner legend {
  float: none;
  width: auto;
  font-size: 1rem;
  font-weight: 620;
  margin: 0 0 0.25rem;
}

.ad-owner > .form-text { margin: 0 0 1rem; }

/* The activity log, stacked on a phone: three columns of a date, an address
   and a sentence do not fit 390px, and a sideways-scrolling record is one
   nobody reads to the end. Each entry becomes a short block instead. */
@media (max-width: 640px) {
  .ad-activity thead { display: none; }
  .ad-activity,
  .ad-activity tbody,
  .ad-activity tr,
  .ad-activity td { display: block; width: 100%; }
  .ad-activity tr { padding: 0.75rem 0; border-bottom: 1px solid var(--ad-line); }
  .ad-activity td { padding: 0.1rem 0; border: 0; overflow-wrap: anywhere; }
  .ad-activity td:first-child { font-size: 0.78rem; color: var(--ad-mute); }
}

/* -- Subscription standing ----------------------------------------------
   The notice the shell shows when a trial or payment is ending or has
   lapsed. Warn by default, stop when something is actually paused. */

.ad-standing {
  margin: 0 0 1.1rem;
  padding: 0.7rem 0.95rem;
  border-radius: var(--ad-radius-sm);
  border: 1px solid color-mix(in srgb, var(--ad-warn) 30%, transparent);
  background: var(--ad-warn-wash);
  color: var(--ad-ink);
  font-size: 0.86rem;
}

.ad-standing-stop {
  border-color: color-mix(in srgb, var(--ad-stop) 30%, transparent);
  background: var(--ad-stop-wash);
}

.ad-plan .ad-plan-standing { margin: -0.5rem 0 0.9rem; }
