﻿html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--bs-body-bg), 0 0 0 0.25rem #258cfb;
}

[data-bs-theme="dark"] .btn:focus,
[data-bs-theme="dark"] .btn:active:focus,
[data-bs-theme="dark"] .btn-link.nav-link:focus,
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--bs-body-bg), 0 0 0 0.25rem #8b5cf6;
}

/* Sticky footer, flexbox version. The template's original approach
   (html{position:relative} + body{margin-bottom:60px} + an absolutely
   positioned .footer) hard-codes a 60px single-line footer; the Important
   Notice is several lines tall, so the footer now sits in normal flow and is
   pushed down by margin-top:auto instead. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* The content wrapper takes the slack so the footer stays at the bottom on
   short pages without overlapping anything on tall ones. */
body > .container {
  flex: 1 0 auto;
}

/* ── Important legal notice (site footer) ──────────────────────────────
   Deliberately loud: it is a disclaimer that has to be noticed, so it gets a
   heavy left rule, a tinted ground and full-size body text rather than the
   muted small print around it. Amber reads as "read me" in both themes
   without the alarm of red. */
.important-notice {
  border: 1px solid var(--bs-warning-border-subtle);
  border-left: 6px solid #E8A33D;
  background-color: var(--bs-warning-bg-subtle);
  color: var(--bs-body-color);
  border-radius: .5rem;
  padding: 1rem 1.25rem;
  font-size: 1rem;
  line-height: 1.6;
  /* The footer used to be white-space:nowrap; make sure nothing here can
     re-introduce that and push the page sideways. */
  white-space: normal;
  overflow-wrap: anywhere;
}

.important-notice .important-notice-icon {
  font-size: 1.4rem;
  color: #B57407;
  line-height: 1;
}

/* On near-black, the subtle amber ground almost vanishes - lift it and add a
   ring so the block still reads as a distinct panel. */
[data-bs-theme="dark"] .important-notice {
  border-color: rgba(240, 182, 92, .5);
  border-left-color: #F0B65C;
  background-color: rgba(240, 182, 92, .12);
  color: #FBE7C6;
  box-shadow: 0 0 0 1px rgba(240, 182, 92, .12), 0 .5rem 1.5rem rgba(0, 0, 0, .5);
}

[data-bs-theme="dark"] .important-notice .important-notice-icon {
  color: #F0B65C;
}

/* ── World Championship announcement (site footer) ─────────────────────
   The cheerful counterpart to .important-notice above, and deliberately the
   opposite of it in every way: warm gradient instead of a warning ground, a
   sparkle instead of a hazard triangle, teal-to-violet instead of amber.
   This is the one piece of footer content meant to be read for pleasure.

   Both themes are defined explicitly rather than leaning on Bootstrap's
   subtle tokens - those are tuned for status colours, and a celebration that
   renders as a muted alert is not a celebration. */
.worlds-banner {
  background: linear-gradient(135deg, #E8F7FB 0%, #F3ECFF 55%, #FFF3E0 100%);
  border: 1px solid rgba(124, 58, 237, .22);
  border-left: 6px solid #0FA3B1;
  border-radius: .75rem;
  padding: 1.25rem 1.5rem;
  color: #1B365D;
  line-height: 1.6;
  white-space: normal;
  overflow-wrap: anywhere;
  box-shadow: 0 .5rem 1.25rem rgba(27, 54, 93, .10);
}

.worlds-banner p {
  margin-bottom: .6rem;
  max-width: 78ch;
}

.worlds-banner-title {
  font-size: 1.2rem;
  font-weight: 700;
  color: #7C3AED;
  margin-bottom: .35rem;
  letter-spacing: -.01em;
}

.worlds-banner-dates {
  font-weight: 600;
  color: #0B7C88;
}

.worlds-banner-icon {
  font-size: 1.6rem;
  line-height: 1;
  color: #E8A33D;
}

/* On the near-black ground the pastel gradient would glare, so the dark
   variant keeps the same three hues at low alpha and brightens the text
   instead - bright and happy, not a torch. */
[data-bs-theme="dark"] .worlds-banner {
  background: linear-gradient(135deg, rgba(15, 163, 177, .16) 0%, rgba(124, 58, 237, .18) 55%, rgba(232, 163, 61, .14) 100%);
  border-color: rgba(167, 139, 250, .32);
  border-left-color: #22D3EE;
  color: #E6ECF5;
  box-shadow: 0 0 0 1px rgba(167, 139, 250, .10), 0 .5rem 1.5rem rgba(0, 0, 0, .45);
}

[data-bs-theme="dark"] .worlds-banner-title {
  color: #C4B5FD;
}

[data-bs-theme="dark"] .worlds-banner-dates {
  color: #67E8F9;
}

[data-bs-theme="dark"] .worlds-banner-icon {
  color: #FBBF24;
}

/* ── Landing page ──────────────────────────────────────────────────────
   The hero and the program-tile bands are brand navy, which sits too close
   to the dark theme's near-black ground. Give them a defined edge and lift
   them off the page rather than changing the brand colour. */
.landing-hero {
  box-shadow: 0 .5rem 1.5rem rgba(16, 27, 46, .18);
}

[data-bs-theme="dark"] .landing-hero {
  border: 1px solid rgba(167, 139, 250, .45);
  box-shadow: 0 0 0 1px rgba(167, 139, 250, .18), 0 1rem 2.5rem rgba(0, 0, 0, .65);
}

/* Same problem one level down: a navy band on a dark card had no edge. */
[data-bs-theme="dark"] .program-band {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14);
}

/* ── Act-as impersonation banner ───────────────────────────────────────
   Intentionally the loudest thing on the page, and identical in both themes.
   While this bar is showing, a staff member has full control of someone
   else's account - it must never blend into a theme or be mistaken for a
   routine notification.

   Loud in COLOUR, not in height. Since 2026-09-09 the bar is a single row
   (see Views/Shared/Components/ActAsBanner/Default.cshtml for why) - the
   red and the shadow carry the warning; the bar no longer needs three lines
   of a phone's screen to do it. */
.act-as-banner {
  background-color: #B3261E;
  color: #fff;
  border-bottom: 3px solid #7F1710;
  box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .3);
  z-index: 1080;
}

/* The connective words - "Acting as", "signed in as", "since" - step back so
   the two addresses read as the content of the row. Was `.act-as-banner
   .small` when the bar had a second line; the second line is gone and this
   is what replaced it. */
.act-as-banner-muted {
  color: rgba(255, 255, 255, .85);
}

