/*
Theme Name: RallyTrain Theme
Theme URI: https://rallytrain.org
Description: The RallyTrain theme. Self-contained classic theme with no external parent; site identity, palette, and page copy come from the RallyTrain plugin's per-site settings (defaults ship as Henry PTA's black and gold).
Author: RallyTrain
Version: 2.19.2
Requires at least: 6.0
Tested up to: 6.7
Requires PHP: 8.0
Text Domain: rallytrain
*/

/* ============================================================
   Webfonts: self-hosted, matching the PTA's social-graphic style.
   Bebas Neue and Yellowtail, both SIL Open Font License, via
   fontsource builds of the Google Fonts originals.
   ============================================================ */
@font-face {
  font-family: "RT Display";
  src: url("assets/fonts/bebas-neue-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "RT Script";
  src: url("assets/fonts/yellowtail-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   Default palette: Henry PTA black + gold (overridden per site via rt_palette)
   ============================================================ */
:root {
  --rt-primary: #f4b41c;
  --rt-primary-bright: #ffc845;
  --rt-secondary: #d99a00;
  /* Text ON the primary fill, and the muted divider gray. Both are real
     palette tokens; the plugin's inline :root overrides this fallback. */
  --rt-on-primary: #141414;
  --rt-neutral: #c9c3b4;
  --rt-black: #141414;
  --rt-ink: #1f1f1f;
  --rt-cream: #fbf6ec;
  --rt-white: #ffffff;
  --rt-gray: #6b6b6b;
  --rt-line: #e7e2d6;

  --rt-font: "Helvetica Neue", Arial, "Segoe UI", system-ui, sans-serif;
  --rt-font-display: "RT Display", "Arial Narrow", sans-serif;
  --rt-radius: 10px;
  --rt-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
  /* Small elements: buttons, social tiles, the banner's copy panels. Tighter
     and a touch darker than the card shadow, because a 24px blur under a 58px
     button reads as a smudge rather than a raised edge. The plugin overrides
     all three from the brand's depth setting. */
  --rt-shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.16);
  /* Fuller-width layouts by request: use most of the viewport, capped for
     very large monitors. Everything keying off this token widens together. */
  --rt-maxw: min(1440px, 94vw);

  /* Section grounds. Deliberately separate from the chrome tokens: a dark
     content band is not the header. */
  --rt-section-dark: var(--rt-black);
  --rt-on-section-dark: var(--rt-white);
  --rt-on-section-dark-muted: color-mix(in srgb, var(--rt-white) 78%, transparent);
  --rt-section-tint: #f4f4f4;

  /* Hero artwork when a site has supplied none: its own colours, not another
     school's photograph. */
  --rt-hero-fallback: linear-gradient(135deg, var(--rt-black) 0%, var(--rt-secondary) 55%, var(--rt-primary) 140%);
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Canvas matches the chrome so navigating between pages doesn't flash a
     different colour before the next page's header paints. */
  background-color: var(--rt-chrome);
}

body {
  margin: 0;
  font-family: var(--rt-font);
  color: var(--rt-ink);
  background: var(--rt-white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; }

/* Links, EXCEPT anything that is really a button.
   A button is an <a>, so the plain link colours were landing on buttons too.
   That was invisible on .rt-btn only because it carries !important on color,
   and it bit the Block Editor's buttons hard: `a:hover` is (0,1,1) while the
   hover that theme.json emits is `:root :where(...)`, which is (0,1,0), so the
   global rule won and a white label on a blue fill turned black on hover.
   Excluding buttons here fixes the whole class of it rather than one button. */
a { color: var(--rt-secondary); }
/* The darken-on-hover belongs to CONTENT links only. Chrome contexts (nav,
   footer, social tiles) sit on dark or brand-colour grounds where black is
   unreadable, and this rule's :not() chain out-specifies their own :hover
   rules (0,4,1 vs 0,1,2), which is exactly how every site's nav links were
   turning black on hover. */
a:not(.rt-btn):not(.wp-element-button):not(.wp-block-button__link):not(.rt-nav a):not(.rt-site-footer a):not(.rt-social-link):not(.rt-menu-toggle):hover {
  color: var(--rt-black);
}

h1, h2, h3, h4 {
  /* The display face's own weight, NOT a blanket 800. Every one of these
     fonts ships a single weight (Bebas 400, Oswald 500), so asking for 800
     found no matching face and the browser faked a bold by thickening the
     glyphs: doubled, shadow-like edges on every heading, on both sites. */
  font-weight: var(--rt-font-display-weight, 700);
  line-height: 1.15;
  color: var(--rt-black);
  /* Negative tracking suits a normal-width bold face and fights a condensed
     display one, which is what all of these are. The heading groups below set
     their own positive tracking; this is the sane default for the rest. */
  letter-spacing: 0.01em;
}

.rt-container {
  width: 100%;
  max-width: var(--rt-maxw);
  margin: 0 auto;
  padding: 0 20px;
}

/* ============================================================
   Header
   ============================================================ */
.rt-site-header {
  background: var(--rt-chrome);
  border-bottom: 4px solid var(--rt-chrome-accent);
  position: sticky;
  top: 0;
  z-index: 100;
}
.rt-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 20px;
}
.rt-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
}
.rt-brand img { height: 56px; width: auto; display: block; }
.rt-brand-text { display: flex; flex-direction: column; line-height: 1.05; }
.rt-brand-name {
  color: var(--rt-on-chrome);
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.rt-brand-tag {
  color: var(--rt-chrome-accent);
  font-size: 0.72rem;
  font-style: italic;
  letter-spacing: 0.02em;
}

/* Nav */
.rt-nav ul {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
}
.rt-nav a {
  color: var(--rt-on-chrome);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
  padding: 8px 14px;
  border-radius: 6px;
  /* Never break a nav label mid-phrase ("Staff Favorite / Things"); below
     the drawer breakpoint the whole nav becomes the off-canvas menu. */
  white-space: nowrap;
}
.rt-nav a:hover,
.rt-nav .current-menu-item > a {
  /* The chrome's own accent, which each chrome preset picks to READ on that
     chrome: bright primary on dark, on-primary on a brand-colour bar, deep
     secondary on light. Choosing one accent for all three is how you get a
     2.89:1 nav link on a blue brand. */
  color: var(--rt-chrome-accent);
  background: color-mix(in srgb, var(--rt-on-chrome) 10%, transparent);
}

/* Dropdown sub-menus (desktop): open on hover, and on keyboard focus via
   :focus-within so tabbing through the menu reaches every child link. */
.rt-nav li {
  position: relative;
}
.rt-nav ul ul {
  display: none;
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 220px;
  flex-direction: column;
  align-items: stretch;
  background: var(--rt-chrome);
  border: 1px solid var(--rt-secondary);
  border-radius: var(--rt-radius);
  box-shadow: var(--rt-shadow);
  padding: 6px;
  gap: 2px;
  z-index: 1003;
}
.rt-nav li:hover > ul,
.rt-nav li:focus-within > ul {
  display: flex;
}
/* Invisible bridge over the 10px gap so hover doesn't drop while the
   mouse travels from the nav item down into the panel. */
.rt-nav ul ul::before {
  content: "";
  position: absolute;
  top: -12px;
  left: 0;
  right: 0;
  height: 12px;
}
.rt-nav ul ul a {
  display: block;
  padding: 10px 14px;
}

/* A submenu section break: give a menu item the rt-menu-sep class in
   Appearance > Menus and it draws a rule above itself (News/Media uses it
   to divide the news half from the media half). */
.rt-nav ul ul li.rt-menu-sep {
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  margin-top: 6px;
  padding-top: 6px;
}

/* Third-level flyout: opens beside the dropdown panel. The generic
   li:hover / li:focus-within open rules above cover every depth. */
.rt-nav ul ul ul {
  top: 0;
  left: 100%;
  margin-left: 2px;
}
/* If the flyout would run off the right edge of the viewport (rightmost
   nav items), flip it to open leftward instead. */
.rt-nav > ul > li:nth-last-child(-n+2) ul ul {
  left: auto;
  right: 100%;
  margin-left: 0;
  margin-right: 2px;
}
/* Same guard for the LEVEL-2 panel itself: right-align it under the last
   two nav items so it never runs off the viewport (which also summoned a
   horizontal scrollbar). */
.rt-nav > ul > li:nth-last-child(-n+2) > ul {
  left: auto;
  right: 0;
}

/* Dropdown indicator: a Font Awesome glyph that tips over like an animated
   caret when its menu opens. Which glyph is per-site config (--rt-nav-icon,
   printed by rt_assets): Henry are the Hawks and wear a feather, Cox are the
   Comets and wear a meteor. A mascot is identity, not a hardcoded default. */
.rt-nav .menu-item-has-children > a::after {
  content: var(--rt-nav-icon, "\f52d");
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  font-style: normal;
  font-size: 0.78em;
  line-height: 1;
  display: inline-block;
  margin-left: 7px;
  vertical-align: 0;
  /* The chrome accent, matching the nav links beside it. */
  color: var(--rt-chrome-accent);
  transition: transform 0.25s ease, color 0.15s ease;
}
/* Every transform below COMPOSES with --rt-nav-icon-transform, the site's
   chosen resting orientation, so turning the icon to match a school's logo
   never fights the open animation. */
.rt-nav .menu-item-has-children > a::after {
  transform: var(--rt-nav-icon-transform, none);
}
/* Open states: the icon tips over with the menu, caret-style. */
.rt-nav > ul > .menu-item-has-children:hover > a::after,
.rt-nav > ul > .menu-item-has-children:focus-within > a::after {
  /* The spin direction comes from the site config: mirrored icons rotate
     backwards on screen, so their entries supply -90deg to still LOOK
     like a clockwise drop. Fallback is rotate(0deg), never the keyword
     none: "none rotate(90deg)" is invalid and drops the declaration. */
  transform: var(--rt-nav-icon-transform, rotate(0deg)) rotate(var(--rt-nav-icon-hover-rotate, 90deg));
}
/* Inside dropdown panels: flyout opens to the side. */
.rt-nav ul ul .menu-item-has-children > a::after {
  float: right;
  margin-top: 3px;
}
.rt-nav ul ul .menu-item-has-children:hover > a::after,
.rt-nav ul ul .menu-item-has-children:focus-within > a::after {
  transform: var(--rt-nav-icon-transform, rotate(0deg)) rotate(var(--rt-nav-icon-hover-rotate, 90deg));
}
/* Where the flyout flips leftward (rightmost nav items), mirror the
   feather and its spin so the motion points the same way the menu opens. */
.rt-nav > ul > li:nth-last-child(-n+2) ul .menu-item-has-children > a::after {
  transform: var(--rt-nav-icon-transform, rotate(0deg)) scaleX(-1);
  float: left;
  margin-left: 0;
  margin-right: 7px;
}
.rt-nav > ul > li:nth-last-child(-n+2) ul .menu-item-has-children:hover > a::after,
.rt-nav > ul > li:nth-last-child(-n+2) ul .menu-item-has-children:focus-within > a::after {
  transform: var(--rt-nav-icon-transform, rotate(0deg)) scaleX(-1) rotate(90deg);
}

/* Sits on the dark header beside the nav links, so it matches them: white
   label. The plain primary put a blue label on a blue bar (2.89:1 for Cox);
   the outline keeps the brand edge in the bright primary, which reads there. */
.rt-menu-toggle {
  display: none;
  background: transparent;
  border: 2px solid var(--rt-chrome-accent);
  color: var(--rt-on-chrome);
  border-radius: 6px;
  padding: 6px 10px;
  font-weight: 700;
  cursor: pointer;
}
.rt-menu-toggle:hover,
.rt-menu-toggle:focus-visible {
  color: var(--rt-chrome-accent);
  background: color-mix(in srgb, var(--rt-on-chrome) 10%, transparent);
}

/* Drawer close button + backdrop: hidden on desktop, shown in the mobile @media. */
.rt-nav-overlay { display: none; }

/* Submenu toggle buttons exist only for the drawer's collapsed-submenu mode;
   desktop navigation opens on hover/focus and never shows them. */
.rt-submenu-toggle { display: none; }
/* Same reasoning: the drawer background is the dark brand colour. */
.rt-nav-close {
  display: none;
  background: transparent;
  border: 0;
  color: var(--rt-on-chrome);
  font-size: 1.5rem;
  line-height: 1;
  padding: 4px 12px;
  cursor: pointer;
}
.rt-nav-close:hover,
.rt-nav-close:focus-visible { color: var(--rt-chrome-accent); }

/* Kill drawer/overlay transitions during window resize, so the drawer doesn't
   animate into view when the viewport crosses the mobile breakpoint. */
.rt-resizing .rt-nav,
.rt-resizing .rt-nav-overlay { transition: none; }

/* ============================================================
   Home dashboard sections
   ============================================================ */
.rt-home-heading {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
  font-size: clamp(1.6rem, 3.2vw, 2.2rem);
  margin: 0 0 6px;
}
.rt-home-more {
  text-align: center;
  margin: 26px 0 0;
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Get Involved: three big doors. */
.rt-home-trio {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  max-width: var(--rt-maxw);
  margin: 10px auto 0;
}
@media (min-width: 760px) {
  .rt-home-trio { grid-template-columns: repeat(3, 1fr); }
}
.rt-home-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 26px 22px;
  border-radius: var(--rt-radius);
  background: var(--rt-chrome);
  border: 2px solid var(--rt-secondary);
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.rt-home-panel:hover {
  transform: translateY(-3px);
  box-shadow: var(--rt-shadow);
}
.rt-home-panel-title {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-size: 1.6rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--rt-chrome-accent);
}
.rt-home-panel-note {
  color: var(--rt-on-chrome-muted);
  font-size: 0.95rem;
}
.rt-home-panel--gold {
  background: var(--rt-primary);
  border-color: var(--rt-black);
}
/* This panel is filled with the primary, so its text takes the on-primary
   token. The old near-black + rgba(0,0,0,.75) pair assumed a light fill and
   measured 2.89:1 on a blue brand. */
.rt-home-panel--gold .rt-home-panel-title { color: var(--rt-on-primary); }
.rt-home-panel--gold .rt-home-panel-note { color: var(--rt-on-primary); opacity: 0.82; }

/* This month's staff birthdays. */
.rt-home-birthdays {
  list-style: none;
  padding: 0;
  margin: 14px auto 0;
  max-width: 560px;
  display: grid;
  gap: 8px;
}
.rt-home-birthdays li {
  display: flex;
  align-items: center;
  gap: 12px;
}
.rt-home-bday-day {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--rt-black);
  /* Bright primary, not plain primary, for the same reason the initials tiles
     and the nav use it: plain primary on the near-black disc is 2.89:1 for a
     blue brand (Cox), under the bar even for large bold text. */
  color: var(--rt-primary-bright);
  font-weight: 800;
}

/* Quick links block. */
.rt-home-links {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  max-width: var(--rt-maxw);
  margin: 10px auto 0;
}
@media (min-width: 700px) {
  .rt-home-links { grid-template-columns: repeat(3, 1fr); }
}
.rt-home-links h3 {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-bottom: 2px solid var(--rt-primary);
  padding-bottom: 4px;
  margin: 0 0 10px;
}
.rt-home-links ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 6px;
}

