/* ==========================================================================
   Event Registration Web App — single stylesheet
   --------------------------------------------------------------------------
   Contents
     1. Design tokens (colours, spacing, radii as CSS custom properties)
     2. Reset and base typography
     3. Page frame: header, main column, footer
     4. Shared components: buttons, badges, cards, forms, tables,
        flash messages, detail lists, dialog
     5. Page-specific sections: home, event detail, confirmation,
        organiser list/detail, error page
     6. Small-screen adjustments
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Brand palette: deep teal with a warm amber accent */
  --colour-brand: #0e6e6e;
  --colour-brand-dark: #0a5252;
  --colour-accent: #e8a13d;
  --colour-ink: #1d2d35;          /* main text */
  --colour-ink-soft: #5a6b74;     /* secondary text */
  --colour-line: #d9e2e4;         /* borders and dividers */
  --colour-paper: #ffffff;        /* card surfaces */
  --colour-wash: #f4f7f7;         /* page background */
  --colour-danger: #b3372f;
  --colour-danger-dark: #942d26;
  --colour-success-bg: #e7f4ec;
  --colour-success-ink: #205b38;
  --colour-error-bg: #fbeae8;
  --colour-error-ink: #8c2b24;

  --radius: 10px;
  --shadow: 0 1px 3px rgba(29, 45, 53, 0.10), 0 4px 14px rgba(29, 45, 53, 0.06);
  --page-width: 62rem;

  /* Two font stacks: a distinctive display font for the brand name and
     headings, and the fast system stack for body text. */
  --font-display: "Space Grotesk", "Segoe UI", system-ui, Arial, sans-serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}

/* --------------------------------------------------------------------------
   2. Reset and base typography
   -------------------------------------------------------------------------- */

/* Space Grotesk is bundled in static/fonts/ (SIL Open Font License). It is a
   variable font, so one small file covers every weight we use. font-display
   swap paints text immediately in the fallback and swaps the web font in when
   it has loaded, so text is never invisible. */
@font-face {
  font-family: "Space Grotesk";
  src: url("../fonts/space-grotesk.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
* {
  box-sizing: border-box;
  margin: 0;
}

/* The `hidden` attribute must win even over a component's own display rule
   (e.g. .event-card is a flex column). The home-page filter toggles [hidden]
   on cards and rows to hide the ones that do not match the search. */
[hidden] {
  display: none !important;
}

/* A very wide element (e.g. the participant table) scrolls inside its own box;
   this guard makes sure it can never make the whole page scroll sideways. */
html {
  overflow-x: hidden;
}

body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--colour-ink);
  background-color: var(--colour-wash);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-display);
  line-height: 1.25;
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.2rem; }

a {
  color: var(--colour-brand);
}

/* Hides text visually but keeps it for screen readers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   3. Page frame
   -------------------------------------------------------------------------- */
.site-header {
  /* A soft diagonal gradient plus a thin amber underline gives the bar more
     depth than a flat colour, without changing the brand palette. */
  background: linear-gradient(135deg, var(--colour-brand) 0%, var(--colour-brand-dark) 100%);
  color: #ffffff;
  border-bottom: 3px solid var(--colour-accent);
  box-shadow: var(--shadow);
}

.site-header-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 0.7rem 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  color: #ffffff;
  text-decoration: none;
}

.brand-mark {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25));
}

/* The brand name (in the display font) sits beside a small italic tagline. */
.brand-text {
  display: flex;
  align-items: baseline;   /* name and tagline share a baseline */
  flex-wrap: wrap;         /* the tagline drops below the name if space is tight */
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.4rem;
  letter-spacing: -0.01em;
  line-height: 1.1;
}

.brand-text small {
  /* Back to the body font, italic, for a clear tagline beside the name. */
  font-family: var(--font-body);
  font-weight: 400;
  font-style: italic;
  font-size: 0.74rem;
  color: rgba(255, 255, 255, 0.82);
}

.site-nav {
  display: flex;
  gap: 0.25rem;
}

.site-nav a {
  color: #ffffff;
  text-decoration: none;
  padding: 0.4rem 0.8rem;
  border-radius: 6px;
  font-weight: 500;
  transition: background-color 0.15s ease;
}

.site-nav a:hover {
  background-color: rgba(255, 255, 255, 0.16);
}

.page {
  flex: 1;                          /* pushes the footer to the bottom */
  width: 100%;
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 1.75rem 1.25rem 3rem;
}

