/* ==========================================================================
   Material Design 3 — Components
   Two jobs:
     1. Re-implement the legacy class vocabulary (.btn/.card/.form-control/…)
        as genuine M3 components, so existing markup renders as Material.
     2. Provide native M3 components (.m3-*) for the new shell and chrome.
   Style intent: plain. Low elevation, flat surfaces, no gradients.
   ========================================================================== */

/* --------------------------------------------------------------------------
   State layer — the M3 interaction primitive

   Every interactive surface gets one: a translucent overlay of its own
   currentColor at the spec opacities (hover 8%, focus 10%, pressed 10%).
   This is what makes hover feel consistent across the whole app.
   -------------------------------------------------------------------------- */
.m3-state,
.m3-drawer-item,
.m3-list-item,
.list-group-item-action,
.dropdown-item,
.m3-chip,
.nav-link,
.m3-segmented-item,
.m3-fab,
.m3-nav-icon,
.m3-nav-rail-icon,
.card-interactive {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.m3-state::after,
.m3-drawer-item::after,
.m3-list-item::after,
.list-group-item-action::after,
.dropdown-item::after,
.m3-chip::after,
.nav-link::after,
.m3-segmented-item::after,
.m3-fab::after,
.m3-nav-icon::after,
.m3-nav-rail-icon::after,
.card-interactive::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--md-duration-short) var(--md-easing-standard);
  z-index: 0;
}

.m3-state:hover::after,
.m3-drawer-item:hover::after,
.m3-list-item:hover::after,
.list-group-item-action:hover::after,
.dropdown-item:hover::after,
.m3-chip:hover::after,
.nav-link:hover::after,
.m3-segmented-item:hover::after,
.m3-fab:hover::after,
.card-interactive:hover::after,
/* Nav items are hovered by their whole target, but the state layer belongs
   to the pill around the icon — that is where M3 draws it. */
.m3-nav-item:hover .m3-nav-icon::after,
.m3-nav-rail-item:hover .m3-nav-rail-icon::after {
  opacity: var(--md-state-hover);
}

.m3-state:focus-visible::after,
.m3-drawer-item:focus-visible::after,
.m3-list-item:focus-visible::after,
.list-group-item-action:focus-visible::after,
.dropdown-item:focus-visible::after,
.m3-chip:focus-visible::after,
.nav-link:focus-visible::after,
.m3-segmented-item:focus-visible::after,
.m3-fab:focus-visible::after,
.card-interactive:focus-visible::after,
.m3-nav-item:focus-visible .m3-nav-icon::after,
.m3-nav-rail-item:focus-visible .m3-nav-rail-icon::after {
  opacity: var(--md-state-focus);
}

.m3-state:active::after,
.m3-drawer-item:active::after,
.m3-list-item:active::after,
.list-group-item-action:active::after,
.dropdown-item:active::after,
.m3-chip:active::after,
.nav-link:active::after,
.m3-segmented-item:active::after,
.m3-fab:active::after,
.card-interactive:active::after,
.m3-nav-item:active .m3-nav-icon::after,
.m3-nav-rail-item:active .m3-nav-rail-icon::after {
  opacity: var(--md-state-pressed);
}

/* Lift the label/icon above the state layer.
   `:not(.m3-ripple)` is essential: these selectors have the same specificity as
   `.m3-ripple` but come later, so without it they would override the ripple's
   `position:absolute`, drop it into normal flow, and blow up the element's size
   on every click. */
.m3-drawer-item > *:not(.m3-ripple),
.m3-list-item > *:not(.m3-ripple),
.dropdown-item > *:not(.m3-ripple),
.m3-chip > *:not(.m3-ripple),
.nav-link > *:not(.m3-ripple),
.m3-segmented-item > *:not(.m3-ripple),
.m3-fab > *:not(.m3-ripple) { position: relative; z-index: 1; }

/* Hover must never linger on touch: a tapped element would otherwise keep its
   state layer until the next tap elsewhere. */
@media (hover: none) {
  .m3-state:hover::after,
  .m3-drawer-item:hover::after,
  .m3-list-item:hover::after,
  .list-group-item-action:hover::after,
  .dropdown-item:hover::after,
  .m3-chip:hover::after,
  .nav-link:hover::after,
  .m3-segmented-item:hover::after,
  .m3-fab:hover::after,
  .card-interactive:hover::after,
  .btn:hover::after,
  .m3-nav-item:hover .m3-nav-icon::after,
  .m3-nav-rail-item:hover .m3-nav-rail-icon::after { opacity: 0; }
  .table-hover > tbody tr:hover { background-color: transparent; }
}

/* Ripple (emitted by m3.js) */
.m3-ripple {
  position: absolute !important;
  border-radius: 50%;
  transform: scale(0);
  background-color: currentColor;
  opacity: .16;
  pointer-events: none;
  animation: m3-ripple-in 420ms var(--md-easing-standard) forwards;
  z-index: 0;
}
@keyframes m3-ripple-in { to { transform: scale(1); opacity: 0; } }

/* --------------------------------------------------------------------------
   Buttons  — .btn maps onto the M3 button family
   -------------------------------------------------------------------------- */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 10px 24px;
  border: none;
  border-radius: var(--md-shape-full);
  font: var(--md-label-large);
  letter-spacing: .0071em;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  background-color: transparent;
  color: var(--md-primary);
  transition: box-shadow var(--md-duration-medium) var(--md-easing-standard),
              background-color var(--md-duration-medium) var(--md-easing-standard);
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { text-decoration: none; }
.btn > *:not(.m3-ripple) { position: relative; z-index: 1; }
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--md-duration-short) var(--md-easing-standard);
  z-index: 0;
}
.btn:hover::after { opacity: var(--md-state-hover); }
.btn:focus-visible::after { opacity: var(--md-state-focus); }
.btn:active::after { opacity: var(--md-state-pressed); }
.btn:disabled, .btn.disabled {
  cursor: default;
  pointer-events: none;
  opacity: 1;
  background-color: color-mix(in srgb, var(--md-on-surface) 12%, transparent);
  color: color-mix(in srgb, var(--md-on-surface) 38%, transparent);
  box-shadow: none;
}
.btn:disabled::after, .btn.disabled::after { opacity: 0; }

