/* ==========================================================================
   APMF 2027 Bangkok — theme v2
   Design system from the `APMF 2027.dc.html` prototype handoff.
   v1 is kept in backups/theme-v1/ — see that folder's RESTORE.md.
   ========================================================================== */

:root {
  /* Brand gradient, from the client's "APMF 2027" wordmark artwork */
  --grad-start: #5b6bf5;
  --grad-mid: #a15fd0;
  --grad-end: #f472b6;
  --brand-gradient: linear-gradient(95deg, var(--grad-start) 0%, var(--grad-mid) 55%, var(--grad-end) 100%);
  /* lighter version, for gradient text sitting on the dark hero photo */
  --brand-gradient-light: linear-gradient(95deg, #8f9dff 0%, #cf9cf3 52%, #ffa2d0 100%);

  --purple: #7c4bb5;
  --purple-hover: #63359a;
  --purple-light: #8a55c4;
  --purple-tint: #f7eefc;
  --magenta: #d6469a;
  --magenta-dark: #b03680;
  --magenta-bright: #e0559f;
  --magenta-hover: #c1408c;
  --deep: #4b2a72;
  --deep-muted: #7a5a9c;
  --pink-pale: #f3b9da;
  --lilac-pale: #efe4f7;
  --off-white: #fbf6fe;
  --banner-base: #1b1046;

  --bg: oklch(0.98 0.008 70);
  --surface: oklch(0.99 0.006 70);
  --line: oklch(0.89 0.012 70);
  --line-light: oklch(0.92 0.01 70);
  /* The whole scale a step darker, 29 Sep 2026. The client set the site
     beside apmf2025malaysia.org.my, whose body copy is flat #000, and asked
     for darker ink. These keep the warm tint that belongs to the brand rather
     than going to black, but the gap between the levels is kept so that
     "muted" and "faint" still read as quieter than the main ink. */
  --ink: oklch(0.20 0.02 60);
  --ink-muted: oklch(0.30 0.018 60);
  --ink-soft: oklch(0.38 0.015 60);
  --ink-faint: oklch(0.45 0.015 60);

  --green: #09846f;
  --red: #b42318;

  --font-serif: "Instrument Serif", Georgia, serif;
  --font-sans: "IBM Plex Sans Thai", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  --container: 1200px;
  /* the side margin every band and section uses; widened below, where a phone
     or tablet has no window frame of its own to give the text any air */
  --gutter: 44px;

  /* v1 aliases, so anything not yet reworked still resolves to a real colour */
  --blue: var(--purple);
  --blue-dark: var(--purple-hover);
  --pink: var(--magenta);
  --muted: var(--ink-soft);
  --panel: var(--surface);
}

* {
  box-sizing: border-box;
}

body,
body.app-body,
body.admin-body {
  margin: 0;
  min-height: 100vh;
  color: var(--ink);
  background: var(--bg);
  font-family: var(--font-sans);
  /* The base both sides start from. The public pages are lifted to 18px far
     below, in the .app-body block -- that is the one place the two sides are
     told apart, and a second opinion up here would only be another thing to
     keep in step. */
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--purple);
  text-decoration: none;
}

a:hover {
  color: var(--magenta);
  text-decoration: none;
}

h1,
h2,
h3,
.h4,
.h5 {
  text-wrap: pretty;
}

h1,
h2 {
  font-family: var(--font-serif);
  font-weight: 400;
  letter-spacing: -0.01em;
}

h1 {
  font-size: clamp(34px, 4.4vw, 52px);
  line-height: 1.08;
}

/* The section heading, and the only place its size is set. 34 on a desktop
   and 28 below 992px, the two figures the client named -- a clamp used to
   stand here and it only reached 34 on a wide monitor, so a laptop got
   something in between and looked like a third size. `.h4` rides along
   because it is the class that makes a non-heading element read as one. */
h2,
.h4 {
  font-size: 34px;
  line-height: 1.15;
}

@media (max-width: 991px) {
  h2,
  .h4 {
    font-size: 28px;
  }
}

h3,
.h5 {
  font-family: var(--font-sans);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.35;
}

p {
  text-wrap: pretty;
}

/* --- shared layout ------------------------------------------------------ */

.app-shell,
.admin-shell {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 56px var(--gutter) 72px;
}

.page-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 32px;
}

.page-head h1 {
  margin: 0 0 8px;
}

.page-head p {
  max-width: 760px;
  margin: 0;
  /* Body size. It sits under a page title like a standfirst does, but it is
     not one -- .page-lead is the standfirst, and two paragraphs in the same
     place at 30px would leave nothing for the eye to step down to. */
  font-size: 18px;
  color: var(--ink-soft);
}

.page-lead {
  /* 95ch, not 60. The committee page's standfirst is 105 characters and broke
     with "Bangkok 2027." alone on a second line; 95ch is the point at which it
     sits on one. Measured against the longest standfirst on the site too -- at
     173 characters that one still wraps, but to two full lines rather than
     three. */
  max-width: 95ch;
  margin: 0 0 40px;
  /* Lead size, shared with the other standfirsts -- see .intro-lead. */
  font-size: 22px;
  line-height: 1.3;
  color: var(--ink-muted);
}

@media (max-width: 991px) {
  .page-lead {
    font-size: 20px;
  }
}

.page-lead-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 32px;
}

.page-lead-row .page-lead {
  margin-bottom: 0;
  /* There is a button beside it, not a column of text, so the lead can run
     wider here than the 60ch a full-width paragraph is held to.

     max-content lets a one-sentence lead take exactly the width it needs and
     stay on one line, which a fixed cap cannot do: 82ch was 812px against a
     sentence that wanted 825, so "on this page." dropped to a second line and
     the width of the break moved with the deadline date. It is still held to
     the width of the row, and to 92ch, so a longer lead wraps at a readable
     measure rather than running the full width of a wide screen. */
  width: max-content;
  max-width: min(100%, 92ch);
}

/* the small uppercase mono line that opens most sections */
.eyebrow {
  display: block;
  margin: 0 0 12px;
  font-family: var(--font-mono);
  /* Left at 16.5 while the body copy went to 18: this is a label above a
     heading, not a line anybody reads, and it is set in caps and mono where
     the same number looks larger than it does in the running text. */
  font-size: 16.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--magenta);
}

.mono-micro {
  font-family: var(--font-mono);
  /* A label, not something read in sentences, so it stays below the body copy
     -- see .eyebrow. The site-wide lift to 18px had taken it level with the
     text it labels. */
  font-size: 16.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.tbc {
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.06em;
  color: var(--magenta);
}

/* --- page banner (inner pages) ------------------------------------------ */

.page-banner {
  position: relative;
  width: 100%;
  min-height: 180px;
  display: flex;
  align-items: center;
  background-color: var(--banner-base);
  background-image: linear-gradient(
      90deg,
      rgba(27, 16, 70, 0.88) 0%,
      rgba(27, 16, 70, 0.72) 32%,
      rgba(27, 16, 70, 0.3) 58%,
      rgba(27, 16, 70, 0) 88%
    ),
    url("../images/banner-bangkok.jpg");
  background-size: cover;
  background-position: right 60%;
}

.page-banner::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: var(--brand-gradient);
}

.page-banner-inner {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 36px var(--gutter);
}

.page-banner h1 {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: 400;
  color: #fff;
  font-size: clamp(32px, 4.2vw, 50px);
  text-shadow: 0 2px 14px rgba(12, 6, 40, 0.7);
}

/* --- surfaces ----------------------------------------------------------- */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: none;
}

.panel-body {
  padding: 24px 22px;
}

.panel + .panel {
  margin-top: 18px;
}

/* …except where panels are laid out by a grid, which already spaces them: the
   stacking margin was pushing every card after the first 18px down its column,
   so a row of category cards sat one card higher at the left. */
.registration-type-grid .panel + .panel,
.card-grid .panel + .panel,
.step-grid .panel + .panel,
.metric-grid .panel + .panel {
  margin-top: 0;
}

.info-card {
  min-width: 0;
  padding: 24px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
}

.info-card h3 {
  margin: 0 0 10px;
  font-size: 18px;
  font-weight: 600;
}

.info-card p {
  margin: 0 0 10px;
  font-size: 18px;
  color: var(--ink-muted);
}

