@charset "UTF-8";
/* ==========================================================================
   Devoteam @ LEAP 2026 — booking system design system
   --------------------------------------------------------------------------
   Direction: "neon fluid glass". The client's agenda deck is a near-black
   indigo ground lit from behind by a poppy/violet fluid ribbon, with content
   floating on translucent glass. This stylesheet rebuilds that atmosphere in
   pure CSS: a fixed aurora field, a fine grain veil, and cards that read as
   lit panes rather than flat boxes.

   Colours, typeface and the type scale come from the Devoteam brand
   guidelines and are not design decisions. Composition, depth, spacing,
   state signalling and motion are.

   Mobile-first: base styles target a 375px phone held one-handed at the
   booth; the grid widens at 768px and 1100px.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Typeface — self-hosted. The venue network is untrusted and may block
   Google Fonts; a booth screen must never fall back mid-event because a CDN
   is unreachable. Weights 300/400/500/600/700/800 are self-hosted as static
   woff2 files in /assets/fonts/. If they are ever missing, the
   Helvetica/Arial stack below carries the pages.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/assets/fonts/montserrat-300.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/montserrat-400.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/montserrat-500.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/montserrat-600.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/montserrat-700.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/assets/fonts/montserrat-800.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Brand — Devoteam guidelines, do not substitute */
  --dt-poppy:        #f8485e;
  --dt-poppy-light:  #fca2ae;
  --dt-poppy-lighter:#fddade;
  --dt-ink:          #3c3c3a;   /* text on light — never #000 */
  --dt-white:        #ffffff;
  --dt-violet:       #63238c;
  --dt-grey-light:   #efeeee;

  /* Success green. Not a brand colour — the guidelines have no "confirmed" hue,
     and poppy is the alert/error red, so reusing it here would say the opposite
     of what the moment means. The deep fill carries a white tick at 4.1:1,
     clearing the 3:1 that a large graphic owes; the bright tone is for the ring
     and glow, which carry no meaning on their own. */
  --dt-success:      #2fbf71;
  --dt-success-deep: #1c8f55;

  /* Gold, for the violin interludes. Also outside the brand palette, and for
     the same reason as the green: poppy and violet both mean something on an
     agenda card already — chosen, refused, clashing — and an interlude means
     none of those. It is warm rather than loud, which suits a slot that is on
     the programme but asks nothing of the reader. On --dt-ground the light
     tone carries small text at about 11:1. */
  --dt-gold:         #e8c26a;
  --dt-gold-deep:    #8a6a1e;

  /* Dark ground, sampled from the agenda deck */
  --dt-ground:       #080d23;
  --dt-ground-2:     #0a091e;
  --dt-ground-3:     #2b214c;

  /* Glass surfaces, matching the agenda card treatment */
  --dt-surface:      rgba(255, 255, 255, 0.07);
  --dt-surface-line: rgba(255, 255, 255, 0.14);
  --dt-on-dark:      #ffffff;
  --dt-on-dark-mute: rgba(255, 255, 255, 0.66);

  /* Type scale — brand web scale */
  --dt-hero: 4rem; --dt-h1: 3rem; --dt-h2: 2.25rem; --dt-h3: 1.75rem;
  --dt-h4: 1.5rem; --dt-h5: 1.25rem; --dt-h6: 1rem;
  --dt-body: 1rem; --dt-small: 0.875rem; --dt-micro: 0.75rem;

  --dt-radius: 12px;
  --dt-radius-lg: 20px;

  /* --- Derived: the top of the ground ramp, rhythm, depth, motion -------- */
  --dt-ground-4:     #3a2250;
  --dt-surface-2:    rgba(255, 255, 255, 0.045);
  --dt-surface-hi:   rgba(255, 255, 255, 0.11);
  --dt-line-soft:    rgba(255, 255, 255, 0.08);

  --dt-font: "Montserrat", Helvetica, Arial, sans-serif;

  /* 4px rhythm */
  --dt-s1: 0.25rem; --dt-s2: 0.5rem;  --dt-s3: 0.75rem; --dt-s4: 1rem;
  --dt-s5: 1.5rem;  --dt-s6: 2rem;    --dt-s7: 3rem;    --dt-s8: 4rem;
  --dt-s9: 6rem;

  --dt-tap: 48px;                      /* minimum touch target */
  --dt-measure: 34rem;                 /* comfortable reading width */
  --dt-shell: 72rem;                   /* page max width */
  --dt-gutter: var(--dt-s5);

  --dt-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.32);
  --dt-shadow-2: 0 18px 40px -24px rgba(0, 0, 0, 0.85);
  --dt-shadow-3: 0 32px 70px -30px rgba(0, 0, 0, 0.9);
  --dt-glow:     0 0 0 1px rgba(248, 72, 94, 0.35),
                 0 8px 30px -8px rgba(248, 72, 94, 0.55),
                 0 2px 10px -4px rgba(99, 35, 140, 0.55);

  --dt-ease: cubic-bezier(0.2, 0.7, 0.25, 1);
  --dt-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dt-fast: 140ms;
  --dt-mid: 260ms;
  --dt-slow: 520ms;

  --dt-focus: 3px;
}

/* --------------------------------------------------------------------------
   3. Reset — narrow and deliberate
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;   /* never let iOS re-scale our type */
  text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
}

ul[class], ol[class] { padding: 0; list-style: none; }

img, svg, video { display: block; max-width: 100%; height: auto; }

button, input, select, textarea { font: inherit; color: inherit; }

a { color: inherit; }

table { border-collapse: collapse; width: 100%; }

::selection {
  background: var(--dt-poppy);
  color: var(--dt-white);
}

/* --------------------------------------------------------------------------
   4. The ground and its atmosphere
   The ramp #080d23 -> #0a091e -> #2b214c -> #3a2250 lives on <body>, with two
   fixed layers above it: an aurora of poppy and violet blooms (the deck's
   fluid ribbon, abstracted) and a grain veil that stops the large gradients
   banding on cheap phone panels.
   -------------------------------------------------------------------------- */
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--dt-ground);
  font-family: var(--dt-font);
  font-size: var(--dt-body);
  font-weight: 400;
  line-height: 1.6;
  color: var(--dt-on-dark);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body::before,
body::after {
  content: "";
  position: fixed;
  inset: -20vmax;
  z-index: -2;
  pointer-events: none;
}

/* Aurora: four blooms weighted so the brightest sits behind the top of the
   page where the hero lands, cooling to near-black past the fold.

   The alphas are not taste — they are the ceiling. Everything on this page is
   read against whatever the aurora happens to be painting underneath, so the
   blooms were measured down until the worst case (every bloom stacked at full
   strength, brighter than any point that actually occurs) still clears WCAG AA
   for text and 3:1 for edges. See the task report for the numbers. */
body::before {
  background:
    radial-gradient(58vmax 42vmax at 18% -6%, rgba(248, 72, 94, 0.20), transparent 62%),
    radial-gradient(46vmax 46vmax at 88% 6%,  rgba(99, 35, 140, 0.32), transparent 64%),
    radial-gradient(70vmax 40vmax at 50% 42%, rgba(58, 34, 80, 0.42),  transparent 70%),
    radial-gradient(40vmax 34vmax at 6% 82%,  rgba(248, 72, 94, 0.10), transparent 66%),
    /* The ramp runs #080d23 -> #0a091e and on toward --dt-ground-3 (#2b214c)
       and --dt-ground-4 (#3a2250), damped to roughly 45% so the violet end
       stays behind the content instead of competing with it. Written as
       literals: one unsupported colour function would invalidate the whole
       declaration and take the entire aurora with it. */
    linear-gradient(168deg, #080d23 0%, #0a091e 38%, #191433 74%, #1e1433 100%);
  animation: dt-drift 48s var(--dt-ease) infinite alternate;
  will-change: transform;
}

/* Grain veil — inline SVG turbulence, no extra network request. */
body::after {
  z-index: -1;
  opacity: 0.4;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.35'/%3E%3C/svg%3E");
}

@keyframes dt-drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1.02); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.09); }
}

/* --------------------------------------------------------------------------
   5. Typography
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
  line-height: 1.15;
  text-wrap: balance;
  color: var(--dt-on-dark);
}

h1 { font-size: var(--dt-h1); font-weight: 700; letter-spacing: -0.02em; }
h2 { font-size: var(--dt-h2); font-weight: 700; letter-spacing: -0.015em; }
h3 { font-size: var(--dt-h3); font-weight: 600; letter-spacing: -0.01em; }
h4 { font-size: var(--dt-h4); font-weight: 600; }
h5 { font-size: var(--dt-h5); font-weight: 500; }
h6 {
  font-size: var(--dt-h6);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

p { text-wrap: pretty; }
p + p { margin-top: var(--dt-s4); }

/* Hero display type. Clamped so a 4rem headline never overflows a 375px
   viewport, and never grows past the brand ceiling on a booth monitor. */
.dt-hero-title {
  font-size: clamp(2.25rem, 9vw, var(--dt-hero));
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.dt-hero-title em {
  font-style: normal;
  color: var(--dt-poppy);
}

.dt-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--dt-s2);
  margin: 0;
  font-size: var(--dt-small);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-white);
}