/* Filled buttons (primary action) */
.btn-primary {
  background-color: var(--md-primary);
  background-image: linear-gradient(
    135deg,
    color-mix(in srgb, var(--md-primary) 80%, white) 0%,
    color-mix(in srgb, var(--md-primary) 80%, black) 100%
  );
  color: var(--md-on-primary);
}
.btn-secondary { background-color: var(--md-secondary); color: var(--md-on-secondary); }
.btn-success { background-color: var(--md-success); color: var(--md-on-success); }
.btn-danger { background-color: var(--md-error); color: var(--md-on-error); }
.btn-warning { background-color: var(--md-warning); color: var(--md-on-warning); }
.btn-info { background-color: var(--md-tertiary); color: var(--md-on-tertiary); }
.btn-dark { background-color: var(--md-inverse-surface); color: var(--md-inverse-on-surface); }
.btn-light { background-color: var(--md-surface-container-high); color: var(--md-on-surface); }

/* Tonal buttons — M3's filled-tonal role */
.btn-tonal, .btn-label-primary { background-color: var(--md-primary-container); color: var(--md-on-primary-container); }
.btn-label-secondary { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.btn-label-success { background-color: var(--md-success-container); color: var(--md-on-success-container); }
.btn-label-danger { background-color: var(--md-error-container); color: var(--md-on-error-container); }
.btn-label-warning { background-color: var(--md-warning-container); color: var(--md-on-warning-container); }
.btn-label-info { background-color: var(--md-tertiary-container); color: var(--md-on-tertiary-container); }

/* Outlined buttons */
.btn-outline-primary { border: 1px solid var(--md-outline); color: var(--md-primary); }
.btn-outline-secondary { border: 1px solid var(--md-outline); color: var(--md-on-surface-variant); }
.btn-outline-success { border: 1px solid var(--md-outline); color: var(--md-success); }
.btn-outline-danger { border: 1px solid var(--md-outline); color: var(--md-error); }
.btn-outline-warning { border: 1px solid var(--md-outline); color: var(--md-warning); }
.btn-outline-info { border: 1px solid var(--md-outline); color: var(--md-tertiary); }
.btn-outline-dark { border: 1px solid var(--md-outline); color: var(--md-on-surface); }
.btn-outline-primary:focus-visible, .btn-outline-secondary:focus-visible { border-color: var(--md-primary); }

/* Text buttons.
   Scoped to `.btn.btn-text` on purpose: the app already uses a bare
   `<span class="btn-text">` as the label inside buttons (the auth forms swap
   its textContent). An unscoped `.btn-text` rule repainted those labels in
   the primary colour — invisible on a filled primary button. */
.btn.btn-text, .btn.btn-link { background-color: transparent; color: var(--md-primary); padding-left: 12px; padding-right: 12px; }
.btn.btn-link:hover { text-decoration: none; }

/* Sizes */
.btn-xs { min-height: 32px; padding: 6px 12px; font: var(--md-label-medium); }
/* 44px is the WCAG 2.5.8 / iOS minimum touch target. .btn-sm was 36px, which
   made every filter chip, pagination control and small action button an
   undersized tap target. Padding is unchanged so the visual weight is similar. */
.btn-sm { min-height: 44px; padding: 8px 16px; font: var(--md-label-large); }
.btn-lg { min-height: 48px; padding: 14px 28px; font: var(--md-label-large); font-size: 15px; }
.btn-block, .d-grid > .btn { width: 100%; }

/* Icon button — M3 spec draws a 40dp circle, but a 40px target fails WCAG
   2.5.8 (44px min). Use 44px for the real box so the tap area genuinely
   measures 44px; the visual circle stays modest because the icon glyph and
   border-radius are unchanged. A ::after overlay was tried first and rejected:
   it does not enlarge the element's own hit box. */
.btn-icon {
  min-height: 44px;
  width: 44px;
  padding: 8px;
  border-radius: 50%;
  gap: 0;
}
.btn-icon.btn-sm { min-height: 44px; width: 44px; }
.btn-icon.btn-lg { min-height: 48px; width: 48px; }

/* Elevation only where M3 calls for it */
.btn-primary:not(.btn-flat), .btn-danger:not(.btn-flat), .btn-success:not(.btn-flat) { box-shadow: var(--md-elevation-0); }
.btn-primary:hover, .btn-danger:hover, .btn-success:hover { box-shadow: var(--md-elevation-1); }
/* Signature violet glow on the app's highest-emphasis action. */
.btn-primary:hover { box-shadow: 0 4px 16px rgba(var(--md-glow-rgb), .45); }
.btn-primary:focus-visible { box-shadow: 0 0 0 3px rgba(var(--md-glow-rgb), .35); }

.btn-group { display: inline-flex; gap: 8px; flex-wrap: wrap; }
.btn-group > .btn { flex: 0 1 auto; }

/* FAB */
.m3-fab {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 56px;
  min-width: 56px;
  padding: 0 16px;
  border: none;
  border-radius: var(--md-shape-lg);
  background-color: var(--md-primary-container);
  color: var(--md-on-primary-container);
  box-shadow: var(--md-elevation-2);
  font: var(--md-label-large);
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  transition: box-shadow var(--md-duration-medium) var(--md-easing-standard);
}
.m3-fab:hover { box-shadow: var(--md-elevation-3); }
.m3-fab.m3-fab-small { height: 40px; min-width: 40px; border-radius: var(--md-shape-md); }
.m3-fab.m3-fab-large { height: 96px; min-width: 96px; border-radius: var(--md-shape-xl); }

/* --------------------------------------------------------------------------
   Cards — M3 elevated / filled / outlined
   -------------------------------------------------------------------------- */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;
  background-color: var(--md-surface-container-low);
  background-clip: border-box;
  border: none;
  border-radius: var(--md-shape-md);
  box-shadow: var(--md-elevation-1);
  color: var(--md-on-surface);
}
.card-outlined, .card.border {
  box-shadow: none;
  border: 1px solid var(--md-outline-variant);
  background-color: var(--md-surface);
  /* Subtle diagonal wash instead of a flat fill — depth without competing
     with the content, since this is the app's dominant card style. */
  background-image: linear-gradient(160deg, color-mix(in srgb, var(--md-primary) 6%, transparent) 0%, transparent 60%);
  transition: border-color var(--md-duration-medium) var(--md-easing-standard),
              box-shadow var(--md-duration-medium) var(--md-easing-standard);
}
.card-filled { box-shadow: none; background-color: var(--md-surface-container-highest); }