/* Board teaser inherits the roster cards; just cap its width. */
.rt-bm--teaser {
  max-width: var(--rt-maxw);
  margin: 10px auto 0;
}

/* ============================================================
   Footer columns
   ============================================================ */
.rt-footer-cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: 26px;
  text-align: left;
  padding: 8px 0 22px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--rt-chrome-line);
}
@media (min-width: 620px) {
  .rt-footer-cols { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 980px) {
  .rt-footer-cols { grid-template-columns: repeat(4, 1fr); }
}
.rt-footer-col h3 {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  font-size: 1.1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rt-chrome-accent);
  margin: 0 0 10px;
}
.rt-footer-col ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 7px;
}
.rt-footer-col a {
  color: var(--rt-on-chrome-muted);
  text-decoration: none;
  font-size: 0.94rem;
}
.rt-footer-col a:hover {
  /* Same reasoning as the nav: this is a link on the dark footer. */
  color: var(--rt-chrome-accent);
}

/* ============================================================
   Page-title banner: the social-graphic strip
   ============================================================ */
.rt-title-banner {
  position: relative;
  background: var(--rt-chrome);
  border-top: 2px solid var(--rt-chrome-accent);
  border-bottom: 2px solid var(--rt-chrome-accent);
  padding: 44px 24px 40px;
  margin: 0 0 36px;
  text-align: center;
  overflow: hidden;
}
/* Angled stripe accents at the band edges.
   --rt-chrome-accent, not --rt-primary. This band IS the chrome, and on a
   site whose chrome preset is `brand` the two are the same colour: Cox's
   stripes, sparkles and ribbon all painted rgb(37,102,182) on an
   rgb(37,102,182) band and simply were not there. The accent token is the one
   each preset picks to READ on its own chrome, which is why the nav uses it. */
.rt-title-banner::before,
.rt-title-banner::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 130px;
  background: repeating-linear-gradient(
    -55deg,
    var(--rt-chrome-accent) 0 10px,
    transparent 10px 34px
  );
  opacity: 0.28;
  pointer-events: none;
}
.rt-title-banner::before { left: -20px; }
.rt-title-banner::after { right: -20px; transform: scaleX(-1); }
.rt-title-banner-inner {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 18px;
}
/* Watermark: the site mark, big, anchored to the band's right edge and
   bleeding off it, faded in from the left so the title never fights it. It
   used to hang off the title's own box (right: -110px on the inline-flex), so
   its position wandered with title length; anchoring to the band itself puts
   it in the same place on every page. */
.rt-title-banner-watermark {
  position: absolute;
  top: 50%;
  right: max(2%, 16px);
  width: clamp(170px, 24vw, 320px);
  aspect-ratio: 1;
  transform: translateY(-50%) rotate(-6deg);
  background: var(--rt-banner-logo, none) center / contain no-repeat;
  opacity: 0.14;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 45%);
  mask-image: linear-gradient(to right, transparent, #000 45%);
  pointer-events: none;
}
@media (max-width: 700px) {
  .rt-title-banner-watermark { width: 150px; opacity: 0.1; }
}
/* The title is a lockup now: the crisp site mark beside the words, the same
   recipe the art variant already uses. */
.rt-title-banner .rt-title-banner-heading {
  display: flex;
  align-items: center;
  gap: 0.35em;
}
.rt-title-banner-heading {
  margin: 0;
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  font-size: clamp(2.4rem, 5.5vw, 3.6rem);
  letter-spacing: 0.05em;
  line-height: 1;
  text-transform: uppercase;
  /* Chrome-aware, not white: the banner sits ON the chrome, so on a light
     chrome a hardcoded white heading is 1:1 against its own background. */
  color: var(--rt-on-chrome, #fff);
  /* Even line lengths instead of a long line and an orphaned word. */
  text-wrap: balance;
  overflow-wrap: break-word;
}
/* Long titles on a phone.
   A news title is not a page title: "Parent/Player Weekly Agenda for the Week
   of 8/17 - Westwood Scrimmage" is sixty-odd characters where "Coaches" is
   seven. Three things ganged up on it at 390px. The clamp could not go below
   2.4rem, so the type stayed at 38px however narrow the screen got. The band's
   24px padding, the two sparkles and the mark left the words barely 200px to
   live in, about eight characters a line. And line-height 1 packs those lines
   tight enough to touch, which is the part that reads as squished.
   So on a phone the type may shrink, the furniture gives room back, and the
   lines get somewhere to breathe. Short titles are unaffected on desktop. */
@media (max-width: 700px) {
  .rt-title-banner { padding: 30px 14px 28px; }
  .rt-title-banner-inner { gap: 10px; }
  .rt-title-banner-heading {
    font-size: clamp(1.5rem, 7vw, 2.4rem);
    line-height: 1.12;
    letter-spacing: 0.03em;
  }
  .rt-title-banner-mark { height: 0.8em; }
}
/* Angled ribbon under the title, like the graphic's gold bars. Same accent
   token as the stripes above, for the same reason. */
.rt-title-banner-ribbon {
  display: block;
  width: 130px;
  height: 9px;
  margin: 16px auto 0;
  background: var(--rt-chrome-accent);
  transform: skewX(-24deg);
}
/* Sparkle flourishes beside the page title. Font Awesome rather than an
   inlined SVG: the old one baked in Henry's gold, so every other PTA wore a
   gold sparkle regardless of its own palette. */
.rt-sparkle {
  flex: none;
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  font-style: normal;
  font-size: 18px;
  line-height: 1;
  color: var(--rt-chrome-accent);
}
.rt-sparkle::before { content: "\f005"; }

/* ---- The art variant: the page header cut from the home banner ----
   Same ground, same mark tiled across it, same wash, same divider. The tile is
   sized `contain`, so this shorter band shrinks the mark and repeats it more
   often than the banner does, with nothing to configure. */
.rt-title-banner--art {
  background-color: var(--rt-hero-ground, var(--rt-chrome));
  background-image:
    var(--rt-hero-wash, none),
    var(--rt-hero-tile, none);
  background-repeat: no-repeat, var(--rt-hero-tile-repeat, repeat-x);
  background-size: cover, var(--rt-hero-tile-size, contain);
  background-position: center, center var(--rt-hero-tile-offset, 24%);
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 180px;
  padding: 30px 24px;
  margin: 0 0 36px;
}
/* The panel both art bands put their copy on. Shared, because the page-title
   banner and the single-activity header sit one click apart and a panel on one
   with bare text on the other looks like two different sites.
   The divider eats into the band from below, so it needs room to sit clear of
   the ridgeline rather than being sliced by it. */
.rt-title-banner--art .rt-title-banner-panel,
.rt-act-detail-head--art .rt-act-detail-head-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto 26px;
  padding: 0.5em 0.8em;
  border-radius: var(--rt-radius, 5px);
  background: color-mix(in srgb, var(--rt-white) 88%, transparent);
  box-shadow: var(--rt-shadow);
}
/* 88%, not the 70% the Divi original used. The panel is only as light as what
   shows through it, and the brand colour on a 70% panel over this navy
   measured 3.27:1: past the large-text floor and nowhere near the normal one.
   At 88% the worst case (panel over the darkest part of the ground) measures
   4.50:1, so it clears the normal-text bar outright, and the title is large
   text anyway. Still reads as translucent. */
.rt-title-banner--art .rt-title-banner-heading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35em;
  color: var(--rt-primary);
  text-shadow: none;
}
.rt-title-banner-mark {
  flex: none;
  width: auto;
  height: 1.05em;
  display: block;
}
@media (max-width: 600px) {
  .rt-title-banner--art { min-height: 140px; padding: 22px 16px; }
  .rt-title-banner--art .rt-title-banner-panel { margin-bottom: 18px; }
}

/* The single-activity header wears the same ground. It is NOT a page-title
   banner (it carries a date tile, a when/where line and lens chips, so it
   keeps its own markup), but it sits one click from pages that are, and a
   flat chrome bar next to a tiled one reads as an unfinished page. The plugin
   sets the artwork variables and the --art class; everything else is here,
   because the ground is the theme's business. */
