/* Self-hosted fonts. Outfit ships as a single variable woff2 covering
   weights 300-900; Fira Code as one file for 400 + 500. Replaced the
   Google Fonts CDN @import to drop the third-party DNS lookup +
   render-blocking serial fetch (slow on mobile carriers). */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('/fonts/outfit.woff2') format('woff2');
}
@font-face {
  font-family: 'Fira Code';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('/fonts/firacode.woff2') format('woff2');
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  padding: 0;
  font-family: 'Outfit', sans-serif;
  background: var(--cream);
  color: var(--gray-900);
}

a { color: inherit; }

/* ---- CARDS ---- */
/* ---- Containers are BORDERLESS (owner, 2026-09-11: "borderless containers") ----
   The 1px line goes; a soft shadow replaces it. The shadow is not decoration:
   a white card on the club's cream ground separates by surface contrast alone
   on some themes (military reads 1.276:1, fine) but NINE of the shipped
   presets fall to 1.05-1.13:1, where a borderless card would simply vanish.
   All 13 dashboard-skins override .card's border with !important, so skinned
   clubs are untouched by this and keep their own card language. */
.card {
  background: var(--white);
  border: 0;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 8px rgba(15, 23, 42, 0.04);
}
.card-hover { transition: all 0.2s; }
.card-hover:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
}
.stat-card {
  background: var(--white);
  border: 0;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 8px rgba(15, 23, 42, 0.04);
}

/* ---- BUTTONS ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: none;
  transition: all 0.15s;
  text-decoration: none;
}
.btn-primary { background: var(--accent); color: var(--accent-text, #fff); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary { background: var(--primary); color: #fff; }
.btn-secondary:hover { background: var(--primary-light); }
.btn-outline { background: transparent; color: var(--gray-700); border: 1px solid var(--gray-300); }
.btn-outline:hover { background: var(--gray-100); }
.btn-ghost { background: transparent; color: var(--gray-600); }
.btn-ghost:hover { background: var(--gray-100); }
.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { background: #b91c1c; }
.btn-sm { padding: 5px 12px; font-size: 12px; }

/* ---- FORM CONTROLS ---- */
.input {
  padding: 9px 14px;
  border: 1px solid var(--gray-300);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  outline: none;
  width: 100%;
  background: var(--white);
  transition: border-color 0.15s;
}
.input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 8%, transparent);
}
select.input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%236b7280%22%20stroke-width%3D%222%22%3E%3Cpath%20d%3D%22m6%209%206%206%206-6%22%2F%3E%3C%2Fsvg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
}

/* Club-store detail page Color / Size selects — heavy themed treatment.
   Reads less like a form field and more like a CTA button: 2px accent
   border, pill radius, bold text, soft accent shadow on hover, and an
   accent-colored chevron painted at runtime (see club-store-detail.php
   JS theming pass). All sourced from var(--accent) so themes auto-flow. */
select.cs-pf-color,
select.cs-pf-size {
  appearance: none;
  background-color: var(--white);
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 14px 14px;
  border: 2px solid var(--accent);
  border-radius: 14px;
  padding: 14px 46px 14px 18px;
  font-size: 15px;
  font-weight: 600;
  color: var(--gray-900);
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
  transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s, transform 0.05s;
  width: 100%;
  box-sizing: border-box;
  outline: none;
}
select.cs-pf-color:hover:not(:disabled),
select.cs-pf-size:hover:not(:disabled) {
  background-color: color-mix(in srgb, var(--accent) 6%, var(--white));
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 22%, transparent);
}
select.cs-pf-color:active:not(:disabled),
select.cs-pf-size:active:not(:disabled) {
  transform: translateY(1px);
}
select.cs-pf-color:focus,
select.cs-pf-size:focus {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent);
}
select.cs-pf-color:disabled,
select.cs-pf-size:disabled {
  border-color: var(--gray-300);
  background-color: var(--gray-100);
  color: var(--gray-500);
  cursor: not-allowed;
  font-weight: 500;
  box-shadow: none;
}
textarea.input { resize: vertical; min-height: 80px; }

.label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-700);
  margin-bottom: 6px;
}

.form-group { margin-bottom: 18px; }