/* Cards that actually do something get M3 hover feedback. Detected from the
   markup so existing pages need no changes. */
.card[onclick], a.card, .card.cursor-pointer, .card[role="button"] { cursor: pointer; }
.card[onclick]:hover, a.card:hover, .card.cursor-pointer:hover, .card[role="button"]:hover {
  box-shadow: var(--md-elevation-2);
}
.card-outlined[onclick]:hover, a.card-outlined:hover, .card-outlined.cursor-pointer:hover, .card-outlined[role="button"]:hover {
  border-color: var(--md-primary);
}
.card[onclick]:active, a.card:active, .card.cursor-pointer:active, .card[role="button"]:active {
  box-shadow: var(--md-elevation-1);
}
@media (hover: none) {
  .card[onclick]:hover, a.card:hover, .card.cursor-pointer:hover, .card[role="button"]:hover {
    box-shadow: var(--md-elevation-1);
  }
}
.card-body { flex: 1 1 auto; padding: 16px; }
.card-header {
  padding: 16px 16px 8px;
  background-color: transparent;
  border-bottom: none;
  font: var(--md-title-medium);
  color: var(--md-on-surface);
}
.card-header.with-border, .card-header.border-bottom { border-bottom: 1px solid var(--md-outline-variant); padding-bottom: 12px; }
.card-footer { padding: 8px 16px 16px; background-color: transparent; border-top: none; }
.card-title { margin: 0 0 4px; font: var(--md-title-medium); color: var(--md-on-surface); }
.card-subtitle { margin: 0 0 8px; font: var(--md-body-medium); color: var(--md-on-surface-variant); }
.card-text { font: var(--md-body-medium); color: var(--md-on-surface-variant); margin: 0 0 8px; }
.card-img, .card-img-top { width: 100%; border-radius: var(--md-shape-md) var(--md-shape-md) 0 0; display: block; }
.card-img-bottom { width: 100%; border-radius: 0 0 var(--md-shape-md) var(--md-shape-md); display: block; }
.card-link { color: var(--md-primary); }

/* --------------------------------------------------------------------------
   Text fields — M3 outlined text field
   -------------------------------------------------------------------------- */
.form-label {
  display: block;
  margin-bottom: 6px;
  font: var(--md-body-small);
  letter-spacing: .0333em;
  color: var(--md-on-surface-variant);
}
.col-form-label { padding-top: 8px; font: var(--md-body-small); color: var(--md-on-surface-variant); }

/* Filled, rounded text fields — the app-wide input style.

   The resting border used to be `1px solid transparent`, on the theory that a
   filled container reads as a field on its own. It does not, because the fill
   token is --md-surface-container-high and several containers the fields sit in
   use that SAME token. Measured on the running app (dark, 390px):

     join dialog   field rgb(40,42,45) on .modal-content rgb(40,42,45) = 1.00:1
     withdraw      field rgb(40,42,45) on page rgb(26,28,30)           = 1.19:1
     login         username field on its card                          = 1.00:1

   i.e. the input had no perceivable edge at all — WCAG 1.4.11 wants 3:1 for a
   control boundary. --md-outline is used instead of --md-outline-variant
   because the variant only reaches 1.83:1 (dark) / 1.66:1 (light) against these
   surfaces, while --md-outline measures 4.55:1 dark and 3.85:1 light against
   both the page and the dialog. Width was already 1px, so nothing reflows —
   only the colour changes. Hover moves up to --md-on-surface so the
   rest -> hover -> focus progression is still legible. */
.form-control, .form-select {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: 12px 16px;
  font: var(--md-body-large);
  color: var(--md-on-surface);
  background-color: var(--md-surface-container-high);
  background-clip: padding-box;
  border: 1px solid var(--md-outline);
  border-radius: var(--md-shape-md);
  appearance: none;
  transition: border-color var(--md-duration-short) var(--md-easing-standard),
              background-color var(--md-duration-short) var(--md-easing-standard),
              box-shadow var(--md-duration-short) var(--md-easing-standard);
}
.form-control::placeholder { color: var(--md-on-surface-variant); opacity: .7; }
/* Rest is now --md-outline, so hover has to step ABOVE it or there would be no
   hover feedback at all (both states would paint the same colour). */
.form-control:hover, .form-select:hover { border-color: var(--md-on-surface); }
.form-control:focus, .form-select:focus {
  outline: none;
  background-color: var(--md-surface);
  border-color: var(--md-primary);
  box-shadow: inset 0 0 0 1px var(--md-primary), 0 0 0 3px rgba(var(--md-glow-rgb), .18);
}
.form-control:disabled, .form-select:disabled, .form-control[readonly] {
  background-color: color-mix(in srgb, var(--md-on-surface) 6%, transparent);
  /* Deliberately dimmer than the resting outline: an unusable field should read
     as recessed, which is also how M3 draws a disabled text field. */
  border-color: var(--md-outline-variant);
  color: color-mix(in srgb, var(--md-on-surface) 38%, transparent);
}
.form-control.is-invalid, .form-select.is-invalid { border-color: var(--md-error); }
.form-control.is-invalid:focus { box-shadow: inset 0 0 0 1px var(--md-error); }
.form-control.is-valid { border-color: var(--md-success); }
textarea.form-control { min-height: 96px; resize: vertical; }
.form-control-sm { min-height: 40px; padding: 8px 12px; font: var(--md-body-medium); }
.form-control-lg { min-height: 56px; padding: 16px; }