/* The hairline that turns the eyebrow into a datum line running under the
   hero — the one repeated motif that ties the pages together, and the reason
   the eyebrow still reads as poppy without setting poppy type. */
.dt-eyebrow::before {
  content: "";
  flex: none;
  width: 1.75rem;
  height: 2px;
  border-radius: 2px;
  background: var(--dt-poppy);
}

.dt-lede {
  max-width: var(--dt-measure);
  font-size: var(--dt-h5);
  font-weight: 300;
  line-height: 1.5;
  color: var(--dt-on-dark-mute);
}

.dt-muted { color: var(--dt-on-dark-mute); }
.dt-small { font-size: var(--dt-small); }
.dt-micro {
  font-size: var(--dt-micro);   /* the floor — nothing goes below this */
  letter-spacing: 0.03em;
}

.dt-numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

a:not([class]) {
  color: var(--dt-white);
  text-decoration-color: var(--dt-poppy);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.22em;
  transition: color var(--dt-fast) var(--dt-ease);
}
a:not([class]):hover {
  color: var(--dt-white);
  text-decoration-thickness: 3px;
}

/* --------------------------------------------------------------------------
   6. Focus — one visible treatment everywhere. The whole flow is keyboard
   operable; booth staff work a laptop and rarely reach for the trackpad.
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: var(--dt-focus) solid var(--dt-poppy);
  outline-offset: 2px;
  /* A dark moat around the ring. The aurora behind any given element is not
     knowable at author time, and poppy on its brightest patch measures 2.92:1
     — just under the 3:1 a focus indicator owes. The moat pins the ring's
     immediate surround to the ground colour, so the indicator holds anywhere
     on the page, over any future background image. */
  box-shadow: 0 0 0 calc(var(--dt-focus) + 3px) rgba(8, 13, 35, 0.88);
  border-radius: 4px;
}

.dt-skip {
  position: absolute;
  left: var(--dt-s4);
  top: calc(-1 * var(--dt-tap) - var(--dt-s4));
  z-index: 100;
  padding: var(--dt-s3) var(--dt-s4);
  border-radius: var(--dt-radius);
  background: var(--dt-poppy);
  color: var(--dt-white);
  /* Same white-on-poppy pair as .dt-btn, so the same large-text floor. */
  font-size: 1.1875rem;
  font-weight: 700;
  text-decoration: none;
  transition: top var(--dt-fast) var(--dt-ease);
}
.dt-skip:focus { top: var(--dt-s4); }

/* Hidden visually, still read aloud and still focusable. */
.dt-sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   7. Shell and layout
   -------------------------------------------------------------------------- */
.dt-shell {
  width: 100%;
  max-width: var(--dt-shell);
  margin-inline: auto;
  padding-inline: var(--dt-gutter);
}

.dt-shell--narrow { max-width: 46rem; }

.dt-section { padding-block: var(--dt-s7); }
.dt-section--tight { padding-block: var(--dt-s6); }

.dt-stack > * + * { margin-top: var(--dt-s4); }
.dt-stack--lg > * + * { margin-top: var(--dt-s6); }
.dt-stack--sm > * + * { margin-top: var(--dt-s2); }

.dt-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--dt-s3);
}

.dt-grid {
  display: grid;
  gap: var(--dt-s4);
  grid-template-columns: 1fr;
}

.dt-rule {
  height: 1px;
  border: 0;
  background: linear-gradient(90deg, transparent, var(--dt-surface-line) 15%, var(--dt-surface-line) 85%, transparent);
}

/* Header — a glass rail that stays legible once cards scroll beneath it. */
.dt-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--dt-s4);
  min-height: 64px;
  padding: var(--dt-s3) var(--dt-gutter);
  padding-top: calc(var(--dt-s3) + env(safe-area-inset-top));
  background: rgba(8, 13, 35, 0.72);
  border-bottom: 1px solid var(--dt-line-soft);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
}

.dt-header__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--dt-s3);
  font-weight: 700;
  letter-spacing: -0.01em;
  text-decoration: none;
}

.dt-header__brand img { height: 26px; width: auto; }

/* A brand mark plus a handful of nav actions is more than one row can hold
   at 375px. Stack rather than let the row's own centring squeeze the two
   apart and overlap: brand on its own line, the nav cluster (which already
   wraps its own buttons) on the next. */
@media (max-width: 767px) {
  .dt-header {
    flex-wrap: wrap;
    row-gap: var(--dt-s3);
  }
  .dt-header__brand,
  .dt-header .dt-cluster {
    flex: 1 1 100%;
  }
}

.dt-header__meta {
  font-size: var(--dt-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-on-dark-mute);
}

.dt-footer {
  padding: var(--dt-s6) var(--dt-gutter) calc(var(--dt-s6) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--dt-line-soft);
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

/* --------------------------------------------------------------------------
   8. .dt-card — the glass pane. Session cards extend this, so the treatment
   carries the whole product.
   -------------------------------------------------------------------------- */
.dt-card {
  position: relative;
  padding: var(--dt-s5);
  background: var(--dt-surface);
  border: 1px solid var(--dt-surface-line);
  border-radius: var(--dt-radius-lg);
  box-shadow: var(--dt-shadow-2);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  backdrop-filter: blur(14px) saturate(130%);
  transition:
    border-color var(--dt-mid) var(--dt-ease),
    box-shadow var(--dt-mid) var(--dt-ease),
    transform var(--dt-mid) var(--dt-ease-out),
    background-color var(--dt-mid) var(--dt-ease);
}

/* Specular hairline along the top edge — the single detail that makes the
   pane read as lit glass rather than a flat translucent rectangle. */
.dt-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  border-radius: inherit;
  background: linear-gradient(90deg,
    transparent,
    rgba(255, 255, 255, 0.55) 22%,
    rgba(255, 255, 255, 0.28) 55%,
    transparent);
  pointer-events: none;
}

.dt-card--flush { padding: 0; overflow: hidden; }
.dt-card--quiet { background: var(--dt-surface-2); box-shadow: var(--dt-shadow-1); }

.dt-card--centered {
  max-width: 34rem;
  margin: var(--dt-s7) auto;
  text-align: center;
}
.dt-card--centered .dt-eyebrow { justify-content: center; }

/* Feature pane: a poppy edge-light bleeding in from the left. */
.dt-card--feature { overflow: hidden; }
.dt-card--feature::after {
  content: "";
  position: absolute;
  inset: -40% auto -40% -30%;
  width: 60%;
  background: radial-gradient(closest-side, rgba(248, 72, 94, 0.16), transparent 70%);
  pointer-events: none;
}
.dt-card--feature > * { position: relative; }

.dt-card__title { font-size: var(--dt-h4); font-weight: 600; }
.dt-card__body { color: var(--dt-on-dark-mute); }

/* --------------------------------------------------------------------------
   9. .dt-btn
   Brand: solid Red Poppy, 12px radius, medium weight, white label.

   CONTRAST. White on solid #f8485e measures 3.454:1, which clears WCAG AA for
   large text (3:1) but not for normal-size text (4.5:1). The brand fixes both
   the fill and the label colour, so the resolution is the type: the label is
   set at 1.1875rem (19px) / weight 700, over the 18.66px-bold threshold at
   which AA Large applies. The fill stays exactly as the guidelines specify.
   A bolder label also reads better on a phone in a noisy hall.

   Consequences to preserve if you edit this block:
     - never take a solid-poppy label below 18.67px or under weight 700;
     - .dt-btn--sm shortens the padding only, never the type, for that reason;
     - .dt-skip is the same white-on-poppy pair and carries the same floor.
   Where a fully compliant alternative existed it was used anyway:
   .dt-btn--ghost and .dt-btn--quiet carry white labels at 10:1 or better, and
   .dt-badge--poppy is a tinted fill rather than a solid one for this reason.
   -------------------------------------------------------------------------- */
.dt-btn {
  --dt-btn-bg: var(--dt-poppy);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--dt-s2);
  min-height: var(--dt-tap);
  padding: var(--dt-s3) var(--dt-s5);
  border: 1px solid transparent;
  border-radius: var(--dt-radius);
  background: var(--dt-btn-bg);
  color: var(--dt-white);
  font-family: inherit;
  /* >= 18.66px at weight 700 is the threshold where WCAG treats text as large
     and the 3:1 ratio applies. White on poppy is 3.454:1, so the label must
     not drop below either of these two values. */
  font-size: 1.1875rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.01em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  box-shadow: var(--dt-shadow-1);
  transition:
    transform var(--dt-fast) var(--dt-ease),
    filter var(--dt-fast) var(--dt-ease),
    box-shadow var(--dt-mid) var(--dt-ease),
    background-color var(--dt-fast) var(--dt-ease);
}

.dt-btn:hover { filter: brightness(1.07); }
.dt-btn:active { transform: translateY(1px) scale(0.995); }

.dt-btn:disabled,
.dt-btn[aria-disabled="true"],
.dt-btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
  transform: none;
  box-shadow: none;
}

.dt-btn--block { display: flex; width: 100%; }