.info-card p:last-child {
  margin-bottom: 0;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 32px;
  padding: 18px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.metric {
  min-width: 0;
}

.metric span {
  display: block;
  margin-bottom: 4px;
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.metric strong {
  font-size: 16px;
  font-weight: 500;
}

/* Live countdown tile on the homepage. Mono digits with tabular figures so the
   seconds tick without the row shifting sideways. */
/* `.metric span` above would otherwise blockify these, so the selectors are
   scoped to .metric to win on specificity. */
.metric .countdown {
  display: flex;
  gap: 15px;
  align-items: flex-start;
}

.metric .cd-unit {
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
  line-height: 1;
}

.cd-unit b {
  font-family: var(--font-mono);
  font-size: 21px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--deep);
}

.cd-unit i {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 18px;
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.cd-now {
  font-size: 16px;
  font-weight: 500;
}

.notice-banner {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 16px 20px;
  margin-bottom: 24px;
  background: var(--purple-tint);
  border: 1px solid oklch(0.88 0.04 320);
  border-radius: 6px;
  font-size: 18px;
  color: var(--ink-muted);
}

.notice-banner > span:first-child {
  color: var(--purple);
}

/* --- tables ------------------------------------------------------------- */

.table-wrap {
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

/* The two airport tables answer the same three questions, and they sit one
   above the other, so their columns have to line up -- read down the page,
   a cost that starts in a different place each time reads as a different
   kind of number.

   Left to itself a table is measured from its own contents, and the first
   table's longest route is half a line longer than anything in the second,
   so the two came out with the columns in different places. `fixed` stops
   the contents having a vote: the widths below are the same in both, and the
   long route wraps inside its column instead of widening it.

   No rules between the columns -- the client asked for them aligned, not
   divided. */
.route-table {
  table-layout: fixed;
  /* Fixed widths and a phone do not mix on their own: at 390px the three
     columns came out at 146/58/88 and every cell broke to one word a line.
     The floor below is the width at which the columns still read, and
     .table-wrap already scrolls sideways past it -- so on a phone the table
     is pushed rather than crushed, and the two tables stay the same shape as
     each other at every screen size. */
  min-width: 560px;
}

.route-table th:nth-child(1) { width: 50%; }
.route-table th:nth-child(2) { width: 20%; }
.route-table th:nth-child(3) { width: 30%; }

.data-table thead tr {
  background: oklch(0.96 0.01 70);
}

.data-table th {
  padding: 14px 20px;
  text-align: left;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.data-table td {
  padding: 16px 20px;
  border-top: 1px solid var(--line-light);
  font-size: 18px;
  vertical-align: top;
}

.data-table th .hint,
.data-table td .hint {
  display: block;
  margin-top: 3px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-faint);
}

.data-table td.period-current {
  background: var(--purple-tint);
  font-weight: 500;
  color: var(--purple);
}

.period-now {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 7px;
  border-radius: 3px;
  background: oklch(0.94 0.04 350);
  color: var(--magenta-dark);
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* --- buttons ------------------------------------------------------------ */

/* These started as overrides of Bootstrap's .btn, which theme v1 loads from a
   CDN. v2 has no Bootstrap, so the base has to be here or a <button> keeps the
   browser's own chrome and an <a class="btn"> renders as underlined text. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 500;
  line-height: 1.25;
  padding: 14px 24px;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.btn:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}

.btn-sm {
  padding: 8px 14px;
  font-size: 18px;
}

.btn-primary {
  background: var(--purple);
  border-color: var(--purple);
  color: #fff;
}

.btn-primary:hover,
.btn-primary:focus {
  background: var(--purple-hover);
  border-color: var(--purple-hover);
  box-shadow: none;
}

.btn-outline-secondary,
.btn-outline-primary {
  border: 1px solid var(--purple);
  background: transparent;
  color: var(--purple);
}

.btn-outline-secondary:hover,
.btn-outline-primary:hover {
  background: var(--purple-tint);
  border-color: var(--purple);
  color: var(--purple);
}

.btn-lg {
  padding: 15px 28px;
  font-size: 16px;
}

/* the hero call to action, the one place a gradient pill is used */
.btn-pill {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 16px 46px;
  border: none;
  border-radius: 999px;
  background: var(--brand-gradient);
  color: #fff;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(161, 95, 208, 0.32);
  transition: filter 0.2s ease;
}

.btn-pill:hover {
  filter: brightness(1.07);
  color: #fff;
}

/* --- forms -------------------------------------------------------------- */

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}

.form-grid .full {
  grid-column: 1 / -1;
}

label {
  display: block;
  margin-bottom: 6px;
  font-size: 18px;
  font-weight: 500;
}

.form-control,
.custom-select,
textarea,
input[type="file"] {
  display: block;
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #fff;
  font-family: var(--font-sans);
  font-size: 18px;
  line-height: 1.45;
  color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

input[type="file"] {
  padding: 9px 13px;
  color: var(--ink-soft);
}

.custom-select {
  /* Kanit's ascenders and descenders are taller than a 1.45 line box, and a
     select clips rather than overflowing, which cut the top and bottom off
     every chosen option. Selects take their own metrics instead. */
  line-height: normal;
  min-height: 46px;
  /* the chevron is drawn here because appearance:none removes the native one */
  appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%237a5a9c' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

.form-control::placeholder,
textarea::placeholder {
  color: oklch(0.72 0.02 60);
}

.form-control-sm,
.custom-select-sm {
  padding: 7px 10px;
  font-size: 14px;
}

.custom-select-sm {
  padding-right: 30px;
  background-position: right 9px center;
  min-height: 36px;
}

.form-control:focus,
.custom-select:focus {
  border-color: var(--purple-light);
  box-shadow: 0 0 0 3px var(--purple-tint);
}

textarea.form-control {
  min-height: 132px;
}

.hint {
  display: block;
  margin-top: 6px;
  font-size: 18px;
  color: var(--ink-faint);
}

.choice-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

.choice {
  display: flex;
  gap: 12px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
}

.choice input {
  margin-top: 4px;
}

.choice strong {
  display: block;
  margin-bottom: 4px;
}

.choice span {
  color: var(--ink-soft);
}

.choice-inline {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-weight: 400;
  cursor: pointer;
}

.choice-inline input {
  margin-top: 4px;
  flex-shrink: 0;
}

/* --- registration ------------------------------------------------------- */

.registration-type-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

/* Every card is the height of the tallest in the row, and the description
   takes up whatever slack is left, so the prices line up across the row
   however long each description runs. */
.type-card {
  display: flex;
  color: var(--ink);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.type-card .panel-body {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.type-card:hover {
  color: var(--ink);
  border-color: var(--purple-light);
  box-shadow: 0 6px 20px rgba(124, 75, 181, 0.1);
}


.type-card h2 {
  margin: 8px 0 6px;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0;
}

.type-card p {
  flex: 1 1 auto;
  margin: 0 0 16px;
  font-size: 18px;
  color: var(--ink-soft);
}

.type-card strong {
  display: block;
  margin-top: auto;
  font-size: 17px;
  font-weight: 500;
  color: var(--purple);
}

/* The action inside the card: a pill rather than a full-width bar, so the
   price above it stays the loudest thing on the card. */
.type-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  align-self: flex-start;
  margin-top: 12px;
  padding: 9px 16px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--purple-tint);
  color: var(--deep);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.type-btn:hover {
  border-color: var(--purple);
  background: var(--purple);
  color: #fff;
}

.type-btn:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}


.type-click {
  flex-shrink: 0;
  opacity: 0.75;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.type-card:hover .type-click {
  opacity: 1;
  transform: translateX(2px);
}

.rate-pending {
  color: var(--ink-faint) !important;
  font-size: 18px !important;
  font-weight: 400 !important;
  font-style: italic;
}

.type-kicker {
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--magenta);
}

.registration-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  background: var(--purple-tint);
  border: 1px solid oklch(0.88 0.04 320);
  border-radius: 6px;
}

.registration-summary span {
  font-weight: 500;
}

.registration-summary strong {
  font-size: 19px;
  color: var(--purple);
}

/* Each card lays its four parts — name, price, note, button — on rows shared
   with its neighbours, so the prices sit on one line, the notes on the next and
   the buttons on the last, however many lines a category name takes. */
.tier-grid .tier-card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 4;
  align-content: start;
}

/* Where subgrid is unavailable the cards stay a column and the note takes the
   slack, which still lines the buttons up even if the prices do not. */
@supports not (grid-template-rows: subgrid) {
  .tier-grid .tier-card {
    display: flex;
    flex-direction: column;
  }

  .tier-grid .tier-note {
    flex: 1 1 auto;
  }
}

.tier-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 24px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
}

.tier-card h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 500;
}

.tier-price {
  font-family: var(--font-serif);
  font-size: 32px;
  line-height: 1.1;
  color: var(--purple);
}

.tier-price.rate-pending {
  font-family: var(--font-sans);
  font-size: 16px !important;
}

.tier-note {
  margin: 0;
  font-size: 18px;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* the same small pill the register page uses, so one card reads the same
   wherever it appears */
.tier-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  align-self: end;
  justify-self: start;
  padding: 9px 16px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--purple-tint);
  color: var(--deep);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: none;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.tier-btn:hover {
  border-color: var(--purple);
  background: var(--purple);
  color: #fff;
}

.tier-btn:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}


/* --- people ------------------------------------------------------------- */

.person-card {
  min-width: 0;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
}

.person-card .role {
  font-family: var(--font-mono);
  font-size: 18px;
  /* 0.1em was set when the longest of these read "SCIENTIFIC COMMITTEE CHAIR".
     "LOCAL ORGANIZING COMMITTEE CHAIR" is six characters longer, and at that
     tracking the label sprawled. Tightened at the client's request; it is still
     letter-spaced, which is what makes these read as labels rather than as
     headings. */
  /* Set at 0.1em when the longest of these read "SCIENTIFIC COMMITTEE CHAIR".
     "LOCAL ORGANIZING COMMITTEE CHAIR" is six characters longer and sprawled at
     that tracking; normal spacing is what the client asked for and is also what
     holds all three on one line on a desktop. */
  letter-spacing: normal;
  word-spacing: -0.25em;
  text-transform: uppercase;
  color: var(--magenta);
  line-height: 1.45;
}

/* Measured: from 1200px up all three titles sit on one line, so nothing is
   reserved there. Below it the longest wraps and its card's name would drop a
   line below its neighbours', so every card reserves both lines -- only in the
   range where that actually happens. */
@media (max-width: 1199px) {
  .person-card .role {
    min-height: 2.9em;
  }
}

.person-card .name {
  margin: 8px 0 3px;
  /* A name is content, so it reads at the site's reading size. */
  font-size: 18px;
  font-weight: 500;
}

.person-card .place {
  font-size: 18px;
  color: var(--ink-soft);
}

.name-list {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 2;
  column-gap: 32px;
}

.name-list li {
  break-inside: avoid;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-light);
  font-size: 18px;
}

/* --- partners / sponsors ------------------------------------------------ */

.partner-wall {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px 40px;
  padding: 26px 20px;
  /* White, not the page's warm off-white surface.
     Four of the five marks are supplied with an opaque white background baked
     into the file -- only the society's flower has transparency -- so against
     a surface that is very slightly yellow each one sat in a visible white
     rectangle. Matching the panel to the files is the one fix that does not
     involve re-cutting somebody else's logo. */
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
}

.partner-logo img {
  display: block;
  height: 60px;
  width: auto;
  max-width: 180px;
  object-fit: contain;
}

/* One height for all of them only works while they are all about as wide as
   they are tall, and four of these are. The MAC mark is a line of type, 155
   by 67, so at the same height it drew 139px wide against 52-80px for the
   rest -- two and a half times the room, which read as one logo shouted and
   four spoken. Sized to take about as much space as its neighbours instead;
   the height is what changes because the width follows from it. */
.partner-logo.partner-mac img {
  height: 44px;
}

/* The other way round: the APMF marks carry white margin inside the file, so
   at 60px only 39-40px of each was artwork, against 52 for the society's
   flower and 102 for MAC. Given the height that brings their artwork up to the
   others; 88px was tried and made them the loud ones instead. The 11th
   meeting's own mark was taken out of the strip afterwards; its rule is left
   here for if it goes back. */
.partner-logo.partner-apmf img,
.partner-logo.partner-apmf2027 img {
  height: 80px;
}

/* TCEB read a touch small beside the others at the shared 60px; 5% up. */
.partner-logo.partner-tceb img {
  height: 63px;
}

/* Two to a line on a phone.
 *
 * Left to wrap by itself the row breaks on how wide each mark happens to be,
 * and these are 60 to 102px: from about 380 to 500px three fit and the fourth
 * drops underneath on its own, which reads as one of them being left out.
 * Narrower than that it already falls into two and two, and above it all four
 * sit on one line, so the columns are only imposed across the band where the
 * wrapping goes wrong. */
@media (max-width: 500px) {
  .partner-wall {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 22px 12px;
    justify-items: center;
    align-items: center;
    /* The space down the middle is not `column-gap`: the two columns are half
       the panel each and the marks are centred in them, so most of it is the
       room left over beside each mark. Raising the side padding is what takes
       it out -- narrowing the columns by this much on each side closes the
       middle by the same amount. Measured at 390px: the wider row ran 102px
       down the middle, and 26px a side brings it to 76. */
    padding-left: 46px;
    padding-right: 46px;
  }
}

/* Each one opens larger, because the wording under these marks cannot be read
   at strip size. */
.partner-logo {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  line-height: 0;
  cursor: zoom-in;
  border-radius: 4px;
}

.partner-logo:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 4px;
}

/* A mark is not a photograph: it sits on its own white card rather than
   filling the frame.
   The card is given a size and the mark is fitted inside it, because these
   files are small -- 98 to 155px across -- and `width: auto` would have drawn
   them at that size, which is barely larger than the strip they were opened
   from. Fitting scales each one up to the card while keeping its proportions,
   about two to two and a half times, which is where the wording under them
   becomes readable and short of where the edges go soft. */
.lightbox.is-logo img {
  width: min(380px, 86vw);
  max-width: min(380px, 86vw);
  height: min(300px, 52vh);
  object-fit: contain;
  background: #fff;
  padding: 22px;
  border-radius: 10px;
}

.sponsor-wall {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
}

.sponsor-slot {
  height: 78px;
  display: grid;
  place-items: center;
  border: 1px dashed oklch(0.84 0.012 70);
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

/* --- misc --------------------------------------------------------------- */

.photo-tile {
  display: grid;
  place-items: center;
  text-align: center;
  padding: 20px;
  background-color: oklch(0.95 0.012 70);
  background-image: repeating-linear-gradient(135deg, oklch(0.92 0.03 350) 0 2px, transparent 2px 11px);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 18px;
  line-height: 1.7;
  color: var(--ink-soft);
}

.alert {
  border-radius: 6px;
  font-size: 18px;
}

.alert-success {
  background: oklch(0.95 0.05 160);
  border-color: oklch(0.85 0.07 160);
  color: var(--green);
}

.status-pill {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 3px 9px;
  border-radius: 3px;
  background: var(--purple-tint);
  color: var(--magenta-dark);
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.text-muted {
  color: var(--ink-soft) !important;
}

@media (max-width: 700px) {
  .app-shell,
  .admin-shell {
    padding: 36px var(--gutter) 56px;
  }

  .page-banner {
    min-height: 130px;
  }

  .page-banner-inner {
    padding: 24px var(--gutter);
  }

  .page-lead-row,
  .page-head {
    flex-direction: column;
    align-items: flex-start;
  }
}

@media (max-width: 600px) {
  .name-list {
    columns: 1;
  }
}

/* leading avatar in the committee roster: portrait when we have one, the
   person's initials when we do not */

.avatar {
  display: block;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--purple-tint);
}

.avatar-initials {
  display: grid;
  place-items: center;
  color: var(--purple);
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0.02em;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* committee roster: a role sub-heading, then the people under it, each line
   led by a portrait or the person's initials */
.role-heading {
  margin: 26px 0 12px;
  font-family: var(--font-mono);
  /* Under the names beside it, which are 18px. The note this replaces said
     exactly that and then the site-wide lift to 18px on 30 Sep 2026 walked
     straight into it: uppercase and letter-spaced, at the same number it
     takes more room and more attention than the name it is labelling.
     16.5px, the same figure as .eyebrow and .mono-micro, which are the same
     kind of thing. */
  font-size: 16.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--magenta);
}

.panel-body > .role-heading:first-child {
  margin-top: 0;
}

.person-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.person-list li {
  display: grid;
  /* the name column is capped rather than elastic so the country sits just
     after it, instead of being flung to the far right on wide screens */
  grid-template-columns: 44px minmax(0, 340px) minmax(0, 1fr);
  align-items: center;
  gap: 16px;
  padding: 7px 0;
}

/* 18px, the size the rest of the site reads at, rather than the 16 and 14.5
   these were set at. These two classes are used on the committee page and
   nowhere else, so nothing else moves with them. */
.person-name {
  font-size: 18px;
  font-weight: 500;
}

.person-country {
  font-size: 18px;
  color: var(--ink-soft);
}

@media (max-width: 560px) {
  .person-list li {
    grid-template-columns: 44px minmax(0, 1fr);
    row-gap: 2px;
  }

  .person-country {
    grid-column: 2;
  }
}


/* the roster sits in from the panel edge so the avatars are not flush against
   the border */
/* The roster sits further in from the panel edge than a normal panel body:
   the indent is what gives the role headings and the portrait column their own
   margin. clamp() keeps it from eating the row on a narrow screen. */
/* Three tabs of indent on the left: 120px past the panel's own padding.
   The history, so nobody re-litigates it: up to 150px originally, then none at
   all, then one tab, now three. The names still have 640px to sit in at the
   widest, which is more than the 340px the name column asks for and the
   country beside it. */
.roster-body {
  padding: 26px 34px 26px 154px;
}

@media (max-width: 600px) {
  /* No room for the indent on a phone; the names need the width more. */
  .roster-body {
    padding: 20px 16px 20px 22px;
  }
}


/* --- portrait lightbox --------------------------------------------------- */

/* only real photographs are clickable; the initials circles stay inert */
button.avatar-btn {
  padding: 0;
  border: none;
  background: none;
  cursor: zoom-in;
  line-height: 0;
  border-radius: 50%;
}

button.avatar-btn:focus-visible {
  outline: 2px solid var(--purple-light);
  outline-offset: 2px;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(16, 8, 40, 0.82);
}

.lightbox.is-open {
  display: flex;
}

.lightbox-inner {
  max-width: 420px;
  width: 100%;
  text-align: center;
}

.lightbox img {
  width: 100%;
  max-width: 340px;
  height: auto;
  border-radius: 8px;
  display: block;
  margin: 0 auto 14px;
}

.lightbox-name {
  color: #fff;
  font-size: 18px;
  font-weight: 500;
}

.lightbox-role {
  margin-top: 4px;
  color: rgba(255, 255, 255, 0.72);
  font-size: 18px;
}

.lightbox-close {
  position: absolute;
  top: 18px;
  right: 22px;
  width: 40px;
  height: 40px;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 50%;
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.lightbox-close:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* the header is sticky and about 117px tall unshrunk, so a jump to #venue or
   #explore would otherwise land with the heading hidden behind it. 110px clears
   it at every width — the bar measures 57px shrunk on a wide screen and up to
   99px unshrunk in a narrow one — and a gap above the heading does no harm. */
#welcome,
#venue,
#hotel,
#travel,
#explore {
  scroll-margin-top: 110px;
}

/* --- travel: how to get here, and what to see nearby --------------------- */

.text-small {
  font-size: 18px;
  color: var(--muted);
}

/* fixed two columns rather than auto-fit: at three columns the fourth card
   drops onto a row of its own and leaves two thirds of it empty */
.route-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin: 0 0 26px;
}