.form-select {
  padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2343474e'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 20px;
}

.form-text { margin-top: 4px; font: var(--md-body-small); color: var(--md-on-surface-variant); }
.invalid-feedback { display: none; margin-top: 4px; font: var(--md-body-small); color: var(--md-error); }
.is-invalid ~ .invalid-feedback, .invalid-feedback.d-block { display: block; }
.valid-feedback { display: none; margin-top: 4px; font: var(--md-body-small); color: var(--md-success); }

/* Input group */
.input-group { position: relative; display: flex; flex-wrap: nowrap; align-items: stretch; width: 100%; }
.input-group > .form-control, .input-group > .form-select { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; }
.input-group > .form-control:not(:first-child), .input-group > .form-select:not(:first-child) {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.input-group > .form-control:not(:last-child), .input-group > .form-select:not(:last-child) {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.input-group-text {
  display: flex;
  align-items: center;
  padding: 12px 14px;
  font: var(--md-body-medium);
  color: var(--md-on-surface-variant);
  background-color: var(--md-surface-container-high);
  /* Matches the field it is joined to; a transparent edge here would leave the
     addon looking detached now that the field itself is outlined. */
  border: 1px solid var(--md-outline);
  border-radius: var(--md-shape-md);
}
.input-group > .input-group-text:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; padding-right: 4px; }
.input-group > .input-group-text:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; padding-left: 4px; }
/* Addon and field are one control, so drop the edge where they meet -- otherwise
   the seam shows as a doubled 2px line down the middle of the group. */
.input-group > .input-group-text:first-child { border-right-color: transparent; }
.input-group > .input-group-text:last-child { border-left-color: transparent; }
.input-group > .form-control:not(:first-child), .input-group > .form-select:not(:first-child) { border-left-color: transparent; }
.input-group > .form-control:not(:last-child), .input-group > .form-select:not(:last-child) { border-right-color: transparent; }
/* The field keeps its own focus ring; the addon reads as part of it. */
.input-group:focus-within > .input-group-text { background-color: var(--md-surface); border-color: var(--md-primary); }
.input-group:focus-within > .input-group-text:first-child { border-right-color: transparent; }
.input-group:focus-within > .input-group-text:last-child { border-left-color: transparent; }
.input-group:focus-within > .form-control, .input-group:focus-within > .form-select { box-shadow: none; }
.input-group:focus-within > .form-control:not(:first-child) { border-left-color: transparent; }
.input-group:focus-within > .form-control:not(:last-child) { border-right-color: transparent; }
.input-group > .btn { border-radius: var(--md-shape-md); }
.input-group > .btn:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; }

/* Checkbox / radio / switch */
.form-check { display: flex; align-items: center; gap: 8px; min-height: 40px; padding-left: 0; margin-bottom: 0; }
.form-check-input {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin: 0;
  appearance: none;
  background-color: transparent;
  border: 2px solid var(--md-on-surface-variant);
  border-radius: 2px;
  cursor: pointer;
  transition: background-color var(--md-duration-short) var(--md-easing-standard),
              border-color var(--md-duration-short) var(--md-easing-standard);
}
.form-check-input[type="radio"] { border-radius: 50%; }
.form-check-input:checked { background-color: var(--md-primary); border-color: var(--md-primary); }
.form-check-input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
  background-size: 14px; background-position: center; background-repeat: no-repeat;
}
.form-check-input[type="radio"]:checked { background-color: transparent; box-shadow: inset 0 0 0 4px var(--md-primary); }
.form-check-input:focus-visible { outline: 2px solid var(--md-primary); outline-offset: 2px; }
.form-check-label { font: var(--md-body-medium); color: var(--md-on-surface); cursor: pointer; }

/* M3 switch */
.form-switch { display: flex; align-items: center; gap: 12px; }
.form-switch .form-check-input {
  width: 52px;
  height: 32px;
  border-radius: var(--md-shape-full);
  background-color: var(--md-surface-container-highest);
  border: 2px solid var(--md-outline);
  position: relative;
  transition: background-color var(--md-duration-medium) var(--md-easing-standard);
}
.form-switch .form-check-input::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 4px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background-color: var(--md-outline);
  transform: translateY(-50%);
  transition: transform var(--md-duration-medium) var(--md-easing-emphasized),
              width var(--md-duration-medium) var(--md-easing-emphasized),
              height var(--md-duration-medium) var(--md-easing-emphasized),
              background-color var(--md-duration-medium) var(--md-easing-standard);
}
.form-switch .form-check-input:checked {
  background-color: var(--md-primary);
  border-color: var(--md-primary);
  background-image: none;
}
.form-switch .form-check-input:checked::before {
  width: 24px; height: 24px;
  transform: translate(20px, -50%);
  background-color: var(--md-on-primary);
}

/* --------------------------------------------------------------------------
   Badges — M3 label / suggestion chip
   -------------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  font: var(--md-label-small);
  letter-spacing: .0455em;
  line-height: 16px;
  /* Full pill by default now — reads more like a modern status chip than
     the original spec's slightly-rounded rectangle. */
  border-radius: var(--md-shape-full);
  background-color: var(--md-primary-container);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, .10), transparent);
  color: var(--md-on-primary-container);
  white-space: nowrap;
  vertical-align: middle;
}
.badge.rounded-pill { border-radius: var(--md-shape-full); padding: 4px 10px; }
.badge-dot, .badge.badge-dot { width: 8px; height: 8px; padding: 0; border-radius: 50%; }
.badge-center { justify-content: center; }

/* Chips */
.m3-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--md-outline-variant);
  border-radius: var(--md-shape-sm);
  background-color: transparent;
  color: var(--md-on-surface-variant);
  font: var(--md-label-large);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.m3-chip.m3-chip-selected {
  background-color: var(--md-secondary-container);
  color: var(--md-on-secondary-container);
  border-color: transparent;
  box-shadow: 0 0 0 1px rgba(var(--md-glow-rgb), .3), 0 2px 8px rgba(var(--md-glow-rgb), .25);
}