.rt-act-detail-head--art {
  position: relative;
  background-color: var(--rt-hero-ground, var(--rt-chrome));
  background-image:
    var(--rt-hero-wash, none),
    var(--rt-hero-tile, none);
  background-repeat: no-repeat, var(--rt-hero-tile-repeat, repeat-x);
  background-size: cover, var(--rt-hero-tile-size, contain);
  background-position: center, center var(--rt-hero-tile-offset, 24%);
  padding-bottom: 64px;   /* room for the ridgeline to cut in */
}
/* On the panel the copy reads dark, the same way the page title does. The
   white-with-a-text-shadow it used on flat chrome is unreadable here: the
   comets run straight through the letterforms. */
.rt-act-detail-head--art .rt-act-detail-title {
  color: var(--rt-primary);
  text-shadow: none;
}
.rt-act-detail-head--art .rt-act-detail-when {
  color: var(--rt-ink);
  text-shadow: none;
}

/* Display font across section headings for social-style cohesion. */
.rt-bm-heading,
.rt-ft-group-title,
.rt-bm-chairrole-title,
.rt-section h2,
.rt-content h2,
.rt-entry-title {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  /* The chosen face's own weight, not a flat 400. Bebas and Anton are 400,
     Oswald is 500; asking every one of them for 400 left Oswald with no
     matching face and the browser synthesised one, which is most of why these
     read as soft. */
  font-weight: var(--rt-font-display-weight, 400);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* A SIZE. This group had none, so every heading outside a home-page section
     inherited the browser's default h2 and rendered at 24px against prod's
     32px. A condensed display face at 24px is also where "blurry" comes from:
     the strokes are too fine to resolve. The .rt-section h2 rule below still
     wins for home-page bands, which set their own scale. */
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  line-height: 1.2;
  /* Prod is rgba(0,0,0,0.75), softer than the solid near-black these were
     using. Derived from the ink token so it follows the palette, and it still
     measures over 9:1 on white. */
  color: color-mix(in srgb, var(--rt-ink) 82%, transparent);
}

/* Plugin module pages (Favorite Things, Board Members) render their own
   layout inside the page container. Under a foreign theme the plugin centres
   these itself; under this theme it defers to us, so we have to actually do
   it or every group heading sits hard against the left edge. */
.rt-ft-group-title,
.rt-ft-section-title,
.rt-ft-bd-month-title {
  text-align: center;
}
.rt-ft-list,
.rt-ft-groups,
.rt-bm {
  margin-left: auto;
  margin-right: auto;
}

/* Script accent for the tagline, echoing the social graphics' swash. */
.rt-brand-tag,
.rt-footer-tag,
.rt-tagline {
  font-family: "RT Script", cursive;
  font-style: normal;
}

/* ============================================================
   Buttons
   ============================================================ */
.rt-btn {
  display: inline-block;
  background: var(--rt-primary);
  /* The palette's own text-on-primary token, not the near-black. Black on a
     gold fill happens to work (Henry, 9.99:1) but on a blue one it is 2.89:1,
     so every button on a dark-brand site was failing. Identical for Henry. */
  color: var(--rt-on-primary) !important;
  font-weight: 800;
  text-decoration: none;
  padding: 14px 28px;
  border-radius: var(--rt-radius);
  border: 2px solid var(--rt-primary);
  box-shadow: var(--rt-shadow-sm);
  transition: transform 0.08s ease, background 0.15s ease, box-shadow 0.15s ease;
  letter-spacing: 0.02em;
  /* Label and hover mark stay on one line. */
  white-space: nowrap;
}
/* Darken on hover rather than brighten. Brightening the fill while the label
   stays on-primary drops a blue brand to 3.04:1; the secondary holds 9.15
   for Cox and 7.52 for Henry. */
.rt-btn:hover {
  background: var(--rt-secondary);
  border-color: var(--rt-primary-bright);
  /* Rise and cast further: the shadow is what makes the 1px move read as a
     lift rather than as a jitter. */
  transform: translateY(-2px);
  box-shadow: var(--rt-shadow);
}
.rt-btn:active {
  transform: translateY(0);
  box-shadow: var(--rt-shadow-sm);
}
/* Hover mark: a Font Awesome glyph that slides in on the RIGHT of the label.
   The glyph is a brand setting (--rt-btn-icon), so a PTA picks its own.
   It sits in the normal inline flow rather than being absolutely positioned,
   which is what keeps it on the text baseline at any button size: the Divi
   original was absolutely positioned with its own line-height and drifted low
   the moment the button's type changed. */
.rt-btn::after {
  content: var(--rt-btn-icon, none);
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  font-size: 0.85em;
  display: inline-block;
  /* The space is reserved ALWAYS, and only opacity and transform animate.
     Growing the width on hover animates LAYOUT: the button changes size, the
     label reflows, and a label that only just fitted wraps to a second line
     the moment you point at it. Nothing here changes the box, so the button
     is the same size hovered or not and the mark simply appears. */
  width: 1em;
  margin-left: 0.4em;
  opacity: 0;
  transform: translateX(-0.3em);
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.rt-btn:hover::after,
.rt-btn:focus-visible::after {
  opacity: 1;
  transform: translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  .rt-btn::after { transition: none; }
}
.rt-btn--lg { font-size: 1.15rem; padding: 18px 40px; }
.rt-btn--ghost {
  background: transparent;
  color: var(--rt-white) !important;
  border-color: var(--rt-white);
}
.rt-btn--ghost:hover {
  background: var(--rt-white);
  color: var(--rt-black) !important;
}
/* On light sections the white ghost button disappears; use the ink outline. */
.rt-section:not(.rt-section--dark) .rt-btn--ghost {
  color: var(--rt-black) !important;
  border-color: var(--rt-black);
}
/* Hover fills it dark, so the label needs the BRIGHT primary: plain primary
   on the dark fill is 2.89:1 for a blue brand. */
.rt-section:not(.rt-section--dark) .rt-btn--ghost:hover {
  background: var(--rt-black);
  color: var(--rt-primary-bright) !important;
}

/* ============================================================
   Forms (site-wide control layer)
   ------------------------------------------------------------
   Every text-like control inside page content inherits the brand
   look from the theme, so modules never restyle inputs ad hoc.
   Modules only add LAYOUT (grids, cards) on top of this.
   ============================================================ */
.rt-page input[type="text"],
.rt-page input[type="email"],
.rt-page input[type="url"],
.rt-page input[type="tel"],
.rt-page input[type="search"],
.rt-page input[type="number"],
.rt-page input[type="password"],
.rt-page input[type="date"],
.rt-page textarea,
.rt-page select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 1.02rem;
  color: var(--rt-ink);
  background: var(--rt-white);
  border: 1.5px solid #cfc8b8;
  border-radius: var(--rt-radius);
  padding: 13px 16px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.rt-page input:focus,
.rt-page textarea:focus,
.rt-page select:focus {
  outline: none;
  border-color: var(--rt-primary);
  box-shadow: 0 0 0 4px rgba(244, 180, 28, 0.25);
}
.rt-page ::placeholder {
  color: #a89f8c;
  opacity: 1;
}
.rt-page label {
  font-weight: 600;
  color: var(--rt-ink);
}
/* Buttons: every button is clickable, so every button gets the pointer,
   never rely on per-button styling for that. Fix the browser's system font
   without clobbering .rt-btn's weight/size (the `font` shorthand would
   reset both). */
.rt-page button,
.rt-btn {
  cursor: pointer;
}
.rt-page button {
  font-family: inherit;
}
/* File inputs: brand the picker button instead of the browser's grey chip. */
.rt-page input[type="file"] {
  font: inherit;
  color: var(--rt-gray);
}
.rt-page input[type="file"]::file-selector-button {
  cursor: pointer;
  background: var(--rt-primary);
  color: var(--rt-on-primary);
  border: 2px solid var(--rt-primary);
  border-radius: var(--rt-radius);
  font: inherit;
  font-weight: 800;
  padding: 10px 18px;
  margin-right: 12px;
  transition: background 0.15s ease;
}
.rt-page input[type="file"]::file-selector-button:hover {
  background: var(--rt-primary-bright);
  border-color: var(--rt-primary-bright);
}

/* Helper + fine-print text under fields. */
.rt-form-help {
  color: var(--rt-gray);
  font-size: 0.95rem;
  margin: 8px 0 0;
}
/* A generic white content card, matching the module card look. */
.rt-card {
  background: var(--rt-white);
  border: 1px solid var(--rt-line);
  border-radius: var(--rt-radius);
  /* The brand's own depth, not a hardcoded 5% smudge. At 0.05 the card read
     as a flat rectangle with a hairline border and nothing lifted off the
     page, which is most of why the whole thing looked flat. */
  box-shadow: var(--rt-shadow);
  padding: 26px 30px;
}

/* ============================================================
   Hero (home)
   ============================================================ */
/* The banner art is per-site: --rt-hero-image, from the hero_image_id config.
   A site WITHOUT its own artwork gets --rt-hero-fallback, a gradient in its
   own palette, never a photograph. The theme used to fall back to a bundled
   hero-banner.jpg, which is Henry's badge and hawk over Henry Middle School,
   so every other PTA put another school's building on its own front page.
   The banner art (badge + streaks + school) is baked into hero-banner.jpg and
   painted by ::after, capped at 1280px and centered over a black base.
   Desktop: the message sits on the RIGHT (clearing the baked-in badge) and the
   in-message logo is hidden. Mobile (<=768px): ::after reframes to the school so
   the badge scrolls off-screen, the in-message logo shows, and content centers.
   Content + button always show. */
.rt-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  justify-content: center;      /* centers the 1280 stage */
  min-height: clamp(460px, 60vh, 560px);
  color: var(--rt-on-chrome);
  text-align: center;
  border-bottom: 4px solid var(--rt-primary);
  background: var(--rt-chrome);   /* fills the sides beyond the 1280 banner */
}
/* A shaped divider is already the bottom edge. Drawing the accent rule as well
   put a blue line straight across the page under Cox's white mountain ridge. */
.rt-hero--divided { border-bottom: 0; }
/* The banner art + legibility scrim, capped at 1280 and centered. A horizontal
   mask feathers its left/right edges into the black base, so on screens wider
   than 1280 the banner fades out instead of stretching. */
.rt-hero::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 1280px;
  z-index: -1;
  background:
    linear-gradient(90deg, transparent 28%, rgba(0,0,0,0.50) 64%, rgba(0,0,0,0.72) 100%),
    var(--rt-hero-image, var(--rt-hero-fallback)) center center / cover no-repeat;
}
/* Only past the 1280 cap (where black margins appear) feather the banner's
   edges into the base; below that the banner fills edge-to-edge unchanged. */
@media (min-width: 1281px) {
  .rt-hero::after {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
  }
}
/* Stage caps the message width and pins it to the right of the banner. */
.rt-hero-stage {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 44px clamp(24px, 5vw, 72px);
}
.rt-hero-inner { max-width: 560px; }
.rt-hero img.rt-hero-logo {
  display: none;               /* desktop: the badge is already in the banner art */
  width: 120px;
  height: auto;
  margin: 0 auto 14px;
  filter: drop-shadow(0 6px 20px rgba(0,0,0,0.6));
}
.rt-hero h1 {
  color: var(--rt-on-chrome);
  font-size: clamp(2rem, 5.2vw, 3.1rem);
  margin: 0 0 6px;
  text-shadow: 0 2px 18px rgba(0,0,0,0.6);
}
.rt-hero .rt-tagline {
  color: var(--rt-primary);
  font-style: italic;
  font-size: clamp(1rem, 2.4vw, 1.35rem);
  margin: 0 0 12px;
}
.rt-hero .rt-hero-rule {
  display: block;
  width: 64px; height: 4px;
  margin: 0 auto 16px;
  background: var(--rt-primary);
  border-radius: 2px;
}
.rt-hero .rt-hero-cry {
  color: var(--rt-primary);
  font-weight: 800;
  font-size: clamp(1.25rem, 3vw, 1.85rem);
  margin: 0 0 10px;
  text-shadow: 0 2px 16px rgba(0,0,0,0.5);
}
.rt-hero .rt-hero-lead {
  max-width: 600px;
  margin: 0 auto 24px;
  color: rgba(255,255,255,0.88);
  font-size: clamp(0.98rem, 2vw, 1.1rem);
  text-shadow: 0 1px 10px rgba(0,0,0,0.5);
}
.rt-hero .rt-hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
}
/* Mobile: focus the banner on the school (badge scrolls off-screen), full-bleed
   (no 1280 cap or edge fade), center the message, and show the in-message logo
   since the baked-in badge is gone. */
