/*
 * AttenDyn visual identity.
 *
 * Loaded after Bootstrap 5.3, and works mostly by retargeting Bootstrap's own
 * custom properties so existing utility classes inherit the identity instead
 * of every template needing bespoke markup. Bespoke components live at the
 * bottom, under "Components".
 *
 * The subject is a pass system -- badges, QR stubs, serial numbers, a door --
 * so serial data (EK numbers, counts, times) is set in tabular monospace, and
 * the ticket screen carries a real perforation. Two densities share one
 * identity: a calm centred column for attendees on phones, and the dense,
 * high-contrast `.desk` layout for staff working a door for hours.
 *
 * On colour: teal on a cool slate ramp, the same identity the admin and the
 * sign-in page carry, so a person crossing between the three surfaces stays in
 * one product. One accent carries identity and the primary action; amber, red
 * and green stay reserved for meaning.
 *
 * The teal comes in two stops and they are not interchangeable:
 *
 *   --ad-accent       fills, borders, focus rings, chart series, the brand.
 *                     Never small type, never a filled button under white.
 *   --ad-accent-deep  teal that is *type*, and the ground of a filled accent
 *                     button. Light mode darkens it to clear 4.5:1 on white,
 *                     on paper and on its own wash; dark mode brightens it
 *                     instead, for the same reason.
 *
 * That split is why --ad-accent is not simply used everywhere: at #0d9488 it
 * measures 3.74:1 on white, which is fine behind a 2.25rem figure and not fine
 * under a 0.72rem badge.
 */

/* ==========================================================================
   Tokens
   ========================================================================== */

:root {
  /* A cool slate ramp. Neutrals are biased a few degrees toward the accent
     rather than being a dead grey, so surfaces and type read as belonging to
     the same family -- and warm greys under a teal accent read muddy. */
  --ad-ink: #0f172a;
  --ad-paper: #f6f9fa;
  --ad-surface: #ffffff;
  --ad-surface-sunk: #eef2f5;
  --ad-line: #e4eaee;
  --ad-line-strong: #cbd5dd;
  /* Darker than the admin's slate mute. The same token here sets 0.7rem
     eyebrows and form hints that get read on a phone held at a door in
     daylight, and it has to clear 4.5:1 on the sunk surface too, where the
     admin's #64748b measured 4.23. */
  --ad-mute: #556274;

  /* One accent, used for identity and the primary action only. Two stops --
     see the note at the top of this file for which goes where. */
  --ad-accent: #0d9488;
  --ad-accent-deep: #0a736a;
  --ad-accent-wash: #e6f5f3;
  --ad-accent-rgb: 13, 148, 136;

  /* Semantic state, kept strictly separate from the accent. Teal sits closer
     to the green that means "checked in" than the indigo it replaced did, so
     the separation that used to be automatic is now held by hand: the accent
     is blue-green and desaturated, the outcome green is yellow-green and
     brighter, and the two never sit in the same component. If they are ever
     forced together, move the accent -- green is the one carrying meaning.
     These three values are deliberately unchanged by the teal retarget: they
     already clear AA on the new grounds, and "checked in" is the last colour
     in this app that should shift under anyone working a door. */
  --ad-ok: #14724a;
  --ad-ok-wash: #e3f3ea;
  --ad-warn: #97600f;
  --ad-warn-wash: #fbf0dc;
  --ad-stop: #b02a21;
  --ad-stop-wash: #fbe9e7;

  --ad-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04);
  --ad-shadow-md: 0 2px 4px rgba(15, 23, 42, 0.05), 0 8px 24px rgba(15, 23, 42, 0.07);
  --ad-shadow-lg: 0 4px 8px rgba(15, 23, 42, 0.06), 0 20px 48px rgba(15, 23, 42, 0.11);

  --ad-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --ad-mono: ui-monospace, SFMono-Regular, "SF Mono", "JetBrains Mono", Menlo,
    Consolas, "Liberation Mono", monospace;

  --ad-radius: 10px;
  --ad-radius-sm: 7px;
  --ad-radius-lg: 16px;

  /* The selected tab has to sit *above* its strip. In light that means the
     strip recedes and the tab is the page's brightest surface; in dark the
     relationship inverts, so both are named rather than hard-coded. */
  --ad-tabstrip: var(--ad-surface-sunk);
  --ad-tab-active: var(--ad-surface);
}

/* Dark is not an afterthought here: door staff often work a dim room, and the
   desk screens are the ones on for hours. Tokens flip; components never
   define a colour of their own outside this block and its light counterpart. */
:root[data-bs-theme="dark"] {
  --ad-ink: #e6edf0;
  --ad-paper: #0a1216;
  --ad-surface: #111d23;
  --ad-surface-sunk: #17262d;
  --ad-line: #1f313a;
  --ad-line-strong: #2f4650;
  --ad-mute: #90a4ac;

  /* The deep stop inverts here: on a dark ground the type-safe teal is the
     brighter one, so --ad-accent-deep stays "the one you set type in" and
     changes direction rather than changing job. */
  --ad-accent: #2dd4bf;
  --ad-accent-deep: #5eead4;
  --ad-accent-wash: #10312f;
  --ad-accent-rgb: 45, 212, 191;

  --ad-ok: #3fbf86;
  --ad-ok-wash: #14281f;
  --ad-warn: #d9a441;
  --ad-warn-wash: #2a2216;
  --ad-stop: #f4796b;
  --ad-stop-wash: #2e1a19;

  --ad-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --ad-shadow-md: 0 2px 4px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.36);
  --ad-shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.34), 0 20px 48px rgba(0, 0, 0, 0.46);

  --ad-tabstrip: var(--ad-paper);
  --ad-tab-active: var(--ad-surface-sunk);
}