/* --------------------------------------------------------------------------
   Alerts — rendered as M3 banners
   -------------------------------------------------------------------------- */
/* Block, not flex. Alert bodies are prose with inline `<strong>` / `<code>` /
   `<a>` mixed into the text. As a flex container each of those became its own
   flex item on a single non-wrapping line, which (a) forced the page wider than
   the viewport on narrow screens and (b) injected a stray 12px gap either side
   of every inline tag. Block layout lets the text wrap the way prose should;
   the few alerts that genuinely need a row opt in with `d-flex` themselves. */
.alert {
  position: relative;
  display: block;
  padding: 16px;
  margin-bottom: 16px;
  border: none;
  border-radius: var(--md-shape-md);
  font: var(--md-body-medium);
  background-color: var(--md-surface-container-high);
  color: var(--md-on-surface);
  overflow-wrap: break-word;
}
/* Leading icon: keep it beside the first line rather than on its own row. */
.alert > i:first-child,
.alert > .material-symbols-outlined:first-child,
.alert > svg:first-child { vertical-align: -.125em; }
/* Alerts that opt into a row still need their children to be able to shrink. */
.alert.d-flex > * { min-width: 0; }
.alert.d-flex > i, .alert.d-flex > .material-symbols-outlined, .alert.d-flex > svg { flex-shrink: 0; }
.alert-primary { background-color: var(--md-primary-container); color: var(--md-on-primary-container); }
.alert-secondary { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.alert-success { background-color: var(--md-success-container); color: var(--md-on-success-container); }
.alert-danger { background-color: var(--md-error-container); color: var(--md-on-error-container); }
.alert-warning { background-color: var(--md-warning-container); color: var(--md-on-warning-container); }
.alert-info { background-color: var(--md-tertiary-container); color: var(--md-on-tertiary-container); }
.alert-dark { background-color: var(--md-surface-container-highest); color: var(--md-on-surface); }
.alert-light { background-color: var(--md-surface-container-low); color: var(--md-on-surface); }
.alert-heading { font: var(--md-title-small); margin-bottom: 4px; }
.alert-dismissible { padding-right: 48px; }
.alert .btn-close { position: absolute; top: 12px; right: 12px; }

.btn-close {
  width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3C/svg%3E") center/18px no-repeat;
  cursor: pointer; opacity: .7; color: inherit;
}
.btn-close:hover { opacity: 1; }

/* --------------------------------------------------------------------------
   Tables — M3 data table
   -------------------------------------------------------------------------- */
.table {
  width: 100%;
  margin-bottom: 0;
  color: var(--md-on-surface);
  border-collapse: collapse;
  vertical-align: middle;
  font: var(--md-body-medium);
}
.table > thead { background-color: transparent; }
.table > thead th {
  padding: 12px 16px;
  font: var(--md-title-small);
  color: var(--md-on-surface-variant);
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--md-outline-variant);
  text-transform: none;
  letter-spacing: .0071em;
}
.table > tbody td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--md-outline-variant);
  vertical-align: middle;
}
.table > tbody tr:last-child td { border-bottom: none; }
.table > tbody tr { transition: background-color var(--md-duration-short) var(--md-easing-standard); }
.table-hover > tbody tr:hover { background-color: color-mix(in srgb, var(--md-primary) 10%, transparent); }
.table-striped > tbody tr:nth-of-type(odd) { background-color: color-mix(in srgb, var(--md-on-surface) 4%, transparent); }
.table-bordered, .table-bordered td, .table-bordered th { border: 1px solid var(--md-outline-variant); }
.table-borderless td, .table-borderless th { border: none; }
.table-sm td, .table-sm th { padding: 8px 12px; }
.table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-dark { background-color: var(--md-inverse-surface); color: var(--md-inverse-on-surface); }
.table caption { padding: 12px 16px; color: var(--md-on-surface-variant); text-align: left; }

/* --------------------------------------------------------------------------
   Tabs — M3 primary tabs
   -------------------------------------------------------------------------- */
.nav { display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; }
.nav-tabs {
  border-bottom: 1px solid var(--md-outline-variant);
  gap: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav-tabs::-webkit-scrollbar { display: none; }
.nav-item { margin-bottom: 0; }
.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 12px 16px;
  border: none;
  background: none;
  font: var(--md-title-small);
  color: var(--md-on-surface-variant);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  overflow: hidden;
  isolation: isolate;
  transition: color var(--md-duration-medium) var(--md-easing-standard);
}
.nav-link:hover { color: var(--md-on-surface); text-decoration: none; }
.nav-tabs .nav-link.active {
  color: var(--md-primary);
  background: none;
  border: none;
}
/* The M3 tab indicator: a 3dp bar with rounded top corners. */
.nav-tabs .nav-link.active::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background-color: var(--md-primary);
  border-radius: 3px 3px 0 0;
  box-shadow: 0 0 8px rgba(var(--md-glow-rgb), .6);
}
.nav-pills { gap: 8px; }
.nav-pills .nav-link { border-radius: var(--md-shape-sm); min-height: 32px; padding: 6px 16px; border: 1px solid var(--md-outline-variant); }
.nav-pills .nav-link.active { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); border-color: transparent; }
.nav-pills .nav-link.active::before { display: none; }
.tab-content > .tab-pane { display: none; }
.tab-content > .tab-pane.active, .tab-content > .active { display: block; }
.fade { transition: opacity var(--md-duration-medium) var(--md-easing-standard); }
.fade:not(.show) { opacity: 0; }
.fade.show { opacity: 1; }

/* --------------------------------------------------------------------------
   Lists — M3 list items
   -------------------------------------------------------------------------- */
