:root {
  --teal:     #1D9E75;
  --teal-lt:  #E1F5EE;
  --teal-dk:  #085041;
  /* Hover shade for every filled brand button, and the two ends of the
     marketing banner gradient. Variables rather than literals because all four
     used to be hardcoded greens, which meant a site rebranded to another colour
     still had a green hero and went green under the pointer. Editable at
     /admin/settings — see domain.ThemeFields. */
  --teal-hover: #189265;
  --hero-from:  #085041;
  --hero-to:    #1A8E6E;
  --border:   #D3D1C7;
  --text:     #2C2C2A;
  --text-m:   #5F5E5A;
  --text-s:   #888780;
  --bg:       #F8F7F4;
  --white:    #FFFFFF;
  --error:    #993C1D;
  --error-lt: #FAECE7;
  --success:  #1D9E75;
  --info:     #185FA5;
  --info-lt:  #E6EFF8;
  /* Raised surfaces sitting on --bg: the calendar grid, the context panel,
     tab panels, the rail. Referenced since M10 but never defined, so every
     one of them rendered transparent until M27. */
  --card:     #FFFFFF;
  /* Amber — "someone is waiting on you", between --info and --error. Used by
     the dashboard rail and the scan-due calendar event type. */
  --warn:     #8A5A11;
  --warn-lt:  #FBF0DC;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

a { color: var(--teal); text-decoration: none; }
a:hover { text-decoration: underline; }

.bg-pattern {
  position: fixed;
  inset: 0;
  /* Tinted from the brand variables via color-mix rather than hardcoded rgba,
     so the ambient wash follows an admin-chosen palette (see domain/theme.go)
     instead of staying green behind, say, an orange site. */
  background-image:
    radial-gradient(circle at 20% 50%, color-mix(in srgb, var(--teal) 6%, transparent) 0%, transparent 60%),
    radial-gradient(circle at 80% 20%, color-mix(in srgb, var(--info) 5%, transparent) 0%, transparent 50%);
  pointer-events: none;
  z-index: 0;
}

.page {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px 16px;
  position: relative;
  z-index: 1;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 40px;
}
/* In the portal top bar the brand is a link to the home page. It should look
   exactly as it did as a plain block: no underline on hover, and a visible
   focus ring for keyboard users, since it is now a tab stop. */
a.brand, a.brand:hover { text-decoration: none; color: inherit; }
a.brand:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; border-radius: 6px; }
.brand-mark {
  width: 42px;
  height: 42px;
  background: var(--teal);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.5px;
}
/* Brand mark carrying an admin-set logo (views/layouts/brandmark.templ).
   Both mark classes are fixed squares sized for a two-letter monogram. A real
   logo is horizontal artwork that usually incorporates the organisation's
   name, so it gets the room the wordmark used to take: the layouts hide their
   own text when a logo is set, and these rules let the mark grow sideways
   instead of being boxed into a square.

   The chip — the brand-coloured ground with rounded corners — is kept only
   where it earns its place. A "reversed" (white) logo is invisible on the
   white dashboard topnav and the light login page, so those keep it. The
   marketing nav is already --teal-dk, where a chip is just a box drawn around
   artwork that does not need one. */
.brand-mark-logo {
  width: auto;
  height: auto;
  padding: 0;
  background: none;
  border-radius: 0;
}
.brand-mark-logo img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  object-fit: contain;
}

/* Marketing nav: dark ground already, so the logo sits bare and large. The
   nav grows with it rather than clipping — the height was fixed at 60px for a
   34px monogram.

   Written as a two-class selector, not as `.brand-mark-logo` alone: the
   `.mkt-brand-mark` rules sit ~2300 lines further down this file, so at equal
   specificity they would win on source order and put the chip back. */
.mkt-brand-mark.brand-mark-logo {
  width: auto;
  height: auto;
  padding: 0;
  background: none;
  border-radius: 0;
}
.mkt-brand-mark.brand-mark-logo img { max-height: 44px; }

/* Dashboard topnav and login page: light grounds, so the chip stays. It is
   sized to the logo now rather than to a monogram, with generous horizontal
   padding so the artwork is not cramped against the corners. */
.topnav .brand-mark-logo,
.page .brand-mark-logo {
  background: var(--teal-dk);
  border-radius: 10px;
  padding: 6px 14px;
  display: flex;
  align-items: center;
}
.topnav .brand-mark-logo img { max-height: 34px; max-width: 260px; }
.page .brand-mark-logo img { max-height: 46px; max-width: 320px; }

/* The logo incorporates the organisation's name, so the layouts' own wordmark
   would say it twice and leave the logo squeezed into what is left over.

   Hidden here rather than omitted from the markup, which makes
   `brand-mark-logo` the single switch for the whole treatment: the fallback in
   base.templ removes that class when the image fails to load, and the monogram
   and the wordmark return together. Omitting the text server-side instead
   would leave a broken logo showing a bare "EP" with no name at all. */
.brand-mark-logo + .mkt-brand-name,
.brand-mark-logo + .brand-text .brand-name { display: none; }

/* With the wordmark hidden the portal label is the only text left beside the
   mark, and 11px uppercase reads as a stray caption next to a 44px logo. */
.brand-mark-logo + .brand-text .brand-sub { font-size: 13px; letter-spacing: 0.06em; }

.brand-text { display: flex; flex-direction: column; }
.brand-name { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.2; }
.brand-sub  { font-size: 11px; color: var(--text-s); letter-spacing: 0.04em; text-transform: uppercase; }

.card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 40px;
  width: 100%;
  max-width: 400px;
  box-shadow: 0 2px 16px rgba(0,0,0,0.06), 0 0 0 1px rgba(0,0,0,0.03);
}

.card-title { font-size: 20px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.card-sub   { font-size: 14px; color: var(--text-m); margin-bottom: 28px; }

.form-group { margin-bottom: 18px; }
label { display: block; font-size: 13px; font-weight: 500; color: var(--text); margin-bottom: 6px; }

input[type="email"],
input[type="password"],
input[type="text"] {
  width: 100%;
  padding: 10px 13px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  color: var(--text);
  background: var(--white);
  outline: none;
  transition: border-color 0.15s;
  font-family: inherit;
}
input:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--teal) 12%, transparent);
}
input::placeholder { color: var(--text-s); }

/* A textarea inside a card form (the access-request form on the sign-in page)
   matches the inputs around it. Scoped to .form-group so the dashboard's
   .form-grid textareas, styled further down, are left alone. */
.form-group textarea {
  width: 100%;
  padding: 10px 13px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--text);
  background: var(--white);
  outline: none;
  transition: border-color 0.15s;
  font-family: inherit;
  min-height: 84px;
  resize: vertical;
}
.form-group textarea:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--teal) 12%, transparent);
}
.form-group textarea::placeholder { color: var(--text-s); }

.form-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 22px;
}
.remember { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-m); cursor: pointer; }
.remember input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--teal); cursor: pointer; }
.forgot { font-size: 13px; color: var(--teal); font-weight: 500; }

.btn-primary {
  /* Natural-width by default; use .btn-block for full-width (login form etc). */
  display: inline-block;
  padding: 9px 16px;
  background: var(--teal);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  transition: background 0.15s;
}
.btn-primary:hover { background: var(--teal-hover); text-decoration: none; }
.btn-primary:disabled { background: var(--text-s); cursor: not-allowed; }

.btn-block { width: 100%; padding: 11px; }

.footer { padding: 24px; text-align: center; font-size: 12px; color: var(--text-s); }
.footer a { color: var(--text-s); }

.locations {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-bottom: 32px;
  flex-wrap: wrap;
}
.loc-badge {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-s);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 4px 12px;
}
.loc-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--teal); }

.flash {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 13px;
  margin-bottom: 18px;
  transition: opacity 700ms ease, transform 700ms ease;
}
.flash-msg { flex: 1; }
.flash-close {
  background: transparent;
  border: 0;
  color: inherit;
  opacity: 0.55;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  font-family: inherit;
}
.flash-close:hover { opacity: 1; }
.flash-fade { opacity: 0; transform: translateY(-4px); }
.flash-error   { background: var(--error-lt); color: var(--error); border: 1px solid #f1c5b5; }
.flash-success { background: var(--teal-lt);  color: var(--teal-dk); border: 1px solid #b8e0d0; }
.flash-info    { background: #E7F0FB; color: var(--info); border: 1px solid #c5d8ef; }

.muted { color: var(--text-m); font-size: 13px; }
.text-error { color: var(--error); }

/* Honeypot spam-trap field (Tier 1 mitigation, see docs/security.md
   discussion) — off-screen for real users; tabindex="-1" and aria-hidden on
   the field itself keep it out of tab order / screen readers too. Bots that
   auto-fill every input still see and fill it since it's real markup, not
   display:none (some bots skip those). */
.hp-field {
  position: absolute;
  left: -9999px;
  top: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ────────── Dashboard layout ────────── */

:root {
  --sidebar-w: 220px;
  --topnav-h:  56px;
}

body.dashboard-body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
  font-size: 14px;
}

.topnav {
  height: var(--topnav-h);
  background: var(--white);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 16px;
  flex-shrink: 0;
  z-index: 10;
}

/* The 32px square is the "EP" monogram fallback only. A real logo carries the
   brand-mark-logo class and must keep its own auto width/height (set on
   .brand-mark-logo) so a horizontal logo isn't crushed into a square — hence
   :not(.brand-mark-logo), which also outranks that rule on source order. */
.topnav .brand-mark:not(.brand-mark-logo) {
  width: 32px;
  height: 32px;
  border-radius: 7px;
  font-size: 13px;
}
/* .brand carries a 40px bottom margin for the stacked login/marketing layouts.
   In the topnav's centred flex row that margin is added to the item's box and
   shifts the logo up out of the 56px bar, where body{overflow:hidden} clips its
   top edge. The bar spaces itself, so the brand needs no margin here. */
.topnav .brand { margin-bottom: 0; }

.topnav-spacer { flex: 1; }

.notif-bell {
  position: relative;
}
.notif-bell-btn {
  position: relative;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 18px;
  padding: 4px 8px;
  line-height: 1;
}
.notif-badge {
  position: absolute;
  top: 0;
  right: 2px;
  background: var(--error);
  color: var(--white);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 8px;
  min-width: 14px;
  text-align: center;
}
.notif-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 320px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  z-index: 20;
}
.notif-dropdown-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.notif-mark-all {
  background: none;
  border: none;
  color: var(--info);
  font-size: 12px;
  cursor: pointer;
}
.notif-empty {
  padding: 20px 14px;
  color: var(--text-s);
  font-size: 13px;
  text-align: center;
}
.notif-list { list-style: none; }
.notif-item {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--border);
}
.notif-item:last-child { border-bottom: none; }
.notif-item-unread { background: var(--teal-lt); }
.notif-item-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 14px;
  text-decoration: none;
  color: var(--text);
}
.notif-item-title { font-size: 13px; font-weight: 600; }
.notif-item-text { font-size: 12px; color: var(--text-m); }
.notif-item-time { font-size: 11px; color: var(--text-s); }
.notif-item-mark {
  background: none;
  border: none;
  color: var(--teal-dk);
  cursor: pointer;
  padding: 0 12px;
  font-size: 13px;
}

.topnav-user {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-m);
}
.topnav-user .role-pill {
  background: var(--teal-lt);
  color: var(--teal-dk);
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* The "viewing as" control replaces the role pill for anyone holding more than
   one role. Styled as a pill rather than a form field on purpose: it sits where
   the pill sat and says the same thing, and looking like a settings control
   would oversell what it does — it changes the view, never the permissions. */
.view-switcher {
  display: flex;
  align-items: center;
  gap: 6px;
}
.view-switcher-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
}
.view-switcher-select {
  background: var(--teal-lt);
  color: var(--teal-dk);
  border: 1px solid transparent;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.view-switcher-select:hover { border-color: var(--teal-dk); }
.view-switcher-select:focus-visible { outline: 2px solid var(--teal-dk); outline-offset: 1px; }
.view-switcher-go {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid var(--teal-dk);
  background: none;
  color: var(--teal-dk);
  cursor: pointer;
}

/* On a phone the label is the first thing to go — the select still carries the
   role name, which is the part that matters. */
@media (max-width: 640px) {
  .view-switcher-label { display: none; }
}

.topnav-user a {
  color: var(--text-m);
  font-size: 13px;
}
/* A long name shouldn't push Sign out off the bar — truncate instead. */
.topnav-user-name {
  color: var(--text);
  font-weight: 500;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shell {
  display: flex;
  flex: 1;
  min-height: 0;
}

.sidebar {
  width: var(--sidebar-w);
  background: var(--white);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  overflow-y: auto;
  padding: 12px 0;
}
.nav-section { padding: 4px 12px; margin-top: 8px; margin-bottom: 2px; }
.nav-section-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-s);
  padding: 0 4px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-m);
  cursor: pointer;
  text-decoration: none;
  margin: 1px 8px;
  border-radius: 7px;
}
.nav-item:hover { background: var(--bg); color: var(--text); text-decoration: none; }
.nav-item.active { background: var(--teal-lt); color: var(--teal-dk); }
.nav-icon {
  width: 18px;
  text-align: center;
  flex-shrink: 0;
  color: var(--text-s);
}
.nav-item.active .nav-icon { color: var(--teal); }

.sidebar-footer {
  margin-top: auto;
  padding: 12px 8px;
  border-top: 1px solid var(--border);
}

/* Off-canvas toggle (M22.3) — hidden on desktop; shown + wired up only
   inside the ≤768px block below. Kept here so both the button and the
   backdrop it drives are defined next to the sidebar they control. */
.sidebar-toggle {
  display: none;
  background: none;
  border: none;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
  color: var(--text);
}
.sidebar-backdrop {
  display: none;
}

.main {
  flex: 1;
  /* A flex item defaults to min-width:auto, so .main refuses to shrink below
     its widest child — one long filename in a returned-form link is enough to
     push the whole page wider than a phone screen and cut it off. min-width:0
     lets it shrink so its own overflow-wrap can do its job. */
  min-width: 0;
  overflow-y: auto;
  padding: 24px;
  background: var(--bg);
}
.main h1 {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 8px;
}

/* ── Dashboard shell, mobile (M22.3) ──────────────────────────────────────── */
/* The sidebar is a fixed 220px column at every width above this — on a
   375px phone that alone would consume ~59% of the viewport, before .main
   even gets a look-in. Below 768px it becomes an off-canvas drawer:
   position:fixed + translateX, toggled by the hamburger button in
   dashboardTopNav (views/layouts/dashboard.templ) flipping a class on
   <body> — plain onclick attributes, no JS file needed for two lines. */
@media (max-width: 768px) {
  .sidebar-toggle { display: block; }
  .sidebar {
    position: fixed;
    top: var(--topnav-h);
    bottom: 0;
    left: 0;
    z-index: 30;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.15);
  }
  body.sidebar-open .sidebar { transform: translateX(0); }
  body.sidebar-open .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: var(--topnav-h) 0 0 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 29;
  }
  .main { padding: 16px; }
}

.context-panel {
  width: 320px;
  background: var(--white);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  padding: 20px;
  flex-shrink: 0;
}