/* Wide layout, used by both the organiser registrations page and the home page.
   Raising the shared --page-width variable on <body> widens the header bar and
   the content together (both read this variable), so they stay aligned while
   the multi-column tables get the room to show every column on desktop. */
.layout-wide {
  --page-width: 78rem;
}
/* The organiser table wraps long values (emails, event names) instead of
   scrolling, so all eight columns are visible at once on desktop. A slightly
   smaller font and tighter padding give the eight columns room, so ordinary
   words stay on one line and only an over-long email ever breaks. The id keeps
   this ahead of the default `white-space: nowrap`; on phones the mobile rules
   still stack each row into a card. */
#registration-table {
  font-size: 0.85rem;
}
#registration-table th,
#registration-table td {
  padding: 0.5rem 0.6rem;
  white-space: normal;
  overflow-wrap: break-word;
}

.site-footer {
  border-top: 1px solid var(--colour-line);
  background-color: var(--colour-paper);
  text-align: center;
  padding: 1.1rem 1.25rem;
  font-size: 0.85rem;
  color: var(--colour-ink-soft);
}

.site-footer-note {
  font-size: 0.75rem;
}

.breadcrumb {
  margin-bottom: 1rem;
  font-size: 0.9rem;
}

/* --------------------------------------------------------------------------
   4. Shared components
   -------------------------------------------------------------------------- */

/* Buttons — <a> and <button> share the same classes, so both need the
   same box model and font. */
.button {
  display: inline-block;
  padding: 0.5rem 1.05rem;
  border: 1px solid transparent;
  border-radius: 7px;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
  cursor: pointer;
}

.button-primary {
  background-color: var(--colour-brand);
  color: #ffffff;
}

.button-primary:hover {
  background-color: var(--colour-brand-dark);
}

.button-secondary {
  background-color: var(--colour-paper);
  color: var(--colour-brand);
  border-color: var(--colour-line);
}

.button-secondary:hover {
  border-color: var(--colour-brand);
}

.button-danger {
  background-color: var(--colour-danger);
  color: #ffffff;
}

.button-danger:hover {
  background-color: var(--colour-danger-dark);
}

.button-small {
  /* Size only: pair with a colour class such as .button-primary. */
  padding: 0.2rem 0.7rem;
  font-size: 0.85rem;
}

.button-block {
  display: block;
  text-align: center;
}

/* Badges — small status/price pills used on cards and in the table. */
.badge {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
}

.badge-price {
  background-color: var(--colour-accent);
  color: #3d2a08;
}

.badge-places {
  background-color: var(--colour-success-bg);
  color: var(--colour-success-ink);
}

.badge-full {
  background-color: var(--colour-error-bg);
  color: var(--colour-error-ink);
}

.badge-confirmed {
  background-color: var(--colour-success-bg);
  color: var(--colour-success-ink);
}

.badge-cancelled {
  background-color: var(--colour-error-bg);
  color: var(--colour-error-ink);
}

/* Flash messages (one-off notices after an action) */
.flash-stack {
  margin-bottom: 1.25rem;
}

.flash {
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  font-weight: 500;
  margin-bottom: 0.5rem;
}

.flash-success {
  background-color: var(--colour-success-bg);
  color: var(--colour-success-ink);
}

.flash-error {
  background-color: var(--colour-error-bg);
  color: var(--colour-error-ink);
}

/* --- Test-status page (/tests) ------------------------------------------- */
/* Pass/fail pills reuse the same green/red as the confirmed/cancelled badges. */
.badge-pass {
  background-color: var(--colour-success-bg);
  color: var(--colour-success-ink);
}
.badge-fail {
  background-color: var(--colour-error-bg);
  color: var(--colour-error-ink);
}

.test-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1.75rem;
}
.test-summary-meta {
  color: var(--colour-ink-soft);
  font-size: 0.9rem;
}

.test-group {
  margin-bottom: 2rem;
}
.test-group h2 {
  font-size: 1.1rem;
  margin-bottom: 0.6rem;
}
.test-group-count {
  color: var(--colour-ink-soft);
  font-weight: 400;
}

.test-table td {
  vertical-align: top;
  /* The base .data-table is nowrap; test names and descriptions must wrap. */
  white-space: normal;
  overflow-wrap: break-word;
}
/* The "Details" disclosure uses the native <details> element, so the
   description works with no JavaScript. */