/* ==========================================================================
   Bootstrap retargeting
   ========================================================================== */

:root,
:root[data-bs-theme="dark"] {
  --bs-font-sans-serif: var(--ad-sans);
  --bs-font-monospace: var(--ad-mono);

  --bs-body-bg: var(--ad-paper);
  --bs-body-color: var(--ad-ink);
  --bs-emphasis-color: var(--ad-ink);
  --bs-secondary-color: var(--ad-mute);
  --bs-tertiary-bg: var(--ad-surface-sunk);
  --bs-border-color: var(--ad-line);
  --bs-border-radius: var(--ad-radius);
  --bs-border-radius-sm: var(--ad-radius-sm);
  --bs-border-radius-lg: var(--ad-radius-lg);

  --bs-primary: var(--ad-accent);
  --bs-primary-rgb: var(--ad-accent-rgb);
  /* Link text is type, so it takes the deep stop. The hover mixes toward the
     ink, which darkens it in light and brightens it in dark -- one rule that
     gains contrast in both themes instead of losing it in one. */
  --bs-link-color: var(--ad-accent-deep);
  --bs-link-hover-color: color-mix(in srgb, var(--ad-accent-deep) 78%, var(--ad-ink));
  --bs-link-color-rgb: var(--ad-accent-rgb);

  --bs-focus-ring-color: rgba(var(--ad-accent-rgb), 0.3);
}

body {
  background-color: var(--ad-paper);
  color: var(--ad-ink);
  font-family: var(--ad-sans);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.005em;
}

/* -- Type scale ----------------------------------------------------------- */

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-weight: 640;
  letter-spacing: -0.021em;
  text-wrap: balance;
  color: var(--ad-ink);
}

h1, .h1 { font-size: clamp(1.85rem, 1.4rem + 1.6vw, 2.5rem); letter-spacing: -0.03em; }
h2, .h2 { font-size: 1.6rem; }
h3, .h3 { font-size: 1.35rem; }
h4, .h4 { font-size: 1.15rem; }
h5, .h5 { font-size: 1.03rem; }
h6, .h6 { font-size: 0.9rem; }

p { text-wrap: pretty; }

.text-muted { color: var(--ad-mute) !important; }

/* -- Cards ---------------------------------------------------------------- */

.card {
  --bs-card-bg: var(--ad-surface);
  --bs-card-border-color: var(--ad-line);
  --bs-card-border-radius: var(--ad-radius-lg);
  --bs-card-inner-border-radius: calc(var(--ad-radius-lg) - 1px);
  --bs-card-cap-bg: transparent;
  --bs-card-color: var(--ad-ink);
  box-shadow: var(--ad-shadow-sm);
}

.card-header {
  border-bottom-color: var(--ad-line);
  font-size: 0.72rem;
  font-weight: 660;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ad-mute);
  padding-block: 0.85rem;
}

.shadow-sm { box-shadow: var(--ad-shadow-md) !important; }

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

.btn {
  --bs-btn-font-weight: 560;
  --bs-btn-border-radius: var(--ad-radius-sm);
  --bs-btn-padding-x: 1.15rem;
  --bs-btn-padding-y: 0.55rem;
  letter-spacing: -0.008em;
  transition: transform 0.06s ease, background-color 0.12s ease,
    border-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}

.btn:active { transform: translateY(1px); }

/* The one filled accent button on the public side: Register, Download pass,
   Check in. It takes the deep stop rather than the accent, because white on
   #0d9488 is 3.74:1 and this is 1rem type someone reads outdoors. */
.btn-primary {
  --bs-btn-bg: var(--ad-accent-deep);
  --bs-btn-border-color: var(--ad-accent-deep);
  --bs-btn-hover-bg: color-mix(in srgb, var(--ad-accent-deep) 86%, black);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--ad-accent-deep) 86%, black);
  --bs-btn-active-bg: color-mix(in srgb, var(--ad-accent-deep) 78%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--ad-accent-deep) 78%, black);
  --bs-btn-disabled-bg: var(--ad-accent-deep);
  --bs-btn-disabled-border-color: var(--ad-accent-deep);
  --bs-btn-color: #fff;
  --bs-btn-hover-color: #fff;
  --bs-btn-active-color: #fff;
  --bs-btn-disabled-color: #fff;
}

/* Dark inverts the pair: the ground is the accent and brightens on hover, and
   the label goes to the page ground rather than to white. */
:root[data-bs-theme="dark"] .btn-primary {
  --bs-btn-bg: var(--ad-accent);
  --bs-btn-border-color: var(--ad-accent);
  --bs-btn-hover-bg: var(--ad-accent-deep);
  --bs-btn-hover-border-color: var(--ad-accent-deep);
  --bs-btn-active-bg: var(--ad-accent-deep);
  --bs-btn-active-border-color: var(--ad-accent-deep);
  --bs-btn-disabled-bg: var(--ad-accent);
  --bs-btn-disabled-border-color: var(--ad-accent);
  --bs-btn-color: #0a1216;
  --bs-btn-hover-color: #0a1216;
  --bs-btn-active-color: #0a1216;
  --bs-btn-disabled-color: #0a1216;
}