.empty-state {
  border: 1px dashed var(--border);
  border-radius: 12px;
  padding: 40px;
  text-align: center;
  color: var(--text-m);
  background: var(--white);
}
.empty-state h2 { font-size: 16px; margin-bottom: 6px; color: var(--text); }
.empty-state p { font-size: 13px; }

/* ────────── Card grid + settings forms ────────── */

.section-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  margin-bottom: 16px;
  max-width: 720px;
}
.section-card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 4px;
}
.section-card-sub {
  font-size: 13px;
  color: var(--text-m);
  margin-bottom: 20px;
}

/* ── Dashboard panels ─────────────────────────────────────────────────────── */
/* The guardian dashboard's three summaries. A figure, a caption and sometimes
   a note: the answer first, then what it is an answer to. */
.panel-figure {
  font-size: 28px;
  font-weight: 600;
  color: var(--teal-dk);
  line-height: 1.1;
  margin-bottom: 2px;
}
/* The caption sits directly under its figure, so it loses the gap that
   .section-card-sub carries when it follows a title instead. */
.panel-figure + .section-card-sub { margin-bottom: 12px; }
.panel-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-m);
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.panel-list { list-style: none; margin: 0; padding: 0; }
.panel-list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.panel-list li:last-child { border-bottom: none; }
.panel-list li:first-child { padding-top: 0; }
.panel-list-main { font-size: 14px; font-weight: 500; color: var(--text); }
.panel-list-sub { font-size: 13px; color: var(--text-m); white-space: nowrap; }

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 18px;
}
.form-grid .full { grid-column: span 2; }
@media (max-width: 480px) {
  /* Two cramped ~150px fields side by side isn't a broken layout (minmax(0,
     1fr) already prevents overflow), but a single touch-friendly column
     reads much better on a phone. */
  .form-grid { grid-template-columns: 1fr; }
  .form-grid .full { grid-column: span 1; }
}
.form-grid label { margin-bottom: 4px; }
.form-grid input,
.form-grid textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 13px;
  font-family: inherit;
  background: var(--white);
}
.form-grid textarea { min-height: 80px; resize: vertical; }
.form-grid input:focus,
.form-grid textarea:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--teal) 12%, transparent);
  outline: none;
}

.form-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.btn-secondary {
  display: inline-block;
  padding: 9px 14px;
  background: var(--white);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
}
.btn-secondary:hover { background: var(--bg); text-decoration: none; }
.btn-danger {
  padding: 7px 12px;
  background: var(--white);
  color: var(--error);
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
}
.btn-danger:hover { background: var(--error-lt); border-color: #f1c5b5; }

.location-list { list-style: none; }
.location-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.location-row:last-child { border-bottom: none; }
.location-meta {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
.location-name { font-size: 14px; font-weight: 500; color: var(--text); }
.location-sub  { font-size: 12px; color: var(--text-s); }
.status-pill {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: 10px;
}
.status-pill.active   { background: var(--teal-lt);  color: var(--teal-dk); }
.status-pill.inactive { background: #EEE; color: var(--text-s); }
.status-pill.invited  { background: #FFF3CD; color: #6B4F00; }
/* Two timetable rules on one slot silently double a squad's sessions, so the
   marker is a warning colour rather than a neutral tag. */
.status-pill-error    { background: var(--error-lt); color: var(--error); }
.status-pill.archived { background: #EEE; color: var(--text-s); }

/* ────────── Page header (M7) ────────── */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.page-header h1 { margin-bottom: 4px; }

/* ────────── Filter bar (M7) ────────── */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.filter-bar input[type=search] {
  flex: 1;
  height: 36px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 14px;
}
.checkbox-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-s);
}

/* ────────── Athlete card + grid (M7) ────────── */
.athlete-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
.athlete-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  transition: background 0.1s, border-color 0.1s;
}
.athlete-card:hover {
  background: var(--bg);
  border-color: var(--teal);
  text-decoration: none;
}
.athlete-card-body {
  flex: 1;
  min-width: 0;
}
.athlete-card-name { font-weight: 500; font-size: 14px; color: var(--text); }
.athlete-card-meta { font-size: 12px; color: var(--text-s); margin-top: 2px; }
.athlete-card-tags { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.athlete-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg);
  flex-shrink: 0;
}
.athlete-avatar-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 14px;
  color: var(--teal-dk);
  background: var(--teal-lt);
}

/* ────────── Squad tier badges (M7) ────────── */
.squad-badge {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: 10px;
}
.squad-badge-gold   { background: #FFE8A3; color: #6B4F00; }
.squad-badge-silver { background: #E5E7EB; color: #374151; }
.squad-badge-bronze { background: #F3C8A9; color: #6B3814; }

/* ────────── Profile page (M7) ────────── */
.profile-header {
  display: flex;
  gap: 24px;
  align-items: center;
  flex-wrap: wrap;
}
.profile-meta {
  font-size: 13px;
  color: var(--text-s);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.profile-meta span.muted { color: var(--text-s); }

/* ────────── Tab bar (M7 stub) ────────── */
.profile-tabs { margin-top: 16px; }
.tab-bar {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}
.tab {
  background: none;
  border: none;
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text-s);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.tab-active { color: var(--teal-dk); border-bottom-color: var(--teal); font-weight: 500; }
.tab-disabled { opacity: 0.5; cursor: not-allowed; }
.tab-panel { background: var(--card); }

/* ────────── Alerts (M7) ────────── */
.alert {
  padding: 10px 12px;
  border-radius: 6px;
  font-size: 13px;
}
.alert-info { background: #E0F2FE; color: #075985; border: 1px solid #BAE6FD; }

/* ────────── Guardian list (M8) ────────── */
.guardian-list { list-style: none; margin: 0 0 0; padding: 0; }
.guardian-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.guardian-row:last-child { border-bottom: none; }
.guardian-meta { display: flex; flex-direction: column; }
.guardian-name { font-size: 14px; font-weight: 500; color: var(--text); }
.guardian-sub  { font-size: 12px; color: var(--text-s); }

.form-grid select {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 14px;
  background: var(--card);
}
.form-grid select:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px var(--teal-lt);
}

/* ────────── Guardian dashboard (M8) ────────── */
.athlete-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}
.athlete-switcher-chip {
  padding: 6px 14px;
  border-radius: 16px;
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--text-s);
  font-size: 13px;
  text-decoration: none;
}
.athlete-switcher-chip:hover {
  background: var(--bg);
  text-decoration: none;
}
.athlete-switcher-chip-active {
  background: var(--teal-lt);
  border-color: var(--teal);
  color: var(--teal-dk);
  font-weight: 500;
}
.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-top: 16px;
}

/* ────────── Squads (M9) ────────── */
.squad-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}
.squad-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.1s;
}
.squad-card:hover {
  border-color: var(--teal);
  text-decoration: none;
}
.squad-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.squad-card-title { font-size: 15px; font-weight: 600; color: var(--text); }
.squad-card-meta { font-size: 12px; color: var(--text-s); display: flex; gap: 6px; align-items: center; }

.capacity-wrap { display: flex; flex-direction: column; gap: 6px; }
.capacity-meter {
  height: 10px;
  background: var(--bg);
  border-radius: 6px;
  overflow: hidden;
}
.capacity-fill {
  height: 100%;
  background: var(--teal);
  transition: width 0.2s;
}
.capacity-label { font-size: 12px; color: var(--text-s); }

.roster-list, .rule-list, .waitlist-list { list-style: none; margin: 0; padding: 0; }
.roster-row, .rule-row, .waitlist-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.roster-row:last-child, .rule-row:last-child, .waitlist-row:last-child { border-bottom: none; }
.roster-meta, .rule-meta, .waitlist-meta { display: flex; flex-direction: column; flex: 1; }
.roster-name, .rule-day, .waitlist-name { font-size: 14px; font-weight: 500; color: var(--text); }
.roster-sub, .rule-sub, .waitlist-sub { font-size: 12px; color: var(--text-s); }
.rule-time { font-size: 13px; color: var(--text); }
.waitlist-notes { font-size: 12px; color: var(--text); font-style: italic; margin-top: 4px; }
.waitlist-position {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--teal-lt);
  color: var(--teal-dk);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 13px;
}

/* Tab links (anchor variant so non-Alpine tabs work) */
a.tab { text-decoration: none; }
a.tab:hover { color: var(--teal-dk); text-decoration: none; }

/* ────────── Calendar (M10) ────────── */
.cal-layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 16px;
  align-items: flex-start;
}
/* .cal-main scrolls the 7-day grid horizontally rather than crushing every
   column illegibly — a fixed min-width on .cal-grid (below) is what makes
   there be something to scroll. Same ≤768px breakpoint as the sidebar. */
.cal-main { min-width: 0; overflow-x: auto; }
@media (max-width: 768px) {
  .cal-layout { grid-template-columns: 1fr; }
}
.cal-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  /* Wraps because the All/My events toggle makes five controls, which is one
     too many for a phone-width row. */
  flex-wrap: wrap;
}
.cal-week-label { flex: 1; text-align: center; font-weight: 500; color: var(--text); }

/* All events / My events. A joined pair rather than two loose buttons, so
   "which half of the club am I looking at" reads without comparing labels. */
.cal-scope {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 7px;
  overflow: hidden;
}
.cal-scope-opt {
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-m);
  background: var(--white);
  text-decoration: none;
  white-space: nowrap;
}
.cal-scope-opt + .cal-scope-opt { border-left: 1px solid var(--border); }
.cal-scope-opt:hover { background: var(--bg); text-decoration: none; }
.cal-scope-opt.is-on { background: var(--teal); color: var(--white); }
.cal-scope-opt.is-on:hover { background: var(--teal-hover); }

.cal-grid {
  display: grid;
  grid-template-columns: 60px repeat(7, 1fr);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  min-width: 700px;
}
.cal-time-col, .cal-day-col {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
}
.cal-day-col:last-child { border-right: none; }
.cal-time-col { background: var(--bg); }

.cal-day-head {
  height: 44px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4px;
  background: var(--bg);
}
.cal-day-name { font-size: 11px; text-transform: uppercase; color: var(--text-s); letter-spacing: 0.04em; }
.cal-day-num { font-size: 13px; font-weight: 500; }

.cal-slot {
  height: 22px;
  border-bottom: 1px dashed transparent;
  position: relative;
}
.cal-slot-hour { border-bottom-color: var(--border); }
.cal-time-label {
  position: absolute;
  top: -6px;
  right: 6px;
  font-size: 10px;
  color: var(--text-s);
}

.cal-day-body { position: relative; flex: 1; }