/* Secondary: poppy on the ground, no fill. */
.dt-btn--ghost {
  background: transparent;
  border-color: var(--dt-poppy);
  color: var(--dt-white);
  box-shadow: none;
}
.dt-btn--ghost:hover {
  background: rgba(248, 72, 94, 0.12);
  border-color: var(--dt-poppy);
  filter: none;
}

/* Tertiary: a quiet action sitting on glass. */
.dt-btn--quiet {
  background: var(--dt-surface);
  border-color: var(--dt-surface-line);
  color: var(--dt-white);
  box-shadow: none;
}
.dt-btn--quiet:hover { background: var(--dt-surface-hi); filter: none; }

/* Manage mode, zero selections: cancelling every session is a legitimate
   thing to let someone do, but the button must never look like the ordinary
   solid-poppy save action or a guest could hit it by habit. Tinted fill with
   a poppy edge — the same language .dt-badge--poppy and the unavailable
   session states already use for "different, read me first" — rather than
   a second colour the brand guidelines don't provide. The label text itself
   ("Cancel all my sessions" vs "Save changes") is the second, non-colour
   signal. */
.dt-btn--danger {
  background: rgba(248, 72, 94, 0.18);
  border-color: var(--dt-poppy);
  color: var(--dt-white);
  box-shadow: none;
}
.dt-btn--danger:hover { background: rgba(248, 72, 94, 0.28); filter: none; }

/* Hero CTA only — the subtle neon glow the guidelines permit. */
.dt-btn--hero {
  min-height: 56px;
  padding-inline: var(--dt-s6);
  font-size: var(--dt-h5);
  box-shadow: var(--dt-glow);
}
.dt-btn--hero:hover {
  box-shadow:
    0 0 0 1px rgba(248, 72, 94, 0.5),
    0 12px 44px -8px rgba(248, 72, 94, 0.7),
    0 4px 16px -4px rgba(99, 35, 140, 0.65);
}

/* Still a full 48px target, and still the large-text label — only the
   horizontal padding tightens. Shrinking the type here would drop a solid
   poppy button out of AA Large. */
.dt-btn--sm { padding-inline: var(--dt-s4); }

/* --------------------------------------------------------------------------
   10. .dt-field — forms
   Inputs never drop below 16px: anything smaller makes iOS zoom the viewport
   on focus, which throws a guest out of the form mid-answer.
   -------------------------------------------------------------------------- */
.dt-field { display: block; }
.dt-field + .dt-field { margin-top: var(--dt-s5); }

.dt-field__label {
  display: block;
  margin-bottom: var(--dt-s2);
  font-size: var(--dt-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--dt-white);
}

.dt-field__label .dt-req { margin-left: 2px; }

.dt-field__hint {
  margin-top: var(--dt-s2);
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

/* A .dt-field that is a <fieldset> — a group of radios under one question.
   The UA border and padding are the only difference from the <div> case. */
fieldset.dt-field {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* One radio and its wording. A full row, so the whole line is the target. */
.dt-choice {
  display: flex;
  align-items: flex-start;
  gap: var(--dt-s3);
  padding: var(--dt-s2) 0;
  cursor: pointer;
  line-height: 1.4;
}
.dt-choice input[type="radio"] {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.1rem 0 0;
  accent-color: var(--dt-poppy);
  cursor: pointer;
}

.dt-field__control,
.dt-field input[type="text"],
.dt-field input[type="email"],
.dt-field input[type="tel"],
.dt-field input[type="url"],
.dt-field input[type="password"],
.dt-field input[type="search"],
.dt-field input[type="number"],
.dt-field input[type="date"],
.dt-field input[type="time"],
.dt-field select,
.dt-field textarea {
  display: block;
  width: 100%;
  min-height: var(--dt-tap);
  padding: var(--dt-s3) var(--dt-s4);
  font-family: inherit;
  font-size: max(16px, 1rem);
  font-weight: 400;
  line-height: 1.4;
  color: var(--dt-white);
  background: rgba(255, 255, 255, 0.05);
  /* Heavier than --dt-surface-line: a control's boundary is a UI component and
     owes 3:1, where a card's decorative edge does not. */
  border: 1px solid rgba(255, 255, 255, 0.46);
  border-radius: var(--dt-radius);
  -webkit-appearance: none;
  appearance: none;
  transition:
    border-color var(--dt-fast) var(--dt-ease),
    background-color var(--dt-fast) var(--dt-ease),
    box-shadow var(--dt-fast) var(--dt-ease);

  /* Date and time inputs are drawn partly by the engine — the spin fields, the
     calendar and clock icons, and the picker panel that drops out of them. On a
     light OS those default to a light treatment, which on this ground meant a
     white box with white text in it. This hands the engine the right scheme for
     all of them at once. */
  color-scheme: dark;
}

/* The engine's own calendar and clock glyphs are black by default and vanish
   against the field. Inverting is the only hook WebKit exposes for them. */
.dt-field input[type="date"]::-webkit-calendar-picker-indicator,
.dt-field input[type="time"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.7;
  cursor: pointer;
}

.dt-field input[type="date"]::-webkit-calendar-picker-indicator:hover,
.dt-field input[type="time"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

.dt-field textarea { min-height: 7.5rem; resize: vertical; }

.dt-field select {
  padding-right: var(--dt-s7);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--dt-white) 50%),
    linear-gradient(135deg, var(--dt-white) 50%, transparent 50%);
  background-position:
    right calc(var(--dt-s4) + 6px) center,
    right var(--dt-s4) center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

/* Belt and braces for the popup list. color-scheme alone does not cover every
   engine, and where it is ignored the <option> rows inherit the select's white
   text onto the OS's white list background — invisible items. These two rules
   paint the rows explicitly, so the list is legible even when the engine
   refuses the dark scheme. Firefox additionally needs the checked/hover rows
   named, which it takes from the system highlight otherwise. */
.dt-field select option,
.dt-field select optgroup {
  color: var(--dt-white);
  background-color: var(--dt-ground-2);
}

.dt-field select option:checked,
.dt-field select option:hover {
  color: var(--dt-white);
  background-color: var(--dt-ground-3);
}

.dt-field ::placeholder { color: rgba(255, 255, 255, 0.66); opacity: 1; }

.dt-field input:hover,
.dt-field select:hover,
.dt-field textarea:hover { border-color: rgba(255, 255, 255, 0.68); }

/* Older engines that do not match :focus-visible on form controls still get
   a visible edge; :focus-visible then adds the full ring. */
.dt-field input:focus,
.dt-field select:focus,
.dt-field textarea:focus {
  outline: none;
  border-color: var(--dt-poppy);
}

.dt-field input:focus-visible,
.dt-field select:focus-visible,
.dt-field textarea:focus-visible {
  outline: none;
  border-color: var(--dt-poppy);
  background: rgba(255, 255, 255, 0.08);
  box-shadow:
    0 0 0 var(--dt-focus) var(--dt-poppy),
    0 0 0 calc(var(--dt-focus) + 3px) rgba(8, 13, 35, 0.88);
}

/* Chrome's yellow autofill wash would tear a hole in the dark ground. */
.dt-field input:-webkit-autofill,
.dt-field input:-webkit-autofill:hover,
.dt-field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--dt-white);
  -webkit-box-shadow: 0 0 0 1000px #16172f inset;
  caret-color: var(--dt-white);
}

/* Error: poppy edge, but the message below carries the meaning. */
.dt-field--error input,
.dt-field--error select,
.dt-field--error textarea {
  border-color: var(--dt-poppy);
  background: rgba(248, 72, 94, 0.08);
}

.dt-field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--dt-s2);
  margin-top: var(--dt-s2);
  font-size: var(--dt-small);
  font-weight: 500;
  color: var(--dt-white);
}

.dt-field-error::before {
  content: "!";
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.15rem;
  border-radius: 50%;
  background: var(--dt-poppy);
  color: var(--dt-white);
  font-size: var(--dt-micro);
  font-weight: 700;
  line-height: 1;
}

/* Native checkbox/radio, tinted and given a usable target. */
.dt-check {
  display: flex;
  align-items: flex-start;
  gap: var(--dt-s3);
  min-height: var(--dt-tap);
  padding-block: var(--dt-s2);
  cursor: pointer;
}
.dt-check input[type="checkbox"],
.dt-check input[type="radio"] {
  flex: none;
  width: 1.375rem;
  height: 1.375rem;
  margin-top: 0.2rem;
  accent-color: var(--dt-poppy);
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   11. Alerts, badges, meters, steps
   Badges are filled with white labels — the word comes first, the colour only
   reinforces it.
   -------------------------------------------------------------------------- */
.dt-alert {
  display: flex;
  gap: var(--dt-s3);
  padding: var(--dt-s4);
  border: 1px solid var(--dt-surface-line);
  border-left: 3px solid var(--dt-poppy);
  border-radius: var(--dt-radius);
  background: rgba(255, 255, 255, 0.06);
  font-size: var(--dt-small);
}
.dt-alert__title { font-weight: 700; }
.dt-alert--ok { border-left-color: var(--dt-white); }
.dt-alert--note { border-left-color: var(--dt-violet); }

.dt-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  color: var(--dt-white);
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* Poppy badge: a tinted fill with a poppy edge rather than a solid poppy
   ground — white on solid poppy is 3.45:1, under AA for label-sized type. */
.dt-badge--poppy {
  background: rgba(248, 72, 94, 0.18);
  border: 1px solid var(--dt-poppy);
}
.dt-badge--violet { background: var(--dt-violet); }
.dt-badge--outline {
  background: transparent;
  border: 1px solid var(--dt-surface-line);
  color: var(--dt-on-dark-mute);
}

/* Seats-left meter. The fill width is set per session on the element. */
.dt-meter {
  display: block;      /* a <span> would ignore the height entirely */
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}
.dt-meter__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--dt-poppy), var(--dt-poppy-light));
}