.btn-success {
  --bs-btn-bg: var(--ad-ok);
  --bs-btn-border-color: var(--ad-ok);
  --bs-btn-hover-bg: color-mix(in srgb, var(--ad-ok) 86%, black);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--ad-ok) 86%, black);
  --bs-btn-active-bg: color-mix(in srgb, var(--ad-ok) 80%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--ad-ok) 80%, black);
  --bs-btn-disabled-bg: var(--ad-ok);
  --bs-btn-disabled-border-color: var(--ad-ok);
}

:root[data-bs-theme="dark"] .btn-success {
  --bs-btn-color: #0a1216;
  --bs-btn-hover-color: #0a1216;
  --bs-btn-active-color: #0a1216;
  --bs-btn-disabled-color: #0a1216;
  --bs-btn-hover-bg: color-mix(in srgb, var(--ad-ok) 86%, white);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--ad-ok) 86%, white);
}

.btn-outline-primary {
  --bs-btn-color: var(--ad-accent-deep);
  --bs-btn-border-color: color-mix(in srgb, var(--ad-accent) 45%, transparent);
  --bs-btn-hover-bg: var(--ad-accent-deep);
  --bs-btn-hover-border-color: var(--ad-accent-deep);
  --bs-btn-active-bg: color-mix(in srgb, var(--ad-accent-deep) 86%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--ad-accent-deep) 86%, black);
}

:root[data-bs-theme="dark"] .btn-outline-primary {
  --bs-btn-hover-bg: var(--ad-accent);
  --bs-btn-hover-border-color: var(--ad-accent);
  --bs-btn-active-bg: var(--ad-accent-deep);
  --bs-btn-active-border-color: var(--ad-accent-deep);
  --bs-btn-hover-color: #0a1216;
  --bs-btn-active-color: #0a1216;
}

.btn-outline-secondary {
  --bs-btn-color: var(--ad-ink);
  --bs-btn-border-color: var(--ad-line-strong);
  --bs-btn-hover-bg: var(--ad-surface-sunk);
  --bs-btn-hover-border-color: var(--ad-line-strong);
  --bs-btn-hover-color: var(--ad-ink);
  --bs-btn-active-bg: var(--ad-surface-sunk);
  --bs-btn-active-border-color: var(--ad-mute);
  --bs-btn-active-color: var(--ad-ink);
}

.btn-outline-warning {
  --bs-btn-color: var(--ad-warn);
  --bs-btn-border-color: color-mix(in srgb, var(--ad-warn) 45%, transparent);
  --bs-btn-hover-bg: var(--ad-warn);
  --bs-btn-hover-border-color: var(--ad-warn);
  --bs-btn-hover-color: #fff;
  --bs-btn-active-bg: var(--ad-warn);
  --bs-btn-active-border-color: var(--ad-warn);
  --bs-btn-active-color: #fff;
}

:root[data-bs-theme="dark"] .btn-outline-warning {
  --bs-btn-hover-color: #0a1216;
  --bs-btn-active-color: #0a1216;
}

/* -- Forms ---------------------------------------------------------------- */

/* Crispy wraps option sets in a fieldset, so the question arrives as a
   <legend> rather than a label. Both are the same thing to a reader and must
   look the same. */
.form-label,
fieldset legend {
  font-size: 0.78rem;
  font-weight: 620;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--ad-mute);
  margin-bottom: 0.4rem;
}

fieldset legend {
  float: none;
  width: auto;
  padding: 0;
  line-height: 1.4;
}

/* Crispy stamps the legend with .fs-6, which carries !important, so matching
   the surrounding labels needs the same weapon. */
fieldset > legend.form-label {
  font-size: 0.78rem !important;
}

.form-control,
.form-select {
  background-color: var(--ad-surface);
  border-color: var(--ad-line-strong);
  border-radius: var(--ad-radius-sm);
  color: var(--ad-ink);
  padding: 0.62rem 0.85rem;
  font-size: 1rem;
}

.form-control::placeholder { color: color-mix(in srgb, var(--ad-mute) 72%, transparent); }

/* Crispy renders the required marker glued to the label text. Give it air and
   a colour of its own so an uppercase label does not read as "NAME STAR". */
.asteriskField {
  margin-left: 0.25em;
  color: var(--ad-stop);
  font-weight: 700;
}

.form-control:focus,
.form-select:focus {
  background-color: var(--ad-surface);
  border-color: var(--ad-accent);
  color: var(--ad-ink);
  box-shadow: 0 0 0 3px rgba(var(--ad-accent-rgb), 0.16);
}

.form-control.is-invalid,
.form-select.is-invalid {
  border-color: var(--ad-stop);
  box-shadow: none;
}

.form-control.is-invalid:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ad-stop) 20%, transparent);
}

.form-text {
  color: var(--ad-mute);
  font-size: 0.82rem;
}

.invalid-feedback {
  color: var(--ad-stop);
  font-size: 0.82rem;
  font-weight: 500;
}

.form-check-input {
  border-color: var(--ad-line-strong);
  width: 1.15em;
  height: 1.15em;
  margin-top: 0.15em;
}

.form-check-input:checked {
  background-color: var(--ad-accent);
  border-color: var(--ad-accent);
}

.form-check-input:focus {
  border-color: var(--ad-accent);
  box-shadow: 0 0 0 3px rgba(var(--ad-accent-rgb), 0.16);
}

.form-check-label { color: var(--ad-ink); }

/* Radio groups (ratings, choice questions) read better as a row of chips than
   as a stack of dots, and they become tap targets on a phone. */
.ad-choices .form-check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.42rem 0.8rem 0.42rem 0.7rem;
  margin: 0 0.4rem 0.4rem 0;
  border: 1px solid var(--ad-line-strong);
  border-radius: 999px;
  min-height: 2.4rem;
}