.cal-event {
  position: absolute;
  /* left/width come from the inline style: overlapping events are laid out in
     lanes server-side, so a block only spans the column when nothing collides
     with it. Do not reinstate left/right here — it would flatten every lane
     back on top of its neighbours. */
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 11px;
  line-height: 1.2;
  color: white;
  text-decoration: none;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
  cursor: pointer;
  box-sizing: border-box;
}
.cal-event:hover { text-decoration: none; opacity: 0.92; }
.cal-event-time { font-weight: 600; font-size: 10px; opacity: 0.85; }
.cal-event-title {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-event-sub {
  opacity: 0.85;
  font-size: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Density: a three-lane cluster leaves roughly 58px of column per block, so
   the block sheds detail rather than overflowing. The side panel still has
   everything, and the tooltip carries the full time range and title. */
.cal-event-compact { padding: 3px 5px; }
.cal-event-compact .cal-event-sub { display: none; }
.cal-event-dense { padding: 2px 4px; font-size: 10px; }
.cal-event-dense .cal-event-sub,
.cal-event-dense .cal-event-time { display: none; }
.cal-event-dense .cal-event-badge { display: none; }

/* Split squad card: pool centre on top, squad tier underneath. The two bands
   ARE the card — no padding on the anchor itself, or a strip of the old
   background shows between them and the split stops reading as one object.
   Colours and their contrast-checked foregrounds come from the inline style
   (calendar_colour.go), not from here. */
.cal-event-split { padding: 0; display: flex; flex-direction: column; }
.cal-band {
  flex: 1 1 50%;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 1px 5px;
  overflow: hidden;
}
.cal-band-label {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-band-top { font-size: 10px; }
.cal-band-top .cal-event-time { font-weight: 700; opacity: 0.9; flex: none; }
.cal-band-bottom { font-size: 11px; }
/* Density applies to the bands too: at three lanes the time disappears and
   both halves shrink to a single truncated word each. */
.cal-event-dense .cal-band { padding: 0 3px; font-size: 10px; }
.cal-event-dense .cal-band-top .cal-event-time { display: none; }
.cal-event-compact .cal-band { padding: 1px 4px; }

/* An event running outside 04:00–22:00 is clamped to the edge of the grid.
   The dashed edge says "continues beyond here" so a 02:00 session cannot be
   mistaken for one starting at 04:00. */
.cal-event-clip-start {
  border-top: 2px dashed rgba(255,255,255,0.75);
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.cal-event-clip-end {
  border-bottom: 2px dashed rgba(255,255,255,0.75);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.cal-event-badge {
  display: inline-block;
  background: rgba(255,255,255,0.25);
  border-radius: 2px;
  padding: 0 4px;
  margin-left: 4px;
  font-size: 9px;
  font-weight: 700;
}

.cal-event-squad { background: #3B82F6; }
.cal-event-private { background: #8B5CF6; }
.cal-event-clinic { background: #14B8A6; }
.cal-event-nutrition { background: #F59E0B; }
.cal-event-scan_due { background: #D97706; }
.cal-event-billing_due { background: #EF4444; }
.cal-event-google_cal { background: #6B7280; }
.cal-event-bookable { background: #10B981; border: 1px dashed rgba(255,255,255,0.6); }

/* Session panel: recurrence controls. The repeat form and the series notice
   sit below the main actions, separated by a rule so they read as "what
   happens to this session going forward" rather than another action button. */
.cal-panel-repeat,
.cal-panel-series {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.cal-panel-repeat label { font-size: 13px; font-weight: 600; }
.cal-panel-repeat select { flex: 0 1 auto; }
.cal-panel-repeat small,
.cal-panel-series p { flex: 1 0 100%; margin: 0; }

/* Booking panel slot grid */
.booking-form { display: flex; flex-direction: column; gap: 8px; }
.booking-slots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.booking-slot {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; padding: 6px 10px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--card); color: var(--text);
}
.booking-slot-open { cursor: pointer; border-color: #10B981; color: #0f7a55; font-weight: 600; }
.booking-slot-open:hover { background: #10B981; color: #fff; }
.booking-slot-taken { opacity: 0.6; }

.cal-panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  min-height: 200px;
}
.cal-panel-inner { padding: 14px; }
.cal-panel-head { display: flex; gap: 8px; margin-bottom: 8px; }
.cal-panel-section { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); }
.cal-panel-section-title { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-s); margin-bottom: 6px; }
.cal-panel-actions { display: flex; gap: 8px; margin-top: 16px; }

.session-meta { display: grid; grid-template-columns: 70px 1fr; gap: 6px 12px; margin-top: 8px; font-size: 13px; }
.session-meta dt { color: var(--text-s); }
.session-meta dd { color: var(--text); margin: 0; }

.session-pill {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--teal-lt);
  color: var(--teal-dk);
}
.session-pill-squad { background: #DBEAFE; color: #1E40AF; }
.session-pill-private { background: #EDE9FE; color: #5B21B6; }
.session-pill-clinic { background: #CCFBF1; color: #115E59; }

/* ────────── Library (M11) ────────── */

/* Page header actions cluster (multiple buttons on the right). */
.page-header-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

/* Filter card variant: vertical stacking, no flex collapse. */
.library-filter { margin-bottom: 16px; }
.library-filter .form-grid { display: grid; gap: 12px; }

/* Tag chip group used for both filter selection and form input. */
.tag-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #FFF;
  color: var(--text);
  font-size: 12px;
  cursor: pointer;
  user-select: none;
}
.tag-chip:hover { border-color: var(--teal-dk); }
.tag-chip input { display: none; }
.tag-chip-on {
  background: var(--teal-lt);
  border-color: var(--teal-dk);
  color: var(--teal-dk);
  font-weight: 600;
}

/* Tag pills are read-only versions used on cards / detail pages. */
.tag-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.03em;
  background: #EEF2FF;
  color: #3730A3;
  margin: 2px;
}
.tag-pill.tag-stroke { background: #DBEAFE; color: #1E40AF; }
.tag-pill.tag-level  { background: #DCFCE7; color: #166534; }
.tag-pill.tag-focus  { background: #FEF3C7; color: #92400E; }

.status-pill.library-type {
  background: #E0E7FF;
  color: #3730A3;
}

/* Library card grid mirrors squad-grid. */
.library-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
.library-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  text-decoration: none;
  transition: border-color 120ms ease;
}
.library-card:hover { border-color: var(--teal-dk); }
.library-card-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}
.library-card-title { font-size: 15px; font-weight: 600; color: var(--text); }
.library-card-pills { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
.library-card-desc { font-size: 13px; color: var(--text-s); margin: 0; }
.library-card-tags { display: flex; flex-wrap: wrap; gap: 4px; }

/* Plan composer + version chain (M23 Phase 2). */
.module-chain { list-style: decimal inside; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.module-chain-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--bg-subtle, #f6f7f9); border-radius: 8px; }
.module-chain-name { font-weight: 600; }
.library-picker-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border-bottom: 1px solid var(--border, #e5e7eb); }
.library-picker-row > div { display: flex; align-items: center; gap: 8px; }

/* Detail page row layout. */
.library-detail-row {
  display: grid;
  grid-template-columns: 70px 1fr;
  align-items: center;
  gap: 12px;
  padding: 6px 0;
}
.library-detail-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
}
.library-detail-value { display: flex; flex-wrap: wrap; gap: 4px; }
.library-detail-prose { font-size: 14px; line-height: 1.55; color: var(--text); margin: 0; white-space: pre-wrap; }

/* Picker modal (consumed by M12 plan builder). */
.library-picker { display: flex; flex-direction: column; gap: 12px; max-height: 70vh; }
.library-picker-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.library-picker-filter input[type=search],
.library-picker-filter select {
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
}
.library-picker-results {
  flex: 1;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
}
.library-picker-list { list-style: none; margin: 0; padding: 0; }
.library-picker-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.library-picker-row:last-child { border-bottom: 0; }
.library-picker-row-main { flex: 1; min-width: 0; }
.library-picker-row-title { font-size: 14px; font-weight: 600; color: var(--text); }
.library-picker-row-meta { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.library-picker-add { padding: 4px 12px; font-size: 12px; }
.library-picker-empty { padding: 24px; text-align: center; }

/* Calendar polish (post-M10) */
.cal-day-today { background: rgba(31, 168, 111, 0.04); }
.cal-day-today .cal-day-head {
  background: var(--teal-lt);
  color: var(--teal-dk);
}
.cal-day-today .cal-day-name,
.cal-day-today .cal-day-num { color: var(--teal-dk); }
.cal-now {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--teal-dk);
  z-index: 2;
  pointer-events: none;
}
.cal-now::before {
  content: '';
  position: absolute;
  left: -3px;
  top: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--teal-dk);
}
.cal-empty {
  padding: 12px;
  text-align: center;
  font-size: 13px;
}

.cal-empty-state {
  text-align: center;
  padding: 32px 16px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: var(--white);
  margin-top: 16px;
}
.cal-empty-state h3 { font-size: 14px; font-weight: 600; margin-bottom: 6px; color: var(--text); }
.cal-empty-state p { margin-bottom: 12px; }
.cal-panel-hint { padding: 12px; }

/* ────────── Dashboard rail (M27) ────────── */
/* The right-hand column of /calendar: the event context panel on top, the
   activity rail underneath. Stacked rather than side-by-side so clicking an
   event pushes the rail down instead of replacing it. */
.cal-side {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}
/* Until a session is clicked the panel holds nothing but a hint, so it drops
   its card treatment and lets the rail lead the column. Browsers without
   :has() simply keep the card, which is how it looked before M27. */
.cal-panel:has(> .cal-panel-hint) {
  background: none;
  border-style: dashed;
  min-height: 0;
}

.rail { display: flex; flex-direction: column; gap: 12px; }

.rail-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
}
.rail-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.rail-card-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-s);
  margin: 0;
}
.rail-count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 10px;
  background: var(--bg);
  color: var(--text-m);
}
.rail-count-info   { background: var(--teal-lt);  color: var(--teal-dk); }
.rail-count-warn   { background: var(--warn-lt);  color: var(--warn); }
.rail-count-danger { background: var(--error-lt); color: var(--error); }

.rail-empty {
  font-size: 12px;
  color: var(--text-s);
  padding: 2px 0 4px;
}

.rail-list { list-style: none; display: flex; flex-direction: column; gap: 2px; }

/* Each row carries a colour only on its left edge — enough to group the
   sources inside a mixed card without turning the rail into a traffic light. */
.rail-item {
  border-left: 2px solid var(--border);
  border-radius: 0 4px 4px 0;
}
.rail-item-info   { border-left-color: var(--info); }
.rail-item-warn   { border-left-color: var(--warn); }
.rail-item-danger { border-left-color: var(--error); }

.rail-item-body {
  display: block;
  padding: 6px 8px;
  color: inherit;
  text-decoration: none;
  border-radius: 0 4px 4px 0;
}
a.rail-item-body:hover { background: var(--bg); text-decoration: none; }
a.rail-item-body:hover .rail-item-title { color: var(--teal-dk); }

.rail-item-title {
  display: block;
  font-size: 13px;
  line-height: 1.35;
  color: var(--text);
}
.rail-item-meta {
  display: block;
  font-size: 11px;
  color: var(--text-s);
  margin-top: 1px;
}

.rail-more {
  font-size: 11px;
  color: var(--text-s);
  padding: 6px 8px 0;
}

/* On a phone the rail is the least urgent thing on the page — the week grid
   and any open event come first, so it falls to the bottom of the stack. */
@media (max-width: 768px) {
  .cal-side { gap: 12px; }
}

/* ────────── Personal bests (M30.1) ────────── */
.pb-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.pb-table th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  font-weight: 600;
  padding: 8px 12px;
  border-bottom: 2px solid var(--border);
}
.pb-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
.pb-table tr:last-child td, .pb-table tr:last-child th { border-bottom: none; }
.pb-table .pb-time-col { text-align: right; }
.pb-event {
  font-weight: 500;
  color: var(--text);
  font-size: 14px;
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.pb-time { text-align: right; }
/* Times line up for comparison down the column, which is the whole point of
   the grid — proportional digits would make 1:02.95 and 1:11.10 ragged. */
.pb-time-value {
  display: block;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  font-weight: 600;
  color: var(--text);
}
.pb-time-meta { display: block; font-size: 11px; color: var(--text-s); margin-top: 1px; }
.pb-time-empty { color: var(--text-s); }

.pb-group { padding: 12px 0; border-bottom: 1px solid var(--border); }
.pb-group:last-child { border-bottom: none; padding-bottom: 0; }
.pb-group:first-of-type { padding-top: 4px; }
.pb-group-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.pb-group-title { font-size: 14px; font-weight: 600; margin: 0; color: var(--text); }
.pb-group-course {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--teal-dk);
  background: var(--teal-lt);
  padding: 2px 8px;
  border-radius: 10px;
}
.pb-group-best {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--teal-dk);
}

.pb-swims { list-style: none; display: flex; flex-direction: column; gap: 2px; }
/* Time, date and meet as a grid so the columns align across every swim in the
   group; the delete action is pushed to the end of the row. */
.pb-swim {
  display: grid;
  grid-template-columns: 90px 110px 1fr auto;
  gap: 4px 12px;
  align-items: baseline;
  padding: 5px 8px;
  border-radius: 4px;
  font-size: 13px;
}
.pb-swim:hover { background: var(--bg); }
.pb-swim-best { background: var(--teal-lt); }
.pb-swim-best:hover { background: var(--teal-lt); }
.pb-swim-time { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text); }
.pb-swim-star { color: var(--teal); margin-left: 4px; }
.pb-swim-date { color: var(--text-m); }
.pb-swim-meet { color: var(--text-m); min-width: 0; overflow-wrap: anywhere; }
/* Notes sit under the first three columns rather than adding a fifth. */
.pb-swim-notes { grid-column: 1 / -1; font-size: 12px; }
.pb-swim-delete { justify-self: end; }

/* On a phone the four-column row is too tight — stack the meet and notes
   under the time and date instead of crushing every column. */
@media (max-width: 600px) {
  .pb-swim { grid-template-columns: 80px 1fr auto; }
  .pb-swim-meet { grid-column: 1 / -1; }
}

/* ────────── Training video (M32) ────────── */
.video-list { display: flex; flex-direction: column; gap: 16px; }
.video-card { padding: 0; overflow: hidden; }
.video-card-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px 10px;
}
.video-card-title { font-size: 15px; font-weight: 600; margin: 0; color: var(--text); }
.video-card-meta { font-size: 12px; margin-top: 2px; }
.video-focus {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--teal-dk);
  background: var(--teal-lt);
  padding: 3px 9px;
  border-radius: 10px;
}
/* 16:9 letterbox on a dark ground so portrait phone clips sit centred rather
   than stretching the card. preload="none" in the markup keeps a page of
   clips from streaming all at once. */
.video-player { background: #14161a; }
.video-player video {
  display: block;
  width: 100%;
  max-height: 420px;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: #14161a;
}
.video-notes { padding: 12px 16px; border-top: 1px solid var(--border); }
.video-notes-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  margin-bottom: 4px;
}
.video-notes p { font-size: 14px; line-height: 1.5; color: var(--text); white-space: pre-wrap; }
.video-card-actions { padding: 10px 16px 14px; display: flex; justify-content: flex-end; }
.video-picked { font-size: 12px; margin-top: 4px; }

.video-progress {
  height: 8px;
  border-radius: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  overflow: hidden;
  margin-bottom: 6px;
}
.video-progress-bar {
  height: 100%;
  background: var(--teal);
  transition: width 0.2s ease;
}

/* ────────── Utilities ────────── */
/* Wraps any table too wide for its container so the table scrolls on its own
   rather than forcing the whole page sideways. Applied to every wide table —
   see the responsive pass in docs/UX_REVIEW_2026-08.md. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
/* A data table squeezed into 343px wraps every cell onto four lines and stops
   being a table. Below this width it scrolls inside .table-scroll instead,
   which keeps the rows readable and the page itself from moving sideways. */
.table-scroll > .data-table { min-width: 560px; }

/* ────────── Responsive pass (2026-08-27) ────────── */
/* Everything below fixes layouts that overflowed at tablet or phone widths.
   The rule of the pass: the page body must never scroll horizontally — if
   something is genuinely too wide (a table, the week grid) it scrolls inside
   its own container. */

/* Long unbroken strings — an email address, a file name, a raw ID — are the
   other common cause of a sideways page. Let them break rather than push. */
.main { overflow-wrap: break-word; }

/* Title and action button sat on one row and refused to wrap, so a long
   athlete name pushed "+ New event" off the edge. */
.page-header { flex-wrap: wrap; }

@media (max-width: 768px) {
  /* The top nav is on every signed-in page, so its overflow moved every page.
     The brand sub-label and the actor identity are the parts that can go:
     the role pill and Sign out are what a coach actually needs up there. */
  .topnav { padding: 0 12px; gap: 10px; }
  .topnav .brand-sub { display: none; }
  .topnav .brand-name { font-size: 14px; }
  .topnav-user-name { display: none; }

  /* 320px wide anchored to a bell near the right edge means the dropdown
     hangs off the left of a 375px screen. */
  .notif-dropdown { width: min(320px, calc(100vw - 24px)); }

  /* Invoice line items: four columns of currency at ~70px each. Description
     takes the full row, and the figures share the row beneath it. */
  .inv-items-row { grid-template-columns: repeat(3, 1fr); gap: 4px 8px; }
  .inv-items-row > *:first-child { grid-column: 1 / -1; font-weight: 500; }

  /* Audit log rows: a 110px timestamp plus a 140px action, both
     flex-shrink:0, left the resource column holding an unbreakable
     "person:<uuid>" with nowhere to go. Stack them and let the id wrap. */
  .audit-row { flex-wrap: wrap; gap: 2px 12px; }
  .audit-time, .audit-action { width: auto; }
  .audit-resource { flex-basis: 100%; min-width: 0; overflow-wrap: anywhere; }

  /* Filter bars and tab rows: wrap rather than overflow. */
  .filter-bar { flex-wrap: wrap; }
  .tab-bar { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .table-actions { justify-content: flex-end; }
}

/* Touch targets. Below 768px every control is being tapped with a thumb, not
   clicked with a cursor, so the small dense controls that are fine on a
   desktop become misses. 40px is the practical floor (WCAG 2.5.8 asks 24px,
   Apple and Google both advise 44px); these are grown by padding so the
   visual weight barely changes. */
@media (max-width: 768px) {
  .sidebar-toggle,
  .notif-bell-btn {
    min-width: 40px;
    min-height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .btn-sm,
  .btn-danger,
  .btn-danger-sm,
  .btn-link,
  .attendance-back,
  .breadcrumb-link,
  .attendance-mark-all {
    min-height: 40px;
    display: inline-flex;
    align-items: center;
  }
  .tag-chip, .tag-chip-on { min-height: 36px; display: inline-flex; align-items: center; }
  .btn-danger-link, .flash-close { min-height: 40px; min-width: 40px; display: inline-flex; align-items: center; justify-content: center; }
  /* Selects outside .form-grid (the role filter on /admin/users) keep the
     browser default height, which is a 19px hit area on a phone. */
  select { min-height: 40px; }
  /* Marketing site: the card CTAs and footer nav are plain anchors sized by
     their text. Give them a thumb-sized band without changing how they read. */
  .mkt-card a { display: inline-block; padding: 8px 0; }
  .mkt-footer-nav a { display: inline-block; padding: 8px 0; }
  .footer a { display: inline-block; padding: 8px 4px; }
  /* The chip's remove affordance is a 15px glyph — the hardest thing on the
     page to hit accurately. */
  .tag-chip-x { min-width: 32px; min-height: 32px; display: inline-flex; align-items: center; justify-content: center; }
}

@media (max-width: 480px) {
  /* Page actions go full width so they are comfortable to tap rather than
     squeezed against the right edge. */
  .page-header-actions { width: 100%; }
  .page-header-actions .btn-primary,
  .page-header-actions .btn-secondary { flex: 1; text-align: center; }

  /* Attendance history: the date column plus a name plus a status pill is
     one column too many at this width. */
  .att-history-row { grid-template-columns: 1fr auto; }
  .att-history-row > *:first-child { grid-column: 1 / -1; }
}

.mt-4  { margin-top: 4px; }
.mt-8  { margin-top: 8px; }
.mt-12 { margin-top: 12px; }
.mt-16 { margin-top: 16px; }
.mt-20 { margin-top: 20px; }
.mt-24 { margin-top: 24px; }
.mb-12 { margin-bottom: 12px; }
.mb-16 { margin-bottom: 16px; }
.mb-20 { margin-bottom: 20px; }
.mb-24 { margin-bottom: 24px; }
.my-12 { margin: 12px 0; }
.text-center { text-align: center; }

/* Forms rendered inline (no extra margins, sits next to siblings). Used for
   single-button POST forms that act like delete/unlink controls. */
.inline-form { display: inline; margin: 0; }

/* profile-meta defaults to a column; .profile-meta-row arranges horizontally. */
.profile-meta-row { flex-direction: row; gap: 12px; }

/* form-grid variant where the last input takes the row and a button sits to
   its right. Used by add-athlete / add-rule / add-guardian forms. Pair with
   .mt-12 or .mt-16 for top spacing. */
.form-grid-inline-action {
  grid-template-columns: 1fr auto;
  align-items: end;
}
.form-grid-inline-action > .full-1 { grid-column: 1; }

/* ────────── Plan library (M12) ────────── */

.plan-card-sub { font-size: 12px; margin: 4px 0; }

/* Versions list */
.version-list { list-style: none; margin: 0; padding: 0; }
.version-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}
.version-row:last-child { border-bottom: 0; }
.version-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.version-num { font-size: 14px; font-weight: 600; color: var(--text); }
.version-label { color: var(--text-m); }
.version-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* Bulk mark-as-paid bar (unpaid invoices) */
.bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.bulk-selectall {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-m);
}
.data-table .col-check { width: 32px; text-align: center; }

/* CMS version-history block summaries */
.version-blocks {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.version-blocks li {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.block-type-pill {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-m);
  background: var(--surface-2, #f1f3f5);
  border-radius: 4px;
  padding: 2px 8px;
  white-space: nowrap;
}
.block-preview {
  color: var(--text);
  font-size: 14px;
  overflow-wrap: anywhere;
}

/* Locked banner */
.locked-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: #FFF7E6;
  border: 1px solid #F2C66A;
  border-radius: 8px;
  margin-bottom: 16px;
}
.locked-banner-icon { font-size: 22px; }
.locked-banner-body { flex: 1; }
.locked-banner-body strong { display: block; color: #6B4F00; margin-bottom: 2px; }

/* Block list (swim builder + read-only) */
.block-list { list-style: none; margin: 0; padding: 0; }
.block-row {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 10px;
  background: var(--white);
}
.block-row-editable { cursor: grab; }
.block-row-editable.dragging { opacity: 0.5; }
.block-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.block-title { font-weight: 600; margin: 4px 0; }
.block-stats {
  display: flex;
  gap: 12px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-s);
  flex-wrap: wrap;
}
.drag-handle {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 2px 8px;
  cursor: grab;
  font-size: 16px;
  color: var(--text-s);
}
.block-add-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 16px;
}

/* Picker modal (used by both library-item picker and plan picker) */
.library-picker-host {
  position: relative;
  z-index: 50;
}
.library-picker-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.35);
  z-index: 50;
}
.library-picker-modal {
  position: fixed;
  top: 10vh;
  left: 50%;
  transform: translateX(-50%);
  width: min(640px, 90vw);
  max-height: 80vh;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.15);
  z-index: 51;
  display: flex;
  flex-direction: column;
}
.library-picker-modal .library-picker { padding: 16px; max-height: 70vh; }
.library-picker-modal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}

[x-cloak] { display: none !important; }

/* ── Attendance form (M13) ─────────────────────────────────────────────── */
.attendance-shell { max-width: 720px; margin: 0 auto; padding: 0 0 96px; }

.attendance-sticky-head {
  position: sticky; top: 0; z-index: 5;
  background: var(--white);
  border-bottom: 1px solid var(--border);
  padding: 12px 16px;
  margin: -16px -16px 12px;
}
.attendance-head-row {
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap;
}
.attendance-back { font-weight: 600; color: var(--text-m); }
.attendance-head-title { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.attendance-head-title strong { font-size: 16px; }
.attendance-head-title span { font-size: 13px; color: var(--text-m); }
.attendance-mark-all { font-size: 14px; padding: 6px 10px; }

.attendance-counts {
  display: flex; gap: 12px; margin-top: 8px; flex-wrap: wrap;
  font-size: 13px;
}
.att-count {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px; background: var(--bg);
}
.att-count-present  { color: var(--success); }
.att-count-late     { color: var(--info); }
.att-count-absent   { color: var(--error); }
.att-count-notified { color: var(--text-m); }
.att-count-todo     { color: var(--text-s); }

.attendance-progress {
  height: 4px; background: var(--bg); border-radius: 2px; margin-top: 8px;
  overflow: hidden;
}
.attendance-progress-bar { height: 100%; background: var(--success); transition: width .15s; }

.attendance-form { padding: 0 16px; }
.attendance-list { display: flex; flex-direction: column; gap: 10px; }

.att-row {
  background: var(--white);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  display: flex; flex-direction: column; gap: 10px;
}
.att-row-present  { border-left-color: var(--success); }
.att-row-late     { border-left-color: var(--info); }
.att-row-absent   { border-left-color: var(--error); }
.att-row-notified { border-left-color: var(--text-m); background: var(--bg); }
.att-row-unmarked { border-left-color: var(--border); }

.att-row-head { display: flex; align-items: center; gap: 10px; }
.att-avatar {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--teal-lt); color: var(--teal-dk);
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 14px;
  overflow: hidden; flex-shrink: 0;
}
.att-avatar img { width: 100%; height: 100%; object-fit: cover; }
.att-name { flex: 1; display: flex; flex-direction: column; }
.att-name strong { font-size: 15px; }
.att-tag {
  display: inline-block; font-size: 11px; padding: 1px 6px;
  border-radius: 999px; margin-top: 2px; background: var(--bg);
  color: var(--text-m); align-self: flex-start;
}
.att-tag-notified { background: var(--error-lt); color: var(--error); }

.att-buttons { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
.att-btn {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  font-size: 12px; font-weight: 600;
  color: var(--text-m);
  cursor: pointer;
  min-height: 56px;
  transition: background .1s, border-color .1s, color .1s;
}
.att-glyph { font-size: 22px; line-height: 1; }
.att-btn:hover { border-color: var(--text-m); }
.att-btn-present.active { background: var(--success); border-color: var(--success); color: var(--white); }
.att-btn-late.active    { background: var(--info);    border-color: var(--info);    color: var(--white); }
.att-btn-absent.active  { background: var(--error);   border-color: var(--error);   color: var(--white); }

.att-notes input {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 6px; font-size: 14px;
}

.attendance-sticky-foot {
  position: fixed; bottom: 0; left: 0; right: 0;
  background: var(--white); border-top: 1px solid var(--border);
  padding: 12px 16px;
  display: flex; justify-content: center;
  z-index: 6;
}
.attendance-save { width: 100%; max-width: 640px; padding: 14px; font-size: 16px; }

.attendance-empty { text-align: center; padding: 32px 16px; color: var(--text-m); }

/* ── Attendance history (athlete profile tab, session panel) ───────────── */
.att-history-list { display: flex; flex-direction: column; gap: 8px; }
.att-history-row {
  display: grid; grid-template-columns: 90px 1fr auto;
  align-items: center; gap: 12px;
  padding: 10px 12px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.att-history-date { font-weight: 600; font-size: 14px; }
.att-history-meta { color: var(--text-m); font-size: 13px; }
.att-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  background: var(--bg); color: var(--text-m);
}
.att-pill-present  { background: #DCF1E8; color: #0F5F46; }
.att-pill-late     { background: #DDE9F6; color: #1B4982; }
.att-pill-absent   { background: var(--error-lt); color: var(--error); }
.att-pill-notified { background: var(--bg); color: var(--text-m); }

.att-rate-card {
  background: var(--white); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px 16px;
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 12px;
}
.att-rate-value { font-size: 28px; font-weight: 700; color: var(--teal-dk); }
.att-rate-label { color: var(--text-m); font-size: 13px; }

/* Session-panel attendance ring + chips */
.cal-att-ring {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 0;
}
.cal-att-ring-svg { flex-shrink: 0; }
.cal-att-ring-text { display: flex; flex-direction: column; }
.cal-att-ring-rate { font-weight: 700; font-size: 18px; color: var(--teal-dk); }
.cal-att-ring-detail { font-size: 12px; color: var(--text-m); }
.cal-att-chips {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px;
}
.cal-att-chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 8px; border-radius: 999px;
  font-size: 11px; background: var(--bg); color: var(--text-m);
}
.cal-att-chip-present  { background: #DCF1E8; color: #0F5F46; }
.cal-att-chip-late     { background: #DDE9F6; color: #1B4982; }
.cal-att-chip-absent   { background: var(--error-lt); color: var(--error); }
.cal-att-chip-notified { background: var(--bg); color: var(--text-m); }
.cal-att-take-link { font-size: 13px; }

@media (max-width: 480px) {
  .attendance-shell { padding-bottom: 92px; }
  .att-buttons { gap: 4px; }
  .att-btn { padding: 12px 2px; font-size: 11px; min-height: 52px; }
  .att-glyph { font-size: 20px; }
}

/* ── Body scans (M14) ───────────────────────────────────────────────────── */

.body-scan-delta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px; margin-top: 8px;
}
.body-scan-delta-card {
  background: var(--white); border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: 10px; padding: 12px 14px;
}
.body-scan-delta-better { border-left-color: #1D9E75; }
.body-scan-delta-worse  { border-left-color: #C0892F; }
.body-scan-delta-flat   { border-left-color: var(--border); }
.body-scan-delta-label  { color: var(--text-m); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.body-scan-delta-value  { font-size: 22px; font-weight: 700; color: var(--text); margin-top: 4px; }
.body-scan-delta-unit   { font-weight: 400; font-size: 13px; color: var(--text-m); margin-left: 2px; }
.body-scan-delta-change { font-size: 13px; margin-top: 6px; color: var(--text-m); }
.body-scan-delta-better .body-scan-delta-change { color: #0F5F46; }
.body-scan-delta-worse  .body-scan-delta-change { color: #8A6321; }
.body-scan-delta-empty  { padding: 8px 0; }
.body-scan-delta-foot   { font-size: 12px; margin-top: 12px; }

.body-scan-chart-svg {
  width: 100%; height: auto; max-height: 280px; display: block;
  background: var(--white); border: 1px solid var(--border); border-radius: 10px;
  padding: 8px; box-sizing: border-box;
}
.body-scan-chart-empty { padding: 16px 0; }
.body-scan-chart-legend {
  display: flex; flex-wrap: wrap; gap: 16px; margin-top: 10px; font-size: 13px; color: var(--text-m);
}
.body-scan-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.body-scan-swatch { display: inline-block; width: 14px; height: 4px; border-radius: 2px; }
.body-scan-swatch-weight { background: #1D9E75; }
.body-scan-swatch-fat    { background: #C0892F; }
.body-scan-swatch-muscle { background: #3A6EA5; }

.body-scan-list { display: flex; flex-direction: column; gap: 10px; }
.body-scan-row {
  background: var(--white); border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 6px;
}
.body-scan-row-date    { font-weight: 600; color: var(--text); }
.body-scan-row-metrics {
  display: flex; flex-wrap: wrap; gap: 12px; color: var(--text-m); font-size: 14px;
}
.body-scan-row-metrics strong { color: var(--text); }
.body-scan-row-notes   { font-size: 13px; }
.body-scan-row-file    { font-size: 13px; }

/* Documents (M14) */
.document-list { display: flex; flex-direction: column; gap: 8px; }
.document-row {
  background: var(--white); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 14px; display: grid; grid-template-columns: 1fr auto; gap: 6px;
  align-items: center;
}
.document-row-main { grid-column: 1; display: flex; align-items: center; gap: 10px; }
.document-row-meta { grid-column: 1; font-size: 12px; }
.document-row-name { font-weight: 600; color: var(--text); }
.document-row form { grid-column: 2; grid-row: 1 / span 2; }

.doc-type-badge {
  display: inline-flex; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; background: var(--bg); color: var(--text-m);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.doc-type-consent       { background: #DDE9F6; color: #1B4982; }
.doc-type-medical       { background: var(--error-lt); color: var(--error); }
.doc-type-assessment    { background: #DCF1E8; color: #0F5F46; }
.doc-type-body_scan_raw { background: #F1E9D7; color: #8A6321; }

.tab-link {
  text-decoration: none;
  display: inline-block;
}

/* ── Skills library ──────────────────────────────────────────────────────── */
.skill-media-badge { font-size: 12px; color: var(--text-m); }

.skill-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 16px;
  margin-top: 12px;
}
.skill-photo-card { display: flex; flex-direction: column; gap: 6px; }
.skill-photo-img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--border);
}
.skill-photo-caption { font-size: 12px; color: var(--text-m); }

.skill-video-list { display: flex; flex-direction: column; gap: 24px; margin-top: 12px; }
.skill-video-card { display: flex; flex-direction: column; gap: 8px; }
.skill-video-title { font-weight: 600; }
.skill-video-embed { position: relative; width: 100%; padding-bottom: 56.25%; /* 16:9 */ }
.skill-video-embed iframe,
.skill-video-embed video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border-radius: 8px; border: 1px solid var(--border);
}
.skill-video-link a { color: var(--accent); text-decoration: underline; }

.skill-instructions p { margin: 0 0 8px; line-height: 1.6; }
.skill-instructions p:last-child { margin-bottom: 0; }

/* Skill badge — shown on plan builder blocks and version detail */
.skill-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px;
  background: #DCF1E8; color: #0F5F46;
  font-size: 12px; font-weight: 600; text-decoration: none;
  white-space: nowrap;
}
.skill-badge:hover { background: #c7e9d8; }

/* Skill link row in the plan builder block */
.skill-link-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 32px; }

/* Block skill link on version detail */
.block-skill-link { margin-top: 4px; }

/* btn-xs: extra-small destructive/action button */
.btn-xs {
  padding: 2px 8px; font-size: 12px;
  border: 1px solid var(--border); border-radius: 4px;
  background: var(--bg); color: var(--text); cursor: pointer;
}
.btn-xs:hover { background: var(--error-lt); color: var(--error); border-color: var(--error); }

/* tag-chip-label: checkbox-inside-chip for edit forms */
.tag-chip-label {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--border); font-size: 13px; cursor: pointer;
  user-select: none;
}
.tag-chip-label input[type="checkbox"] { width: 14px; height: 14px; }
.tag-chip-label:hover { background: var(--bg); }

/* req: required field asterisk */
.req { color: var(--error); }

/* alert-error: inline validation error block */
.alert-error {
  padding: 10px 14px; border-radius: 6px;
  background: var(--error-lt); color: var(--error);
  border: 1px solid var(--error); font-size: 14px;
}

/* Skill picker search box inside modal */
.library-picker-search { padding: 12px; border-bottom: 1px solid var(--border); }
.library-picker-search input { width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }

/* ══════════ Billing (M16) ══════════ */

/* Summary stat cards */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.stat-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal);
  border-radius: 8px;
  padding: 14px 16px;
}
.stat-card-label { font-size: 12px; color: var(--text-s); text-transform: uppercase; letter-spacing: 0.04em; }
.stat-card-value { font-size: 24px; font-weight: 600; color: var(--text); margin-top: 4px; }
.stat-card-warn { border-left-color: var(--error); }
.stat-card-warn .stat-card-value { color: var(--error); }

.audit-list { list-style: none; padding: 0; margin: 0; font-size: 13px; }
.audit-row { display: flex; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.audit-row:last-child { border-bottom: none; }
.audit-time { color: var(--text-s); width: 110px; flex-shrink: 0; }
.audit-action { font-weight: 600; width: 140px; flex-shrink: 0; }
.audit-resource { color: var(--text-s); }

/* Section card head with an action on the right */
.section-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.section-card-head .section-card-title { margin-bottom: 0; }

/* Invoice list table (clickable rows) */
.inv-table { display: flex; flex-direction: column; }
.inv-row {
  display: grid;
  grid-template-columns: 1.2fr 1.6fr 1fr 1fr 0.9fr 0.9fr;
  gap: 12px;
  align-items: center;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
}
.inv-row:last-child { border-bottom: none; }
a.inv-row:hover { background: var(--teal-lt); }
.inv-row-head {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  font-weight: 600;
}
.inv-row-head:hover { background: transparent; }
.inv-number { font-weight: 600; }
.inv-col-right { text-align: right; }
@media (max-width: 768px) {
  /* 6 proportional columns still means ~60px each on a phone — column
     headers stop being meaningful once every row is a stacked card, so
     the head row is hidden rather than also squeezed. */
  .inv-row { grid-template-columns: 1fr; gap: 2px; text-align: left; }
  .inv-row-head { display: none; }
  .inv-col-right { text-align: left; }
}

/* Status badges */
.inv-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.inv-badge-draft     { background: #ECEBE6; color: var(--text-m); }
.inv-badge-sent      { background: #E3EEFA; color: var(--info); }
.inv-badge-paid      { background: var(--teal-lt); color: var(--teal-dk); }
.inv-badge-overdue   { background: var(--error-lt); color: var(--error); }
.inv-badge-cancelled { background: #ECEBE6; color: var(--text-s); text-decoration: line-through; }

/* Detail layout */
.billing-detail-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 16px;
  align-items: start;
}
.billing-detail-side { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 900px) {
  .billing-detail-grid { grid-template-columns: 1fr; }
}

/* Line items (read-only on detail) */
.inv-items { display: flex; flex-direction: column; }
.inv-items-row {
  display: grid;
  grid-template-columns: 3fr 0.6fr 1fr 1fr;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.inv-items-head {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  font-weight: 600;
}

/* Totals block */
.inv-totals { margin-top: 12px; margin-left: auto; max-width: 280px; }
.inv-total-line {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  font-size: 14px;
  color: var(--text-m);
}
.inv-total-grand {
  border-top: 2px solid var(--border);
  margin-top: 6px;
  padding-top: 8px;
  font-size: 18px;
  font-weight: 600;
  color: var(--teal-dk);
}
.inv-notes { margin-top: 16px; }

/* Payments */
.payment-row { padding: 8px 0; border-bottom: 1px solid var(--border); }
.payment-row:last-child { border-bottom: none; }
.payment-row-main { display: flex; justify-content: space-between; align-items: baseline; }
.payment-amount { font-weight: 600; }
.payment-row-meta { font-size: 13px; margin-top: 2px; }

/* Invoice form — editable line item rows */
.inv-form-items { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.inv-form-row {
  display: grid;
  grid-template-columns: 3fr 1.1fr 0.7fr 1fr 36px;
  gap: 8px;
  align-items: center;
}
.inv-form-row input { width: 100%; padding: 7px 9px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }
@media (max-width: 480px) {
  /* No sub-classes on the 5 anonymous cells (description/amount/qty/total/
     remove) to collapse into a neater 2-column layout without templ
     changes — stacking every field full-width is the safe, correct option. */
  .inv-form-row { grid-template-columns: 1fr; gap: 4px; padding-bottom: 10px; border-bottom: 1px dashed var(--border); }
  .inv-form-head { display: none; }
}
.inv-form-head {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  font-weight: 600;
}
.btn-danger-link {
  background: none;
  border: none;
  color: var(--error);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 4px;
}
.btn-danger-link:hover { color: #c0492a; }
.checkbox-field { display: flex; align-items: center; }
.checkbox-field label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.checkbox-field input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--teal); }

/* ── Marketing / Public website ─────────────────────────────────────────────
   Body class: marketing-body
   Layout: full-width nav, sections with constrained inner, sticky footer
   ─────────────────────────────────────────────────────────────────────────── */

.marketing-body { display: flex; flex-direction: column; min-height: 100vh; }
.marketing-body .bg-pattern { display: none; }

/* ── Nav ──────────────────────────────────────────────────────────────────── */
.mkt-nav {
  background: var(--teal-dk);
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
}
.mkt-nav-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 8px 24px;
  display: flex;
  align-items: center;
  gap: 32px;
  /* min-height rather than height: 60px was sized for the 34px monogram, and a
     logo is taller. The bar keeps its old height on a site with no logo set
     and grows to fit one where there is. */
  min-height: 60px;
}
.mkt-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: #fff;
  white-space: nowrap;
}
.mkt-brand:hover { text-decoration: none; }
.mkt-brand-mark {
  background: var(--teal);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.mkt-brand-name {
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  letter-spacing: -0.01em;
}
.mkt-nav-links {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 4px;
}
/* The always-visible half of the nav: Athletes (sign in) + Book a Trial.
   margin-left:auto here as well as on .mkt-nav-links so the actions still sit
   hard right once the section links are hidden on a phone. */
.mkt-nav-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.mkt-nav-links + .mkt-nav-actions { margin-left: 8px; }
.mkt-nav-links a {
  color: rgba(255,255,255,0.82);
  font-size: 14px;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: 6px;
  text-decoration: none;
  transition: color 0.15s, background 0.15s;
}
.mkt-nav-links a:hover { color: #fff; background: rgba(255,255,255,0.1); text-decoration: none; }
.mkt-nav-cta {
  background: var(--teal) !important;
  color: #fff !important;
  border-radius: 6px !important;
  padding: 7px 16px !important;
  font-weight: 600 !important;
}
.mkt-nav-cta:hover { background: var(--teal-hover) !important; }
/* Outlined, not filled: sitting next to "Book a Trial" it has to read as the
   second action. A returning family looks for it; a first-time visitor should
   still be pulled to the trial booking. */
.mkt-nav-login {
  border: 1px solid rgba(255,255,255,0.45);
  border-radius: 6px;
  padding: 6px 14px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}
.mkt-nav-login:hover {
  background: rgba(255,255,255,0.14);
  border-color: #fff;
}

/* ── Main / Sections ──────────────────────────────────────────────────────── */
.mkt-main { flex: 1; }

.mkt-section { padding: 64px 0; }
.mkt-section.mkt-bg-alt { background: #fff; }
/* The home-page "What We Offer" and "Two Convenient Locations" bands carry no
   vertical padding of their own, so their headings crowded the section above.
   Match the standard section rhythm. */
.mkt-services,
.mkt-locations { padding: 64px 0; }
.mkt-section-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 24px;
}
.mkt-section h2 {
  font-size: 28px;
  font-weight: 700;
  color: var(--teal-dk);
  margin-bottom: 24px;
  letter-spacing: -0.02em;
}

/* ── Hero ─────────────────────────────────────────────────────────────────── */
.mkt-hero {
  background: linear-gradient(135deg, var(--hero-from) 0%, var(--hero-to) 100%);
  color: #fff;
  padding: 80px 24px;
  text-align: center;
}
.mkt-hero-photo {
  background-image:
    linear-gradient(135deg,
      color-mix(in srgb, var(--hero-from) 86%, transparent) 0%,
      color-mix(in srgb, var(--hero-to) 80%, transparent) 100%),
    url("/static/img/site/freestyle-race.webp");
  background-size: cover;
  background-position: center 45%;
  background-repeat: no-repeat;
}
.mkt-hero-content {
  max-width: 720px;
  margin: 0 auto;
}
.mkt-hero h1 {
  font-size: clamp(32px, 5vw, 52px);
  font-weight: 800;
  color: #fff;
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin-bottom: 16px;
}
.mkt-hero-sub {
  font-size: 18px;
  color: rgba(255,255,255,0.85);
  line-height: 1.6;
  margin-bottom: 32px;
}
.mkt-page-hero {
  background: linear-gradient(135deg, var(--hero-from) 0%, var(--hero-to) 100%);
  padding: 48px 0;
  color: #fff;
}
.mkt-page-hero .mkt-section-inner { max-width: 1100px; margin: 0 auto; padding: 0 24px; }
.mkt-page-hero h1 {
  font-size: clamp(26px, 4vw, 40px);
  font-weight: 800;
  color: #fff;
  letter-spacing: -0.02em;
  margin-bottom: 12px;
}
.mkt-lead {
  font-size: 17px;
  color: rgba(255,255,255,0.85);
  line-height: 1.6;
  max-width: 680px;
}

/* ── Error pages (M22.1) ──────────────────────────────────────────────────── */
.error-hero { min-height: 60vh; display: flex; align-items: center; }
.error-code {
  font-size: 72px;
  font-weight: 800;
  color: rgba(255,255,255,0.35);
  line-height: 1;
  margin-bottom: 8px;
}
.error-actions { display: flex; gap: 12px; margin-top: 24px; }
.error-actions button { cursor: pointer; font-family: inherit; }

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.btn-mkt-primary {
  display: inline-block;
  background: var(--teal);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  padding: 13px 28px;
  border-radius: 8px;
  text-decoration: none;
  transition: background 0.15s, transform 0.1s;
  border: none;
  cursor: pointer;
}
.btn-mkt-primary:hover { background: var(--teal-hover); color: #fff; text-decoration: none; transform: translateY(-1px); }
/* The hero's second button. It was a translucent outline, and now carries the
   same fill as the primary beside it — asked for directly, and the two read as
   a pair of equal invitations rather than one offer and a footnote.

   Matched in full, not just in colour: two teal buttons at different weights
   and paddings looks like a mistake rather than a choice. Only the gap between
   them is its own. */
.btn-mkt-secondary {
  display: inline-block;
  background: var(--teal);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  padding: 13px 28px;
  border-radius: 8px;
  text-decoration: none;
  border: none;
  margin-left: 12px;
  transition: background 0.15s, transform 0.1s;
  cursor: pointer;
}
.btn-mkt-secondary:hover { background: var(--teal-hover); color: #fff; text-decoration: none; transform: translateY(-1px); }

/* ── Cards ────────────────────────────────────────────────────────────────── */
.mkt-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
  margin-top: 16px;
}
.mkt-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 28px;
  transition: box-shadow 0.2s;
}
.mkt-card:hover { box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
.mkt-card h3 {
  font-size: 18px;
  font-weight: 700;
  color: var(--teal-dk);
  margin-bottom: 10px;
  letter-spacing: -0.01em;
}
.mkt-card p { font-size: 15px; line-height: 1.6; color: var(--text-m); margin-bottom: 14px; }
.mkt-card a { font-size: 14px; font-weight: 600; color: var(--teal); }
.mkt-card-icon { font-size: 28px; margin-bottom: 14px; }
/* Location cards carry two links: the details page and an external Google Maps
   search. Lay them out as a row that wraps to stacked links on narrow cards. */
.mkt-card-links { display: flex; flex-wrap: wrap; gap: 4px 20px; }
.mkt-map-link { color: var(--teal); }

/* ── Two-col layout ───────────────────────────────────────────────────────── */
.mkt-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }
.mkt-col h2 { font-size: 26px; font-weight: 700; color: var(--teal-dk); margin-bottom: 16px; letter-spacing: -0.02em; }
.mkt-col p { font-size: 15px; line-height: 1.7; color: var(--text-m); margin-bottom: 14px; }
.mkt-list { padding-left: 20px; margin: 12px 0 16px; }
.mkt-list li { font-size: 15px; line-height: 1.7; color: var(--text-m); margin-bottom: 6px; }
.mkt-col-aside { position: relative; }
.mkt-service-tag {
  font-size: 18px;
  font-weight: 700;
  color: var(--teal-dk);
  padding: 16px 20px;
  background: var(--teal-lt);
  border-radius: 10px;
  margin-bottom: 16px;
  display: inline-block;
}
.mkt-service-detail {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 20px;
  font-size: 14px;
  line-height: 1.8;
  color: var(--text-m);
  /* Sits directly under each service heading now, ahead of the body copy. */
  margin: 0 0 20px;
}

/* ── Testimonials ─────────────────────────────────────────────────────────── */
.mkt-testimonials { padding: 64px 0; background: var(--bg); }
.mkt-testimonial-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
  margin-top: 8px;
}
.mkt-testimonial-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
}
.mkt-testimonial-text {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text);
  margin-bottom: 16px;
}
/* The body is rendered CMS HTML, so it arrives as block elements. Strip the
   stray margins off the ends to keep the card's padding even. */
.mkt-testimonial-text > :first-child { margin-top: 0; }
.mkt-testimonial-text > :last-child { margin-bottom: 0; }

/* Quote styling belongs to the short-quote kind only: the marks used to be
   literal characters in home.templ, and moved here when the body became
   rendered content (templ parses a text run and a component call as one node,
   so `"@richTextInline(…)"` was emitted verbatim rather than invoked). A
   multi-paragraph success story is not a pull quote, so .mkt-testimonial-quoted
   gates both the italics and the marks. */
.mkt-testimonial-quoted { font-style: italic; }
/* On the first and last paragraph, not on the container.
   The body is rendered CMS HTML, so the container holds a block-level <p>, and
   a pseudo-element placed beside a block becomes an anonymous block box of its
   own — which is why the marks sat on their own lines above and below the
   quote. Hanging them off the paragraphs puts them back in the text flow,
   where a quotation mark belongs. Scoped to <p> so a quote that somehow opens
   with a list or an image does not get a mark stranded beside it. */
.mkt-testimonial-quoted > p:first-child::before { content: '\201C'; }
.mkt-testimonial-quoted > p:last-child::after  { content: '\201D'; }

/* A success story gets the page width to itself: photos in the body need the
   room, and stacked single-column cards read as one story after another
   rather than three columns of unrelated text. */
.mkt-story-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  margin-top: 8px;
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}
.mkt-story-card { padding: 32px; }
.mkt-story-card .mkt-testimonial-text { font-size: 16px; }
/* A story is wide enough to carry its photographs at the card's full width. */
/* Stories used to force every image to the card's full width. That upscaled
   anything smaller than the card — a screenshot blown past its own resolution
   reads as stretched — and left a column of pictures at wildly different
   heights, since a portrait photograph and a landscape screenshot at the same
   width are nothing like the same size. Width is now left to the image and
   bounded below, so nothing is ever enlarged and everything sits inside the
   same box. */
.mkt-story-card .mkt-testimonial-text img { margin: 20px auto; }
.mkt-testimonial-author { font-size: 14px; }
.mkt-testimonial-author strong { display: block; color: var(--teal-dk); font-weight: 600; }
.mkt-testimonial-author span { color: var(--text-s); }
.mkt-cta-row { margin-top: 32px; text-align: center; }

/* ── Published timetable ──────────────────────────────────────────────────── */
/* A squad per card, a day per row. Not a seven-column grid: a week with two
   locations and several squads is already wide on a laptop and unreadable on a
   phone, and the question a family is actually asking is "when does my
   daughter's squad swim", which is one squad at a time. */
.mkt-timetable-location { margin-bottom: 48px; }
.mkt-timetable-location:last-of-type { margin-bottom: 0; }
.mkt-timetable-location h2 {
  font-size: 24px;
  font-weight: 700;
  color: var(--teal-dk);
  margin-bottom: 20px;
}
.mkt-timetable-squads {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
}
.mkt-timetable-squad {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px 24px;
}
.mkt-timetable-squad h3 {
  font-size: 17px;
  font-weight: 600;
  color: var(--teal-dk);
  margin-bottom: 12px;
}
/* Day and time on one line, the day fixed so the times align down the card. */
.mkt-timetable-days { margin: 0; font-size: 15px; }
.mkt-timetable-days dt {
  float: left;
  clear: left;
  width: 105px;
  color: var(--text-m);
  padding: 5px 0;
}
.mkt-timetable-days dd {
  margin: 0 0 0 105px;
  padding: 5px 0;
  color: var(--text);
}
@media (max-width: 420px) {
  /* Below this the two columns crush the times onto two lines each; stacking
     reads better than wrapping. */
  .mkt-timetable-days dt { float: none; width: auto; padding-bottom: 0; }
  .mkt-timetable-days dd { margin-left: 0; padding-top: 1px; font-weight: 500; }
}

/* ── Gallery ──────────────────────────────────────────────────────────────── */
/* Two layouts, deliberately different.

   The index is a list of events, so its covers are cropped to a common shape
   and the rows line up — scanning for "the March carnival" is the job there.

   Inside an album nothing is cropped. Each photograph keeps its own
   proportions and the column packs them, because a centre crop to a common
   ratio takes swimmers out of their own frame; one photograph had to be
   hand-cropped for exactly that reason. The ragged bottom edge is the cost and
   it is the right trade. */
.mkt-album-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 28px;
}
.mkt-album-tile { display: block; text-decoration: none; color: inherit; }
.mkt-album-tile:hover { text-decoration: none; }
.mkt-album-cover {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: 10px;
  background: var(--teal-lt);
}
.mkt-album-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}
.mkt-album-tile:hover .mkt-album-cover img { transform: scale(1.03); }
.mkt-album-meta { padding: 14px 2px 0; }
.mkt-album-meta h2 {
  font-size: 18px;
  font-weight: 600;
  color: var(--teal-dk);
  margin: 0 0 2px;
}
.mkt-album-meta p { font-size: 14px; color: var(--text-s); margin: 0; }

/* CSS columns rather than a JavaScript masonry: no script, no layout thrash
   on load, and the only cost is that photographs read down a column before
   across — which for a gallery nobody minds. */
.mkt-photo-columns {
  column-count: 3;
  column-gap: 24px;
}
.mkt-photo-item {
  break-inside: avoid;
  margin: 0 0 24px;
}
.mkt-photo-item img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 10px;
}
.mkt-eyebrow { font-size: 14px; margin-bottom: 8px; }
.mkt-eyebrow a { color: rgba(255,255,255,0.85); text-decoration: none; }
.mkt-eyebrow a:hover { color: #fff; text-decoration: underline; }

@media (max-width: 900px) {
  .mkt-photo-columns { column-count: 2; column-gap: 16px; }
  .mkt-photo-item { margin-bottom: 16px; }
}
@media (max-width: 560px) {
  /* One column on a phone. Two would put a portrait photograph at about 160px
     wide, which is a thumbnail of a person rather than a photograph of one. */
  .mkt-photo-columns { column-count: 1; }
  .mkt-album-grid { gap: 20px; }
}

/* ── CTA band ─────────────────────────────────────────────────────────────── */
.mkt-cta-band {
  background: var(--teal-dk);
  color: #fff;
  padding: 64px 0;
  text-align: center;
}
.mkt-cta-band h2 { font-size: 28px; font-weight: 700; color: #fff; margin-bottom: 12px; }
.mkt-cta-band p { font-size: 16px; color: rgba(255,255,255,0.82); margin-bottom: 28px; }

/* ── Locations ────────────────────────────────────────────────────────────── */
.mkt-location-card-detail {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: start;
}
.mkt-location-card-detail--reverse > *:first-child { order: 2; }
.mkt-location-card-detail--reverse > *:last-child { order: 1; }
.mkt-location-info h2 { font-size: 24px; font-weight: 700; color: var(--teal-dk); margin-bottom: 10px; }
.mkt-location-info address { font-style: normal; font-size: 15px; color: var(--text-m); margin-bottom: 14px; line-height: 1.6; }
.mkt-location-info p { font-size: 15px; line-height: 1.7; color: var(--text-m); margin-bottom: 16px; }
.mkt-detail-list { font-size: 14px; }
.mkt-detail-list dt { font-weight: 600; color: var(--text); margin-top: 8px; }
.mkt-detail-list dd { color: var(--text-m); margin-left: 0; }
.mkt-location-map-placeholder {
  background: var(--teal-lt);
  border-radius: 12px;
  min-height: 220px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--teal-dk);
  font-weight: 600;
  font-size: 15px;
  text-align: center;
  border: 1px solid color-mix(in srgb, var(--teal) 20%, transparent);
}

/* ── Marketing photographs ────────────────────────────────────────────────── */
/* One figure style for every photo on the public site. The <img> carries its
   real width/height so the box is reserved before the bytes land; the
   aspect-ratio here is what crops a mixed bag of portrait and landscape
   snapshots into a tidy grid without letterboxing any of them. */
.mkt-photo {
  margin: 0;
  border-radius: 12px;
  overflow: hidden;
  background: var(--teal-lt);
}
.mkt-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Home gallery ─────────────────────────────────────────────────────────── */
.mkt-gallery { padding: 64px 0; }
/* Four across, or two — never three.
   auto-fill used to decide the column count from the available width, which
   meant the grid could land on three columns in two separate bands on the way
   down. A count that fills three rows as well as two and four has to be a
   multiple of twelve, and the gallery is eight photographs now. Fixing the
   track count is what lets the set be any multiple of four: the last row is
   full at every width rather than only at the ones nobody checked. */
.mkt-gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
/* Two up, well before four would squeeze a face into a thumbnail. */
@media (max-width: 560px) {
  .mkt-gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
}
/* A single ratio across the grid is what makes eleven phone photos read as one
   set. Portraits lose some headroom to the crop, which is why object-position
   sits above centre: in this collection the faces and the medals are in the
   top half of the frame. */
.mkt-gallery-grid .mkt-photo { aspect-ratio: 3 / 4; }
.mkt-gallery-grid .mkt-photo img { object-position: center 30%; }

/* ── Testimonials photo strip ─────────────────────────────────────────────── */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-bottom: 40px;
}

/* ── Service sections with a photo ────────────────────────────────────────── */
.mkt-service-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: center;
}
.mkt-service-split--reverse > *:first-child { order: 2; }
.mkt-service-split--reverse > *:last-child { order: 1; }
.mkt-service-split .mkt-photo { aspect-ratio: 4 / 3; }

/* ── Location photo ───────────────────────────────────────────────────────── */
/* Takes the slot the map placeholder held, and keeps its minimum height so a
   venue with a photo and one still waiting on one sit at the same size. */
.mkt-location-photo { min-height: 220px; aspect-ratio: 3 / 2; }

/* Visible to a screen reader, not on screen. The athlete picker's label would
   be noise next to a select that already says "Add an athlete…", but a select
   with no label at all is a control a screen reader cannot announce. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Sessions to bill ─────────────────────────────────────────────────────── */
.bill-session-list { list-style: none; margin: 10px 0 0; padding: 0; }
.bill-session-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.bill-session-row:last-child { border-bottom: none; }
.bill-session-row > a { font-weight: 600; }

/* ── Session sharing ──────────────────────────────────────────────────────── */
.sharer-list { list-style: none; margin: 12px 0 0; padding: 0; }
.sharer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.sharer-row:last-child { border-bottom: none; }
.sharer-add { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.sharer-add select { flex: 1; min-width: 180px; }

/* ── Session reschedule ───────────────────────────────────────────────────── */
/* Folded shut by default: on the calendar's side panel the form is longer
   than the panel itself, and rescheduling is the rarer thing to want. */
.cal-panel-edit { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 12px; }
.cal-panel-edit > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--teal-dk);
  list-style: none;
}
.cal-panel-edit > summary::-webkit-details-marker { display: none; }
.cal-panel-edit > summary::before { content: "▸ "; }
.cal-panel-edit[open] > summary::before { content: "▾ "; }
.cal-panel-editform { margin-top: 12px; }
.cal-panel-editform .form-grid { gap: 10px; }

/* ── Coach profile ────────────────────────────────────────────────────────── */
.mkt-profile-card {
  background: var(--teal-lt);
  border-radius: 12px;
  padding: 28px;
  text-align: center;
}
/* Sizes the wrapper, not the image — .mkt-photo img fills it. The previous
   version set aspect-ratio and object-fit on the <img> itself, which also
   carries width and height attributes, and the two ways of describing the
   same box disagreed: the photo rendered as a tall sliver of her face. Every
   other photo on the site already sizes a figure, and every one of them was
   fine. */
.mkt-profile-photo {
  aspect-ratio: 3 / 4;
  margin: 0 0 16px;
}
/* Only bites when a photo is not already 3:4. Faces sit high in a portrait,
   so a crop should take it off the bottom. */
.mkt-profile-photo img { object-position: center 25%; }
.mkt-profile-name { font-size: 17px; font-weight: 700; color: var(--teal-dk); margin-bottom: 6px; }
.mkt-profile-credentials { font-size: 13px; color: var(--text-m); line-height: 1.6; }

/* ── Blog ─────────────────────────────────────────────────────────────────── */
.mkt-blog-list { display: flex; flex-direction: column; gap: 24px; }
.mkt-blog-card {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 24px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
.mkt-blog-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mkt-blog-card-body { padding: 24px 24px 24px 0; }
.mkt-blog-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.mkt-tag {
  background: var(--teal-lt);
  color: var(--teal-dk);
  font-size: 12px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.mkt-blog-card-title a {
  font-size: 20px;
  font-weight: 700;
  color: var(--teal-dk);
  text-decoration: none;
  display: block;
  margin-bottom: 8px;
}
.mkt-blog-card-title a:hover { color: var(--teal); }
.mkt-blog-card-excerpt { font-size: 14px; color: var(--text-m); line-height: 1.6; margin-bottom: 14px; }
.mkt-blog-read-more { font-size: 14px; font-weight: 600; color: var(--teal); }

/* ── Article / Blog post ──────────────────────────────────────────────────── */
.mkt-article {}
.mkt-article-header { background: linear-gradient(135deg, var(--hero-from) 0%, var(--hero-to) 100%); padding: 48px 0; }
.mkt-article-header .mkt-section-inner { max-width: 800px; }
.mkt-article-header h1 { font-size: clamp(22px,4vw,36px); font-weight: 800; color: #fff; margin: 12px 0 10px; letter-spacing: -0.02em; line-height: 1.2; }
.mkt-article-date { font-size: 14px; color: rgba(255,255,255,0.7); }
.mkt-article-body { max-width: 800px; padding-top: 40px; padding-bottom: 40px; }
.mkt-article-body h2 { font-size: 22px; font-weight: 700; color: var(--teal-dk); margin: 32px 0 12px; }
.mkt-article-body h3 { font-size: 18px; font-weight: 700; color: var(--teal-dk); margin: 24px 0 10px; }
.mkt-article-body p { font-size: 16px; line-height: 1.8; color: var(--text); margin-bottom: 16px; }
.mkt-article-quote {
  border-left: 4px solid var(--teal);
  padding: 16px 20px;
  background: var(--teal-lt);
  border-radius: 0 8px 8px 0;
  margin: 20px 0;
  font-style: italic;
  color: var(--teal-dk);
}
.mkt-article-figure { margin: 20px 0; }
.mkt-article-figure img { width: 100%; border-radius: 8px; }
.mkt-article-figure figcaption { font-size: 13px; color: var(--text-s); margin-top: 6px; text-align: center; }
.mkt-article-footer { padding: 24px 0 48px; max-width: 800px; }

/* ── Contact form ─────────────────────────────────────────────────────────── */
.mkt-form-section { max-width: 720px; }
.mkt-contact-form h2 { font-size: 20px; font-weight: 700; color: var(--teal-dk); margin-bottom: 20px; }
.req { color: var(--error); }

/* ── Footer ───────────────────────────────────────────────────────────────── */
.mkt-footer { background: var(--teal-dk); color: rgba(255,255,255,0.8); padding: 40px 0 0; }
.mkt-footer-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 24px 32px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 32px;
}
.mkt-footer-brand strong { display: block; color: #fff; font-size: 16px; margin-bottom: 8px; }
.mkt-footer-brand p { font-size: 14px; margin-bottom: 4px; line-height: 1.6; }
.mkt-footer-brand a { color: rgba(255,255,255,0.7); font-size: 14px; }
.mkt-footer-brand a:hover { color: #fff; }
.mkt-footer-nav { display: flex; flex-direction: column; gap: 8px; padding-top: 4px; }
.mkt-footer-nav a { color: rgba(255,255,255,0.7); font-size: 14px; text-decoration: none; }
.mkt-footer-nav a:hover { color: #fff; text-decoration: none; }
.mkt-footer-copy {
  border-top: 1px solid rgba(255,255,255,0.12);
  text-align: center;
  font-size: 13px;
  color: rgba(255,255,255,0.45);
  padding: 16px 24px;
}

/* ── Responsive ───────────────────────────────────────────────────────────── */
/* ── Mobile navigation drawer ─────────────────────────────────────────────── */
/* The hamburger appears exactly when the link strip disappears, and opens the
   same list beneath the bar. Before it, the only route to About, Services or
   Locations on a phone was to scroll the entire page and find the footer.

   A class on <body> rather than state held in script: the dashboard's sidebar
   does the same, and it means the only JavaScript is the one-line toggle in
   the markup. */
.mkt-nav-toggle {
  display: none;
  padding: 6px 10px;
  margin-right: 4px;
  font-size: 20px;
  line-height: 1;
  color: rgba(255,255,255,0.9);
  background: transparent;
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: 6px;
  cursor: pointer;
}
.mkt-nav-toggle:hover { background: rgba(255,255,255,0.1); }

.mkt-nav-drawer { display: none; }
/* One breakpoint, one decision: below this the strip does not fit, so it is
   replaced by the button and the drawer. Six links, the logo and the two
   action buttons need roughly 990px of bar between them, and below that the
   strip used to run into the buttons.

   The actions never collapse — "Athletes" is how every family, coach and
   guardian reaches the portal — and the footer still carries the same links
   for anyone who scrolls instead. */
@media (max-width: 1024px) {
  .mkt-nav-links { display: none; }
  .mkt-nav-toggle { display: block; }
  body.mkt-nav-open .mkt-nav-drawer {
    display: flex;
    flex-direction: column;
    padding: 6px 24px 14px;
    border-top: 1px solid rgba(255,255,255,0.12);
  }
  .mkt-nav-drawer a {
    padding: 11px 2px;
    color: rgba(255,255,255,0.88);
    font-size: 15px;
    text-decoration: none;
    border-bottom: 1px solid rgba(255,255,255,0.08);
  }
  .mkt-nav-drawer a:last-child { border-bottom: none; }
  .mkt-nav-drawer a:hover { color: #fff; }
}

@media (max-width: 768px) {
  .mkt-nav-inner { gap: 12px; padding: 8px 16px; }
  .mkt-nav-actions { gap: 6px; }
  .mkt-nav-login { padding: 6px 10px; font-size: 13px; }
  .mkt-nav-cta { padding: 7px 12px !important; font-size: 13px; }
  /* The full business name plus two buttons does not fit a 360px phone; the
     monogram/logo carries the brand there. */
  .mkt-brand-name { display: none; }
  .mkt-two-col { grid-template-columns: 1fr; gap: 24px; }
  .mkt-location-card-detail { grid-template-columns: 1fr; }
  .mkt-location-card-detail--reverse > *:first-child { order: unset; }
  .mkt-location-card-detail--reverse > *:last-child { order: unset; }
  /* The copy leads on a phone; the photo follows it rather than pushing it
     below the fold, so --reverse drops its ordering here the same way the
     location rows do. */
  .mkt-service-split { grid-template-columns: 1fr; gap: 24px; }
  .mkt-service-split--reverse > *:first-child { order: unset; }
  .mkt-service-split--reverse > *:last-child { order: unset; }
  .mkt-gallery-grid { gap: 10px; }
  .mkt-blog-card { grid-template-columns: 1fr; }
  .mkt-blog-card-img { height: 180px; }
  .mkt-blog-card-body { padding: 16px; }
  .mkt-footer-inner { grid-template-columns: 1fr; }
}

/* ── Role chips ───────────────────────────────────────────────────────────── */
/* One chip per role, held or not. A chip is the control as well as the state,
   so the screen is a row of things to click rather than a list beside a form. */
.role-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.role-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 7px 12px;
  font-size: 14px;
  font-family: inherit;
  color: var(--text-m);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.role-chip small { font-size: 11px; color: var(--text-s); }
.role-chip:hover { border-color: var(--teal); color: var(--teal-dk); }
/* Held roles read as filled; the main one is stated rather than offered,
   because it is changed in the form above and a button that always failed
   would be worse than no button. */
.role-chip-held { background: var(--teal-lt); border-color: var(--teal); color: var(--teal-dk); }
.role-chip-primary { background: var(--teal-dk); border-color: var(--teal-dk); color: #fff; cursor: default; }
.role-chip-primary small { color: rgba(255,255,255,0.75); }

/* ── CMS gallery ──────────────────────────────────────────────────────────── */
/* The manager's photo grid. Thumbnails are cropped to a square here and only
   here — this is a list of things to act on, not a presentation of them, and a
   uniform grid is easier to scan for the one being looked for. */
.cms-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 16px;
}
.cms-photo-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--card);
}
.cms-photo-card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
}
.cms-photo-alt { display: flex; gap: 6px; padding: 8px; }
.cms-photo-alt input { flex: 1; min-width: 0; margin: 0; font-size: 13px; }
.cms-photo-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px 8px;
  flex-wrap: wrap;
}

/* ── CMS editor additions ─────────────────────────────────────────────────── */
.breadcrumb-link { display: inline-block; font-size: 13px; color: var(--text-s); margin-bottom: 4px; }
.breadcrumb-link:hover { color: var(--teal); }
.page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 20px; gap: 16px; }
.page-header h1 { font-size: 22px; font-weight: 700; }
.page-header-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.data-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.data-table th { text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-s); font-weight: 600; padding: 8px 12px; border-bottom: 2px solid var(--border); }
.data-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.data-table tr:last-child td { border-bottom: none; }
.table-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.btn-sm { font-size: 13px; padding: 5px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); text-decoration: none; cursor: pointer; white-space: nowrap; }
.btn-sm:hover { border-color: var(--teal); color: var(--teal); text-decoration: none; }
.btn-success { background: var(--success); color: #fff; border: none; padding: 10px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; }
.btn-success:hover { background: var(--teal-hover); }
.btn-danger-sm { background: none; border: 1px solid var(--error); color: var(--error); border-radius: 4px; padding: 2px 8px; font-size: 13px; cursor: pointer; }
.btn-danger-sm:hover { background: var(--error-lt); }
.mt-8 { margin-top: 8px; }
.mt-16 { margin-top: 16px; }

/* The CMS block editor's rules lived here until the WYSIWYG editor replaced it
   (see .richtext further down). They were scoped to .block-editor because
   `.block-row` is also the plan / module / drill composers' drag-and-drop row
   class, and an unscoped grid here overrode theirs. Nothing is scoped to
   .block-editor any more, so the composers own those class names outright. */

/* Theme colour editor (/admin/settings).
   Deliberately plain: the point of the section is that the surrounding page
   recolours as you pick, so the editor's own chrome should stay out of the
   way. Everything here uses the theme variables, which means it previews
   itself along with the rest of the UI. */
.theme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
  margin-bottom: 8px;
}
.theme-field-row { display: flex; align-items: center; gap: 8px; }
.theme-swatch {
  width: 44px;
  height: 34px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--white);
  cursor: pointer;
  flex: none;
}
.theme-swatch:disabled { cursor: not-allowed; opacity: 0.45; }
.theme-hex {
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  text-transform: uppercase;
}
.theme-hex:disabled { background: var(--bg); color: var(--text-s); cursor: not-allowed; }
.theme-auto {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 8px 0 0;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-m);
  cursor: pointer;
}
.theme-auto input { width: 14px; height: 14px; accent-color: var(--teal); cursor: pointer; }
.theme-help { margin-top: 6px; font-size: 12px; color: var(--text-s); line-height: 1.4; }