/* Step indicator for the booking flow. */
.dt-steps {
  display: flex;
  align-items: center;
  gap: var(--dt-s2);
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-on-dark-mute);
}
.dt-steps__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.28);
}
.dt-steps__dot.is-current { background: var(--dt-poppy); transform: scale(1.35); }
.dt-steps__dot.is-done { background: var(--dt-white); }

/* --------------------------------------------------------------------------
   12. Session cards
   Expected markup: a <label class="dt-session dt-card"> wrapping the checkbox,
   the session detail, and an empty <span class="dt-session__flag"></span>.

   Every state is signalled three ways — border treatment, a glyph, and words
   — so colour is never the only carrier, and the state text is generated here
   rather than left to the template, which guarantees the words are present.
   The template may still supply its own wording inside .dt-session__flag; the
   generated copy stands down when it does.
   -------------------------------------------------------------------------- */
/* The card is a plain block; .dt-session__pick inside it is the label, and the
   grid. Splitting the two is what lets a session carry a "Read more" control:
   it sits in the card but outside the label, so opening the blurb cannot tick
   the box. */
.dt-session {
  display: block;
  padding: var(--dt-s4);
}

.dt-session__pick {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--dt-s3) var(--dt-s4);
  align-items: start;
  cursor: pointer;
}

.dt-session:hover {
  border-color: var(--dt-surface-hi);
  background: rgba(255, 255, 255, 0.09);
}

.dt-session__input {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0.35rem 0 0;
  accent-color: var(--dt-poppy);
  cursor: pointer;
}

.dt-session__body { min-width: 0; }

.dt-session__time {
  display: block;
  font-size: var(--dt-small);
  font-weight: 700;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--dt-white);
}

/* The poppy accent on a session card is a mark, not the type. */
.dt-session__time::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 0.5em;
  border-radius: 50%;
  background: var(--dt-poppy);
  vertical-align: 0.15em;
}

.dt-session__title {
  display: block;      /* holds whether the template uses <h3> or <span> */
  margin-top: 0.15rem;
  font-size: var(--dt-h5);
  font-weight: 600;
  line-height: 1.2;
}

.dt-session__meta {
  display: block;
  margin-top: var(--dt-s2);
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

.dt-session__flag {
  display: none;                 /* revealed only once a state class applies */
  grid-column: 1 / -1;           /* its own row: a long label must never
                                    squeeze the session detail column */
  align-items: center;
  gap: 0.4em;
  margin-top: var(--dt-s3);
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-white);
}
.dt-session__flag::before { font-size: var(--dt-small); line-height: 1; }

.dt-session.is-selected .dt-session__flag,
.dt-session.is-full .dt-session__flag,
.dt-session.is-ended .dt-session__flag,
.dt-session.is-clash .dt-session__flag { display: inline-flex; }

/* Chosen — poppy edge doubled with an inset ring so the border reads at 2px
   without the 1px reflow a border-width change would cause, plus a check
   glyph and the word "Selected". */
.dt-session.is-selected {
  border-color: var(--dt-poppy);
  background: rgba(248, 72, 94, 0.10);
  box-shadow: inset 0 0 0 1px var(--dt-poppy), var(--dt-shadow-2);
}
.dt-session.is-selected:hover {
  border-color: var(--dt-poppy);
  background: rgba(248, 72, 94, 0.14);
}
.dt-session.is-selected .dt-session__flag::before { content: "\2713"; }
.dt-session.is-selected .dt-session__flag::after { content: "Selected"; }

/* The unavailable trio — dimmed, non-interactive, each with its own border
   treatment and its own words. Tells apart in greyscale and to a reader. */
.dt-session.is-full,
.dt-session.is-ended,
.dt-session.is-clash {
  opacity: 0.55;
  box-shadow: none;
}
.dt-session.is-full .dt-session__pick,
.dt-session.is-ended .dt-session__pick,
.dt-session.is-clash .dt-session__pick { cursor: not-allowed; }

/* The mandated 55% dim costs roughly three points of contrast, which would
   push muted grey text under AA. Inside a dimmed card everything reverts to
   plain white so the state stays readable rather than merely visible. */
.dt-session.is-full .dt-session__meta,
.dt-session.is-ended .dt-session__meta,
.dt-session.is-clash .dt-session__meta,
.dt-session.is-full .dt-session__time,
.dt-session.is-ended .dt-session__time,
.dt-session.is-clash .dt-session__time { color: var(--dt-white); }
.dt-session.is-full .dt-session__input,
.dt-session.is-ended .dt-session__input,
.dt-session.is-clash .dt-session__input { pointer-events: none; }

.dt-session.is-full:hover,
.dt-session.is-ended:hover,
.dt-session.is-clash:hover {
  border-color: var(--dt-surface-line);
  background: var(--dt-surface);
}

.dt-session.is-full { border-style: dashed; }
.dt-session.is-full .dt-session__flag::before { content: "\25CF"; }
.dt-session.is-full .dt-session__flag::after { content: "Fully booked"; }

.dt-session.is-ended { border-style: dotted; }
.dt-session.is-ended .dt-session__title {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.dt-session.is-ended .dt-session__flag::before { content: "\21BA"; }
.dt-session.is-ended .dt-session__flag::after { content: "Already finished"; }

/* Clash gets a hatched wash rather than a thicker border — a texture cue that
   survives greyscale and costs no layout shift. */
.dt-session.is-clash { border-style: dashed; overflow: hidden; }
.dt-session.is-clash::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: repeating-linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.07) 0 6px,
    transparent 6px 14px);
}
.dt-session.is-clash .dt-session__flag::before { content: "\26A0"; }
.dt-session.is-clash .dt-session__flag::after { content: "Overlaps a session you picked"; }

/* Template-supplied wording wins over the generated copy. */
.dt-session__flag:not(:empty)::after { content: none; }

/* --------------------------------------------------------------------------
   13. Sticky action bar
   The booking CTA follows the guest down a long agenda and clears the iPhone
   home indicator via the safe-area inset.
   -------------------------------------------------------------------------- */
.dt-actionbar {
  position: sticky;
  bottom: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--dt-s4);
  margin-top: var(--dt-s6);
  padding: var(--dt-s3) var(--dt-gutter);
  padding-bottom: calc(var(--dt-s3) + env(safe-area-inset-bottom));
  background: rgba(8, 13, 35, 0.82);
  border-top: 1px solid var(--dt-surface-line);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  box-shadow: 0 -18px 40px -28px rgba(0, 0, 0, 0.95);
}

.dt-actionbar__summary {
  order: 1;
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--dt-small);
  line-height: 1.3;
  color: var(--dt-on-dark-mute);
}
.dt-actionbar__count {
  display: block;
  font-size: var(--dt-body);
  font-weight: 700;
  color: var(--dt-white);
}

/* Consent has to be on screen at the moment the guest can act on it, which
   under a sticky "hold the CTA at the bottom" layout means inside the bar
   itself — a note back up in step 1 can scroll off long before the button
   the guest actually taps. flex-basis: 100% forces its own line regardless
   of how much room the count and button leave, ahead of the button in
   source order but placed second visually so the button still reads last. */
.dt-actionbar__consent {
  order: 2;
  flex: 1 1 100%;
  font-size: var(--dt-micro);
  line-height: 1.4;
  color: var(--dt-on-dark-mute);
}
.dt-actionbar__consent a {
  color: var(--dt-poppy-light);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.dt-actionbar__consent a:hover,
.dt-actionbar__consent a:focus-visible { color: var(--dt-poppy-lighter); }

.dt-actionbar .dt-btn { order: 3; flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   14. Error page
   -------------------------------------------------------------------------- */
.page-error {
  display: grid;
  place-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--dt-gutter);
}
.page-error .dt-card--centered { margin: 0; }
.page-error h1 { margin-block: var(--dt-s3); font-size: var(--dt-h2); }
.page-error p { color: var(--dt-on-dark-mute); }
.page-error .dt-btn { margin-top: var(--dt-s5); }

/* --------------------------------------------------------------------------
   15. Motion — one orchestrated entrance, then stillness. Set --i on each
   child to stagger it.
   -------------------------------------------------------------------------- */
@keyframes dt-rise {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}

.dt-reveal {
  animation: dt-rise var(--dt-slow) var(--dt-ease-out) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}

/* --------------------------------------------------------------------------
   16. Wider viewports. Everything above is the 375px phone.
   -------------------------------------------------------------------------- */
@media (min-width: 768px) {
  :root { --dt-gutter: var(--dt-s6); }

  .dt-section { padding-block: var(--dt-s8); }

  .dt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--dt-s5); }
  .dt-grid--single { grid-template-columns: 1fr; }

  .dt-card { padding: var(--dt-s6); }

  .dt-session { padding: var(--dt-s5); }
  .dt-session__pick { grid-template-columns: auto 1fr auto; }
  .dt-session__flag {
    grid-column: 3;
    margin-top: 0;
    align-self: center;
    text-align: right;
  }

  .dt-lede { font-size: var(--dt-h4); }

  .dt-actionbar { padding-inline: var(--dt-s6); }

  /* Hover lift is a pointer affordance. */
  .dt-card--lift:hover { transform: translateY(-3px); box-shadow: var(--dt-shadow-3); }
}