.test-details summary {
  cursor: pointer;
  color: var(--colour-brand);
  font-weight: 600;
  font-size: 0.85rem;
}
.test-details p {
  margin: 0.4rem 0 0;
  max-width: 44rem;
  color: var(--colour-ink-soft);
  font-size: 0.9rem;
}
.test-nodesc {
  color: var(--colour-ink-soft);
}

.notice {
  padding: 1.5rem;
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  color: var(--colour-ink-soft);
}

/* Definition lists used on detail/confirmation pages */
.detail-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 1.25rem;
  row-gap: 0.35rem;
}

.detail-list dt {
  font-weight: 600;
  color: var(--colour-ink-soft);
}

/* Venue street address, shown under the venue name on the event page. */
.venue-address {
  display: block;
  font-size: 0.85rem;
  color: var(--colour-ink-soft);
}

/* Forms */
.form-card {
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.5rem;
  max-width: 40rem;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1rem;
}

.form-field {
  margin-bottom: 1rem;
}

.form-field label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--colour-line);
  border-radius: 7px;
  font: inherit;
  background-color: #ffffff;
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: 2px solid var(--colour-brand);
  outline-offset: 1px;
}

/* Set by registration-form.js when a field fails a client-side check. */
.form-field input.input-invalid,
.form-field textarea.input-invalid {
  border-color: var(--colour-danger);
}

.required {
  color: var(--colour-danger);
}

.optional {
  font-weight: 400;
  color: var(--colour-ink-soft);
  font-size: 0.85rem;
}

.field-error {
  color: var(--colour-danger);
  font-size: 0.85rem;
  margin-top: 0.25rem;
}

.form-required-note {
  color: var(--colour-ink-soft);
  font-size: 0.9rem;
  margin-bottom: 1rem;
}

.form-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

.form-page h1 {
  margin-bottom: 0.25rem;
}

.form-page-intro {
  color: var(--colour-ink-soft);
  margin-bottom: 1rem;
}

/* Tables */
.table-wrap {
  overflow-x: auto;                /* the table scrolls, not the page */
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.data-table th,
.data-table td {
  text-align: left;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--colour-line);
  white-space: nowrap;
}

.data-table tbody tr:last-child td {
  border-bottom: none;
}

.data-table thead th {
  background-color: var(--colour-wash);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--colour-ink-soft);
}

/* Cancelled registrations stand out in red across the whole row. */
.row-cancelled td {
  color: var(--colour-error-ink);
}

/* Zebra striping on the data tables (organiser list + home-page events table)
   for easier row scanning. It shades the cells (not the row) so it also stripes
   the stacked cards on phones, and sits under the red text of a cancelled row
   without clashing. */
#registration-table tbody tr:nth-child(even) td,
.event-table tbody tr:nth-child(even) td {
  background-color: var(--colour-wash);
}

/* Sortable column headers, shared by the organiser table (links, sorted on the
   server) and the home-page events table (buttons, sorted in the browser). The
   whole header is the click target, with an arrow marking the active column and
   its direction. The inner link/button carries the cell padding. */
.data-table th.th-sortable {
  padding: 0;
}
.data-table th.th-sortable > a,
.data-table th.th-sortable > button {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0.6rem 0.85rem;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.data-table th.th-sortable > a:hover,
.data-table th.th-sortable > button:hover {
  color: var(--colour-brand);
}
.data-table th.th-sort-active {
  background-color: #e9eff0;   /* a shade stronger than the header wash */
}
.sort-arrow {
  margin-left: 0.25rem;
  color: var(--colour-brand);
}
/* The organiser table uses tighter cells, so match that padding on its headers. */
#registration-table th.th-sortable {
  padding: 0;
}
#registration-table th.th-sortable > a {
  padding: 0.5rem 0.6rem;
}

/* Secondary line inside a table cell — e.g. the venue under an event name. */
.cell-sub {
  display: block;
  font-size: 0.85em;
  color: var(--colour-ink-soft);
}

/* Native <dialog> confirmation box.
   margin: auto re-centres the modal: the global "* { margin: 0 }" reset
   above removes the browser's default centring margin, so we restore it. */
dialog {
  margin: auto;
  border: none;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.5rem;
  max-width: 24rem;
}

dialog::backdrop {
  background-color: rgba(29, 45, 53, 0.5);
}

.dialog-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.25rem;
  justify-content: flex-end;
}