/* ── Manage-sessions button ───────────────────────────────────────────
   Deliberately loud, and deliberately NOT one of Bootstrap's contextual
   colours. Every semantic slot on this screen is already spoken for -
   primary is Save, danger is Delete, secondary is everything else - so a
   button that needs to be findable at a glance has nowhere in that palette
   to go without reading as one of those actions. Purple carries no meaning
   here, which is exactly why it can be handed one.

   Solid fill with white text rather than an outline: it keeps its contrast
   in both themes, so no dark-mode variant is needed. */
.btn-sessions {
  background-color: #7C3AED;
  border-color: #7C3AED;
  color: #fff;
}

.btn-sessions:hover,
.btn-sessions:active {
  background-color: #6D28D9;
  border-color: #6D28D9;
  color: #fff;
}

.btn-sessions:focus-visible {
  background-color: #6D28D9;
  border-color: #6D28D9;
  color: #fff;
  box-shadow: 0 0 0 .25rem rgba(124, 58, 237, .5);
}

/* ── Brand logo ────────────────────────────────────────────────────────
   The logo is single-colour navy on transparency (see recf-logo.png), which
   is unreadable against the dark navbar - so in dark mode it's flattened to
   white via a filter rather than shipping a second asset to keep in sync. */
.brand-logo {
  width: 40px;
  height: 40px;
}

[data-bs-theme="dark"] .brand-logo {
  filter: brightness(0) invert(1);
}

/* ── Events filter bar ─────────────────────────────────────────────────
   Brand-tied but deliberately NOT the hero's solid navy. Two reasons: this
   strip sits directly above event cards that already carry navy banner
   bands, so a third navy block makes the page top-heavy and competes with
   the content it filters; and small labels on saturated navy sit right at
   the edge of comfortable reading. So: a navy-TINTED ground with a solid
   brand rule across the top - the hero's colour family, at utility weight,
   with text left in theme-native tones that stay comfortably legible. */
.filter-bar {
  /* A real blue tint rather than a blue-leaning grey, with a soft vertical
     fall so the panel has some depth instead of reading as a flat slab.
     Same family as the navy brand rule above it. */
  background: linear-gradient(180deg, #F3F8FF 0%, #E2ECFA 100%);
  border: 1px solid rgba(27, 54, 93, .16);
  border-top: 3px solid #1B365D;
}

/* Every label is laid out identically - flex row, fixed line-height - so the
   one that carries an info icon computes the same height as the plain three
   and the controls below them stay on a single line. */
.filter-bar .form-label {
  display: flex;
  align-items: center;
  gap: .35rem;
  line-height: 1.5;
  min-height: 1.5rem;
  color: #2E4666;
  /* Small and semibold HERE rather than as utility classes on each label:
     the <app-multiselect> fields render their own <label class="form-label">
     and this is what keeps them level with the hand-written ones. */
  font-size: .875rem;
  font-weight: 600;
  margin-bottom: .25rem;
}

/* The hero's amber, darkened for contrast against a light ground. */
.filter-bar .bi-info-circle {
  color: #B57407;
}

.filter-bar hr,
.filter-bar .border-top {
  border-color: rgba(27, 54, 93, .15) !important;
  opacity: 1;
}

/* The actions (Show / Clear Filters) are a ruled-off row of their own on a
   phone, where Show is the one big button, and from xl they share the second
   row with the date pair - so the rule goes there, where it would only draw a
   line under half a row. Bootstrap's border utilities are not responsive. */
.filter-bar .filter-actions {
  border-top: 1px solid rgba(27, 54, 93, .15);
  padding-top: 1rem;
}

[data-bs-theme="dark"] .filter-bar .filter-actions {
  border-top-color: rgba(255, 255, 255, .12);
}

@media (min-width: 1200px) {
  .filter-bar .filter-actions {
    border-top: 0;
    padding-top: 0;
  }
}

/* Tournament / League / Workshops pill on an event card. Outline, theme
   tokens only: every FILLED badge on the card carries status meaning (open,
   full, ended, restricted), so a category pill has to look like none of
   them. Bootstrap's --bs-* tokens flip with data-bs-theme, so no dark rule. */
.event-type-pill {
  color: var(--bs-secondary-color);
  background-color: transparent;
  border: 1px solid var(--bs-border-color);
  font-weight: 600;
}

/* ── Events listing toolbar: phone filter toggle, chips, segments ────────
   Theme tokens throughout, so one set of rules serves both themes; the only
   dark override is the active/primary tint, which the app paints violet in
   dark (see .btn-primary above) where Bootstrap's own --bs-primary stays
   blue. */
.filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  min-height: 44px;
  padding: .375rem .875rem;
  border: 1px solid var(--bs-border-color);
  border-radius: .375rem;
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
  font-weight: 500;
}

.filter-toggle .badge {
  background: var(--bs-primary);
  color: #fff;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  min-height: 32px;
  padding: 0 .625rem 0 .75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 50rem;
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
  font-size: .875rem;
  text-decoration: none;
  white-space: nowrap;
}

.filter-chip:hover {
  color: var(--bs-body-color);
  border-color: var(--bs-secondary-color);
}

.filter-chip .bi {
  font-size: .75em;
  color: var(--bs-secondary-color);
}

.event-views {
  display: flex;
  border: 1px solid var(--bs-border-color);
  border-radius: .375rem;
  overflow: hidden;
  background: var(--bs-body-bg);
}

.event-views a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .375rem;
  flex: 1 1 0;
  min-height: 44px;
  padding: .5rem .875rem;
  color: var(--bs-secondary-color);
  font-size: .875rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.event-views a + a {
  border-left: 1px solid var(--bs-border-color);
}

.event-views a:hover {
  color: var(--bs-body-color);
  background: var(--bs-tertiary-bg);
}

.event-views a.active {
  background: var(--bs-primary);
  color: #fff;
}

.event-views .count {
  font-size: .75rem;
  font-weight: 600;
  padding: .125rem .4rem;
  border-radius: 50rem;
  background: rgba(0, 0, 0, .1);
}

.event-views a.active .count {
  background: rgba(255, 255, 255, .22);
}

[data-bs-theme="dark"] .event-views a.active,
[data-bs-theme="dark"] .filter-toggle .badge {
  background: #7c3aed;
}

[data-bs-theme="dark"] .event-views .count {
  background: rgba(255, 255, 255, .12);
}

@media (max-width: 767.98px) {
  /* Full width under the chips, three equal thumb-sized targets. Tighter
     padding so "Current 4 · Upcoming 12 · Past 60" still fits a 360px
     phone without the third segment being clipped by the rounded frame. */
  .event-views {
    flex: 1 1 100%;
  }

  .event-views a {
    padding: .5rem .5rem;
    font-size: .8125rem;
  }
}