.ad-choices .form-check:has(.form-check-input:checked) {
  border-color: var(--ad-accent);
  background: var(--ad-accent-wash);
}

.ad-choices .form-check-input { float: none; margin: 0; }
.ad-choices .form-check-label { margin: 0; cursor: pointer; }

/* -- Badges --------------------------------------------------------------- */

/* Bootstrap's .text-bg-* are solid blocks. Softening them to a wash plus
   coloured text keeps the desk readable when several sit side by side. */
.badge {
  --bs-badge-font-weight: 620;
  --bs-badge-padding-x: 0.62em;
  --bs-badge-padding-y: 0.42em;
  --bs-badge-border-radius: 999px;
  font-size: 0.72rem;
  letter-spacing: 0.035em;
}

.text-bg-success { background-color: var(--ad-ok-wash) !important; color: var(--ad-ok) !important; }
.text-bg-danger  { background-color: var(--ad-stop-wash) !important; color: var(--ad-stop) !important; }
.text-bg-warning { background-color: var(--ad-warn-wash) !important; color: var(--ad-warn) !important; }
.text-bg-primary { background-color: var(--ad-accent-wash) !important; color: var(--ad-accent-deep) !important; }
.text-bg-secondary {
  background-color: var(--ad-surface-sunk) !important;
  color: var(--ad-mute) !important;
}

/* -- Alerts --------------------------------------------------------------- */

.alert {
  --bs-alert-border-radius: var(--ad-radius);
  border: 1px solid transparent;
  border-left-width: 3px;
  padding: 0.9rem 1.1rem;
}

.alert-success { background: var(--ad-ok-wash); border-color: color-mix(in srgb, var(--ad-ok) 30%, transparent); border-left-color: var(--ad-ok); color: var(--ad-ok); }
.alert-danger  { background: var(--ad-stop-wash); border-color: color-mix(in srgb, var(--ad-stop) 30%, transparent); border-left-color: var(--ad-stop); color: var(--ad-stop); }
.alert-warning { background: var(--ad-warn-wash); border-color: color-mix(in srgb, var(--ad-warn) 30%, transparent); border-left-color: var(--ad-warn); color: var(--ad-warn); }
.alert-info    { background: var(--ad-accent-wash); border-color: color-mix(in srgb, var(--ad-accent) 30%, transparent); border-left-color: var(--ad-accent); color: var(--ad-accent-deep); }
.alert-secondary,
.alert-debug   { background: var(--ad-surface-sunk); border-color: var(--ad-line); border-left-color: var(--ad-line-strong); color: var(--ad-mute); }

.alert-heading { color: inherit; }

/* -- List groups ---------------------------------------------------------- */

.list-group {
  --bs-list-group-bg: var(--ad-surface);
  --bs-list-group-color: var(--ad-ink);
  --bs-list-group-border-color: var(--ad-line);
  --bs-list-group-border-radius: var(--ad-radius);
  --bs-list-group-action-hover-bg: var(--ad-surface-sunk);
  --bs-list-group-action-hover-color: var(--ad-ink);
  --bs-list-group-active-bg: var(--ad-accent-deep);
  --bs-list-group-active-border-color: var(--ad-accent-deep);
}

/* -- Toasts --------------------------------------------------------------- */

.toast {
  --bs-toast-border-radius: var(--ad-radius);
  box-shadow: var(--ad-shadow-lg);
  border: 1px solid transparent;
  border-left-width: 4px;
  font-weight: 520;
}

.toast.text-bg-success { border-left-color: var(--ad-ok); }
.toast.text-bg-danger  { border-left-color: var(--ad-stop); }
.toast.text-bg-warning { border-left-color: var(--ad-warn); }
.toast.text-bg-primary { border-left-color: var(--ad-accent); }

/* .text-bg-* now yields dark text, so the light close button would vanish. */
.toast .btn-close-white {
  filter: none;
}

/* Toasts clear the sticky desk bar. One landing on top of the event name and
   the connection badge hides the two things a desk must always be able to
   see, and on a phone it covers the whole bar. */
.ad-toasts {
  position: fixed;
  top: 4.75rem;
  right: 0;
  z-index: 1080;
  padding: 1rem;
  max-width: min(26rem, 100vw);
}

:root[data-bs-theme="dark"] .toast .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }

/* -- Focus ---------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--ad-accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.btn:focus-visible {
  box-shadow: 0 0 0 3px rgba(var(--ad-accent-rgb), 0.28);
  outline: none;
}

/* ==========================================================================
   Components
   ========================================================================== */

/* -- Chrome --------------------------------------------------------------- */

.ad-nav {
  background: var(--ad-surface);
  border-bottom: 1px solid var(--ad-line);
  padding-block: 0.6rem;
}

.ad-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 680;
  font-size: 1.05rem;
  letter-spacing: -0.025em;
  color: var(--ad-ink);
  text-decoration: none;
}

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

/* Two overlapping discs -- one arriving, one already through. The same mark
   the sign-in page carries, but filled from the tokens rather than from the
   two fixed teals that page uses, because this one sits on a light surface as
   often as a dark one and has to hold its separation on both. */
.ad-brand-mark {
  width: 2.05rem;
  height: 1.16rem;
  flex: none;
}

.ad-brand-mark .ad-mark-a {
  fill: color-mix(in srgb, var(--ad-accent) 58%, var(--ad-surface));
}

.ad-brand-mark .ad-mark-b {
  fill: var(--ad-accent);
  fill-opacity: 0.92;
}