/* --------------------------------------------------------------------------
   5. Page-specific sections
   -------------------------------------------------------------------------- */

/* Home page hero banner */
.hero {
  margin-bottom: 2rem;
  padding: 2.25rem 2.5rem;
  border-radius: 16px;
  color: #ffffff;
  overflow: hidden;
  box-shadow: var(--shadow);
  /* Brand gradient with a warm amber glow in the top-right corner. */
  background:
    radial-gradient(120% 140% at 100% 0%, rgba(232, 161, 61, 0.25) 0%, rgba(232, 161, 61, 0) 45%),
    linear-gradient(135deg, var(--colour-brand) 0%, var(--colour-brand-dark) 100%);
}

.hero-content {
  /* Wide enough for the heading and the intro line to each sit on one line on
     desktop; both wrap naturally on narrower screens. */
  max-width: 56rem;
}

.hero-eyebrow {
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--colour-accent);
  margin-bottom: 0.5rem;
}

.hero-title {
  font-size: 2rem;
  line-height: 1.15;
  color: #ffffff;
  margin: 0;
}

.hero-intro {
  /* 56rem (matching .hero-content) so the whole sentence sits on one line on
     desktop; it still wraps naturally on narrower screens. */
  max-width: 56rem;
  color: rgba(255, 255, 255, 0.85);
  margin-top: 0.75rem;
  font-size: 1.05rem;
}

/* --- Home-page events: keyword filter + card/table view switch ------------ */
/* The controls appear only when JavaScript is available (base.html adds a "js"
   class), so a visitor without JS simply sees the card list. */
.events-controls {
  display: none;
}
.js .events-controls {
  display: block;
  margin-bottom: 1.25rem;
}

/* The wrapper already spaces the controls from the cards, so the filter-bar
   inside it does not need its own bottom margin (the organiser page does). */
.events-controls .filter-bar {
  margin-bottom: 0;
}

/* One button that swaps the two views. It shows the view you switch TO — a
   table icon while the cards are visible, a rows icon while the table is — and
   swaps its icon and label (see events-view.js) as it toggles. */
.view-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--colour-line);
  border-radius: 7px;
  background-color: var(--colour-paper);
  color: var(--colour-ink);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.view-switch:hover {
  border-color: var(--colour-brand);
  color: var(--colour-brand);
}
.view-switch svg {
  width: 1.05rem;
  height: 1.05rem;
  display: block;
}
/* Show the table icon by default; swap to the rows icon in table view. */
.view-switch .icon-rows {
  display: none;
}
.view-switch.showing-table .icon-table {
  display: none;
}
.view-switch.showing-table .icon-rows {
  display: block;
}

/* Show one view at a time. JavaScript sets the class; it defaults to cards, so
   without JS the cards show and the table stays hidden. */
#events-views.view-cards .event-table-wrap {
  display: none;
}
#events-views.view-table .event-card-grid {
  display: none;
}
.event-table td:first-child a {
  font-weight: 600;      /* the event name reads as the row heading */
}
.event-table th,
.event-table td {
  /* Unlike the wide organiser table, the short events table wraps to fit the
     page, so all columns are visible without any sideways scrolling. */
  white-space: normal;
}

.events-empty {
  padding: 1.5rem;
  text-align: center;
  color: var(--colour-ink-soft);
}

.event-card-grid {
  display: grid;
  /* A wider minimum keeps the cards roomy on the wide (78rem) layout — three
     comfortable columns rather than four cramped ones — so each card's footer
     (price + places pills + the Details button) sits on one line. */
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1.25rem;
}

.event-card {
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
}

.event-card-body {
  padding: 1.15rem 1.15rem 0.5rem;
  flex: 1;
}

.event-card-title a {
  color: var(--colour-ink);
  text-decoration: none;
}

.event-card-title a:hover {
  color: var(--colour-brand);
}

.event-card-tagline {
  color: var(--colour-ink-soft);
  font-size: 0.92rem;
  margin: 0.35rem 0 0.75rem;
}

.event-meta {
  list-style: none;
  padding: 0;
  font-size: 0.88rem;
  color: var(--colour-ink-soft);
}

.event-meta li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-bottom: 0.3rem;
}

/* The inline SVG meta icons: drawn as thin outlines in the brand colour,
   sized to sit neatly beside the text. */
