/*
 * clear-aud.css
 * CLEAR-AUD — PeerView Repository Site
 *
 * Component + layout styles for every section. Consumes only the tokens
 * defined in base.css — never raw font-size/family/weight (see
 * docs/typography.md pattern this project follows). Section order below
 * matches the page's visual order top to bottom.
 *
 * Version : 8.9
 * Updated : 2026-11-04 (hero slide faculty/statement refinements)
 */

/* ============================================================
   SHARED COMPONENTS — buttons, tags, badges, links, carousels
   ============================================================ */

.ca-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--typography-family-title);
  font-size: var(--typography-scale-sm);
  font-weight: var(--typography-weight-semibold);
  padding: var(--size-space-xs) var(--size-space-md);
  border-radius: var(--size-radius-full);
  border: none;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition-base), transform var(--transition-fast), color var(--transition-base);
}
.ca-btn--pill {
  background: var(--color-button-primary);
  color: var(--color-text-invert);
}
@media (hover: hover) { .ca-btn--pill:hover { background: var(--color-link-hover); transform: scale(1.03); } }
.ca-btn--white {
  background: #FFFFFF;
  color: var(--color-link);
}
@media (hover: hover) { .ca-btn--white:hover { background: var(--color-light-blue); transform: scale(1.03); } }
.ca-btn--outline {
  background: transparent;
  color: var(--color-link);
  border: 1.5px solid var(--color-link);
}
@media (hover: hover) { .ca-btn--outline:hover { background: var(--color-light-blue); } }

/* Specialty tag styling applies to every .ca-tag-pill site-wide, per direct
   spec ("This applies in all specialty tags btw") — Education and Project
   ECHO both use this class. display:inline-block alone isn't enough to
   keep it from stretching full-width when it's a flex child with no
   align-items set on its parent (the same bug fixed on the hero badge
   earlier) — see .ca-edu-featured__details below for where that's fixed
   for Education's featured card specifically. */
.ca-tag-pill {
  display: inline-block;
  font-family: var(--typography-family-body);
  font-size: var(--typography-scale-xs); /* 12px — corrected back down from a 14px mistake */
  font-weight: var(--typography-weight-regular);
  color: var(--color-text-secondary);
  background: var(--color-light-blue);
  border: 1px solid var(--color-mid-blue);
  border-radius: var(--size-radius-minimal);
  padding: 2px var(--size-space-2xs);
  margin-bottom: var(--size-space-2xs);
}

.ca-badge { display: inline-block; border-radius: var(--size-radius-minimal); padding: 4px var(--size-space-2xs); font-family: var(--typography-family-body); font-size: var(--typography-scale-sm); font-weight: var(--typography-weight-semibold); color: #FFFFFF; }
.ca-badge--negative { background: var(--color-negative); }
/* "Point of Care" indicator (Practice Aids) — matches PeerView's own
   PACE site (pace-alz.com, given directly as the reference): a small
   solid pill immediately after the link title, same blue (their badge's
   background is #009FE3, which is exactly this project's --color-link —
   no translation needed). Font-size scoped down to 12px (--typography-
   scale-xs), matching .ca-tag-pill's size — direct spec ("like other
   tags") — rather than the shared .ca-badge default of 14px, which stays
   as-is for the hero's red tag (the only other .ca-badge consumer). */
.ca-badge--point-of-care { background: var(--color-link); font-size: var(--typography-scale-xs); }

/* Standardized text-link treatment, site-wide (2026-09-25) — color change
   only, no underline (neither static nor animated). Was a sliding-underline
   hover (background-image gradient trick, 0%→100% width on hover);
   simplified per direct instruction ("let's not do the rolling underline
   and stick to just the color change") while auditing link consistency
   (the Biography link not matching ASAM's link style was the trigger).
   This is the one shared rule every text link on the site should use —
   see .ca-person__bio-link below for an element that previously had its
   own one-off hover style and now just reuses this class instead. */
.ca-link { color: var(--color-link); text-decoration: none; transition: color var(--transition-base); }
@media (hover: hover) { .ca-link:hover { color: var(--color-link-hover); } }

.ca-link-list { display: flex; flex-direction: column; gap: var(--size-space-sm); }
.ca-link-item { display: flex; flex-direction: column; gap: 2px; break-inside: avoid; }
.ca-link-item .ca-link { font-size: var(--typography-scale-base); font-weight: var(--typography-weight-semibold); }
.ca-link-item__desc { color: var(--color-text-secondary); }
.ca-link-item--podcast { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--size-space-2xs) var(--size-space-sm); align-items: baseline; text-decoration: none; }

.ca-section-heading { color: var(--color-text-primary); margin-bottom: var(--size-space-lg); }
.ca-section-footnote { color: var(--color-text-primary); margin-top: var(--size-space-md); } /* 16px black, via BodyBase class (was BodySmall/14px gray) */

/* mask-image, not a plain <img> (was one) — per CLAUDE.md "Icons", a plain
   <img> can't have its color controlled by CSS, and the spec calls for a
   gray icon matching the gray credit text next to it. Real aspect ratio is
   27:20 (the source SVG's own viewBox); 20x15 keeps that ratio. */
.ca-icon-credit { display: inline-block; width: 20px; height: 15px; vertical-align: text-bottom; margin-right: var(--size-space-2xs); background-color: var(--color-text-secondary); -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; -webkit-mask-image: url('../images/icons/credits.svg'); mask-image: url('../images/icons/credits.svg'); }

/* Carousel shell — shared by Video Updates / Education / Project ECHO.
   The hero promo carousel reuses the track/card mechanics but has its own
   markup wrapper (.ca-hero-carousel) since it isn't wrapped in .container
   the same way. */
.ca-carousel { overflow: hidden; overflow: clip; } /* clip, not just hidden: hidden is still a scroll container, so keyboard focus on an off-screen card natively scrolled it and fought the transform (PACE 2026-10-16) */
.ca-carousel__track { display: flex; gap: var(--carousel-gap); transition: transform var(--transition-carousel); }
.ca-carousel__controls { display: flex; align-items: center; justify-content: space-between; margin-top: var(--size-space-md); }
/* Real bug (2026-09-25), affecting every carousel's controls, not just
   hero's: setting the `hidden` attribute (ca-carousel.js's `controls.
   hidden = !showControls`, when there aren't enough items to navigate)
   only works via the browser's default `[hidden]{display:none}` UA rule
   — and this class's own unconditional `display:flex` above has equal
   specificity and loads later, so it silently wins and the row stayed
   visible regardless of the `hidden` attribute actually being set
   (confirmed directly: `controls.hidden` read `true` in the DOM, but the
   arrows were still visibly on screen). Restores the attribute's effect
   explicitly rather than relying on the UA default. */
.ca-carousel__controls[hidden] { display: none; }
.ca-carousel__dots { display: flex; align-items: center; gap: var(--size-space-2xs); }
/* Inactive dots are an outlined circle (blue border, transparent fill),
   not a solid gray fill — matches the mockup's light-background dot
   treatment (the hero carousel's own dots are a separate, white-outline
   variant scoped under .ca-carousel__controls--hero, since those sit on a
   photo instead of a white section background). */
.ca-carousel__dot { width: 8px; height: 8px; border-radius: var(--size-radius-full); border: 1.5px solid var(--color-link); padding: 0; background: transparent; cursor: pointer; transition: all var(--transition-base); }
.ca-carousel__dot.is-active { width: 20px; background: var(--color-navy); border-color: var(--color-navy); }
.ca-carousel__arrows { display: flex; gap: var(--size-space-2xs); }
.ca-carousel__arrow { width: 46px; height: 46px; border-radius: 50%; border: none; background: var(--color-button-primary); cursor: pointer; position: relative; transition: background var(--transition-base), opacity var(--transition-base); }
@media (hover: hover) { .ca-carousel__arrow:hover:not(:disabled) { background: var(--color-link-hover); } }
.ca-carousel__arrow:disabled { opacity: 0.35; cursor: default; }
.ca-carousel__arrow::before { content: ''; position: absolute; inset: 0; margin: auto; width: 16px; height: 16px; background: #FFFFFF; -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; }
.ca-carousel__arrow--prev::before { -webkit-mask-image: url('../images/icons/arrow-left.svg'); mask-image: url('../images/icons/arrow-left.svg'); }
.ca-carousel__arrow--next::before { -webkit-mask-image: url('../images/icons/arrow-right.svg'); mask-image: url('../images/icons/arrow-right.svg'); }

/* ============================================================
   TOP BAR
   ============================================================ */
.ca-topbar { position: sticky; top: 0; z-index: 200; background: var(--color-navy); transition: transform var(--transition-slow); }
/* Slides off-screen on scroll-down, slides back in on scroll-up — same
   pattern as Hub_Curriculum's .topnav--hidden. See ca-nav.js initScrollHide(). */
.ca-topbar--hidden { transform: translateY(-100%); }
.ca-topbar__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--size-space-md); height: 78px; }
.ca-topbar__logo { display: flex; align-items: center; gap: var(--size-space-xs); }
.ca-topbar__logo img { height: 40px; }
.ca-topbar__items { display: flex; align-items: center; gap: var(--size-space-md); }