/* ── Public event card (_EventCard.cshtml) ───────────────────────────────
   One partial, two shapes. From md up: a vertical card with a 120px program
   banner (was 160), the date first with the status badge beside it, then
   title, code, place, the one registration date that matters, the pills,
   and teams + price on a line above the button. Below md the same markup
   turns sideways - see the media block. */
.ec-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  flex-shrink: 0;
}

.ec-banner img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  padding: .75rem;
}

.ec-body {
  display: flex;
  flex-direction: column;
  gap: .375rem;
}

.ec-date {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .25rem .5rem;
  font-weight: 600;
}

.ec-range {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .375rem;
}

.ec-range .bi {
  color: var(--bs-secondary-color);
}

.ec-title {
  margin: 0;
}

/* The card, not the text, is the link (stretched-link on the anchor), so
   the title reads as a heading and only signals on hover. */
.ec-title a {
  color: inherit;
  text-decoration: none;
}

.ec-title a:hover {
  text-decoration: underline;
}

.ec-meta {
  display: flex;
  flex-direction: column;
  gap: .125rem;
}

.ec-where .bi {
  color: var(--bs-secondary-color);
  margin-right: .125rem;
}

.ec-reg {
  color: var(--bs-secondary-color);
  font-size: .875em;
}

/* display: contents from md up, so .ec-tags and .ec-foot are direct flex
   items of the body and .ec-foot's margin-top:auto can pin the teams/price
   line (and the button after it) to the bottom of every card in a row. */
.ec-bottom {
  display: contents;
}

.ec-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem;
  margin-top: .125rem;
}

.ec-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-top: auto;
  padding-top: .5rem;
}

.ec-teams {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .375rem;
  color: var(--bs-secondary-color);
}

@media (max-width: 767.98px) {
  /* Sideways: thumbnail left, everything else in a column beside it. The
     code, team count and button are the desktop-only lines; the status
     badge switches to its short spelling (the two spans in the partial). */
  .event-card .card {
    flex-direction: row;
    align-items: stretch;
  }

  .ec-banner {
    width: 80px;
    height: 80px;
    margin: .625rem 0 .625rem .625rem;
    border-radius: .25rem;
  }

  .ec-banner img {
    padding: .375rem;
  }

  .event-card .card-body {
    padding: .625rem;
    gap: .2rem;
    min-width: 0;
  }

  .ec-date {
    font-size: .95em;
  }

  .ec-code,
  .ec-teams,
  .ec-button {
    display: none !important;
  }

  .ec-meta {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: .375rem;
    font-size: .875em;
  }

  .ec-reg {
    font-size: 1em;
  }

  .ec-reg::before {
    content: "·";
    margin-right: .375rem;
  }

  .ec-bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .25rem .5rem;
  }

  .ec-tags {
    margin-top: 0;
  }

  .ec-foot {
    margin-top: 0;
    padding-top: 0;
    margin-left: auto;
  }
}

/* ── Motion on the events listing ────────────────────────────────────────
   Four small things, every one off under prefers-reduced-motion:
   - cards rise in with a 40ms stagger (the view sets --ec-i; capped at 11 so
     a page of twelve finishes inside half a second);
   - chips pop in the same way and shrink out when clicked (the view's script
     delays the navigation by the length of the shrink);
   - a card lifts on hover the way the dashboard tiles do;
   - the phone panel's Show button ticks when its count changes.
   Fill mode is `backwards`, not `both`: `both` would keep the final keyframe
   applied after the animation, and a filled transform:none silently beats
   the hover lift and the chip's shrink. */
@keyframes ec-rise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

.event-card {
  animation: ec-rise .32s ease-out backwards;
  /* Capped at five steps: a page of twelve is fully painted inside 250ms.
     The first cut capped at eleven, which held the last card invisible for
     nearly half a second on the site's most-visited page. */
  animation-delay: calc(min(var(--ec-i, 0), 5) * 40ms);
}

/* THREE classes, for the same reason as .card.filter-bar.collapsing below:
   `[data-bs-theme="dark"] .card` sets its own transition two classes deep
   and later in this file, so a two-class rule here lost in dark mode and
   the hover lift snapped. */
.event-card .card.ec-card {
  transition: transform .15s ease-out, box-shadow .15s ease-out, border-color .15s ease-out;
}

/* The stretched title link overlays the whole card; lift the button above
   it so its own hover and focus states still work. */
.ec-button {
  position: relative;
  z-index: 2;
}

.event-card .card:hover {
  transform: translateY(-2px);
  box-shadow: 0 .5rem 1rem rgba(16, 27, 46, .12);
}

[data-bs-theme="dark"] .event-card .card:hover {
  border-color: #8b5cf6;
  box-shadow: 0 0 0 1px rgba(139, 92, 246, .25), 0 .5rem 1rem rgba(139, 92, 246, .15);
}

@keyframes chip-pop {
  from { opacity: 0; transform: scale(.85); }
  to { opacity: 1; transform: none; }
}

.filter-chip {
  animation: chip-pop .2s ease-out backwards;
  animation-delay: calc(min(var(--chip-i, 0), 8) * 40ms);
  transition: transform .14s ease-in, opacity .14s ease-in, border-color .12s ease-out;
}

.filter-chip.is-leaving {
  transform: scale(.8);
  opacity: 0;
}

@keyframes count-tick {
  0% { transform: scale(1); }
  40% { transform: scale(1.04); }
  100% { transform: scale(1); }
}

#applyFilters.is-updated {
  animation: count-tick .25s ease-out;
}

.event-views a {
  transition: background-color .12s ease-out, color .12s ease-out;
}

/* THE PHONE FILTER PANEL SNAPPED OPEN, and this is why: Bootstrap animates a
   collapse with `.collapsing { transition: height .35s }`, one class deep,
   and this file's `[data-bs-theme="dark"] .card { transition: border-color,
   box-shadow }` is two deep - so on the filter bar, which is a .card, the
   theme rule won and height had no transition at all. Three classes here
   beat both, in either theme. */
.card.filter-bar.collapsing {
  transition: height .35s ease;
}

/* The contents ride the height, and THE TWO DIRECTIONS MIRROR EACH OTHER:
   the same 350ms, the same easing as the height, one keyframe set played
   forwards on open and its reverse on close. The first version faded out in
   200ms with a transition while the panel took 350ms to shrink, and the
   mismatch read as cheap. Animations rather than transitions because nothing
   transitions out of display:none. The direction is read off the toggle's
   aria-expanded, which Bootstrap flips at the START of each move; :has() is
   the only way to reach it from a sibling, and where :has() is unsupported
   the panel still slides, it just does not fade. */