.spec-list {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.spec-list li {
  position: relative;
  margin-bottom: 8px;
  padding-left: 15px;
  font-size: 18px;
  line-height: 1.55;
  color: var(--muted);
}

/* a short rule rather than a bullet, so the list sits quietly in the card */
.spec-list li::before {
  content: "";
  position: absolute;
  top: 11px;
  left: 0;
  width: 7px;
  height: 1px;
  background: var(--pink);
}

.spec-list li:last-child {
  margin-bottom: 0;
}

.spec-list strong {
  color: var(--ink);
  font-weight: 600;
}

.travel-figure {
  margin: 0 0 26px;
}

.travel-figure img {
  display: block;
  width: 100%;
  height: clamp(160px, 22vw, 260px);
  object-fit: cover;
  object-position: center 55%;
  border-radius: 6px;
  border: 1px solid var(--line);
}

.travel-figure figcaption {
  margin-top: 8px;
  font-size: 18px;
  color: var(--muted);
}

.spot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 18px;
  margin: 0 0 26px;
}

.spot-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: inherit;
  text-decoration: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}

.spot-card:hover,
.spot-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 12px 26px rgba(32, 26, 60, 0.12);
  border-color: var(--pink);
}

.spot-photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--line);
}

.spot-body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  padding: 16px 18px 18px;
}

.spot-when {
  font-family: var(--font-mono, inherit);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--pink);
}

.spot-card h3 {
  margin: 6px 0 8px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.3;
}

.spot-card p {
  margin: 0 0 12px;
  font-size: 18px;
  line-height: 1.55;
  color: var(--muted);
}

/* pushed to the bottom so the "getting there" line rules off every card at
   the same place, however long the description above it runs */
.spot-how {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 18px;
  line-height: 1.5;
  color: var(--muted);
}

.photo-credit {
  margin: 14px 0 34px;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted);
}

.photo-credit code {
  font-size: 18px;
}

@media (max-width: 760px) {
  .route-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 560px) {
  .spot-grid {
    grid-template-columns: 1fr;
  }
}

/* --- venue and hotel: split card, static photo cards, missing photo -------- */

/* photo beside its own spec list — used for the congress floor, where the
   picture and the numbers only make sense together */
.split-card {
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  margin: 0 0 26px;
}

.split-photo {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 300px;
  object-fit: cover;
  background: var(--line);
}

.split-body {
  padding: 24px 26px;
}

.split-body h3 {
  margin: 0 0 10px;
  font-size: 18px;
  font-weight: 600;
}

.split-body p {
  margin: 0;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted);
}

.hotel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 18px;
  margin: 0 0 26px;
}

/* the same card, but not a link: nothing to click, so nothing should lift */
.spot-card--static:hover,
.spot-card--static:focus-visible {
  transform: none;
  box-shadow: none;
  border-color: var(--line);
}

.spot-body .spec-list {
  margin-top: 4px;
  margin-bottom: 12px;
}

.spot-body .spec-list li {
  font-size: 18px;
  line-height: 1.5;
}

/* stands in for a hotel photograph until the hotel supplies one, so the card
   keeps its shape and the gap is obvious rather than silently missing */
.photo-pending {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  padding: 20px;
  border-bottom: 1px dashed var(--line);
  background: var(--bg);
  text-align: center;
  font-size: 18px;
  color: var(--muted);
}

@media (max-width: 760px) {
  .split-card {
    grid-template-columns: 1fr;
  }

  .split-photo {
    min-height: 0;
    aspect-ratio: 4 / 3;
  }

  .hotel-grid {
    grid-template-columns: 1fr;
  }
}

/* --- area map and its numbered key ---------------------------------------- */

/* the marker colours are the ones drawn into the map image itself, so the key
   and the pins on the map stay in step; they are deliberately not theme tokens */
:root {
  --map-venue: #c2185b;
  --map-hotel: #6a3fa0;
  --map-rail: #1f6f5c;
  --map-pier: #1264a3;
}

.map-figure {
  margin: 0 0 14px;
}

.map-figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 6px;
}

.map-figure figcaption {
  margin-top: 8px;
  font-size: 18px;
  color: var(--muted);
}

.map-key {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 8px 22px;
  margin: 0 0 26px;
  padding: 0;
  list-style: none;
  font-size: 18px;
  color: var(--muted);
}

.map-key li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  line-height: 1.45;
}

.map-pin {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  color: #fff;
  font-size: 18px;
  font-weight: 600;
  line-height: 22px;
  text-align: center;
}

.map-pin--venue { background: var(--map-venue); }
.map-pin--hotel { background: var(--map-hotel); }
.map-pin--rail  { background: var(--map-rail); }
.map-pin--pier  { background: var(--map-pier); }

/* photo on the right instead of the left, so the two split cards in this
   section do not read as one column of images; the markup order already puts
   the text first, so only the track widths swap */
.split-card--flip {
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
}

@media (max-width: 760px) {
  .split-card--flip {
    grid-template-columns: 1fr;
  }
}

/* --- spacing utilities --------------------------------------------------
 *
 * The markup is written with Bootstrap's spacing class names because theme v1
 * loads Bootstrap. v2 does not, so every mb-4 / mt-5 in these pages was doing
 * nothing and the rhythm between blocks came out uneven. Same scale as
 * Bootstrap's, so the two themes space alike.
 */
.mt-0 { margin-top: 0 !important; }
.mt-1 { margin-top: 4px !important; }
.mt-2 { margin-top: 8px !important; }
.mt-3 { margin-top: 16px !important; }
.mt-4 { margin-top: 24px !important; }
.mt-5 { margin-top: 48px !important; }
.mb-0 { margin-bottom: 0 !important; }
.mb-1 { margin-bottom: 4px !important; }
.mb-2 { margin-bottom: 8px !important; }
.mb-3 { margin-bottom: 16px !important; }
.mb-4 { margin-bottom: 24px !important; }
.mb-5 { margin-bottom: 48px !important; }

/* --- alerts, and panels that toggle ------------------------------------- */

.alert {
  padding: 14px 16px;
  margin-bottom: 22px;
  border: 1px solid;
  border-radius: 6px;
  font-size: 18px;
}

.alert:last-child {
  margin-bottom: 0;
}

.alert-danger {
  background: oklch(0.96 0.03 25);
  border-color: oklch(0.85 0.07 25);
  color: var(--red);
}

.alert-warning {
  background: oklch(0.97 0.04 85);
  border-color: oklch(0.87 0.08 85);
  color: #7a4a00;
}

.alert-success {
  background: oklch(0.96 0.04 165);
  border-color: oklch(0.85 0.07 165);
  color: var(--green);
}

/* Room around a box the page has been scrolled to.

   This is for the browser's own "please fill in this field" as much as for our
   own scrolling: the browser scrolls the offending box only just into view, so
   against a sticky header it lands underneath it, and at the foot of the screen
   it lands with a sliver showing and the bubble drawn off-screen. Either way
   the person sees a complaint and not the box it is about. scroll-margin keeps
   a header's worth of room above and a comfortable gap below. */
.form-control,
.custom-select,
textarea.form-control,
input[type="checkbox"],
input[type="radio"],
input[type="file"] {
  scroll-margin-top: 135px;
  scroll-margin-bottom: 120px;
}

/* The message a form comes back with, and the box it is about.

   scroll-margin-top keeps whichever one is scrolled to clear of the sticky
   header, which is what made the old behaviour so confusing: the page moved,
   but what it moved to was hidden underneath the bar. */