/* The wordmark takes the deep stop: at 1.05rem it is a shade under the size
   that would let it ride on the accent alone. */
.ad-brand-word span {
  color: var(--ad-accent-deep);
}

.ad-nav .nav-link {
  color: var(--ad-mute);
  font-weight: 540;
  font-size: 0.92rem;
}

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

.ad-user {
  font-family: var(--ad-mono);
  font-size: 0.8rem;
  color: var(--ad-mute);
}

.ad-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-sm);
  background: transparent;
  color: var(--ad-mute);
  line-height: 1;
}

.ad-theme-toggle:hover { background: var(--ad-surface-sunk); color: var(--ad-ink); }
.ad-theme-toggle svg { width: 1rem; height: 1rem; }
:root:not([data-bs-theme="dark"]) .ad-theme-toggle .ad-icon-sun { display: none; }
:root[data-bs-theme="dark"] .ad-theme-toggle .ad-icon-moon { display: none; }

.page-footer {
  border-top: 1px solid var(--ad-line);
  padding-block: 1.4rem;
  color: var(--ad-mute);
  font-size: 0.82rem;
}

/* -- Shared bits ---------------------------------------------------------- */

/* Serial data: EK numbers, counts, times, ids. Tabular so columns line up and
   a changing count does not make the row twitch. */
.ad-serial {
  font-family: var(--ad-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.ad-eyebrow {
  font-size: 0.7rem;
  font-weight: 660;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ad-mute);
}

/* Bootstrap ships no min-width utility, and flex children need one to be
   allowed to shrink below their content width. */
.min-w-0 { min-width: 0; }

.ad-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

/* -- Public: front door ---------------------------------------------------
 *
 * The one public page with no task on it: nothing is listed here. It says
 * what the product does in three lines, and points anyone who arrived
 * looking for a form back at the invitation that already carries their link.
 *
 * Plain on purpose. This used to be a full-bleed gradient band borrowed from
 * the sign-in page, which made the quietest page in the product the loudest
 * thing in it. Ordinary paper and the same card surfaces the registration
 * and feedback pages use; the identity is carried by the brand, the type and
 * the accent, which is all a page with nothing to do needs to carry.
 */

.page-front {
  max-width: 54rem;
  margin-inline: auto;
}

.page-front-hero {
  padding-block: clamp(1.75rem, 6vw, 3.25rem) clamp(1.75rem, 4vw, 2.5rem);
  text-align: center;
}

.page-front-title {
  margin: 0.55rem 0 0.85rem;
}

.page-front-copy {
  /* Measured, not full width: the hero is centred, and centred text past
     about 45 characters loses the eye on the way back to the next line. */
  max-width: 42ch;
  margin-inline: auto;
  margin-block: 0;
  font-size: 1.06rem;
  line-height: 1.55;
  color: var(--ad-mute);
}

.page-front-actions {
  margin: 1.6rem 0 0;
}

/* What the product does, one tile each. Three plain statements rather than a
   numbered sequence -- registering, confirming and checking in do happen in
   that order, but nobody reading this page is walking through them. */
.page-front-tile {
  height: 100%;
  padding: 1.15rem 1.25rem;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  box-shadow: var(--ad-shadow-sm);
}

.page-front-tile h2 {
  margin: 0 0 0.3rem;
  font-size: 1rem;
  letter-spacing: -0.015em;
}

.page-front-tile p {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.5;
  color: var(--ad-mute);
}

/* The two questions people actually arrive with -- "where is my event" and
   "where is my pass" -- kept together and set down on the sunk surface, so
   the answer is findable without being the first thing shouted. */
.page-front-note {
  margin-top: clamp(1.5rem, 4vw, 2.25rem);
  padding: 1.2rem 1.35rem;
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  background: var(--ad-surface-sunk);
}

.page-front-note h2 {
  margin: 0 0 0.35rem;
  font-size: 1rem;
  letter-spacing: -0.015em;
}

.page-front-note p {
  margin: 0 0 0.6rem;
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--ad-mute);
}

.page-front-note p:last-child { margin-bottom: 0; }

/* -- Public: the pass -----------------------------------------------------
 *
 * Deliberately not "ad-column", and .page-footer is not "ad-footer", even
 * though everything else here uses the ad- prefix. Both names sit on ad
 * blocker filter lists, and .ad-column wraps the whole of <main> on the
 * public pages -- so a guest running uBlock saw a page with a header, a
 * footer's worth of blank, and nothing else. Keep new wrapper and footer
 * names clear of the prefix.
 */

.page-column {
  max-width: 34rem;
  margin-inline: auto;
}

.ad-pass {
  position: relative;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-md);
  overflow: hidden;
}

.ad-pass-body { padding: 1.9rem 1.75rem; }

.ad-pass-head { margin-bottom: 1.5rem; }
.ad-pass-head h1 { font-size: 1.4rem; margin: 0.35rem 0 0.3rem; }
.ad-pass-head .ad-pass-when { color: var(--ad-mute); font-size: 0.9rem; margin: 0; }

/* The stub: the half of the pass that is actually presented at the door.
   It gets its own ground and a perforated edge, because the two halves of a
   ticket do different jobs and a single flat card hides that. */
.ad-stub {
  position: relative;
  padding: 2rem 1.75rem 1.75rem;
  background: var(--ad-surface-sunk);
  border-top: 2px dashed var(--ad-line-strong);
}

/* The notches. Filled with the page ground and clipped by the card's own
   overflow, so they read as bites taken out of the edge. */