/* Rich text editor (views/components/richtext.templ + static/js/richtext.js).
   The editable surface is styled to read like the finished page rather than
   like a form field — that is the whole point of a WYSIWYG box — while the
   border and toolbar keep it recognisable as something you can type into. */
.richtext { display: flex; flex-direction: column; gap: 0; width: 100%; }
.rt-toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-wrap: wrap;
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  background: var(--white);
}
.rt-btn {
  min-width: 28px;
  padding: 3px 7px;
  font-size: 13px;
  line-height: 1.4;
  font-family: inherit;
  color: var(--text-m);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
}
.rt-btn:hover { background: var(--teal-lt); color: var(--teal-dk); border-color: var(--border); }
.rt-btn:active { background: var(--teal); color: var(--white); }
.rt-btn-bold      { font-weight: 700; }
.rt-btn-italic    { font-style: italic; }
.rt-btn-underline { text-decoration: underline; }
.rt-btn-strike    { text-decoration: line-through; }
.rt-block, .rt-type {
  height: 26px;
  padding: 0 6px;
  font-size: 12px;
  font-family: inherit;
  color: var(--text-m);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
}
.rt-block { margin-right: 4px; }
/* The three type controls read as one group, set a little apart from the
   block-style select on their left and the command buttons on their right.
   They are narrower than .rt-block because their labels are one short word;
   left to size themselves the font list would be as wide as "Small heading"
   and push the buttons onto a second row in the testimonial manager's
   narrower column. */