@keyframes filter-bar-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

@keyframes filter-bar-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(-6px); }
}

.events-filters:has(.filter-toggle[aria-expanded="true"]) .filter-bar.collapsing .card-body {
  animation: filter-bar-in .35s ease both;
}

.events-filters:has(.filter-toggle[aria-expanded="false"]) .filter-bar.collapsing .card-body {
  animation: filter-bar-out .35s ease both;
}

/* The toggle says which way it is: the chevron turns over and the button
   takes the primary tint while the panel is open. */
.filter-toggle {
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}

.filter-toggle-chevron {
  font-size: .75em;
  margin-left: .125rem;
  transition: transform .35s ease;
}

.filter-toggle[aria-expanded="true"] .filter-toggle-chevron {
  transform: rotate(180deg);
}

.filter-toggle[aria-expanded="true"] {
  border-color: var(--bs-primary);
}

[data-bs-theme="dark"] .filter-toggle[aria-expanded="true"] {
  border-color: #7c3aed;
}

@media (prefers-reduced-motion: reduce) {
  .event-card,
  .filter-chip,
  #applyFilters.is-updated,
  .events-filters:has(.filter-toggle[aria-expanded="true"]) .filter-bar.collapsing .card-body,
  .events-filters:has(.filter-toggle[aria-expanded="false"]) .filter-bar.collapsing .card-body {
    animation: none;
  }

  .event-card .card.ec-card,
  .filter-chip,
  .event-views a,
  .card.filter-bar.collapsing,
  .filter-toggle,
  .filter-toggle-chevron {
    transition: none;
  }

  .event-card .card:hover {
    transform: none;
  }

  .filter-chip.is-leaving {
    transform: none;
    opacity: 1;
  }
}

/* .card.filter-bar, not just .filter-bar: the generic
   [data-bs-theme="dark"] .card rule further down this file has equal
   specificity and would otherwise win on source order and repaint the top
   rule violet. */
[data-bs-theme="dark"] .card.filter-bar {
  /* Navy-leaning lift over the #212529 body ground rather than a colour of
     its own, so it recedes behind the cards instead of glowing.
     background-image must be cleared explicitly - the light rule sets a
     gradient via the `background` shorthand, and overriding only
     background-color would leave that gradient painted on top. */
  background-color: #232936;
  background-image: none;
  border-color: rgba(167, 139, 250, .28);
  border-top-color: #7C6BD6;
  box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .45);
}

[data-bs-theme="dark"] .filter-bar .form-label {
  color: #B9C7DC;
}

[data-bs-theme="dark"] .filter-bar .bi-info-circle {
  color: #E8A33D;
}

[data-bs-theme="dark"] .filter-bar hr,
[data-bs-theme="dark"] .filter-bar .border-top {
  border-color: rgba(255, 255, 255, .12) !important;
}

/* "Learn More and Get Involved" links. A plain outline button was too quiet
   for what is effectively the page's secondary navigation, so these get a
   filled ground, a sized icon and a hover lift - noticeable without
   competing with the amber primary CTA in the hero. */
.involve-btn {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: .75rem 1.15rem;
  border-radius: .6rem;
  border: 1px solid var(--bs-border-color);
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 1px 2px rgba(16, 27, 46, .06), 0 4px 12px rgba(16, 27, 46, .08);
  transition: transform .12s ease-out, box-shadow .12s ease-out, border-color .12s ease-out;
}

.involve-btn:hover,
.involve-btn:focus-visible {
  color: var(--bs-body-color);
  border-color: #1B365D;
  transform: translateY(-2px);
  box-shadow: 0 2px 4px rgba(16, 27, 46, .1), 0 10px 22px rgba(16, 27, 46, .16);
}

/* The icon carries the colour, so the buttons stay legible as a set while
   still being individually recognisable. */
.involve-btn .bi {
  font-size: 1.25rem;
  color: #1B365D;
  flex: none;
}

[data-bs-theme="dark"] .involve-btn {
  border-color: rgba(167, 139, 250, .4);
  background-color: rgba(167, 139, 250, .08);
  box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .45);
}

[data-bs-theme="dark"] .involve-btn:hover,
[data-bs-theme="dark"] .involve-btn:focus-visible {
  border-color: #a78bfa;
  box-shadow: 0 0 0 1px rgba(167, 139, 250, .35), 0 .75rem 1.5rem rgba(0, 0, 0, .6);
}

[data-bs-theme="dark"] .involve-btn .bi {
  color: #a78bfa;
}