.list-group { display: flex; flex-direction: column; border-radius: var(--md-shape-md); overflow: hidden; }
.list-group-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 56px;
  padding: 12px 16px;
  background-color: var(--md-surface-container-low);
  border: none;
  border-bottom: 1px solid var(--md-outline-variant);
  color: var(--md-on-surface);
  font: var(--md-body-large);
}
.list-group-item:last-child { border-bottom: none; }
.list-group-item > * { min-width: 0; }
.list-group-item-action { cursor: pointer; }
.list-group-item.active { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.list-group-flush .list-group-item { background-color: transparent; }

/* list-style/padding/margin resets matter whenever this is used on a real
   <ul>/<ol> (e.g. MatchDetails.jsx's solo participant list) rather than a
   plain <div> — without them the browser's own decimal/bullet marker and
   ~40px indent show up alongside (and misaligned with) this component's
   own flex-row layout and any manually-rendered numbering. */
.m3-list { display: flex; flex-direction: column; list-style: none; padding-left: 0; margin: 0; }
.m3-list-item {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 56px;
  padding: 8px 16px;
  color: var(--md-on-surface);
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.m3-list-item > * { min-width: 0; }
.m3-list-item .m3-list-headline { font: var(--md-body-large); }
.m3-list-item .m3-list-supporting { font: var(--md-body-medium); color: var(--md-on-surface-variant); }
.m3-divider { height: 1px; background-color: var(--md-outline-variant); border: 0; margin: 0; }

/* --------------------------------------------------------------------------
   Dialog — M3 basic dialog (drop-in for the legacy .modal markup)
   -------------------------------------------------------------------------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 1055;
  display: none;
  overflow-x: hidden;
  overflow-y: auto;
  outline: 0;
}
.modal.show { display: block; }
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1050;
  background-color: var(--md-scrim);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  opacity: 0;
  transition: opacity var(--md-duration-medium) var(--md-easing-standard),
              backdrop-filter var(--md-duration-medium) var(--md-easing-standard),
              -webkit-backdrop-filter var(--md-duration-medium) var(--md-easing-standard);
}
.modal-backdrop.show {
  opacity: .55;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.modal-dialog {
  position: relative;
  width: auto;
  margin: 24px;
  display: flex;
  align-items: center;
  min-height: calc(100% - 48px);
  pointer-events: none;
  transform: scale(.85);
  opacity: 0;
  transition: transform var(--md-duration-long) var(--md-easing-emphasized),
              opacity var(--md-duration-medium) var(--md-easing-standard);
}
.modal.show .modal-dialog { transform: scale(1); opacity: 1; }
.modal-dialog-centered { align-items: center; }
.modal-dialog-scrollable .modal-content { max-height: calc(100vh - 48px); overflow: hidden; }
.modal-dialog-scrollable .modal-body { overflow-y: auto; }
@media (min-width: 576px) {
  .modal-dialog { max-width: 560px; margin: 28px auto; min-height: calc(100% - 56px); }
  .modal-sm { max-width: 400px; }
}
@media (min-width: 992px) { .modal-lg, .modal-xl { max-width: 800px; } }
.modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  pointer-events: auto;
  background-color: var(--md-surface-container-high);
  border: 1px solid var(--md-outline-variant);
  border-radius: var(--md-shape-xl);
  box-shadow: var(--md-elevation-3);
  outline: 0;
  overflow: hidden;
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 24px 24px 0;
  border-bottom: none;
}
.modal-title { font: var(--md-headline-small); color: var(--md-on-surface); margin: 0; }
.modal-body { position: relative; flex: 1 1 auto; padding: 16px 24px; font: var(--md-body-medium); color: var(--md-on-surface-variant); }
.modal-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 8px 24px 24px;
  border-top: none;
}
body.modal-open { overflow: hidden; }

/* --------------------------------------------------------------------------
   Snackbar — replaces toastr
   -------------------------------------------------------------------------- */
#m3-snackbar-host {
  position: fixed;
  left: 50%;
  bottom: calc(var(--md-nav-bar-height) + 16px);
  transform: translateX(-50%);
  z-index: 1090;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(560px, calc(100vw - 32px));
  pointer-events: none;
}
.m3-snackbar {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 14px 16px;
  border-radius: var(--md-shape-xs);
  background-color: var(--md-inverse-surface);
  color: var(--md-inverse-on-surface);
  box-shadow: var(--md-elevation-3);
  font: var(--md-body-medium);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--md-duration-medium) var(--md-easing-standard),
              transform var(--md-duration-medium) var(--md-easing-emphasized);
}
.m3-snackbar.m3-in { opacity: 1; transform: translateY(0); }
.m3-snackbar-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.m3-snackbar-action {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: var(--md-inverse-primary);
  font: var(--md-label-large);
  cursor: pointer;
  /* 44px min for WCAG 2.5.8 — this is the dismiss control on the demo banner
     and on toasts, so it must be reliably tappable. */
  min-height: 44px;
  padding: 8px 12px;
  border-radius: var(--md-shape-xs);
}
.m3-snackbar-icon { flex: 0 0 auto; font-size: 20px; }
/* The snackbar body is --md-inverse-surface, i.e. the opposite lightness to the
   page, so the icons must use the inverse semantic inks. The plain
   --md-error / --md-success / --md-warning are tuned for the page surface and
   land light-on-light in dark mode (~1.31:1, effectively invisible). */
.m3-snackbar.m3-snackbar-error .m3-snackbar-icon { color: var(--md-inverse-error); }
.m3-snackbar.m3-snackbar-success .m3-snackbar-icon { color: var(--md-inverse-success); }
.m3-snackbar.m3-snackbar-warning .m3-snackbar-icon { color: var(--md-inverse-warning); }

/* --------------------------------------------------------------------------
   Top app bar / navigation bar / drawer
   -------------------------------------------------------------------------- */