.ca-topbar__search { position: relative; display: flex; align-items: center; }
.ca-topbar__search input { width: 332px; height: 46px; border-radius: var(--size-radius-full); border: none; padding: 0 52px 0 var(--size-space-sm); font-family: var(--typography-family-body); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); color: var(--color-text-primary); background: #FFFFFF; }
.ca-topbar__search input::placeholder { color: var(--color-text-light); }
.ca-topbar__search-submit { position: absolute; right: 3px; top: 3px; width: 40px; height: 40px; border-radius: 50%; border: none; background: var(--color-button-primary); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background var(--transition-base); }
@media (hover: hover) { .ca-topbar__search-submit:hover { background: var(--color-link-hover); } }
.ca-icon-search, .ca-icon-share { width: 16px; height: 16px; display: inline-block; background: #FFFFFF; -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; }
.ca-icon-search { -webkit-mask-image: url('../images/icons/search.svg'); mask-image: url('../images/icons/search.svg'); }
.ca-icon-share  { -webkit-mask-image: url('../images/icons/share.svg');  mask-image: url('../images/icons/share.svg'); }

.ca-topbar__specialty-trigger { display: inline-flex; align-items: center; gap: var(--size-space-xs); height: 46px; padding: 0 var(--size-space-md); border-radius: var(--size-radius-full); border: none; background: var(--color-button-primary); color: #FFFFFF; font-family: var(--typography-family-body); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); cursor: pointer; transition: background var(--transition-base); }
@media (hover: hover) { .ca-topbar__specialty-trigger:hover { background: var(--color-link-hover); } }

/* Active-filter count. Always present in the layout — toggling
   `visibility` (not `display`) so the badge's reserved space never
   collapses, which is what keeps the trigger button (and everything
   flexed alongside it) a fixed width whether a filter is on or off. See
   CLAUDE.md "Filter by Specialty badge". Updated by
   ca-search.js's updateSpecialtyBadges() on every filter change.

   aspect-ratio (not a fixed height) is what keeps this a true circle —
   the old version paired a fixed height with min-width + horizontal
   padding, and for a single digit the padding alone pushed the resolved
   width past min-width while height stayed fixed, rendering an oval.
   Locking height to whatever width resolves to keeps it circular at any
   digit count instead. */
.ca-specialty-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 20px;
  aspect-ratio: 1 / 1;
  padding: 0 3px;
  border-radius: var(--size-radius-full);
  background: var(--color-negative);
  color: #FFFFFF;
  font-family: var(--typography-family-title);
  font-size: var(--typography-scale-xs);
  font-weight: var(--typography-weight-semibold);
  line-height: 1;
  visibility: hidden;
}
.ca-specialty-badge.is-visible { visibility: visible; }
/* dropdown-arrow.svg is a stacked up+down double-chevron (a "toggle" glyph,
   not a single directional arrow), so it isn't rotated on open/close —
   rotating a vertically-symmetric shape 180deg is a no-op. Open/closed
   state reads from the trigger's own background color instead. */
.ca-topbar__specialty-chevron { width: 8px; height: 13px; flex-shrink: 0; display: inline-block; background: #FFFFFF; -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; -webkit-mask-image: url('../images/icons/dropdown-arrow.svg'); mask-image: url('../images/icons/dropdown-arrow.svg'); }

/* Placeholder shell for the specialty menu — restyle-able from a
   screenshot pending (see CLAUDE.md "Open items"); behavior is final.
   Positioned relative to .ca-topbar (position:sticky already establishes
   a containing block) rather than the trigger's own wrapper, so it stays
   reachable when opened from the mobile trigger inside .ca-topbar__overlay
   — see the HTML comment at its markup for why. */
/* position:fixed with top/left set inline by ca-nav.js, computed from the
   trigger's live getBoundingClientRect() on every open — this is what
   guarantees correct alignment under the trigger at any viewport width or
   topbar height, instead of the static CSS offset that used to drift out
   of alignment (see CLAUDE.md). */
.ca-specialty-menu {
  position: fixed;
  min-width: 260px;
  max-width: 320px;
  max-height: 420px;
  overflow-y: auto;
  background: #FFFFFF;
  border-radius: var(--size-radius-rounded);
  box-shadow: 0 12px 32px rgba(22, 26, 46, 0.18);
  padding: var(--size-space-md);
  display: none;
  flex-direction: column;
  z-index: 300;
}
.ca-specialty-menu.is-open { display: flex; }
.ca-specialty-menu__list { display: flex; flex-direction: column; gap: var(--size-space-sm); margin-bottom: var(--size-space-md); }
.ca-specialty-menu__option { display: flex; align-items: center; gap: var(--size-space-xs); cursor: pointer; }
/* Matches the page-nav (sub-nav) label size — "a bit smaller" per feedback,
   was the base 16px body size. */
.ca-specialty-menu__option-label { font-family: var(--typography-family-body); font-size: var(--typography-scale-sm); color: var(--color-text-primary); }
/* Custom checkbox — native control restyled via appearance:none, checkmark
   drawn with a rotated pseudo-element border rather than an icon asset. */
.ca-specialty-menu__checkbox {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin: 0;
  border: 1.5px solid var(--color-border-midgray);
  border-radius: var(--size-radius-minimal);
  cursor: pointer;
  position: relative;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ca-specialty-menu__checkbox:checked { background: var(--color-navy); border-color: var(--color-navy); }
.ca-specialty-menu__checkbox:checked::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 10px;
  border: solid #FFFFFF;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.ca-specialty-menu__actions { display: flex; gap: var(--size-space-2xs); }
/* white-space: nowrap (2026-09-30) — "View Results"/"Clear All" were
   wrapping to 2 lines, reported directly via screenshot. Root cause:
   flex:1 with normal text wrapping lets the browser shrink each button
   below its one-line text width (a flex item's default min-width:auto
   only protects the longest unbreakable WORD, not the whole phrase, once
   wrapping is allowed) — this stops it from ever shrinking past the
   phrase's own natural width instead. */
.ca-specialty-menu__actions .ca-btn { flex: 1; padding: var(--size-space-xs) var(--size-space-sm); font-size: var(--typography-scale-sm); white-space: nowrap; }
.ca-specialty-menu__empty { padding: var(--size-space-xs); color: var(--color-text-secondary); }

.ca-topbar__share { display: inline-flex; align-items: center; gap: var(--size-space-2xs); background: none; border: none; color: #FFFFFF; font-family: var(--typography-family-body); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); cursor: pointer; }
.ca-topbar__share-label { }

.ca-topbar__hamburger { display: none; background: none; border: none; color: #FFFFFF; padding: var(--size-space-2xs); }
.ca-topbar__overlay { display: none; }

/* ============================================================
   SUB-NAV
   ============================================================ */
.ca-subnav { position: sticky; top: 78px; z-index: 190; background: #FFFFFF; box-shadow: 0 2px 8px rgba(0,0,0,0.06); display: flex; align-items: center; justify-content: center; gap: var(--size-space-lg); padding: 0 var(--size-space-2xl); height: 46px; overflow-x: auto; scrollbar-width: none; transition: top var(--transition-slow); }
.ca-subnav::-webkit-scrollbar { display: none; }
/* No underline treatment (removed per feedback) — hover/active read from
   color alone. */
.ca-subnav__link { flex: 0 0 auto; color: var(--color-link); font-family: var(--typography-family-body); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); text-decoration: none; padding: var(--size-space-2xs) 0; transition: color var(--transition-base); white-space: nowrap; }
.ca-subnav__link.is-active { color: var(--color-link-hover); }
@media (hover: hover) { .ca-subnav__link:hover { color: var(--color-link-hover); } }
/* Pinned flush to the viewport top when the topbar above it has scrolled
   out of view — see ca-nav.js initScrollHide(). */
.ca-subnav.ca-subnav--pinned-top { top: 0; }

/* ============================================================
   HERO
   ============================================================ */
/* padding-bottom: 0 — the carousel sits flush with the section's bottom
   edge; the gap before Video Updates is that section's own top padding,
   not this one's bottom padding (keeps the boundary single-owned). */
/* padding-bottom is NOT 0 (2026-09-28, was 0 before this fix) — .ca-hero
   has its own overflow:hidden (needed to clip the background video), which
   was clipping the hero carousel's own drop-shadow flat at the section's
   bottom edge with zero room to render into, even after moving the shadow
   off the carousel itself onto .ca-hero-carousel__frame (see that rule's
   own comment) — that fix alone wasn't enough, since the outer SECTION's
   overflow:hidden clips just as hard as the carousel's own did. Reported
   directly via screenshot, twice, before landing on the real fix: give the
   section a small bottom padding so the shadow has somewhere to actually
   render, and take that same amount back off #video-updates's own top
   padding below so the total Hero->Video-Updates gap doesn't change. */
.ca-hero { position: relative; overflow: hidden; background: linear-gradient(135deg, #EAF4F9 0%, #F7FBFD 60%, #EFF7FB 100%); padding-top: var(--size-space-2xl); padding-bottom: var(--size-space-md); }
.ca-hero__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
/* Fakes a fade-to-white into the video, which has none baked in: 20%
   opacity white at the top (video reads clearly) ramping to fully opaque
   white at the bottom (where the wordmark/carousel sit on solid ground). */
.ca-hero__bg-overlay { position: absolute; inset: 0; z-index: 0; pointer-events: none; background: linear-gradient(to bottom, rgba(255,255,255,0.2) 0%, rgba(255,255,255,1) 100%); }
.ca-hero__inner { position: relative; z-index: 1; }
.ca-hero__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--size-space-lg); margin-bottom: var(--size-space-lg); flex-wrap: wrap; }
.ca-hero__brand { max-width: 720px; }
/* 80px is a one-off hero display size, deliberately outside the small
   type scale in base.css (which tops out at 30px) — sized down at each
   responsive breakpoint below rather than left fluid, to match how the
   rest of the layout (§7 responsive) steps at fixed widths instead of
   scaling continuously. margin: 0 — no gap to the tagline beneath it. */
.ca-hero__wordmark { font-family: var(--typography-family-title); font-style: italic; font-weight: var(--typography-weight-bold); font-size: 80px; letter-spacing: -0.01em; color: var(--color-hero-wordmark); margin: 0; }
/* font-style: normal — the TM mark reads as upright in the mockup even
   though the wordmark itself is italic; margin-left in em (not a spacing
   token) so "a bit of space" scales with the wordmark's own font-size
   across breakpoints instead of needing separate tuning at each one. */
.ca-hero__wordmark sup { font-size: 0.35em; top: -1.6em; font-style: normal; margin-left: 0.15em; }
/* max-width dropped (was 600px, tighter than .ca-hero__brand's own
   720px) — at 30px it was forcing an unnecessary 3rd wrap line; letting
   it use the full brand column width is what gets it to 2 lines. */
.ca-hero__tagline { color: var(--color-text-primary); margin: 0 0 var(--size-space-sm) 0; }
/* Per direct spec (2026-09-30): the letters spelling out CLEAR-AUD within
   the tagline itself (the C in "Curriculum", L in "Leveraging", etc.) pick
   up the same teal-blue as the wordmark above — a literal reuse of
   --color-hero-wordmark, not a new one-off color, so the two always move
   together if that shade is ever revisited. Applied via a span wrapping
   just the one letter in each word, in json/content_clear-aud.json's
   hero.tagline field (HTML is allowed there, see "Displayed content
   fields render raw HTML" in CLAUDE.md). */
.ca-hero__tagline-accent { color: var(--color-hero-wordmark); }
.ca-hero__supporter { color: var(--color-text-primary); margin: 0; }

/* align-self: flex-end — bottom-aligns the box against the brand
   column's height (the row's own align-items stays flex-start, since
   the brand column's content should still anchor to the top). */
.ca-hero__collaborator { flex: 0 0 320px; align-self: flex-end; background: #FFFFFF; border-radius: var(--size-radius-rounded); padding: var(--size-space-sm) var(--size-space-xs); box-shadow: 0 8px 24px rgba(22,26,46,0.08); }
.ca-hero__collaborator-label { color: var(--color-text-primary); margin: 0 0 var(--size-space-2xs) 0; }
.ca-hero__collaborator-logo { margin-bottom: var(--size-space-sm); }
/* ASAM.svg is the full lockup (mark + wordmark + description) as one
   piece of artwork — sized to the full width of the box, height follows
   its own ~4.7:1 ratio. */
.ca-hero__collaborator-logo img { width: 100%; height: auto; display: block; }
.ca-hero__collaborator .ca-btn { width: 100%; font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); }

/* position:relative — containing block for the controls overlay below.
   border + box-shadow: white frame with a soft lift, per the mockup
   (measured off the real screenshot: ~2px white edge, not a thick
   border). */
/* box-shadow deliberately lives on .ca-hero-carousel__frame (the plain
   wrapper in home.html), not here — this element's own overflow:hidden
   (needed to clip the sliding track) clips its own box-shadow too, most
   visibly cutting off the bottom edge where a drop-shadow extends
   furthest. border-radius is repeated on both elements on purpose, so
   the shadow's own rounded shape lines up with this element's clipped
   corners. */
.ca-hero-carousel__frame { border-radius: var(--size-radius-rounded); box-shadow: 0 8px 24px rgba(22,26,46,0.12); }
.ca-hero-carousel { position: relative; overflow: hidden; overflow: clip; border-radius: var(--size-radius-rounded); border: 2px solid #FFFFFF; }
.ca-hero-carousel__track { display: flex; gap: var(--carousel-gap); transition: transform var(--transition-carousel); }
/* The whole slide is the click target (2026-09-25) — an <a> for 'event'
   items, a <div data-video-trigger> for 'video' ones (see
   buildEventSlide(), ca-render.js) — so this needs the usual link resets
   regardless of which tag it actually is, plus cursor:pointer for the
   non-anchor case. */
.ca-hero-slide { position: relative; flex: 0 0 100%; min-height: 420px; border-radius: var(--size-radius-rounded); overflow: hidden; display: flex; align-items: flex-start; text-decoration: none; color: inherit; cursor: pointer; }
/* The background photo is a real <img> now, not a CSS background-image —
   needed so it can zoom on hover independently of the card's own fixed
   border-radius/overflow-hidden frame, same technique as every other
   card's thumbnail (.ca-card__thumb img). Sits behind the scrim/content
   (z-index 0, both of which are position:relative/z-index:1) and fills
   the slide exactly via object-fit:cover, matching the old background-
   size:cover behavior pixel-for-pixel. */
.ca-hero-slide__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; transition: transform var(--transition-slow); }
@media (hover: hover) { .ca-hero-slide:hover .ca-hero-slide__bg { transform: scale(1.04); } }
/* --hero-slide-scrim (gradient, desktop) / --hero-slide-scrim-mobile (flat,
   ≤767px — see the media query below) are set inline per-slide
   (ca-render.js, from the item's optional scrimColor in events.json); these
   are the fallbacks for slides that don't set one. See CLAUDE.md "Hero
   carousel". The fading 3-stop gradient works on desktop, where content is
   capped at 75% width and never reaches the low-opacity edge — but at
   mobile widths content spans the full card and a long title/subtitle can
   wrap right into that low-opacity zone and go illegible against the photo
   (a real bug, confirmed on a real device screenshot), so mobile gets a
   flat, constant-opacity wash instead. */
.ca-hero-slide__scrim { position: absolute; inset: 0; background: var(--hero-slide-scrim, linear-gradient(90deg, rgba(0,119,190,0.88) 0%, rgba(0,119,190,0.55) 45%, rgba(0,119,190,0.05) 75%)); }
/* align-items: flex-start (on the flex column itself) — without it,
   flex's default `stretch` cross-axis alignment blockifies the badge's
   inline-block display and forces it (and anything else that isn't
   already a full-width block) to fill the column's width instead of
   hugging its own content.
   No `gap` here (deliberately) — the vertical rhythm between tag / date /
   title / subtitle / city / CTA is non-uniform per direct spec (8 / 8 / 0
   / 8 / 24px), which a single flex `gap` can't express since it applies
   the same value between every pair of children. Each child instead
   carries its own margin-bottom below, in that exact sequence.
   padding is a uniform 40px/lg on all 4 sides — "content should always
   be 40px from the top", per direct spec — the top-align comes from
   .ca-hero-slide's own align-items: flex-start (this element's own
   padding-top just sets the exact 40px gap once anchored there).
   padding-bottom is still deliberately larger than the other 3 sides
   (2xl+sm = 96px, not lg's 40px) — this isn't in tension with top-align,
   it's a *separate* reserved-space concern: the controls overlay is
   absolutely positioned at the card's own bottom (out of flow, so it
   can't push the card taller on its own), and .ca-hero-slide has no
   fixed height — its own height is however tall its content forces it to
   be. For a SHORT slide, min-height covers this with room to spare and
   the extra bottom padding is invisible slack. But for the TALLEST slide
   (a 3-line-wrapping title, confirmed via a real device screenshot on
   mobile, where content is 100% width) the content's natural height
   already exceeds min-height, so the card's real height becomes exactly
   "content's own height" with zero slack — without this reserved bottom
   padding, the CTA's bottom would land flush with the card's bottom edge
   and directly collide with the controls (confirmed: the "Register"
   button and the nav arrow sat at the same height in that screenshot).
   Removing this reservation once (reasoning it was made obsolete by
   top-align) was wrong — it only looked fine on a short slide. Every
   slide is stretched to match the tallest one anyway (.ca-hero-carousel
   __track's default flex `stretch`), so this padding effectively grows
   the *whole carousel's* height enough for its tallest current slide,
   not just this one.
   max-width: 75% — "about 3/4 of the width of the card on desktop", per
   direct spec (was a fixed 560px). */
.ca-hero-slide__content { position: relative; z-index: 1; padding: var(--size-space-lg) var(--size-space-lg) calc(var(--size-space-2xl) + var(--size-space-sm)) var(--size-space-lg); max-width: 75%; display: flex; flex-direction: column; align-items: flex-start; }
.ca-hero-slide__content .ca-badge { margin-bottom: var(--size-space-2xs); } /* tag -> date: 8px */
.ca-hero-slide__date { color: #FFFFFF; margin-bottom: var(--size-space-2xs); } /* date -> title: 8px */
.ca-hero-slide__title { color: #FFFFFF; margin: 0; } /* title -> subtitle: 0px */
/* 20px semibold italic, per direct spec ("subtitle is semi bold") — no
   utility class covers italic, so this stays a scoped override rather
   than a new .Title/.Body variant just for one field. */
.ca-hero-slide__subtitle { color: #FFFFFF; font-size: var(--typography-scale-lg); font-weight: var(--typography-weight-semibold); font-style: italic; margin: 0 0 var(--size-space-2xs) 0; } /* subtitle -> faculty: 8px */
/* faculty is available on every hero promo contentType (2026-09-28, was
   video-only) — 8px above (from title or subtitle, whichever is the
   nearest preceding sibling; margins collapse to whichever is larger when
   both are present), 24px below to the CTA. */
.ca-hero-slide__faculty { color: #FFFFFF; margin: var(--size-space-2xs) 0 var(--size-space-md) 0; }
.ca-hero-slide__content .ca-btn { align-self: flex-start; font-size: var(--typography-scale-base); margin-top: 0; }
/* statement (2026-09-28) — an optional line styled like faculty, but
   rendered after the CTA button rather than before it (e.g. a CME-credit
   note). */
.ca-hero-slide__statement { color: rgba(255, 255, 255, 0.9); margin: var(--size-space-md) 0 0 0; max-width: 560px; } /* 24px below the CTA = the 24px above it; 14px regular (BodySmall) + capped width so the fine print wraps to ~2 lines and stays quieter than the faculty (2026-11-04) */
.ca-hero-slide__faculty { line-height: 1.5; }
/* CTA reflects hover even when the pointer is elsewhere on the card, not
   just directly over the CTA span itself — the whole card is the click
   target now, so its most visible affordance should visually respond to
   that. Same treatment .ca-btn--white:hover already gives itself. */
@media (hover: hover) { .ca-hero-slide:hover .ca-btn { background: var(--color-light-blue); transform: scale(1.03); } }
/* Overlaid at the bottom of the card itself — not a separate row below
   the carousel like before. bottom: 24px/md is a direct user spec (not
   matched to the content block's own 40px/lg padding — those are
   independent insets that happen to both be design-token values).
   Colors are inverted from the shared .ca-carousel__arrow/__dot styling
   (white circle + blue icon, white-outline inactive dots) because this
   sits on a photo, not the white section background every other
   carousel's controls sit on. */
/* pointer-events: none on the row itself, re-enabled on the dots/arrows
   individually (2026-09-25) — now that the whole slide is a click/hover
   target of its own, this row's `justify-content: space-between` leaves
   a wide empty gap between the dots (left) and arrows (right) that's
   still part of this container's own bounding box, spanning almost the
   full card width at a fixed height near the bottom — without this, that
   empty strip silently ate every hover/click meant for the slide
   underneath it, confirmed directly ("the bottom portion of the card is
   still not reflecting the hover/click"). This is a pure visual overlay
   now; only its two real controls remain interactive. */
.ca-carousel__controls--hero { position: absolute; left: var(--size-space-lg); right: var(--size-space-lg); bottom: var(--size-space-md); z-index: 2; pointer-events: none; }
.ca-carousel__controls--hero .ca-carousel__dot,
.ca-carousel__controls--hero .ca-carousel__arrow { pointer-events: auto; }
.ca-carousel__controls--hero .ca-carousel__dot { background: transparent; border: 1.5px solid #FFFFFF; }
.ca-carousel__controls--hero .ca-carousel__dot.is-active { background: var(--color-navy); border-color: var(--color-navy); }
.ca-carousel__controls--hero .ca-carousel__arrow { background: #FFFFFF; }
@media (hover: hover) { .ca-carousel__controls--hero .ca-carousel__arrow:hover:not(:disabled) { background: var(--color-light-blue); } }
.ca-carousel__controls--hero .ca-carousel__arrow::before { background: var(--color-link); }

/* ============================================================
   SECTIONS — generic wrapper
   ============================================================ */
.ca-section { padding: var(--size-space-2xl) 0; background: #FFFFFF; }
.ca-section--tinted { background: var(--color-surface-primary); }
/* Two plain (non-tinted, white) sections back to back would otherwise each
   contribute their own 80px top+bottom padding, doubling into a 160px gap
   at the shared boundary — generic rule (2026-09-26), replacing what used
   to be individually hand-tuned #video-updates/#steering-committee padding
   overrides: whenever a plain .ca-section is immediately followed by
   another plain .ca-section, the TOP one's bottom padding collapses to 0
   and the following section's own top padding is left to supply the gap
   instead — the same "one owner per boundary" pattern used site-wide
   (see Hero -> Video Updates below). .ca-section--tinted is excluded on
   both sides: a background-color change at the boundary still needs the
   full gap to read as two distinct sections. This is a :has() rule so any
   future pair of adjacent white sections gets it automatically — no need
   to hand-add another #id override every time one's added. */
.ca-section:not(.ca-section--tinted):has(+ .ca-section:not(.ca-section--tinted)) {
  padding-bottom: 0;
}
/* The rule above only checks the IMMEDIATE next sibling's class — it
   doesn't know that a section can go empty and hide itself at runtime
   (CA.hideIfEmpty, see CLAUDE.md "Content lifecycle"). Practice Aids and
   More Resources are both .ca-section--tinted and both CAN go fully empty
   (unlike ASAM/Project ECHO, which never hide) — when one does, its
   [hidden] attribute gives it zero visual height, but it's still the DOM
   sibling the rule above inspects, so it still reads as "the next section
   is tinted" and refuses to collapse the white section before it. Result:
   a real double gap (confirmed directly — emptying Practice Aids left
   Education's own 80px bottom padding AND ASAM's own 80px top padding both
   in effect, 160px total, reported via screenshot). This second rule
   bridges exactly that one-hidden-section-in-between case: a plain white
   section whose next sibling is a HIDDEN tinted one, itself followed by
   another plain white section, also collapses its own bottom padding —
   same as if the hidden tinted section weren't there at all. Covers both
   real cases on this page (Education -> hidden Practice Aids -> ASAM, and
   ASAM -> hidden More Resources -> Project ECHO) without hardcoding either
   pair's ids. */
.ca-section:not(.ca-section--tinted):has(+ .ca-section--tinted[hidden] + .ca-section:not(.ca-section--tinted)) {
  padding-bottom: 0;
}
/* Steering Committee keeps its own tighter 40px top padding (not the
   standard 80px) since it reads as a sub-section of Video Updates, not a
   fully separate section (it has no sub-nav entry of its own — see
   "Sections" above) — orthogonal to the generic white-adjacency rule
   above, which only ever touches the PRECEDING section's bottom padding. */
#steering-committee { padding-top: var(--size-space-lg); }
/* Hero -> Video Updates: Hero now owns a small slice of this gap on its
   own bottom (var(--size-space-md), 2026-09-28 — see .ca-hero's own
   padding-bottom comment for why) so its carousel's drop-shadow has room
   to render before Hero's own overflow:hidden clips it. Video Updates
   gives back that exact same amount from its top so the total gap is
   unchanged from the standard 80px — same "one owner per boundary" idea
   as the rest of this file, just split across two owners instead of one
   for this specific pair, since the amount taken off the top here only
   exists to compensate for what Hero now needs on its own bottom. */
#video-updates { padding-top: calc(var(--size-space-2xl) - var(--size-space-md)); }

/* ============================================================
   CARDS — Video Updates / Education / Project ECHO
   ============================================================ */
.ca-card { display: flex; flex-direction: column; text-decoration: none; color: inherit; cursor: pointer; }
.ca-card__thumb { position: relative; border-radius: var(--size-radius-rounded); overflow: hidden; aspect-ratio: 16/9; background: var(--color-surface-primary); }
.ca-card__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--transition-slow); }
@media (hover: hover) { .ca-card:hover .ca-card__thumb img { transform: scale(1.04); } }
.ca-card__meta { padding: var(--size-space-md) 0 var(--size-space-xs) 0; } /* thumb -> meta: 24px, per direct spec (was 12px); bottom unchanged */
.ca-card__date { color: var(--color-text-secondary); margin: 0 0 var(--size-space-2xs) 0; }
.ca-card__title { color: var(--color-link); margin: 0 0 var(--size-space-2xs) 0; transition: color var(--transition-base); }
@media (hover: hover) { .ca-card:hover .ca-card__title { color: var(--color-link-hover); } }
.ca-card__faculty { color: var(--color-text-secondary); margin: 0; }
/* Featured search-result rows only (.ca-card__subtitle, .ca-card--list
   .ca-badge) — subtitle is bolder/black, one step down from the title,
   distinct from the plain gray .ca-card__faculty meta line right below
   it; badge gets the same small bottom
   margin .ca-tag-pill already has elsewhere, so it doesn't sit flush
   against the date line above it. See buildFeaturedListRow(). */
.ca-card__subtitle { color: var(--color-text-primary); font-weight: var(--typography-weight-semibold); margin: 0 0 var(--size-space-2xs) 0; }
.ca-card--list .ca-badge { margin-bottom: var(--size-space-2xs); }
/* margin-top: 0 — the faculty -> credit 8px gap is supplied by
   .ca-card__faculty's own margin-bottom (scoped to Education above), not
   here; .ca-card__credit only ever renders in Education (buildEduMeta),
   so stacking a second 8px here would double the gap to 16px. */
.ca-card__credit { color: var(--color-text-secondary); margin: 0; display: flex; align-items: center; }

/* Coming Soon (2026-09-25) — a non-clickable variant of any of the three
   card types that use this shared .ca-card shell (Video Updates,
   Education carousel, Project ECHO). The date text itself is swapped to
   "Coming Soon" in the card builders (buildCardDateHtml(), ca-render.js);
   this class just needs to (1) turn the title black instead of link-blue,
   since it's no longer a link, and (2) cancel the shared :hover
   treatments above — .ca-card:hover still fires on mouseover regardless
   of whether the element is actually clickable (no click handler behind
   it doesn't stop :hover from matching), so without this override a
   coming-soon card would still visually invite a click it doesn't honor.
   cursor:default for the same reason. Equal specificity to the base
   .ca-card:hover rules (one class + :hover each) but declared later in
   the cascade, so it correctly wins for cards carrying both classes. */
.ca-card--coming-soon { cursor: default; }
.ca-card--coming-soon .ca-card__title { color: var(--color-text-primary); }
@media (hover: hover) { .ca-card--coming-soon:hover .ca-card__thumb img { transform: none; } }
@media (hover: hover) { .ca-card--coming-soon:hover .ca-card__title { color: var(--color-text-primary); } }
/* Education's featured card reuses this same .ca-card--coming-soon class
   (see renderEducation()/buildFeaturedActivity() — a Coming Soon item can
   still legitimately land in the featured slot when it's the only
   content available) but has its own separate hover rules
   (.ca-edu-featured__link:hover ...) targeting its own thumb/title
   classes, not .ca-card__thumb's — so the two overrides above don't
   reach it. Same cancellation, scoped to the featured wrapper instead. */
@media (hover: hover) { .ca-edu-featured__link.ca-card--coming-soon:hover .ca-edu-featured__thumb img { transform: none; } }
@media (hover: hover) { .ca-edu-featured__link.ca-card--coming-soon:hover .ca-card__title { color: var(--color-text-primary); } }

/* Corner ribbon — a small square wrapper (overflow:hidden) clipping an
   oversized inner band, rotated -45deg. Wrapper W=160px, band width 300px.
   top/left given directly by the user (tuned by hand against the real
   reference until it looked right, after several rounds of this session's
   own math-derived offsets not landing correctly) — treat these two values
   as the source of truth for this rule rather than re-deriving them. */
.ca-ribbon-wrap { position: absolute; top: 0; left: 0; width: 160px; height: 160px; overflow: hidden; z-index: 1; pointer-events: none; }
.ca-ribbon { position: absolute; top: 29px; left: -96px; width: 300px; text-align: center; transform: rotate(-45deg); background: var(--color-negative); color: #FFFFFF; font-family: var(--typography-family-title); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-semibold); padding: 10px 0; line-height: 20px; text-transform: capitalize; }

/* Video Updates-specific overrides — scoped to .ca-card--update rather than
   the shared .ca-card__faculty/.ca-carousel__track rules, since those are
   also used by Education and Project ECHO. Project ECHO hasn't been
   reviewed yet and still uses the shared gray-name / 16px-gap treatment;
   Education has since gotten its own equivalent 24px-gap override below. */
.ca-card--update .ca-card__faculty { color: var(--color-text-primary); }
[data-carousel-track="updates"] { gap: var(--size-space-md); }
/* Education's own 24px carousel gap — see the Video Updates comment above
   for why this is scoped per-track rather than touching the shared
   --carousel-gap token (Project ECHO still uses it at 16px, unreviewed).
   ca-carousel.js reads each track's real gap from its computed style, so
   no JS change was needed to keep 3 cards fitting flush at this width. */
[data-carousel-track="activities"] { gap: var(--size-space-md); }

/* List (search results) modifier — same content, compact horizontal row.
   align-items: flex-start (was center, 2026-09-25) — the thumbnail should
   top-align with its meta content, not sit vertically centered against
   it, per direct spec. */
.ca-card--list { flex-direction: row; gap: var(--size-space-sm); align-items: flex-start; }
.ca-card--list .ca-card__thumb { flex: 0 0 140px; aspect-ratio: 16/9; }
.ca-card--list .ca-card__meta { padding: 0; }
/* The "New" ribbon (.ca-ribbon-wrap) is a fixed 160x160px box hand-sized
   for a full carousel thumbnail — in this compact list layout (search
   results only) the thumb shrinks to 140x78.75px, so the ribbon's own
   overflow:hidden clipping happens at the wrong point and the diagonal
   band cuts off abruptly instead of reading as a clean corner ribbon
   (reported directly via screenshot). Scaled down to match instead of
   re-deriving new pixel constants — same proven geometry, just smaller. */
.ca-card--list .ca-ribbon-wrap { transform: scale(0.5); transform-origin: top left; }

/* ============================================================
   VIDEO UPDATES / EDUCATION carousels — their spec-driven overrides
   ([data-carousel-track="updates"|"activities"], .ca-card--update .ca-card__faculty)
   live up with the shared .ca-card rules above, not here.
   ============================================================ */

/* ============================================================
   STEERING COMMITTEE / FACULTY person cards
   ============================================================ */
/* 24px gap between person blocks, both directions, per direct spec (was
   80px column / 40px row). */
.ca-committee-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-space-md); }
.ca-person { display: flex; gap: var(--size-space-md); }
.ca-person__photo { width: 94px; height: 120px; object-fit: cover; border-radius: var(--size-radius-rounded); flex-shrink: 0; }
.ca-person__info { flex: 1; min-width: 0; }
.ca-person__name { color: var(--color-text-primary); margin: 0; } /* name -> credentials: 0px, per direct spec */
.ca-person__credentials { color: var(--color-text-primary); margin: 0 0 var(--size-space-2xs) 0; } /* renamed from .ca-person__institution (2026-09-25), matching Faculty's own institutionLines->credentials rename */
/* Standardized to the shared .ca-link treatment (2026-09-25) — was its own
   one-off style (static text-decoration:underline, color-only hover),
   out of step with every other text link on the site (ASAM's list links,
   Practice Aids, More Resources, the video modal's Sources list), which
   all share .ca-link's no-default-underline + animated-underline-on-
   hover + color-transition treatment via the shared class. The .ca-link
   class is added directly in the markup (buildPersonCard(), ca-render.js)
   rather than duplicated here; only the button-element resets that
   .ca-link doesn't cover (it's normally used on <a>) stay in this rule. */
.ca-person__bio-link { background: none; border: none; padding: 0; }

/* ============================================================
   EDUCATION
   ============================================================ */
.ca-edu-featured { margin-bottom: var(--size-space-lg); }
.ca-edu-featured__link { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-space-md); text-decoration: none; color: inherit; align-items: stretch; } /* thumb -> details: 24px, per direct spec (was 40px) */
/* Featured card's title hover, and (2026-09-25) its thumbnail zoom — the
   shared .ca-card:hover rules don't reach here, since the featured card
   is a different wrapper (.ca-edu-featured__link, not .ca-card), the same
   gap missed twice now when Video Updates/carousel cards got their hover
   treatment: the title-color fix already accounted for this; the
   thumbnail zoom (.ca-card:hover .ca-card__thumb img { transform:
   scale(1.04) }) was the same class of miss, just not caught until a
   direct screenshot comparison against other cards' hover behavior. */
@media (hover: hover) { .ca-edu-featured__link:hover .ca-card__title { color: var(--color-link-hover); } }
@media (hover: hover) { .ca-edu-featured__link:hover .ca-edu-featured__thumb img { transform: scale(1.04); } }
.ca-edu-featured__thumb { position: relative; border-radius: var(--size-radius-rounded); overflow: hidden; }
.ca-edu-featured__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; min-height: 320px; transition: transform var(--transition-slow); }
/* justify-content: flex-start (was center) — top-align the meta block
   against the thumb, per direct spec. align-items: flex-start is the same
   inline-block-gets-stretched-by-flex fix as the hero badge/committee
   pattern earlier: without it, the tag pill (display:inline-block) was
   being cross-axis-stretched to the column's full width. Title size no
   longer needs a CSS override here — it's set directly via the TitleXLarge
   utility class in buildEduMeta()'s markup now (30px, matching the
   carousel card's own TitleMedium/18px override), per this project's
   convention that utility classes are the source of truth for type sizes. */
.ca-edu-featured__details { display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; }
/* Education-specific spacing/color, scoped rather than touching the
   shared .ca-tag-pill/.ca-card__faculty rules (also used by Project ECHO,
   not yet reviewed): tag -> date 16px (was the shared 8px), faculty color
   black (was the shared gray) with an 8px gap to credit (was 0). */
.ca-edu-featured__details .ca-tag-pill,
.ca-card--activity .ca-tag-pill { margin-bottom: var(--size-space-sm); }
.ca-edu-featured__details .ca-card__faculty,
.ca-card--activity .ca-card__faculty { color: var(--color-text-primary); margin-bottom: var(--size-space-2xs); }

/* ============================================================
   PRACTICE AIDS
   ============================================================ */
.ca-practice-aids { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--size-space-2xl); row-gap: var(--size-space-lg); }
.ca-practice-aids__category { margin-bottom: var(--size-space-lg); } /* 40px between subsections, per direct spec — already matched, unchanged */
.ca-practice-aids__category:last-child { margin-bottom: 0; }
.ca-practice-aids__category-heading { color: var(--color-text-primary); margin: 0 0 var(--size-space-md) 0; } /* subheader -> list: 24px (was 16px) */
.ca-practice-aids .ca-link-list { gap: 4px; } /* was 0px — bumped for breathing room (2026-09-25, direct spec, applied to every link-list site-wide), no spacing token this small */
.ca-practice-aids .ca-link-item { flex-direction: row; }
.ca-practice-aids .ca-link-item::before { content: '\2022'; color: var(--color-link); margin-right: var(--size-space-2xs); }
/* List item: 16px regular (was the shared .ca-link-item .ca-link's semibold)
   — scoped to Practice Aids specifically, since that shared rule also
   drives ASAM/More Resources link lists, unreviewed and still semibold. */
.ca-practice-aids .ca-link-item .ca-link { font-weight: var(--typography-weight-regular); }
/* Practice Aids never shows a description sub-line by design (unlike ASAM/
   More Resources, which deliberately do) — description still exists on
   every Practice Aids item for search matching, it just shouldn't ever
   render. Originally hidden with a CSS override here, but that only
   reached this live section, not a Practice Aids row reused inside the
   search-results modal (a different DOM context entirely) — so a search
   result still leaked the description through (reported directly,
   2026-09-26). Suppressed properly at the source instead: buildLinkItem()
   takes an opts.hideDescription flag, passed by both this section's own
   renderer and ca-search.js's practiceAids category, so neither context
   can regress independently of the other. */

/* ============================================================
   ASAM RESOURCES
   ============================================================ */
/* 40px between header/logo/description/list, per direct spec (a follow-up
   correction — an earlier pass had set these to 0, since superseded).
   Heading -> logo-row is already 40px via the shared .ca-section-heading
   margin-bottom, so logo-row itself only needs a bottom margin, not top. */
.ca-asam__logo-row { margin: 0 0 var(--size-space-lg) 0; }
/* Same full lockup as the hero collaborator logo, larger since this
   section has the full-width column to itself. */
/* 500px on desktop, per direct spec (was 320px) — the global `img {
   max-width:100%}` reset (base.css) already shrinks it on narrow
   viewports, so no separate mobile breakpoint override is needed. */
.ca-asam__logo-row img { width: 500px; height: auto; display: block; }
/* max-width removed — description spans the full section width, per
   direct spec (was capped at 960px). margin-bottom 40px — logo -> desc and
   desc -> list gaps, per the follow-up spec above. */
.ca-asam__description { color: var(--color-text-primary); margin: 0 0 var(--size-space-lg) 0; }
/* Group headings (2026-09-30) — ASAM's list grew from one flat list to
   several categorized ones (item.groupHeading, renderAsam()), same idea as
   Practice Aids' category headings / More Resources' group headings, so
   reuses their exact 20px-bold-black treatment (TitleLarge) rather than a
   new size. 40px between groups matches Practice Aids' own subsection
   spacing; 16px heading->list matches More Resources' group heading. */
.ca-asam__group { margin-bottom: var(--size-space-lg); }
.ca-asam__group:last-child { margin-bottom: 0; }
.ca-asam__group-heading { color: var(--color-text-primary); margin: 0 0 var(--size-space-sm) 0; }
.ca-link-list--asam { gap: 4px; } /* was 0px, bumped for breathing room (2026-09-25) — see .ca-practice-aids .ca-link-list above */
/* Bullet pulled out via absolute positioning into a reserved left padding,
   rather than the item's old flex-row-with-wrap layout — that approach
   needed the description's margin-left to *guess* the bullet+gap's real
   rendered width (a fixed 24px token that didn't actually match it,
   confirmed via pixel measurement: description sat ~7px right of the
   link's own left edge). With both the link and description now simply
   block children inside the same padding-box, they share an exact left
   edge with no guessing. */
.ca-link-list--asam .ca-link-item { display: block; position: relative; padding-left: var(--size-space-sm); }
.ca-link-list--asam .ca-link-item::before { content: '\2022'; color: var(--color-link); position: absolute; left: 0; top: 0; }
.ca-link-list--asam .ca-link-item .ca-link { font-weight: var(--typography-weight-regular); } /* 16px no bold — scoped, Practice Aids/More Resources unaffected */
.ca-link-list--asam .ca-link-item__desc { margin-left: 0; font-size: var(--typography-scale-base); color: var(--color-text-primary); } /* 16px black (was 14px gray) */

/* ============================================================
   MORE RESOURCES accordion
   ============================================================ */
/* Panels sit flush together as one bordered group ("sandwich") — the
   group itself (not each panel) carries the border/radius/overflow-hidden,
   so only the very first and last panel's corners actually round (clipped
   by the group's own shape); middle panels are flat. Panels no longer
   have their own border/radius/margin-bottom — a shared border-bottom
   (skipped on the last panel) draws the single divider line between each,
   instead of doubled-up per-panel borders with gaps. */
.ca-accordion { border: 1px solid var(--color-border-midgray); border-radius: var(--size-radius-rounded); overflow: hidden; } /* 16px corner radius, per direct spec (was 4px) */
.ca-accordion__panel { border-bottom: 1px solid var(--color-border-midgray); background: #FFFFFF; }
.ca-accordion__panel:last-child { border-bottom: none; }
/* Inactive: #FAFDFE background + link-blue text (was white bg). This exact
   value is a one-off, direct from the user — a near-white pale blue, not
   the same as the --color-light-blue token (#E5F1F8, visibly bluer) used
   elsewhere for tag pills etc., so it's a raw hex here rather than a
   reused/renamed token. Active (.is-open): solid link-blue background +
   white text — unchanged, this already matched the mockup. */
.ca-accordion__header { width: 100%; display: flex; align-items: center; justify-content: space-between; padding: var(--size-space-sm) var(--size-space-md); background: #FAFDFE; border: none; cursor: pointer; color: var(--color-link); text-align: left; transition: background-color var(--transition-base); }
/* Hover uses the existing --color-light-blue token (#E5F1F8) — the same
   "light hover" already established elsewhere (.ca-btn--white/--outline)
   — rather than inventing another one-off color. Only for the inactive
   state; an open panel's own solid-blue background doesn't need a hover
   treatment. */
@media (hover: hover) { .ca-accordion__header:hover { background: var(--color-light-blue); } }
.ca-accordion__panel.is-open .ca-accordion__header { background: var(--color-button-primary); color: #FFFFFF; }
@media (hover: hover) { .ca-accordion__panel.is-open .ca-accordion__header:hover { background: var(--color-button-primary); } }
/* Same dropdown-arrow.svg double-chevron as the specialty trigger above —
   no rotation, the header's background color already carries open/closed
   state. */
.ca-accordion__chevron { width: 10px; height: 16px; flex-shrink: 0; background: currentColor; -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; -webkit-mask-image: url('../images/icons/dropdown-arrow.svg'); mask-image: url('../images/icons/dropdown-arrow.svg'); }
/* Animated open/close (2026-09-25) — same max-height + overflow:hidden
   technique already proven for the mobile drawer's accordion panels (see
   CLAUDE.md "Mobile navigation" > Animation for the full history of why
   grid-template-rows: 0fr/1fr was tried and rejected there: it doesn't
   reliably collapse to a true zero height inside auto-height ancestors,
   leaking a sliver of content even while "closed"). Padding moved off
   this element onto a new .ca-accordion__body-inner, same split the
   mobile version already uses — confirmed directly (not assumed) that
   max-height:0 on box-sizing:border-box does NOT force the box's own
   padding down to zero, so a padded max-height:0 element still renders
   at its padding's height (measured 48px, i.e. the two var(--size-
   space-md) top/bottom, when this was tried on this element directly).
   With no padding of its own, this element collapses to a genuine 0.
   1600px is a generous cap, not a measured exact height (same tradeoff
   the mobile version accepts, deliberately, in exchange for needing no
   JS height measurement) — the real content today (the largest panel,
   "Resources for All Clinicians", 3 groups/18 items) measures ~1065px
   via scrollHeight, so this comfortably covers it plus room to grow. */
.ca-accordion__body { max-height: 0; overflow: hidden; transition: max-height var(--transition-base); }
.ca-accordion__panel.is-open .ca-accordion__body { max-height: 1600px; }
.ca-accordion__body-inner { padding: var(--size-space-md); }
.ca-accordion__group { margin-bottom: var(--size-space-lg); }
.ca-accordion__group:last-child { margin-bottom: 0; }
.ca-accordion__group-heading { color: var(--color-text-primary); margin: 0 0 var(--size-space-sm) 0; } /* 20px bold black, via TitleLarge class */
/* Optional italic intro line above a group's list (e.g. Clinical Trials'
   "the following are actively recruiting..." disclaimer) — only rendered
   when a group's JSON sets `note`, per real source content from the
   user's own PDF, not present on every group. */
.ca-accordion__group-note { font-style: italic; color: var(--color-text-primary); margin: 0 0 var(--size-space-sm) 0; } /* 16px black, via BodyBase class (was BodySmall/14px gray) */
/* Same list treatment as ASAM (see "ASAM Resources" above): tight (4px
   gap, was 0px — see .ca-practice-aids .ca-link-list above) items, 16px
   regular link text, 16px black description, bullet absolutely
   positioned into a reserved padding so the link and description share
   one exact left edge instead of a guessed margin. */
.ca-accordion__group .ca-link-list { gap: 4px; }
.ca-accordion__group .ca-link-item { display: block; position: relative; padding-left: var(--size-space-sm); }
.ca-accordion__group .ca-link-item::before { content: '\2022'; color: var(--color-link); position: absolute; left: 0; top: 0; }
.ca-accordion__group .ca-link-item .ca-link { font-weight: var(--typography-weight-regular); }
.ca-accordion__group .ca-link-item__desc { margin-left: 0; font-size: var(--typography-scale-base); color: var(--color-text-primary); }

/* ============================================================
   PROJECT ECHO
   ============================================================ */
/* 40px between header/logo/description/carousel, per direct spec — same
   pattern as ASAM: heading -> logo is already 40px via the shared
   .ca-section-heading margin-bottom, so logo-row only needs its own
   bottom margin (was 24px top+bottom). Description's own 40px
   margin-bottom already covered desc -> carousel, no change needed there. */
.ca-echo__logo-row { margin: 0 0 var(--size-space-lg) 0; }
.ca-echo__logo-row img { width: 200px; height: auto; }
/* max-width removed — full section width, per direct spec (was 1100px). */
.ca-echo__description { color: var(--color-text-primary); margin: 0 0 var(--size-space-lg) 0; }
/* Carousel card — bring up to the same treatment Video Updates/Education
   already got: 16px tag->date gap (was the shared 8px) and black faculty
   (was gray) — matching Education's carousel card specifically, since
   ECHO's card shape (tag + date + title + faculty) is the same one. */
.ca-card--echo .ca-tag-pill { margin-bottom: var(--size-space-sm); }
.ca-card--echo .ca-card__faculty { color: var(--color-text-primary); }
[data-carousel-track="echo"] { gap: var(--size-space-md); } /* 24px carousel gap, same scoped pattern as Video Updates/Education */

/* ============================================================
   FACULTY — full-bleed auto-scroll carousel
   ============================================================ */
.ca-faculty { position: relative; overflow: hidden; padding: var(--size-space-2xl) 0; background: linear-gradient(180deg, var(--color-brand) 0%, var(--color-navy) 100%); }
.ca-faculty__heading { text-align: center; color: #FFFFFF; margin: 0 0 var(--size-space-lg) 0; }
.ca-faculty-carousel { overflow: hidden; }
.ca-faculty-carousel__track { display: flex; gap: 32px; width: max-content; animation: ca-faculty-scroll var(--faculty-duration, 90s) linear infinite; }
@keyframes ca-faculty-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (hover: hover) { .ca-faculty-carousel:hover .ca-faculty-carousel__track { animation-play-state: paused; } }
@media (prefers-reduced-motion: reduce) { .ca-faculty-carousel__track { animation: none; } }

.ca-faculty-card { flex: 0 0 350px; border-radius: var(--size-radius-rounded); overflow: hidden; background: #FFFFFF; text-align: center; } /* was 260px, per direct spec */
.ca-faculty-card__band { position: relative; height: 60px; background: var(--color-mid-blue); }
.ca-faculty-card__band img { position: absolute; left: 50%; bottom: -50px; transform: translateX(-50%); width: 100px; height: 100px; border-radius: 50%; object-fit: cover; border: 4px solid #FFFFFF; }
.ca-faculty-card__name { margin: 60px var(--size-space-sm) 0 var(--size-space-sm); color: var(--color-text-primary); } /* 16px bold black, via TitleBase class; 0px to credentials, per direct spec (was 8px) */
.ca-faculty-card__credentials { margin: 0 var(--size-space-sm) var(--size-space-md) var(--size-space-sm); color: var(--color-text-primary); } /* 14px black — renamed from .ca-faculty-card__institution now that Faculty is derived from activities.json's faculty[] (name+credentials+photo), not a separate roster with institutionLines */

/* ============================================================
   PODCAST
   ============================================================ */
/* padding-bottom: 0 — the episodes panel matching the LEFT column's own
   height (via grid stretch, below) isn't enough on its own to reach the
   section's literal bottom edge, since the section's own standard 80px
   bottom padding still sits beneath the whole grid either way. Direct
   spec ("goes all the way to the bottom of the section with no padding
   under") means the section boundary itself, not just column-to-column
   parity — confirmed by a follow-up screenshot showing a large gap below
   the panel that an earlier pass's height-matching alone didn't close.
   podcast_bg.webp (a soft, near-white decorative photo) also lives here now
   — moved up from .ca-podcast (the inner grid/container) per direct spec,
   so "top left" resolves against the section's own box rather than the
   centered, inset .container beneath it. */
#podcasts {
  position: relative;
  padding-bottom: 0;
  background-image: url('../images/podcast/podcast_bg.webp');
  background-repeat: no-repeat;
  background-position: top left;
  background-size: auto;
}
/* align-items: stretch (was start) — makes both grid columns match the
   taller one's height, which is what lets the episodes panel's own
   height:100% (below) reach flush to the section's bottom edge instead of
   stopping wherever its own content happened to end, per direct spec
   ("the episode tray goes all the way to the bottom of the section with
   no padding under"). */
.ca-podcast {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--size-space-2xl);
  align-items: stretch;
  position: relative;
}
.ca-podcast__heading { margin: 0 0 var(--size-space-lg) 0; }
.ca-podcast__now-playing-label { color: var(--color-link); margin: 0 0 var(--size-space-sm) 0; }
.ca-podcast__now-playing { position: relative; z-index: 1; display: flex; flex-direction: column; }
/* Content sits above the background photo, not blended into it. No
   background/padding/radius here — the mockup has no visible "card"
   boundary, content just flows on the section's own background. */
.ca-podcast__now-playing-card { position: relative; z-index: 1; }
.ca-podcast__np-date { color: var(--color-text-secondary); margin: 0 0 4px 0; } /* 4px — no spacing token this small */
.ca-podcast__np-title { color: var(--color-text-primary); margin: 0 0 var(--size-space-2xs) 0; } /* 8px */
.ca-podcast__np-faculty { color: var(--color-text-primary); margin: 0 0 var(--size-space-md) 0; } /* 24px */
.ca-podcast__transport { display: flex; align-items: center; gap: var(--size-space-2xs); } /* 8px between play/time/scrubber/time, per direct spec (was 16px). 40px before platform CTAs is already supplied by .ca-podcast__platforms' own margin-top below — was already correct, not duplicated here */
.ca-podcast__play { width: 46px; height: 46px; border-radius: 50%; border: none; background: var(--color-button-primary); cursor: pointer; flex-shrink: 0; display: flex; align-items: center; justify-content: center; transition: background var(--transition-base); }
@media (hover: hover) { .ca-podcast__play:hover:not(:disabled) { background: var(--color-link-hover); } }
.ca-podcast__play:disabled { opacity: 0.5; cursor: default; }
/* mask-image, white fill (was a plain <img> with a fill="black" source —
   a real bug: a black triangle on a blue circle read as nearly invisible,
   confirmed against the mockup which shows it clearly white). */
.ca-podcast__play-icon { display: block; width: 16px; height: 18px; background-color: #FFFFFF; -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; }
.ca-podcast__play-icon--play { -webkit-mask-image: url('../images/icons/play.svg'); mask-image: url('../images/icons/play.svg'); }
.ca-podcast__play-icon--pause { -webkit-mask-image: url('../images/icons/pause.svg'); mask-image: url('../images/icons/pause.svg'); width: 14px; }
.ca-podcast__scrubber { flex: 1; height: 4px; accent-color: var(--color-button-primary); cursor: pointer; }
.ca-podcast__time { color: var(--color-text-secondary); min-width: 40px; font-size: var(--typography-scale-xs); }

/* Platform CTAs — corrected after directly pixel-sampling the mockup,
   which showed something different from an earlier "colored circle +
   white icon" badge design: there's no circle at all. Each icon is just
   rendered in its own brand color, directly on the white button
   background — confirmed by scanning the mockup's actual pixels around
   each icon (no solid color fill anywhere near the size of a circle, only
   the icon's own linework colored). Border-radius is a small 4px (was a
   full pill) and text is black/gray, not blue. */
.ca-podcast__platforms { display: flex; gap: var(--size-space-sm); margin-top: var(--size-space-lg); flex-wrap: wrap; }
.ca-podcast__platform-btn { display: flex; align-items: center; gap: var(--size-space-2xs); border: 1px solid var(--color-border-midgray); border-radius: var(--size-radius-minimal); padding: var(--size-space-2xs) var(--size-space-sm); text-decoration: none; color: var(--color-text-secondary); font-family: var(--typography-family-body); font-size: var(--typography-scale-xs); transition: all var(--transition-base); }
@media (hover: hover) { .ca-podcast__platform-btn:hover { background: var(--color-surface-primary); border-color: var(--color-text-secondary); } }
.ca-podcast__platform-btn strong { font-family: var(--typography-family-title); font-size: var(--typography-scale-sm); color: var(--color-text-primary); }
.ca-podcast__platform-icon { display: block; flex-shrink: 0; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-position: center; mask-position: center; -webkit-mask-size: contain; mask-size: contain; }
.ca-podcast__platform-icon--apple { width: 21px; height: 24px; background-color: #B54DEF; -webkit-mask-image: url('../images/icons/apple.svg'); mask-image: url('../images/icons/apple.svg'); }
.ca-podcast__platform-icon--spotify { width: 24px; height: 24px; background-color: #1DB954; -webkit-mask-image: url('../images/icons/spotify.svg'); mask-image: url('../images/icons/spotify.svg'); }
/* TuneIn: real (recolored) icon image, capped by height like the other
   non-square real icon files elsewhere on the site (see CLAUDE.md
   "Icons") so it doesn't stretch. */
.ca-podcast__platform-icon--plain { width: auto; height: 26px; }

/* align-self: start (2026-09-25, replaces the earlier height:100% +
   JS height-sync approach) — this panel now sizes to its own natural
   content (header + the list's own max-height cap below) instead of
   being grid-stretched to match the left column and then measured back
   via JS. The old approach (a ResizeObserver reading the left column's
   rendered height and applying it as this panel's inline height) broke
   down the moment the episode list's own natural content grew taller
   than the left column: grid's align-items:stretch pulls the SHORTER
   side up to match the TALLER one, so once the list became the taller
   side, it was stretching the left column to match the list instead of
   the other way around — and the JS then measured that inflated,
   list-driven height and fed it right back onto the panel, so the "cap"
   grew in lockstep with the very thing it was supposed to be capping
   (confirmed directly: 20 episodes produced a 1922px cap that barely
   exceeded the list's own 1942px content height, instead of the fixed
   left-column-driven cap this was meant to enforce). A plain CSS
   max-height on the list (below) doesn't have this feedback-loop
   failure mode, and align-self:start here means this panel no longer
   participates in the grid's stretch behavior at all — simpler and
   robust regardless of episode count, at the cost of the two columns no
   longer being guaranteed pixel-flush at the bottom on every screen
   (an accepted tradeoff for dropping the fragile JS). */
/* Border is a plain light gray (was --color-link/blue) — confirmed against
   the mockup: the panel's own edge is --color-border-midgray, not blue. */
/* Bottom corners square (was rounded on all 4) — matches the section's
   own bottom edge treatment (see #podcasts padding-bottom:0 above). */
.ca-podcast__episodes { align-self: start; display: flex; flex-direction: column; border: 1px solid var(--color-border-midgray); border-radius: var(--size-radius-rounded) var(--size-radius-rounded) 0 0; overflow: hidden; }
/* --color-brand (#0077BE), not --color-button-primary (#009FE3) — the
   mockup's header is a darker, more saturated blue than the lighter link
   blue used elsewhere; confirmed by sampling the actual mockup pixels. */
.ca-podcast__episodes-header { background: var(--color-brand); color: #FFFFFF; padding: var(--size-space-sm) var(--size-space-md); flex-shrink: 0; }
/* max-height + overflow-y:auto — a real, fixed cap (was an unbounded
   flex:1 relying on the JS height-sync above to ever actually engage
   scrolling; see the .ca-podcast__episodes comment for why that broke).
   455px is a direct spec value, comfortably shows ~4-5 episodes before
   scrolling kicks in.
   min-height:0 stays even though max-height alone is normally enough to
   cap a flex item — belt-and-suspenders against the known flex
   min-height:auto gotcha (flex items default to never shrinking below
   their own content's natural height, which can fight overflow:auto in
   some browsers/layouts) that this exact list already hit once before. */
.ca-podcast__episodes-list { flex: 1; min-height: 0; max-height: 455px; overflow-y: auto; }
/* Custom scrollbar — thin and quiet rather than the OS default's full
   width, so it reads as part of the design instead of a browser chrome
   afterthought. scrollbar-width/scrollbar-color cover Firefox; the
   ::-webkit-scrollbar-* rules cover Chrome/Safari/Edge. Neutral gray
   (--color-border-midgray), matching the panel's own border color,
   darkening on hover for a little feedback without introducing a new
   one-off color. */
.ca-podcast__episodes-list { scrollbar-width: thin; scrollbar-color: var(--color-border-midgray) transparent; }
.ca-podcast__episodes-list::-webkit-scrollbar { width: 6px; }
.ca-podcast__episodes-list::-webkit-scrollbar-track { background: transparent; }
.ca-podcast__episodes-list::-webkit-scrollbar-thumb { background: var(--color-border-midgray); border-radius: var(--size-radius-full); }
@media (hover: hover) { .ca-podcast__episodes-list::-webkit-scrollbar-thumb:hover { background: var(--color-text-secondary); } }
.ca-podcast__episode { padding: var(--size-space-sm) var(--size-space-md); cursor: pointer; transition: background var(--transition-fast); }
@media (hover: hover) { .ca-podcast__episode:hover { background: var(--color-surface-primary); } }
.ca-podcast__episode.is-active { background: var(--color-light-blue); }
.ca-podcast__episode-date { color: var(--color-text-secondary); margin: 0 0 4px 0; } /* 4px — no spacing token this small */
.ca-podcast__episode-title { color: var(--color-link); margin: 0 0 4px 0; } /* 4px */
.ca-podcast__episode.is-active .ca-podcast__episode-title { color: var(--color-navy); }
.ca-podcast__episode-faculty { color: var(--color-text-primary); margin: 0; }

/* ============================================================
   FOOTER
   ============================================================ */
.ca-footer { background: linear-gradient(90deg, var(--color-brand) 0%, var(--color-navy) 100%); padding: var(--size-space-2xl) 0; }
.ca-footer__inner { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--size-space-lg); flex-wrap: wrap; }
/* align-items: flex-start — same "flex default stretch" bug fixed
   elsewhere this session (hero badge, committee tag pill): without it,
   the logo <img> (width:auto) was being cross-axis-stretched to match
   the container's own width, which is wider than the logo's own natural
   size — the wordmark rendered shifted right/"centered" relative to the
   icon row below it rather than flush left, confirmed by both boxes'
   widths being identical (247.25px) when they shouldn't be. */
.ca-footer__brand { display: flex; flex-direction: column; align-items: flex-start; gap: var(--size-space-sm); }
.ca-footer__logo { height: 30px; width: auto; }
.ca-footer__social { display: flex; gap: var(--size-space-2xs); }
.ca-footer__social-icon { width: 42px; height: 42px; border-radius: 50%; background: #FFFFFF; display: flex; align-items: center; justify-content: center; transition: transform var(--transition-fast); }
@media (hover: hover) { .ca-footer__social-icon:hover { transform: scale(1.08); } }
/* mask-image, blue fill (was a plain <img>, black by default — the
   mockup shows these blue, confirmed by sampling its actual pixels).
   mask-size:contain fits each glyph's own aspect ratio (facebook's "f" is
   narrow, youtube's play-badge is taller, etc.) within one fixed box
   without stretching, same effect as the old cap-by-height approach but
   without needing per-icon dimensions. */
.ca-footer__social-icon-glyph { display: block; width: 20px; height: 20px; background-color: var(--color-link); -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-position: center; mask-position: center; -webkit-mask-size: contain; mask-size: contain; }
.ca-footer__copyright { color: rgba(255,255,255,0.85); margin: 0; } /* 16px via BodyBase class in index.html (was BodySmall/14px) */
.ca-footer__links { display: flex; align-items: center; gap: var(--size-space-2xs); flex-wrap: wrap; margin-top: var(--size-space-md); }
.ca-footer__links a { color: #FFFFFF; text-decoration: underline; font-family: var(--typography-family-body); font-size: var(--typography-scale-base); } /* 16px, was 14px — legal links are JS-generated with no utility class in the markup to swap, so this stays a direct token reference like the rest of this file's link styling */
.ca-footer__links-sep { color: rgba(255,255,255,0.6); }

/* ============================================================
   MODAL
   ============================================================ */
body.ca-scroll-lock { overflow: hidden; }
.ca-modal-overlay { position: fixed; inset: 0; z-index: 500; background: rgba(22,26,46,0.6); display: none; align-items: center; justify-content: center; padding: var(--size-space-md); }
.ca-modal-overlay.is-open { display: flex; }
.ca-modal { position: relative; background: #FFFFFF; border-radius: var(--size-radius-rounded); max-width: 720px; width: 100%; max-height: 88vh; overflow-y: auto; padding: var(--size-space-lg); }
/* No circle/background (was a gray filled circle) — plain blue "X", per
   the reference screenshot. Covers every modal (bio, video, search
   results, share) since they all share this one close button markup.
   Sized/inset so `inset + size` never exceeds .ca-modal's own 40px
   padding (var(--size-space-lg)) — the previous 24px inset + 28px size
   summed to 52px, 12px past the content box's top-right corner, so the
   button visually overlapped whatever content started there (e.g. the
   video player's own corner). 12px inset + 24px size = 36px, a 4px
   buffer inside the padding gutter — button and content never overlap,
   at any modal size, including once extra content (sources list, etc.)
   is added below. */
.ca-modal__close { position: absolute; top: var(--size-space-xs); right: var(--size-space-xs); width: 24px; height: 24px; border-radius: var(--size-radius-minimal); border: none; background: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; transition: opacity var(--transition-fast); z-index: 1; }
@media (hover: hover) { .ca-modal__close:hover { opacity: 0.7; } }
/* close.svg (user-supplied) — replaces the earlier x.svg reuse of the
   share modal's network-icon glyph with a dedicated close icon. */
.ca-modal__close-icon { display: block; width: 16px; height: 16px; background-color: var(--color-link); -webkit-mask: no-repeat center / contain; mask: no-repeat center / contain; -webkit-mask-image: url('../images/icons/close.svg'); mask-image: url('../images/icons/close.svg'); }

.ca-bio-modal { display: flex; gap: var(--size-space-lg); flex-wrap: wrap; }
.ca-bio-modal__photo { width: 140px; height: 180px; object-fit: cover; border-radius: var(--size-radius-rounded); flex-shrink: 0; }
.ca-bio-modal__body { flex: 1; min-width: 240px; }
.ca-bio-modal__name { margin: 0 0 var(--size-space-2xs) 0; }
.ca-bio-modal__credentials { color: var(--color-text-secondary); margin: 0 0 var(--size-space-sm) 0; } /* renamed from .ca-bio-modal__institution (2026-09-25) */
.ca-bio-modal__bio { color: var(--color-text-primary); margin: 0; }

.ca-video-modal__player { border-radius: var(--size-radius-rounded); overflow: hidden; margin-bottom: var(--size-space-sm); background: #000; }
.ca-video-modal__player video { width: 100%; display: block; }
/* JW Player iframe embed — the 56.25%-padding-bottom aspect-ratio box
   trick (JWP's own recommended markup), since an iframe has no
   intrinsic size for the browser to lay out against the way a <video>
   does. Kept as a modifier on the same .ca-video-modal__player (still
   supplies the rounded corners/overflow-clip/black backdrop) rather
   than a separate class, so both the JWP and native-<video> paths share
   one visual shell. */
.ca-video-modal__player--iframe { position: relative; padding-bottom: 56.25%; height: 0; }
.ca-video-modal__player--iframe iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; display: block; }
.ca-video-modal__date { color: var(--color-text-secondary); margin: 0 0 var(--size-space-2xs) 0; }
.ca-video-modal__title { margin: 0 0 var(--size-space-2xs) 0; }
.ca-video-modal__faculty { color: var(--color-text-primary); margin: 0; } /* 14px black regular, was gray semibold (BodySmall class swap in ca-modal.js) */
/* Optional sources list appended below the faculty line — same visual
   treatment as the ASAM Resources list (16px regular black link,
   bulleted, 0px gap between rows) via the shared .ca-link-list--asam
   modifier, so it reads as the same "list of sources" pattern used
   elsewhere rather than a one-off. */
.ca-video-modal__sources { margin-top: var(--size-space-md); padding-top: var(--size-space-md); border-top: 1px solid var(--color-border-midgray); }
.ca-video-modal__sources-heading { margin: 0 0 var(--size-space-sm) 0; }

.ca-search-results__heading { display: flex; align-items: baseline; gap: var(--size-space-2xs); flex-wrap: wrap; margin: 0 0 var(--size-space-md) 0; }
.ca-search-results__count { color: var(--color-text-secondary); font-weight: var(--typography-weight-regular); }
.ca-search-results__group { margin-bottom: var(--size-space-lg); }
.ca-search-results__group-heading { display: flex; align-items: baseline; gap: var(--size-space-2xs); margin: 0 0 var(--size-space-sm) 0; color: var(--color-text-primary); }
.ca-search-results__group-count { color: var(--color-text-secondary); }
.ca-search-results__rows { display: flex; flex-direction: column; gap: var(--size-space-md); } /* 24px, was 16px, per direct spec */
/* Each row wraps the category's real card/list-item markup plus an
   optional SERP-style description snippet underneath (2026-09-25) — see
   ca-search.js's renderResults()/buildRow(). A small, tight gap between
   the card and its own snippet — the 24px above is between *rows*, this
   is within one. */
.ca-search-results__row { display: flex; flex-direction: column; gap: var(--size-space-2xs); }
.ca-search-results__snippet { color: var(--color-text-secondary); }
.ca-search-results__empty { color: var(--color-text-secondary); }

/* Ported from PeerVoice homesite's share-modal__* (2x2 network grid + copy
   row) — same structure, recolored from teal to this brand's blue/navy. */
.ca-share-modal__heading { margin: 0 0 var(--size-space-md) 0; color: var(--color-navy); }
.ca-share-modal__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-space-xs); margin-bottom: var(--size-space-sm); }
.ca-share-modal__btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--size-space-2xs);
  padding: var(--size-space-md) var(--size-space-sm);
  background: var(--color-button-primary);
  border: none;
  border-radius: var(--size-radius-minimal);
  color: #FFFFFF;
  font-family: inherit;
  cursor: pointer;
  width: 100%;
  transition: background var(--transition-base), transform var(--transition-base);
}
@media (hover: hover) { .ca-share-modal__btn:hover { background: var(--color-link-hover); transform: scale(1.03); } }
.ca-share-modal__icon { width: 28px; height: 28px; flex-shrink: 0; }
.ca-share-modal__label { font-family: var(--typography-family-body); font-size: var(--typography-scale-xs); font-weight: var(--typography-weight-semibold); letter-spacing: 0.03em; }

.ca-share-modal__copy-row { display: flex; gap: 0; border: 1.5px solid var(--color-link); border-radius: var(--size-radius-minimal); overflow: hidden; }
.ca-share-modal__input { flex: 1; padding: var(--size-space-xs) var(--size-space-sm); border: none; outline: none; font-family: var(--typography-family-body); font-size: var(--typography-scale-sm); color: var(--color-text-secondary); background: #FFFFFF; min-width: 0; }
.ca-share-modal__copy-btn { padding: var(--size-space-xs) var(--size-space-md); background: var(--color-button-primary); color: #FFFFFF; font-family: var(--typography-family-title); font-size: var(--typography-scale-sm); font-weight: var(--typography-weight-semibold); border: none; cursor: pointer; transition: background var(--transition-base); flex-shrink: 0; }
@media (hover: hover) { .ca-share-modal__copy-btn:hover { background: var(--color-link-hover); } }

/* ============================================================
   RESPONSIVE — first draft (CLAUDE.md section 7: polish together)
   ============================================================ */
@media (max-width: 1279px) {
  .ca-hero__wordmark { font-size: 56px; }
  .ca-edu-featured__link { grid-template-columns: 1fr; }
  .ca-edu-featured__thumb img { min-height: 260px; }
  .ca-podcast { grid-template-columns: 1fr; }
}

@media (max-width: 1023px) {
  .ca-committee-grid { grid-template-columns: 1fr; }
  .ca-practice-aids { grid-template-columns: 1fr; }
  .ca-faculty-card { flex: 0 0 220px; }
}

/* Mobile hamburger switch-over moved up to 1120px (was 767px) — the
   desktop items row (search+specialty+share) was cramped/wrapping in the
   ~768-1120px tablet range. See CLAUDE.md "mobile breakpoint". Below this
   width the sub-nav bar disappears entirely — its links move into the
   drawer's "Page Navigation" accordion instead (CANav.buildSubnav()
   renders both from the same manifest). */
@media (max-width: 1120px) {
  .ca-topbar__items { display: none; }
  .ca-topbar__hamburger { display: flex; align-items: center; justify-content: center; }
  .ca-subnav { display: none; }

  /* White, not navy like the top bar it drops from — deliberate switch:
     it lets every row below reuse the SAME light-mode styling the desktop
     specialty dropdown and sub-nav already use (no parallel dark-mode
     color overrides to maintain), and matches the rest of the page's
     content, which is white throughout. Rows run edge-to-edge (no padding
     on the drawer itself) with padding inside each row instead, and a
     single hairline divider per row — the previous version had a border
     on both edges of each accordion item plus a separate bordered "card"
     around Share, which read as too busy.

     position:absolute (off .ca-topbar, which is already position:sticky
     and so a valid containing block) instead of being in normal flow —
     the drawer used to be an in-flow sibling inside .ca-topbar, so
     opening it grew .ca-topbar's own height and pushed the sub-nav/page
     content down with it. Absolute positioning floats it over the page
     instead. top:100% lands it exactly at the header's own 78px height.

     Open/close reveals like a shade unrolling down from under the bar —
     the whole white panel (background + shadow + content together), not
     just the text inside an already-fully-sized box. Tried a translateY
     approach here first (slide the inner content while the outer just
     clipped via overflow:hidden) and it looked wrong for exactly that
     reason: the outer's own background/shadow occupy its full auto-height
     immediately once `visibility` flips, since transform doesn't affect
     layout — so the box appeared instantly at full size and only the text
     inside it visibly slid, reading as "empty box appears, content rolls
     in" rather than one thing unrolling. Animating `max-height` on the
     OUTER element itself (same technique already used for the accordion
     panels below) fixes that — the container's visible height, and
     therefore its background/shadow, grows in step with reveals rather
     than existing at full size from the first frame.

     900px cap: generous enough that it won't clip the drawer even with
     its largest accordion (Page Navigation, ~8 links) expanded — collapsed
     rows (~268px) + that panel expanded (~450px) is safely under it. This
     trades a bit of animation-easing precision for that guarantee (the
     same tradeoff already made for the accordion panels' own 800px cap,
     for the same reason: max-height eases toward a fixed number, not the
     real content height, so a generous cap makes the reveal feel a touch
     quicker than the full transition duration on lighter content — worth
     it over ever clipping real content). Not a JS-measured exact value on
     purpose — this stays correct without needing to know actual content
     height, the same property `max-height` gets right for the accordion
     panels.

     This is NOT the CSS grid-template-rows 0fr->1fr technique, deliberately
     — an earlier version of this drawer used it, nested two levels deep
     with the accordion panels' own height animation, and that caused a
     real bug: opening an accordion after the drawer had already finished
     opening clipped the accordion's content to a stale height, because
     the outer grid's 1fr track doesn't reliably re-measure once the inner
     grid's content changes size out from under it. max-height's simple
     clamp (not a live proportional distribution) doesn't have that
     interaction.

     `visibility` is included in the transition so it isn't
     focusable/clickable while closed — the browser flips a transitioned
     `visibility` to "visible" immediately on open and to "hidden" only
     at the end of the close transition, so it stays visible throughout
     the closing animation. */
  .ca-topbar__overlay {
    /* Overrides the base (desktop) `display: none` — visibility here is
       handled by the visibility/pointer-events/max-height/overflow-clip
       below, not display, so this must stay non-none at every state
       within this breakpoint or nothing can ever become visible
       regardless of the .is-open class (this was a real bug: the base
       rule was still winning because nothing in this block overrode
       `display` anymore once the animation technique changed — see
       CLAUDE.md). */
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 250;
    overflow: hidden;
    max-height: 0;
    background: #FFFFFF;
    box-shadow: 0 12px 24px rgba(0,0,0,0.15);
    visibility: hidden;
    pointer-events: none;
    transition: max-height var(--transition-slow), visibility var(--transition-slow);
  }
  .ca-topbar__overlay.is-open { max-height: 900px; visibility: visible; pointer-events: auto; }
  .ca-topbar__overlay-inner { display: flex; flex-direction: column; }

  /* Padding + divider live on this wrapper; the form inside is untouched
     from its desktop styling (pill input, round submit button) — see the
     HTML comment above it for why padding can't live on the form itself. */
  .ca-topbar__search-row--overlay { padding: var(--size-space-md); border-bottom: 1px solid var(--color-border-midgray); }
  .ca-topbar__search--overlay { width: 100%; }
  .ca-topbar__search--overlay input { width: 100%; border: 1px solid var(--color-border-midgray); }

  /* Filter by Specialty / Page Navigation triggers and the Share button
     all read as one consistent row style now — no separate bordered box
     around Share, no double borders around the accordion items. Text and
     icons use the link color, same as any other clickable row on the
     site. NOTE: the border-bottom rule below MUST come after this one —
     it targets .ca-topbar__share--overlay too, and this rule's `border:
     none` shorthand would otherwise wipe out that border-bottom if it
     came first (this happened once already; equal-specificity, later
     rule in the cascade wins per property, and `border: none` touches
     border-bottom-* too even though it isn't spelled out). */
  .ca-mobile-accordion-trigger,
  .ca-topbar__share--overlay {
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--size-space-xs);
    background: none;
    border: none;
    padding: var(--size-space-sm) var(--size-space-md);
    color: var(--color-link);
    font-family: var(--typography-family-body);
    font-size: var(--typography-scale-base);
    font-weight: var(--typography-weight-regular);
    cursor: pointer;
    text-align: left;
  }
  /* Trigger rows split label/chevron to opposite ends; Share's icon+label
     are one unit and stay together at the left (was space-between on
     both, which pushed Share's icon and label to opposite edges). */
  .ca-mobile-accordion-trigger { justify-content: space-between; }
  .ca-topbar__share--overlay { justify-content: flex-start; }

  .ca-mobile-accordion-trigger .ca-topbar__specialty-chevron,
  .ca-topbar__share--overlay .ca-icon-share { background: var(--color-link); }
  /* Groups the badge + chevron together on the trigger's right edge, so
     justify-content:space-between puts the label on the left and this
     pair on the right rather than spreading all three apart evenly. Same
     idea on the left — icon + label are one unit, not two more items for
     space-between to spread apart (Filter has 4 children total without
     this: icon, label, badge, chevron — grouping into __left/__right
     keeps it to the 2 groups space-between actually wants). */
  .ca-mobile-accordion-trigger__left,
  .ca-mobile-accordion-trigger__right { display: inline-flex; align-items: center; gap: var(--size-space-xs); }
  .ca-icon-filter, .ca-icon-page-nav {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    display: inline-block;
    background: var(--color-link);
    -webkit-mask: no-repeat center / contain;
    mask: no-repeat center / contain;
  }
  .ca-icon-filter { -webkit-mask-image: url('../images/icons/filter.svg'); mask-image: url('../images/icons/filter.svg'); }
  .ca-icon-page-nav { -webkit-mask-image: url('../images/icons/page-nav.svg'); mask-image: url('../images/icons/page-nav.svg'); }
  /* max-height + overflow:hidden, NOT the grid-template-rows 0fr->1fr
     trick — that trick was tried here first and genuinely didn't work:
     it needs a *definite* available height to proportion 0fr/1fr against,
     and this grid sits inside auto-height ancestors the whole way up (the
     accordion item, the drawer's inner wrapper, the drawer itself), so
     0fr wasn't reliably collapsing to a true zero height — content
     visibly peeked through even while "closed" (confirmed in a real
     browser via Playwright, not just guessed at). max-height is a known,
     simpler technique for exactly this situation: less perfectly eased
     (it ramps toward a fixed cap rather than the exact content height),
     but it can't have this failure mode. 800px comfortably covers today's
     content (the largest is Page Navigation's ~8 links) with real margin
     for both lists to grow. Sole animated element now — dropped the
     `-inner` wrapper's role in the animation; it still exists for the
     indent below. padding-left is deliberately more than the trigger's
     own (--size-space-md + --size-space-sm) — the extra indent is what
     visually reads as "belongs to the trigger above it" rather than
     sitting flush with it. */
  .ca-mobile-accordion-panel { max-height: 0; overflow: hidden; transition: max-height var(--transition-base); }
  .ca-mobile-accordion-item.is-open .ca-mobile-accordion-panel { max-height: 800px; }
  .ca-mobile-accordion-panel__inner { padding: 0 var(--size-space-md) var(--size-space-md) calc(var(--size-space-md) + var(--size-space-sm)); }
  /* No color overrides needed for the option list here anymore — it's the
     same white background as the desktop dropdown, so the default
     .ca-specialty-menu__option-label / __empty colors already work. */

  /* One divider per block, on the outer container rather than the
     trigger/panel individually — that's what keeps it to exactly one line
     whether the accordion is open (line lands after the panel) or closed
     (line lands right under the trigger), instead of a line at every
     internal seam. Share isn't an accordion (no panel state), so it just
     carries its own — see the comment above for why this rule has to
     stay AFTER the `border: none` rule above it. */
  .ca-mobile-accordion-item,
  .ca-topbar__share--overlay { border-bottom: 1px solid var(--color-border-midgray); }

  .ca-mobile-nav-link { display: block; padding: var(--size-space-sm) 0; color: var(--color-link); font-family: var(--typography-family-body); font-size: var(--typography-scale-base); font-weight: var(--typography-weight-regular); text-decoration: none; }
}

@media (max-width: 767px) {
  /* Real bug, reported directly (2026-09-28): on mobile these three
     carousels (Video Updates/Education/Project ECHO) show one card at a
     time — card 2+ starts off-screen, moved there by the carousel's own
     translateX paging, not by page scroll. The entrance-animation
     IntersectionObserver (ca-animations.js) doesn't reliably pick up a
     card crossing into view purely from that transform-driven paging
     (it's built around the page's own scroll, not a sibling carousel's
     independent transform), so a paged-to card could stay stuck at
     opacity:0 indefinitely instead of ever fading in. Simplest, most
     robust fix at this breakpoint: skip the per-card animation
     entirely, card just renders at rest immediately — the same "always
     visible, no animation" treatment prefers-reduced-motion already
     gets, just scoped to mobile carousel cards specifically instead of
     site-wide. Desktop is unaffected (there this same class of card is
     still simultaneously in view within the viewport at up to 3-up, so
     the observer never has this problem). Steering Committee's grid is
     deliberately NOT included here — it's a static grid, not a sliding
     carousel, so its cards were never exposed to this bug in the first
     place. */
  [data-carousel-track="updates"] .animate-on-enter,
  [data-carousel-track="activities"] .animate-on-enter,
  [data-carousel-track="echo"] .animate-on-enter {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* Hierarchy rebalance (2026-09-28), direct feedback against a phone
     screenshot: the wordmark shrinks faster across breakpoints than
     everything set in TitleXLarge (hero tagline, every section heading,
     the hero-slide card title) — which never shrinks at all on its own —
     so by ~480px the two read at nearly the same size and the brand name
     stops reading as the dominant element. Bumped the wordmark up and
     added a mobile-only TitleXLarge step-down so the ratio between them
     holds at small widths instead of collapsing. Scoped to the shared
     .TitleXLarge utility (not a per-element override) since every
     instance of "too-big secondary text" the user pointed at — tagline,
     hero card title, section headings — is that same one class. */
  .ca-hero__wordmark { font-size: 48px; }
  .TitleXLarge { font-size: 24px; }
  /* Same rebalance, one step down: these three headings share TitleLarge
     (20px) and sit directly under a TitleXLarge heading in the page's own
     hierarchy (Steering Committee under Video Updates' H2; Practice Aids/
     More Resources subheaders under their own section H2) — once
     TitleXLarge dropped to 24px above, leaving these at 20px flattened
     that relationship. Scoped to these three selectors specifically,
     not the shared .TitleLarge class, since that class also drives the
     Podcast "Now Playing" label, which wasn't part of this ask and reads
     fine at its current size. */
  .ca-section-heading.TitleLarge,
  .ca-practice-aids__category-heading,
  .ca-accordion__group-heading,
  .ca-asam__group-heading { font-size: 18px; }
  .ca-hero-slide { min-height: 320px; }
  /* Footer legal links stack one per row on mobile instead of wrapping
     inline with "|" separators — direct feedback that inline reads
     cramped at this width. The separators become redundant once each
     link is its own row, so they're hidden rather than left dangling
     at the end of a line. */
  .ca-footer__links { flex-direction: column; align-items: flex-start; gap: var(--size-space-2xs); }
  .ca-footer__links-sep { display: none; }
  /* padding-bottom stays the larger, desktop-matching reserve (see the
     base rule's comment) — the controls' own size/inset don't shrink at
     this breakpoint, and this is exactly the breakpoint where the tallest
     slide's title/subtitle wrap the most (100%-width content), so the
     clearance is needed here at least as much as on desktop. */
  .ca-hero-slide__content { max-width: 100%; padding: var(--size-space-md) var(--size-space-md) calc(var(--size-space-2xl) + var(--size-space-sm)) var(--size-space-md); }
  /* Flat wash, not the desktop gradient — see the base .ca-hero-slide__scrim
     comment for why. --hero-slide-scrim-mobile is the per-slide override
     (ca-render.js); the rgba() here is the fallback for slides that don't
     set a custom scrimColor. */
  .ca-hero-slide__scrim { background: var(--hero-slide-scrim-mobile, rgba(0,119,190,0.82)); }

  .ca-card--list { flex-direction: column; align-items: stretch; }
  .ca-card--list .ca-card__thumb { flex-basis: auto; }

  /* Larger than the ≤1023px tablet size (220px) — that width read as too
     cramped on real phones, per direct feedback ("way too tight"),
     mainly for multi-line credentials text (e.g. "Washington University
     School of Medicine") wrapping awkwardly in the ~188px text column
     220px left after side padding. 280px gives that text real room
     without needing a font-size change. This rule comes after the
     ≤1023px block in the cascade, so at ≤767px (where both queries are
     simultaneously true) it correctly wins over the 220px rule by source
     order, same specificity. */
  .ca-faculty-card { flex: 0 0 280px; }
}

@media (max-width: 479px) {
  /* 56px, not the --size-space-lg (40px) every other breakpoint's section
     padding was inheriting down to here unchanged — direct feedback
     ("section padding seems a bit tight") against a phone screenshot.
     No token sits between lg/40 and 2xl/80, so this is a raw value
     (same pattern already used elsewhere in this file for an in-between
     size with no matching token), picked to open the sections up
     noticeably without reintroducing the full 80px desktop rhythm on a
     narrow phone. */
  .ca-section { padding: 56px 0; }
  /* Continues the hierarchy rebalance from the ≤767px block above — the
     wordmark needs to stay ahead of TitleXLarge's own further step-down
     at this width, not just hold the 767px ratio. */
  .ca-hero__wordmark { font-size: 36px; }
  .TitleXLarge { font-size: 22px; }
  .ca-podcast__episodes-list { max-height: 280px; }
  .ca-footer__inner { flex-direction: column; }
}