.event-meta-icon {
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  fill: none;
  stroke: var(--colour-brand);
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.event-card-footer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.85rem 1.15rem;
  border-top: 1px solid var(--colour-line);
}

.event-card-footer .button {
  margin-left: auto;
}

/* Event detail page */
.event-detail-tagline {
  color: var(--colour-ink-soft);
  margin: 0.35rem 0 1.25rem;
  font-size: 1.05rem;
}

.event-detail-layout {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 1.5rem;
  align-items: start;
}

.event-detail-main h2 {
  margin-bottom: 0.5rem;
}

.event-detail-aside {
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem;
  display: grid;
  gap: 1.1rem;
}

/* Availability widget (bar is animated by event-availability.js) */
.availability-heading {
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--colour-ink-soft);
  margin-bottom: 0.4rem;
}

.availability-bar {
  height: 0.7rem;
  border-radius: 999px;
  background-color: var(--colour-wash);
  border: 1px solid var(--colour-line);
  overflow: hidden;
}

.availability-bar-fill {
  height: 100%;
  background-color: var(--colour-accent);
  border-radius: inherit;
  transition: width 0.6s ease;     /* makes the JS update glide */
}

.availability-text {
  font-size: 0.9rem;
  color: var(--colour-ink-soft);
  margin-top: 0.35rem;
}

.fully-booked-note {
  background-color: var(--colour-error-bg);
  color: var(--colour-error-ink);
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  font-weight: 500;
}

/* Confirmation page */
.confirmation {
  display: flex;
  justify-content: center;
}

.confirmation-card {
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2rem;
  max-width: 34rem;
  text-align: center;
  display: grid;
  gap: 1rem;
  justify-items: center;
}

.confirmation-icon {
  width: 3.2rem;
  height: 3.2rem;
  border-radius: 50%;
  background-color: var(--colour-success-bg);
  color: var(--colour-success-ink);
  font-size: 1.8rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.confirmation-card .detail-list {
  text-align: left;
}

.confirmation-note {
  font-size: 0.9rem;
  color: var(--colour-ink-soft);
}

/* Organiser pages */
.admin-header {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

/* Organiser stat panel — a clean row of headline figures below the page
   title. Each figure is a small card; the row wraps to fewer-per-line on
   narrow screens. */
.stats-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 0 0 1.5rem;
}
.stat {
  display: flex;
  flex-direction: column-reverse;   /* big value on top, label beneath */
  gap: 0.1rem;
  flex: 1 1 7rem;
  padding: 0.7rem 0.9rem;
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
}
.stat-value {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1.05;
  color: var(--colour-brand);
}
.stat-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--colour-ink-soft);
}
.stat-confirmed .stat-value {
  color: var(--colour-success-ink);
}
.stat-cancelled .stat-value {
  color: var(--colour-error-ink);
}

/* Filter bar — the same event dropdown + text search on both the home page and
   the organiser page. Each field stacks its label above the control; the search
   field grows to fill the rest of the row. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
  margin-bottom: 1rem;
}
.filter-field {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}
.filter-field label {
  font-weight: 600;
  font-size: 0.9rem;
}
.filter-field select,
.filter-field input {
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--colour-line);
  border-radius: 7px;
  font: inherit;
  background-color: #ffffff;
}
.filter-field-grow {
  flex: 1 1 16rem;
}
.filter-field-grow input {
  width: 100%;
}

/* With JavaScript on, changing the event dropdown filters instantly (the home
   page filters in place; the organiser page submits the form), so the manual
   "Filter" button is redundant. Without JS it stays and the form works as a
   normal GET submit. */
.js .filter-submit {
  display: none;
}

.search-status {
  margin-top: 0.5rem;
  font-size: 0.88rem;
  color: var(--colour-ink-soft);
}

.detail-panels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  margin-bottom: 1.25rem;
}