@media (max-width: 768px) {
  .rt-hero { min-height: clamp(500px, 78vh, 640px); }
  .rt-hero::after {
    max-width: none;
    background:
      linear-gradient(180deg, rgba(0,0,0,0.42) 0%, rgba(0,0,0,0.30) 42%, rgba(0,0,0,0.62) 100%),
      var(--rt-hero-image, var(--rt-hero-fallback)) 84% center / cover no-repeat;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .rt-hero-stage { justify-content: center; padding: 40px 22px 46px; }
  .rt-hero-inner { max-width: 480px; margin: 0 auto; }
  .rt-hero img.rt-hero-logo { display: block; }
}

/* ============================================================
   Sections / cards
   ============================================================ */
.rt-section { padding: 56px 0; }
.rt-section--cream { background: var(--rt-cream); }

/* Quiet tint: the step between white and dark. Cooler than --rt-cream, which
   is a warm parchment and reads yellow beside a blue brand. */
.rt-section--tint { background: var(--rt-section-tint); }

/* Dark strip, used by the home sections carrying full-bleed media (a Facebook
   wall reads better against dark than against cream). Text is inverted here so
   anything dropped inside inherits readable colour instead of the body ink.

   Its ground is --rt-section-dark, NOT the chrome. Chrome is the header and
   the footer; a dark CONTENT band is not chrome, and tying the two together
   meant that choosing brand chrome turned the masonry band, the join band and
   the footer all into one flat colour. A site is entitled to a blue header and
   dark content bands. */
.rt-section--dark {
  background: var(--rt-section-dark);
  color: var(--rt-on-section-dark);
}
.rt-section--dark h1,
.rt-section--dark h2,
.rt-section--dark h3 {
  color: var(--rt-on-section-dark);
}
/* Doubled `.rt-section` on the lead is deliberate. The generic
   `.rt-section .rt-section-lead` further down sets --rt-gray, and at equal
   (0,2,0) specificity the later rule wins: the join band's copy measured
   #6b6b6b on #191d33, which is 2.91:1 and fails 4.5:1 outright. (0,3,0) here
   settles it on specificity rather than on which rule happens to come last. */
.rt-section.rt-section--dark .rt-section-lead,
.rt-section--dark p {
  color: var(--rt-on-section-dark-muted);
}
.rt-section--dark a:not(.rt-btn) {
  color: var(--rt-primary-bright);
}

/* Full-bleed: the section's own child manages its width (feed plugins ship
   their own grid), so we contribute the strip and the vertical rhythm only. */
.rt-section--flush { padding-left: 0; padding-right: 0; }

/* Two editorial cards side by side, stacking on narrow screens. */
.rt-home-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
  align-items: start;
}

/* Closing join call: copy left, button right, stacked on phones. */
.rt-home-join-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
/* Same doubling, same reason: the generic lead rule below centres it, so the
   heading sat flush left with its own paragraph centred underneath. */
.rt-home-join .rt-home-heading,
.rt-section.rt-home-join .rt-section-lead {
  text-align: left;
  margin-left: 0;
}
.rt-home-join-cta { margin: 0; }
.rt-home-join-cta > p { margin: 0; }
/* Button above, social row beneath it, both flush right against the copy. */
.rt-home-join-cta .rt-social { justify-content: flex-end; }

/* ---- Facebook feed height cap: STUB, goes with the carousel ----
   Smash Balloon's carousel sizes itself to its TALLEST slide, so one portrait
   photo makes the whole band enormous even when every other image is
   landscape. That is the plugin's behaviour, not ours, and the real fix is the
   replacement carousel.

   The same cap lives in divi-bridge.css scoped to #et-boc, which reached the
   Divi home page only. The composed page is not builder content, so it
   inherited none of it and the carousel measured 908px against a 526px
   reference. This copy is the one that survives: divi-bridge.css is deleted
   when Divi goes.

   Scoped to cff- classes so nothing else on the page is affected, and `cover`
   crops the overflow rather than squashing the photo. */
.rt-section [class*="cff"] img {
  max-height: 23em;
  object-fit: cover;
}

/* ---- Social accounts ----
   A row of Font Awesome brand marks, one per account the PTA has configured. */
.rt-social {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 0;
  padding: 0;
}
/* One tile treatment for every network, not each in its own brand colour.
   A row of Facebook blue, Instagram pink and X black is three logos competing
   with the page rather than a set of links; matching them makes the row read
   as one control and lets the PTA's own palette stay in charge. Sized to
   prod's 64px, which is also a generous touch target. */
.rt-social-link {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 10px;
  color: var(--rt-white);
  text-decoration: none;
  background: var(--rt-black);
  box-shadow: var(--rt-shadow-sm);
  transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.rt-social-link:hover,
.rt-social-link:focus-visible {
  background: var(--rt-primary);
  color: var(--rt-on-primary);
  transform: translateY(-2px);
  box-shadow: var(--rt-shadow);
}
.rt-social-icon {
  font-size: 1.6rem;
  line-height: 1;
}
@media (max-width: 600px) {
  .rt-social-link { width: 52px; height: 52px; }
  .rt-social-icon { font-size: 1.3rem; }
}
.rt-section h2 {
  font-size: clamp(1.6rem, 3.5vw, 2.2rem);
  margin: 0 0 8px;
  text-align: center;
}
.rt-section .rt-section-lead {
  text-align: center;
  max-width: 720px;
  margin: 0 auto 36px;
  color: var(--rt-gray);
  font-size: 1.08rem;
}
.rt-brand-rule {
  width: 64px; height: 4px; background: var(--rt-primary);
  border: 0; margin: 14px auto 24px; border-radius: 2px;
}
/* The invite under the sponsor logos. Its own rule rather than a utility
   because the plugin owns the band above it and never loads Tailwind. */
.rt-sponsors-cta { margin: 40px 0 0; text-align: center; }

/* Generic page content */
/* No padding on the article itself. Full-bleed bands (title banner, 404 hero,
   page-builder sections, the home hero) must meet the header and the footer
   edge to edge; padding here put a white stripe above and below every one of
   them. The breathing room belongs to reading-width content, so it lives on
   the container instead, and only when that container actually opens or ends
   the page. */
.rt-page { padding: 0; }
.rt-page > .rt-container:first-child { padding-top: 48px; }
.rt-page > .rt-container:last-child { padding-bottom: 64px; }
.rt-page .rt-entry-title { text-align: center; margin-bottom: 8px; }
/* Center the 404 / "nothing here" fallback message under its centered title. */
.rt-page > .rt-container > .rt-section-lead { text-align: center; }
.rt-content { max-width: min(1100px, 100%); margin: 0 auto; }
/* A page whose content is a signup card grid is a browsing surface, not
   reading-width prose: give it the site's full band so a season of cards
   uses the screen instead of stacking two-wide between white gutters. */
.rt-content:has(.rt-sg) { max-width: var(--rt-maxw); }
.rt-content h2 { margin-top: 1.6em; }
.rt-content img { border-radius: var(--rt-radius); }

/* ---- Core Block Editor buttons ----
   Pages built in the block editor use core's button, not .rt-btn, and it was
   getting none of the same treatment: no depth, no motion, and no hover state
   at all, so the stylesheet's global a:hover took over and turned a white
   label black on a blue fill. Colours and hover live in theme.json so the
   editor canvas agrees; what cannot be expressed there is here.

   :where() keeps the specificity at zero so a per-button colour chosen in the
   editor still wins. */
:where(.wp-block-button__link) {
  box-shadow: var(--rt-shadow-sm);
  transition: transform 0.08s ease, background 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
}
.wp-block-button__link:hover,
.wp-block-button__link:focus-visible {
  /* Stated here as well as in theme.json. theme.json emits its hover through
     :where(), which is zero-specificity by design, so anything at (0,1,1) or
     above outranks it. Belt and braces on the one property whose failure is a
     black label on a dark fill. */
  color: var(--rt-on-primary);
  transform: translateY(-2px);
  box-shadow: var(--rt-shadow);
}
.wp-block-button__link:active {
  transform: translateY(0);
  box-shadow: var(--rt-shadow-sm);
}

/* The outline variant. Core paints it from currentColor, which left it in the
   near-black body colour rather than the brand, and gave it no hover at all:
   the one button on the page that did nothing when you pointed at it. It fills
   in on hover, the same move .rt-btn--ghost makes. */
.wp-block-button.is-style-outline > .wp-block-button__link {
  color: var(--rt-primary);
  border-color: currentColor;
  box-shadow: none;
}
.wp-block-button.is-style-outline > .wp-block-button__link:hover,
.wp-block-button.is-style-outline > .wp-block-button__link:focus-visible {
  background-color: var(--rt-primary);
  border-color: var(--rt-primary);
  color: var(--rt-on-primary);
  box-shadow: var(--rt-shadow);
}
/* On a dark band the brand outline can disappear; use the band's own text
   colour, matching how .rt-btn--ghost behaves there. */
.rt-section--dark .wp-block-button.is-style-outline > .wp-block-button__link {
  color: var(--rt-on-section-dark);
}

/* ============================================================
   Footer
   ============================================================ */
.rt-site-footer {
  background: var(--rt-chrome);
  color: var(--rt-on-chrome-muted);
  padding: 40px 20px;
  text-align: center;
  border-top: 4px solid var(--rt-chrome-accent);
}
.rt-site-footer a {
  color: var(--rt-chrome-accent);
  text-decoration: none;
  border-radius: 6px;
  transition: background 0.15s ease, color 0.15s ease;
}
/* A highlight rather than only an underline: the footer links are the site's
   social and contact routes, and an underline alone is easy to miss against a
   coloured chrome. The wash is derived from the chrome's own text colour, so
   it works on dark, brand and light chrome alike. */
.rt-site-footer a:hover,
.rt-site-footer a:focus-visible {
  color: var(--rt-on-chrome);
  background: color-mix(in srgb, var(--rt-on-chrome) 14%, transparent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rt-footer-col a {
  display: inline-block;
  padding: 2px 8px;
  margin-left: -8px;
}
.rt-footer-tag { color: var(--rt-chrome-accent); font-style: italic; margin-top: 6px; }
.rt-footer-legal {
  /* The same muted token as the rest of the footer rather than a second,
     fainter level: at 58% this measured 3.03:1 on Cox's brand-colour chrome,
     and it is small text, so it needs the full 4.5. Size alone sets it apart. */
  color: var(--rt-on-chrome-muted);
  font-size: 0.85rem;
  margin-top: 10px;
}
/* District-required not-the-school disclaimer: same muted contrast rules as
   the legal line, measured, never fainter. */
.rt-footer-disclaimer {
  max-width: 860px;
  margin: 14px auto 0;
  color: var(--rt-on-chrome-muted);
  font-size: 0.8rem;
  line-height: 1.5;
}

/* ============================================================
   Print: strip site chrome everywhere. The off-canvas drawer is a
   fixed full-height black panel; print rendering ignores its
   off-screen transform and paints it over the page, so hide it.
   ============================================================ */
@media print {
  .rt-site-header,
  .rt-site-footer,
  .rt-nav,
  .rt-nav-overlay,
  .rt-menu-toggle {
    display: none !important;
  }
  /* The html canvas is painted black for overscroll on screen; on paper
     that black floods everything past the content. */
  html,
  body {
    background: #fff !important;
  }
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1150px) {
  .rt-menu-toggle { display: inline-block; flex-shrink: 0; }

  /* Long booster names: the brand may shrink and wrap instead of running
     under the Menu button (desktop keeps the one-line nowrap treatment). */
  .rt-brand { min-width: 0; flex: 1 1 auto; }
  .rt-brand-text { min-width: 0; }
  .rt-brand-name { white-space: normal; overflow-wrap: break-word; }

  /* Primary nav becomes an off-canvas drawer sliding in from the right. */
  .rt-nav {
    position: fixed;
    top: 0;
    right: 0;
    height: 100dvh;
    width: 90%;
    background: var(--rt-chrome);
    border-left: 4px solid var(--rt-primary);
    transform: translateX(100%);
    transition: transform 0.3s ease;
    /* Pin a stable compositing layer so the browser doesn't tear it down at
       the end of the slide transition (which flashed the viewport white). */
    will-change: transform;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    z-index: 1002;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }
  .rt-nav.is-open { transform: translateX(0); }

  .rt-nav-close { display: block; align-self: flex-end; }
  .rt-nav ul { flex-direction: column; align-items: stretch; padding: 8px 8px 24px; }
  .rt-nav a { display: block; padding: 12px 16px; }

  /* In the drawer, sub-menus render inline as an always-visible indented
     list (no hover on touch) with a gold guide line. */
  .rt-nav ul ul {
    display: flex;
    position: static;
    min-width: 0;
    background: transparent;
    border: 0;
    border-left: 2px solid var(--rt-secondary);
    border-radius: 0;
    box-shadow: none;
    margin-left: 16px;
    padding: 0 0 0 4px;
  }
  .rt-nav ul ul a { font-size: 0.9em; padding: 10px 16px; }
  .rt-nav ul ul::before { display: none; }
  /* Third level in the drawer: same static nested-list treatment, one
     indent deeper; undo the desktop flyout offsets. */
  /* Each level steps FURTHER in than its parent. This used to indent 12px
     against the second level's 16px, so the third level's guide line drifted
     back toward its parent's and the nesting read as one flat list. */
  .rt-nav ul ul ul {
    margin-left: 22px;
    margin-right: 0;
    left: auto;
    right: auto;
  }
  /* The desktop rule that flips flyouts leftward under the last two nav items
     is more specific than the one above (0,2,4 vs 0,1,3), so it leaked into
     the drawer and zeroed the indent there, which is why the third tier under
     the RIGHTMOST menus (Resources -> School Links) sat almost on top of its
     parent's guide line. Nothing flies out in a drawer, so undo it here at
     matching specificity. */
  .rt-nav > ul > li:nth-last-child(-n+2) ul ul {
    margin-left: 16px;
    margin-right: 0;
    left: auto;
    right: auto;
  }
  .rt-nav > ul > li:nth-last-child(-n+2) ul ul ul {
    margin-left: 22px;
  }
  /* Drawer lists render fully expanded, so pointer glyphs would imply a
     toggle that does not exist. */
  .rt-nav .menu-item-has-children > a::after { display: none; }

  /* ---- Collapsed-submenu mode (per-site setting: Settings > Mobile menu).
     Long menus made the always-expanded drawer scroll forever, so a site can
     start every submenu closed behind a real toggle button that nav.js
     injects. The parent link itself still navigates on first tap. ---- */
  body.rt-mobile-submenus-closed .rt-nav ul ul { display: none; }
  body.rt-mobile-submenus-closed .rt-nav li.rt-submenu-open > ul { display: flex; }
  body.rt-mobile-submenus-closed .rt-nav .menu-item-has-children { position: relative; }
  body.rt-mobile-submenus-closed .rt-nav .menu-item-has-children > a { margin-right: 52px; }
  body.rt-mobile-submenus-closed .rt-nav .rt-submenu-toggle {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    width: 48px;
    height: 44px;
    border: 0;
    background: transparent;
    cursor: pointer;
    color: var(--rt-chrome-accent);
  }
  body.rt-mobile-submenus-closed .rt-nav .rt-submenu-toggle::before {
    content: var(--rt-nav-icon, "\f52d");
    font-family: "Font Awesome 7 Free";
    font-weight: 900;
    font-style: normal;
    font-size: 0.9em;
    line-height: 1;
    display: inline-block;
    transform: var(--rt-nav-icon-transform, none);
    transition: transform 0.25s ease;
  }
  body.rt-mobile-submenus-closed .rt-nav .rt-submenu-open > .rt-submenu-toggle::before {
    transform: var(--rt-nav-icon-transform, rotate(0deg)) rotate(var(--rt-nav-icon-hover-rotate, 90deg));
  }

  /* Full-screen dimmed backdrop behind the drawer (dialog-style). */
  .rt-nav-overlay {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease;
    z-index: 1001;
  }
  .rt-nav-overlay.is-open { opacity: 1; visibility: visible; }

  body.rt-nav-open { overflow: hidden; }
}

/* ============================================================
   404: page not found

   Shares the title banner's treatment (dark band, brand rules,
   angled stripes, logo watermark) so a lost visitor still knows
   whose site they are on. Copy is config; this is only its shape.
   ============================================================ */
.rt-404-hero {
  position: relative;
  background: var(--rt-chrome);
  border-top: 2px solid var(--rt-primary);
  border-bottom: 2px solid var(--rt-primary);
  padding: 64px 24px 56px;
  margin: 0 0 40px;
  text-align: center;
  overflow: hidden;
}
/* The site's own artwork, when it has one, sits behind at low contrast so
   the text keeps its ratio no matter what image a PTA uploads. */
.rt-404-hero--image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--rt-404-image) center / cover no-repeat;
  opacity: 0.35;
  pointer-events: none;
}
/* No image: fall back to the same angled brand stripes the title banner
   uses, so an unconfigured site still looks deliberate. */
.rt-404-hero:not(.rt-404-hero--image)::before,
.rt-404-hero:not(.rt-404-hero--image)::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 130px;
  background: repeating-linear-gradient(
    -55deg,
    var(--rt-primary) 0 10px,
    transparent 10px 34px
  );
  opacity: 0.28;
  pointer-events: none;
}
.rt-404-hero:not(.rt-404-hero--image)::before { left: -20px; }
.rt-404-hero:not(.rt-404-hero--image)::after { right: -20px; transform: scaleX(-1); }