.rt-type { max-width: 108px; }
.rt-type:first-of-type { margin-left: 4px; }
.rt-type:last-of-type { margin-right: 4px; }

/* The editable surface. Squared off at the top so it reads as one control with
   the toolbar sitting on it. */
.rt-editor {
  min-height: 180px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 0 0 6px 6px;
  background: var(--white);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}
.rt-editor:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 2px var(--teal-lt); }
[data-rt-mode="inline"] .rt-editor { min-height: 62px; }

/* Placeholder. `:empty` cannot be used — the browser keeps a stray <br> in an
   emptied contenteditable — so richtext.js sets .rt-empty from the rendered
   content instead. */
.rt-empty .rt-editor::before {
  content: attr(data-placeholder);
  color: var(--text-s);
  pointer-events: none;
  display: block;
}

/* Show the author what the site will show them. These mirror .mkt-prose. */
.rt-editor > :first-child { margin-top: 0; }
.rt-editor > :last-child { margin-bottom: 0; }
.rt-editor p { margin: 0 0 12px; }
.rt-editor h2 { font-size: 24px; font-weight: 700; color: var(--teal-dk); margin: 20px 0 10px; }
.rt-editor h3 { font-size: 19px; font-weight: 700; color: var(--teal-dk); margin: 18px 0 8px; }
.rt-editor h4 { font-size: 16px; font-weight: 600; margin: 16px 0 6px; }
.rt-editor ul, .rt-editor ol { margin: 0 0 12px 22px; }
.rt-editor li { margin-bottom: 4px; }
.rt-editor blockquote {
  margin: 14px 0; padding: 8px 14px;
  border-left: 4px solid var(--teal); background: var(--teal-lt);
  color: var(--teal-dk); font-style: italic;
}
.rt-editor a { color: var(--teal); text-decoration: underline; }
.rt-editor img { max-width: 100%; height: auto; border-radius: 6px; }
.rt-editor code { padding: 1px 4px; border-radius: 3px; background: var(--bg); font-size: 14px; }