.m3-top-app-bar {
  position: sticky;
  top: 0;
  z-index: 1030;
  display: flex;
  align-items: center;
  gap: 4px;
  height: var(--md-top-app-bar-height);
  padding: 8px 4px 8px 4px;
  background-color: color-mix(in srgb, var(--md-surface) 85%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  color: var(--md-on-surface);
  border-bottom: 1px solid var(--md-outline-variant);
  transition: background-color var(--md-duration-medium) var(--md-easing-standard),
              box-shadow var(--md-duration-medium) var(--md-easing-standard);
}
/* On scroll m3.js raises the bar to surface-container. */
.m3-top-app-bar.m3-scrolled {
  background-color: color-mix(in srgb, var(--md-surface-container) 90%, transparent);
  box-shadow: var(--md-elevation-0);
  border-bottom: 1px solid var(--md-outline-variant);
}
.m3-top-app-bar-title {
  flex: 1 1 auto;
  font: var(--md-title-large);
  color: var(--md-on-surface);
  padding-left: 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.m3-top-app-bar-actions { display: flex; align-items: center; gap: 0; flex: 0 0 auto; }

.m3-nav-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1030;
  display: flex;
  align-items: flex-start;
  justify-content: space-around;
  height: var(--md-nav-bar-height);
  padding: 12px 8px 16px;
  background-color: color-mix(in srgb, var(--md-surface-container) 92%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-top: 1px solid var(--md-outline-variant);
  border-left: 1px solid var(--md-outline-variant);
  border-right: 1px solid var(--md-outline-variant);
  border-radius: var(--md-shape-xl) var(--md-shape-xl) 0 0;
  box-shadow: 0 -8px 24px rgba(var(--md-glow-rgb), .25);
}
.m3-nav-item {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  flex: 1 1 0;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--md-on-surface-variant);
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.m3-nav-item:hover { text-decoration: none; }
/* M3 active indicator: a 32dp tall pill behind the icon. */
.m3-nav-icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 32px;
  border-radius: var(--md-shape-full);
  font-size: 24px;
  transition: background-color var(--md-duration-medium) var(--md-easing-standard),
              color var(--md-duration-medium) var(--md-easing-standard);
}
.m3-nav-item.active .m3-nav-icon {
  background-color: var(--md-primary);
  background-image: linear-gradient(
    135deg,
    color-mix(in srgb, var(--md-primary) 80%, white) 0%,
    color-mix(in srgb, var(--md-primary) 80%, black) 100%
  );
  color: var(--md-on-primary);
  animation: m3-indicator-in var(--md-duration-long) var(--md-easing-emphasized);
}
/* The indicator widens into place — M3's selection motion. */
@keyframes m3-indicator-in {
  from { clip-path: inset(0 40% 0 40% round 16px); }
  to   { clip-path: inset(0 0 0 0 round 16px); }
}
@media (prefers-reduced-motion: reduce) {
  .m3-nav-item.active .m3-nav-icon { animation: none; }
  .m3-ripple { animation-duration: 1ms; }
}
.m3-nav-label {
  font: var(--md-label-medium);
  color: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.m3-nav-item.active .m3-nav-label { color: var(--md-primary); font-weight: 700; }

/* Navigation drawer */
.m3-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 1040;
  background-color: var(--md-scrim);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--md-duration-medium) var(--md-easing-standard), visibility var(--md-duration-medium);
}
.m3-drawer-scrim.m3-open { opacity: .32; visibility: visible; }
.m3-drawer {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 1045;
  width: 320px;
  max-width: 85vw;
  background-color: var(--md-surface-container-low);
  border-radius: 0 var(--md-shape-lg) var(--md-shape-lg) 0;
  transform: translateX(-100%);
  transition: transform var(--md-duration-long) var(--md-easing-emphasized);
  /* The scroll container is a separate inner element, not this one — some
     WebView engines mishandle touch-scroll gestures on an element that
     carries BOTH overflow-y:auto AND a CSS transform at the same time
     (exactly what this element needs for its slide-in animation), which
     read as "the drawer just doesn't scroll" on-device. overflow here is
     only to keep content clipped to the rounded corners. */
  overflow: hidden;
}
.m3-drawer.m3-open { transform: translateX(0); }
.m3-drawer-scroll {
  height: 100%;
  /* Without this, `padding` below adds on top of the 100% height instead
     of being carved out of it, making this box taller than its
     fixed-height parent — which the parent's own overflow:hidden then
     permanently clips off, cutting off the bottom of the list (the "Sign
     out" button/last nav item) no matter how far you scroll. */
  box-sizing: border-box;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  padding: 12px;
}
.m3-drawer-headline { padding: 16px; font: var(--md-title-small); color: var(--md-on-surface-variant); }
.m3-drawer-item {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 56px;
  padding: 0 16px;
  margin-bottom: 4px;
  border-radius: var(--md-shape-full);
  color: var(--md-on-surface-variant);
  font: var(--md-label-large);
  text-decoration: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.m3-drawer-item:hover { text-decoration: none; }
.m3-drawer-item .m3-drawer-icon { font-size: 24px; flex: 0 0 auto; }
.m3-drawer-item.active {
  background-color: var(--md-secondary-container);
  color: var(--md-on-secondary-container);
  box-shadow: inset 3px 0 0 var(--md-primary);
}

/* Navigation rail (admin, desktop) */
.m3-nav-rail {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 1035;
  width: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  background-color: var(--md-surface);
  overflow-y: auto;
  scrollbar-width: thin;
}
.m3-nav-rail-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 4px 0;
  color: var(--md-on-surface-variant);
  text-decoration: none;
  cursor: pointer;
}
.m3-nav-rail-item:hover { text-decoration: none; }
.m3-nav-rail-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 32px;
  border-radius: var(--md-shape-full);
  font-size: 24px;
  transition: background-color var(--md-duration-medium) var(--md-easing-standard);
}
.m3-nav-rail-item.active .m3-nav-rail-icon { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.m3-nav-rail-label { font: var(--md-label-medium); text-align: center; }

/* --------------------------------------------------------------------------
   Progress / loading
   -------------------------------------------------------------------------- */
.progress {
  display: flex;
  height: 4px;
  overflow: hidden;
  background-color: var(--md-surface-container-highest);
  border-radius: var(--md-shape-full);
}
.progress-bar {
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: var(--md-primary);
  transition: width var(--md-duration-long) var(--md-easing-standard);
}
.m3-linear-progress { position: relative; height: 4px; overflow: hidden; background-color: var(--md-surface-container-highest); border-radius: var(--md-shape-full); }
.m3-linear-progress::before {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  background-color: var(--md-primary);
  animation: m3-indeterminate 1.6s var(--md-easing-standard) infinite;
}
@keyframes m3-indeterminate {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

.spinner-border, .m3-circular-progress {
  display: inline-block;
  width: 24px;
  height: 24px;
  border: 3px solid color-mix(in srgb, var(--md-primary) 24%, transparent);
  border-right-color: var(--md-primary);
  border-radius: 50%;
  animation: m3-spin .8s linear infinite;
}
.spinner-border-sm { width: 16px; height: 16px; border-width: 2px; }
@keyframes m3-spin { to { transform: rotate(360deg); } }

/* Skeletons — used heavily by the SPA and admin while data loads. */
.skeleton, .admin-skeleton {
  position: relative;
  overflow: hidden;
  background-color: var(--md-surface-container-highest);
  border-radius: var(--md-shape-xs);
  color: transparent !important;
}
.skeleton::after, .admin-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--md-on-surface) 6%, transparent), transparent);
  animation: m3-shimmer 1.4s infinite;
}
@keyframes m3-shimmer { 100% { transform: translateX(100%); } }