/* ---- BADGES ---- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  border-radius: 50px;
  font-size: 11px;
  font-weight: 600;
}

/* ---- PAGE HEADERS ---- */
.page-title {
  font-size: 24px;
  font-weight: 700;
  color: var(--gray-900);
  margin: 0 0 4px;
}
.page-subtitle {
  font-size: 14px;
  color: var(--gray-500);
  /* Was 24px. The subtitle sits inside .page-header's inner div, which is a
     flex item and therefore a BFC root, so this margin cannot collapse out —
     it just inflates the header box on top of the header's own bottom margin.
     Two shared declarations were producing a 48px dead band under every
     member page title. */
  margin: 0 0 12px;
}

/* ---- GRIDS ---- */
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }

/* Direct .card children of grid-* containers stretch to fill their cell
   and become flex columns. Keeps every card in a row the same height
   regardless of variable middle content — page-specific cards can use
   margin-top:auto on their footer/CTA row to pin to the bottom. */
.grid-2 > .card,
.grid-3 > .card,
.grid-4 > .card {
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* ---- TABLES ---- */
.table { width: 100%; border-collapse: collapse; }
.table th {
  text-align: left;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  color: var(--gray-500);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-bottom: 2px solid var(--gray-200);
}
.table td {
  padding: 12px 14px;
  font-size: 13px;
  border-bottom: 1px solid var(--gray-100);
}
.table tr:hover td { background: var(--gray-50); }

/* ---- TABS ---- */
.tab-bar {
  display: flex;
  gap: 6px;
  margin-bottom: 24px;
  flex-wrap: wrap;
  padding: 4px 0;
}
.tab-bar::-webkit-scrollbar { display: none; }
.tab {
  padding: 8px 18px;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-600);
  cursor: pointer;
  border: 1px solid var(--gray-200);
  border-radius: 50px;
  transition: all 0.15s;
  background: var(--white);
  font-family: inherit;
  white-space: nowrap;
  text-align: center;
}
.tab.active {
  color: #fff;
  background: var(--primary);
  border-color: var(--primary);
  font-weight: 600;
}
.tab:hover:not(.active) {
  background: var(--gray-100);
  border-color: var(--gray-300);
  color: var(--gray-800);
}

/* ---- EMPTY STATE ---- */
.empty-state {
  text-align: center;
  padding: 60px 20px;
  color: var(--gray-400);
}
.empty-state svg {
  margin-bottom: 16px;
  opacity: 0.4;
}

/* ---- TOGGLE ---- */
.toggle {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex-shrink: 0;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .slider {
  position: absolute;
  cursor: pointer;
  top: 0; left: 0; right: 0; bottom: 0;
  background: var(--gray-300);
  border-radius: 24px;
  transition: 0.3s;
}
.toggle .slider::before {
  content: '';
  position: absolute;
  height: 18px;
  width: 18px;
  left: 3px;
  bottom: 3px;
  background: #fff;
  border-radius: 50%;
  transition: 0.3s;
}
.toggle input:checked + .slider { background: var(--accent); }
.toggle input:checked + .slider::before { transform: translateX(20px); }

/* ---- Inline container borders, same treatment ----
   Changing .card reaches 141 nodes; the member surface hard-codes its
   container borders INLINE in each page's JS, and the two sets barely
   intersect (live.php — the page the owner screenshotted — has zero .card
   nodes). These attribute rules catch the two NEUTRAL greys only.
   ⚠️ Deliberately NOT the semantic borders: #fecaca / #dc2626 (danger),
   #bbf7d0 / #86efac (success) and #f3d488 (warning) are status banners whose
   edge is the signal. Only #e5e7eb and #d1d5db are containers.
   ⚠️ border-color:transparent, not border:0 — the 1px keeps its place in the
   box so nothing shifts by 2px.
   ⚠️ Scoped to :not([data-tpl]) so skinned clubs keep their own card
   language; the owner asked for this on the default theme. */
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border:1px solid #e5e7eb"],
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border: 1px solid #e5e7eb"],
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border:1.5px solid #e5e7eb"],
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border: 1.5px solid #e5e7eb"],
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border:1px solid #d1d5db"],
body:not(.tc-admin) #page-content:not([data-tpl]) [style*="border: 1px solid #d1d5db"] {
  border-color: transparent !important;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 8px rgba(15, 23, 42, 0.04);
}