/* URL entry for the link and image buttons. */
.rt-urlbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 6px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg);
  font-size: 13px;
}
.rt-urlbar[hidden] { display: none; }
.rt-urlbar label { font-weight: 600; color: var(--text-m); margin: 0; }
.rt-urlinput { flex: 1; min-width: 200px; margin: 0; }
/* Image size guidance. Takes the whole row under the controls rather than
   sitting among them: at the testimonial manager's width it would otherwise
   squeeze the URL field down to nothing. */
.rt-urlhint {
  flex-basis: 100%;
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-s);
}
.rt-urlhint[hidden] { display: none; }
.rt-urlhint strong { color: var(--text-m); font-weight: 600; }

/* Testimonial manager */
.testimonial-row { margin-bottom: 12px; }
.testimonial-row:last-child { margin-bottom: 0; }
.testimonial-row-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
/* A preview of the stored body in the manager list. It holds rendered block
   content now, not one italic sentence, so it clamps the photos a story may
   carry rather than letting a full-width image push the row open. */
.testimonial-row-text { font-size: 14px; color: var(--text-m); margin-bottom: 8px; }
.testimonial-row-text > :first-child { margin-top: 0; }
.testimonial-row-text > :last-child { margin-bottom: 0; }
.testimonial-row-text img { max-width: 160px; height: auto; border-radius: 4px; }
.badge { background: var(--teal-lt); color: var(--teal-dk); font-size: 12px; font-weight: 600; padding: 2px 8px; border-radius: 10px; }
.testimonial-edit-form summary { font-size: 13px; color: var(--teal); cursor: pointer; font-weight: 600; }
.checkbox-row { display: flex; gap: 20px; align-items: center; }
.checkbox-row label { display: flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; }