/* --------------------------------------------------------------------------
   Avatar
   -------------------------------------------------------------------------- */
.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: var(--md-primary-container);
  color: var(--md-on-primary-container);
  font: var(--md-title-medium);
  flex-shrink: 0;
  overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-xs { width: 24px; height: 24px; font: var(--md-label-small); }
.avatar-sm { width: 32px; height: 32px; font: var(--md-label-medium); }
.avatar-lg { width: 56px; height: 56px; font: var(--md-headline-small); }
.avatar-xl { width: 72px; height: 72px; font: var(--md-headline-medium); }
.avatar-initial { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }

/* --------------------------------------------------------------------------
   Dropdown / menu — M3 menu
   -------------------------------------------------------------------------- */
.dropdown, .dropup { position: relative; }
.dropdown-menu {
  position: absolute;
  z-index: 1000;
  display: none;
  min-width: 112px;
  max-width: 280px;
  padding: 8px 0;
  margin: 4px 0 0;
  background-color: var(--md-surface-container);
  border: none;
  border-radius: var(--md-shape-xs);
  box-shadow: var(--md-elevation-2);
  list-style: none;
}
.dropdown-menu.show { display: block; }
.dropdown-menu-end { right: 0; left: auto; }
.dropdown-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 48px;
  padding: 12px 12px;
  border: 0;
  background: none;
  color: var(--md-on-surface);
  font: var(--md-label-large);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.dropdown-item:hover { text-decoration: none; }
.dropdown-item.active { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.dropdown-divider { height: 1px; margin: 8px 0; background-color: var(--md-outline-variant); border: 0; }
.dropdown-header { padding: 8px 12px; font: var(--md-title-small); color: var(--md-on-surface-variant); }

/* --------------------------------------------------------------------------
   Tooltip
   -------------------------------------------------------------------------- */
.m3-tooltip {
  position: absolute;
  z-index: 1080;
  padding: 4px 8px;
  border-radius: var(--md-shape-xs);
  background-color: var(--md-inverse-surface);
  color: var(--md-inverse-on-surface);
  font: var(--md-body-small);
  pointer-events: none;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Accordion
   -------------------------------------------------------------------------- */
.accordion { display: flex; flex-direction: column; gap: 8px; }
.accordion-item { background-color: var(--md-surface-container-low); border: none; border-radius: var(--md-shape-md); overflow: hidden; }
.accordion-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 56px;
  padding: 16px;
  border: 0;
  background: none;
  color: var(--md-on-surface);
  font: var(--md-title-medium);
  text-align: left;
  cursor: pointer;
}
.accordion-button::after {
  content: "";
  width: 24px; height: 24px; flex-shrink: 0;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  transition: transform var(--md-duration-medium) var(--md-easing-standard);
}
.accordion-button:not(.collapsed)::after { transform: rotate(180deg); }
.accordion-body { padding: 0 16px 16px; font: var(--md-body-medium); color: var(--md-on-surface-variant); }
.collapse:not(.show) { display: none; }
.collapsing { height: 0; overflow: hidden; transition: height var(--md-duration-long) var(--md-easing-standard); }

/* --------------------------------------------------------------------------
   Segmented button (M3) — used for filters
   -------------------------------------------------------------------------- */
.m3-segmented {
  display: inline-flex;
  border: 1px solid var(--md-outline);
  border-radius: var(--md-shape-full);
  overflow: hidden;
}
.m3-segmented-item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 12px;
  border: 0;
  border-right: 1px solid var(--md-outline);
  background: transparent;
  color: var(--md-on-surface);
  font: var(--md-label-large);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.m3-segmented-item:last-child { border-right: 0; }
.m3-segmented-item.active { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }

/* --------------------------------------------------------------------------
   Page scaffold
   -------------------------------------------------------------------------- */
.m3-page {
  padding: 16px;
  padding-bottom: calc(var(--md-nav-bar-height) + 24px);
  max-width: 1100px;
  margin: 0 auto;
}
.m3-section-title {
  font: var(--md-title-medium);
  color: var(--md-on-surface);
  margin: 24px 0 12px;
}
.m3-section-title:first-child { margin-top: 0; }

/* Icon fonts keep their metrics inside M3 components. */
.ti, .fas, .far, .fab, .material-symbols-outlined { line-height: 1; vertical-align: middle; }
.material-symbols-outlined {
  font-family: "Material Symbols Outlined";
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  display: inline-block;
  white-space: nowrap;
  direction: ltr;
  font-feature-settings: "liga";
  -webkit-font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
}
.material-symbols-outlined.m3-filled { font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 24; }