/* ---- Compact member cards (owner 2026-09-11: "make smaller, more compact
   cards") ----------------------------------------------------------------
   tcMemberCardHtml() builds these with INLINE styles, so overriding needs
   !important. Scoped to :not([data-tpl]) because the 13 skins carry their own
   card language (members-skins.js:103 already re-pads .member-card-inner).
   The banner was aspect-ratio 2/1 — on a 3-up grid that is a ~197px photo
   before any text, which is most of the card's height. */
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-banner { aspect-ratio: 2.8 / 1 !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-avatar-ring { margin-top: -22px !important; border-width: 2px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-avatar-ring > * { width: 52px !important; height: 52px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .member-card-inner { padding: 0 12px 12px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .member-card-inner .member-name { margin-top: 7px !important; font-size: 14px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-badges { margin-top: 6px !important; gap: 5px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-details { margin-top: 8px !important; gap: 3px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .member-detail-line { font-size: 11.5px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .member-meta { margin-top: 10px !important; padding-top: 9px !important; font-size: 11.5px !important; }
body:not(.tc-admin) #page-content:not([data-tpl]) .mc-cta { margin-top: 9px !important; font-size: 12px !important; }

/* ---- RESPONSIVE ---- */
@media (max-width: 1024px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .tab { padding: 10px 16px; font-size: 13px; }
  #page-content { padding: 16px !important; }
}
@media (max-width: 480px) {
  #page-content { padding: 12px !important; }
  .app-header { padding: 0 12px !important; }
  .tab { padding: 10px 12px; font-size: 12px; min-height: 40px; }
  .tab-bar {
    gap: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap;
  }
  .tab-bar .tab { white-space: nowrap; flex-shrink: 0; }
  /* TOUCH TARGETS. The type stays at its desktop size (these are the same
     buttons, not a smaller variant) — only the BOX grows, via min-height, so
     a thumb has something to land on. 44px is the iOS Human Interface
     minimum; .btn-sm keeps a visible step down at 40px rather than
     collapsing the two sizes into one. Before this the pair measured 36px
     and 28px, and .btn-sm additionally SHRANK on mobile (11px/5px padding),
     which is backwards: a phone is where a control needs to be bigger. */
  .btn { font-size: 13px; min-height: 44px; }
  .btn-sm { font-size: 12px; padding: 7px 12px; min-height: 40px; }
  h1, h2 { font-size: 20px !important; }
  .card { padding: 14px !important; }
  .grid-2, .grid-3, .grid-4 {
    grid-template-columns: 1fr !important;
    gap: 10px !important;
  }
  .form-group { margin-bottom: 12px; }
  .label { font-size: 12px; }
  /* 16px, NOT 14px, and not for looks: iOS Safari zooms the whole page in
     when a field whose font-size is under 16px takes focus, and it does not
     zoom back out. Every form in the member app (account, the filter rows,
     the trip-report wizard) was doing that on a phone. The bare-element half
     of the selector catches the ~260 inputs that never got the .input class.
     Keep any future mobile field at 16px or larger for the same reason. */
  .input, select.input, textarea.input,
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="file"]),
  select, textarea { font-size: 16px; }
  .input, select.input, textarea.input { padding: 10px 12px; }
  /* 86 fields set font-size INLINE (13px on 62 of them), and an inline
     declaration outranks any author rule, so the floor above never reaches
     them — the same escalation the admin block below documents. None of the
     86 is above 16px, so this only ever raises. min-height (not height) is
     enough to beat the handful that also pin an inline 34-38px height, since
     min-height clamps height. Checkboxes, radios and the offscreen file
     inputs are excluded: they set a deliberate 16-18px box. */
  input[style]:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="file"]),
  select[style], textarea[style] { font-size: 16px !important; min-height: 40px; }
  .table th, .table td { padding: 10px 8px; font-size: 12px; }
  .table { font-size: 12px; }
  .modal-overlay > div { max-width: calc(100vw - 24px) !important; border-radius: 12px !important; }
  .page-header { flex-direction: column; align-items: flex-start !important; gap: 10px; }
  .page-header .btn { width: 100%; justify-content: center; }
  /* The off-canvas sidebar is thumb-driven on a phone. Its rows carry their
     padding INLINE from shared.js (renderSidebarNav), so height is set here
     where nothing inline competes for it. */
  #sidebar nav a, #sidebar .nav-group-header { min-height: 44px; }
  #sidebar .nav-group-header { display: flex; align-items: center; }
}

/* ---- AUTO-LINKED TEXT ----
   Used by tcLinkifyText (shared.js) and tc_linkify_text (lib/text_linkify.php)
   wherever member/admin free-form text gets URL/email auto-linking. The
   in-app surface (Trail Talk posts today) inherits this rule; public club
   templates override per-theme. word-break stops a long URL from breaking
   horizontal post layout. */
.tc-autolink {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.tc-autolink:hover { color: var(--primary-light); }

/* Link chip: URL in member COMMENT text rendered as a host-only pill
   (tcLinkifyText {chips:true} / tc_linkify_text($chips=true)). Sized to sit
   inline with 13-14px comment text without growing the line box:
   12px * 1.4 + 2px padding + 2px border < 13.5px * 1.55. The full URL is in
   href + title. Tokens only, so club themes recolor it for free. */
.tc-linkchip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  padding: 1px 8px;
  border-radius: 12px;
  border: 1px solid #dc2626;
  background: #dc2626;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  vertical-align: baseline;
  white-space: nowrap;
  overflow: hidden;
  text-decoration: none;
  font-family: 'Outfit', sans-serif;
  color: #fff;
}
.tc-linkchip::after { content: "\2197"; font-size: 10px; opacity: .9; }
.tc-linkchip:hover { background: #b91c1c; border-color: #b91c1c; color: #fff; text-decoration: none; }

/* Emoji reaction chips on comment threads (tcReactionChipsHtml, shared.js):
   convoy + event discussion, trip-report comments, pack comments, Trading
   Post questions. Trail Talk has its own .tt-react-* chips — not these.
   NO fixed colours: the resting chip is drawn off currentColor (so it reads
   on every Member 2.0 skin, dark ones included, without a per-template
   rule) and the active states tint with the club accent while the text
   keeps the surrounding ink. The glyph is desaturated until someone has
   reacted (.tc-react-glyph-dim), Trail Talk parity. */
.tc-react-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tc-react-btn {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 28px; padding: 3px 10px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, currentColor, transparent 78%);
  background: color-mix(in srgb, currentColor, transparent 96%);
  color: inherit; font: inherit; font-size: 12px; line-height: 1.2;
  cursor: pointer; transition: background .15s ease, border-color .15s ease, transform .12s ease;
}
.tc-react-btn:hover { border-color: color-mix(in srgb, currentColor, transparent 55%); }
.tc-react-btn:active { transform: scale(.96); }
.tc-react-btn.tc-react-has {
  background: color-mix(in srgb, var(--accent, var(--primary, #C8842A)), transparent 88%);
  border-color: color-mix(in srgb, var(--accent, var(--primary, #C8842A)), transparent 55%);
}
.tc-react-btn.tc-react-on {
  background: color-mix(in srgb, var(--accent, var(--primary, #C8842A)), transparent 72%);
  border-color: var(--accent, var(--primary, #C8842A));
  font-weight: 700;
}
.tc-react-btn.tc-react-ro { cursor: default; }
.tc-react-glyph-dim { filter: grayscale(1); opacity: .55; }
.tc-react-count { font-weight: 700; font-size: 11px; }
.tc-linkchip:active { background: #991b1b; border-color: #991b1b; color: #fff; }

/* ---- SCROLLBARS ---- */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--gray-400); }

/* ===========================================================================
   ADMIN UI LAYER — the shared admin design language (restyle Batch 0).
   Reference: the admin hub redesign (admin-dashboard.php, Mike's mock,
   2026-09-01). Everything here is scoped under body.tc-admin, which
   renderAppShell (shared.js) adds only when tcIsAdminSectionPage() is true —
   nothing below can reach a member page. Cache-busting rides the existing
   styles.css?v=tc_asset_v() link every page already emits.

   RULED (Mike, 2026-09-01): admin surfaces are theme fixed — the TrailConvoy
   pine/paper/orange set below, never the club theme. Every color in the
   admin section comes from these tokens; pages add only genuinely
   page-specific layout in their own <style> blocks.
   =========================================================================== */
body.tc-admin {
  --ah-ink: #22432E;          /* primary ink */
  --ah-paper: #FDFBF4;        /* card ground */
  --ah-line: #E3DAC4;         /* borders, dividers */
  --ah-line-hover: #CBBF9F;   /* card hover border */
  --ah-title: #23211A;        /* card title ink */
  --ah-desc: #6E6754;         /* secondary text */
  --ah-chev: #9A9078;         /* chevron, muted glyphs */
  --ah-accent: #C8842A;       /* orange CTA (matches the chrome close) */
  --ah-accent-hover: #B07325; /* CTA hover */
  --ah-heading: #1C1C14;      /* section eyebrow / page title ink */
  /* Page ground (Mike, 2026-09-18). Fixed for every club: the Military
     theme's background (#E4E4DF) taken darker so the paper cards read as
     cards against it — #DCDCD6 on the first pass, dropped again to #D2D2CB
     the same day on "a little darker".
     Before this the admin body wore COLORS.cream —
     the CLUB theme's ground — which on the pale themes sat within a hair of
     --ah-paper and flattened every card on the page. renderAppShell paints
     the body with var(--ah-bg) on admin pages only; member pages keep
     COLORS.cream untouched. */
  --ah-bg: #D2D2CB;
}

/* ---- Width containers ----
   .ah-page is the default: capped and centered (satisfies every deliberate
   cap in the admin surface; ends stretched grids at 1920px). .ah-page-wide
   is the opt-out for spreadsheets and split editors. Both bake in the
   -12px pull toward the admin bar that the hub used to hack in via
   .ah-eyebrow:first-child (the shell's page-content padding is 28px;
   Mike wants the first heading tighter). */
body.tc-admin .ah-page { max-width: 1240px; margin: -12px auto 0; }
body.tc-admin .ah-page-wide { margin-top: -12px; }

/* ---- Section eyebrow ----
   ⚠️ RESTORED 2026-09-11. This rule was deleted by accident when the member
   width container was reverted: that edit walked forward to the third closing
   brace after its own declaration and took the next rule with it, so the admin
   hub's section headings ("Club Administration", "Setup and Feature Settings")
   silently lost their size, weight, colour and margin. Nothing but the owner
   noticing caught it. If you remove a block by brace counting, diff the
   removed TEXT, not the line count.
   Bolder than the original 18px/700, per the owner on the same report. The
   spacing was 34/20 on the first pass and trimmed to 26/12 when he said that
   was a little too much; the original was 18/8. */
body.tc-admin .ah-eyebrow {
  font-size: 19px;
  font-weight: 800;
  color: var(--ah-heading);
  margin: 26px 0 12px;
}
/* The first heading used to be pinned tight to the admin bar (margin-top:0).
   The owner asked for margin AROUND this row, and on the hub this row also
   carries the Help button on its right, so it gets air above as well. */
body.tc-admin .ah-page > .ah-eyebrow:first-child { margin-top: 10px; }

/* ---- Chevron cards (the hub's nav-card anatomy) ---- */
body.tc-admin .ah-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--ah-paper);
  border: 1px solid var(--ah-line);
  border-radius: 10px;
  padding: 12px 14px;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.15s, border-color 0.15s, transform 0.15s;
}
body.tc-admin .ah-card:hover {
  border-color: var(--ah-line-hover);
  box-shadow: 0 3px 10px rgba(60, 50, 20, 0.10);
  transform: translateY(-1px);
}
body.tc-admin .ah-chev { color: var(--ah-chev); }
body.tc-admin .ah-card .ah-chev { position: absolute; top: 10px; right: 10px; }
body.tc-admin .ah-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--ah-title);
  line-height: 1.2;
}
body.tc-admin .ah-desc {
  font-size: 12.5px;
  color: var(--ah-desc);
  line-height: 1.45;
  margin-top: 3px;
}
body.tc-admin .ah-glyph {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ah-ink);
}
body.tc-admin .ah-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 5px;
  margin-left: 7px;
  border-radius: 50px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  vertical-align: 2px;
}

/* ---- Compact card rows (the hub's Setup zone) ---- */
body.tc-admin .ah-setup-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(225px, 1fr));
  gap: 10px;
}
body.tc-admin .ah-setup-grid .ah-card { align-items: center; padding: 10px 28px 10px 12px; }
body.tc-admin .ah-setup-grid .ah-chev { top: 50%; transform: translateY(-50%); }
body.tc-admin .ah-setup-grid .ah-title { font-size: 14px; }

/* ---- Buttons ----
   .ah-btn is the accent CTA (generalized from the hub's .ah-new-event;
   works as <a> or <button>). Add .ah-btn-ghost for the quiet bordered
   secondary. The real :disabled rule replaces the per-page inline
   opacity hacks (admin-tools, import-map, store). */
body.tc-admin .ah-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--ah-accent);
  color: #fff;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 8px;
  /* 6+1px border = the reference .ah-new-event's 7px 13px borderless box:
     the ghost variant needed a real border, this keeps outer dims exact. */
  padding: 6px 12px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s;
}
body.tc-admin .ah-btn:hover:not(:disabled) { background: var(--ah-accent-hover); }
body.tc-admin .ah-btn:disabled { opacity: 0.55; cursor: not-allowed; }
body.tc-admin .ah-btn.ah-btn-ghost {
  background: #fff;
  color: var(--ah-title);
  border-color: var(--ah-line);
}
body.tc-admin .ah-btn.ah-btn-ghost:hover:not(:disabled) {
  background: #fff;
  border-color: var(--ah-line-hover);
}
/* Destructive CTA (promoted to the shared layer in Batch 5 — the pages of
   Batches 0-4 carried it as an inline style="background:COLORS.danger" on
   ~48 modal confirms, which also defeated the shared :hover feedback
   because an inline background outranks the rule above). Same geometry as
   .ah-btn; semantic danger ground at rest (--danger is the runtime token
   shared.js injects from COLORS.danger, the same value the inline sites
   painted), a darkened danger on hover. */
body.tc-admin .ah-btn.ah-btn-danger { background: var(--danger); }
body.tc-admin .ah-btn.ah-btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 82%, #000); }
/* Compact size (promoted to the shared layer in Batch 4 item 4 — second-
   consumer rule: admin-newsletter carried it as the disclosed page-local
   .nl-sm chip for its block toolbars and row actions; the meetings family's
   item toolbars, outcome buttons and modal steppers are the second consumer).
   Combine with .ah-btn-ghost for the quiet chip; works as <a> or <button>. */
body.tc-admin .ah-btn.ah-btn-sm { padding: 3px 8px; font-size: 12px; border-radius: 7px; }
/* Icon-only borderless button (promoted to the shared layer in Batch 4 —
   second-consumer rule: admin-waivers' document-card edit / view /
   download / delete trio and category-row delete, and the interactive
   waiver builder's field-card move / remove controls; the base
   .btn-ghost.btn-sm icon buttons it replaces leaked club grays). Works as
   <a> or <button>; muted glyph at rest, ink on hover. Add
   .ah-icon-btn-danger for the semantic red hover on delete controls. */
body.tc-admin .ah-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 6px;
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  color: var(--ah-chev);
  text-decoration: none;
  font: inherit;
  line-height: 0;
  transition: color 0.12s, background 0.12s;
}
body.tc-admin .ah-icon-btn:hover:not(:disabled) { color: var(--ah-ink); background: rgba(60, 50, 20, 0.05); }
body.tc-admin .ah-icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
body.tc-admin .ah-icon-btn.ah-icon-btn-danger:hover:not(:disabled) { color: var(--danger); }

/* ---- Tables ----
   One admin table idiom: a paper card with line rules and zebra rows.
   ALWAYS render .ah-table inside an .ah-table-wrap (the overflow-x
   contract — wide rosters scroll inside the card, never the page).
   Add .ah-table-sticky to the wrap for the sticky-header spreadsheet
   variant (the wrap then owns vertical scroll too). */
body.tc-admin .ah-table-wrap {
  overflow-x: auto;
  background: var(--ah-paper);
  border: 1px solid var(--ah-line);
  border-radius: 10px;
}
body.tc-admin .ah-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: var(--ah-title);
}
body.tc-admin .ah-table th {
  text-align: left;
  padding: 9px 12px;
  font-size: 11px;
  font-weight: 700;
  color: var(--ah-desc);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--ah-line);
  background: var(--ah-paper);
  white-space: nowrap;
}
body.tc-admin .ah-table td {
  padding: 9px 12px;
  border-top: 1px solid var(--ah-line);
}
body.tc-admin .ah-table tbody tr:first-child td { border-top: none; }
body.tc-admin .ah-table tbody tr:nth-child(even) td { background: rgba(60, 50, 20, 0.035); }
body.tc-admin .ah-table-sticky { max-height: 72vh; overflow-y: auto; }
body.tc-admin .ah-table-sticky th { position: sticky; top: 0; z-index: 1; }
/* Sticky variant runs the SEPARATE border model: with border-collapse:
   collapse, cell borders paint on the collapsed table grid and do not
   travel with a position:sticky th in Chrome/WebKit — the header's 1px
   bottom rule scrolls away with the body. In the separate model each th
   owns its border-bottom, so the rule sticks with the header. (The
   pre-restyle .nmr-table carried border-collapse: separate for exactly
   this; the shared td border-top / tr:first-child td override pair keeps
   the row rules single-lined in either model.) */
body.tc-admin .ah-table-sticky .ah-table { border-collapse: separate; border-spacing: 0; }

/* ---- Row lists ----
   Div-grid "tables" (store SKUs, orders) and stacked row lists adopt the
   same paper-card-with-line-rules look without becoming <table>s. */
body.tc-admin .ah-rows {
  background: var(--ah-paper);
  border: 1px solid var(--ah-line);
  border-radius: 10px;
  overflow: hidden;
}
body.tc-admin .ah-rows > * + * { border-top: 1px solid var(--ah-line); }

/* ---- Forms ----
   .ah-form is a paper well; labels and inputs inside pick up the line
   borders. Existing .input/.label hook classes keep working on top. */
body.tc-admin .ah-form {
  background: var(--ah-paper);
  border: 1px solid var(--ah-line);
  border-radius: 10px;
  padding: 16px;
}
body.tc-admin .ah-form .label {
  font-size: 12px;
  font-weight: 600;
  color: var(--ah-desc);
}
body.tc-admin .ah-form .input {
  border: 1px solid var(--ah-line);
  background: #fff;
}
body.tc-admin .ah-form .input:focus {
  border-color: var(--ah-line-hover);
  box-shadow: 0 0 0 3px rgba(200, 132, 42, 0.12);
}

/* Fixed palette for the standard hook classes ANYWHERE in the admin section
   (modals included — the .ah-form rules above only reach its own wells).
   The base .input:focus ring is var(--primary) and the checked .toggle
   slider is var(--accent): both club theme, ruled out of admin surfaces
   (de-theming is RULED). Surfaced by the announcements pilot (its upsell
   toggles + modal inputs); one rule each ends the leak for every batch. */
body.tc-admin .input:focus {
  border-color: var(--ah-line-hover);
  box-shadow: 0 0 0 3px rgba(200, 132, 42, 0.12);
}
body.tc-admin .toggle input:checked + .slider { background: var(--ah-ink); }
/* Disabled toggles dim (promoted to the shared layer in Batch 3 — second
   consumer rule: admin-settings' access matrix disables Post toggles when
   Read is off, and admin-payments dims each payment-method toggle while its
   save round-trip is in flight; the base .toggle has no :disabled look). */
body.tc-admin .toggle input:disabled + .slider { opacity: 0.55; cursor: not-allowed; }

/* ---- Page title band (adminPageHeader in shared.js) ----
   Thin one-line orientation band under the admin bar: title, inline
   subtitle, actions on the right, a line rule below. Pending Mike's
   title-band ruling (plan open question 2) — cheap to remove. */
body.tc-admin .ah-page-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  /* 20, between the original 16 and the 24 that read as too much: this row
     carries the page title AND the help button on its right. */
  margin: 0 0 20px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ah-line);
}
body.tc-admin .ah-page-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--ah-heading);
  margin: 0;
  line-height: 1.2;
}
body.tc-admin .ah-page-sub { font-size: 12.5px; color: var(--ah-desc); }
body.tc-admin .ah-page-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ---- Sub-tabs (adminSubTabs in shared.js) ----
   Flat segmented pills on a paper strip; the active pill fills with the
   fixed pine ink (never the club theme). Deep-link contracts (data-subtab,
   data-help, ?tab=/hash hrefs) ride the markup, not the styling. */