@media (min-width: 1100px) {
  :root { --dt-gutter: var(--dt-s7); }

  .dt-section { padding-block: var(--dt-s9); }
  .dt-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dt-grid--halves { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* The agenda stays one column even on desktop: one decision per row beats a
     wall of three-across cards when someone is choosing between sessions. */
  .dt-agenda { max-width: 56rem; margin-inline: auto; }

  .dt-actionbar {
    max-width: var(--dt-shell);
    margin-inline: auto;
    border-radius: var(--dt-radius-lg) var(--dt-radius-lg) 0 0;
  }
}

/* Touch devices never see the lift. */
@media (hover: none) {
  .dt-card--lift:hover { transform: none; }
  .dt-session:hover { border-color: var(--dt-surface-line); background: var(--dt-surface); }
  .dt-session.is-selected:hover { border-color: var(--dt-poppy); background: rgba(248, 72, 94, 0.10); }
}

/* --------------------------------------------------------------------------
   17. Reduced motion — honoured completely. Nothing moves, nothing fades in;
   everything is simply already there.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }

  body::before { animation: none; transform: none; }

  .dt-reveal { opacity: 1; transform: none; animation: none; }

  .dt-btn:active,
  .dt-card--lift:hover,
  .dt-steps__dot.is-current { transform: none; }
}

/* --------------------------------------------------------------------------
   18. Forced colours (Windows high contrast) — keep the structure readable
   once the OS replaces the palette wholesale.
   -------------------------------------------------------------------------- */
@media (forced-colors: active) {
  .dt-card,
  .dt-session,
  .dt-btn,
  .dt-field input,
  .dt-field select,
  .dt-field textarea { border: 1px solid CanvasText; }

  .dt-session.is-selected { border-width: 3px; }

  body::before,
  body::after { display: none; }
}

/* --------------------------------------------------------------------------
   19. Print — booth staff print the day's roster.
   -------------------------------------------------------------------------- */
@media print {
  body { background: #fff; color: var(--dt-ink); }
  body::before, body::after { display: none; }

  .dt-header, .dt-actionbar, .dt-btn, .dt-skip { display: none !important; }

  .dt-card, .dt-session {
    background: #fff;
    border: 1px solid #cfcfcf;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    break-inside: avoid;
  }
  .dt-card::before { display: none; }

  h1, h2, h3, h4, h5, h6,
  .dt-session__title,
  .dt-session__time,
  .dt-session__meta,
  .dt-muted { color: var(--dt-ink); }
}

/* --------------------------------------------------------------------------
   20. The attendee flow
   Hero, staged form, day-tabbed agenda, seat state and the confirmation pass.
   Everything here composes the primitives above; nothing restates them.
   -------------------------------------------------------------------------- */

/* --- 20.1 Hero ------------------------------------------------------------
   The theme image is the client's own fluid-ribbon artwork. It carries the
   atmosphere; the scrim carries the legibility. Two stacked washes: a vertical
   one darkening top and bottom, and a horizontal one weighted to the left,
   where the type sits. At the brightest point of the artwork the type still
   reads over roughly 22% image / 78% ground, which keeps white above 15:1 — so
   the headline never depends on which part of the ribbon lands behind it.
   -------------------------------------------------------------------------- */
.dt-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: calc(var(--dt-s6) + env(safe-area-inset-top)) var(--dt-s7);
  border-bottom: 1px solid var(--dt-surface-line);
}

.dt-hero__media,
.dt-hero__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.dt-hero__media {
  z-index: -2;
  background-color: var(--dt-ground);
  background-image: url("/assets/img/hero-1000.webp");
  background-size: cover;
  background-position: 62% 50%;
}

.dt-hero__scrim {
  z-index: -1;
  background:
    linear-gradient(180deg,
      rgba(8, 13, 35, 0.88) 0%,
      rgba(8, 13, 35, 0.58) 46%,
      rgba(8, 13, 35, 0.97) 100%),
    linear-gradient(90deg,
      rgba(8, 13, 35, 0.92) 0%,
      rgba(8, 13, 35, 0.48) 62%,
      rgba(8, 13, 35, 0.16) 100%);
}

.dt-hero__inner > * + * { margin-top: var(--dt-s4); }

.dt-hero__logo {
  width: auto;
  height: 30px;
  margin-bottom: var(--dt-s5);
}

.dt-hero__detail {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--dt-s3);
  font-size: var(--dt-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--dt-white);
}

.dt-hero__detail span { white-space: nowrap; }

.dt-hero__detail span + span::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 4px;
  margin-right: 0.75em;
  border-radius: 50%;
  background: var(--dt-poppy);
  vertical-align: 0.2em;
}

/* --- 20.2 Staged sections -------------------------------------------------
   The two steps are numbered in the legend rather than gated behind each
   other: the agenda has to be readable the moment the QR code resolves.
   -------------------------------------------------------------------------- */
.dt-step {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;     /* a fieldset defaults to min-content and breaks the grid */
}

.dt-step__legend {
  display: block;
  width: 100%;
  padding: 0;
  margin-bottom: var(--dt-s2);
  font-size: var(--dt-h3);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: var(--dt-white);
  text-wrap: balance;
}

.dt-step__no {
  display: inline-grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-right: var(--dt-s3);
  border: 1px solid var(--dt-poppy);
  border-radius: 50%;
  background: rgba(248, 72, 94, 0.18);
  font-size: var(--dt-small);
  font-weight: 700;
  vertical-align: 0.1em;
}