.rt-404-inner {
  position: relative;
  z-index: 1;
  max-width: 720px;
  margin: 0 auto;
}

/* Oversized, low-contrast, and aria-hidden: decoration, not the heading. */
.rt-404-code {
  font-family: var(--rt-font-display);
  font-size: clamp(4rem, 14vw, 9rem);
  line-height: 0.9;
  margin: 0 0 8px;
  letter-spacing: 0.08em;
  /* Bright primary, not plain primary: this sits on the dark band, where a
     blue brand's plain primary is 2.89:1 (Cox). Decoration, but it should
     still read as brand rather than mud. */
  color: var(--rt-chrome-accent);
  opacity: 0.55;
}

.rt-404-heading {
  font-family: var(--rt-font-display);
  font-size: clamp(1.6rem, 4vw, 2.6rem);
  color: var(--rt-on-chrome);
  margin: 0 0 14px;
  letter-spacing: 0.03em;
}
.rt-404-lead {
  color: var(--rt-chrome-accent);
  font-size: 1.12rem;
  font-weight: 600;
  margin: 0 0 10px;
}
.rt-404-body {
  color: var(--rt-on-chrome);
  opacity: 0.86;
  margin: 0;
}

.rt-404-content {
  max-width: 640px;
  margin: 0 auto 56px;
  text-align: center;
}
.rt-404-subheading {
  font-family: var(--rt-font-display);
  font-size: 1.25rem;
  margin: 0 0 18px;
}