.ad-stub::before,
.ad-stub::after {
  content: "";
  position: absolute;
  top: -0.65rem;
  width: 1.3rem;
  height: 1.3rem;
  border-radius: 50%;
  background: var(--ad-paper);
}

.ad-stub::before { left: -0.65rem; }
.ad-stub::after { right: -0.65rem; }

/* Always white with black modules regardless of theme: a scanner reads
   contrast, not our palette, and inverting a QR breaks a good many readers. */
.ad-qr {
  display: block;
  margin: 0 auto;
  width: 208px;
  max-width: 100%;
  height: auto;
  padding: 0.85rem;
  background: #fff;
  border-radius: var(--ad-radius-sm);
  box-shadow: var(--ad-shadow-sm);
  /* QR modules are hard pixels; smoothing them costs scan reliability. */
  image-rendering: pixelated;
}

.ad-stub-note {
  text-align: center;
  margin: 1.1rem 0 0;
  font-size: 0.92rem;
  font-weight: 540;
}

.ad-stub-meta {
  text-align: center;
  margin: 0.3rem 0 0;
  font-size: 0.8rem;
  color: var(--ad-mute);
}

/* A labelled scrap of identity: "Registered to", "Staff number". */
.ad-field { margin-top: 1.25rem; }
.ad-field .ad-eyebrow { display: block; margin-bottom: 0.2rem; }
.ad-field p { margin: 0; font-size: 0.95rem; overflow-wrap: anywhere; }

/* -- Desk: staff-facing screens ------------------------------------------- */

.ad-deskbar {
  background: var(--ad-surface);
  border-bottom: 1px solid var(--ad-line);
  position: sticky;
  top: 0;
  z-index: 1030;
}

.ad-deskbar-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  padding-block: 0.9rem;
}

.ad-deskbar h1 {
  font-size: 1.12rem;
  margin: 0;
  line-height: 1.25;
}

.ad-deskbar .ad-eyebrow { display: block; margin-bottom: 0.15rem; }

.ad-desk-tabs {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.22rem;
  background: var(--ad-tabstrip);
  border-radius: var(--ad-radius-sm);
  margin-left: auto;
}

.ad-desk-tab {
  padding: 0.4rem 0.85rem;
  border-radius: 5px;
  font-size: 0.85rem;
  font-weight: 570;
  color: var(--ad-mute);
  text-decoration: none;
  white-space: nowrap;
}

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

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

/* Connection state earns a dot rather than only a word: at a glance across a
   desk, colour and motion read faster than reading "Reconnecting". */
.ad-conn {
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.3rem 0.7rem 0.3rem 0.6rem;
  border-radius: 999px;
  background: var(--ad-surface-sunk);
  color: var(--ad-mute);
  white-space: nowrap;
}

.ad-conn::before {
  content: "";
  width: 0.46rem;
  height: 0.46rem;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.ad-conn[data-state="live"] { background: var(--ad-ok-wash); color: var(--ad-ok); }
.ad-conn[data-state="reconnecting"] { background: var(--ad-warn-wash); color: var(--ad-warn); }
.ad-conn[data-state="offline"] { background: var(--ad-stop-wash); color: var(--ad-stop); }

.ad-conn[data-state="reconnecting"]::before { animation: ad-pulse 1.1s ease-in-out infinite; }

@keyframes ad-pulse {
  50% { opacity: 0.25; }
}

.ad-stock {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.82rem;
  color: var(--ad-mute);
}

.ad-stock b {
  font-family: var(--ad-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.05rem;
  font-weight: 620;
  color: var(--ad-ink);
}

/* The scanner viewport. html5-qrcode injects its own markup, so this mostly
   exists to stop it looking like a 2013 file-upload widget. */
.ad-scanner {
  border-radius: var(--ad-radius);
  overflow: hidden;
  background: var(--ad-surface-sunk);
}

.ad-scanner video { display: block; width: 100% !important; border-radius: var(--ad-radius); }
.ad-scanner img[alt="Info icon"] { display: none; }
.ad-scanner > div { border: 0 !important; padding: 0 !important; }

.ad-scanner button {
  border: 1px solid var(--ad-line-strong);
  background: var(--ad-surface);
  color: var(--ad-ink);
  border-radius: var(--ad-radius-sm);
  padding: 0.45rem 0.9rem;
  font-size: 0.88rem;
  font-weight: 560;
  margin: 0.35rem 0.25rem;
}

.ad-scanner button:hover { background: var(--ad-surface-sunk); }
.ad-scanner select { max-width: 100%; }
.ad-scanner a { color: var(--ad-accent-deep); }
.ad-scanner span { color: var(--ad-mute); font-size: 0.85rem; }

/* The subject card: who is at the door right now. Deliberately the largest
   type on the screen -- it is read at arm's length, at an angle, in a hurry. */
.ad-subject {
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  background: var(--ad-surface);
  box-shadow: var(--ad-shadow-md);
  overflow: hidden;
}

/* State is carried by a rail down the edge as well as by the badge, so the
   card's status survives peripheral vision. */
.ad-subject { border-left: 4px solid var(--ad-line-strong); }
.ad-subject[data-state="in"] { border-left-color: var(--ad-ok); }
.ad-subject[data-state="out"] { border-left-color: var(--ad-line-strong); }

.ad-subject-body { padding: 1.5rem 1.65rem; }

.ad-subject-name {
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2rem);
  font-weight: 660;
  letter-spacing: -0.028em;
  margin: 0 0 0.2rem;
  overflow-wrap: anywhere;
}

.ad-subject-id {
  font-family: var(--ad-mono);
  font-size: 0.88rem;
  color: var(--ad-mute);
  margin: 0;
  overflow-wrap: anywhere;
}

.ad-subject-states {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  flex: none;
}

.ad-subject-answers {
  margin: 1.25rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--ad-line);
  display: grid;
  grid-template-columns: minmax(7rem, auto) 1fr;
  gap: 0.5rem 1.25rem;
  font-size: 0.9rem;
}