/* ── CMS prose section (content blocks on public pages) ─────────────────── */
.mkt-prose { max-width: 720px; }
.mkt-prose h2 { font-size: 26px; font-weight: 700; color: var(--teal-dk); margin: 32px 0 12px; }
.mkt-prose h3 { font-size: 20px; font-weight: 600; color: var(--teal-dk); margin: 24px 0 8px; }
.mkt-prose h4 { font-size: 17px; font-weight: 600; margin: 20px 0 6px; }
.mkt-prose p { font-size: 16px; line-height: 1.7; color: var(--text-m); margin-bottom: 16px; }
.mkt-prose blockquote { border-left: 4px solid var(--teal); padding: 12px 20px; margin: 24px 0; background: var(--teal-lt); border-radius: 0 6px 6px 0; font-style: italic; color: var(--teal-dk); }
.mkt-prose figure { margin: 24px 0; }
.mkt-prose figure img { width: 100%; border-radius: 8px; }
.mkt-prose figcaption { font-size: 13px; color: var(--text-lt); text-align: center; margin-top: 6px; }
/* The WYSIWYG editor can emit any of these, so the prose containers have to
   have an opinion about all of them — an unstyled <ul> here is an author
   adding a list and watching it come out unindented. Shared with the article
   body, which renders the same authored HTML. */
.mkt-prose ul, .mkt-prose ol,
.mkt-article-body ul, .mkt-article-body ol { margin: 0 0 16px 24px; font-size: 16px; line-height: 1.8; color: var(--text-m); }
.mkt-prose li, .mkt-article-body li { margin-bottom: 6px; }
.mkt-prose a, .mkt-article-body a { color: var(--teal); text-decoration: underline; }
.mkt-prose a:hover, .mkt-article-body a:hover { color: var(--teal-dk); }
/* Every image an author drops into a rich-text body, wherever that body ends
   up: a page's prose section, an article, or a testimonial card.

   max-width alone is not enough. The testimonial grid is auto-fit/minmax, and
   a track sized from a 4000px photo blew the columns apart and overlapped the
   cards — the images were constrained on story cards only, so a quote card
   rendered the file at its natural size. The cap on height is for the other
   direction: a portrait photo that satisfies max-width can still run several
   screens tall. Both are needed, and with height:auto the aspect ratio is
   preserved while the image shrinks to satisfy whichever binds first. */
.mkt-prose img,
.mkt-article-body img,
.mkt-testimonial-text img {
  display: block;
  max-width: 100%;
  width: auto;
  height: auto;
  max-height: 520px;
  margin: 16px auto;
  border-radius: 8px;
}
/* Inside a card the cap is tighter than in an article.
   A card is a quote or a short write-up with a picture in it, not a photo
   essay: at 520px the image is the card and the words are a caption under it.
   Capping the height rather than fixing an aspect ratio keeps every image the
   same scale without cropping anybody out of their own photograph — the
   pictures here are of named athletes and their families. */
.mkt-testimonial-text img { max-height: 320px; }
.mkt-prose hr, .mkt-article-body hr { border: none; border-top: 1px solid var(--border); margin: 28px 0; }