.rt-404-links {
  list-style: none;
  margin: 0 0 32px;
  padding: 0;
  display: grid;
  gap: 12px;
}
.rt-404-links a {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 14px;
  padding: 14px 18px;
  border: 2px solid var(--rt-line);
  border-radius: var(--rt-radius);
  background: var(--rt-white);
  color: var(--rt-ink);
  text-decoration: none;
  font-weight: 600;
  text-align: left;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.rt-404-links a:hover,
.rt-404-links a:focus-visible {
  /* Darken toward secondary rather than brighten: white-on-bright hits
     3.04:1 on a blue brand, which fails. */
  border-color: var(--rt-secondary);
  background: var(--rt-cream);
  color: var(--rt-ink);
  transform: translateY(-1px);
}
.rt-404-links i {
  color: var(--rt-secondary);
  width: 1.4em;
  text-align: center;
  flex: none;
}

.rt-404-content .search-form {
  display: flex;
  gap: 8px;
  margin: 0 0 28px;
}
.rt-404-content .search-form label { flex: 1; }
.rt-404-content .search-field {
  width: 100%;
  padding: 12px 14px;
  border: 2px solid var(--rt-line);
  border-radius: var(--rt-radius);
  font: inherit;
}
.rt-404-content .search-field:focus {
  outline: none;
  border-color: var(--rt-primary);
}
.rt-404-content .search-submit {
  padding: 12px 20px;
  border: 0;
  border-radius: var(--rt-radius);
  background: var(--rt-primary);
  color: var(--rt-on-primary);
  font-weight: 700;
  cursor: pointer;
}
.rt-404-content .search-submit:hover { background: var(--rt-secondary); color: var(--rt-white); }

.rt-404-closing {
  color: var(--rt-gray);
  font-style: italic;
  margin: 0;
}
.rt-404-closing i {
  color: var(--rt-primary);
  margin: 0 6px;
  /* The Divi original wanted twinkling sparkles and never got the CSS. */
  animation: rt-twinkle 2.4s ease-in-out infinite;
}
.rt-404-closing i:last-child { animation-delay: 1.2s; }

@keyframes rt-twinkle {
  0%, 100% { opacity: 0.35; transform: scale(0.9); }
  50%      { opacity: 1;    transform: scale(1.1); }
}
@media (prefers-reduced-motion: reduce) {
  .rt-404-closing i { animation: none; opacity: 1; }
}

/* ============================================================
   Block styles: the reusable pieces an officer picks from the
   sidebar. Registered in functions.php; this is what they look
   like. Icons come from the design system so a page never has to
   carry icon markup a future editor can break.
   ============================================================ */

/* Checklist: for "what your membership pays for" style lists. */
.is-style-rt-check {
  list-style: none;
  padding-left: 0;
}
.is-style-rt-check > li {
  position: relative;
  padding-left: 1.9em;
  margin-bottom: 0.5em;
}
.is-style-rt-check > li::before {
  content: "\f00c"; /* fa-check */
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  position: absolute;
  left: 0;
  top: 0.1em;
  color: var(--rt-primary);
  font-size: 0.9em;
}

/* Brand markers: the site's own mascot glyph as the bullet, so a plain list
   still reads as this PTA's. Same token the nav indicator uses. */
.is-style-rt-brand-marker {
  list-style: none;
  padding-left: 0;
}
.is-style-rt-brand-marker > li {
  position: relative;
  padding-left: 1.9em;
  margin-bottom: 0.5em;
}
.is-style-rt-brand-marker > li::before {
  content: var(--rt-nav-icon, "\f005");
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  position: absolute;
  left: 0;
  top: 0.1em;
  color: var(--rt-secondary);
  font-size: 0.85em;
}

/* Card: a white panel that lifts off the page, matching the board and
   activity cards so a hand-written page sits in the same visual family. */
.is-style-rt-card {
  background: var(--rt-white);
  border: 1px solid var(--rt-line);
  border-top: 5px solid var(--rt-primary);
  border-radius: var(--rt-radius);
  box-shadow: var(--rt-shadow-lift);
  padding: 28px 30px;
  margin-bottom: 28px;
}
.is-style-rt-card > *:first-child { margin-top: 0; }
.is-style-rt-card > *:last-child { margin-bottom: 0; }

/* Callout: a quieter tinted panel for an aside or a caveat. */
.is-style-rt-callout {
  background: var(--rt-cream);
  border-left: 5px solid var(--rt-secondary);
  border-radius: var(--rt-radius);
  padding: 20px 24px;
  margin-bottom: 28px;
}
.is-style-rt-callout > *:first-child { margin-top: 0; }
.is-style-rt-callout > *:last-child { margin-bottom: 0; }

/* ============================================================
   Hero: the banner recipe
   A coloured ground with the PTA's own mark tiled across it, a
   gradient wash over the top, and a shaped divider below. This
   is Cox's, and it lived only inside Divi until now.
   ============================================================ */
.rt-hero--banner {
  position: relative;
  background-color: var(--rt-hero-ground, var(--rt-black));
  /* Wash over tile over ground. The wash sits on top so the mark reads as
     texture rather than as a row of logos. */
  background-image:
    var(--rt-hero-wash, none),
    var(--rt-hero-tile, none);
  background-repeat: no-repeat, var(--rt-hero-tile-repeat, repeat-x);
  background-size: cover, var(--rt-hero-tile-size, contain);
  background-position: center, center var(--rt-hero-tile-offset, 24%);
  min-height: clamp(320px, 34vw, 420px);
}
/* The banner carries its own ground, so it must not inherit the photo
   recipe's image stack. BOTH halves of that stack: the stage, and .rt-hero
   ::after, which is where the photo recipe actually paints. Turning off only
   the stage left ::after painting the photo fallback gradient plus its
   right-hand darkening scrim straight over the tiled mark, which is why Cox's
   comets came out washed and the right of the banner went almost black. */
.rt-hero--banner .rt-hero-stage { background: none; }
.rt-hero--banner::after { content: none; }

/* ---- Banner copy: panels, not a scrim ----
   A tiled mark is busy across the whole width, so a corner gradient cannot
   make a paragraph readable over it. Each block gets its own translucent
   panel instead, which is what this banner used for years before it moved
   out of Divi. Both panels take brand tokens, so they follow the palette. */
.rt-hero--banner .rt-hero-stage {
  justify-content: flex-start;
  align-items: center;
}
.rt-hero--banner .rt-hero-inner {
  max-width: min(1080px, 100%);
  width: 100%;
  text-align: left;
}
.rt-hero--banner .rt-hero-greeting {
  display: flex;
  align-items: center;
  gap: 0.4em;
  margin: 0 0 clamp(18px, 3vw, 34px);
  padding: 0.3em 0.55em;
  border-radius: var(--rt-radius, 5px);
  /* Light panel, dark type: the reverse of the rest of the banner, which is
     what makes the greeting the first thing read rather than one more bright
     shape among the artwork. */
  background: color-mix(in srgb, var(--rt-white) 72%, transparent);
  box-shadow: var(--rt-shadow);
  color: var(--rt-hero-ground, var(--rt-black));
  font-family: var(--rt-font-display, inherit);
  font-size: clamp(1.6rem, 4.4vw, 3.4rem);
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.1;
  text-shadow: none;
}
.rt-hero--banner .rt-hero-mark {
  flex: none;
  width: auto;
  height: 1.25em;
  display: block;
}
/* Lead and button on one line, the button hugging the right of the panel row
   the way it does on the printed banner. Wraps on narrow screens. */
.rt-hero--banner .rt-hero-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(16px, 3vw, 40px);
}
.rt-hero--banner .rt-hero-lead {
  flex: 1 1 26em;
  max-width: 46em;
  margin: 0;
  padding: 0.75em 0.9em;
  border-radius: var(--rt-radius, 5px);
  /* 92%, not the 80% the Divi original used. A translucent panel is only as
     dark as what shows through it, and the artwork has near-white highlights:
     at 80% the white type over them measured 3.97:1, under the 4.5 floor. At
     92% the worst case is 4.92:1 and the panel still reads as translucent. */
  background: color-mix(in srgb, var(--rt-primary) 92%, transparent);
  box-shadow: var(--rt-shadow);
  color: var(--rt-on-primary);
  font-size: clamp(0.98rem, 1.5vw, 1.2rem);
  text-shadow: none;
}
.rt-hero--banner .rt-hero-cta {
  flex: none;
  justify-content: flex-start;
  margin: 0;
}
/* The banner's button inverts: a white slab with the brand colour as the
   label. On artwork this dense a brand-filled button is just another coloured
   shape, while white reads as the one thing to press. */
.rt-hero--banner .rt-hero-cta .rt-btn {
  background: var(--rt-white);
  border-color: var(--rt-white);
  color: var(--rt-primary) !important;
}
.rt-hero--banner .rt-hero-cta .rt-btn:hover {
  background: var(--rt-cream);
  border-color: var(--rt-cream);
}
@media (max-width: 768px) {
  .rt-hero--banner .rt-hero-stage { justify-content: center; }
  .rt-hero--banner .rt-hero-greeting { justify-content: center; text-align: center; }
  .rt-hero--banner .rt-hero-row { justify-content: center; }
  .rt-hero--banner .rt-hero-cta { width: 100%; justify-content: center; }
}

/* Divider: sits at the foot of the banner and paints in the colour of the
   section that follows, via currentColor on the wrapper. */
.rt-hero-divider {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px; /* -1 kills the hairline gap sub-pixel rounding leaves */
  line-height: 0;
  pointer-events: none;
  color: var(--rt-white);
}
.rt-hero-divider svg {
  display: block;
  width: 100%;
  height: clamp(38px, 5vw, 70px);
}
/* When the band under the hero is not white, the divider follows it. */
.rt-hero--banner + .rt-section--tint .rt-hero-divider,
.rt-hero-divider--tint { color: var(--rt-section-tint); }
.rt-hero-divider--dark { color: var(--rt-section-dark); }

/* ============================================================
   Bio cards: coach and staff profiles
   A responsive profile card in the same visual family as the
   board and activity cards: portrait on the left, name, role,
   and bio on the right, stacking to a single column on phones.
   Cards without a photo get a quiet brand-tinted placeholder so
   a mixed roster still reads as one set.
   ============================================================ */
.rt-bio-card {
  display: flex;
  align-items: flex-start;
  gap: 28px;
  background: var(--rt-white);
  border: 1px solid var(--rt-line);
  border-top: 5px solid var(--rt-primary);
  border-radius: var(--rt-radius);
  box-shadow: var(--rt-shadow-lift);
  padding: 28px 30px;
  margin-bottom: 28px;
}
.rt-bio-photo {
  flex: 0 0 200px;
  margin: 0;
}
.rt-bio-photo img {
  display: block;
  width: 100%;
  aspect-ratio: 5 / 6;
  object-fit: cover;
  object-position: top center;
  border-radius: calc(var(--rt-radius) - 3px);
}
/* Placeholder when no portrait exists yet. */
.rt-bio-photo--placeholder {
  aspect-ratio: 5 / 6;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rt-cream);
  border: 1px solid var(--rt-line);
  border-radius: calc(var(--rt-radius) - 3px);
}
.rt-bio-photo--placeholder::before {
  content: "\f007"; /* fa-user */
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  font-size: 56px;
  color: color-mix(in srgb, var(--rt-bio-accent, var(--rt-primary)) 45%, var(--rt-line));
}
/* No portrait yet: the coach's SCHOOL mark holds the frame instead of a
   generic head, so the card still says who they coach for. */
.rt-bio-photo--logo {
  aspect-ratio: 5 / 6;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rt-cream);
  border: 1px solid var(--rt-line);
  border-radius: calc(var(--rt-radius) - 3px);
}
.rt-bio-photo--logo img {
  width: 72%;
  height: auto;
  max-height: 72%;
  aspect-ratio: auto;
  object-fit: contain;
  border-radius: 0;
}
.rt-bio-body {
  flex: 1;
  min-width: 0;
}
.rt-bio-name {
  margin: 0 0 2px;
}
.rt-bio-role {
  margin: 0 0 14px;
  font-weight: 700;
  font-size: 0.92em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--rt-secondary);
}
.rt-bio-body p:last-child { margin-bottom: 0; }
@media (max-width: 700px) {
  .rt-bio-card {
    flex-direction: column;
    align-items: center;
    padding: 24px 20px;
  }
  .rt-bio-photo {
    flex-basis: auto;
    width: min(240px, 70%);
  }
  .rt-bio-body { width: 100%; }
}

/* Bio page intro: the one-line welcome above the cards, centered and a
   step larger than body copy. */
.rt-bio-intro {
  text-align: center;
  font-size: 1.3em;
  max-width: 44em;
  margin: 0 auto 36px;
}

/* Bio groups: a run of cards under one banner, e.g. a feeder school's
   staff. The group carries that school's brand via two custom props:
   --rt-bio-accent (card top border) and --rt-bio-accent-ink (role line,
   must stay readable on white). Cards outside a group keep site brand. */
.rt-bio-group {
  margin-bottom: 44px;
}
.rt-bio-group-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 44px 0 24px;
}
.rt-bio-group-head img {
  height: 60px;
  width: auto;
}
.rt-bio-group-head h2 {
  margin: 0;
}
.rt-bio-group .rt-bio-card { border-top-color: var(--rt-bio-accent, var(--rt-primary)); }
.rt-bio-group .rt-bio-role { color: var(--rt-bio-accent-ink, var(--rt-secondary)); }

/* Page intro: the centered welcome line under a title banner. The bio pages
   introduced this look; .rt-bio-intro is the same recipe and stays as an
   alias for the content already published with it. */
.rt-page-intro {
  text-align: center;
  font-size: 1.3em;
  max-width: 44em;
  margin: 0 auto 36px;
}

/* ============================================================
   News cards: the posts index
   A responsive grid of story cards. Posts without a featured
   image (most of the archive) get a quiet dark brand panel with
   the site mark instead of a gray hole. The newest post leads
   the page at full width.
   ============================================================ */