.ad-subject-answers dt {
  font-weight: 560;
  color: var(--ad-mute);
}

.ad-subject-answers dd { margin: 0; overflow-wrap: anywhere; }

.ad-subject-answers:empty { display: none; }

/* Explains a disabled action, so the desk is not left guessing why the
   button will not take. */
.ad-subject-hint {
  margin: 1rem 0 0;
  font-size: 0.88rem;
  color: var(--ad-warn);
}

/* Door actions are hit fast, sometimes one-handed, sometimes by someone
   holding a scanner in the other hand. 56px minimum. */
.ad-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  margin-top: 1.4rem;
}

.ad-actions .btn {
  min-height: 3.5rem;
  padding-inline: 1.6rem;
  font-size: 1.02rem;
  flex: 1 1 12rem;
}

/* A dimmed accent button still reads as "press me". A refused action should
   look inert, and the hint beside it says why. */
.ad-actions .btn:disabled,
.ad-actions .btn.disabled {
  opacity: 1;
  background: var(--ad-surface-sunk);
  border-color: var(--ad-line);
  color: var(--ad-mute);
  cursor: not-allowed;
}

.ad-placeholder {
  padding: 2.75rem 1.5rem;
  text-align: center;
  color: var(--ad-mute);
  border: 1px dashed var(--ad-line-strong);
  border-radius: var(--ad-radius-lg);
  font-size: 0.92rem;
}

/* -- Search results ------------------------------------------------------- */

.ad-results {
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  background: var(--ad-surface);
  box-shadow: var(--ad-shadow-md);
  max-height: 17rem;
  overflow-y: auto;
  padding: 0.25rem;
}

.ad-results .list-group-item {
  border: 0;
  border-radius: var(--ad-radius-sm);
  padding: 0.6rem 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-align: left;
}

.ad-results .list-group-item.active {
  background: var(--ad-accent-wash);
  color: var(--ad-ink);
}

/* -- Dashboard ------------------------------------------------------------ */

.ad-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.85rem;
}

/* Four saturated cards shout equally and so say nothing. A quiet surface lets
   the figure carry the weight, and the figure takes the colour its state
   already means elsewhere in the app -- green is "checked in" on the badges
   and on the pie chart, so it is green here too. */
.ad-stat {
  position: relative;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  box-shadow: var(--ad-shadow-sm);
  padding: 1.1rem 1.25rem;
}

.ad-stat[data-tone="accent"] .ad-stat-value { color: var(--ad-accent); }
.ad-stat[data-tone="ok"] .ad-stat-value { color: var(--ad-ok); }
.ad-stat[data-tone="warn"] .ad-stat-value { color: var(--ad-warn); }
.ad-stat[data-tone="stop"] .ad-stat-value { color: var(--ad-stop); }

.ad-stat-label {
  display: block;
  font-size: 0.7rem;
  font-weight: 660;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ad-mute);
  margin-bottom: 0.5rem;
}

.ad-stat-value {
  font-family: var(--ad-mono);
  font-variant-numeric: tabular-nums;
  font-size: 2.25rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.04em;
  margin: 0;
}

.ad-stat-sub {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  color: var(--ad-mute);
}

.ad-chart {
  position: relative;
  height: 240px;
}

/* The share of the list that is in, printed inside the ring rather than read
   off a legend. Chart.js cannot draw into the hole, so the figure is ordinary
   markup sitting in it -- and stays out of the way of the pointer. */
.ad-chart-dial .ad-dial-centre {
  position: absolute;
  inset: 0;
  bottom: 2.6rem; /* clear of the legend, so the figure centres on the ring */
  display: grid;
  place-content: center;
  text-align: center;
  pointer-events: none;
}

.ad-dial-centre b {
  display: block;
  font-family: var(--ad-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.9rem;
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--ad-ok);
}

.ad-dial-centre span {
  font-size: 0.7rem;
  font-weight: 660;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ad-mute);
}

/* -- Dashboard: a bar for a proportion ------------------------------------
 *
 * Used for the headline turnout and for every group row, so the same shape
 * always means the same thing: how much of this is in.
 */

.ad-meter {
  height: 5px;
  border-radius: 999px;
  background: var(--ad-surface-sunk);
  overflow: hidden;
}

.ad-meter > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--ad-ok);
  transition: width 0.35s ease;
}

/* A group nobody has arrived from is the one worth spotting: a hairline of
   the stop colour reads as "none yet" where a bar of zero width reads as a
   rendering fault. */
.ad-meter[data-empty="true"] {
  background: var(--ad-stop-wash);
}

.ad-stat .ad-meter {
  margin-top: 0.7rem;
}

/* The three timings under the counters. One quiet line, because they are
   context for the numbers above rather than numbers in their own right. */
.ad-pace-strip {
  margin: 0.85rem 0 0;
  min-height: 1.2rem;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--ad-mute);
}

/* Title, then whatever explains it, then the downloads hard right. */
.ad-card-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.ad-card-note {
  font-weight: 400;
  font-size: 0.78rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ad-mute);
}