body.tc-admin .ah-subtabs {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  background: var(--ah-paper);
  border: 1px solid var(--ah-line);
  border-radius: 10px;
  margin: 0 0 18px;
}
body.tc-admin .ah-subtab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: 7px;
  border: 1px solid transparent;
  font-size: 13px;
  font-weight: 600;
  color: var(--ah-desc);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
body.tc-admin .ah-subtab:hover:not(.active) { color: var(--ah-title); background: rgba(60, 50, 20, 0.05); }
body.tc-admin .ah-subtab.active { background: var(--ah-ink); color: #fff; }
/* Control-slot variant (restyle Batch 5, adminSubTabs opts.control): the
   strip and a right-aligned control share one row; the row owns the bottom
   margin so the strip's own margin is zeroed inside it. The control is
   whatever the caller passes (analytics: an .input select); the slot only
   places it. */
body.tc-admin .ah-subtabs-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 18px;
}
body.tc-admin .ah-subtabs-row .ah-subtabs { margin-bottom: 0; }
body.tc-admin .ah-subtabs-control { display: flex; align-items: center; gap: 8px; }
body.tc-admin .ah-subtabs-control .input { margin: 0; }

/* ---- Modal + toast paper skin ----
   Same scope, class names untouched (tests pin .modal-overlay /
   .modal-close-btn). openModal/showToast set inline styles, hence the
   !important — same precedent as the 480px modal override above. */