.rt-news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 26px;
  margin-bottom: 36px;
}
.rt-news-card {
  display: flex;
  flex-direction: column;
  background: var(--rt-white);
  border: 1px solid var(--rt-line);
  border-top: 5px solid var(--rt-primary);
  border-radius: var(--rt-radius);
  box-shadow: var(--rt-shadow-lift);
  overflow: hidden;
}
.rt-news-media {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--rt-black);
}
.rt-news-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.rt-news-media-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.rt-news-media-fallback img {
  width: auto;
  height: 58%;
  object-fit: contain;
}
.rt-news-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
  padding: 20px 22px 22px;
}
.rt-news-date {
  margin: 0;
  font-size: 0.82em;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rt-gray);
}
.rt-news-title {
  margin: 0;
  font-size: 1.15em;
  line-height: 1.35;
}
.rt-news-title a {
  color: var(--rt-ink);
  text-decoration: none;
}
.rt-news-card:hover .rt-news-title a { text-decoration: underline; }
.rt-news-excerpt {
  margin: 0;
  flex: 1;
  color: color-mix(in srgb, var(--rt-ink) 82%, transparent);
}
.rt-news-more {
  font-weight: 700;
  font-size: 0.92em;
  color: var(--rt-secondary);
}
.rt-news-more::after {
  content: "\f054"; /* fa-chevron-right */
  font-family: "Font Awesome 7 Free";
  font-weight: 900;
  font-size: 0.8em;
  margin-left: 7px;
}
/* The lead story: full row, media beside copy. */
.rt-news-card--lead {
  grid-column: 1 / -1;
  flex-direction: row;
}
.rt-news-card--lead .rt-news-media {
  flex: 0 0 46%;
  aspect-ratio: auto;
  min-height: 280px;
}
.rt-news-card--lead .rt-news-body {
  justify-content: center;
  padding: 30px 34px;
}
.rt-news-card--lead .rt-news-title { font-size: 1.6em; }
@media (max-width: 760px) {
  .rt-news-card--lead { flex-direction: column; }
  .rt-news-card--lead .rt-news-media { aspect-ratio: 16 / 9; min-height: 0; }
}
/* Pagination under the grid. */
.rt-page .pagination .nav-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-bottom: 10px;
}
.rt-page .pagination .page-numbers {
  display: inline-block;
  min-width: 42px;
  padding: 9px 14px;
  text-align: center;
  background: var(--rt-white);
  border: 1px solid var(--rt-line);
  border-radius: var(--rt-radius);
  color: var(--rt-ink);
  font-weight: 700;
  text-decoration: none;
}
.rt-page .pagination .page-numbers.current {
  background: var(--rt-primary);
  border-color: var(--rt-primary);
  color: var(--rt-on-primary, #fff);
}
.rt-page .pagination .page-numbers:not(.current):hover {
  border-color: var(--rt-primary);
}

/* The live-search component hides rows with the hidden attribute; these
   display rules out-specify the UA's [hidden] default, so restate it. */
.rt-bio-card[hidden],
.rt-bio-group[hidden] {
  display: none;
}

/* ============================================================
   Gameday hero (hero_style = gameday)
   An NFL-poster matchup in landscape: the two helmets bookend a
   center lockup of RANGERS VS OPPONENT over full-bleed art. The
   art is left-anchored and over-scaled so its baked-in helmet
   crops off the right edge, clearing room for the away helmet;
   the opponent's right-facing helmet art is mirrored to meet
   ours nose to nose.
   ============================================================ */
.rt-hero--gameday::after {
  max-width: none;
  background-image:
    linear-gradient(90deg, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.12) 30%, rgba(0, 0, 0, 0.12) 70%, rgba(0, 0, 0, 0.42) 100%),
    var(--rt-hero-image, var(--rt-hero-fallback));
  background-size: cover, cover;
  background-position: center center, center center;
  background-repeat: no-repeat, no-repeat;
}
@media (min-width: 1281px) {
  .rt-hero--gameday::after {
    -webkit-mask-image: none;
            mask-image: none;
  }
}
.rt-hero--gameday .rt-hero-stage {
  justify-content: center;
  max-width: none;
}
/* The gloss pass: two diagonal light sweeps and a top/bottom vignette over
   the whole band, the sheen every broadcast matchup graphic carries. */
.rt-hero--gameday .rt-hero-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(115deg, transparent 18%, rgba(255, 255, 255, 0.08) 30%, transparent 41%),
    linear-gradient(115deg, transparent 56%, rgba(255, 255, 255, 0.05) 67%, transparent 76%),
    linear-gradient(180deg, rgba(0, 0, 0, 0.30), transparent 24%, transparent 70%, rgba(0, 0, 0, 0.48));
}
.rt-hero--gameday .rt-hero-inner {
  max-width: 1240px;
  width: 100%;
  text-align: center;
}
.rt-matchup-kicker {
  margin: 0 0 8px;
  font-weight: 800;
  font-size: clamp(0.9rem, 1.8vw, 1.1rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--rt-primary-bright, var(--rt-primary));
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
}
.rt-matchup-face {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 2.6vw, 40px);
}
/* A no-tickets game wears a badge where the tickets button would sit, so
   free entry reads as a celebration rather than a missing link. */
.rt-matchup-free {
  display: inline-block;
  padding: 10px 26px;
  border: 2px dashed var(--rt-primary-bright, var(--rt-primary));
  border-radius: 999px;
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rt-primary-bright, var(--rt-primary));
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
  animation: rt-free-pulse 1.8s ease-in-out infinite;
}
@keyframes rt-free-pulse {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
  50% { transform: scale(1.06); box-shadow: 0 0 22px rgba(255, 255, 255, 0.18); }
}
@media (prefers-reduced-motion: reduce) {
  .rt-matchup-free { animation: none; }
}
/* Each helmet sits on a lit "stage": a soft spotlight burst behind it, a
   ground shadow beneath, and a punchier grade on the art itself. All of it is
   presentation; the helmet art (each school's brand) is untouched. */
.rt-matchup-helm {
  position: relative;
  flex: none;
  display: block;
}
.rt-matchup-helm::before {
  content: "";
  position: absolute;
  inset: -22%;
  background: radial-gradient(
    closest-side,
    rgba(255, 255, 255, 0.30),
    rgba(237, 28, 36, 0.16) 48%,
    transparent 72%
  );
  pointer-events: none;
}
.rt-matchup-helm::after {
  content: "";
  position: absolute;
  left: 12%;
  right: 12%;
  bottom: -7%;
  height: 14%;
  background: radial-gradient(ellipse closest-side, rgba(0, 0, 0, 0.65), transparent 70%);
  pointer-events: none;
}
.rt-matchup-helmet {
  position: relative;
  width: clamp(170px, 23vw, 340px);
  height: auto;
  display: block;
  filter:
    saturate(1.14)
    contrast(1.08)
    drop-shadow(0 18px 42px rgba(0, 0, 0, 0.85))
    drop-shadow(0 0 26px rgba(255, 255, 255, 0.14));
}
/* The away helmet is mirrored so two right-facing helmet arts meet nose to
   nose; opponent decals mirror with it, the standard trade every matchup
   graphic makes. */
.rt-matchup-helmet--away { transform: scaleX(-1); }
.rt-matchup-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  min-width: 0;
  /* A quiet screen behind the lockup: banner art with a center mark (VR's)
     would otherwise sit directly under the text. */
  padding: 20px clamp(18px, 3vw, 48px) 22px;
  border-radius: 20px;
  background: radial-gradient(
    closest-side,
    rgba(8, 8, 10, 0.72),
    rgba(8, 8, 10, 0.46) 62%,
    transparent 100%
  );
}
.rt-matchup-names {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(2px, 0.6vw, 8px);
  margin: 0;
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  font-size: clamp(1.8rem, 4vw, 3.4rem);
  line-height: 1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #ffffff;
}
/* Chrome-plate lettering: a white-to-steel gradient clipped to the glyphs.
   text-shadow does not paint through clipped text, so depth comes from a
   drop-shadow filter instead. */
.rt-matchup-name {
  background: linear-gradient(180deg, #ffffff 42%, #e3e6ea 78%, #c3c7cd);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.9)) drop-shadow(0 8px 22px rgba(0, 0, 0, 0.6));
}
.rt-matchup-vs {
  font-size: 0.68em;
  transform: skewX(-8deg);
  background: linear-gradient(180deg, var(--rt-primary-bright, #ff5157) 30%, var(--rt-primary, #ed1c24) 80%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  filter:
    drop-shadow(0 0 18px rgba(237, 28, 36, 0.65))
    drop-shadow(0 3px 10px rgba(0, 0, 0, 0.85));
}
/* The kickoff slate: one row per level, Varsity loudest, then JV, then
   Freshman, each a step smaller and quieter. */
.rt-matchup-slate {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.rt-matchup-slate-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 4px 14px;
  color: rgba(255, 255, 255, 0.94);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.75);
}
.rt-matchup-slate-level {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--rt-primary-bright, var(--rt-primary));
}
.rt-matchup-slate-venue a {
  color: inherit;
  text-decoration: underline;
}
.rt-matchup-slate-row--t1 { font-size: clamp(1.05rem, 2.3vw, 1.3rem); }
.rt-matchup-slate-row--t2 { font-size: clamp(0.92rem, 2vw, 1.05rem); color: rgba(255, 255, 255, 0.96); }
.rt-matchup-slate-row--t3 { font-size: clamp(0.85rem, 1.8vw, 0.95rem); color: rgba(255, 255, 255, 0.9); }
.rt-matchup-slate-row--t1 .rt-matchup-slate-level { letter-spacing: 0.14em; }
.rt-hero--gameday .rt-hero-cta { justify-content: center; }
@media (max-width: 820px) {
  /* Phones: the helmets pair up top, the lockup runs beneath them. */
  .rt-matchup-face { flex-wrap: wrap; row-gap: 4px; }
  .rt-matchup-slate { gap: 10px; }
  .rt-matchup-slate-row { flex-direction: column; gap: 1px; line-height: 1.25; }
  .rt-matchup-helmet { width: clamp(120px, 36vw, 170px); }
  .rt-matchup-helmet--home { order: 1; }
  .rt-matchup-helmet--away { order: 2; }
  .rt-matchup-center { order: 3; flex-basis: 100%; }
}

/* Links that live on the hero's dark art must not take the global black
   hover: black on a near-black banner is invisible. The chain must be the
   global rule's EXACT chain: its :not(.rt-nav a) and :not(.rt-site-footer a)
   arguments carry compound selectors that count toward specificity, so a
   shorter chain here loses (0,7,1 vs 0,8,3) and the venue link went black on
   the gameday banner. Identical chain plus .rt-hero wins outright. */
.rt-hero a:not(.rt-btn):not(.wp-element-button):not(.wp-block-button__link):not(.rt-nav a):not(.rt-site-footer a):not(.rt-social-link):not(.rt-menu-toggle):hover {
  color: var(--rt-primary-bright, var(--rt-primary));
}
.rt-footer-embed {
  margin: 18px 0 6px;
  min-height: 120px;
}

/* Single post chrome: the dateline above the article, the way back below. */
.rt-post-date {
  margin: 0 0 18px;
  font-size: 0.85em;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rt-gray);
}
.rt-post-back {
  margin-top: 34px;
}

/* ---- Matchup title banner (Season Schedule team pages) ---------------------
   The page-title band as the gameday face-off: reuses the rt-matchup-* family
   the home hero draws, but on the title banner's own dark ground and with
   un-mirrored marks (school logos have no facing). */