.ad-card-tools {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin-left: auto;
}

/* The pace line and the page-level downloads share a row: both are context
   for the counters above rather than content in their own right. */
.ad-dash-bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}

/* An offer, not a call to action. These sit in the corner of a panel someone
   is watching, so they stay quiet until the pointer finds them -- and they
   look identical whether the markup is a link (a file the server builds) or a
   button (an image the page builds). */
.ad-export {
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-sm);
  background: transparent;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ad-mute);
  white-space: nowrap;
  cursor: pointer;
}

.ad-export:hover,
.ad-export:focus-visible {
  border-color: var(--ad-accent);
  color: var(--ad-accent-deep);
}

/* -- Dashboard: turnout by group ------------------------------------------
 *
 * The counters say how many are in; this says which parts of the crowd they
 * came from, which is what tells you whether to hold the opening or start.
 */

.ad-segments {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.4rem 1.75rem;
}

.ad-segment-title {
  margin: 0 0 0.7rem;
  font-size: 0.7rem;
  font-weight: 660;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ad-mute);
}

.ad-segment-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

/* Name and count on one line, the bar spanning underneath both: the eye runs
   down the bars to compare, and down the names to find one. */
.ad-segment-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 0.75rem;
}

.ad-segment-row .ad-meter {
  grid-column: 1 / -1;
}

.ad-segment-name {
  font-size: 0.87rem;
  color: var(--ad-ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.ad-segment-name:hover {
  color: var(--ad-accent-deep);
  text-decoration: underline;
}

.ad-segment-count {
  font-family: var(--ad-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--ad-mute);
}

.ad-segment-count b {
  font-weight: 600;
  color: var(--ad-ok);
}

.ad-segment-more,
.ad-empty-note {
  margin: 0.7rem 0 0;
  font-size: 0.8rem;
  color: var(--ad-mute);
}

/* -- Dashboard: latest arrivals -------------------------------------------
 *
 * A counter that is not moving looks exactly like a counter nobody is adding
 * to. This panel is how you tell a quiet door from a stalled one.
 */

.ad-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
  align-content: start;
}

.ad-feed-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--ad-line);
}

.ad-feed-row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.ad-feed-main {
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
}

.ad-feed-name {
  font-size: 0.89rem;
  color: var(--ad-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ad-feed-tag {
  flex: none;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: var(--ad-accent-wash);
  color: var(--ad-accent-deep);
}

.ad-feed-meta {
  flex: none;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.ad-feed-time {
  display: block;
  font-family: var(--ad-mono);
  font-size: 0.8rem;
  color: var(--ad-ink);
}

.ad-feed-ago {
  display: block;
  font-size: 0.7rem;
  color: var(--ad-mute);
}

/* -- Misc ----------------------------------------------------------------- */

.ad-notice {
  max-width: 34rem;
  margin-inline: auto;
  text-align: center;
  padding: 2.75rem 1.75rem;
  background: var(--ad-surface);
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius-lg);
  box-shadow: var(--ad-shadow-md);
}

.ad-notice h1 { font-size: 1.45rem; margin-bottom: 0.5rem; }
.ad-notice p { color: var(--ad-mute); margin-bottom: 0; }

.ad-notice-mark {
  width: 3rem;
  height: 3rem;
  margin: 0 auto 1.1rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ad-ok-wash);
  color: var(--ad-ok);
}

.ad-notice-mark[data-tone="warn"] { background: var(--ad-warn-wash); color: var(--ad-warn); }
.ad-notice-mark svg { width: 1.5rem; height: 1.5rem; }

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

@media (max-width: 575.98px) {
  /* The note explains the panel to someone reading it; on a phone it lands
     under its own title and reads as a second heading. The panel is legible
     without it. */
  .ad-card-note { display: none; }
  .ad-pass-body { padding: 1.5rem 1.15rem; }
  .ad-tear { margin-inline: -1.15rem; }
  .ad-subject-body { padding: 1.25rem 1.15rem; }
  .ad-subject-states { flex-direction: row; align-items: center; }
  .ad-desk-tabs { margin-left: 0; width: 100%; }
  .ad-desk-tab { flex: 1; text-align: center; }
}

/* -- Desk: adding a walk-in ----------------------------------------------
 *
 * Appears only after a search has come back empty, so it reads as the answer
 * to "they are not on the list" rather than as a second thing to fill in.
 */

.ad-walk-in-add {
  font-weight: 600;
  color: var(--ad-accent-deep);
}

.ad-walk-in {
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  background: var(--ad-surface-sunk);
  padding: 1rem;
  display: grid;
  gap: 0.75rem;
}

.ad-walk-in-title {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ad-mute);
}

.ad-walk-in-field {
  display: grid;
  gap: 0.3rem;
}

.ad-walk-in-field label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ad-ink);
}

/* The form fields are rendered by Django, so they arrive without Bootstrap's
   classes -- style them by element rather than adding markup in the view. */
.ad-walk-in-field input,
.ad-walk-in-field select,
.ad-walk-in-field textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font: inherit;
  color: var(--ad-ink);
  background: var(--ad-paper);
  border: 1px solid var(--ad-line-strong);
  border-radius: var(--ad-radius-sm);
}

.ad-walk-in-field input:focus,
.ad-walk-in-field select:focus,
.ad-walk-in-field textarea:focus {
  outline: 2px solid var(--ad-accent);
  outline-offset: 1px;
  border-color: var(--ad-accent);
}

.ad-walk-in-note {
  margin: 0;
  font-size: 0.85rem;
  color: var(--ad-mute);
}