@media (prefers-reduced-motion: reduce) {
  .involve-btn,
  .involve-btn:hover,
  .involve-btn:focus-visible {
    transform: none;
    transition: none;
  }
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

/* Dark mode toggle icon - shows the icon for the mode a click switches TO
   (moon in light mode invites going dark, sun in dark mode invites going
   back to light), not the current mode. */
.theme-icon-sun {
  display: none;
}

[data-bs-theme="dark"] .theme-icon-sun {
  display: inline-block;
}

[data-bs-theme="dark"] .theme-icon-moon {
  display: none;
}

/* Dark mode accent - violet/blue highlights instead of flat gray-on-gray.
   Links get a lighter, more saturated violet than the button/border accent
   so they still read as clickable against dark backgrounds. .btn-primary
   and .nav-pills active state are re-themed in _Layout.cshtml.css, right
   next to the light-mode rules they override. */
[data-bs-theme="dark"] {
  --bs-link-color: #a78bfa;
  --bs-link-color-rgb: 167, 139, 250;
  --bs-link-hover-color: #c4b5fd;
  --bs-link-hover-color-rgb: 196, 181, 253;
}

/* Cards default to a flat, barely-there border in Bootstrap's stock dark
   theme - give them a violet tint plus a hover glow (every dashboard card
   is a link) so the grid doesn't look dull. */
[data-bs-theme="dark"] .card {
  border-color: rgba(139, 92, 246, 0.35);
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

[data-bs-theme="dark"] a:hover > .card {
  border-color: #8b5cf6;
  box-shadow: 0 0 0 1px rgba(139, 92, 246, 0.25), 0 0.5rem 1rem rgba(139, 92, 246, 0.15);
}

/* Buttons/navbar-brand/nav-pills: overridden here (not in
   _Layout.cshtml.css) because MVC's CSS isolation only scopes that file's
   rules to markup written directly in _Layout.cshtml - a .btn-primary
   rendered by any other view never picks it up. Extra attribute-selector
   specificity here beats Bootstrap's own single-class .btn-primary rule
   regardless of stylesheet load order. */
[data-bs-theme="dark"] .navbar-brand {
  color: #a78bfa;
}

[data-bs-theme="dark"] .btn-primary {
  background-color: #7c3aed;
  border-color: #6d28d9;
}

[data-bs-theme="dark"] .btn-primary:hover,
[data-bs-theme="dark"] .btn-primary:active {
  background-color: #6d28d9;
  border-color: #5b21b6;
}

[data-bs-theme="dark"] .nav-pills .nav-link.active,
[data-bs-theme="dark"] .nav-pills .show > .nav-link {
  background-color: #7c3aed;
  border-color: #6d28d9;
}
/* ── Contextual table rows in dark mode ──────────────────────────────────
   Bootstrap 5.3 made almost everything theme-aware and then left the table
   variants behind: .table-success/-warning/-info/-danger are defined ONCE,
   as the light-mode pastels (#d1e7dd, #fff3cd, ...), with a near-black
   --bs-table-color to sit on them. Under data-bs-theme="dark" - which is
   this app's DEFAULT, not an opt-in - a report that marks paid rows green
   turns into bands of bright mint across a #212529 page. That is what made
   GrantReports/AllocationDetail, ByOrg and TeamFeeGrantList look lit up.

   Fixed here rather than in the views because it is not those pages' bug:
   the same classes carry status on OrgReports, PaymentDiagnostic and
   UserAccounts, and swapping them for something bespoke per page would
   scatter the convention instead of repairing it.

   Each variant is re-tinted as a low-alpha wash of its own hue over
   whatever the row already sits on, so striping and hover still read
   underneath, and the text colour is handed back to the body colour rather
   than the light-mode near-black that would otherwise be unreadable. */
[data-bs-theme="dark"] .table-success,
[data-bs-theme="dark"] .table-warning,
[data-bs-theme="dark"] .table-info,
[data-bs-theme="dark"] .table-danger,
[data-bs-theme="dark"] .table-primary,
[data-bs-theme="dark"] .table-secondary {
  --bs-table-color: var(--bs-body-color);
  --bs-table-striped-color: var(--bs-body-color);
  --bs-table-active-color: var(--bs-body-color);
  --bs-table-hover-color: var(--bs-body-color);
  color: var(--bs-body-color);
}

[data-bs-theme="dark"] .table-success {
  --bs-table-bg: rgba(25, 135, 84, .16);
  --bs-table-border-color: rgba(25, 135, 84, .32);
  --bs-table-striped-bg: rgba(25, 135, 84, .22);
  --bs-table-active-bg: rgba(25, 135, 84, .28);
  --bs-table-hover-bg: rgba(25, 135, 84, .24);
}

[data-bs-theme="dark"] .table-warning {
  --bs-table-bg: rgba(255, 193, 7, .14);
  --bs-table-border-color: rgba(255, 193, 7, .30);
  --bs-table-striped-bg: rgba(255, 193, 7, .20);
  --bs-table-active-bg: rgba(255, 193, 7, .26);
  --bs-table-hover-bg: rgba(255, 193, 7, .22);
}

[data-bs-theme="dark"] .table-info {
  --bs-table-bg: rgba(13, 202, 240, .14);
  --bs-table-border-color: rgba(13, 202, 240, .30);
  --bs-table-striped-bg: rgba(13, 202, 240, .20);
  --bs-table-active-bg: rgba(13, 202, 240, .26);
  --bs-table-hover-bg: rgba(13, 202, 240, .22);
}

[data-bs-theme="dark"] .table-danger {
  --bs-table-bg: rgba(220, 53, 69, .16);
  --bs-table-border-color: rgba(220, 53, 69, .32);
  --bs-table-striped-bg: rgba(220, 53, 69, .22);
  --bs-table-active-bg: rgba(220, 53, 69, .28);
  --bs-table-hover-bg: rgba(220, 53, 69, .24);
}

[data-bs-theme="dark"] .table-primary {
  --bs-table-bg: rgba(124, 58, 237, .18);
  --bs-table-border-color: rgba(124, 58, 237, .34);
  --bs-table-striped-bg: rgba(124, 58, 237, .24);
  --bs-table-active-bg: rgba(124, 58, 237, .30);
  --bs-table-hover-bg: rgba(124, 58, 237, .26);
}

[data-bs-theme="dark"] .table-secondary {
  --bs-table-bg: rgba(173, 181, 189, .12);
  --bs-table-border-color: rgba(173, 181, 189, .26);
  --bs-table-striped-bg: rgba(173, 181, 189, .18);
  --bs-table-active-bg: rgba(173, 181, 189, .24);
  --bs-table-hover-bg: rgba(173, 181, 189, .20);
}

/* The group headers on GrantReports/ByOrg. bg-primary-subtle IS theme-aware,
   but its dark value is tuned to Bootstrap's own blue - this app's dark
   accent is the violet used for .btn-primary above, and the stock blue read
   as a second, competing brand colour down the page. */
.grant-group-header {
  background-color: rgba(13, 110, 253, .10);
  border: 1px solid rgba(13, 110, 253, .20);
}

[data-bs-theme="dark"] .grant-group-header {
  background-color: rgba(124, 58, 237, .18);
  border: 1px solid rgba(167, 139, 250, .28);
}

/* Free-text entered by users (Event Partner content, legacy award text).
   Rendered as SANITISED HTML, not encoded text - the stored data is markup for
   238 of 364 events, so encoding it showed visible tags on the public page.
   See EventContentSanitizer, which strips script, style, remote content and
   every CSS property, and turns newlines into <br> for the rows that really
   are plain text.

   No white-space: pre-wrap here: the content carries its own block elements
   now, and pre-wrap would add the source newlines on top of them. */
.user-text {
  overflow-wrap: break-word;
}

/* Pasted markup often arrives wrapped in a trailing empty paragraph. */
.user-text > p:last-child {
  margin-bottom: 0;
}

/* Sanitised content carries NO class and NO style attribute, so a table stored
   by the editor (or inherited from V1's Kendo Editor, which is what most of the
   surviving markup is - see AwardTextNormalizer) reaches the page as bare
   <table><tr><td>. Bootstrap's .table class cannot be applied to it, and V1's
   look came from the inline styles the sanitiser now strips, so without these
   descendant rules a table renders as run-together text with no cell edges.
   These are also what the editor surface shows, via .user-text. */
.user-text table {
  width: 100%;
  margin-bottom: 1rem;
  border-collapse: collapse;
}

.user-text th,
.user-text td {
  padding: 0.375rem 0.5rem;
  border: var(--bs-border-width) solid var(--bs-border-color);
  vertical-align: top;
}

.user-text th {
  text-align: left;
  background-color: var(--bs-tertiary-bg);
}

.user-text blockquote {
  margin: 0 0 1rem;
  padding-left: 0.75rem;
  border-left: 0.25rem solid var(--bs-border-color);
  color: var(--bs-secondary-color);
}

/* An <hr> inside prose, not the page-level divider Bootstrap styles. */
.user-text hr {
  margin: 1rem 0;
  opacity: 0.4;
}

/* ── Event edit form ───────────────────────────────────────────────────
   The event form is thirty-five fields across seven sections and is the
   longest form in the app by some way. Everything here exists to make that
   length navigable rather than to decorate it.

   Bootstrap variables throughout, never literals, so the whole thing follows
   the app's dark default without a parallel set of dark rules. The two
   exceptions below say why they are exceptions. */

/* The section a heading anchors to must clear the sticky nav, or clicking
   "Location" scrolls the heading under it. */
.event-form-section {
  scroll-margin-top: 1.5rem;
}

/* Same again for the accordion rows inside Event page content, which the
   section nav now links to individually. Without it, clicking "Agenda" puts
   that row's top edge flush against the top of the window with the section it
   belongs to scrolled entirely out of sight. */
.event-form-section .accordion-item {
  scroll-margin-top: 1.5rem;
}

.event-header-card {
  border-left: 0.25rem solid var(--bs-primary);
}

/* ── Field hints ──────────────────────────────────────────────────────
   A HINT SITS BETWEEN THE LABEL AND THE CONTROL, never under it. Under the
   control it is read after the answer has already been given - "Enter 0 for a
   free event" arrived below the box someone had already left blank, and
   "Leave everything unticked to open this event to all teams" arrived below
   ten association checkboxes they had already ticked. Above, it is part of
   the question.

   It also clears the space below the control, which asp-validation-for wants.
   The two used to stack there, so a red error came up as the SECOND line under
   a box, beneath a grey sentence that was still true and still competing for
   the same glance.

   .field-hint rather than .form-text because the two now mean different
   things - a hint is instruction, an error is a result - and because
   Bootstrap's .form-text carries a margin-top that is simply wrong above a
   control.

   THE TWO GAPS ARE DELIBERATELY UNEQUAL - about 2px above, 8px below. A hint
   sat exactly between a label and a control belongs to neither, and the eye
   groups by proximity before it reads anything. Pulled up against the label it
   reads as part of the question; the larger gap below is what separates the
   question from the answer.

   Size and colour are Bootstrap's .form-text values unchanged. Moving help
   text is not a licence to shrink it - it is now read BEFORE the field rather
   than as a footnote after it, so if anything it is being asked to work
   harder. Hierarchy comes from the colour and the position instead.

   65ch is the measure the section subtitles use, so guidance wraps the same
   way at both scales. */
.field-hint {
  margin-top: -0.375rem;
  margin-bottom: 0.5rem;
  max-width: 65ch;
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--bs-secondary-color);
}

/* Hints are the one place on this form where bold is load-bearing - it marks
   the default ("leave these unticked", "only Approved publishes"). At
   .8125rem in secondary grey, bolding alone does not survive; it needs the
   body colour too. */
.field-hint strong {
  color: var(--bs-body-color);
  font-weight: 600;
}

/* A hint that has become the whole content of its block - the empty-state
   notes on the filtered checkbox grids - has no control under it to sit
   against, so the tight bottom margin would strand it. */
.field-hint:last-child {
  margin-bottom: 0;
}

/* ── Keeping a row of fields on one line ──────────────────────────────
   THE COST OF MOVING HINTS UP, and it has to be paid in CSS rather than by
   shortening the text. Hints wrap to different numbers of lines in a
   col-md-3, so Basics rendered its four selects at three different heights -
   Season and Qualification level (no hint) up against their labels, Program
   type and Event type (two-line hints) a full two lines lower. Read down the
   row rather than across it and that is just a broken grid.

   Bootstrap already stretches the columns of a row to a common height. This
   makes each one a flex column and drops the control to its bottom edge, so
   every control in the row lands on the same line whatever happened above it -
   including in a column with no hint at all, which is the case that rules out
   the obvious alternative of letting .field-hint grow to fill the slack.

   ONLY THE SHORT CONTROLS. .form-control, .form-select, .input-group and a
   single .form-check are all about one control-height, which is what makes
   "bottom of the column" and "same line" the same statement. app-editor,
   app-chips, the checkbox grids and the read-only HTML blocks are all
   different heights and are deliberately left top-aligned - bottom-aligning a
   five-row chip box against a one-line read-only div would invent a
   misalignment rather than fix one.

   In the error state the control rides up by the height of its message, since
   asp-validation-for is the flex item after it. That is the right way round:
   an error is worth a nudge out of the grid. */
.event-form-section .row > [class*="col-"] {
  display: flex;
  flex-direction: column;
}

.event-form-section .row > [class*="col-"] > .form-control,
.event-form-section .row > [class*="col-"] > .form-select,
.event-form-section .row > [class*="col-"] > .input-group,
.event-form-section .row > [class*="col-"] > .form-check {
  margin-top: auto;
}

/* ── Section nav ─────────────────────────────────────────────────────
   Sticky rail of section links with a left bar on the one you are in.
   Rendered by <app-section-nav> (TagHelpers/AppSectionNavTagHelper.cs) for
   the event form, which uses the sub-rows and dots below, and the Dashboard,
   which adds .section-nav-strip for the phone treatment. New long pages
   should use the component rather than grow a third markup.

   top is the page's own top padding, not a header offset - this app's navbar
   scrolls away rather than sticking. */
.section-nav {
  position: sticky;
  top: 1rem;
}

.section-nav .list-group-item {
  background-color: transparent;
  border: 0;
  border-left: 0.1875rem solid transparent;
  border-radius: 0;
  padding: 0.5rem 0.75rem;
  color: var(--bs-secondary-color);
}

.section-nav .list-group-item:hover {
  color: var(--bs-body-color);
  background-color: var(--bs-tertiary-bg);
}

/* .active is set by the page's script as you scroll, so this doubles as a
   position indicator - which is the reason the nav is worth its width at all. */
.section-nav .list-group-item.active {
  color: var(--bs-primary);
  background-color: transparent;
  border-left-color: var(--bs-primary);
  font-weight: 600;
}

/* Count beside a row: "8" on the Dashboard, "2/6" on the event form. Tabular
   figures so the number does not jitter sideways as it changes. */
.section-nav-count {
  flex-shrink: 0;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--bs-secondary-color);
}