.form-error {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  scroll-margin-top: 120px;
}
.form-error-jump {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.form-error-note {
  margin: -12px 0 22px;
  font-size: 18px;
  color: var(--ink-soft, #6b6155);
}

.form-control.is-invalid,
.custom-select.is-invalid {
  border-color: var(--red, #c0392b);
  background: oklch(0.985 0.012 25);
  box-shadow: 0 0 0 3px oklch(0.9 0.05 25 / 0.45);
  scroll-margin-top: 140px;
}
.form-control.is-invalid:focus,
.custom-select.is-invalid:focus {
  outline: none;
  border-color: var(--red, #c0392b);
  box-shadow: 0 0 0 3px oklch(0.88 0.07 25 / 0.55);
}
/* a checkbox is small and square; the ring above would sit oddly on it */
input[type="checkbox"].is-invalid,
input[type="radio"].is-invalid {
  outline: 2px solid var(--red, #c0392b);
  outline-offset: 3px;
  scroll-margin-top: 160px;
}

/* "Your confirmation may be in Junk." Deliberately not an .alert: nothing has
   gone wrong, and a red or amber box on the page that says the registration
   succeeded reads as a failure. The house pink, the same tone the emails use,
   with an envelope beside it. Literal fallbacks because this block has to
   survive a stylesheet that arrives without its custom properties. */
.junk-notice {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 16px 18px;
  margin: 0 0 28px;
  border: 1px solid #f0cfe2;
  border-left: 4px solid var(--magenta, #d6469a);
  border-radius: 8px;
  background: #fceef6;
  color: #5a3247;
  font-size: 18px;
  line-height: 1.6;
}
.junk-notice-icon {
  flex: none;
  font-size: 20px;
  line-height: 1.3;
  color: var(--magenta, #d6469a);
}
.junk-notice p {
  margin: 0 0 8px;
}
.junk-notice p:last-child {
  margin-bottom: 0;
}
.junk-notice-head {
  font-weight: 600;
  color: #7a2b58;
}
.junk-notice-th {
  font-size: 18px;
  color: #6b4358;
}
@media (max-width: 640px) {
  .junk-notice {
    padding: 14px;
    gap: 10px;
  }
}

/* the card / tax-invoice blocks on the registration form are shown and hidden
   by js/registration.js; without this rule they were all visible at once */
.is-hidden {
  display: none !important;
}

.payment-panel {
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
}

/* --- public status lookup ----------------------------------------------- */

.lookup-panel .form-grid {
  max-width: 720px;
}

.status-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px 24px;
  padding-bottom: 20px;
  margin-bottom: 28px;
  border-bottom: 1px solid var(--line);
}

.status-name {
  margin: 6px 0 0;
  font-size: clamp(24px, 3vw, 32px);
  line-height: 1.2;
}

.status-badge {
  padding: 8px 14px;
  border: 1px solid;
  border-radius: 999px;
  font-size: 18px;
  font-weight: 500;
  white-space: nowrap;
}

.status-waiting {
  background: var(--purple-tint);
  border-color: oklch(0.86 0.06 310);
  color: var(--deep);
}

.status-done {
  background: oklch(0.96 0.04 165);
  border-color: oklch(0.85 0.07 165);
  color: var(--green);
}

.status-stopped {
  background: oklch(0.96 0.03 25);
  border-color: oklch(0.85 0.07 25);
  color: var(--red);
}

/* a step the delegate has already passed, in the shared .tracker list */
.tracker-row.is-reached .tracker-dot {
  background: var(--purple);
  box-shadow: 0 0 0 3px var(--purple-tint);
}

.tracker-row.is-reached .tracker-step {
  color: var(--deep);
}

/* --- group registration -------------------------------------------------- */

.step-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}

.step-card {
  padding: 24px 22px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
}

.step-card h3 {
  margin: 14px 0 8px;
  font-size: 18px;
}

.step-card p {
  margin: 0;
  font-size: 18px;
  color: var(--ink-muted);
}

.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--purple-tint);
  color: var(--deep);
  font-family: var(--font-mono);
  font-size: 18px;
}

.download-row,
.submit-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.review-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 24px;
  margin-bottom: 14px;
}

/* the review table holds inputs rather than text, so its cells need less
   padding than a reading table and a size floor per column */
.review-table th,
.review-table td {
  padding: 8px 10px;
  vertical-align: top;
}

.review-table .form-control,
.review-table .custom-select {
  min-width: 140px;
  padding: 8px 10px;
  font-size: 14.5px;
}

.review-table .row-number {
  padding-top: 16px;
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: 18px;
}

.review-table .row-invalid {
  background: oklch(0.97 0.02 25);
}

.review-table .row-invalid .form-control,
.review-table .row-invalid .custom-select {
  border-color: oklch(0.75 0.12 25);
}

.row-error {
  display: block;
  margin-top: 6px;
  font-size: 18px;
  color: var(--red);
}

.data-table tfoot .total-row td {
  border-top: 2px solid var(--line);
  font-weight: 600;
  color: var(--deep);
}

/* --- admin chrome -------------------------------------------------------
 * Theme v1 styles these from its own stylesheet; v2 never carried them over,
 * so the admin pages were falling back to unstyled defaults.
 */

.admin-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px 24px;
  padding: 16px clamp(20px, 4vw, 56px);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.admin-title {
  margin: 0;
  font-size: 20px;
}

.admin-meta {
  font-size: 14px;
  color: var(--ink-soft);
}

.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.admin-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 6px 10px;
  border-radius: 4px;
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 500;
  text-decoration: none;
}

.admin-nav a:hover {
  background: var(--purple-tint);
  color: var(--deep);
}

.receipt {
  width: min(820px, calc(100% - 32px));
  margin: 32px auto;
  padding: 32px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
}

.receipt-row {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-light);
}

.brand-logo {
  width: min(320px, 52vw);
  height: auto;
}

@media print {
  .no-print {
    display: none !important;
  }

  .receipt {
    width: 100%;
    margin: 0;
    border: 0;
  }
}

/* a pointer inside a form, where a field used to be */
.inline-note {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  align-items: baseline;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--purple);
  border-radius: 4px;
  background: var(--purple-tint);
  font-size: 18px;
}

.inline-note strong {
  color: var(--deep);
}

/* the delegate list folded into a group's row in the admin table */
.group-list {
  margin-top: 8px;
}

.group-list summary {
  cursor: pointer;
  color: var(--purple);
  font-size: 18px;
}

.group-list ol {
  margin: 8px 0 0;
  padding-left: 20px;
  font-size: 18px;
}

.group-list li {
  margin-bottom: 4px;
}

.group-list .hint {
  display: inline;
  margin-left: 4px;
}

/* --- back to top --------------------------------------------------------- */

.to-top {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  background: var(--purple);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(45, 20, 80, 0.28);
  transition: background 0.15s ease, transform 0.15s ease;
}

.to-top:hover {
  background: var(--purple-hover);
  transform: translateY(-2px);
}

.to-top:focus-visible {
  outline: 2px solid var(--magenta-bright);
  outline-offset: 3px;
}

.to-top[hidden] {
  display: none;
}

@media (max-width: 600px) {
  .to-top {
    right: 14px;
    bottom: 14px;
    width: 42px;
    height: 42px;
  }
}

/* The spam honeypot: off-screen rather than display:none, since a field that
   is display:none is the first thing a bot learns to skip. Nobody sees it, it
   is out of the tab order, and screen readers are told to ignore it. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- side margins on small screens ---------------------------------------
 *
 * One variable drives the edge on every band, section, banner and footer, so
 * the whole page moves together. A tablet gets the most, because it has the
 * width to spare and content running to the bezel looks unfinished there.
 */
@media (max-width: 1199px) {
  :root {
    --gutter: 38px;
  }
}

@media (max-width: 767px) {
  :root {
    --gutter: 26px;
  }
}

@media (max-width: 380px) {
  :root {
    --gutter: 20px;
  }
}

/* --- the delegate's provisional receipt ----------------------------------
 *
 * A document rather than a page section: it has to read as something you can
 * hand to a finance office, and print without the site around it.
 */

.receipt-doc {
  padding: 34px 32px;
  margin-bottom: 24px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}

.receipt-head {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: center;
  padding-bottom: 18px;
  margin-bottom: 20px;
  border-bottom: 2px solid var(--deep);
}

.receipt-logo {
  height: 58px;
  width: auto;
}

.receipt-title {
  font-size: 23px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--deep);
}

.receipt-title-th {
  margin-top: 2px;
  font-size: 16px;
  color: var(--ink-muted);
}

/* not a rubber stamp graphic: the payment state is the single most important
   thing on this document and has to survive a black-and-white printer */
.receipt-stamp {
  display: inline-block;
  padding: 6px 14px;
  margin-bottom: 18px;
  border: 1px dashed var(--purple-light);
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--deep);
}

.receipt-total {
  border-bottom-width: 2px;
  border-bottom-color: var(--line);
  font-size: 18px;
}

.receipt-total span {
  font-weight: 600;
  color: var(--deep);
}

.receipt-note {
  margin: 22px 0;
  padding: 14px 16px;
  border-left: 3px solid var(--magenta);
  background: var(--purple-tint);
  font-size: 18px;
  line-height: 1.6;
  color: var(--ink-muted);
}

.receipt-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 24px;
  font-size: 18px;
  line-height: 1.7;
  color: var(--ink-soft);
}

.receipt-delegates ol {
  margin: 6px 0 0;
  padding-left: 20px;
}

@media print {
  /* the receipt is the page; everything else is screen furniture */
  .preview-bar,
  .announce-bar,
  .site-header,
  .page-banner,
  .site-footer,
  .to-top,
  .no-print {
    display: none !important;
  }

  .app-shell {
    max-width: none;
    padding: 0;
  }

  .receipt-doc {
    border: 0;
    padding: 0;
  }

  .receipt-note {
    background: transparent;
  }
}

/* Five rate cards: three across, then the last two side by side, both rows
   filling the width. Six columns make that exact — the first three span two
   each, the last two span three each. Applied by PHP only when there really
   are five cards, so the grid falls back to auto-fit if the list changes. */