body.tc-admin .modal-overlay > div {
  background: var(--ah-paper) !important;
  border: 1px solid var(--ah-line) !important;
  border-radius: 10px !important;
}
body.tc-admin .tc-toast-backdrop > div {
  background: var(--ah-paper) !important;
  border-radius: 10px !important;
}

/* ---- Admin-layer responsive ---- */
@media (max-width: 480px) {
  body.tc-admin .ah-subtabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  body.tc-admin .ah-page-actions { width: 100%; margin-left: 0; }
  /* The sub-tab row's control drops under the strip and fills the width
     (the pre-restyle analytics selects went width:100% !important at this
     breakpoint). The caller-supplied control legitimately carries its desktop
     sizing inline (analytics: width:180px / max-width:300px), and an inline
     declaration outranks any class rule, so the reset needs !important to
     win, the same precedent as the modal paper skin above. */
  body.tc-admin .ah-subtabs-control { width: 100%; }
  body.tc-admin .ah-subtabs-control .input { width: 100% !important; max-width: none !important; }
  /* Two meeting bands (admin-meeting-edit, admin-meeting-live driver) carry
     the legacy .page-header HOOK class through adminPageHeader's wrapClass
     slot (meetings-pages.mjs 11c pins the band by it). The base mobile rule
     above (`.page-header { flex-direction: column; align-items: flex-start
     !important }`) would stack them where every other band stays a row —
     pin the shared band layout back; !important answers !important. */
  body.tc-admin .ah-page-head.page-header { flex-direction: row; align-items: baseline !important; gap: 10px; }
}