/* THE PHONE TREATMENT. Below lg the rail would eat the screen, which is why
   the event form hides its own. The Dashboard cannot afford to: an RSM on a
   phone has twenty-odd tiles in one column. So the same list turns into a
   row pinned to the top that scrolls sideways, with the bar moved from the
   left edge to the bottom edge. The negative margins bleed it to the
   container's padding so the first row is not indented from the tiles. */
@media (max-width: 991.98px) {
  /* THE COLUMN STICKS, NOT THE NAV. A sticky element cannot leave its
     containing block, and below lg the nav's column is exactly as tall as
     the nav - so a sticky nav had nowhere to go and scrolled off with the
     page. The column's containing block is the whole row. Found 2026-09-09;
     an earlier screenshot had it "pinned" only because the scroll position
     happened to match its natural offset. */
  .dash-nav-col {
    position: sticky;
    top: 0;
    z-index: 3;
  }

  .section-nav-strip {
    position: static;
    margin: 0 calc(-1 * var(--bs-gutter-x, 1.5rem) / 2);
    padding: 0 calc(var(--bs-gutter-x, 1.5rem) / 2);
    background-color: var(--bs-body-bg);
    border-bottom: var(--bs-border-width) solid var(--bs-border-color);
  }

  .section-nav-strip .list-group {
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .section-nav-strip .list-group::-webkit-scrollbar {
    display: none;
  }

  .section-nav-strip .list-group-item {
    flex: 0 0 auto;
    /* Bootstrap gives .list-group-item-action width: 100%, which in a row
       makes every entry a full screen wide and the strip one item at a time. */
    width: auto;
    border-left: 0;
    border-bottom: 0.1875rem solid transparent;
    padding: 0.625rem 0.625rem;
    white-space: nowrap;
  }

  .section-nav-strip .list-group-item:hover {
    background-color: transparent;
  }

  .section-nav-strip .list-group-item.active {
    border-bottom-color: var(--bs-primary);
  }

  /* An anchor jump has to land below the pinned strip, not under it. */
  .dash-section {
    scroll-margin-top: 3.5rem;
  }
}

/* ── Dashboard tiles ─────────────────────────────────────────────────
   One notch denser than a default card: four across at xxl, tighter padding,
   smaller description. The page is scanned, not read. */
.dash-tile .card-body {
  padding: 0.875rem 1rem;
}

.dash-tile .card-title {
  margin-bottom: 0.25rem;
}

.dash-tile .card-text {
  font-size: 0.875rem;
  margin-bottom: 0;
}

.dash-tile.card {
  transition: border-color 0.12s ease-in-out;
}

a:hover > .dash-tile.card,
a:focus-visible > .dash-tile.card {
  border-color: var(--bs-primary);
}

.dash-section {
  margin-bottom: 2rem;
}

.dash-filter {
  max-width: 28rem;
}

@media (prefers-reduced-motion: reduce) {
  .dash-tile.card {
    transition: none;
  }
}

/* ── Sub-entries under a section ──────────────────────────────────────
   The six Event page content fields, listed under their parent so the nav can
   show which of them are blank without anything being opened.

   THE GUIDE LINE IS THE WHOLE "UNDER" AFFORDANCE. Indentation alone reads as
   indentation at this scale - three or four pixels of hierarchy against a
   14-row list. A rule down the left edge makes the group a group, and it lands
   under the parent's ICON rather than its text, which is where the eye already
   is after reading the row above.

   Not a nested .list-group: that reintroduces Bootstrap's borders and radii,
   both of which this nav strips two rules up. */
.section-nav-sub {
  margin-left: 1.375rem;
  border-left: var(--bs-border-width) solid var(--bs-border-color);
}

.section-nav .section-nav-subitem {
  padding-left: 0.625rem;
  font-size: 0.875rem;
  /* The parent rows carry a 3px transparent left border that turns primary when
     active. A sub-row must not, or it would double up with the guide line it
     sits against; its own active state is carried by the dot and the weight. */
  border-left: 0;
}

/* THE FILLED/EMPTY MARKER, and deliberately the same solid-vs-outline pair as
   the "Filled in" / "Empty" badges on the accordion rows it mirrors. A word
   badge does not survive being shrunk to nav scale, but solid-vs-hollow does,
   and it means the two halves of the screen are saying one thing in two sizes.

   NEUTRAL, NEVER GREEN. All six fields are optional and the card says so out
   loud - a tick would turn six legitimately blank rows into six failures.

   flex-shrink guards the shape: a long label in a narrow column would
   otherwise squash the ring into an oval. */
.section-nav-dot {
  flex-shrink: 0;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 0.0625rem solid var(--bs-secondary-color);
}

.section-nav-dot.is-filled {
  background-color: var(--bs-secondary-color);
}

.section-nav .section-nav-subitem.active .section-nav-dot {
  border-color: var(--bs-primary);
}

.section-nav .section-nav-subitem.active .section-nav-dot.is-filled {
  background-color: var(--bs-primary);
}

/* THE PARENT OF THE ROW YOU ARE IN. Without this, scrolling into Event page
   content would light a sub-row and leave its parent looking like a section you
   are not in - which is worse than the single highlight this replaced.

   Lighter than .active on purpose: the bar says which section, the bold sub-row
   says which part of it. Two bold primary rows would compete to answer the same
   question. */
.section-nav .list-group-item.is-current {
  color: var(--bs-body-color);
  border-left-color: var(--bs-primary);
}

/* Sticky save bar. The Save button used to sit below six rich-text editors;
   this keeps it and the unsaved-changes indicator on screen for the whole
   form. */
.event-form-actions {
  position: sticky;
  bottom: 0;
  z-index: 3;
  padding: 0.75rem 1rem;
  margin-bottom: 1.5rem;
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 -0.25rem 0.75rem rgba(0, 0, 0, 0.08);
}

/* On a near-black ground a shadow is invisible, so the bar needs an edge
   instead - otherwise it floats over the content with nothing separating it. */
[data-bs-theme="dark"] .event-form-actions {
  box-shadow: 0 -0.25rem 0.75rem rgba(0, 0, 0, 0.5);
  border-color: var(--bs-border-color-translucent);
}

/* The three date pairs, kept visually apart so "registration closes" is not
   read as a fourth event date. */
.event-date-group {
  padding-left: 0.75rem;
  border-left: 0.1875rem solid var(--bs-border-color);
}

/* Replaces <select multiple size="6"> + "Ctrl/Cmd-click to select multiple".
   auto-fill rather than a fixed column count: grade level names are short and
   award names are long, and the same rule has to hold both without either
   wrapping mid-word or stranding one item per row. */
.event-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 0.25rem 1rem;
}