@media (min-width: 900px) {
  .tier-grid-3-2 {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .tier-grid-3-2 .tier-card,
  .tier-grid-3-2 .type-card {
    grid-column: span 2;
  }

  .tier-grid-3-2 .tier-card:nth-child(n + 4),
  .tier-grid-3-2 .type-card:nth-child(n + 4) {
    grid-column: span 3;
  }
}

/* the benefits column of the sponsorship table */
.package-list {
  margin: 0;
  padding-left: 18px;
}

.package-list li {
  margin-bottom: 3px;
}

.package-list li:last-child {
  margin-bottom: 0;
}

/* A category with no announced rate keeps its button so the row still reads
   the same, but it is a <span>, not a link. Muted and cursor: not-allowed so
   it does not invite a click, with the reason in its title attribute. */
.type-btn-off,
.tier-btn.type-btn-off {
  border-style: dashed;
  background: transparent;
  color: var(--muted, #6b6588);
  cursor: not-allowed;
}

.type-btn-off:hover,
.tier-btn.type-btn-off:hover {
  border-color: inherit;
  background: transparent;
  color: var(--muted, #6b6588);
}

.type-btn-off .type-click {
  opacity: 0.5;
}

/* The pair of buttons beside a page lead. They wrap onto their own line on a
   phone instead of squeezing the sentence next to them. */
.lead-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

/* A button for something that exists but cannot be done yet: it stays on the
   page and says why on hover, rather than disappearing and leaving the visitor
   wondering where the form went. */
.btn-off,
.btn-off:hover,
.btn-off:focus,
.btn-off:active {
  border-style: dashed;
  background: transparent;
  color: var(--muted, #6b6588);
  cursor: not-allowed;
  box-shadow: none;
}

/* the live word count under the abstract body */
.word-count {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.word-count.is-over {
  color: #b3261e;
}

/* --- registration: the attendee cards and the running total --- */

.attendee-card {
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 12px;
  padding: 20px;
  margin-bottom: 18px;
  background: rgba(255, 255, 255, 0.5);
}

.attendee-head {
  display: flex;
  align-items: center;
  /* "Copy details from above" sits next to the heading it acts on, rather than
     across the card where it read as decoration. Remove stays pushed to the
     far side: a destructive button does not belong beside the title. */
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 14px;
}

.attendee-head .attendee-remove { margin-left: auto; }

.total-box {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  align-items: flex-end;
  padding: 18px 20px;
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.6);
}

.total-box strong {
  display: block;
  font-size: 1.15rem;
}

.total-box-sum {
  margin-left: auto;
  text-align: right;
}

.total-box-sum strong {
  font-size: 1.5rem;
}

.choice.is-disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.access-code {
  font-family: var(--font-mono, monospace);
  font-size: 1.6rem;
  letter-spacing: 0.22em;
  margin: 6px 0 10px;
}

/* --- the rate table: each category is a way into its own form --- */

.row-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: inherit;
  text-decoration: none;
}

.row-link:hover,
.row-link:focus {
  color: var(--brand, #6b2fa0);
  text-decoration: none;
}

.row-click {
  flex: none;
  opacity: 0.65;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.rate-row:hover .row-click {
  opacity: 1;
  transform: translateY(-1px);
}

.rate-row:hover {
  background: rgba(107, 47, 160, 0.04);
}

/* a category with no price yet: the icon stays, dimmed, and says why on hover */
.rate-row-off:hover {
  background: transparent;
}

.row-link-off,
.row-link-off:hover {
  color: var(--muted, #6b6588);
  cursor: not-allowed;
}

.row-link-off .row-click {
  opacity: 0.22;
}

.rate-row-off:hover .row-click {
  opacity: 0.22;
  transform: none;
}

/* The three period columns are the same width, whatever the prices in them,
   so the eye can compare them. The name column keeps the rest. */
.rate-table {
  table-layout: fixed;
  min-width: 660px;
}

.rate-table col:not(.rate-col-name) {
  width: 20%;
}

.rate-table .rate-col-name {
  width: 40%;
}

.rate-table th,
.rate-table td {
  word-break: normal;
  overflow-wrap: anywhere;
}

/* the account details shown beside the slip upload */
.bank-box {
  border: 1px solid var(--line, #e7e1ef);
  border-left: 3px solid var(--brand, #6b2fa0);
  border-radius: 10px;
  padding: 16px 18px;
  background: rgba(255, 255, 255, 0.6);
}

.bank-box-title {
  display: block;
  margin-bottom: 8px;
}

.bank-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 18px;
  margin: 0;
}

.bank-list dt {
  color: var(--muted, #6b6588);
  font-weight: 400;
}

.bank-list dd {
  margin: 0;
  font-weight: 600;
}

@media (max-width: 520px) {
  .bank-list {
    grid-template-columns: 1fr;
    gap: 0 0;
  }

  .bank-list dd {
    margin-bottom: 8px;
  }
}

/* Four across where the fields are short enough for it, dropping to two and
   then one as the screen narrows. */
.form-grid-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 1000px) {
  .form-grid-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .form-grid-4 {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* the line between one part of a form and the next */
.form-divider {
  border: 0;
  border-top: 1px solid var(--line, #e7e1ef);
  margin: 32px 0 28px;
}

.form-grid-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 900px) {
  .form-grid-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .form-grid-3 {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* the card step while it is only a demonstration */
.demo-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #fde68a;
  color: #7c5b00;
  font-family: var(--font-mono, monospace);
  font-size: 18px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  vertical-align: middle;
}

.demo-box {
  border: 1px dashed #d9b65a;
  border-radius: 10px;
  padding: 14px 16px;
  background: #fffaf0;
  color: #6b5322;
}

.demo-box strong {
  display: block;
  margin-bottom: 4px;
}

/* The logs page says plainly when mail is only being written down, not sent.
   Amber rather than red: it is a setting waiting to be turned on, not a fault. */
.panel.mail-off {
  border-left: 4px solid #c8860d;
  background: #fdf6e6;
}
.panel.mail-off strong { color: #8a5b00; }

/* Logs: three tables behind three tabs. The panels are only hidden once the
   script has run, so with JavaScript off the page still shows all three. */
.log-tabs {
  display: flex;
  gap: 4px;
  margin: 0 0 -1px;
  flex-wrap: wrap;
}
.log-tab {
  appearance: none;
  border: 1px solid #e3ddd2;
  border-bottom: none;
  background: #f6f2ea;
  color: #6b6155;
  font: inherit;
  font-weight: 500;
  padding: 10px 18px;
  border-radius: 8px 8px 0 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.log-tab:hover { color: #2f2a24; background: #fbf8f2; }
.log-tab.is-active {
  background: #fff;
  color: #2f2a24;
  border-color: #e3ddd2;
  box-shadow: inset 0 3px 0 #8a1538;
}
.log-tab-count {
  font-size: 0.78em;
  background: #e8e1d5;
  color: #6b6155;
  border-radius: 999px;
  padding: 1px 8px;
  min-width: 26px;
  text-align: center;
}
.log-tab.is-active .log-tab-count { background: #8a1538; color: #fff; }
.log-tabs-ready .log-panel { display: none; }
.log-tabs-ready .log-panel.is-active { display: block; }
.log-tabs + .log-panel { border-top-left-radius: 0; }

/* The official receipt as the secretariat prints it: issuer block at the top,
   a signature line at the foot, and no site furniture on paper. */
.receipt-page { background: #fff; padding: 32px; }
.receipt-actions { margin-bottom: 24px; display: flex; gap: 8px; }
.receipt-issuer {
  font-size: 0.92rem;
  line-height: 1.6;
  color: #4a443c;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid #e3ddd2;
}
.receipt-sign {
  margin-top: 56px;
  text-align: right;
  font-size: 0.9rem;
  line-height: 1.6;
  color: #4a443c;
}
.receipt-sign-line {
  width: 240px;
  margin-left: auto;
  border-bottom: 1px solid #6b6155;
  margin-bottom: 8px;
}
.receipt-sign-issuer { font-weight: 500; color: #2f2a24; }

@media print {
  .receipt-page { padding: 0; }
  .no-print { display: none !important; }
}

/* A specimen receipt has to be unmistakable, including once it is printed:
   the band stays on paper while the explanation above it does not. */
.receipt-specimen {
  background: repeating-linear-gradient(135deg, #fdf0f4, #fdf0f4 12px, #fbe4ec 12px, #fbe4ec 24px);
  border: 1px dashed #8a1538;
  color: #8a1538;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-align: center;
  padding: 8px;
  margin-bottom: 22px;
  border-radius: 4px;
}
.receipt-preview-note {
  background: #fdf6e6;
  border-left: 4px solid #c8860d;
  border-radius: 4px;
  padding: 14px 18px;
  margin-bottom: 22px;
  font-size: 0.92rem;
  line-height: 1.6;
  color: #4a443c;
  max-width: 820px;
}

/* Settings is eleven panels long; this is the shortcut to the one you came for.
   scroll-margin keeps the heading clear of the sticky top bar on arrival. */
.settings-jump {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 22px;
}
.settings-jump a {
  display: inline-block;
  padding: 6px 14px;
  border: 1px solid #e3ddd2;
  border-radius: 999px;
  background: #fff;
  color: #6b6155;
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
}
.settings-jump a:hover {
  border-color: #8a55c4;
  color: #4b2a72;
  background: #f7eefc;
  text-decoration: none;
}
.panel[id] { scroll-margin-top: 20px; }
.panel[id]:target {
  box-shadow: 0 0 0 2px #8a55c4;
  transition: box-shadow 0.3s ease;
}

/* The sample-receipt buttons sit above the fields they preview. */
.receipt-preview-buttons {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 20px;
}
.receipt-preview-buttons .hint { flex: 1 1 260px; margin: 0; }

/* ---------------------------------------------------------------------------
   The receipt on screen. This mirrors app/receipt_pdf.php: same bands, same
   order, same two colours out of the logo — navy #1B0060 for words, pink
   #D860A8 for the bands. A4 proportions, so what is on screen is what prints.
   --------------------------------------------------------------------------- */
:root {
  --rcpt-navy: #1b0060;
  --rcpt-pink: #d860a8;
  --rcpt-pink-pale: #fceef6;
  --rcpt-ink: #2f2a24;
  --rcpt-soft: #6b6155;
  --rcpt-line: #e3ddd2;
}

.rcpt-doc {
  /* Sarabun is the face the PDF embeds; asking for it first is what makes the
     screen and the file look like the same document. The rest are what the
     admin and the public layouts already load. */
  font-family: 'Sarabun', 'Kanit', 'IBM Plex Sans Thai', system-ui, sans-serif;
  width: 100%;
  max-width: 210mm;
  margin: 0 auto;
  background: #fff;
  color: var(--rcpt-ink, #2f2a24);
  box-shadow: 0 2px 18px rgba(27, 0, 96, 0.12);
  position: relative;
  overflow: hidden;
}

.rcpt-band {
  /* white, like the PDF; the rule is the separation */
  background: #fff;
  color: var(--rcpt-navy, #1b0060);
  border-top: 2px solid var(--rcpt-pink, #d860a8);
}
.rcpt-band-top { border-top: none; border-bottom: 2px solid var(--rcpt-pink, #d860a8); }
.rcpt-band-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 22px;
  min-height: 118px;
}
.rcpt-logo-panel {
  /* the band is pale now, so the wordmark needs nothing behind it */
  background: transparent;
  padding: 0;
  flex: 0 0 auto;
  line-height: 0;
}
.rcpt-logo-panel img { width: 148px; height: auto; }

.rcpt-issuer {
  text-align: right;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.82rem;
  line-height: 1.45;
}
.rcpt-issuer strong { font-size: 1.12rem; margin-bottom: 3px; }

.rcpt-body { padding: 26px 46px 34px; }

.rcpt-title {
  font-family: inherit;
  color: var(--rcpt-navy, #1b0060);
  text-align: center;
  font-size: 2.6rem;
  font-weight: 700;
  margin: 12px 0 0;
  line-height: 1.1;
}
.rcpt-title-th {
  text-align: center;
  color: var(--rcpt-soft, #6b6155);
  font-size: 1rem;
  margin: 2px 0 0;
}
.rcpt-specimen-line {
  text-align: center;
  color: var(--rcpt-pink, #d860a8);
  font-weight: 600;
  margin: 6px 0 0;
}

.rcpt-meta {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  border-bottom: 2px solid var(--rcpt-ink, #2f2a24);
  padding: 22px 0 8px;
  font-size: 0.95rem;
}
.rcpt-label { color: var(--rcpt-soft, #6b6155); font-weight: 400; }

.rcpt-h2 {
  font-size: 1.02rem;
  font-weight: 700;
  margin: 22px 0 10px;
  color: var(--rcpt-ink, #2f2a24);
}

.rcpt-billed {
  display: grid;
  grid-template-columns: 78px 1fr;
  gap: 5px 10px;
  margin: 0 0 26px;
  font-size: 0.92rem;
}
.rcpt-billed dt { color: var(--rcpt-soft, #6b6155); font-weight: 600; }
.rcpt-billed dd { margin: 0; }

.rcpt-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.rcpt-table th,
.rcpt-table td { border: 1px solid var(--rcpt-ink, #2f2a24); padding: 11px 14px; }
.rcpt-table thead th {
  background: #fff;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-align: left;
}
.rcpt-table thead th:last-child,
.rcpt-table tbody td:last-child,
.rcpt-table tfoot td { text-align: right; width: 34%; }
.rcpt-table tbody td { border-top: none; border-bottom: none; }
.rcpt-table tbody tr:first-child td { padding-top: 16px; }
.rcpt-table tbody tr:last-child td { padding-bottom: 28px; }
.rcpt-table tfoot th,
.rcpt-table tfoot td {
  background: #fff;
  color: var(--rcpt-navy, #1b0060);
  font-weight: 700;
  font-size: 1rem;
  border-top-width: 2px;
}
.rcpt-table tfoot th,
.rcpt-table tfoot td { text-align: right; }

.rcpt-pay { color: var(--rcpt-pink, #d860a8); margin: 20px 0 0; font-size: 0.95rem; }

.rcpt-close {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 30px;
  margin-top: 16px;
  min-height: 96px;
}
.rcpt-note {
  font-size: 0.82rem;
  color: var(--rcpt-soft, #6b6155);
  max-width: 300px;
  line-height: 1.55;
}
.rcpt-note strong { display: block; color: var(--rcpt-soft, #6b6155); }

.rcpt-sign {
  display: grid;
  grid-template-columns: auto minmax(150px, 210px) auto;
  align-items: end;
  gap: 4px 8px;
  font-size: 0.86rem;
  color: var(--rcpt-soft, #6b6155);
  text-align: center;
}
.rcpt-sign-lead { text-align: right; }
.rcpt-sign-line { border-bottom: 1px solid var(--rcpt-ink, #2f2a24); height: 26px; }
.rcpt-sign-role { text-align: left; }
.rcpt-sign-issuer {
  grid-column: 2;
  font-weight: 600;
  color: var(--rcpt-ink, #2f2a24);
  padding-top: 4px;
}

.rcpt-band-foot {
  display: flex;
  justify-content: space-around;
  align-items: center;
  gap: 16px;
  padding: 16px 22px;
  font-size: 0.82rem;
  min-height: 62px;
}

/* the watermark, matching the one drawn across the PDF */
.rcpt-doc.is-specimen .rcpt-body::before {
  content: "SPECIMEN";
  position: absolute;
  top: 44%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-28deg);
  font-size: 5.2rem;
  font-weight: 700;
  color: rgba(216, 96, 168, 0.22);
  letter-spacing: 0.06em;
  pointer-events: none;
  white-space: nowrap;
}

@media (max-width: 640px) {
  .rcpt-band-top { flex-direction: column; align-items: flex-start; }
  .rcpt-issuer { text-align: left; }
  .rcpt-body { padding: 20px 18px 26px; }
  .rcpt-title { font-size: 2rem; }
  .rcpt-close { flex-direction: column; align-items: stretch; }
  .rcpt-band-foot { flex-direction: column; gap: 4px; }
}

@media print {
  .rcpt-doc { box-shadow: none; max-width: none; }
  .rcpt-band,
  .rcpt-table thead th,
  .rcpt-table tfoot th,
  .rcpt-table tfoot td { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ---------------------------------------------------------------------------
   Required fields on the registration and abstract forms.

   The star is drawn from the control's own `required` attribute rather than
   typed into each label, so it can never claim a field is required when the
   browser will happily accept it empty, or stay silent on one that is. Labels
   sit immediately before their control throughout this form, which is what
   makes the adjacent-sibling test work.
   --------------------------------------------------------------------------- */
#registrationForm label:has(+ [required])::after,
#submit label:has(+ [required])::after {
  content: " *";
  color: var(--magenta, #d6469a);
  font-weight: 600;
}

.required-legend {
  font-size: 18px;
  color: var(--ink-soft, #6b6155);
  margin: 0 0 22px;
}
.required-legend .req {
  color: var(--magenta, #d6469a);
  font-weight: 600;
}

/* The "email me the sample" row in the Receipt panel. */
.receipt-sample-send {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px 16px;
  margin: 0 0 22px;
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.6);
}
.receipt-sample-send label { margin: 0; font-weight: 500; }
.receipt-sample-send .form-control { width: auto; min-width: 240px; }
.receipt-sample-send .hint { flex: 1 1 280px; margin: 0; }

/* ---------------------------------------------------------------------------
   Check in. Read at arm's length on a phone, by somebody standing at a desk
   with a queue in front of them: big name, one colour that says the answer.
   --------------------------------------------------------------------------- */
.checkin-lookup {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.checkin-lookup label { margin: 0; font-weight: 500; }
.checkin-lookup .form-control {
  width: auto;
  min-width: 220px;
  font-size: 1.15rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.checkin-card { border-left: 6px solid var(--line, #e7e1ef); }
.checkin-card.is-good { border-left-color: #1f8a4c; background: #f2fbf5; }
.checkin-card.is-seen { border-left-color: #c8860d; background: #fdf6e6; }
.checkin-card.is-warn { border-left-color: #b3261e; background: #fdf1f0; }
.checkin-card.is-bad { border-left-color: #b3261e; background: #fdf1f0; }

.checkin-state {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.is-good .checkin-state { color: #1f8a4c; }
.is-seen .checkin-state { color: #8a5b00; }
.is-warn .checkin-state { color: #b3261e; }

.checkin-name {
  font-size: 2rem;
  line-height: 1.2;
  margin: 0 0 6px;
}
.checkin-meta { color: var(--ink-soft, #6b6155); margin: 0 0 14px; line-height: 1.6; }
.checkin-warn {
  background: #fff;
  border: 1px solid #f0c9c6;
  border-radius: 8px;
  padding: 12px 14px;
  margin: 0 0 14px;
}
.checkin-when, .checkin-diet { margin: 0 0 12px; }
.checkin-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 4px; }

@media (max-width: 640px) {
  .checkin-name { font-size: 1.6rem; }
  .checkin-lookup .form-control { min-width: 0; flex: 1 1 140px; }
}

/* The payment slip beside a registration: a thumbnail to glance at, a PDF
   badge where there is nothing to show. */
.slip-thumb {
  display: block;
  padding: 0;
  margin: 0 0 6px;
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 6px;
  background: #fff;
  cursor: zoom-in;
  overflow: hidden;
  line-height: 0;
}
.slip-thumb img {
  width: 104px;
  height: 78px;
  object-fit: cover;
  display: block;
}
.slip-thumb:hover { border-color: #8a55c4; }

.slip-pdf { display: inline-flex; align-items: center; gap: 7px; }
.slip-pdf-tag {
  display: inline-block;
  background: #b3261e;
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: 3px;
}

/* a slip is a document, not a portrait, so it gets more room than the
   committee photographs the lightbox was built for */
.slip-lightbox-inner { max-width: 820px; }
.slip-lightbox-inner img {
  max-width: 100%;
  max-height: 78vh;
  width: auto;
  background: #fff;
}

/* The one rule on the refund page that everything else follows from, set apart
   so nobody can say they did not see it. */
.policy-headline {
  border-left: 4px solid var(--magenta, #d6469a);
  background: #fceef6;
  border-radius: 6px;
  padding: 18px 20px;
  margin: 0 0 22px;
}
.policy-headline h2 { margin-bottom: 8px; }
.policy-th {
  margin-top: 10px;
  color: var(--ink-muted, #4a443c);
  font-size: 18px;
}

/* The one box the status pages now ask for: a code read off an email and typed
   in, so it is set large and spaced, and shown in capitals whatever is typed. */
.code-input {
  font-size: 1.3rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  max-width: 320px;
}

/* ---------------------------------------------------------------------------
   One abstract, as the secretariat reads it. Two columns on a wide screen: the
   work on the left to read, the people and the decision on the right to act on.
   --------------------------------------------------------------------------- */
.abstract-columns {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
.abstract-body {
  white-space: pre-wrap;
  margin-bottom: 14px;
  line-height: 1.75;
}
.abstract-file-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
/* tall enough to read a one-page A4 without scrolling the page as well */
.abstract-viewer {
  width: 100%;
  height: 760px;
  border: 0;
  border-top: 1px solid var(--line, #e7e1ef);
  display: block;
}

.detail-list {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  gap: 6px 12px;
  margin: 0;
}
.detail-list dt { color: var(--ink-soft, #6b6155); font-weight: 500; }
.detail-list dd { margin: 0; word-break: break-word; }
/* "Name for the certificate" does not fit the 128px the admin panels use */
.detail-list-wide { grid-template-columns: 210px minmax(0, 1fr); }
@media (max-width: 560px) {
  .detail-list,
  .detail-list-wide {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .detail-list dd { margin-bottom: 10px; }
}

.review-card {
  border-top: 1px solid var(--line, #e7e1ef);
  padding-top: 12px;
  margin-top: 12px;
}
.review-card:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.review-scores {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 6px 0 8px;
  font-size: 0.88rem;
  color: var(--ink-soft, #6b6155);
}
.review-quote {
  background: #faf7f2;
  border-radius: 6px;
  padding: 10px 12px;
  font-size: 0.9rem;
  margin: 0 0 8px;
}
.review-quote.review-private { background: #f4f6fb; }

/* the decision is three choices and nothing else, laid out so the wrong one is
   hard to hit by accident */
.decision-choices { display: grid; gap: 8px; margin-top: 4px; }
.decision-choice {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 8px;
  padding: 12px 14px;
  cursor: pointer;
  background: #fff;
  margin: 0;
}
.decision-choice:hover { border-color: #8a55c4; }
.decision-choice:has(input:checked) {
  border-color: #8a55c4;
  background: #f7eefc;
  box-shadow: inset 0 0 0 1px #8a55c4;
}
.decision-choice input { margin: 0; }
/* the states that are not decisions: same control, quieter, in a row */
.decision-states {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.decision-choice-sm {
  padding: 7px 12px;
  font-size: 0.9rem;
  color: var(--ink-soft, #6b6155);
}

@media (max-width: 1000px) {
  .abstract-columns { grid-template-columns: minmax(0, 1fr); }
  .abstract-viewer { height: 560px; }
}

/* One booking: the five numbers that answer "what is this" across the top,
   then the detail. */
.booking-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin: 0 0 22px;
}
.booking-figure {
  border: 1px solid var(--line, #e7e1ef);
  border-radius: 10px;
  background: #fff;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.booking-figure-label {
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft, #6b6155);
}
.booking-figure strong { font-size: 1.28rem; line-height: 1.25; word-break: break-word; }
.booking-figure .hint { margin: 0; }

.booking-money td:last-child { text-align: right; white-space: nowrap; }
.booking-money .booking-total td { border-top: 2px solid var(--ink, #2f2a24); }

.booking-activity { list-style: none; margin: 0; padding: 0; font-size: 0.9rem; }
.booking-activity li {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 7px 0;
  border-top: 1px solid var(--line-light, #eee);
}
.booking-activity li:first-child { border-top: 0; }
.booking-activity li > span:first-child { color: var(--ink-soft, #6b6155); min-width: 84px; }

/* The delegate list's filter row: a wide search box and two narrow selects. */
.delegate-filters {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 14px;
  align-items: end;
}
.delegate-filter-actions { display: flex; gap: 8px; }
.delegate-diet {
  display: inline-block;
  background: #fdf6e6;
  border: 1px solid #f0dcae;
  border-radius: 999px;
  padding: 1px 9px;
  font-size: 0.78rem;
  color: #8a5b00;
  margin-top: 4px;
}
@media (max-width: 900px) {
  .delegate-filters { grid-template-columns: minmax(0, 1fr); }
}

/* The booking page. Wide tables get the whole width; the pair of narrow things
   sits side by side; the history folds away because it is reference, not the
   reason anybody opened the page. */
.booking-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
  margin-bottom: 18px;
}

/* payment, receipt and tickets read as one row of things you can do */
.booking-actions {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 26px;
  align-items: start;
}
.booking-actions h3 { margin-bottom: 10px; }
.booking-actions .hint { display: block; margin-top: 8px; }

.booking-history { padding: 0; }
.booking-history > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 20px;
  cursor: pointer;
  list-style: none;
}
.booking-history > summary::-webkit-details-marker { display: none; }
.booking-history > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--ink-soft, #6b6155);
  transition: transform 0.15s ease;
}
.booking-history[open] > summary::after { transform: rotate(180deg); }
.booking-history > summary .hint {
  background: #f1ece6;
  border-radius: 999px;
  padding: 1px 9px;
  font-size: 0.8rem;
}
.booking-history > summary:hover { background: #faf7f2; }
.booking-history .panel-body { padding-top: 0; }
.booking-history .data-table .nowrap { white-space: nowrap; }

@media (max-width: 1100px) {
  .booking-grid { grid-template-columns: minmax(0, 1fr); }
  .booking-actions { grid-template-columns: minmax(0, 1fr); gap: 22px; }
}

/* A list table: many rows read at a glance rather than a few rows full of
   controls. Tighter than the default, numbers right-aligned so they compare
   down the column, and a muted row number to count and to refer to. */
/* The three working lists — Registrations, Delegates, Abstracts — are read by
   the row, not by the sentence: more rows on screen at once is worth more than
   larger type, so this scale is a step down from the rest of the admin. The
   controls inside a row come down with it, or they set the row height and the
   tighter cells buy nothing. */
.list-table { font-size: 0.82rem; }
.list-table th,
.list-table td {
  padding: 7px 9px;
  vertical-align: top;
}
.list-table thead th {
  white-space: nowrap;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.list-table .hint { font-size: 0.78rem; line-height: 1.45; }
.list-table .btn-sm {
  padding: 5px 11px;
  font-size: 12.5px;
}
.list-table .status-pill {
  min-height: 22px;
  padding: 2px 7px;
  font-size: 9.5px;
}
.list-table code { font-size: 0.78em; }
.list-table .form-control,
.list-table .custom-select {
  padding: 5px 9px;
  font-size: 12.5px;
}
.list-table tbody tr:hover { background: #faf7f2; }
.list-table .num { text-align: right; white-space: nowrap; }
.list-table .col-n {
  width: 1%;
  text-align: right;
  color: var(--ink-faint, #8b8378);
  font-variant-numeric: tabular-nums;
}
.list-table .hint { display: block; margin: 2px 0 0; }
/* a reference is one token and reads as nonsense broken over three lines */
.list-table .col-ref,
.list-table .col-ref a { white-space: nowrap; }

/* The title is the one column allowed to be long; everything else on an
   abstract row is a word or two, so give it the room and stop it stretching the
   table past the screen. */
.list-table .col-title { min-width: 250px; max-width: 470px; }
.list-table .abstract-title {
  font-weight: 600;
  line-height: 1.35;
}

/* the submitted text, folded. Open, it is capped and scrolls, so one long
   abstract cannot push every other row off the screen -- which is the stack
   this page was rebuilt to get away from. */
.body-fold {
  margin: 6px 0 4px;
}
.body-fold > summary {
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--ink-soft, #6b6155);
  list-style-position: inside;
}
.body-fold > summary:hover { color: var(--plum, #4b2a72); }
.body-text {
  max-height: 260px;
  overflow: auto;
  margin-top: 6px;
  padding: 10px 12px;
  border: 1px solid var(--line, #e3ddd2);
  border-radius: 6px;
  background: #fff;
  font-size: 0.85rem;
  line-height: 1.6;
  white-space: normal;
}

/* the small row of links under a cell: download, open, final file */
.cell-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: 4px;
  font-size: 0.8rem;
}
.cell-tools .hint { display: inline; margin: 0; }
.list-table .col-actions {
  width: 1%;
  white-space: nowrap;
}
.list-table .col-actions .btn + .btn { margin-left: 6px; }

/* the reviewer picker, in a full-width row under the abstract it belongs to */
.assign-row > td {
  background: #faf7f2;
  border-top: 0;
}
.assign-panel { padding: 4px 0 6px; }
.assign-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 22px;
  margin: 10px 0 12px;
}
.assign-choices .choice-inline { margin: 0; }
.assign-choices .hint { display: inline; margin-left: 4px; }
.assign-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
}
.assign-actions .hint { margin: 0; }

/* The author's own abstracts. Same table as the admin lists, read by people
   who see it once, so the rows that ask something of them stand out from the
   rows that only report. It used to be a step larger than the admin's tables
   as well; both are 18px now, so that part of the difference is gone. */
.account-table { font-size: 18px; }
.account-table .abstract-title {
  display: block;
  font-weight: 600;
  line-height: 1.35;
}
.account-table .status-badge { white-space: nowrap; }
.account-note > td,
.account-action > td {
  border-top: 0;
  padding-top: 0;
  padding-bottom: 16px;
}
.account-note > td { color: var(--ink-soft, #6b6155); }
.account-action > td {
  background: #fceef6;
  border-left: 3px solid var(--magenta, #d6469a);
  padding: 14px 16px 16px;
}
.account-action .download-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin: 6px 0 4px;
}
.account-action .download-row .form-control { flex: 1 1 320px; }
.account-link-form label {
  display: block;
  margin-bottom: 2px;
  font-weight: 600;
  font-size: 18px;
}

/* adding somebody who is not on the list yet, from the same panel */
.assign-panel.new-reviewer {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line, #e3ddd2);
}
.new-reviewer-grid {
  display: grid;
  grid-template-columns: 1.2fr 1.4fr 1fr auto;
  gap: 10px 14px;
  align-items: end;
  margin: 10px 0 10px;
}
.new-reviewer-grid label {
  display: block;
  margin-bottom: 3px;
  font-size: 0.78rem;
  color: var(--ink-soft, #6b6155);
}
.new-reviewer-go { padding-bottom: 1px; }
@media (max-width: 900px) {
  .new-reviewer-grid { grid-template-columns: 1fr; }
}

/* anything that must not be broken across two lines, a date most of all */
.nowrap { white-space: nowrap; }
.list-table code { font-size: 0.82em; }

/* the slip on a list row is a glance, not a picture to study */
.slip-thumb-sm img { width: 56px; height: 42px; }

/* A page that is mostly a wide table gets the screen. The 1200px column is
   right for forms and prose and wrong for eleven columns of figures. */
.admin-body.is-wide .admin-shell { max-width: 1760px; }
.admin-body.is-wide .booking-figures { max-width: 1100px; }
.admin-body.is-wide .delegate-filters { max-width: 1400px; }

/* The status control on a list row: a select and a button on one line, sized
   so the row stays a row. */
.row-status {
  display: flex;
  align-items: center;
  gap: 6px;
}
.row-status .custom-select-sm {
  width: auto;
  min-width: 150px;
  max-width: 190px;
  font-size: 0.82rem;
}
.row-status .btn { flex: 0 0 auto; }

/* The worked example under the receipt-numbering boxes. */
.receipt-number-example {
  background: #fceef6;
  border-left: 4px solid var(--magenta, #d6469a);
  border-radius: 6px;
  padding: 12px 14px;
}
.receipt-number-example code { font-size: 1.02em; font-weight: 600; }
.receipt-number-example .hint { display: block; margin-top: 4px; }

/* The review button sits with the status on an abstract row, so it is the
   first thing on the right rather than something found by unfolding. */
.abstract-row-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  text-align: right;
  flex: 0 0 auto;
}

/* Download / Email again on a receipt: side by side, and the disabled one still
   reads as a button so it is plainly a thing that exists and is switched off,
   not a thing that is missing. */
.receipt-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-top: 6px;
}
.receipt-actions form { margin: 0; }
.receipt-actions .btn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* the master switch above the receipt settings it governs */
.setting-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px 22px;
  margin: 0 0 22px;
  padding: 14px 16px;
  border: 1px solid var(--line, #e3ddd2);
  border-left: 3px solid var(--magenta, #d6469a);
  border-radius: 8px;
  background: #fff;
}
.setting-switch > div { flex: 0 0 auto; min-width: 260px; }
.setting-switch label { display: block; margin-bottom: 4px; font-weight: 600; }
.setting-switch .hint { flex: 1 1 320px; }

/* Sending a revision back: a form inside a table row, so it needs its own
   vertical rhythm — the table's cell padding is for cells, not for fields. */
.revision-form {
  display: grid;
  gap: 4px;
  margin-top: 10px;
  max-width: 820px;
}
.revision-form label {
  margin-top: 8px;
  font-weight: 600;
  font-size: 18px;
}
.revision-form label .hint { display: inline; font-weight: 400; }
.revision-form textarea { min-height: 200px; }
.revision-go {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin-top: 14px;
}
.revision-go .hint { margin: 0; }

/* the ticket cell on a delegate row: the button, with the code under it in
   the same five-character grouping the ticket itself prints */
.ticket-cell {
  white-space: nowrap;
}
.ticket-cell form { margin: 0 0 4px; }
.ticket-cell code {
  font-size: 0.8rem;
  color: var(--ink-soft, #6b6155);
  letter-spacing: 0.06em;
}

/* The two columns that carry several short facts, one per line. Without a floor
   on their width the phrases wrap mid-way — "waiting for / revision 1" — which
   reads worse than the run-on sentence they replaced. */
.list-table .col-status { min-width: 144px; }
.list-table .col-reviews { min-width: 138px; }
.list-table .col-status .status-pill { margin-bottom: 4px; }

/* "Earlier versions" is a warning as much as a heading: what is under it is not
   what is being decided on. */
.heading-alert { color: var(--red, #c0392b); }

/* Anything that destroys data: set apart, never mixed in with the ordinary
   controls, and never a single click. */
.danger-zone {
  border-color: oklch(0.85 0.07 25);
}
.danger-zone > .panel-body { border-left: 3px solid var(--red, #c0392b); }
.danger-zone h2,
.danger-zone h3 { color: var(--red, #c0392b); }
.reset-block + .reset-block {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line, #e3ddd2);
}
.danger-form { margin: 10px 0 0; }
.danger-form label { display: block; margin-bottom: 4px; font-size: 0.92rem; }
.danger-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
.danger-row .form-control { max-width: 260px; }
.btn-danger {
  background: var(--red, #c0392b);
  border-color: var(--red, #c0392b);
  color: #fff;
}
.btn-danger:hover { background: #a32f22; border-color: #a32f22; color: #fff; }
.btn-danger[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn-outline-danger {
  border: 1px solid oklch(0.85 0.07 25);
  color: var(--red, #c0392b);
  background: #fff;
}
.btn-outline-danger:hover { background: oklch(0.97 0.03 25); color: var(--red, #c0392b); }

/* Asking "remove?" in the row it is about, rather than in a browser dialog that
   can be suppressed, dismissed unread, or never shown at all with JS off. */
.confirm-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin: 0;
  white-space: nowrap;
}
.confirm-inline strong { color: var(--red, #c0392b); }
tr.is-confirming > td { background: oklch(0.975 0.02 25); }

/* the word that has to be typed before a person is removed */
.confirm-word {
  width: 130px;
  max-width: 130px;
  text-transform: uppercase;
}

/* the status dropdown in a row: wide enough for its longest short label */
.list-table .row-status .custom-select { min-width: 152px; }

/* ---------------------------------------------------------------------------
   Messages between a delegate and the secretariat, on the booking they concern.

   Read as a conversation, oldest at the top. A reply from the secretariat is
   tinted and indented so the two sides can be told apart at a glance without
   needing a label to be read.
   --------------------------------------------------------------------------- */
.msg-thread {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
  display: grid;
  gap: 14px;
}
.msg {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  background: var(--surface, #fff);
}
.msg-staff {
  background: var(--purple-tint);
  border-color: var(--purple-light);
  margin-left: 24px;
}
.msg-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
  margin-bottom: 6px;
  font-size: 0.9rem;
}
.msg-who time {
  color: var(--ink-soft, #6b6155);
  font-size: 0.85rem;
  white-space: nowrap;
}
.msg-body {
  margin: 0;
  line-height: 1.55;
}

.msg-form {
  display: grid;
  gap: 4px;
  max-width: 820px;
}
.msg-form label {
  font-weight: 600;
  font-size: 0.92rem;
}
.msg-form textarea { min-height: 120px; }
.msg-go { margin-top: 12px; }

/* On a phone the indent costs more than it explains, and a long name beside a
   date that must not break pushes the date off the edge — so they stack. */
@media (max-width: 640px) {
  .msg-staff { margin-left: 0; }
  .msg-who { display: block; }
  .msg-who time {
    display: block;
    white-space: normal;
    margin-top: 2px;
  }
}

/* The unread count on the registrations list: the point is to be countable at
   a glance, so it carries the number rather than a dot. */
.msg-flag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--magenta);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The Enquiries queue: two different kinds of message read as one list.
   --------------------------------------------------------------------------- */
.enquiry-snip {
  margin: 2px 0 3px;
  color: var(--ink-soft, #6b6155);
  line-height: 1.45;
}
/* A row nobody has answered yet earns a mark down its left edge — the eye
   finds it without reading the state column. */
.list-table tr.is-waiting > td:first-child {
  box-shadow: inset 3px 0 0 var(--magenta);
}
.enquiry-open > td {
  background: var(--purple-tint);
  border-top: 0;
}
/* The reply and the note side by side: they are written at the same moment,
   and putting one under the other hides whichever is second. */
.enquiry-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 24px;
  margin-top: 18px;
}
.enquiry-actions .msg-form { max-width: none; }
.enquiry-actions .msg-form textarea { min-height: 110px; }
/* The state pill in the Enquiries queue reads as one phrase, so it is not
   allowed to break across two lines inside a narrow column. */
.list-table .row-status .status-pill { white-space: nowrap; }

/* Figures that are the point of the block they are in, rather than one fact
   among many -- the visitor counts and the dashboard's record counts. Large
   enough to read across the room, which is how a dashboard is read. */
.metric-lg strong {
  display: block;
  font-size: 34px;
  font-weight: 500;
  line-height: 1.1;
}

.metric-lg strong small {
  font-size: 16px;
  font-weight: 400;
  color: var(--ink-faint);
}

/* Five counts that belong on one line. `auto-fit` with a 200px floor put the
   fifth on a row of its own, because five of those plus four 32px gaps come to
   more than the 1200px column allows. Naming the number of columns says what
   is wanted; the gap comes down to pay for it, and below 900px they go two up
   rather than being squeezed to nothing. */
.metric-grid.is-five {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 20px;
}

@media (max-width: 900px) {
  .metric-grid.is-five {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Inside a panel the grid already has the panel's own edges, so its rules
   would be a second line against the first. */
.panel-body > .metric-grid {
  padding-bottom: 0;
  border-bottom: 0;
}

/* Two panels side by side on the Visitors page where the screen allows it --
   the periods and the countries are both short lists and read better beside
   each other than one under the other. Below 1100px they stack, because at
   that point each would be too narrow for its own bars. */
.visit-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin-bottom: 24px;
}

.visit-columns .panel {
  margin: 0;
  min-width: 0;
}

@media (max-width: 1100px) {
  .visit-columns {
    grid-template-columns: 1fr;
  }
}

/* One person's way through the site, on the Visitors page. The arrows carry
   the order, so they are quieter than the pages they sit between. */
.visit-path {
  margin-top: 2px;
  line-height: 1.7;
}

.visit-arrow {
  margin: 0 6px;
  color: var(--ink-faint);
}

.visit-here {
  font-weight: 600;
  color: var(--purple);
}

/* The address, in mono so that a column of them lines up and a digit that
   differs is easy to spot. */
.visit-ip {
  font-family: var(--font-mono);
  font-size: 0.9em;
  color: var(--ink-soft);
}

/* The little arrow that opens the page itself, beside the link that filters
   by it. Small and grey: the filter is the main action here. */
.visit-open {
  margin-left: 6px;
  color: var(--ink-faint);
  text-decoration: none;
}

.visit-open:hover {
  color: var(--purple);
}

/* The bar beside each day on the Visitors page. Drawn against the busiest day
   on show rather than a fixed scale, so the shape of a month reads the same
   whether the site is doing ten a day or ten thousand. Decoration only -- the
   figure it illustrates is in the column beside it. */
.visit-bar-cell {
  width: 34%;
  min-width: 120px;
}

.visit-bar {
  display: block;
  height: 10px;
  min-width: 2px;
  border-radius: 999px;
  background: var(--purple);
  opacity: 0.55;
}

/* "Did you mean gmail.com?" under an email field. Quiet: it is a question
   about something that is probably fine, not a refusal, so it does not take
   the red an error takes. The suggestion itself is the button, because the
   address is the thing being offered and making it the target saves a line. */
.email-suggest {
  margin: 6px 0 0;
  font-size: 16.5px;
  color: var(--ink-soft);
}

.email-suggest-fix {
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--purple);
  background: none;
  border: 0;
  border-bottom: 1px solid currentColor;
  cursor: pointer;
}

.email-suggest-fix:hover {
  color: var(--magenta);
}

/* ---------------------------------------------------------------------------
   How big the admin's text is, and the control that changes it.

   The public side was lifted to 18px and the admin was meant to stay where it
   was; but a lift done by replacing every `16.5px` in this file moved the
   rules the two sides share, and the admin went up with them. The rules below
   hand those back, and they read their figure from one custom property rather
   than repeating a number, so the buttons in the top bar only have to set the
   property.

   16 is the default and what the client asked for. 18 and 20 are there for a
   long day, a large screen, or eyes that would rather not squint at a column
   of reference numbers. The choice is kept in the browser it was made in --
   it is a comfort setting for whoever is looking, not a property of the site,
   and two people sharing the one admin login may want different answers.
   --------------------------------------------------------------------------- */
.admin-body { --admin-text: 16px; }
.admin-body[data-text="18"] { --admin-text: 18px; }
.admin-body[data-text="20"] { --admin-text: 20px; }

body.admin-body { font-size: var(--admin-text); }

/* Every rule in this file that names a size and is used on both sides. Listed
   rather than globbed, so adding one here is a decision somebody made. */
.admin-body .page-head p,
.admin-body .metric span,
.admin-body .data-table td,
.admin-body .btn,
.admin-body .btn-sm,
.admin-body .form-control,
.admin-body .custom-select,
.admin-body textarea,
.admin-body input[type="file"],
.admin-body .hint,
.admin-body .person-card .name,
.admin-body .person-card .place,
.admin-body .alert,
.admin-body .junk-notice,
.admin-body .status-pill,
.admin-body .status-badge,
.admin-body .lightbox-name {
  font-size: var(--admin-text);
}

/* The control itself: three plain figures in the top bar, the chosen one
   filled in. Small, because it is furniture rather than a tool. */
.admin-textsize {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 10px;
}

.admin-textsize button {
  min-width: 30px;
  padding: 3px 6px;
  font-family: var(--font-mono);
  /* Fixed, not var(--admin-text): the control must not resize itself under
     the finger that is pressing it. */
  font-size: 13px;
  line-height: 1.2;
  color: inherit;
  background: none;
  border: 1px solid currentColor;
  border-radius: 4px;
  opacity: 0.55;
  cursor: pointer;
}

.admin-textsize button:hover { opacity: 0.85; }

/* The top bar is --surface, a near-white, so a white wash would have marked
   the chosen figure invisibly. The house purple instead: it is the colour the
   admin already uses for the thing that is currently selected. */
.admin-textsize button[aria-pressed="true"] {
  opacity: 1;
  font-weight: 600;
  color: var(--purple);
  background: var(--purple-tint);
  border-color: var(--purple);
}

/* ---------------------------------------------------------------------------
   Public pages read one step larger than the admin.

   The two sides are read differently. The admin is a working tool scanned in
   rows, and its lists were deliberately tightened so more of them fit on a
   screen. The public site is read in sentences, on delegates' own phones, by
   people who are often reading a second language — there the larger size is
   worth the extra scrolling.

   Scoped to .app-body, because every class touched here is shared with the
   admin and changing the rules themselves would undo that tightening.
   --------------------------------------------------------------------------- */
/* `body.app-body` and not `.app-body`: the base rule near the top of this file
   is written `body.app-body`, which outweighs a bare class, and a bare class
   here would lose in silence — the headings would grow and the paragraphs
   would not. */
body.app-body { font-size: 18px; }
/* The standfirst is set with the other leads, at .page-lead further up. It
   used to be overridden to 18.5px here, half a step above the body copy, and
   that line outranked the rule it was meant to nudge -- so the lead size set
   there never reached the page. */
.app-body .info-card p { font-size: 18px; }
.app-body .data-table td { font-size: 18px; }
/* The hint carries real instructions — what a code looks like, what a file may
   weigh, why a box is empty — so it is brought close to the body text rather
   than kept at the small size it had when it was only a footnote. */
.app-body .hint { font-size: 18px; }

/* ---------------------------------------------------------------------------
   The interest panel, shown where a form is not open yet.
   --------------------------------------------------------------------------- */
/* The panel is the only thing on the page now, so it is allowed to look like
   it: a heavier edge than an ordinary panel, and a tint behind it. */
.interest-panel {
  border: 2px solid var(--purple);
  border-radius: 14px;
  background: var(--purple-tint);
}

/* "Register Soon" is the first thing to read and the reason the form is not
   here, so it is a boxed flag rather than the quiet grey eyebrow it replaced. */
.interest-flag {
  display: inline-block;
  margin: 0 0 14px;
  padding: 7px 16px;
  border: 2px solid var(--magenta);
  border-radius: 999px;
  background: #fff;
  color: var(--magenta);
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.interest-blurb {
  /* 75ch, not the 62ch a body paragraph gets. The abstracts notice is 98
     characters and broke with "page soon." alone on a second line, which reads
     as a mistake rather than as a wrapped sentence. At 75ch it sits on one
     line, and the measure is still inside the range that is comfortable to
     read. `pretty` keeps the longer notices on the other two panels from
     ending on a stranded word or two as the screen narrows. */
  max-width: 75ch;
  text-wrap: pretty;
  margin: 0 0 22px;
  color: var(--ink-muted);
}
.interest-form .form-grid { margin-bottom: 4px; }
/* `.hint` is a block elsewhere, which pushed "— optional" onto its own line and
   left those two boxes sitting lower than the ones beside them. */
.interest-form label .hint { display: inline; font-weight: 400; }
.interest-go {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-top: 18px;
}
.interest-go .hint { margin: 0; max-width: 52ch; }

/* The honeypot: off-screen rather than display:none, because some robots skip
   what is hidden and a screen reader is told to ignore it by aria-hidden. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Accommodation: one background colour per section.

   The page is long and covers six unrelated subjects — the venue, hotels,
   getting here, what to see, day trips and onsite practicalities. Read as one
   column of white panels they run together, and somebody arriving from the
   menu at #explore cannot tell where that section begins or ends.

   The colour reaches the edges of the window while the text stays in the
   container. That is done with a spread box-shadow rather than the usual
   `margin-left: calc(50% - 50vw)`, because 100vw includes the scrollbar and
   that trick adds a horizontal scrollbar of its own on Windows. `clip-path`
   trims the shadow to a horizontal band so it cannot bleed up or down.
   --------------------------------------------------------------------------- */
.sec-band {
  position: relative;
  padding: 44px 0 48px;
}
.sec-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: inherit;
  box-shadow: 0 0 0 100vmax var(--sec-bg, transparent);
  clip-path: inset(0 -100vmax);
  background: var(--sec-bg, transparent);
}
.sec-band > .sec-inner > :first-child { margin-top: 0; }
.sec-band > .sec-inner > :last-child { margin-bottom: 0; }

/* Six tints, alternating warm and cool so no two neighbours are close enough
   to be mistaken for one section. They stay pale: this is a reading page, and
   the panels inside each section have to keep their own edges. */
.sec-venue    { --sec-bg: oklch(0.985 0.006 70); }
.sec-hotel    { --sec-bg: oklch(0.965 0.014 300); }
.sec-travel   { --sec-bg: oklch(0.975 0.012 200); }
.sec-explore  { --sec-bg: oklch(0.972 0.018 60); }
.sec-daytrips { --sec-bg: oklch(0.968 0.016 150); }
.sec-onsite   { --sec-bg: oklch(0.962 0.012 20); }

/* The heading carries the section's own rule, so the eye finds the start of a
   section even mid-scroll when the colour change is off-screen. */
.sec-band > .sec-inner > h2:first-child {
  padding-bottom: 10px;
  border-bottom: 2px solid var(--purple-light);
}

/* ---------------------------------------------------------------------------
   A switch shown on the page it governs.

   The same control is in Settings, but a setting is easiest to judge with the
   thing it changes in front of you. Only an admin is ever sent this markup.
   It is deliberately unlike the rest of the page — a dark bar, not a panel —
   so it cannot be mistaken for something a visitor is seeing.
   --------------------------------------------------------------------------- */
.admin-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  margin: 0 0 28px;
  padding: 14px 18px;
  border-radius: 10px;
  background: var(--deep);
  color: var(--off-white);
}
.admin-switch strong {
  display: block;
  font-size: 0.95rem;
  font-weight: 600;
}
.admin-switch .hint {
  color: oklch(0.80 0.04 300);
  margin-top: 2px;
}
.admin-switch-state {
  display: inline-block;
  margin-bottom: 4px;
  padding: 2px 10px;
  border-radius: 999px;
  background: oklch(0.42 0.08 150);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.admin-switch-state.is-on { background: var(--magenta); }
.admin-switch form { margin: 0; }

/* The hazard stripe along the top says "work in progress" before a word of it
   is read. Kept to the edge so it cannot interfere with the text. */
.under-construction { position: relative; overflow: hidden; }
.under-construction::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: repeating-linear-gradient(
    -45deg,
    var(--magenta) 0 12px,
    oklch(0.88 0.10 40) 12px 24px
  );
}
.under-construction .panel-body { padding-top: 26px; }

/* The one question the registration interest form asks beyond contact details.
   Boxed, because a question set among a run of text boxes is easy to scroll
   past, and it is the one thing here that must be answered. */
.interest-news {
  margin: 20px 0 0;
  padding: 14px 16px 12px;
  border: 1px solid var(--purple-light);
  border-radius: 10px;
  background: #fff;
}
/* A <legend> is drawn sitting on the fieldset's own border, which cut the box
   in half. Floating it to the full width takes it out of that position and
   puts it inside the box as an ordinary heading, while it stays a legend for a
   screen reader — which is the reason for using a fieldset at all. */
.interest-news legend {
  float: left;
  width: 100%;
  padding: 0;
  margin-bottom: 12px;
  font-weight: 600;
}
.interest-news legend + label { clear: both; }
/* The asterisk means the same thing on every form on the site, so the rule is
   not tied to the one it was first written for. */
.req {
  color: var(--magenta);
  font-weight: 700;
}
.req-note {
  margin: 0 0 14px;
  font-size: 18px;
  color: var(--ink-soft);
}
.interest-news label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 8px;
  font-weight: 400;
  line-height: 1.45;
  cursor: pointer;
}
.interest-news label:last-child { margin-bottom: 0; }
.interest-news input {
  margin: 3px 0 0;
  width: 17px;
  height: 17px;
  flex: none;
  cursor: pointer;
}
.interest-news-hint {
  display: block;
  margin-top: 6px;
  max-width: 62ch;
}

/* ---------------------------------------------------------------------------
   A dropdown that can be typed into, wrapped around a real <select>.

   The <select> is hidden rather than removed: it is still what the form
   submits and still what `required` validates, so with no JavaScript the page
   falls back to an ordinary dropdown and nothing is lost.
   --------------------------------------------------------------------------- */
.ss { position: relative; }
.ss-native {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 100%;
  height: 100%;
  /* left where it is, so the browser can scroll to it and show its own
     "please fill this in" bubble against the right box */
}
.ss-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  text-align: left;
  cursor: pointer;
  background: #fff;
}
.ss-button::after {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--ink-soft);
  border-bottom: 2px solid var(--ink-soft);
  transform: translateY(-2px) rotate(45deg);
}
.ss-button.is-empty { color: var(--ink-faint); }

.ss-panel {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 12px 28px rgba(40, 20, 60, 0.16);
}
.ss-search { margin-bottom: 6px; }
.ss-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 240px;
  overflow-y: auto;
}
.ss-item {
  padding: 7px 10px;
  border-radius: 6px;
  cursor: pointer;
  line-height: 1.35;
}
.ss-item:hover { background: var(--purple-tint); }
.ss-item.is-on {
  background: var(--purple);
  color: #fff;
}
.ss-list.is-empty::after {
  content: "Nothing matches that.";
  display: block;
  padding: 7px 10px;
  color: var(--ink-faint);
}

/* ---------------------------------------------------------------------------
   The Interest list: one field per column, so the widths are set here rather
   than left to the content. Without this the institution names stretch the
   table and the date wraps to three lines.
   --------------------------------------------------------------------------- */
.interest-table th:nth-child(1),
.interest-table td:nth-child(1) { width: 104px; }
.interest-table th:nth-child(4),
.interest-table td:nth-child(4) { min-width: 180px; }
.interest-table th:nth-child(5),
.interest-table td:nth-child(5) { width: 120px; }
.interest-table th:nth-child(6),
.interest-table td:nth-child(6) { min-width: 200px; }

.interest-table .col-asked {
  width: 96px;
  white-space: nowrap;
}
.interest-table .col-asked .hint { margin-top: 0; }
.interest-table .col-asked .hint + .hint { margin-top: 2px; }

/* ---------------------------------------------------------------------------
   The small uppercase labels -- the eyebrows, the metric captions, the state
   pills -- are set in the monospace face, where every character has the same
   advance and the space is as wide as a letter. Added to the tracking each of
   them carried, the gaps between words read as gaps rather than as spaces,
   which is what the client pointed at on 28 Sep 2026.

   So: no tracking, and the space pulled back to about what a proportional font
   gives. The face stays monospace -- that is what makes these read as labels
   rather than as headings -- and only the spacing changes.

   Scoped to what a visitor reads. The admin tables, the receipt stamp and the
   preview bar keep their own spacing: they are chrome and internal documents,
   nobody asked about them, and the receipt is typeset to fit A4.
   --------------------------------------------------------------------------- */
.eyebrow,
.mono-micro,
.tbc,
.metric span,
.cd-unit i,
.period-now,
.type-kicker,
.status-pill,
.role-heading,
.interest-flag,
.date-flag,
.day-tab-label,
.session-kind,
.tracker-state,
.footer-head {
  letter-spacing: normal;
  word-spacing: -0.25em;
}