.dt-step__note {
  max-width: var(--dt-measure);
  margin-bottom: var(--dt-s5);
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

.dt-req { color: var(--dt-poppy); font-weight: 700; }

/* --- 20.3 Day tabs --------------------------------------------------------
   Shipped hidden. app.js reveals them and takes the panels over, so a guest
   with JavaScript disabled gets all four days stacked and never meets a
   control that does nothing.
   -------------------------------------------------------------------------- */
.dt-daytabs {
  display: flex;
  gap: var(--dt-s2);
  margin-bottom: var(--dt-s5);
  padding-bottom: var(--dt-s1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.dt-daytabs::-webkit-scrollbar { display: none; }
.dt-daytabs[hidden] { display: none; }

.dt-daytab {
  flex: 1 0 auto;
  display: grid;
  gap: 1px;
  align-content: center;
  min-height: var(--dt-tap);
  padding: var(--dt-s2) var(--dt-s3);
  border: 1px solid var(--dt-surface-line);
  border-radius: var(--dt-radius);
  background: var(--dt-surface-2);
  color: var(--dt-on-dark-mute);
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    border-color var(--dt-fast) var(--dt-ease),
    background-color var(--dt-fast) var(--dt-ease),
    color var(--dt-fast) var(--dt-ease);
}

.dt-daytab__day {
  font-size: var(--dt-small);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.dt-daytab__date {
  font-size: var(--dt-micro);
  font-weight: 500;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
}

/* The chosen day is signalled three ways — a poppy edge, a tinted fill and a
   brightened label — so it survives greyscale. A solid poppy fill is avoided
   deliberately: white on it is 3.45:1, under AA for label-sized type. */
.dt-daytab[aria-selected="true"] {
  border-color: var(--dt-poppy);
  background: rgba(248, 72, 94, 0.18);
  color: var(--dt-white);
  box-shadow: inset 0 -3px 0 0 var(--dt-poppy);
}

.dt-daytab:hover { border-color: var(--dt-surface-hi); color: var(--dt-white); }

/* Live count of picks on a day the guest is not currently looking at — the
   whole point of tabbing an agenda is that nothing hides behind a tab. */
.dt-daytab__count {
  justify-self: center;
  margin-top: 2px;
  padding: 0 0.45rem;
  border-radius: 999px;
  background: var(--dt-poppy);
  color: var(--dt-white);
  font-size: var(--dt-micro);
  font-weight: 700;
  line-height: 1.5;
}
.dt-daytab__count:empty { display: none; }

/* --- 20.4 Agenda ---------------------------------------------------------- */
.dt-agenda__day + .dt-agenda__day { margin-top: var(--dt-s6); }
.dt-agenda__day[hidden] { display: none; }

.dt-agenda__heading {
  margin-bottom: var(--dt-s4);
  font-size: var(--dt-h6);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-on-dark-mute);
}

.dt-agenda__list { display: grid; gap: var(--dt-s3); }

/* --- 20.5 Session card ----------------------------------------------------
   .session-card is the contract app.js reads; .dt-session .dt-card is what it
   looks like. Two names on purpose: renaming the styling never breaks the
   script, and vice versa.
   -------------------------------------------------------------------------- */
.session-card:has(input:focus-visible) { border-color: var(--dt-poppy); }

.session-card__seats {
  display: block;
  margin-top: var(--dt-s2);
  font-size: var(--dt-small);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--dt-white);
}

/* Decorative only. The seat count is stated in words directly above it, so the
   bar carries no information of its own. */
.session-card__meter { max-width: 9rem; margin-top: var(--dt-s2); }

.session-card__note,
.session-card__rejected {
  display: block;
  margin: var(--dt-s3) 0 0;
  padding: var(--dt-s2) var(--dt-s3);
  border-left: 3px solid var(--dt-poppy);
  border-radius: 0 var(--dt-radius) var(--dt-radius) 0;
  background: rgba(248, 72, 94, 0.12);
  font-size: var(--dt-small);
  font-weight: 500;
  color: var(--dt-white);
}

/* A pick the server refused. It stays enabled — the guest has to be able to
   untick it — so it is marked rather than dimmed, unlike the unavailable trio.
   Declared after .is-selected so the marking wins on a card that is both. */
.dt-session.is-rejected {
  border-color: var(--dt-poppy);
  border-style: solid;
  background: rgba(248, 72, 94, 0.12);
  box-shadow: inset 3px 0 0 0 var(--dt-poppy), var(--dt-shadow-2);
}
.dt-session.is-rejected .dt-session__flag { display: inline-flex; }
.dt-session.is-rejected .dt-session__flag::before { content: "\26A0"; }
/* A refused pick is still ticked, so .is-selected is on the card too and would
   otherwise leave the badge reading "Selected" next to a warning triangle. */
.dt-session.is-rejected .dt-session__flag::after { content: "Not booked"; }

/* --- 20.5b The session blurb ----------------------------------------------
   The agenda copy runs from one line to fourteen hundred characters. A short
   one is simply printed; a long one is folded into a <details>, whose
   <summary> doubles as the opening sentence so the fold still says something
   about the talk rather than only "Read more".

   <details> rather than a scripted toggle: it works with JavaScript off, it is
   a real disclosure to a screen reader without any ARIA of ours, and it sits
   outside the picking label so opening it never ticks the box.
   -------------------------------------------------------------------------- */
.dt-session__desc {
  margin-top: var(--dt-s3);
  font-size: var(--dt-small);
  line-height: 1.55;
  color: var(--dt-on-dark-mute);
}
.dt-session__desc p { margin: 0; }
.dt-session__desc p + p { margin-top: var(--dt-s3); }

/* The blurb is indented to the title's column so it reads as part of the
   session detail rather than as a note under the whole card. 1.5rem is the
   checkbox, and the gap matches .dt-session__pick's column gap. */
.dt-session__desc,
.dt-session__more { padding-left: calc(1.5rem + var(--dt-s4)); }

.dt-session__more { margin-top: var(--dt-s3); }

.dt-session__more-toggle {
  display: block;
  cursor: pointer;
  font-size: var(--dt-small);
  line-height: 1.55;
  color: var(--dt-on-dark-mute);
  list-style: none;             /* Firefox */
}
/* Safari and older Chrome still draw the disclosure triangle without this. */
.dt-session__more-toggle::-webkit-details-marker { display: none; }

.dt-session__more-toggle:focus-visible {
  outline: 2px solid var(--dt-poppy);
  outline-offset: 3px;
  border-radius: var(--dt-radius);
}

/* The cue is generated rather than written into the template so its wording
   always matches the state of the fold it belongs to. */
.dt-session__more-cue {
  display: inline-block;
  margin-top: var(--dt-s2);
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-white);
  border-bottom: 1px solid rgba(255, 255, 255, 0.35);
}
.dt-session__more-cue::after { content: "Read more \203A"; }
.dt-session__more[open] .dt-session__more-cue::after { content: "Show less \2039"; }

/* Open: the summary's own lede would repeat the first line of the full text,
   so it stands down and the <details> body carries the whole blurb. */
.dt-session__more[open] .dt-session__more-lede { display: none; }
.dt-session__more[open] .dt-session__more-cue { margin-top: 0; }
.dt-session__more[open] .dt-session__desc { margin-top: var(--dt-s3); }
.dt-session__more .dt-session__desc { padding-left: 0; }

/* --- 20.5c Interludes ------------------------------------------------------
   The violin sets between talks. They are on the agenda so the running order
   reads straight, and they are deliberately not cards: no border, no seat
   count, no checkbox, nothing to click. The rule down the left is the only
   mark, which keeps a run of four of them from competing with the sessions
   they sit between.
   -------------------------------------------------------------------------- */
.dt-interlude {
  display: grid;
  /* The same two columns as .dt-session__pick, and the same gap: the times and
     titles of a whole day line up whether or not the row can be booked. */
  grid-template-columns: auto 1fr;
  gap: var(--dt-s3) var(--dt-s4);
  align-items: start;
  padding: var(--dt-s4);
  border-color: rgba(232, 194, 106, 0.34);
  background: rgba(232, 194, 106, 0.07);
  cursor: default;
}

/* No hover response: unlike a session card there is nothing here to press. */
.dt-interlude:hover {
  border-color: rgba(232, 194, 106, 0.34);
  background: rgba(232, 194, 106, 0.07);
}

/* Stands where the checkbox stands, at the same 1.5rem, so the body column
   starts on the same line as a talk's. */
.dt-interlude__mark {
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.35rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(232, 194, 106, 0.5);
  background: rgba(232, 194, 106, 0.12);
  font-size: var(--dt-small);
  line-height: 1;
  color: var(--dt-gold);
}
.dt-interlude__mark::before { content: "\266A"; }   /* eighth note */

.dt-interlude__body { min-width: 0; }

.dt-interlude__time {
  display: block;
  font-size: var(--dt-small);
  font-weight: 700;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--dt-gold);
}

.dt-interlude__title {
  display: block;
  margin-top: 0.15rem;
  /* A step down from .dt-session__title: an interlude is part of the running
     order, not a thing to weigh up against the talk above it. */
  font-size: var(--dt-h6);
  font-weight: 600;
  line-height: 1.25;
  color: var(--dt-white);
}

.dt-interlude__note {
  grid-column: 1 / -1;      /* its own row on a phone, beside the body above 768 */
  display: block;
  margin-top: 0;
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-on-dark-mute);
}

@media (min-width: 768px) {
  .dt-interlude { padding: var(--dt-s5); grid-template-columns: auto 1fr auto; }
  .dt-interlude__note { grid-column: 3; align-self: center; text-align: right; }
}

/* On paper the wash disappears with every other card fill, so the gold has to
   survive as the rule down the left. */
@media print {
  .dt-interlude {
    background: none;
    border-left: 3px solid var(--dt-gold-deep);
  }
  .dt-interlude__time { color: var(--dt-gold-deep); }
  .dt-interlude__title { color: var(--dt-ink); }
}

/* --- 20.6 Action bar on a phone -------------------------------------------
   Side by side, "3 sessions selected" and a 19px/700 button need about 350px,
   which a 375px phone minus its gutters does not have — the count wraps to
   three lines and the bar grows to a fifth of the viewport. Below 520px the
   bar stacks instead: a compact count line, the consent line (see 13 above —
   it forces its own row at every width, not only here), and a full-width
   primary, the standard checkout-bar shape. flex-wrap is already on at every
   width now that consent needs its own row; only the row spacing, the count
   size and hiding the longer hint are phone-specific.
   -------------------------------------------------------------------------- */
@media (max-width: 519px) {
  .dt-actionbar {
    row-gap: var(--dt-s2);
    padding-block: var(--dt-s2);
    padding-bottom: calc(var(--dt-s2) + env(safe-area-inset-bottom));
  }
  .dt-actionbar__summary { flex: 1 1 100%; }
  .dt-actionbar__count { font-size: var(--dt-small); }
  .dt-actionbar__hint { display: none; }
  .dt-actionbar .dt-btn { flex: 1 1 100%; }
}

/* --- 20.7 Confirmation ----------------------------------------------------
   A pass rather than a receipt: each booked slot is a stub with a perforated
   poppy edge, so the page reads as something to keep.
   -------------------------------------------------------------------------- */
.dt-pass { display: grid; gap: var(--dt-s3); }

.dt-pass__stub {
  position: relative;
  padding: var(--dt-s4) var(--dt-s4) var(--dt-s4) var(--dt-s5);
  border: 1px solid var(--dt-surface-line);
  border-left: 3px dashed var(--dt-poppy);
  border-radius: 0 var(--dt-radius) var(--dt-radius) 0;
  background: var(--dt-surface);
}

.dt-pass__when {
  font-size: var(--dt-small);
  font-weight: 700;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--dt-white);
}

.dt-pass__what {
  margin-top: 0.15rem;
  font-size: var(--dt-h5);
  font-weight: 600;
  line-height: 1.2;
}