.event-check-grid-wide {
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

/* ── The locked (read-only) render ────────────────────────────────────
   The <fieldset disabled> is what makes a locked form safe - a disabled
   control posts nothing. These rules only make it legible.

   WHY NOT JUST LEAVE IT GREY. Bootstrap's disabled styling means "temporarily
   unavailable, try again", which is not what has happened: the event is
   approved and this is a permanent, correct state showing live values. Grey
   boxes read as a broken page, and a broken page gets emailed about. Flat text
   reads as "this is what is published", which is what an Event Partner is
   actually looking at it for. */
.event-form-locked .form-control:disabled,
.event-form-locked .form-select:disabled {
  background-color: transparent;
  border-color: transparent;
  border-bottom-color: var(--bs-border-color);
  border-radius: 0;
  padding-left: 0;
  color: var(--bs-body-color);
  opacity: 1;
}

/* The caret would still be drawn on a disabled select, promising a menu that
   cannot open. */
.event-form-locked .form-select:disabled {
  background-image: none;
}

.event-form-locked .form-check-input:disabled {
  opacity: 1;
}

.event-form-locked .form-check-input:disabled ~ .form-check-label {
  opacity: 1;
}

/* Rich text shown as rendered markup rather than as a disabled textarea full
   of raw HTML source, which is what a locked <app-editor> would otherwise be. */
.event-readonly-html {
  background-color: var(--bs-tertiary-bg);
  min-height: 2.5rem;
}

/* asp-validation-summary ALWAYS renders its container, errors or not - so
   giving it .alert-danger painted an empty red box across the top of a form
   that had nothing wrong with it.

   Hidden by the class ASP.NET Core puts there itself rather than by rendering
   the div conditionally: the summary has to be able to APPEAR when client-side
   validation finds a problem, and jquery.validate.unobtrusive swaps
   .validation-summary-valid for .validation-summary-errors to do it. A Razor
   @if would have removed the element the script needs to fill. */
[data-validation-summary].validation-summary-valid {
  display: none;
}

/* ── Non-production environment marker ────────────────────────────────
   Rendered by Views/Shared/_EnvironmentBanner.cshtml, which _Layout omits
   entirely on production - so none of this is ever on the live site.

   AMBER, NOT RED. Red is spoken for: .act-as-banner above uses #B3261E to mean
   "you are inside someone else's account", which is a warning about what your
   next click will do. This is a statement about which building you are standing
   in. They can be on screen at the same time and must not read as the same
   thing. #E8A33D is already the accent on .worlds-banner-icon, so this borrows
   a hue the palette had rather than introducing one. */
:root {
  --env-accent: #E8A33D;
}

.env-banner {
  background-color: #FFF3DC;
  color: #5A3B00;
  border-bottom: 2px solid var(--env-accent);
}

.env-banner-icon {
  font-size: 1.25rem;
  line-height: 1;
  color: #A8631A;
}

.env-banner-text {
  line-height: 1.4;
}

/* The per-slot sentence from App:EnvironmentNotice, which is where "this writes
   to LIVE data" goes. Its own line and heavier than the surrounding prose: it
   is the half of the banner that changes what a tester is allowed to click, and
   inline it read as a footnote to the label. */
.env-banner-notice {
  display: block;
  font-weight: 600;
}

.env-banner-link {
  color: #7A4A00;
  font-weight: 600;
  white-space: nowrap;
}

.env-banner-link:hover,
.env-banner-link:focus {
  color: #5A3B00;
}

/* Keeps the strip legible on the near-black ground without turning it into a
   lamp - the same approach .worlds-banner takes for its dark variant. */
[data-bs-theme="dark"] .env-banner {
  background-color: rgba(232, 163, 61, .16);
  color: #F5D9A8;
  border-bottom-color: rgba(232, 163, 61, .55);
}

[data-bs-theme="dark"] .env-banner-icon {
  color: var(--env-accent);
}

[data-bs-theme="dark"] .env-banner-link {
  color: #FFD07A;
}

[data-bs-theme="dark"] .env-banner-link:hover,
[data-bs-theme="dark"] .env-banner-link:focus {
  color: #FFE6B8;
}

/* THE PART THAT SURVIVES SCROLLING. The strip scrolls away with the header
   because it cannot be sticky - .act-as-banner already holds that slot, and
   two sticky-top siblings stack at the same offset. This outlines the viewport
   instead, so the answer to "which site am I on" is on screen permanently
   without costing a second row of chrome on every page.

   z-index above the Bootstrap modal (1055) and the act-as banner (1080) on
   purpose: a dialog is exactly when someone is about to commit something, and
   it is the worst moment for the marker to be the thing that got covered.

   pointer-events: none is load-bearing. Without it this element is a full-
   viewport overlay and the site is unusable - nothing beneath it can be
   clicked. */
.env-frame {
  position: fixed;
  inset: 0;
  border: 3px solid var(--env-accent);
  pointer-events: none;
  z-index: 2000;
}

/* A fixed element repeats on every printed page, and a printout does not
   scroll, so the frame has nothing to add there. The strip prints - a hard copy
   of a test page should still say it came from one. */
@media print {
  .env-frame {
    display: none;
  }
}

/* ── A page that IS one measured column ────────────────────────────────
   Centred, not left-aligned - and that is deliberately a DIFFERENT rule
   from the ~20 `style="max-width: 40rem"` blocks elsewhere in the app.

   Those are ONE element inside a wider page: a form under a full-width
   <h1>, above or beside a full-width grid. Hugging the left keeps them
   aligned with everything around them, which is right.

   This is for a page where there IS nothing around them - account setup is
   the whole page, five panels and a stepper and no other content. Left
   aligned, it strands the entire page in the left 58% of a 1320px
   container and leaves 528px of nothing beside it. Stretching instead is
   not the alternative: the panels are forms, and a 650px-wide City input
   or an organization row with its number at x=294 and its program name at
   x=1560 is worse than the whitespace.

   THE HEADING GOES INSIDE IT. A centred stepper under a left-aligned <h1>
   reads as a bug; the measure has to own the whole page or none of it.

   --page-measure overrides the width for a flow that needs a different
   one. Guided flows to come (event creation, bulk import) get this class
   rather than another inline style. */
.page-measure {
  max-width: var(--page-measure, 48rem);
  margin-inline: auto;
}