.detail-panel {
  background-color: var(--colour-paper);
  border: 1px solid var(--colour-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem;
}

.detail-panel h2 {
  margin-bottom: 0.6rem;
}

.detail-panel h2 + .detail-list {
  margin-bottom: 1rem;
}

.detail-actions {
  display: flex;
  gap: 0.75rem;
}

/* Error page */
.error-page {
  text-align: center;
  padding: 3rem 0;
  display: grid;
  gap: 1rem;
  justify-items: center;
}

/* Venue links: the venue name on the home cards/table and the event page.
   Styled as a subtle link (dotted underline) so it reads as clickable without
   competing with the event-name links around it. */
.venue-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted var(--colour-line);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.venue-link:hover,
.venue-link:focus-visible {
  color: var(--colour-brand);
  border-bottom-color: currentColor;
}

/* Venue detail page: hero image, header, facilities. The two-column body
   reuses .event-detail-layout (and its phone stacking), so only the pieces
   unique to venues are defined here. */
.venue-hero {
  height: clamp(150px, 28vw, 260px);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 1.5rem;
  box-shadow: var(--shadow);
}
.venue-hero-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Shown when no real photo has been added to static/venues/ yet: a
   self-contained banner whose colour is set per venue via the --v-hue
   custom property (passed inline by the template). */
.venue-hero-banner {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.92);
  background:
    radial-gradient(circle at 78% 22%,
      hsl(calc(var(--v-hue) + 32) 60% 55% / 0.55), transparent 55%),
    linear-gradient(135deg,
      hsl(var(--v-hue) 48% 40%), hsl(calc(var(--v-hue) + 28) 52% 26%));
}
.venue-hero-banner svg {
  width: 76px;
  height: 76px;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25));
}
.venue-detail-header {
  margin-bottom: 1.5rem;
}
.venue-detail-address {
  color: var(--colour-ink-soft);
  margin-top: 0.25rem;
}
.venue-events-heading {
  margin-top: 2rem;
}

/* A leading icon inside a button, e.g. the "Get directions" map pin. */
.button-icon {
  width: 1em;
  height: 1em;
  margin-right: 0.5rem;
  vertical-align: -0.15em;
}

/* Facilities checklist in the venue aside. */
.facility-heading {
  margin-top: 1.5rem;
  margin-bottom: 0.75rem;
}
.facility-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}
.facility-list li {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  color: var(--colour-ink);
}
.facility-list li svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 0.15rem;
  color: var(--colour-brand);
}

/* --------------------------------------------------------------------------
   6. Small-screen adjustments
   -------------------------------------------------------------------------- */
@media (max-width: 44rem) {
  .event-detail-layout,
  .detail-panels,
  .form-row {
    grid-template-columns: 1fr;    /* stack the two-column layouts */
  }

  /* On phones the filter bar stacks: each field fills the width, so a long
     event name in the <select> can never push the page wider than the screen. */
  .filter-bar {
    flex-direction: column;
    align-items: stretch;
  }
  .filter-field,
  .filter-field-grow {
    flex: none;
    width: 100%;
  }
  .filter-field select,
  .filter-field input {
    width: 100%;
  }
  /* Keep the view toggle compact rather than stretching to full width. */
  .view-switch {
    align-self: flex-start;
  }

  /* Organiser table: an 8-column table cannot fit a phone, and letting it
     scroll sideways makes the whole page zoom out (the layout viewport grows to
     the table's width). Instead each row becomes a stacked "Label: value" card,
     so every field is readable with no sideways scrolling. */
  .table-wrap {
    overflow-x: visible;
    border: none;
    background: none;
    box-shadow: none;
  }
  .data-table thead {
    /* Each cell shows its own label (data-label), so the header row is hidden —
       but kept in the DOM, positioned off-screen, for screen readers. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }
  .data-table,
  .data-table tbody,
  .data-table tr,
  .data-table td {
    display: block;
  }
  .data-table tr {
    margin-bottom: 0.85rem;
    padding: 0.35rem 0.9rem;
    border: 1px solid var(--colour-line);
    border-radius: var(--radius);
    background-color: var(--colour-paper);
    box-shadow: var(--shadow);
  }
  .data-table td {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--colour-line);
    white-space: normal;      /* let long values wrap instead of widening */
    text-align: right;
  }
  .data-table tr td:last-child {
    border-bottom: none;
  }
  .data-table td::before {
    /* The column name, pulled from the cell's data-label attribute. */
    content: attr(data-label);
    font-weight: 600;
    color: var(--colour-ink-soft);
    text-align: left;
  }

  /* Header: let the nav drop onto its own full-width row below the brand,
     with the two links sharing the width so they are easy to tap. */
  .site-nav {
    width: 100%;
  }
  .site-nav a {
    flex: 1;
    text-align: center;
  }

  /* Hero: a more compact banner on phones. */
  .hero {
    padding: 1.75rem 1.5rem;
  }
  .hero-title {
    font-size: 1.55rem;
  }
}