/* ---- BADGE GLYPH (.tcbg) — the club patch ----
   Rendered by tcBadgeGlyph() in shared.js. A layered shield: ink rim, thin
   light ring, gradient fill face (--tcbg-fill), white glyph. --tcbg-size sets
   the box; the rim reads --tpl-ink on skinned pages and --primary otherwise. */
.tcbg { position: relative; display: inline-block; width: var(--tcbg-size, 56px); height: var(--tcbg-size, 56px); flex-shrink: 0; vertical-align: middle; filter: drop-shadow(0 3px 3px rgba(0,0,0,0.28)); }
.tcbg-rim, .tcbg-ring, .tcbg-face { position: absolute; clip-path: polygon(50% 0%, 100% 17%, 100% 62%, 50% 100%, 0% 62%, 0% 17%); }
.tcbg-rim  { inset: 0; background: var(--tcbg-rim, var(--tpl-ink, var(--primary, #1f1f19))); }
.tcbg-ring { inset: calc(var(--tcbg-size, 56px) * 0.075); background: rgba(255,255,255,0.55); }
/* Flat fill first; the color-mix() gradient only where it is supported (iOS
   15 lacks it). A plain fallback declaration would not help here: the var()
   inside makes an unsupported mix invalid at computed time, not parse time. */
.tcbg-face { inset: calc(var(--tcbg-size, 56px) * 0.105); background: var(--tcbg-fill, #7d7a55); }
@supports (color: color-mix(in srgb, red, blue)) {
  .tcbg-face { background: linear-gradient(165deg, color-mix(in srgb, var(--tcbg-fill, #7d7a55), #fff 16%) 0%, var(--tcbg-fill, #7d7a55) 52%, color-mix(in srgb, var(--tcbg-fill, #7d7a55), #000 24%) 100%); }
}
.tcbg-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; padding-bottom: calc(var(--tcbg-size, 56px) * 0.07); filter: drop-shadow(0 1px 1px rgba(0,0,0,0.5)); }
.tcbg-icon svg { stroke-width: 2.4; }
.tcbg-text { font-family: var(--tpl-disp, inherit); font-weight: 700; font-size: calc(var(--tcbg-size, 56px) * 0.42); line-height: 1; letter-spacing: -0.02em; }
.tcbg-locked { filter: grayscale(1) opacity(0.5) drop-shadow(0 2px 2px rgba(0,0,0,0.2)); }
.tcbg-locked .tcbg-face { background: #8b8b84; }
.tcbg-locked .tcbg-ring { background: rgba(255,255,255,0.35); }