/* ── Author type controls ────────────────────────────────────────────────────
   The font, size and spacing an author picks in the CMS editor. These are the
   only classes content/typeclasses.go lets through the sanitiser; anything
   else in a class attribute drops the attribute, so this file and that one
   have to agree.

   Defined once and unscoped on purpose: the same authored HTML is rendered by
   .mkt-prose, .mkt-article-body, the testimonial cards, the manager's preview
   and the editor itself, and a control that worked in only some of those would
   make the editor lie about the result.

   Each selector names its class twice. The containers set type on the element
   (.mkt-prose p is 0,1,1), which would otherwise outrank a single class at
   0,1,0 and leave every control doing nothing on the public page. Doubling to
   0,2,0 wins without reaching for !important, which would in turn be
   unoverridable at a breakpoint. */
.rt-font-serif.rt-font-serif { font-family: Georgia, 'Times New Roman', Times, serif; }
.rt-font-mono.rt-font-mono   { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

/* Relative, not absolute: the base size differs between the editor, a prose
   section and a testimonial card, so em keeps one step meaning the same thing
   proportionally in all of them, and keeps a Large heading larger than Large
   body copy rather than flattening the two. */
.rt-size-sm.rt-size-sm { font-size: 0.875em; }
.rt-size-lg.rt-size-lg { font-size: 1.25em; }
.rt-size-xl.rt-size-xl { font-size: 1.5em; }

/* Spacing is one control over two properties, because "spacing" to an author
   means both the leading inside a paragraph and the gap after it; moving only
   one looks like the control half-worked. The gap is in em so it tracks the
   size chosen above. */
.rt-lead-tight.rt-lead-tight     { line-height: 1.4; margin-bottom: 0.5em; }
.rt-lead-relaxed.rt-lead-relaxed { line-height: 2;   margin-bottom: 1.5em; }

/* A list carries its spacing on the items, not the box. */
.rt-lead-tight.rt-lead-tight > li     { margin-bottom: 0.15em; }
.rt-lead-relaxed.rt-lead-relaxed > li { margin-bottom: 0.6em; }
.mkt-prose code, .mkt-article-body code { padding: 1px 5px; border-radius: 4px; background: var(--bg); font-size: 14px; }
.mkt-article-body h4 { font-size: 16px; font-weight: 700; color: var(--teal-dk); margin: 20px 0 8px; }
.mkt-article-body blockquote {
  border-left: 4px solid var(--teal); padding: 12px 20px; margin: 20px 0;
  background: var(--teal-lt); border-radius: 0 8px 8px 0;
  font-style: italic; color: var(--teal-dk);
}
.mkt-article-body figure { margin: 24px 0; }
.mkt-article-body figcaption { font-size: 13px; color: var(--text-s); text-align: center; margin-top: 6px; }

/* ── Shared free-form tag input (components/tag_input.templ) ───────────────── */
.tag-input {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 9px; background: #fff; cursor: text; min-height: 42px;
}
.tag-input:focus-within { border-color: var(--teal); }
.tag-chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--teal-lt); color: var(--teal-dk);
  border-radius: 6px; padding: 3px 8px; font-size: 13px; font-weight: 500;
}
.tag-chip-x {
  border: none; background: none; color: var(--teal-dk); cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0; opacity: .65;
}
.tag-chip-x:hover { opacity: 1; }
.tag-entry-wrap { position: relative; flex: 1; min-width: 120px; }
.tag-entry {
  width: 100%; border: none; outline: none; font-size: 14px;
  background: transparent; padding: 4px 2px;
}
.tag-suggestions {
  position: absolute; top: 100%; left: 0; z-index: 20; margin-top: 4px;
  min-width: 180px; max-height: 220px; overflow-y: auto;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,.08); padding: 4px;
}
.tag-suggestion {
  display: block; width: 100%; text-align: left; border: none; background: none;
  padding: 7px 10px; font-size: 13px; color: var(--text-m); cursor: pointer;
  border-radius: 6px;
}
.tag-suggestion:hover { background: var(--teal-lt); color: var(--teal-dk); }

/* ── Drill library: tag rows + item list / composer (M24) ─────────────────── */
.tag-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.drill-item-list { margin: 0; padding-left: 20px; }
.drill-item { padding: 4px 0; font-size: 14px; color: var(--text-d, #2A2A28); }
.drill-edit-row { display: flex; align-items: center; gap: 8px; }
.drill-item-input {
  flex: 1; border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; font-size: 14px; outline: none;
}
.drill-item-input:focus { border-color: var(--teal); }

/* ── Skill media + drill-skill chain (M24 Phase 2) ────────────────────────── */
.skill-video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.skill-video iframe, .skill-video video { width: 100%; aspect-ratio: 16/9; border: 0; border-radius: 8px; background: #000; }
.skill-video-title { font-size: 13px; color: var(--text-m); margin-top: 6px; }
.skill-video-edit { display: flex; flex-direction: column; gap: 6px; }
.skill-photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.skill-photo img { width: 100%; border-radius: 8px; display: block; }
.skill-photo figcaption { font-size: 12px; color: var(--text-lt); text-align: center; margin-top: 4px; }
.media-add-form { border: 1px dashed var(--border); border-radius: 8px; padding: 12px; margin-bottom: 14px; }
.skill-badge { display: inline-flex; align-items: center; gap: 4px; background: var(--teal-lt); color: var(--teal-dk); border-radius: 6px; padding: 3px 8px; font-size: 13px; font-weight: 500; }
.drill-skill-chain { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }

/* ── Trial pipeline kanban (M19) ──────────────────────────────────────────── */
.trial-board {
  display: grid;
  grid-template-columns: repeat(6, minmax(200px, 1fr));
  gap: 12px;
  overflow-x: auto;
  padding-bottom: 4px;
}
.trial-column {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  min-height: 200px;
}
.trial-column-over { border-color: var(--teal); background: var(--teal-lt); }
.trial-column-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-m);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.trial-column-count {
  background: var(--bg);
  border-radius: 10px;
  padding: 1px 7px;
  font-size: 11px;
}
.trial-column-body { padding: 8px; display: flex; flex-direction: column; gap: 8px; }
.trial-column-empty { padding: 8px 4px; font-size: 12px; }
.trial-card {
  display: block;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
  text-decoration: none;
  color: var(--text);
  cursor: grab;
}
.trial-card:hover { border-color: var(--teal); }
.trial-card-name { font-size: 13px; font-weight: 600; }
.trial-card-meta { font-size: 12px; color: var(--text-m); margin-top: 2px; }
.trial-card-date { font-size: 11px; color: var(--text-s); margin-top: 6px; }

.trial-activity { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.trial-activity-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.trial-activity-note { font-size: 13px; }
.trial-activity-time { font-size: 11px; color: var(--text-s); margin-left: auto; }

/* Live-search input at the top of the composer picker modals (M25). */
.library-picker-modal .library-picker-filter { padding: 10px 12px 0; }
.library-picker-modal .library-picker-filter input[type=search] { flex: 1; }

/* Inline quick-create block at the bottom of picker modals (M25). */
.library-picker-quick { border-top: 1px solid var(--border); padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
.library-picker-quick-title { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-s); }
.library-picker-quick-fields { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.library-picker-quick-fields input[type=text],
.library-picker-quick-fields select { height: 32px; padding: 0 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px; }
.library-picker-quick-fields input[type=text] { flex: 1; min-width: 140px; }

/* Session plan picker: module-chain preview per plan row (M25). */
.plan-picker-row { align-items: flex-start; }
.library-picker-row > div.plan-picker-main { display: block; flex: 1; min-width: 0; }
.plan-picker-chain { margin-top: 8px; }

/* "Update to vN" chip on composer rows whose pinned child version is stale (M25). */
.version-chip {
  border: 1px solid var(--teal-dk, #14785a);
  background: var(--teal-lt, #e3f4ee);
  color: var(--teal-dk, #14785a);
  border-radius: 999px;
  font-size: 12px;
  padding: 2px 10px;
  cursor: pointer;
}
.version-chip:hover { background: var(--teal-dk, #14785a); color: #fff; }

/* ── Quick workout (M28) ──────────────────────────────────────────────────────
   The free-text path: sets typed straight onto an event. The compose textarea
   is monospace so columns of repeats line up the way a coach writes them on
   paper, and the saved lines render as a plain numbered list. */
.quick-workout-body {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.7;
  tab-size: 4;
}
.quick-workout-list { margin: 8px 0 0; padding-left: 20px; }
.quick-workout-line {
  padding: 3px 0;
  font-size: 14px;
  color: var(--text-d, #2A2A28);
}
/* A quick workout written as a document. Headings are what separate warm-up
   from main set from per-group changes, so they are tightened rather than
   left at document scale — the panel is narrow and a coach reads this at the
   poolside on a phone. */
.quick-workout-doc {
  margin: 8px 0 0;
  font-size: 14px;
  color: var(--text-d, #2A2A28);
}
.quick-workout-doc > *:first-child { margin-top: 0; }
.quick-workout-doc > *:last-child { margin-bottom: 0; }
.quick-workout-doc p { margin: 0 0 8px; }
.quick-workout-doc h2,
.quick-workout-doc h3,
.quick-workout-doc h4 {
  margin: 14px 0 4px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
}
.quick-workout-doc ul,
.quick-workout-doc ol { margin: 0 0 8px; padding-left: 20px; }
.quick-workout-doc li { padding: 2px 0; }
.quick-workout-doc blockquote {
  margin: 8px 0;
  padding-left: 10px;
  border-left: 2px solid var(--border);
  color: var(--text-m);
}

.quick-workout-total {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  font-weight: 600;
  color: var(--teal-dk, #085041);
}

/* ── Portal welcome page (M33) ────────────────────────────────────────────── */
/* The first screen a family sees. Wider line-height and larger body type than
   the staff portal: this is read once, carefully, often on a phone, rather than
   scanned daily. */
.welcome-message { font-size: 15px; line-height: 1.7; }
.welcome-message p { margin: 0 0 12px; }
.welcome-message p:last-child { margin-bottom: 0; }

.guide-body { font-size: 15px; line-height: 1.7; margin-top: 12px; }
.guide-body h3 {
  font-size: 15px;
  font-weight: 700;
  margin: 20px 0 6px;
  color: var(--text);
}
.guide-body h3:first-child { margin-top: 0; }
.guide-body p { margin: 0 0 10px; }
.guide-body ul, .guide-body ol { margin: 0 0 10px 20px; }
.guide-body li { margin-bottom: 4px; }

/* One swimmer's block of paperwork. */
.form-athlete { border-top: 1px solid var(--border); padding-top: 14px; margin-top: 14px; }
.form-athlete:first-of-type { border-top: none; padding-top: 4px; margin-top: 8px; }
.form-athlete-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.form-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
}
.form-row + .form-row { border-top: 1px dashed var(--border); }
.form-row-main { min-width: 0; }
.form-row-title { font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
.form-row-tick { color: var(--success); font-weight: 700; }
.form-row-desc { font-size: 13px; color: var(--text-s); margin-top: 2px; }
.form-row-meta { font-size: 12px; color: var(--success); margin-top: 4px; }
/* The "view the copy on file" link sits inside the green confirmation line, so
   it needs to read as a link without fighting that colour. */
.form-row-meta a { color: var(--teal-dk); text-decoration: underline; }
.form-row-meta a:hover { color: var(--teal); }
.form-row-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

/* The file input is visually replaced by its own label, styled as a button —
   a bare <input type="file"> cannot be styled and reads as unfinished next to
   the Download link beside it. */
.form-row-upload { margin: 0; }
.form-row-file { position: relative; overflow: hidden; display: inline-block; cursor: pointer; }
.form-row-file input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 100%;
  cursor: pointer;
}

.form-reference { border-top: 1px solid var(--border); margin-top: 18px; padding-top: 12px; }
.form-reference-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-s);
  font-weight: 600;
  margin-bottom: 4px;
}

.welcome-done { margin-top: 20px; text-align: center; }

@media (max-width: 640px) {
  /* Stacked, with the actions full width: on a phone the Download link and the
     upload button next to it are the two things being tapped. Both fill the row
     and centre their labels so they read as an even pair — otherwise Download
     stretches while the upload button, a label sized to its text, stays short. */
  .form-row { flex-direction: column; gap: 10px; }
  .form-row-actions { width: 100%; }
  /* flex-basis:0 so the two share the row evenly rather than each starting from
     its own text width. They land near-equal, not pixel-identical: the upload
     label is white-space:nowrap, so its text ("Replace signed copy") sets a
     min-content floor a few px wider than Download, which is the right trade —
     forcing them equal would clip that label. */
  .form-row-actions > * { flex: 1 1 0; }
  .form-row-actions .btn-sm { justify-content: center; text-align: center; }
  /* The upload control is a <form> wrapping the label-as-button; make the form a
     flex box so its label can grow to the same full width as Download beside it. */
  .form-row-upload { display: flex; }
  .form-row-file { flex: 1; justify-content: center; }
}

/* ── Website media uploads (M33.1) ────────────────────────────────────────── */
/* The file input is hidden behind its label, styled as a button — same pattern
   as the welcome page's signed-form upload. */
.rt-upload, .media-upload { position: relative; overflow: hidden; cursor: pointer; flex-shrink: 0; }
.rt-upload input[type="file"], .media-upload input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 100%;
  cursor: pointer;
}
/* A URL field with an Upload button beside it: the branding logo, a blog
   post's featured image. Named for the pair rather than for the logo, which
   is where it started and no longer the only one. */
.media-field { display: flex; align-items: center; gap: 8px; }
.media-field input[type="text"] { flex: 1; min-width: 0; }
.media-preview {
  display: block;
  max-height: 48px;
  max-width: 200px;
  margin-top: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px;
  background: #fff;
}
/* The script unhides this by clearing the attribute, so the rule has to beat
   .media-preview's own display. */
.media-preview[hidden] { display: none; }

/* ── Portal error pages (M33.2) ───────────────────────────────────────────── */
/* Shown inside the dashboard shell, so this is page content rather than a
   full-bleed hero — the sidebar and top nav are already carrying the frame. */
.portal-error { max-width: 46ch; padding: 32px 0; }
.portal-error-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--text-s);
}
.portal-error h1 { font-size: 24px; font-weight: 700; margin: 6px 0 10px; letter-spacing: -0.02em; }
.portal-error p { margin: 0 0 8px; }
/* The reassurance line matters more than it looks: the page it replaces was
   mistaken for a forced logout. */
.portal-error-hint {
  font-size: 14px;
  color: var(--teal-dk);
  background: var(--teal-lt);
  border-radius: 8px;
  padding: 10px 14px;
  margin: 12px 0 0;
}
.portal-error .form-actions { margin-top: 20px; display: flex; gap: 8px; flex-wrap: wrap; }

/* Editor note on an email template — explains what the author cannot control. */
.email-note {
  background: var(--warn-lt);
  border-left: 3px solid var(--warn);
  border-radius: 0 8px 8px 0;
  padding: 12px 16px;
  font-size: 14px;
}
.email-note strong { display: block; margin-bottom: 4px; }
.email-note p { margin: 0 0 6px; }
.email-note p:last-child { margin-bottom: 0; }