.dt-pass__who {
  margin-top: var(--dt-s2);
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

.dt-keyline {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--dt-s2) var(--dt-s4);
  margin: 0;
  font-size: var(--dt-small);
}
.dt-keyline dt { color: var(--dt-on-dark-mute); white-space: nowrap; }
.dt-keyline dd { margin: 0; color: var(--dt-white); overflow-wrap: anywhere; }

/* --- 20.8 Wider viewports ------------------------------------------------- */
@media (min-width: 768px) {
  .dt-hero { padding-block: calc(var(--dt-s7) + env(safe-area-inset-top)) var(--dt-s8); }
  .dt-hero__media { background-image: url("/assets/img/hero-1800.webp"); }
  .dt-hero__logo { height: 70px; }
  .dt-hero__detail { font-size: var(--dt-body); }

  .dt-step__legend { font-size: var(--dt-h2); }

  .dt-daytab { padding-inline: var(--dt-s4); }

  .dt-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--dt-s5); }
  .dt-fields .dt-field + .dt-field { margin-top: 0; }
  .dt-fields .dt-field--wide { grid-column: 1 / -1; }
}

@media (min-width: 1100px) {
  .dt-hero__inner { max-width: 52rem; }

  /* Left-aligned editorial column rather than the full 72rem shell: a name
     field stretched to 1050px is unreadable, and this keeps step 1 and the
     agenda on the same left rail as the hero headline instead of drifting into
     a centred block of their own. */
  .dt-flow { max-width: 56rem; }
}

/* --------------------------------------------------------------------------
   21. Admin dashboard
   The registrations grid is a real <table> — sortable by the browser's
   find-in-page, copy-pasteable into a spreadsheet — that collapses into
   stacked cards below the tablet breakpoint. Booth staff work this mostly on
   a laptop, occasionally on a phone; a 7-column table is unusable one-handed,
   so each row becomes a card and each cell becomes a labelled line instead of
   forcing a sideways scroll.
   -------------------------------------------------------------------------- */
/* overflow-x: auto alone makes .dt-table-wrap scroll its own content
   correctly, but a wide <table> child still leaks into the *document*'s
   scrollable overflow at >=768px (verified: window.scrollTo() moved the
   whole page sideways even though .dt-table-wrap's own scrollWidth/
   clientWidth already behaved like a contained scrollport). `contain:
   layout` makes this wrapper a genuine containment boundary, so the
   overflowing table can no longer inflate document.documentElement's
   scrollWidth -- verified before/after at 375/767/768/1024/1280px. */
.dt-table-wrap { overflow-x: auto; contain: layout; }

.dt-table { width: 100%; border-collapse: collapse; }

.dt-table th,
.dt-table td {
  padding: var(--dt-s3);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--dt-surface-line);
}

.dt-table thead th {
  font-size: var(--dt-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dt-on-dark-mute);
  white-space: nowrap;
}

.dt-table tbody tr:hover { background: var(--dt-surface-2); }

.dt-table__chips { display: flex; flex-wrap: wrap; gap: var(--dt-s2); }

.dt-table__remove-form { display: inline-block; }

@media (max-width: 767px) {
  /* Visually hidden, not removed: the column headings still name each field
     for a screen reader even though the printed row supplies its own label
     per line. */
  .dt-table thead {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .dt-table, .dt-table tbody, .dt-table tr, .dt-table td { display: block; width: 100%; }

  .dt-table tr {
    margin-bottom: var(--dt-s4);
    padding: var(--dt-s4);
    border: 1px solid var(--dt-surface-line);
    border-radius: var(--dt-radius-lg);
    background: var(--dt-surface);
  }

  .dt-table td {
    display: flex;
    gap: var(--dt-s4);
    justify-content: space-between;
    align-items: flex-start;
    padding: var(--dt-s2) 0;
    border-bottom: 0;
  }

  .dt-table td::before {
    content: attr(data-label);
    flex: none;
    font-size: var(--dt-micro);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--dt-on-dark-mute);
  }

  .dt-table td[data-label="Actions"] { justify-content: flex-end; }
}

@media (min-width: 768px) {
  .dt-filters .dt-fields { align-items: end; }
}

/* --------------------------------------------------------------------------
   22. Capacity board & check-in roster (Task 13)
   The board is 16 cards grouped under four day headings — a grid, not a
   table, so it stays a glanceable wall chart rather than needing the
   .dt-table mobile-card collapse. The roster below it reuses .dt-table
   as-is: it already collapses to one card per attendee under 768px, which is
   exactly the one-handed-at-the-door layout check-in needs.
   -------------------------------------------------------------------------- */
.admin-main {
  width: 100%;
  max-width: var(--dt-shell);
  margin-inline: auto;
  padding: var(--dt-s6) var(--dt-gutter) calc(var(--dt-s7) + env(safe-area-inset-bottom));
}

.admin-main h1 { font-size: var(--dt-h2); }

.admin-day { margin-top: var(--dt-s7); }
.admin-day:first-of-type { margin-top: var(--dt-s5); }

.admin-day h2 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--dt-s3);
  font-size: var(--dt-h4);
  font-weight: 600;
}

.admin-day__date {
  font-size: var(--dt-small);
  font-weight: 400;
  color: var(--dt-on-dark-mute);
}

.admin-board {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--dt-s4);
  margin-top: var(--dt-s4);
}

@media (min-width: 640px) {
  .admin-board { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1100px) {
  .admin-board { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Each card is a link to its roster — the whole card is the tap target, not
   just a small "view" affordance, since this is worked on a phone. */
.admin-board__card {
  display: flex;
  flex-direction: column;
  gap: var(--dt-s2);
  padding: var(--dt-s4);
  background: var(--dt-surface);
  border: 1px solid var(--dt-surface-line);
  border-left: 3px solid var(--dt-surface-line);
  border-radius: var(--dt-radius-lg);
  color: var(--dt-white);
  text-decoration: none;
  box-shadow: var(--dt-shadow-1);
  transition:
    border-color var(--dt-fast) var(--dt-ease),
    background-color var(--dt-fast) var(--dt-ease),
    transform var(--dt-fast) var(--dt-ease);
}

.admin-board__card:hover {
  border-color: var(--dt-surface-hi);
  background: var(--dt-surface-hi);
  transform: translateY(-2px);
}

/* An interlude on the board: same shape so the running order still reads in
   order, but flatter and quieter, because there is nothing here to manage
   except the wording and the clock. */
.admin-board__card--interlude {
  background: transparent;
  box-shadow: none;
  border-left-color: var(--dt-surface-line);
  color: var(--dt-on-dark-mute);
}
.admin-board__card--interlude:hover {
  background: var(--dt-surface);
  transform: none;
}
.admin-board__card--interlude .admin-board__title {
  font-weight: 500;
  color: var(--dt-on-dark-mute);
}

.admin-board__title {
  font-size: var(--dt-h6);
  font-weight: 600;
  line-height: 1.25;
}

.admin-board__speaker {
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

/* The count is the load-bearing signal — fill state is colour-coded on the
   card border and bar too, but this text is what actually says how full a
   session is, so colour is never the only carrier (WCAG 1.4.1). */
.admin-board__count {
  margin-top: auto;
  padding-top: var(--dt-s2);
  font-size: var(--dt-small);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.admin-board__bar {
  display: block;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}

.admin-board__bar span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--dt-violet);
}

/* Low: plenty of seats, calm violet. Medium: filling up, poppy gradient.
   Full: solid poppy plus a dashed edge, so the state still reads without
   colour at all (as .dt-session's own is-full state already does). */
.admin-board__card.is-low { border-left-color: var(--dt-violet); }

.admin-board__card.is-medium { border-left-color: var(--dt-poppy-light); }
.admin-board__card.is-medium .admin-board__bar span {
  background: linear-gradient(90deg, var(--dt-poppy-light), var(--dt-poppy));
}

.admin-board__card.is-full {
  border-left-color: var(--dt-poppy);
  border-left-width: 4px;
  background: rgba(248, 72, 94, 0.08);
}
.admin-board__card.is-full .admin-board__bar span { background: var(--dt-poppy); }

/* --- Roster -------------------------------------------------------------- */
.admin-roster__header { margin-top: var(--dt-s5); }

.admin-roster__counter {
  font-size: var(--dt-h5);
  font-weight: 700;
}

.admin-roster__table-wrap,
.admin-roster__empty { margin-top: var(--dt-s6); }

.admin-roster__empty { text-align: center; }

.admin-roster__checkin {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--dt-s2);
}

.admin-roster__checkin form { width: 100%; max-width: 12rem; }

/* Checked-in state text — the second, non-colour signal that goes with
   tr.is-present below. */
.admin-roster__status {
  font-size: var(--dt-small);
  font-weight: 600;
  color: var(--dt-white);
  white-space: nowrap;
}

/* Toggle is a full .dt-btn, so it already clears the 48px tap-target floor —
   pressed quickly, one-handed, while talking to a guest. */
.admin-roster__toggle { width: 100%; }

.dt-table tr.is-present { background: rgba(248, 72, 94, 0.06); }
.dt-table tr.is-present:hover { background: rgba(248, 72, 94, 0.1); }

@media (max-width: 767px) {
  .dt-table td[data-label="Check-in"] { align-items: center; }
  .admin-roster__checkin { align-items: stretch; width: 100%; }
  .admin-roster__checkin form { max-width: none; }
}


/* --------------------------------------------------------------------------
   Confirmation overlay
   --------------------------------------------------------------------------
   A guest who has just submitted the form is standing at a booth, often mid
   conversation, and needs one unmissable signal that the seats are actually
   held. The page beneath already says so in full — this overlay only raises
   the volume of the same fact.

   Built on <dialog>, so the modality is the browser's: focus trapping, inert
   background and Esc all come from the element rather than from script we
   would have to get right. With JavaScript off the dialog never opens and the
   page reads exactly as before, which is why nothing unique lives in here.
   -------------------------------------------------------------------------- */
.dt-modal {
  width: min(30rem, calc(100vw - 2 * var(--dt-s4)));
  max-height: calc(100dvh - 2 * var(--dt-s5));
  padding: 0;
  overflow: auto;
  color: var(--dt-white);
  background: linear-gradient(160deg, #171a3a 0%, var(--dt-ground-2) 62%);
  border: 1px solid var(--dt-surface-line);
  border-radius: var(--dt-radius-lg);
  box-shadow: var(--dt-shadow-3);
}

.dt-modal::backdrop {
  background: rgba(4, 6, 18, 0.72);
  backdrop-filter: blur(4px);
}

.dt-modal__panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--dt-s4);
  padding: var(--dt-s7) var(--dt-s5) var(--dt-s6);
  text-align: center;
}

.dt-modal__title {
  margin: 0;
  font-size: var(--dt-h3);
  font-weight: 800;
  line-height: 1.15;
}

.dt-modal__body {
  max-width: 26rem;
  margin: 0;
  color: var(--dt-on-dark-mute);
}

/* The reference is the one thing a guest may need to read aloud to a colleague
   at the counter, so it gets tabular figures and room to breathe. */
.dt-modal__ref {
  margin: 0;
  font-size: var(--dt-small);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dt-white);
  font-variant-numeric: tabular-nums;
}