.rt-title-banner--matchup {
  padding: clamp(26px, 5vw, 46px) 20px clamp(22px, 4vw, 40px);
  background:
    radial-gradient(120% 170% at 50% -40%, rgba(217, 164, 0, 0.28), transparent 55%),
    linear-gradient(165deg, #101013, #1b1811);
  color: #fff;
  text-align: center;
}
.rt-title-banner--matchup .rt-matchup-face {
  max-width: min(1440px, 94vw);
  margin: 0 auto;
}
.rt-title-banner--matchup .rt-matchup-kicker {
  margin-bottom: 6px;
}
/* School logos: contained, sized with the band. A mark is only mirrored when
   its record's flip flag says so (a text-free mascot facing away from center);
   lettering must never mirror. */
.rt-title-banner--matchup .rt-matchup-logoimg {
  width: clamp(96px, 14vw, 190px);
  height: auto;
  object-fit: contain;
  transform: none;
}
.rt-title-banner--matchup .rt-matchup-logoimg--flip {
  transform: scaleX(-1);
}
@media (max-width: 759px) {
  /* The matchup band already spans the viewport on a team page (it is the page
     title slot, a full-width child of .rt-page). What made the slate hard to
     read on a phone was the room it had inside that width: the band and the
     lockup screen both carried desktop side padding, so the schedule sat in a
     narrow middle. Cut the padding back and size the slate up so a team page
     reads like the home gameday band. */
  .rt-title-banner--matchup { padding: 22px 6px 24px; }
  /* The angled chrome stripes are sized for a desktop band (130px each); at
     phone width they overlay ~110px of both margins and frame the matchup into
     a cramped centre column. Thin them right down so the helmets and slate own
     the width. Matchup only; plain page-title banners keep their full stripes. */
  .rt-title-banner--matchup::before,
  .rt-title-banner--matchup::after { width: 26px; opacity: 0.2; }
  .rt-title-banner--matchup .rt-matchup-face {
    flex-wrap: wrap;
    row-gap: 4px;
    max-width: 100%;
    gap: 8px;
  }
  .rt-title-banner--matchup .rt-matchup-center {
    order: 3;
    flex-basis: 100%;
    align-items: stretch;   /* the dark stage + slate span the band, not a strip */
    padding: 14px 6px 10px;
  }
  /* Bigger helmets so the top row fills the band instead of huddling centre. */
  .rt-title-banner--matchup .rt-matchup-logoimg {
    width: clamp(120px, 40vw, 168px);
  }
  .rt-title-banner--matchup .rt-matchup-mark--home { order: 1; }
  .rt-title-banner--matchup .rt-matchup-mark--away { order: 2; }
  /* With the full width recovered, let every slate line read larger. */
  .rt-title-banner--matchup .rt-matchup-slate { gap: 12px; }
  /* The slate rows now stretch full width (the centre stage owns the band), so
     their stacked lines must re-center: a column flex falls back to baseline
     (=left) otherwise, which un-centered the text once the band went wider. */
  .rt-title-banner--matchup .rt-matchup-slate-row { align-items: center; }
  .rt-title-banner--matchup .rt-matchup-slate-row--t1 { font-size: 1.22rem; }
  .rt-title-banner--matchup .rt-matchup-slate-row--t2 { font-size: 1.05rem; }
  .rt-title-banner--matchup .rt-matchup-slate-row--t3 { font-size: 1rem; }
}

/* Highlights slider band: tucks close under the hero rather than opening a
   full section's worth of air. */
.rt-section--slider { padding: 26px 0 30px; }

/* ---- "It's Game Day!" hero marquee ----------------------------------------
   Louder than the slider's badge on purpose: this is the first thing every
   visitor sees on the day. A skewed lit plate over the matchup, breathing a
   red glow, with a shine sweeping across every few seconds. */
.rt-matchup-gameday {
  margin: 0 0 14px;
  text-align: center;
}
.rt-matchup-gameday-plate {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 10px 30px;
  overflow: hidden;
  transform: skewX(-10deg);
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  font-size: clamp(1.4rem, 3vw, 2.1rem);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffffff;
  background: linear-gradient(180deg, var(--rt-primary-bright, #ff5157) 0%, var(--rt-primary, #ed1c24) 80%);
  box-shadow:
    0 0 26px rgba(237, 28, 36, 0.55),
    0 8px 28px rgba(0, 0, 0, 0.55);
  animation: rt-gameday-breathe 2.2s ease-in-out infinite;
}
.rt-matchup-gameday-plate .fa-football {
  font-size: 0.8em;
  transform: rotate(-18deg);
}
.rt-matchup-gameday-plate .fa-football:last-child {
  transform: rotate(18deg);
}
/* The shine: a bright diagonal band sweeping the plate. */
.rt-matchup-gameday-plate::after {
  content: "";
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: -30%;
  width: 34%;
  background: linear-gradient(105deg, transparent 0%, rgba(255, 255, 255, 0.55) 50%, transparent 100%);
  animation: rt-gameday-shine 3.2s ease-in-out infinite;
}
@keyframes rt-gameday-breathe {
  0%, 100% {
    transform: skewX(-10deg) scale(1);
    box-shadow: 0 0 26px rgba(237, 28, 36, 0.55), 0 8px 28px rgba(0, 0, 0, 0.55);
  }
  50% {
    transform: skewX(-10deg) scale(1.045);
    box-shadow: 0 0 44px rgba(237, 28, 36, 0.85), 0 8px 30px rgba(0, 0, 0, 0.55);
  }
}
@keyframes rt-gameday-shine {
  0%, 55% { left: -34%; }
  100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce) {
  .rt-matchup-gameday-plate { animation: none; }
  .rt-matchup-gameday-plate::after { animation: none; display: none; }
}
@media (max-width: 700px) {
  .rt-matchup-gameday-plate { gap: 10px; padding: 8px 20px; }
}

/* ---- Centered prose (Furi, 8-20) ------------------------------------------
   Page text centers by default: most engine pages carry a short intro above a
   module, and a small left-locked paragraph on a wide page read as an
   afterthought. Top-level paragraphs also step up in size for the same
   reason. Guards below keep the shapes where centering makes reading HARDER:
   tables keep their own alignment (see schedule.css) and forms stay
   left-labelled. */
.rt-content { text-align: center; }
.rt-content > p { font-size: 1.1rem; }
.rt-content form { text-align: left; }

/* Centered pages need centered lists: markers ride inside the line box so a
   bullet list centers as a unit instead of text-centering around left-hung
   markers. */
.rt-content ul,
.rt-content ol {
  list-style-position: inside;
  padding-left: 0;
}

/* News articles are long-form: centering a whole post is the "harder to
   read" case the centered-prose rule carves out (Furi, on the first
   word-for-word agenda). Posts read left; their lists get their markers
   back. Pages keep the centered default. */
body.single .rt-content {
  text-align: left;
}
body.single .rt-content ul,
body.single .rt-content ol {
  list-style-position: outside;
  padding-left: 2em;
}

/* ---- News article dressing --------------------------------------------------
   The article body rides a white card on the cream ground; a weekly agenda
   opens with the coach's byline card. */
.rt-page--article {
  background: var(--rt-cream, #fbf6ec);
  padding-bottom: 48px;
}
.rt-page--article .rt-content {
  background: var(--rt-white, #ffffff);
  border: 1px solid var(--rt-line, #e7e2d6);
  border-radius: var(--rt-radius, 10px);
  box-shadow: var(--rt-shadow, 0 6px 24px rgba(0, 0, 0, 0.12));
  padding: clamp(22px, 4vw, 48px);
}
/* The PDF's own tight rhythm: agenda paragraphs come from per-line PDF
   fragments, and full default margins read as gaps the coach never typed. */
.rt-page--agenda .rt-content p {
  margin: 0 0 0.7em;
}

.rt-post-byline {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 0 0 22px;
  padding-bottom: 18px;
  border-bottom: 2px solid var(--rt-primary, #ed1c24);
}
.rt-post-byline-photo img {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  object-position: top center;
  border: 3px solid var(--rt-primary, #ed1c24);
  display: block;
}
.rt-post-byline-text {
  display: flex;
  flex-direction: column;
  text-align: left;
  gap: 2px;
}
.rt-post-byline-name {
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  font-size: 1.25rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--rt-ink, #1f1f1f);
}
.rt-post-byline-meta {
  color: var(--rt-gray, #6b6b6b);
  font-size: 0.9rem;
}
.rt-post-back { text-align: center; margin-top: 30px; }

/* Article footer: mobile-first the buttons STACK (download first, centered;
   back below), because a shared row on a narrow card is exactly where they
   collided. Only at >=760px, where the card provably fits both, does the
   download float right and Back to News take the left: back means back. */
.rt-page--article .rt-content { display: flow-root; }
/* p-qualified on purpose: the agenda's tight-paragraph rule
   (.rt-page--agenda .rt-content p) otherwise out-specifies this margin and
   collapses the gap to zero, which stacked the two buttons into each other
   on phones. */
.rt-page--article .rt-content p.rt-post-back {
  text-align: center;
  margin: 26px 0 0;
}
.rt-page--agenda .rt-content > .wp-block-buttons {
  margin: 26px 0 4px;
  justify-content: center;
}
@media (min-width: 760px) {
  .rt-page--article .rt-content p.rt-post-back {
    text-align: left;
  }
  .rt-page--agenda .rt-content > .wp-block-buttons {
    float: right;
    margin-bottom: 0;
  }
}

/* Phone-size title banner: the desktop clamp floor (2.4rem) turned a long
   agenda title into six huge lines. */
@media (max-width: 700px) {
  .rt-title-banner-heading {
    font-size: clamp(1.35rem, 6.5vw, 2.1rem);
    line-height: 1.1;
  }
}
/* Long unbreakable strings (bare URLs the PDF carried as text) must never
   punch through the article card. */
.rt-page--article .rt-content {
  overflow-wrap: break-word;
}

/* ---- Link cards ----------------------------------------------------------
   A grid of outbound destinations that looks like somewhere worth going.

   Pages whose whole job is to hand a family off somewhere else (game day
   meals to Jason's Deli, tickets to HomeTown) were rendering that handoff as
   a row of bare buttons or, worse, a sentence with a link in it. Next to the
   store grid, which gives every item art, a name and a price, the same
   information read as an afterthought.

   These borrow the store card's shape deliberately, so a family moving
   between the two pages sees one site. They are plain markup with no module
   behind them, because the destinations are somebody else's system: a chair
   can hand-author one in the editor. The whole card is the target (the title
   link stretches over it), while the visible button keeps the affordance
   obvious and keyboard users get one real link per card. */
.rt-linkcards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  align-items: stretch;
  gap: 24px;
  max-width: var(--rt-maxw, 1120px);
  margin: 28px auto;
  padding: 0;
  list-style: none;
}
.rt-linkcard {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rt-line, #e7e2d6);
  border-radius: var(--rt-radius, 10px);
  overflow: hidden;
  background: #fff;
  box-shadow: var(--rt-shadow, 0 6px 24px rgba(0, 0, 0, 0.12));
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.rt-linkcard:hover,
.rt-linkcard:focus-within {
  transform: translateY(-3px);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.2);
}
@media (prefers-reduced-motion: reduce) {
  .rt-linkcard { transition: none; }
  .rt-linkcard:hover,
  .rt-linkcard:focus-within { transform: none; }
}
/* The crest band: one big Font Awesome glyph on a brand gradient, so a card
   reads at a glance without anybody sourcing artwork for it.
   The first cut washed the site mark in behind the glyph. On Vista Ridge that
   put a large VR crest directly under a small white icon, and the two simply
   argued: the glyph read as a blob stuck on the R. The mark is already in the
   header twice over, so the band does not need to repeat it. A gradient off
   the site's own chrome and primary carries the brand on its own and leaves
   the glyph the only thing to look at. */
.rt-linkcard-art {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 108px;
  color: var(--rt-on-chrome, #fff);
  background-color: var(--rt-chrome, #141414);
  background-image: linear-gradient(
    135deg,
    color-mix(in srgb, var(--rt-primary, #b3141c) 34%, var(--rt-chrome, #141414)),
    var(--rt-chrome, #141414) 62%
  );
  font-size: 2.9rem;
}
/* A faint diagonal rake, the same gesture the title banner's edges make. */
.rt-linkcard-art::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    -55deg,
    rgba(255, 255, 255, 0.05) 0 10px,
    transparent 10px 34px
  );
}
.rt-linkcard-art > * {
  position: relative;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.45));
}
.rt-linkcard-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 6px;
  padding: 18px 18px 20px;
}
.rt-linkcard-name {
  margin: 0;
  font-family: var(--rt-font-display, "RT Display", "Arial Narrow", sans-serif);
  font-weight: var(--rt-font-display-weight, 400);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 1.2rem;
  line-height: 1.15;
}
.rt-linkcard-name a {
  color: inherit;
  text-decoration: none;
}
/* The stretched hit area. The button below is the visible affordance; this is
   what makes the rest of the card behave the way people expect a tile to. */
.rt-linkcard-name a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.rt-linkcard-note {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--rt-muted, #5d5850);
}
.rt-linkcard-cta {
  display: inline-block;
  align-self: flex-start;
  margin-top: auto;
  padding: 8px 18px;
  border-radius: 999px;
  background: var(--rt-primary, #f4b41c);
  color: var(--rt-on-primary, #141414);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  text-decoration: none;
}
.rt-linkcard:hover .rt-linkcard-cta,
.rt-linkcard:focus-within .rt-linkcard-cta {
  filter: brightness(1.06);
}