.dt-modal__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--dt-s3);
  justify-content: center;
  width: 100%;
  margin-top: var(--dt-s2);
}

/* Sized by their labels, not forced into equal columns: at 19px/700 "View my
   booking" broke onto two lines in a fixed 12rem track. When the pair no longer
   fits one row they wrap and each fills its own. */
.dt-modal__actions .dt-btn {
  flex: 1 1 auto;
  white-space: nowrap;
}

/* The mark ---------------------------------------------------------------- */
.dt-modal__mark {
  width: 88px;
  height: 88px;
  color: var(--dt-success);
}

.dt-modal__mark svg { display: block; width: 100%; height: 100%; }

.dt-modal__mark-ring {
  fill: var(--dt-success-deep);
  stroke: var(--dt-success);
  stroke-width: 2;
}

.dt-modal__mark-tick {
  fill: none;
  stroke: var(--dt-white);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Motion: the ring scales up under the tick, which then draws itself. Both are
   decorative confirmations of a state the text already states, so under
   reduced-motion they simply start finished. */
@media (prefers-reduced-motion: no-preference) {
  .dt-modal[open] .dt-modal__mark-ring {
    transform-origin: 26px 26px;
    animation: dt-mark-pop var(--dt-slow) var(--dt-ease-out) both;
  }

  .dt-modal[open] .dt-modal__mark-tick {
    stroke-dasharray: 36;
    stroke-dashoffset: 36;
    animation: dt-mark-draw var(--dt-mid) var(--dt-ease-out) 220ms both;
  }

  .dt-modal[open] {
    animation: dt-modal-in var(--dt-mid) var(--dt-ease-out) both;
  }
}

@keyframes dt-mark-pop {
  from { transform: scale(0.4); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}

@keyframes dt-mark-draw {
  to { stroke-dashoffset: 0; }
}

@keyframes dt-modal-in {
  from { transform: translateY(12px) scale(0.97); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

@media (max-width: 480px) {
  .dt-modal__panel { padding: var(--dt-s6) var(--dt-s4) var(--dt-s5); }
  .dt-modal__title { font-size: var(--dt-h4); }
  .dt-modal__mark { width: 72px; height: 72px; }
}

/* Forced-colours mode replaces our palette wholesale; the tick must still read
   as a mark rather than dissolving into the panel. */
@media (forced-colors: active) {
  .dt-modal { border: 1px solid CanvasText; }
  .dt-modal__mark-ring { fill: Canvas; stroke: CanvasText; }
  .dt-modal__mark-tick { stroke: CanvasText; }
}


/* --------------------------------------------------------------------------
   Admin editing
   --------------------------------------------------------------------------
   Registration and session edit forms, plus the row and card affordances that
   lead to them. Everything here is behind the admin login and is used standing
   up at a booth, so tap targets stay at the 48px floor and destructive actions
   keep the poppy that means "stop and read this".
   -------------------------------------------------------------------------- */
.dt-backlink {
  color: var(--dt-on-dark-mute);
  text-decoration: none;
  font-weight: 600;
  font-size: 1.25rem;
}

.dt-backlink:hover { color: var(--dt-white); }

/* Row actions wrap rather than squeeze: three controls in a table cell on a
   phone would otherwise shrink below a usable tap target. */
.dt-table__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--dt-s2);
  align-items: center;
}

.dt-list {
  margin: 0;
  padding-left: 1.15rem;
  display: grid;
  gap: var(--dt-s1);
}

.dt-badge--full {
  background: rgba(248, 72, 94, 0.18);
  border: 1px solid var(--dt-poppy);
  color: var(--dt-white);
}

.admin-main__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--dt-s4);
  align-items: center;
  justify-content: space-between;
}

/* The capacity card holds two destinations now — roster and edit — so the card
   is a container and only its body is the link. */
.admin-board__main {
  display: flex;
  flex-direction: column;
  gap: var(--dt-s2);
  color: inherit;
  text-decoration: none;
}

.admin-board__actions {
  display: flex;
  gap: var(--dt-s2);
  margin-top: var(--dt-s3);
  padding-top: var(--dt-s3);
  border-top: 1px solid var(--dt-line-soft);
}

.admin-board__actions .dt-btn { flex: 1 1 auto; }

/* Session picker ---------------------------------------------------------- */
.admin-pick {
  margin: 0;
  padding: 0;
  border: 0;
}

.admin-pick legend {
  padding: 0;
  margin-bottom: var(--dt-s2);
}

.admin-pick__row {
  display: flex;
  align-items: center;
  gap: var(--dt-s3);
  min-height: var(--dt-tap);
  padding: var(--dt-s3);
  margin-bottom: var(--dt-s2);
  background: var(--dt-surface-2);
  border: 1px solid var(--dt-surface-line);
  border-radius: var(--dt-radius);
  cursor: pointer;
  transition:
    border-color var(--dt-fast) var(--dt-ease),
    background-color var(--dt-fast) var(--dt-ease);
}

.admin-pick__row:hover { border-color: var(--dt-surface-hi); background: var(--dt-surface-hi); }

.admin-pick__row.is-selected {
  border-color: var(--dt-poppy);
  background: rgba(248, 72, 94, 0.10);
}

/* Full and ended are dimmed but never disabled: these are exactly the rows an
   admin reaches for when they tick Override. */
.admin-pick__row.is-full,
.admin-pick__row.is-ended { opacity: 0.62; }

.admin-pick__row.is-selected.is-full,
.admin-pick__row.is-selected.is-ended { opacity: 1; }

.admin-pick__box {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  accent-color: var(--dt-poppy);
}

.admin-pick__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.admin-pick__title { font-weight: 600; }

.admin-pick__meta {
  font-size: var(--dt-small);
  color: var(--dt-on-dark-mute);
}

.admin-pick__state {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--dt-s2);
  justify-content: flex-end;
}

/* Consequential checkboxes ------------------------------------------------ */
.admin-toggle {
  display: flex;
  align-items: flex-start;
  gap: var(--dt-s3);
  padding: var(--dt-s4);
  background: var(--dt-surface-2);
  border: 1px solid var(--dt-surface-line);
  border-radius: var(--dt-radius);
  cursor: pointer;
}

.admin-toggle input {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  accent-color: var(--dt-poppy);
}

.admin-toggle > span {
  display: flex;
  flex-direction: column;
  gap: var(--dt-s1);
}

/* The override waives the seat cap the whole system exists to hold, so it is
   marked as the exception it is rather than sitting flush with the rest. */
.admin-toggle--warn {
  border-color: rgba(248, 72, 94, 0.5);
  background: rgba(248, 72, 94, 0.06);
}

@media (max-width: 600px) {
  .admin-pick__row { flex-wrap: wrap; }
  .admin-pick__state { width: 100%; justify-content: flex-start; padding-left: calc(20px + var(--dt-s3)); }
}


/* Which login you are on, stated in the header. A read-only session that looks
   identical to an admin one gets reported as "the buttons are broken". */
.dt-header__role {
  margin-left: 0.5rem;
}

/* A tick rendered only as a glyph is invisible to a screen reader; this pairs
   with a visible aria-hidden glyph to give the same row a text equivalent. */
.dt-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
