/* ============================================================
   CG — components.css
   Themes 2.1 shared component styles.
   Source: generic-cld/assets/css/components.css

   Loaded by every Themes 2.1 theme decorator before theme.css.
   Theme-level overrides go in theme.css (higher specificity or
   custom property redefinition).

   Load order in decorator:
     /var.css                       — primitive + semantic tokens
     /assets/css/components.css  — this file (resolves via generic/ fallback)
     /assets/css/navbar-primary.css
     /theme.css                     — theme overrides
     /site.css                      — site overrides

   Adding a new component:
     1. Build the component in generic/ with a cg- prefixed file name
     2. Add its styles here under a clearly labelled section
     3. Do NOT call $website.addCss() from the component view

   LINK COLORS:
     cg- link tokens resolve as var(--body-link, var(--bs-link-color)) (and the
     -hover pair) — they PREFER the theme's --body-link authoring token and fall
     back to Bootstrap's --bs-link-color when a theme doesn't define it. This lets
     a theme recolor links (base <a> AND cg- children) in any scope by setting
     only --body-link* there; --bs-link-color stays the Bootstrap-side default.
   ============================================================ */


/* ============================================================
   SHARED TOKENS
   Override in var.css or site.css to change for all components.
   ============================================================ */
:root {
    --cg-icon-font: "Font Awesome 7 Pro";

    /* Secondary nav surface — white by default. Both the theme's
       .header-row-secondary outer band and the generic
       .component-navbar-secondary .navbar-secondary surface consume
       these tokens so the row and the navbar render as a single
       unified strip. Override at any scope to retheme (e.g. set
       --cg-nav-sec-bg on :root or .site-header to recolor both). */
    --cg-nav-sec-bg:    #ffffff;
    --cg-nav-sec-color: #212529;
}


/* ============================================================
   PAGE — suppress horizontal page scroll

   Themes 2.1 has full-bleed surfaces that intentionally break out past their
   container: a width=container-fluid dropdown panel is the current one, and any
   100vw break-out behaves the same way. Two things make such an element overhang
   the usable width by a handful of pixels, and either is enough to raise a
   horizontal scrollbar:

     1. 100vw INCLUDES the vertical scrollbar, while the usable width excludes
        it — so a 100vw element is ~15px too wide whenever the page scrolls.
     2. The break-out centres on its containing block. If that block is not
        perfectly centred in the viewport (a reserved scrollbar gutter shifts
        centred containers a few px), the panel leans to one side.

   It compounds: the overhang raises a scrollbar, the scrollbar shifts centred
   content, and the panel leans further out.

   Declared on BODY, not html, and deliberately so. When the root element's
   overflow is `visible`, the UA applies the BODY's overflow to the viewport and
   body itself stays `visible` — so the viewport suppresses the horizontal
   scrollbar while body does NOT become a scroll container. That distinction
   matters: making body a scroll container would break `position: sticky` for
   everything inside it, and this family relies on sticky headers.

   This lives in the shared 2.1 layer so every theme inherits it. It was
   previously present only via site-level CSS on some sites, which is why the
   scroll appeared on themes that happened not to have it.
   ============================================================ */
body {
    overflow-x: hidden;
}


/* ============================================================
   COMPONENT: navbar-primary
   ============================================================ */

/* Custom property hooks — define in theme.css to restyle
   without a template override. Values fall back to inherited
   behavior when not explicitly set. */
.component-navbar-primary {
    /* Top-level nav link size: 1rem in the collapsed menu, 1.15rem once the
       navbar expands (lg+). Stepped down on mobile because the stacked menu
       reads as a list, not a bar. The lg+ value is set in the media query
       right after this block.
       NOTE: navbar-primary.css re-declares this same token and loads AFTER
       this file (components.css → navbar-primary.css → theme.css), so it
       carries an identical pair. Change both or the later file wins. */
    --cg-nav-link-font-size:      1rem;
    --cg-nav-link-font-weight:    inherit;
    --cg-nav-link-text-transform: none;
    --cg-nav-link-letter-spacing: normal;
    --cg-nav-link-px:             0.75rem;
    --cg-nav-link-py:             0.75rem;

    /* Dropdown-panel interior rhythm. Distinct from the --cg-nav-link-*
       tokens above, which belong to the TOP-LEVEL navbar links only.
       Every clickable row inside a panel — .dropdown-item, a panel
       .nav-link, and .dropdown-panel-sub-header-link — shares this padding
       at every breakpoint so the panels read as one list. --indent is the
       left margin that gives nested <ul>s their stepped appearance. */
    --cg-nav-panel-link-px:       0.5rem;
    --cg-nav-panel-link-py:       0.25rem;
    --cg-nav-panel-indent:        0.75rem;
    /* Inline padding for the social icon row only — square glyph targets read
       as one group when they sit tighter than a text row. The first and last
       icons still take --cg-nav-panel-link-px on their outer edge so the row
       aligns with the text rows around it. */
    --cg-nav-panel-icon-px:       0.25rem;
    /* Floor for a single mega COLUMN, the counterpart to
       --cg-nav-dropdown-auto-min-width (the floor for a whole width=auto
       panel). Same 300px by default so a column and a narrow panel bottom
       out at the same width. Desktop only — see .dropdown-panel-col. */
    --cg-nav-panel-col-min-width: 300px;

    --cg-nav-logo-height:         auto;
    --cg-nav-logo-max-height:     2.25rem;
    --cg-nav-logo-max-height-lg:  3.5rem;

    --cg-nav-dropdown-border-top: 3px solid var(--bs-secondary);
    --cg-nav-item-hover-bg:       rgba(0, 0, 0, 0.05);
    --cg-nav-item-hover-bg-dark:  rgba(255, 255, 255, 0.05);
    --cg-nav-divider-color:       rgba(0, 0, 0, 0.125);
    --cg-nav-divider-color-dark:  rgba(255, 255, 255, 0.2);

    /* Collapse panel spacing between nav list, social wrap, and mobile footer. */
    --cg-nav-collapse-gap:        1rem;

    /* Dropdown panel color isolation.
       Panels are styled independently from the navbar — they always render as a
       light surface regardless of the parent navbar's color scheme.
       Override any of these in theme.css on .component-navbar-primary to reskin. */
    --cg-nav-dropdown-bg:              #ffffff;
    --cg-nav-dropdown-color:           #212529;
    --cg-nav-dropdown-link-color:      var(--body-link, var(--bs-link-color));
    --cg-nav-dropdown-link-hover-color: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-nav-dropdown-item-hover-bg:   rgba(0, 0, 0, 0.06);
    --cg-nav-dropdown-divider-color:   rgba(0, 0, 0, 0.1);
    /* Panel section-header underline color (the fading gradient under the col
       headers / .dropdown-header). Defaults to the brand secondary; the gray
       fallback keeps it visible if --bs-secondary isn't site-configured. Themes
       should set this to their own secondary token in theme.css. */
    --cg-nav-dropdown-header-underline: var(--bs-secondary, #6c757d);

    /* Shared drop shadow for floating dropdown panels — applied to
       .dropdown-panel--mega and .navbar-aside-dropdown so all panels
       that float over content read with the same elevation. Override
       in theme.css to retune both panel types together. */
    --cg-nav-dropdown-shadow:          0 6px 16px rgba(0, 0, 0, 0.2);

    /* Toggler — Themes 2.1 uses a single FA glyph via ::before (fa-bars
       closed, fa-xmark when [aria-expanded="true"]). The legacy three
       <span class="icon-bar"> bar-X markup is no longer rendered. */
    --cg-nav-toggler-color:         currentColor;
    --cg-nav-toggler-icon:          "\f0c9";  /* fa-bars */
    --cg-nav-toggler-icon-open:     "\f00d";  /* fa-xmark */
    --cg-nav-toggler-icon-size:     1.25rem;
    --cg-nav-toggler-border-width:  var(--bs-border-width, 1px);
    --cg-nav-toggler-border-color:  transparent;
    --cg-nav-toggler-border-radius: var(--bs-border-radius, 0.375rem);

    /* JS-managed — keeps --cg-aside-width in sync with the rendered
       .navbar-aside width. Consumed by .layout-centered's collapse:
       negative margin-right pulls the collapse's visual box past the
       aside so it spans the full row; symmetric padding-inline of the
       same width mirrors a dead zone on both sides, keeping the
       centered brand at true container center and the inner nav items
       clear of the aside on the right. */
    --cg-aside-width:               0px;
}

/* Top-level nav link size steps up once the navbar expands. Declared as a
   token override rather than a font-size rule so every consumer of
   --cg-nav-link-font-size tracks it — including the split-layout chevron
   button, which sizes its glyph off the same base. */
@media (min-width: 992px) {
    .component-navbar-primary {
        --cg-nav-link-font-size: 1.15rem;
    }
}

/* Strip Bootstrap's default navbar padding — padding is managed by the
   parent (.header-row-primary in the theme) so the navbar should be flush.

   POSITIONING INVARIANT — <nav> is the containing block for dropdown panels.
   position:relative is declared explicitly rather than relying on Bootstrap's
   default, because the container / container-fluid width modes measure their
   offsets against it. Nothing between <nav> and a panel may be positioned:
   not .navbar > .container-fluid, not .navbar-collapse, not .navbar-nav, and
   not the .nav-item (the width-mode rules set that to static themselves).
   Introducing position:relative on any of them silently re-anchors every wide
   panel to that element instead — which is what pushed centered-layout mega
   panels off the left edge of the viewport. Anything needing an absolute
   anchor inside the navbar (e.g. .layout-centered's .navbar-aside) should
   resolve against <nav> too. */
.component-navbar-primary .navbar {
    --bs-navbar-padding-y: 0;
    --bs-navbar-padding-x: 0;
    position: relative;
}

/* Strip Bootstrap's default .navbar-brand vertical padding on both
   primary and secondary navs so the brand sits flush with the navbar's
   line-height. Bootstrap's .navbar-brand rule reads padding-top /
   padding-bottom from --bs-navbar-brand-padding-y, so setting the
   variable to 0 on the brand element is the cleanest override path.
   Themes can override on a per-brand basis by redefining the variable
   at a higher specificity if a vertical pad is wanted later. */
.component-navbar-primary  .navbar-brand,
.component-navbar-secondary .navbar-brand {
    --bs-navbar-brand-padding-y: 0;
}

/* display sits in a :where() wrapper so its specificity is (0,1,0) instead
   of (0,2,0). That lets Bootstrap's .navbar-expand-{bp} .navbar-toggler
   { display: none } rule (also 0,2,0, but loaded earlier) win at the
   expand breakpoint and hide the hamburger on desktop as expected.
   Both navbar-primary and navbar-secondary share the same toggler look. */
:where(.component-navbar-primary, .component-navbar-secondary) .navbar-toggler {
    display: inline-flex;
}

.component-navbar-primary .navbar-toggler,
.component-navbar-secondary .navbar-toggler {
    align-items:     center;
    justify-content: center;
    font-size:       var(--cg-nav-toggler-icon-size);
    line-height:     1;
    color:           var(--cg-nav-toggler-color);
    border:          var(--cg-nav-toggler-border-width) solid var(--cg-nav-toggler-border-color);
    border-radius:   var(--cg-nav-toggler-border-radius);
    /* padding (.75rem) and background (transparent) are inherited from
       global-theme-reset-bs5.css so we don't fight that file's !important. */
}

.component-navbar-primary .navbar-toggler::before,
.component-navbar-secondary .navbar-toggler::before {
    font-family: var(--cg-icon-font);
    font-weight: 900;
    content:     var(--cg-nav-toggler-icon);
    font-size:   inherit;
}

.component-navbar-primary .navbar-toggler[aria-expanded="true"]::before,
.component-navbar-secondary .navbar-toggler[aria-expanded="true"]::before {
    content: var(--cg-nav-toggler-icon-open);
}

/* The toggler tokens are defined on .component-navbar-primary above; mirror
   the same defaults onto .component-navbar-secondary so its .navbar-toggler
   reads identically without requiring per-component theme overrides. */
.component-navbar-secondary {
    --cg-nav-toggler-color:         currentColor;
    --cg-nav-toggler-icon:          "\f0c9";  /* fa-bars */
    --cg-nav-toggler-icon-open:     "\f00d";  /* fa-xmark */
    --cg-nav-toggler-icon-size:     1.25rem;
    --cg-nav-toggler-border-width:  var(--bs-border-width, 1px);
    --cg-nav-toggler-border-color:  transparent;
    --cg-nav-toggler-border-radius: var(--bs-border-radius, 0.375rem);
}

/* Collapse panel — gap between nav list, social wrap, and mobile footer */
.component-navbar-primary .navbar-collapse {
    gap: var(--cg-nav-collapse-gap, 1rem);
}

/* Mobile collapse — cap the expanded menu at viewport height minus
   the navbar's own height so tall menus scroll within the panel
   instead of spilling off the page. The offset is a CSS variable so
   themes can tune it to match their actual navbar height; default
   approximates a standard 64px navbar.
   Above the expand breakpoint the collapse is inline (flex:1 in the
   centered layout) and this cap doesn't apply. */
@media (max-width: 991.98px) {
    .component-navbar-primary .navbar-collapse {
        max-height: calc(100vh - var(--cg-nav-mobile-menu-offset, 64px));
        overflow-y: auto;
        /* The collapse now precedes .navbar-aside in source order (so the menu
           tabs before the aside icons). It carries flex-basis:100%, so push it
           visually last with order so the aside + toggler stay on the brand row
           (lg+ already handles this via the aside's own order:100). */
        order: 1;
    }
}

/* Scoped to direct nav-item children only — prevents these hooks from
   bleeding into .dropdown-item links inside dropdown panels, which should
   stay at the browser/body default (1rem). */
.component-navbar-primary .navbar .navbar-nav > .nav-item > .nav-link,
.component-navbar-primary .navbar .navbar-nav > .nav-item > .d-flex > .nav-link {
    font-size:      var(--cg-nav-link-font-size);
    font-weight:    var(--cg-nav-link-font-weight);
    text-transform: var(--cg-nav-link-text-transform);
    letter-spacing: var(--cg-nav-link-letter-spacing);
}

@media (min-width: 992px) {
    /* Top-level primary nav-links — apply the cg padding tokens at
       desktop. Both layouts (standard + centered) read the same rule
       so the primary's horizontal rhythm stays consistent with the
       secondary nav (which uses --cg-nav-sec-link-px at the same
       0.75rem default). Dropdown-panel items keep their own
       padding rules further down; this targets ONLY direct children
       of .navbar-nav so the inner panel links are unaffected. The
       .d-flex > .nav-link variant covers the legacy Case-2 split
       markup where the link is wrapped in a flex row alongside the
       chevron button. */
    .component-navbar-primary .navbar .navbar-nav > .nav-item > .nav-link,
    .component-navbar-primary .navbar .navbar-nav > .nav-item > .d-flex > .nav-link {
        padding: var(--cg-nav-link-py) var(--cg-nav-link-px);
    }
}

/* Mobile — bump vertical padding on the primary nav's stacked menu
   items so tap targets sit near the 44px guideline, and apply the
   horizontal padding token (--cg-nav-link-px) which on desktop only
   fires inside the layout-standard branch. Mirrors the secondary
   nav's mobile rule. Scoped to .navbar-nav so aside icon-links
   (which use .nav inside .navbar-aside, not .navbar-nav) stay at
   their tight default. Covers both collapse and offcanvas modes —
   both render the menu list as <ul class="navbar-nav">. */
@media (max-width: 991.98px) {
    .component-navbar-primary .navbar-nav .nav-link {
        padding-inline: 0;        /* flush left/right on mobile — indentation comes from nested-UL margins */
        padding-block:  0.375rem; /* half the desktop rhythm to keep the mobile menu short */
    }
}

/* layout=centered — desktop split layout
   The collapse becomes a flex row: nav-left | brand-centered | nav-right | social (absolute)

   .layout-centered is the sole CSS hook — no Bootstrap navbar-expand-{bp} class names appear
   in these selectors. The caller keeps the expand param and this breakpoint in sync.
   To use a different expand breakpoint, override --cg-nav-centered-breakpoint in theme.css
   and duplicate the @media block at the desired min-width.

   Brand variable resets are unconditional — .navbar-brand-centered is hidden below the expand
   point by Bootstrap display utilities so these resets are visually inert until visible. */

.component-navbar-primary.layout-centered .navbar-brand-centered {
    --bs-navbar-brand-padding-y:  0.75rem;
    --bs-navbar-brand-margin-end: 0;
    --bs-navbar-brand-font-size:  inherit;
    --bs-navbar-brand-color:      inherit;
    /* Logo z-index ladder: dropdown panels (1050) < brand (1051) < aside (1052).
       The brand is a flex child of .container-fluid, so z-index applies (and
       creates a stacking context) even without an explicit position. The brand
       and every wide dropdown panel — mega / fluid / members / headlines, all
       z-index:1050 — resolve in the same parent stacking context
       (.header-row-primary). At an equal z-index the later-in-DOM panel would
       win and paint over the logo, covering the bottom of a logo that overhangs
       the primary row (e.g. a theme with a negative --header-logo-offset-bottom).
       Keeping the brand one step above the panel layer holds the logo on top of
       ANY panel, including panel types added later. */
    z-index: 1051;
}

/* Each nav half takes equal space — pins the brand to true center */
.component-navbar-primary.layout-centered .navbar-nav-left,
.component-navbar-primary.layout-centered .navbar-nav-right {
    flex: 1;
}

/* Left items push right toward the brand; right items push left toward the brand */
.component-navbar-primary.layout-centered .navbar-nav-left {
    justify-content: flex-end;
}

.component-navbar-primary.layout-centered .navbar-nav-right {
    justify-content: flex-start;
}

@media (min-width: 992px) {
    .component-navbar-primary.layout-centered .navbar-collapse {
        display: flex !important;
        align-items: stretch;
        justify-content: center;
    }

    /* Why stretch instead of center: the .navbar-brand-centered (with logo)
       is taller than the nav <ul>s. Fluid/mega dropdown panels anchor at
       top:100% of .navbar; if .nav-item is shorter than the navbar, the
       cursor crosses an empty band on its way to the panel, fires mouseleave
       on .nav-item.dropdown, and Bootstrap's hover-open closes the panel.
       Stretching the ULs + their <li>s to the collapse height keeps the
       hover hit-area flush with the panel's top edge. */
    .component-navbar-primary.layout-centered .navbar-nav-left,
    .component-navbar-primary.layout-centered .navbar-nav-right {
        align-items: stretch;
    }

    .component-navbar-primary.layout-centered .navbar-nav-left > .nav-item,
    .component-navbar-primary.layout-centered .navbar-nav-right > .nav-item {
        display:     flex;
        align-items: stretch;
    }

    .component-navbar-primary.layout-centered .navbar-nav-left > .nav-item > .nav-link,
    .component-navbar-primary.layout-centered .navbar-nav-right > .nav-item > .nav-link {
        display:     flex;
        align-items: center;
    }
}

/* Social icon links — reduce Bootstrap's default nav-link horizontal padding
   (1rem → 0.25rem per side) so icons sit tighter as a group. */
.component-navbar-primary .navbar-social-wrap .nav-link {
    padding-inline: 0.25rem;
}

/* ------------------------------------------------------------
   .navbar-aside — holds composite-icon, social icon-dropdown, and
   the navbar-toggler.

   Layout strategy: the aside flexes inline at `order: 100` so it
   sits visually after .navbar-collapse on desktop. Its rendered
   width is tracked in --cg-aside-width by the
   navbar-primary-accessibility.js module on load + window resize.

   For layout-centered, the collapse uses --cg-aside-width to
   "reach under" the aside: a negative margin-right of the same
   width lets the collapse's outer box span the full container row,
   and a symmetric padding-inline reserves a matching dead zone on
   both sides — keeping the centered brand at true container center
   and the inner nav items clear of the aside on the right.
   ------------------------------------------------------------ */
.component-navbar-primary .navbar-aside {
    display:     flex;
    align-items: center;
    gap:         0.25rem;
    margin-left: auto;       /* push to right edge of the brand row */
}

/* Desktop only — push aside past the collapse via flex order. Aside is
   order 100 so it sits to the right of the social panel (order 99 when
   inline-expanded), keeping the visual order: collapse | social panel |
   calendar | (hamburger hidden). On mobile, no order — aside flows
   between brand and collapse via DOM order; margin-left:auto alone
   keeps it pinned to the right of the brand row. */
@media (min-width: 992px) {
    .component-navbar-primary .navbar-aside {
        order: 100;
    }

    /* Centered layout: take the aside OUT OF FLOW (absolute, pinned to the
       navbar's right edge) so it can no longer shift the centered brand/nav.
       Previously the aside flexed inline and the collapse "reached under" it
       via a JS-measured --cg-aside-width (negative margin + symmetric
       padding); when that var was 0/stale (JS not yet run, icons reflowing,
       etc.) the brand drifted off-center by half the aside width. With the
       aside absolute, the collapse spans the full row and centers at true
       container center regardless of the aside width or the JS. */
    /* The aside resolves against <nav> itself, NOT against
       .navbar > .container-fluid. That wrapper used to carry position:relative
       for this purpose, which made IT the containing block for every dropdown
       panel in the centered layout — so a container / container-fluid panel
       measured its 50% against the wrapper instead of <nav> and landed off to
       one side, dragging most of the panel outside the viewport. <nav> has its
       Bootstrap padding zeroed and the wrapper is p-0, so their boxes coincide
       and right:0 lands identically. See the positioning invariant on
       .component-navbar-primary .navbar. */
    .component-navbar-primary.layout-centered .navbar-aside {
        position:    absolute;
        top:         0;
        right:       0;
        bottom:      0;          /* full navbar height; align-items:center centers the glyphs */
        margin-left: 0;          /* override the in-flow margin-left:auto */
        z-index:     1052;       /* top of the ladder: panels 1050 < brand 1051 < aside 1052 — keeps the aside icons above the centered brand where they overlap */
    }

    .component-navbar-primary.layout-centered .navbar-collapse {
        flex:           1 0 100%;
        margin-right:   0;
        /* Reserve a symmetric clearance dead-zone so wide right-side nav items
           don't run under the absolute aside. Still bound to --cg-aside-width
           when the JS has measured it; harmless (0) if not — the centering no
           longer depends on it. */
        padding-inline: var(--cg-aside-width);
    }
}

/* ------------------------------------------------------------
   Aside icon group — the .navbar-aside contains a single CMS include
   of /navbar-aside (rendered by component-nav-template) plus the
   navbar-toggler. The include emits a `<ul class="nav">` of icon
   links; Bootstrap's .nav handles the inline horizontal layout, and
   .nav-link colors cascade from .navbar's --bs-navbar-color so the
   aside icons match the primary nav-link color and hover.

   These rules just polish spacing and centering inside the aside —
   they don't impose new chrome on the inner BS structure.
   ------------------------------------------------------------ */
.component-navbar-primary .navbar-aside .ps-component {
    /* The component-nav-template renders <div class="ps-component
       component-nav layout-standard d-flex flex-column gap-3"> as the
       outer wrapper. Inside the aside that flex-column wrapper has
       only one child (the layout-wrapper), so flex-column is inert
       but the gap utility can leave unwanted vertical padding when
       the parent is a flex-row. Reset to a tight inline-flex so the
       icon ul reads as a single row inside the aside.

       align-self: stretch fills the navbar-aside cross-axis without
       changing the aside's own align-items (which is `center` so the
       navbar-toggler sibling stays vertically centered). align-items:
       stretch then cascades the full height down through the <ul> to
       the <li>s so themes can set borders / dividers on individual
       aside items and have them render flush top-to-bottom. */
    display:     inline-flex;
    align-self:  stretch;
    align-items: stretch;
    margin:      0;
    padding:     0;
    gap:         0;
}

.component-navbar-primary .navbar-aside .ps-component .nav {
    flex-wrap:       nowrap;
    align-items:     stretch;
}

/* Inner <li> — full-height flex cell so borders span the whole
   aside row. The <a> child stays vertically centered via this rule's
   align-items: center; .nav-link--icon's own justify-content: center
   continues to center the icon horizontally. */
.component-navbar-primary .navbar-aside .ps-component .nav > li {
    display:     flex;
    align-items: center;
}

/* Inner <a> — also stretches to the full <li> height so the clickable
   surface matches the visible cell. The link's own flex layout keeps
   the icon (or text) centered within the stretched box; .nav-link--icon's
   justify-content: center (declared above) continues to center the glyph
   horizontally. */
.component-navbar-primary .navbar-aside .ps-component .nav-link {
    align-self:  stretch;
    display:     flex;
    align-items: center;
}

/* Icon-link variant — emitted by component-navbar-aside-template for
   bracket-prefix labels (e.g. "[Facebook]", "[Calendar]"). The link
   reads as a tight icon-only target; the label is .visually-hidden so
   screen readers still announce it. Apply font-size override in
   theme.css via --cg-nav-aside-icon-size. */
.component-navbar-primary .nav-link--icon {
    --cg-nav-aside-icon-size: 1.1rem;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    padding-inline:  0.25rem;
    font-size:       var(--cg-nav-aside-icon-size);
    line-height:     1;
}

/* ------------------------------------------------------------
   navbar-aside dropdown — emitted when an aside entry has a
   secondaryURL. The .dropdown-menu panel hangs from the bottom-
   right of its triggering .nav-item.dropdown (top:100%, right:0
   resolve against the <li>, which keeps Bootstrap's default
   .dropdown { position:relative }). data-bs-display="static" on
   the toggle disables Popper.js so CSS owns positioning.
   ------------------------------------------------------------ */
.component-navbar-primary .navbar-aside-dropdown {
    position:       absolute;
    top:            100%;
    right:          0;
    left:           auto;
    z-index:        1050;
    min-width:      12rem;
    margin-top:     0;
    border:         none;
    border-radius:  0;
    box-shadow:     var(--cg-nav-dropdown-shadow);
    padding:        1rem;   /* match the 1rem wrapper inset of the other panels so the header + items don't touch the edge */
}

/* Dropdown items inside the aside panel — vertical stack of
   left-aligned text links. Bootstrap's .dropdown-item is already
   block-level + left-aligned; this just ensures it overrides any
   inherited centering from the aside's inline-flex layout. */
.component-navbar-primary .navbar-aside-dropdown .dropdown-item {
    display:    block;
    text-align: left;
    width:      100%;
}

/* Dropdown toggle — replace Bootstrap's border caret with FA chevron-down.
   Applies to .dropdown-toggle in both primary AND secondary navs so the
   caret style is consistent across both. (Primary: Case 3 full-link
   toggles inside the navbar / mega panels. Secondary: top-level inline
   dropdown items that open Bootstrap dropdowns below the row.)
   color: inherit ensures the icon matches the nav-link color at all states. */
.component-navbar-primary .dropdown-toggle::after,
.component-navbar-secondary .dropdown-toggle::after {
    border: none;
    color: inherit;
    font-family: var(--cg-icon-font);
    font-weight: 900;
    content: "\f078";
    vertical-align: middle;
    margin-left: 0.35em;
    font-size: 0.7em;
    display: inline-block;
    transition: transform 0.2s ease;
}

/* Rotate the chevron when open (.show) or hovered (.is-hovering for the
   primary's desktop hover-open behavior). Secondary doesn't use
   is-hovering, just .show. */
.component-navbar-primary  .dropdown.show       > .dropdown-toggle::after,
.component-navbar-primary  .dropdown.is-hovering > .dropdown-toggle::after,
.component-navbar-secondary .dropdown.show       > .dropdown-toggle::after {
    transform: rotate(180deg);
}

/* Match nav-link color when panel is open (.show) to the hover color.
   Bootstrap uses --bs-navbar-active-color for .show by default, which may
   differ from hover. This makes open = hover visually consistent. */
.component-navbar-primary .navbar-nav > .nav-item > .nav-link.show,
.component-navbar-primary .navbar-nav > .nav-item.d-flex > .nav-link.show {
    color: var(--bs-navbar-hover-color);
}

/* Split dropdown chevron button — Case 2 (entry has targetURL + children).
   The <a> navigates; this <button> opens the dropdown panel.
   Both are direct children of <li class="... d-flex align-items-stretch">.
   ::after renders the FA chevron-down icon; .show/.is-hovering rotate it. */
.component-navbar-primary .nav-dropdown-chevron {
    background:    transparent;
    border:        none;
    color:         inherit;
    /* Read identically to the inline .dropdown-toggle::after caret. font-size
       tracks the nav-link so the 0.7em glyph below is sized off the same base;
       no left padding (the gap is the ::after's margin-left, exactly like the
       caret); the trailing space is the nav-link's own --cg-nav-link-px, so the
       chevron's right edge lands where a normal nav-link's would. The split
       label drops its padding-right (rule below) so the only space between the
       label text and the glyph is that ::after margin. */
    font-size:     var(--cg-nav-link-font-size);
    padding:       0 var(--cg-nav-link-px) 0 0;
    cursor:        pointer;
    line-height:   1;
    flex-shrink:   0;
    align-self:    stretch;
    display:       flex;
    align-items:   center;
}

.component-navbar-primary .nav-dropdown-chevron::after {
    font-family:   var(--cg-icon-font);
    font-weight:   900;
    content:       "\f078";   /* chevron-down */
    font-size:     0.7em;
    margin-left:   0.35em;   /* same gap as the .dropdown-toggle::after caret */
    display:       inline-block;
    transition:    transform 0.2s ease;
}

/* The split label drops its trailing padding so the chevron's ::after margin
   (above) is the ONLY space between the label text and the glyph — matching the
   single-link case where the caret sits inside the link, ahead of its padding.
   Higher specificity than the base nav-link padding rule so it wins at every
   breakpoint. */
.component-navbar-primary .navbar .navbar-nav > .nav-item.d-flex > .nav-link {
    padding-right: 0;
}

/* Rotate chevron when the dropdown is open (.show = click) or hovered (.is-hovering = JS) */
.component-navbar-primary .dropdown.show > .nav-dropdown-chevron::after,
.component-navbar-primary .dropdown.is-hovering > .nav-dropdown-chevron::after {
    transform: rotate(180deg);
}

/* Open / hovered: shift the chevron to the nav-link hover colour so the split
   item changes together, mirroring the caret (which follows .nav-link.show /
   :hover). Pairs with the rotate selectors above. */
.component-navbar-primary .dropdown.show > .nav-dropdown-chevron,
.component-navbar-primary .dropdown.is-hovering > .nav-dropdown-chevron {
    color: var(--bs-navbar-hover-color);
}

/* Site logo sizing — applied when logo=true is passed to the template */
.component-navbar-primary .cg-navbar-logo {
    height:     var(--cg-nav-logo-height);
    max-height: var(--cg-nav-logo-max-height);
    width:      auto;
}

@media (min-width: 992px) {
    .component-navbar-primary .cg-navbar-logo {
        max-height: var(--cg-nav-logo-max-height-lg);
    }
}


/* ============================================================
   DROPDOWN PANELS — variant layouts for navbar-primary dropdowns
   Activated via panel= and fluid= params on entry secondaryURL.

   panel=columns-1  (default) — single-column, relative to parent <li>
   panel=columns-2            — 2 CSS columns,  relative to parent <li>
   panel=columns-3            — 3 CSS columns,  relative to parent <li>
   panel=mega                 — grouped columns, always full-width of <nav>
   fluid=true                 — modifier: anchors ANY non-mega panel full-width
                                to <nav> instead of the parent <li>
   ============================================================ */

/* columns-2 / columns-3 — CSS column-count splits a flat item list.
   panel=columns-1 (default) needs no class — standard dropdown behavior. */
.component-navbar-primary .dropdown-panel--columns-2 .dropdown-menu-list {
    column-count: 2;
    column-gap: 0;
}

.component-navbar-primary .dropdown-panel--columns-3 .dropdown-menu-list {
    column-count: 3;
    column-gap: 0;
}

/* Mirror Bootstrap's .dropdown-menu[data-bs-popper] positioning on
   cg- primary panels. cg- uses data-bs-display="static" on the
   toggles, which disables Popper and therefore prevents Bootstrap
   from adding the data-bs-popper attribute — meaning the upstream
   rule never fires and the panel falls back to the offset parent's
   origin (top-left of .dropdown <li>) instead of opening below the
   toggle. Re-declaring the same three properties under our own
   selector restores the standard "open beneath the trigger"
   placement at the expand breakpoint.

   Scoped to lg+ because below the breakpoint the panel is in-flow
   inside the collapse (Bootstrap's .navbar-nav .dropdown-menu rule
   forces position:static at mobile). Excludes .dropdown-panel--mega,
   .dropdown-panel--members, .dropdown-panel--headlines, and
   .navbar-aside-dropdown which manage their own positioning (and must open
   flush — no spacer gap).

   Excludes .dropdown-panel outright, which is every first-level panel type.
   Panels get their vertical origin from SHARED PANEL CHROME below (flush, no
   spacer) and their horizontal placement from their --w-* modifier, so this
   rule has nothing to contribute to them and its spacer + left:0 would actively
   fight both: the spacer is what used to make a columns-N panel hang lower than
   a mega panel on the same navbar, and left:0 would defeat the left:50% centring
   that --w-container / --w-container-fluid depend on.

   One :not() per panel type used to be listed here instead (--mega, --members,
   --headlines, --w-container, --w-container-fluid) with --w-auto deliberately
   left IN so it would take the spacer. Excluding the base class covers all of
   them at once and is what makes every panel share one vertical origin.

   What remains in scope: dropdowns inside the primary navbar that are NOT
   panels. Those keep Bootstrap's spacer behavior exactly as before. */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-menu:not(.dropdown-panel):not(.navbar-aside-dropdown) {
        top:        100%;
        left:       0;
        margin-top: var(--bs-dropdown-spacer);
    }
}

/* Dropdown panel color isolation — applies to all panel variants.
   data-bs-theme="light" is set directly on each panel element in the templates,
   which makes Bootstrap reset all its dark-theme tokens natively. The rules below
   then override Bootstrap's light-theme defaults with --cg-nav-dropdown-* tokens
   so themes can reskin panels from theme.css without touching templates. */
/* Color isolation applies to all cg- dropdown panels including the social
   panel — when the panel renders as a popover on mobile it should read
   like the primary dropdown menu (white surface, dropdown link colors).
   The inline-expand blocks below override these tokens back to inherit
   when the panel is flattened onto the navbar at the socialExpand
   breakpoint, so social links match the primary nav colors there. */
.component-navbar-primary .dropdown-menu,
.component-navbar-primary .dropdown-panel--mega {
    background-color: var(--cg-nav-dropdown-bg);
    color:            var(--cg-nav-dropdown-color);

    /* Reset Bootstrap's cascaded color-mode tokens */
    --bs-body-color:                  var(--cg-nav-dropdown-color);
    --bs-body-bg:                     var(--cg-nav-dropdown-bg);
    --bs-nav-link-color:              var(--cg-nav-dropdown-link-color);
    --bs-nav-link-hover-color:        var(--cg-nav-dropdown-link-hover-color);
    --bs-dropdown-bg:                 var(--cg-nav-dropdown-bg);
    --bs-dropdown-color:              var(--cg-nav-dropdown-color);
    --bs-dropdown-link-color:         var(--cg-nav-dropdown-link-color);
    --bs-dropdown-link-hover-color:   var(--cg-nav-dropdown-link-hover-color);
    --bs-dropdown-link-hover-bg:      var(--cg-nav-dropdown-item-hover-bg);
    --bs-dropdown-divider-bg:         var(--cg-nav-dropdown-divider-color);
}

/* Plain <a> tags inside panels that don't carry a BS color class. */
.component-navbar-primary .dropdown-menu a,
.component-navbar-primary .dropdown-panel--mega a {
    color: var(--cg-nav-dropdown-link-color);
}

.component-navbar-primary .dropdown-menu a:hover,
.component-navbar-primary .dropdown-panel--mega a:hover {
    color: var(--cg-nav-dropdown-link-hover-color);
}

/* Mega panel — hidden by default (no .dropdown-menu class, so Bootstrap's CSS
   doesn't handle this). Shown via Bootstrap Dropdown plugin (.show) or CSS hover. */
.component-navbar-primary .dropdown-panel--mega {
    display: none;
}

.component-navbar-primary .dropdown-panel--mega.show {
    display: block;
}

/* Hover-open — desktop only (lg+).
   .is-hovering is applied by the accessibility JS module on mouseenter and removed
   on mouseleave. It is intentionally separate from Bootstrap's .show class so the
   two mechanisms don't interfere:
     Click  → Bootstrap manages .show (open on click, close on click-outside)
     Hover  → JS manages .is-hovering (visible while mouse is over the dropdown)
   CSS :hover is NOT used here — letting the browser pseudo-class control visibility
   independently of Bootstrap's state causes the click-hold behavior to break. */
@media (min-width: 992px) {
    .component-navbar-primary .navbar-nav .dropdown.is-hovering > .dropdown-menu,
    .component-navbar-primary .navbar-nav .dropdown.is-hovering > .dropdown-panel--mega {
        display: block;
    }
}

/* Panel width is controlled entirely by the width= param → a --w-* modifier
   class. See DROPDOWN PANEL WIDTH MODES further down for the positioning rules,
   the :has() containing-block switch, and the tokens.

   Historical note: this is where .dropdown-panel--fluid used to live, together
   with an UNCONDITIONAL .nav-item:has(> .dropdown-panel--mega) { position:
   static } that applied to every mega panel whether or not fluid=true was
   passed. That rule is why a clicked parent dropdown appeared pinned to <nav>
   instead of to its own <li>: it removed the <li>'s positioning context while
   the mega panel itself never declared a `left`, so the panel took its
   vertical origin from <nav>. Both are replaced by the explicit width modes;
   fluid=true is still accepted by the templates and maps to width=container. */

/* ------------------------------------------------------------
   SHARED PANEL CHROME — the single definition of what a first-level primary-nav
   dropdown panel looks like. Every panel type (columns-1/2/3, mega, members,
   headlines) carries `dropdown-panel` and takes all of it, so panel TYPE and
   panel WIDTH never change the surface, the inset, or the vertical origin. A
   variant block below should only add what is genuinely specific to that
   variant — its token hooks and its own inner layout.

   Bootstrap's .dropdown-menu supplies competing structural defaults (padding
   .5rem 0, a 1px border, a border-radius, min-width 10rem, and the
   [data-bs-popper] spacer). Those are all single-class rules and lose to these
   two-class selectors — but only for an element that actually carries the base
   class. A panel rendered WITHOUT it silently keeps the whole Bootstrap look,
   which is how the columns-N panel came to be the one uninset, bordered,
   round-cornered, shadowless, spacer-offset panel while its siblings were not.

   These declarations were previously copy-pasted across the --mega, --members
   and --headlines blocks, which is why they had already drifted: mega set
   border-radius: 0 and the other two did not, so members/headlines kept
   Bootstrap's rounded corners; mega set padding: 1rem a second time; and
   members/headlines needed an explicit margin-top: 0 that mega did not.
   ------------------------------------------------------------ */
.component-navbar-primary .dropdown-panel {
    padding:       1rem;
    /* Square, borderless surface — the shadow separates the panel from the page,
       not a border. Resets Bootstrap's border + radius + 10rem floor so a width
       mode is free to size the panel from its content. */
    border:        none;
    border-radius: 0;
    min-width:     0;
}

/* Mobile — the panel is in-flow inside .navbar-collapse and its CONTENT owns the
   inset: per-column padding on .dropdown-panel-col, the grid gap on the
   headlines/members grids, and the margin-left indent on .dropdown-menu-list /
   .dropdown-panel-members-list. Stacking the 1rem on top would double the
   visual indent. Bootstrap forces position:static here, so no positioning
   belongs in this block either. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel {
        padding: 0;
    }
}

/* Desktop — vertical origin and elevation, shared by every panel type and every
   width mode. Deliberately declares NO `left`/`right`/`width`: horizontal
   placement is the width modifier's job (see DROPDOWN PANEL WIDTH MODES), and a
   `left` here would defeat the left:50% centring the full-bleed modes depend on.

   margin-top: 0 is the point of consistency — every panel opens FLUSH beneath
   its trigger. The mega/members/headlines panels already did; a columns-N panel
   was the exception, taking Bootstrap's --bs-dropdown-spacer via the non-Popper
   rule above and so hanging a couple of px lower than a mega panel on the same
   navbar. The spacer rule now excludes panels for that reason.

   The scroll cap keeps a long panel from running off the bottom of the
   viewport; --cg-nav-panel-offset is the header height to subtract. */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel {
        position:   absolute;
        top:        100%;
        margin-top: 0;
        z-index:    1050;
        box-shadow: var(--cg-nav-dropdown-shadow);
        max-height: calc(100vh - var(--cg-nav-panel-offset, 100px));
        overflow-y: auto;
    }
}

/* Panel header image — the poweredby node's thumbnail, rendered centered at the
   top of any dropdown panel (columns / mega / members / headlines) when the node
   has a thumbnailURL. Centering comes from the .text-center on the wrapper. */
.component-navbar-primary .dropdown-panel-header-img {
    max-width:     100%;
    height:        auto;
    margin-bottom: 0.75rem;
    /* Rounded corners to match the card thumbnails (same token as
       .dropdown-panel-col-header-img). */
    border-radius: var(--bs-border-radius, 0.375rem);
}

/* Dropdown heading images (panel header + mega column / sub-group headers) are a
   desktop flourish — hide them below lg so the collapsed mobile menu stays a
   compact text list. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-header,
    .component-navbar-primary .dropdown-panel-col-header-img {
        display: none;
    }
}

/* mega — grouped column layout panel.
   .dropdown-menu is added to the panel element so Bootstrap's Dropdown plugin
   can find it via SelectorEngine.next(toggle, '.dropdown-menu').

   Surface, inset, elevation and vertical origin all come from SHARED PANEL
   CHROME — this block is only the mega panel's token hooks. */
.component-navbar-primary .dropdown-panel--mega {
    /* Custom property hooks — override in theme.css */
    --cg-mega-col-header-size:        1rem;
    --cg-mega-col-header-weight:      700;
    --cg-mega-col-header-transform:   none;
    --cg-mega-col-header-spacing:     normal;
    --cg-mega-col-divider:            1px solid var(--cg-nav-dropdown-divider-color);
    --cg-mega-col-padding-x:          1rem;
    --cg-mega-col-padding-y:          1rem;
}

/* Mobile panel padding is zeroed once on the base .dropdown-panel class (see the
   shared panel inset above) — the mega variant needs no override of its own, so
   the per-column .dropdown-panel-col padding remains the sole mobile inset. */

/* Mobile — neutralise the panel's inner .container wrapper.
   The mega / members / headlines panels wrap their content in a .container so
   that on DESKTOP the content stays framed to the page width even when the
   panel surface runs edge-to-edge (width=container-fluid). Inside the collapsed
   navbar there is no page frame to align to, and the panel's own padding is
   already zeroed above, so the wrapper's gutter would introduce a second inset
   on top of the per-column padding. Strip it below the expand breakpoint so the
   mobile menu keeps its flush rhythm. Direct child only — a .container used
   inside embedded component markup is left alone. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel--mega > .container,
    .component-navbar-primary .dropdown-panel--members > .container,
    .component-navbar-primary .dropdown-panel--headlines > .container {
        max-width:     none;
        padding-left:  0;
        padding-right: 0;
        margin-left:   0;
        margin-right:  0;
    }
}

/* Desktop positioning, elevation and the scroll cap for the mega panel are the
   shared ones — see SHARED PANEL CHROME. This variant adds nothing at lg+. */

/* ------------------------------------------------------------
   members panel (.dropdown-panel--members)
   A single <ul> laid out as a responsive grid of logo + label cells, powered
   by an image-navbar (the navbar-members node). Carries .dropdown-menu, so the
   hover-open rule (.is-hovering > .dropdown-menu) and Bootstrap's click .show
   already drive open/close; width defaults to container like the mega panel.
   Surface, inset, elevation and vertical origin come from SHARED PANEL CHROME —
   including the border/min-width reset that keeps Bootstrap's .dropdown-menu
   defaults from intruding on the grid.
   ------------------------------------------------------------ */
.component-navbar-primary .dropdown-panel--members {
    /* Custom property hooks — override in theme.css. The list padding is the
       members counterpart to the mega panel's per-column padding: a single
       inset on the <ul> (desktop only) so both panel types share the same
       inset rhythm on top of the panel's own 1rem. The <li>/links themselves
       stay tight like normal menu items — only this wrapper is padded. */
    --cg-members-panel-list-padding: 1rem;   /* desktop-only inset on the <ul> */
    --cg-members-panel-col-gap:      1rem;   /* horizontal space between columns */
    --cg-members-panel-logo-size:    2.25rem;
}

/* The <ul> IS the grid. Column count per breakpoint comes from inline
   --cg-members-cols* custom properties the template sets from the cols /
   colsSm / colsMd / colsLg params (defaults 1 / 2 / 3 / 4). Every cell stays
   in one list so it collapses cleanly in the navbar-collapse. minmax(0,1fr)
   lets long labels wrap instead of forcing the track wider. Rows stay tight
   (the link's own padding supplies the rhythm, like normal menu items); only
   columns get a gap. The desktop inset padding is added at lg+ below. */
.component-navbar-primary .dropdown-panel-members-list {
    display: grid;
    grid-template-columns: repeat(var(--cg-members-cols, 1), minmax(0, 1fr));
    column-gap: var(--cg-members-panel-col-gap);
}

@media (min-width: 576px) {
    .component-navbar-primary .dropdown-panel-members-list {
        grid-template-columns: repeat(var(--cg-members-cols-sm, 2), minmax(0, 1fr));
    }
}

@media (min-width: 768px) {
    .component-navbar-primary .dropdown-panel-members-list {
        grid-template-columns: repeat(var(--cg-members-cols-md, 3), minmax(0, 1fr));
    }
}

@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel-members-list {
        grid-template-columns: repeat(var(--cg-members-cols-lg, 4), minmax(0, 1fr));
        /* Desktop-only inset — the members counterpart to the mega col padding.
           Below lg (the collapsed/mobile menu) this is omitted: the panel
           padding is zeroed and the links carry the mobile touch padding. */
        padding: var(--cg-members-panel-list-padding);
    }
}

.component-navbar-primary .dropdown-panel-members-link {
    display:         flex;
    align-items:     center;
    gap:             0.6rem;
    padding:         0.3rem 0;
    text-decoration: none;
}

.component-navbar-primary .dropdown-panel-members-link:hover,
.component-navbar-primary .dropdown-panel-members-link:focus {
    background-color: var(--cg-nav-dropdown-item-hover-bg);
}

/* Logo — fixed square frame, image contained (logos vary in ratio). */
.component-navbar-primary .dropdown-panel-members-logo {
    flex:            0 0 var(--cg-members-panel-logo-size);
    width:           var(--cg-members-panel-logo-size);
    height:          var(--cg-members-panel-logo-size);
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
}

.component-navbar-primary .dropdown-panel-members-logo img {
    max-width:  100%;
    max-height: 100%;
    object-fit: contain;
}

/* Label — inherits the menu tree's typography (no uppercase / letter-spacing /
   bold), so member names read like every other dropdown link. The link colour
   comes from the shared `.dropdown-menu a` rule above. */
.component-navbar-primary .dropdown-panel-members-label {
    line-height: 1.2;
}

/* Desktop positioning, elevation and the scroll cap that keep a long member list
   from running off the viewport are the shared ones — see SHARED PANEL CHROME.
   Neither the members nor the headlines panel adds anything at lg+. */

/* Flush inside the collapse — the grid gap supplies the rhythm. The zeroing is
   done once on the base .dropdown-panel class (see the shared panel inset near
   the top of this section), so these two variants need no override here. */

/* ------------------------------------------------------------
   headlines panel (.dropdown-panel--headlines)
   A headlines feed (loki/headlines) presented as the shared "More News" card
   component (component-headlines-card-template) embedded in the dropdown —
   a carousel that pages through rows of cards, with an archive button +
   prev/next controls in the footer. Carries .dropdown-menu, so the hover-open
   rule (.is-hovering > .dropdown-menu) and Bootstrap's click .show already
   drive open/close; the desktop self-positioning + mobile flush-padding are
   shared with the members panel above. Width defaults to container so the
   card row gets page-width room; width=auto will squeeze it.
   Surface, inset, elevation and vertical origin — including the border/min-width
   reset off the inherited .dropdown-menu — come from SHARED PANEL CHROME, so
   this variant declares nothing of its own on the panel element.
   ------------------------------------------------------------ */

/* Force the embedded card grid to a FIXED N-up single row (one row per
   carousel page) instead of its native auto-fit behavior, so the dropdown
   shows a tidy row of cards. Column count per breakpoint comes from the
   inline --cg-nav-headlines-cols* the template sets from the cols / colsSm /
   colsMd / colsLg params (defaults 1 / 2 / 2 / 4). minmax(0,1fr) lets cards
   shrink rather than force the track wider. The extra .component-headlines in
   the selector lifts specificity above the card template's own auto-fit rule
   (.component-headlines.layout-card .cg-headlines-grid), which would otherwise
   win on equal specificity since it loads later in this file. */
.component-navbar-primary .dropdown-panel--headlines .component-headlines .cg-headlines-grid {
    grid-template-columns: repeat(var(--cg-nav-headlines-cols, 1), minmax(0, 1fr));
}

@media (min-width: 576px) {
    .component-navbar-primary .dropdown-panel--headlines .component-headlines .cg-headlines-grid {
        grid-template-columns: repeat(var(--cg-nav-headlines-cols-sm, 2), minmax(0, 1fr));
    }
}

@media (min-width: 768px) {
    .component-navbar-primary .dropdown-panel--headlines .component-headlines .cg-headlines-grid {
        grid-template-columns: repeat(var(--cg-nav-headlines-cols-md, 2), minmax(0, 1fr));
    }
}

@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel--headlines .component-headlines .cg-headlines-grid {
        grid-template-columns: repeat(var(--cg-nav-headlines-cols-lg, 4), minmax(0, 1fr));
    }
}

/* ============================================================
   DROPDOWN PANEL WIDTH MODES  (width= param → modifier class)

   ONE param controls panel width. Values borrow Bootstrap's container
   vocabulary so they read the way a theme dev already thinks:

     width=auto             .dropdown-panel--w-auto
                            Collapses to its content — a single-column panel
                            with a few links stays narrow. Floor of 300px,
                            ceiling of 90vw. Anchored under its own <li>.
     width=container        .dropdown-panel--w-container
                            Panel box capped to the site container width and
                            centred over the navbar, so its edges line up with
                            page content.
     width=container-fluid  .dropdown-panel--w-container-fluid
                            Edge-to-edge 100vw, breaking out of a navbar that
                            sits inside a container (which is how both shipped
                            themes build it — this is the width that used to
                            need a per-theme hack).

   Panel type (panel=columns-1|mega|members|headlines) and panel width are
   orthogonal, which is why width is its own param rather than more `panel`
   values: any type can take any width. Default is `auto` for every type.

   FIRST-LEVEL PANELS ONLY. The modifier belongs on the one element that hangs
   off a .nav-item — "dropdown-menu dropdown-panel dropdown-panel--mega
   dropdown-panel--w-*" and its members/headlines/columns equivalents. Mega
   sub-groups are nested <li>/<ul> inside that panel (rendered by the recursive
   mega-col template) and size to it; they must never carry a --w-* class, or a
   second positioning context appears inside an already positioned panel. The
   :has(> …) selectors below are direct-child scoped for the same reason.

   MOBILE IS UNTOUCHED. Every rule here is inside @media (min-width: 992px) —
   the navbar-expand-lg breakpoint. Below it the panel is in-flow inside
   .navbar-collapse and must not be positioned at all. The templates likewise
   never let width= change any markup that renders below lg.

   THIS BLOCK MUST STAY AFTER the SHARED PANEL CHROME block above. That block
   sets min-width:0 and position/top with the same two-class specificity as the
   rules here, so source order is what makes a width mode win. Moving this block
   earlier silently breaks --w-auto's min-width. (The per-variant blocks used to
   be the thing to stay after; they no longer declare either property.)

   Tokens (override in theme.css or inline per panel):
     --cg-nav-dropdown-auto-min-width   default 300px
     --cg-nav-dropdown-auto-max-width   default 90vw
     --cg-nav-dropdown-container-width  default var(--ps-site-width) → 1320px
     --cg-nav-dropdown-fluid-width      default 100vw
   ============================================================ */

/* Width-mode tokens, declared on the component so they are discoverable and
   overridable in one place. Override in theme.css for a site-wide change, or
   inline on a single panel via style="--cg-nav-dropdown-auto-min-width:360px".
   Declared outside a media query so the values are available at every
   breakpoint even though the positioning rules are lg+ only. */
.component-navbar-primary {
    --cg-nav-dropdown-auto-min-width:  300px;
    --cg-nav-dropdown-auto-max-width:  90vw;
    --cg-nav-dropdown-container-width: var(--ps-site-width, 1320px);
    --cg-nav-dropdown-fluid-width:     100vw;
}

/* Positioning context. A width mode that spans wider than the trigger needs
   the <li> to stop being the containing block so the panel can resolve
   against .navbar; --w-auto needs the opposite.

   The last selector is the compatibility hinge: a mega panel with NO width
   mode keeps its historical position:static (i.e. it anchors to <nav>, not
   to its <li>). That is what Reign's mega menus have always rendered, so it
   is preserved verbatim. Opt into the corrected per-<li> anchoring with
   width=auto. */
@media (min-width: 992px) {
    .component-navbar-primary .nav-item:has(> .dropdown-panel--w-auto) {
        position: relative;
    }

    .component-navbar-primary .nav-item:has(> .dropdown-panel--w-container),
    .component-navbar-primary .nav-item:has(> .dropdown-panel--w-container-fluid) {
        position: static;
    }
}

/* auto — as wide as the content, floor of 300px, ceiling so a long label
   can't run off screen. Anchored under the trigger. Panels near the right
   edge can be flipped by adding Bootstrap's .dropdown-menu-end alongside
   the modifier (Popper is disabled on these toggles, so there is no
   automatic flip). */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel--w-auto {
        position:  absolute;
        top:       100%;
        width:     max-content;
        min-width: var(--cg-nav-dropdown-auto-min-width, 300px);
        max-width: var(--cg-nav-dropdown-auto-max-width, 90vw);
    }

    .component-navbar-primary .dropdown-panel--w-auto:not(.dropdown-menu-end) {
        left:  0;
        right: auto;
    }

    .component-navbar-primary .dropdown-panel--w-auto.dropdown-menu-end {
        left:  auto;
        right: 0;
    }
}

/* container — the panel box itself is capped to the site container width and
   centred over the navbar, so its edges line up with page content rather
   than with the trigger or the viewport. --ps-site-width is emitted per site
   by inc-vars, so this tracks each client's configured width automatically. */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel--w-container {
        position:  absolute;
        top:       100%;
        left:      50%;
        right:     auto;
        width:     100%;
        max-width: var(--cg-nav-dropdown-container-width, 1320px);
        /* Pull back by half the panel's RENDERED width to centre it. The panel is
           min(navbar width, container width) wide, so the offset has to be the
           smaller of 50% (percentages resolve against the navbar) and half the
           container width — that keeps it centred whether the navbar is wider or
           narrower than the container. Division is wrapped in calc() for the
           widest support inside min(). The --ps-site-width linkage lives in the
           token declaration above, so it is not repeated here. */
        margin-left: calc(-1 * min(50%, calc(var(--cg-nav-dropdown-container-width, 1320px) / 2)));
        /* border-radius: 0 / min-width: 0 are not repeated here — SHARED PANEL
           CHROME sets both for every panel. */
    }
}

/* container-fluid — edge-to-edge, regardless of how the navbar is constrained.
   Both shipped themes nest <nav> inside a .container / .container-xxl, so
   spanning the navbar only ever reached container width; this is the mode that
   previously required a per-theme override.

   left:50% + a negative viewport margin is the standard break-out; it leaves
   `transform` free for any panel animation. Assumes the navbar is centred in
   the viewport, which is the normal case. If the page shows a vertical
   scrollbar, 100vw includes it and can produce a few px of horizontal
   overflow — set --cg-nav-dropdown-fluid-width to 100% (when the navbar is
   already full-bleed) or to a calc() that subtracts the scrollbar. */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel--w-container-fluid {
        position:    absolute;
        top:         100%;
        left:        50%;
        right:       auto;
        width:       var(--cg-nav-dropdown-fluid-width, 100vw);
        max-width:   var(--cg-nav-dropdown-fluid-width, 100vw);
        margin-left: calc(-0.5 * var(--cg-nav-dropdown-fluid-width, 100vw));
        /* border-radius: 0 / min-width: 0 are not repeated here — SHARED PANEL
           CHROME sets both for every panel. */
    }
}

/* Column layout — override Bootstrap's col-6/col-sm-4 breakpoints so columns
   stack full-width on mobile, go 3-up at sm, and equal-width auto at lg+.
   Per-column padding is kept at every breakpoint so the inter-column
   spacing survives when the columns stack vertically on mobile. The
   "double indent" on mobile is solved at the outer-panel level
   instead (see the mobile override for .dropdown-panel--mega below). */
.component-navbar-primary .dropdown-panel-col {
    flex: 0 0 100%;
    width: 100%;
    padding: var(--cg-mega-col-padding-y) var(--cg-mega-col-padding-x);
}

@media (min-width: 576px) {
    .component-navbar-primary .dropdown-panel-col {
        flex: 0 0 33.333333%;
        width: 33.333333%;
    }
}

/* Below the expand breakpoint (collapsed menu): stack every mega column full-width
   so columns read as one vertical list on phones/tablets, overriding the 576px
   3-up rule above. Placed after it so it wins across the whole mobile range. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-col {
        flex: 0 0 100%;
        width: 100%;
    }

    /* Right-align the dropdown control indicator on mobile.
       Case 3: make the toggle a flex row so the label text stays left and the
       ::after caret is pushed to the right edge.
       Case 2 (split button): the .nav-dropdown-chevron is a flex sibling of the
       label in the d-flex <li>, but it otherwise packs left next to the label —
       push it to the right edge too. */
    .component-navbar-primary .nav-link.dropdown-toggle {
        display:     flex;
        align-items: center;
    }
    /* Right-align both indicators, inset 0.75rem (--cg-nav-link-px) from the
       row edge so they line up with the menu's other navigation icons.
       Case 3 caret: the sibling .nav-link gets padding-inline:0 on mobile
       (above), so without this the caret would sit flush against the edge.
       margin-right restores the same 0.75rem inset the chevron gets from its
       own right padding, so the two indicators align. */
    .component-navbar-primary .nav-link.dropdown-toggle::after {
        margin-left:  auto;
        margin-right: var(--cg-nav-link-px);
    }
    /* Case 2 chevron: push the button to the right edge; its base
       padding-right (--cg-nav-link-px) supplies the matching 0.75rem inset. */
    .component-navbar-primary .nav-dropdown-chevron {
        margin-left: auto;
    }

    /* Stack columns-2 / columns-3 flat-list panels into a single column on mobile
       (they keep their CSS column-count at all widths otherwise). */
    .component-navbar-primary .dropdown-panel--columns-2 .dropdown-menu-list,
    .component-navbar-primary .dropdown-panel--columns-3 .dropdown-menu-list {
        column-count: 1;
    }
}

/* ============================================================
   COMPONENT: component-search (Themes 2.1)
   Template:  generic-cld/component-site-search-template (opt-in; the
              shared component-search-template stays the default)
   Site search, layout=input (the field inline) or layout=modal (a
   magnifier button opening a Bootstrap modal). The inline field and its
   submit button join into one control, as the 2.0 search drew them: the
   field keeps Bootstrap's form-control, minus its right border, and the
   button is a bordered button on a white ground in the link colour. The
   2.0 markup did that with Bootstrap's !important border-end-0 / bg-white
   / border utilities; these rules do it through hooks, so a theme restyles
   the control with ordinary rules or by setting the tokens. Declared
   ahead of the primary nav's .mmf-search rules below, which still win in
   the mobile menu.
   ------------------------------------------------------------
   --cg-search-btn-bg          submit button ground (inline field)
   --cg-search-btn-color       submit button ink, the magnifier
   --cg-search-btn-color-hover
   --cg-search-border-color    the button's border
   ============================================================ */
.component-search.layout-input .cg-search-input {
    border-right-width: 0;
}
.component-search.layout-input .cg-search-btn {
    background-color: var(--cg-search-btn-bg, #fff);
    color:            var(--cg-search-btn-color, var(--bs-link-color, #0d6efd));
    border:           var(--bs-border-width, 1px) solid var(--cg-search-border-color, var(--bs-border-color, #dee2e6));
}
.component-search.layout-input .cg-search-btn:hover,
.component-search.layout-input .cg-search-btn:focus-visible {
    color: var(--cg-search-btn-color-hover, var(--bs-link-hover-color, #0a58ca));
}
/* The button text, when it shows, sits after the magnifier. */
.component-search .cg-search-icon + .cg-search-btn-text:not(.visually-hidden) {
    margin-inline-start: 0.5rem;
}

/* Mobile slideout search (.mmf-search in the mobile-menu-footer). The input renders
   on a white background but inherits the dark navbar scheme's light --bs-body-color,
   so typed text is nearly invisible. Force dark input text + a muted placeholder, and
   theme the submit button (primary color via --body-link) so it reads like the site's
   other buttons instead of the muted default. The footer is d-lg-none, so these only
   apply in the mobile menu. */
.component-navbar-primary .mmf-search .form-control {
    color: var(--bs-dark, #212529);
}
.component-navbar-primary .mmf-search .form-control::placeholder {
    color:   var(--bs-secondary-color, #6c757d);
    opacity: 1;
}
.component-navbar-primary .mmf-search .btn {
    color:            var(--body-link, var(--bs-primary));
    background-color: #fff;
    border-color:     var(--bs-border-color, rgba(0, 0, 0, 0.175));
}
.component-navbar-primary .mmf-search .btn:hover,
.component-navbar-primary .mmf-search .btn:focus-visible {
    color:            var(--body-link-hover, var(--bs-primary));
    background-color: var(--bs-tertiary-bg, rgba(0, 0, 0, 0.03));
}

/* Desktop columns divide the panel evenly (flex-basis 0), so a mega menu with
   six columns gives each one a sixth of the width no matter how narrow that
   gets. --cg-nav-panel-col-min-width puts the same 300px floor under a COLUMN
   that --cg-nav-dropdown-auto-min-width puts under a width=auto PANEL, so a
   column never collapses to a squeezed strip of wrapped labels.

   The floor turns .dropdown-panel-cols (a Bootstrap .row, so already
   flex-wrap: wrap) into a genuinely responsive grid: once the columns can no
   longer fit at 300px each, the surplus wraps to a second row rather than
   everything getting thinner. Columns on that last row still flex-grow, so
   they share the leftover width between them.

   Deliberately lg+ only. Below the expand breakpoint every column is
   flex: 0 0 100% inside the collapsed menu, where a 300px floor would risk
   horizontal overflow on the narrowest handsets. */
@media (min-width: 992px) {
    .component-navbar-primary .dropdown-panel-col {
        flex: 1 0 0%;
        width: auto;
        min-width: var(--cg-nav-panel-col-min-width, 300px);
    }
}

/* Column header — section title link. No bottom divider at any
   breakpoint; the inter-column whitespace (desktop) and the
   stacked rhythm of headers + items (mobile) provide enough
   separation on their own. */
.component-navbar-primary .dropdown-panel-col-header {
    margin-bottom: 0.5rem;
}

/* Optional heading image — rendered by the mega template above the header text
   when the secondaryURL entry carries a thumbnail. Full column width, natural
   height, rounded to match the card thumbnails; applies at all breakpoints. */
.component-navbar-primary .dropdown-panel-col-header-img {
    display:       block;
    width:         100%;
    height:        auto;
    margin-bottom: 1rem;
    border-radius: var(--bs-border-radius, 0.375rem);
}

.component-navbar-primary .dropdown-panel-col-header-link {
    display: block;
    font-size:      var(--cg-mega-col-header-size);
    font-weight:    var(--cg-mega-col-header-weight);
    text-transform: var(--cg-mega-col-header-transform);
    letter-spacing: var(--cg-mega-col-header-spacing);
    color: inherit;
    text-decoration: none;
    padding: 0;
}

/* Header links do NOT underline on hover (consistent with the Bootstrap menu
   foundation); they keep their base text-decoration:none and get a color-change
   hover from the panel's a:hover rule. The gradient underline below is their
   standing "heading" affordance. */

/* Bootstrap .dropdown-header (e.g. a "Social Networking" section label inside a
   panel) — style it like .dropdown-panel-col-header-link so panel section headers
   read consistently: same token-driven size/weight/transform/spacing, no extra
   Bootstrap padding, and it inherits the panel's text color (matching the col
   header). */
.component-navbar-primary .dropdown-header {
    font-size:      var(--cg-mega-col-header-size);
    font-weight:    var(--cg-mega-col-header-weight);
    text-transform: var(--cg-mega-col-header-transform);
    letter-spacing: var(--cg-mega-col-header-spacing);
    color:          inherit;
    padding:        0;
    margin-bottom:  0.5rem;
}

/* Heading emphasis underline — a 2px rule under each panel header that fades from
   the brand secondary color (the standard body link-hover color) on the left to
   transparent on the right, so headers read as headings rather than just another
   link. Rendered as a block ::after (never conflicts with the header's bg/border)
   and applied at all breakpoints (carries to mobile). --bs-secondary is used
   directly because the dark-panel scope remaps --body-link-hover to a light color;
   --bs-secondary (the brand secondary) is not overridden there. The <h6>
   .dropdown-header needs no wrapper — its margin + this ::after match the spacing
   of the .dropdown-panel-col-header div. */
.component-navbar-primary .dropdown-panel-col-header::after,
.component-navbar-primary .dropdown-header::after {
    content:          "";
    display:          block;
    height:           2px;
    margin-top:       0.35rem;
    background-image: linear-gradient(to right, var(--cg-nav-dropdown-header-underline), transparent);
}

/* Column items — child links.
   font-size intentionally inherits (1rem) — not reduced, per design spec.
   Padding comes from the shared panel-link rule near the end of this
   section, so every clickable row in a panel matches. */
.component-navbar-primary .dropdown-panel-col-items .dropdown-item {
    font-size: inherit;
    background-color: transparent;
}

/* Hover / focus background is handled once in the PANEL HOVER / FOCUS section
   near the end of this component — not per list. */

/* Mobile — the panel-link padding and the .dropdown-panel-col-items /
   .dropdown-panel-sub-items indent are NOT overridden here any more: both
   are breakpoint-independent now (see the shared panel-link rule at the end
   of this section), so a panel reads the same stacked or open.

   What stays mobile-only is indenting the two lists that have no nested
   parent on desktop. .dropdown-menu-list is a columns-N panel's flat list and
   .dropdown-panel-members-list is the logo grid — neither sits under a
   section header on desktop, so an indent there would just look like a stray
   inset. Inside the collapsed menu they DO hang off their parent nav item,
   so the indent earns its place. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-menu-list,
    .component-navbar-primary .dropdown-panel-members-list {
        margin-left: var(--cg-nav-panel-indent);
    }
}

/* Divider rule between direct <li> children of .dropdown-panel-col-items.
   Mirrors the .dropdown-panel-sub-group--column border-top pattern
   so plain leaf items and sub-groups share the same visual rhythm.
   The first <li> in each <ul> skips the border so the column doesn't
   open with a stray rule above the first row.

   The <li> deliberately contributes NO padding — the row's inset is owned
   entirely by the .dropdown-item anchor inside it (--cg-nav-panel-link-py), so
   every clickable row in a panel is the same height and the whole row stays
   clickable. This <li> used to add 0.25rem top and bottom, which stacked on top
   of the anchor's own padding: 36px first rows and 40px later ones against the
   30px of an identical .dropdown-panel-sub-items row. */
.component-navbar-primary .dropdown-panel-col-items > li {
    border-top: var(--cg-mega-col-divider);
}

.component-navbar-primary .dropdown-panel-col-items > li:first-child {
    border-top: none;
}

/* Sub-group — level 3+ entries within a mega column.
   Renders when a mega-col entry itself has children (secondaryURL).
   The sub-header acts as a section divider within the column. */
.component-navbar-primary .dropdown-panel-sub-group {
    margin-top: 0.75rem;
}

.component-navbar-primary .dropdown-panel-sub-group:first-child {
    margin-top: 0;
}

/* Sub-group header — the SAME heading treatment as .dropdown-panel-col-header,
   which is the single source of truth for panel headings. Matches its lone
   margin-bottom and, like it, carries no divider and no breakpoint divergence:
   a heading reads identically stacked on mobile and open on desktop.
   It deliberately does NOT get the col header's ::after gradient underline —
   that rule is specific to the column heading at the top of a column. */
.component-navbar-primary .dropdown-panel-sub-header {
    margin-bottom: 0.5rem;
}

/* Mobile — drop the heading gap. In the collapsed menu the sub-header is not a
   heading floating above a group any more, it is a TAP TARGET in a vertical
   stack of tap targets, and the 0.5rem reads as a hole in that stack. The rows
   on either side already carry --cg-nav-panel-link-py, so removing it leaves the
   sub-header at exactly the same rhythm as every other row and condenses the
   menu. Desktop keeps the gap: there the header really is a heading. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-sub-header {
        margin-bottom: 0;
    }
}

@media (max-width: 991.98px) {
    /* Mobile sub-group collapse — sub-items are hidden until the user
       taps the .dropdown-panel-sub-header-link. The JS module
       (navbar-primary-accessibility.js) toggles `.show` on the parent
       .dropdown-panel-sub-group on click. !important defeats the
       inline d-flex utility class on the <ul> when collapsed; once
       .show is added, this rule no longer applies and the original
       display value (flex from d-flex, or block) is restored. */
    .component-navbar-primary .dropdown-panel-sub-group:not(.show) .dropdown-panel-sub-items {
        display: none !important;
    }

    /* Header link grows a chevron caret on mobile so the user knows
       it expands. The link is a flex row; the caret uses
       margin-left:auto to land cleanly at the right edge of the link
       (mirrors the parent dropdown-toggle's far-right caret pattern,
       well clear of the label text). Rotates 180° when the parent
       group is .show. */
    /* Padding, `display: flex` and the non-bold weight are NOT set here — all
       three have to be declared AFTER the base .dropdown-panel-sub-header-link
       typography rule and the shared panel-link padding rule further down, both
       of which match this selector at the same two-class specificity and would
       otherwise win on source order. They live in the mobile block that sits
       immediately after those rules; see MOBILE SUB-HEADER OVERRIDES.

       This is not hypothetical: `display: flex` WAS declared here, and the base
       rule's `display: block` overrode it, which silently disabled the
       margin-left:auto on the caret below — an auto margin does nothing in a
       block container, so the chevron sat against the label instead of at the
       right edge of the row. */
    .component-navbar-primary .dropdown-panel-sub-header-link::after {
        margin-left:  auto;
        padding-left: 0.5rem;
        font-family:  var(--cg-icon-font);
        font-weight:  900;
        content:      "\f078";   /* fa-chevron-down */
        font-size:    0.7em;
        transition:   transform 0.2s ease;
    }
    .component-navbar-primary .dropdown-panel-sub-group.show > .dropdown-panel-sub-header .dropdown-panel-sub-header-link::after {
        transform: rotate(180deg);
    }

    /* Open-state visual indicator on the column variant —  when a
       .dropdown-panel-sub-group--column is .show, the inner
       <ul.dropdown-panel-sub-items> grows a 3px left border using
       the same divider color the rest of the mega panel uses.
       padding-left gives the items breathing room from the border;
       margin-left insets the whole stack so the border sits inside
       the column rhythm rather than flush against the panel edge.
       Reads as "these items belong to the header above". */
    .component-navbar-primary .dropdown-panel-sub-group--column.show > .dropdown-panel-sub-items {
        margin-bottom:  1rem !important;
    }

    /* Drop the row padding on mobile — the borders alone are enough
       to separate stacked rows; the padding eats vertical rhythm at small
       widths. .dropdown-panel-col-items > li is no longer listed here: it
       carries no padding at any breakpoint now, the anchor owns the row inset. */
    .component-navbar-primary .dropdown-panel-sub-group--column {
        padding-top:    0;
        padding-bottom: 0;
    }
}

/* Same typography set as .dropdown-panel-col-header-link, the source of truth
   for panel headings — the token-driven size / weight / transform / spacing, so
   a sub-header inherits whatever heading treatment a theme gives its column
   headers (Reign, for example, uppercases both) instead of rendering as plain
   body text. Applied at every breakpoint: no desktop-only weight, no mobile
   exception, so the heading reads the same stacked or open.

   `padding` is intentionally NOT reset to 0 the way the col-header link does
   it: this link is one of the three panel link types that share
   --cg-nav-panel-link-* padding, which lines its text up with the sub-items
   directly beneath it. The shared rule further down owns that. */
.component-navbar-primary .dropdown-panel-sub-header-link {
    display:         block;
    font-size:       var(--cg-mega-col-header-size);
    font-weight:     var(--cg-mega-col-header-weight);
    text-transform:  var(--cg-mega-col-header-transform);
    letter-spacing:  var(--cg-mega-col-header-spacing);
    color:           inherit;
    text-decoration: none;
}

/* No underline-on-hover for the sub-header link (Bootstrap menu consistency);
   base text-decoration:none stands and the panel a:hover handles the color. */

/* Padding comes from the shared panel-link rule below. */
.component-navbar-primary .dropdown-panel-sub-items .dropdown-item {
    font-size: inherit;
    background-color: transparent;
}

/* Text color on hover/focus inside an INLINE sub-group. The GROUP carries the
   background (see PANEL HOVER / FOCUS below), so the children only need a color
   that reads against it — which is just the panel's standard link-hover color.

   This was a hardcoded var(--bs-white, #fff). That assumed a dark panel: reign
   remaps --bs-dropdown-bg and the hover surface, so white worked there, but any
   theme on the generic light-panel defaults (catalog) painted white text over a
   6% black overlay — invisible. The token resolves correctly in both: #ededed
   on reign's dark panel, --body-link-hover on a light one.

   Because the value is now theme-safe, the selector matches the same
   :has(> .dropdown-panel-sub-items.d-flex.flex-row) test as the background
   rules, so text and background travel together. The value equals what the
   canonical `.dropdown-menu a:hover` rule already yields; this rule stays
   because that one covers :hover only, and this also carries focus. */
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-items .dropdown-item:hover,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-items .dropdown-item:focus {
    color: var(--cg-nav-dropdown-link-hover-color);
}

/* Flex-row sub-item list — scale contents down 10% and vertically center
   each <li> so icons align consistently within their row. */
.component-navbar-primary .dropdown-panel-sub-items.d-flex {
    font-size: 0.9em;
}

.component-navbar-primary .dropdown-panel-sub-items.d-flex > li {
    display: flex;
    align-items: center;
}

/* Icon sentinel items — label wrapped in [brackets] in the CMS.
   Renders a Font Awesome brand icon with visually-hidden text.
   Sized to be tap-friendly; background suppressed so the icon reads clearly.
   Apply font-size override in theme.css via --cg-nav-dropdown-icon-item-size. */
.component-navbar-primary .dropdown-item--icon {
    --cg-nav-dropdown-icon-item-size: 1.25rem;
    font-size:    var(--cg-nav-dropdown-icon-item-size);
    padding:      0.3rem 0.25rem;
    line-height:  1;
    background:   transparent !important;
}

/* Icon + label items — a columns-1/2/3 panel opted in with showIcons=true on
   its entry's secondaryURL, and an item labelled "[icon] Text"
   (component-navbar-primary-submenu-template). Unlike .dropdown-item--icon
   above, the label stays visible: the glyph is decorative (aria-hidden) and the
   text is the link's name. Fixed width so the labels line up down the column
   whatever each glyph's own width; colour follows the link, including hover
   and .active. */
.component-navbar-primary .dropdown-panel-item-icon {
    --cg-nav-panel-item-icon-width: 1.25em;
    display:      inline-block;
    width:        var(--cg-nav-panel-item-icon-width);
    margin-right: 0.5rem;
    text-align:   center;
}

/* Icon group — wraps all icon-sentinel <a> elements into a single <li><ul>
   so they can be aligned as a unit (right-aligned at all sizes). */
/* No margin-top on the icon group. It is a plain <li> in the column list, so
   any extra top margin pushes the social icons off the row's vertical centre
   and they stop lining up with the text rows above them. The group is
   display:none below the expand breakpoint, so this only ever applied on
   desktop — where it was purely harmful. The row's inset comes from the
   anchors inside, exactly like every other row in the panel. */
.component-navbar-primary .dropdown-item-icon-group > ul {
    justify-content: flex-end;
}

/* Mobile overrides for dropdown panel content.
     1. Sub-item lists rendered with `d-flex flex-row flex-wrap` (the
        social-row layout pattern) stack vertically below the expand
        breakpoint. Bootstrap's `.flex-row` utility uses !important,
        so the override has to as well.
     2. Social icon groups (`.dropdown-item-icon-group`, emitted by
        mega-col PASS 2 for `[Brand]` icon-only entries) are hidden on
        mobile — touch users typically reach socials via the
        navbar-aside grid trigger, and the inline row eats vertical
        space inside the collapse. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-sub-items.d-flex.flex-row {
        flex-direction: column !important;
        flex-wrap:      nowrap   !important;
    }

    .component-navbar-primary .dropdown-item-icon-group {
        display: none !important;
    }
}

/* Sub-group column layout — column=true on the column navbar's secondaryURL.
   The sub-header sits ABOVE its item list at all sizes; the items nest under it
   with a slight left indent so they read as children of the header, like a normal
   <ul>. Border-top row separator applies at all sizes. */
.component-navbar-primary .dropdown-panel-sub-group--column {
    margin-top: 0;
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
    border-top: var(--cg-mega-col-divider);
}

.component-navbar-primary .dropdown-panel-sub-group--column:first-child {
    padding-top: 0;
    border-top: none;
}

@media (min-width: 992px) {
    /* Row-style sub-groups — the item list is a horizontal d-flex row (e.g. a sport
       with Schedule / Roster / News). Lay the sub-header on the LEFT and the item
       row on the RIGHT (no header divider) — the original inline treatment. */
    .component-navbar-primary .dropdown-panel-sub-group--column:has(> .dropdown-panel-sub-items.d-flex.flex-row) {
        display:        flex;
        flex-direction: row;
        align-items:    center;
        gap:            0.75rem;
    }
    /* margin-bottom is zeroed because the header sits INLINE with the item row
       here rather than above it. padding-bottom / border-bottom no longer need
       resetting — the sub-header carries neither now that it mirrors
       .dropdown-panel-col-header. */
    .component-navbar-primary .dropdown-panel-sub-group--column:has(> .dropdown-panel-sub-items.d-flex.flex-row) > .dropdown-panel-sub-header {
        flex-shrink:    0;
        min-width:      7rem;
        margin-bottom:  0;
    }
    .component-navbar-primary .dropdown-panel-sub-group--column:has(> .dropdown-panel-sub-items.d-flex.flex-row) > .dropdown-panel-sub-items {
        flex:            1;
        min-width:       0;
        justify-content: flex-end;
    }

    /* Plain-list sub-groups stack their item list UNDER the sub-header (block
       flow). The left indent that makes those items read as its children is
       now the global .dropdown-panel-sub-items margin-left below — applying a
       padding-left here as well would double the inset. */
}

/* ============================================================
   PANEL INTERIOR — shared link padding + nested-list indent

   One rhythm for every clickable row inside a dropdown panel, at every
   breakpoint: .dropdown-item (mega column items, sub-group items, columns-N
   lists), a panel .nav-link (the submenu template emits "nav-link
   dropdown-item" on panel items), and .dropdown-panel-sub-header-link.

   Scoped inside .dropdown-menu — which every panel variant carries — so the
   TOP-LEVEL navbar links keep their own, larger --cg-nav-link-* padding.
   .navbar-aside-dropdown is excluded; the aside menu has its own rhythm.

   The :not() lifts these to four-class specificity, so they beat the
   three-class per-variant rules above (which no longer declare padding —
   this is the single source) and the three-class mobile
   .navbar-nav .nav-link rule near the top of the file, regardless of order.

   Four classes is NOT a general guarantee, and relying on it is how this
   broke once: the layout-standard lg+ padding rule in navbar-primary.css
   is five classes, so its descendant .navbar-nav .nav-link form beat this
   rule and repadded every panel row carrying .nav-link. The durable fix is
   on that side — any top-level navbar-link rule must scope with child
   combinators (> .nav-item > .nav-link) so it cannot reach panel content —
   rather than escalating specificity here. Add padding to a top-level rule
   without that scoping and this rule loses again.

   Deliberately NOT wrapped in a media query: a panel should read the same
   stacked in the collapsed menu as it does open on desktop.
   ============================================================ */
.component-navbar-primary .dropdown-menu:not(.navbar-aside-dropdown) .dropdown-item,
.component-navbar-primary .dropdown-menu:not(.navbar-aside-dropdown) .nav-link,
.component-navbar-primary .dropdown-panel-sub-header-link {
    padding-inline: var(--cg-nav-panel-link-px);
    padding-block:  var(--cg-nav-panel-link-py);
}

/* ------------------------------------------------------------
   MOBILE SUB-HEADER OVERRIDES

   Everything the mobile sub-header needs that collides with an earlier rule at
   equal specificity. All of these selectors are two classes
   (.component-navbar-primary .dropdown-panel-sub-header-link), exactly like the
   base typography rule and the shared panel-link padding rule above, so ONLY
   source order decides — which is why they are gathered here, at the end, rather
   than with the rest of the mobile sub-group styling. Moving this block earlier
   silently reverts all three.
   ------------------------------------------------------------ */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-sub-header-link {
        /* Flex row so the caret's margin-left:auto can push it to the trailing
           edge. The base rule sets display:block, which made that auto margin
           inert and left the chevron tucked against the label. */
        display:     flex;
        align-items: center;

        /* Not bold on mobile. The base rule takes --cg-mega-col-header-weight
           (700) because on desktop this is a column heading. Collapsed, it is a
           row in a list of rows and the bold made it compete with the top-level
           nav items for emphasis. Weight is the only part of the heading
           treatment dropped here — size, transform and letter-spacing still come
           from the theme's tokens, so Reign's uppercase sub-headers stay
           uppercase. */
        font-weight: normal;

        /* Trailing edge keeps the wider --cg-nav-link-px inset so the caret lines
           up vertically with the menu's other arrow controls
           (.dropdown-toggle::after / .nav-dropdown-chevron) rather than sitting
           closer to the edge than they do. The shared rule's padding-inline
           shorthand resets both edges, so this must come after it. */
        padding-inline-end: var(--cg-nav-link-px);
    }
}

/* subHeaderToggle=split (opt-in, component-navbar-primary-template): the
   header link navigates and a chevron button beside it expands the group
   (navbar-sub-toggle.js). The link drops its own caret; the button carries
   it, with the same glyph, rotation and trailing inset. */
@media (max-width: 991.98px) {
    .component-navbar-primary .dropdown-panel-sub-header--split {
        display:     flex;
        align-items: stretch;
    }
    .component-navbar-primary .dropdown-panel-sub-header--split > .dropdown-panel-sub-header-link {
        flex:      1 1 auto;
        min-width: 0;
    }
    .component-navbar-primary .dropdown-panel-sub-header--split > .dropdown-panel-sub-header-link::after {
        content: none;
    }
    .component-navbar-primary .dropdown-panel-sub-toggle {
        flex:            0 0 auto;
        display:         flex;
        align-items:     center;
        justify-content: center;
        min-width:       2.75rem;
        padding:         0 var(--cg-nav-link-px);
        border:          0;
        background:      none;
        color:           inherit;
    }
    .component-navbar-primary .dropdown-panel-sub-toggle::after {
        font-family: var(--cg-icon-font);
        font-weight: 900;
        content:     "\f078";   /* fa-chevron-down */
        font-size:   0.7em;
        transition:  transform 0.2s ease;
    }
    .component-navbar-primary .dropdown-panel-sub-group.show > .dropdown-panel-sub-header .dropdown-panel-sub-toggle::after {
        transform: rotate(180deg);
    }
}

/* Second exception: the social icon row gets its own tighter inline rhythm.
   These are square glyph targets sitting shoulder to shoulder, so the shared
   0.5rem inline padding spaces them out into a loose scatter instead of reading
   as one group. --cg-nav-panel-icon-px (0.25rem) pulls them together, and only
   the FIRST and LAST anchors keep the full --cg-nav-panel-link-px on their
   OUTER edge, so the row still lines up with the text rows above and below it
   rather than looking inset or overhanging.

   The base rule matches the shared panel-link rule's four-class specificity, so
   it has to stay AFTER it — the first/last rules add a pseudo-class and win on
   their own. */
.component-navbar-primary .dropdown-item-icon-group .dropdown-item.dropdown-item--icon {
    padding-inline: var(--cg-nav-panel-icon-px, 0.25rem);
}

.component-navbar-primary .dropdown-item-icon-group > ul > li:first-child .dropdown-item--icon {
    padding-inline-start: var(--cg-nav-panel-link-px);
}

.component-navbar-primary .dropdown-item-icon-group > ul > li:last-child .dropdown-item--icon {
    padding-inline-end: var(--cg-nav-panel-link-px);
}

/* ============================================================
   PANEL HOVER / FOCUS

   Every clickable row in a dropdown panel gets the Bootstrap-style slight
   background shift, from one token: --cg-nav-dropdown-item-hover-bg. The
   generic default is a dark overlay for light panels; a DARK-panel theme must
   remap that token (not just --bs-dropdown-link-hover-bg) or every cg- panel
   row paints black-on-dark and the hover is invisible.

   :focus-visible is paired with :hover throughout so keyboard tabbing lights
   the same surface a pointer does.

   ROW-LEVEL EXCEPTION. Some children of .dropdown-panel-col-items are a single
   ROW made of several elements — a sub-header beside an inline item row. There,
   highlighting each element separately reads as a scatter, so the ROW carries
   the background and its children stay transparent. :has() lets the row light
   up while focus is on any descendant.

   The test for "this group is inline" is
   :has(> .dropdown-panel-sub-items.d-flex.flex-row) — the same signal the
   single-row LAYOUT rules use (see the lg+ block above). It reads the group's
   own item list: flex-row means the items sit in a wrapping horizontal row, so
   the group is inline; a stacked or default list means it is not.

   These rules keyed off .dropdown-panel-sub-group--column instead, which is a
   DIFFERENT axis — column=true is inherited from the enclosing column context,
   while flex= is set per entry — so the two came apart in both directions: a
   --column group with a stacked list got a block highlight that flooded the
   column, and a plain inline group got none. Matching on the item list fixes
   both, and covers --column groups too since they also carry the base class.

   Stacked sub-groups (header ABOVE a vertical list) stay excluded — a
   block-level highlight there would flood half the column — so their items
   highlight individually.

   The matching TEXT color for these rows lives with the other sub-item color
   rules above and uses the same :has() test, so the two never disagree about
   which groups are inline. Both read tokens — nothing here hardcodes a color,
   because the panel surface is light or dark depending on the theme.
   ============================================================ */

/* Baseline: any panel item, any list. */
.component-navbar-primary .dropdown-menu:not(.navbar-aside-dropdown) .dropdown-item:hover,
.component-navbar-primary .dropdown-menu:not(.navbar-aside-dropdown) .dropdown-item:focus-visible {
    background-color: var(--cg-nav-dropdown-item-hover-bg);
}

/* Row-level: single-row children of a column list, and INLINE sub-groups
   (identified by their flex-row item list, not by --column). */
.component-navbar-primary .dropdown-panel-col-items > li:not(.dropdown-panel-sub-group):hover,
.component-navbar-primary .dropdown-panel-col-items > li:not(.dropdown-panel-sub-group):has(:focus-visible),
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row):hover,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row):has(:focus-visible) {
    background-color: var(--cg-nav-dropdown-item-hover-bg);
}

/* …and their children stay transparent, so the row highlights once. */
.component-navbar-primary .dropdown-panel-col-items > li:not(.dropdown-panel-sub-group) .dropdown-item:hover,
.component-navbar-primary .dropdown-panel-col-items > li:not(.dropdown-panel-sub-group) .dropdown-item:focus-visible,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-header-link:hover,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-header-link:focus-visible,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-items .dropdown-item:hover,
.component-navbar-primary .dropdown-panel-sub-group:has(> .dropdown-panel-sub-items.d-flex.flex-row) .dropdown-panel-sub-items .dropdown-item:focus-visible {
    background-color: transparent;
}

/* Nested lists step in from their parent header so the hierarchy reads as a
   traditional indented menu: a column's items sit under its column header,
   and a sub-group's items under its sub-header — indenting again for the
   extra level. Both lists are .list-unstyled in the markup, so this margin is
   the only inset they carry. */
.component-navbar-primary .dropdown-panel-col-items,
.component-navbar-primary .dropdown-panel-sub-items {
    margin-left: var(--cg-nav-panel-indent);
}

/* Nav panel sponsor — sponsor image attached to a sub-navbar.
   Desktop-only (d-none d-lg-flex applied in template). */
.component-navbar-primary .nav-panel-sponsor {
    border-top: var(--cg-nav-dropdown-divider-color) 1px solid;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    font-size: 0.75rem;
    color: var(--cg-nav-dropdown-color);
}

.component-navbar-primary .nav-panel-sponsor img {
    max-height: 3rem;
    width: auto;
}

/* Nav panel ad (waldo) — bottom of dropdown and mega panels. */
.component-navbar-primary .nav-panel-ad {
    border-top: 1px solid var(--cg-nav-dropdown-divider-color);
    padding-top: 0.5rem;
    margin-top: 0.5rem;
}


/* ============================================================
   Newor / Waldo ad placeholders (waldo-tag-*)
   The CMS injects the Waldo loader (generic/inc-head) when media ads are
   enabled; the theme only emits empty #waldo-tag-* targets (see the #adSlot
   macro in the decorator + layout-core), which Waldo fills by id.

   Do NOT position the targets here. Waldo injects its own structure
   (e.g. #waldo-sticky-footer-wrapper) and positions it; the sidebar rails are
   positioned by newormedia-placements.js, and the sticky footer is shown/hidden
   on scroll by the theme JS. global-reset.css (loaded site-wide via inc-head)
   already styles the injected units (#waldo-sticky-footer-wrapper background +
   z-index, the rail transitions, .waldo-display-unit children). Boxing the
   #waldo-tag-* target with position/size fights that injected wrapper —
   collapsed inner (0x0) + overflow. These two classes are ONLY optional
   centering wrappers placed AROUND in-flow slots, never on the target itself.
   ============================================================ */

/* Full-bleed leaderboard band above the header (decorator). */
.ad-band {
    width: 100%;
    padding-block: 0.5rem;
    text-align: center;
}

/* In-content ad band — centered within its container (layout-core). */
.ad-inline {
    margin-block: 1.5rem;
    text-align: center;
}


/* ============================================================
   COMPONENT: navbar-secondary
   ============================================================ */

.component-navbar-secondary {
    --cg-nav-sec-link-font-size: inherit;
    --cg-nav-sec-link-font-weight: inherit;
    --cg-nav-sec-link-text-transform: none;
    --cg-nav-sec-link-letter-spacing: normal;
    --cg-nav-sec-link-px: 0.75rem;
    --cg-nav-sec-link-py: 0.5rem;
    --cg-nav-sec-thumbnail-height: 2rem;
}

/* The secondary navbar is rendered with data-bs-theme="light" in the view.
   The .navbar-secondary surface reads --cg-nav-sec-bg / --cg-nav-sec-color
   from :root (defined at the top of this file), so the navbar and the
   theme's .header-row-secondary outer band always share the same colors.

   The view formerly hardcoded .bg-light here too; because Bootstrap's
   background utilities are !important, it beat this rule and pinned the
   surface to #f8f9fa regardless of the token — a visible seam against a
   theme row painted from the same token. It was removed so this rule is
   the single lever. Do not reintroduce a bg-* utility on that element;
   to drop the surface, set --cg-nav-sec-bg: transparent on it instead. */
.component-navbar-secondary .navbar-secondary {
    background-color: var(--cg-nav-sec-bg);
    color:            var(--cg-nav-sec-color);
}

/* Nav links */
.component-navbar-secondary .navbar-secondary .nav-link {
    font-size: var(--cg-nav-sec-link-font-size);
    font-weight: var(--cg-nav-sec-link-font-weight);
    text-transform: var(--cg-nav-sec-link-text-transform);
    letter-spacing: var(--cg-nav-sec-link-letter-spacing);
    padding-inline: var(--cg-nav-sec-link-px);
    padding-block: var(--cg-nav-sec-link-py);
}

/* Sport/section title */
.component-navbar-secondary .navbar-page-title {
    font-weight: 600;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Sport thumbnail */
.component-navbar-secondary .navbar-secondary-thumbnail {
    height: var(--cg-nav-sec-thumbnail-height);
    width: auto;
    margin-inline-end: 0.5rem;
    flex-shrink: 0;
}

/* The secondary navbar now uses Bootstrap's native navbar-expand-{bp}
   pattern (see component-navbar-secondary-view): at and above the
   `expand` breakpoint the .navbar-collapse is forced to flex inline
   and the .navbar-toggler is hidden by Bootstrap's own CSS; below
   the breakpoint, the toggler appears and the collapse hides until
   clicked. The right-alignment of inline items is handled by
   `.navbar-nav.ms-auto` on the inner <ul>. The previous JS-driven
   `ul-wrapper` + `navbar-secondary-overflow` overflow module is no
   longer wired up (data-module attribute removed in the view). */


/* ------------------------------------------------------------
   Optional offcanvas mode (both primary + secondary nav)

   Templates emit either:
       <div class="collapse navbar-collapse">   (default)
   OR
       <div class="offcanvas offcanvas-{bp} offcanvas-{placement} navbar-{primary|secondary}-offcanvas">
                                                (when offcanvas=true)

   `.offcanvas-{bp}` is Bootstrap's responsive flavor — at and above
   that breakpoint the panel renders inline like a regular collapse;
   below it, it slides in from the chosen edge.

   Top-placement offset
   --------------------
   `offcanvas-top` by default has `top: 0; height: 30vh` — it slides
   in from the very top of the viewport, covering the sticky header.
   To make the panel land directly below the header instead (the
   common "dropdown-style mobile menu" pattern), set
   `--cg-offcanvas-top-offset` on the navbar component (or higher).
   The panel and its backdrop both shift down by that offset; the
   sticky header stays visible and interactive. Themes typically
   set this to the rendered navbar height (e.g. 84px in Reign).

   Backdrop sits at z-index 1045 by default; the sticky header may
   need z-index ≥ 1046 to ride above it when the offset is applied.
   ------------------------------------------------------------ */
.component-navbar-primary,
.component-navbar-secondary {
    --cg-offcanvas-top-offset: 0;
}

@media (max-width: 991.98px) {
    .navbar-primary-offcanvas.offcanvas-top,
    .navbar-secondary-offcanvas.offcanvas-top {
        top:        var(--cg-offcanvas-top-offset, 0);
        height:     auto;
        max-height: calc(100vh - var(--cg-offcanvas-top-offset, 0));
    }
}

/* Dropdown */
.component-navbar-secondary .dropdown-menu {
    border-radius: 0;
}

/* Nested submenu positioning */
.component-navbar-secondary .dropdown-submenu {
    position: absolute;
    left: 100%;
    top: 0;
    display: none;
}

.component-navbar-secondary .dropdown-item.dropdown-toggle:hover + .dropdown-submenu,
.component-navbar-secondary .dropdown-submenu:hover,
.component-navbar-secondary .dropdown-submenu.show {
    display: block;
}

/* ------------------------------------------------------------
   navbar-secondary — mobile responsiveness pass
   ------------------------------------------------------------ */
@media (max-width: 991.98px) {

    /* Allow the page title to shrink and truncate with ellipsis so
       a long sport/section name doesn't force horizontal scroll or
       push the toggler off-screen. Note: changing flex-shrink to 1
       here defeats the base rule's `flex-shrink: 0`. */
    .component-navbar-secondary .navbar-page-title {
        flex-shrink:    1;
        min-width:      0;
        overflow:       hidden;
        text-overflow:  ellipsis;
    }

    /* Touch-friendly target — bump link padding-block from 0.5rem
       to 0.75rem so each tap target sits closer to the 44px guideline. */
    .component-navbar-secondary .navbar-secondary .nav-link {
        padding-block: 0.75rem;
    }

    /* Nested submenus (level-2+) — on desktop these pop out to the
       right via `position:absolute; left:100%`, which renders
       off-screen on a narrow viewport. Stack them inline inside
       their parent dropdown-menu instead, with a 1rem indent per
       level so the hierarchy still reads. Chrome (border, shadow,
       background) is stripped because the items now sit visually
       inside their parent surface. */
    .component-navbar-secondary .dropdown-submenu {
        position:     static;
        display:      block;
        left:         auto;
        top:          auto;
        padding-left: 1rem;
        border:       none;
        box-shadow:   none;
        background:   transparent;
    }

    /* Mobile collapse — cap the expanded menu at viewport height
       minus the navbar's own height so tall menus scroll within the
       panel instead of spilling off the page. Parallels the
       primary navbar-collapse mobile rule and reuses the same
       shared offset variable. Only applies below the expand
       breakpoint; above it Bootstrap forces the collapse to be
       inline-flex and this cap has no effect. */
    .component-navbar-secondary .navbar-collapse {
        max-height: calc(100vh - var(--cg-nav-mobile-menu-offset, 64px));
        overflow-y: auto;
    }
}


/* ============================================================
   COMPONENT: navbar-image
   Row of image links — member logos, sponsor logos, affiliate logos.
   Backed by CMS imagenavbar content via cg-component-navbar-image-
   template + view.

   Layout: CSS Grid wireframe — auto-fitting equal-width cells, each
   separated by a 1-line border (Bootstrap's --bs-border-color /
   --bs-border-width tokens). The list element supplies the top +
   left edges; each <li> supplies its own right + bottom edge, so
   adjacent cells share a single 1px line and the perimeter closes
   without doubled thickness.

   Custom property hooks (override in theme.css):
     --cg-navbar-image-cell-min-width   minmax floor for the grid
                                        track width (drives column
                                        count via auto-fit)
     --cg-navbar-image-cell-padding     inner padding around each logo
     --cg-navbar-image-cell-bg          per-cell background color
                                        (default: Bootstrap --bs-gray-200)
     --cg-navbar-image-logo-mh          logo max-height at mobile
     --cg-navbar-image-logo-mh-lg       logo max-height at lg+
   ============================================================ */
.component-navbar-image {
    --cg-navbar-image-cell-min-width: 6rem;
    --cg-navbar-image-cell-padding:   1rem;
    --cg-navbar-image-cell-bg:        var(--bs-gray-200, #e9ecef);
    --cg-navbar-image-logo-mh:        2.5rem;
    --cg-navbar-image-logo-mh-lg:     3rem;
}

/* Larger logos for the grid (affiliates) + carousel (sponsors) variants;
   the members variant keeps the smaller base sizes above. */
.component-navbar-image.layout-grid,
.component-navbar-image.layout-carousel {
    --cg-navbar-image-logo-mh:    4rem;
    --cg-navbar-image-logo-mh-lg: 6rem;
    /* Approximate rendered height of the controls footer (control button +
       the footer's margin-top). Used to balance the logos vertically — see
       the :has(.cg-inc-footer) rule below. Tune if the controls are resized. */
    --cg-navbar-image-carousel-controls-h: 3rem;
}

/* Vertical centering with controls in normal flow. The component box hugs its
   content (logos + controls footer), so the bottom controls row pushes the
   logos above the box's optical center. When a controls footer is present,
   reserve matching space ABOVE the body so the logos read as vertically
   centered while the controls stay in flow at the bottom (no absolute
   positioning). Gated on :has(.cg-inc-footer) so logo-only carousels (no
   controls) are unaffected. With mobileShowSize each view
   (.cg-navbar-image-view) carries its own body and footer. */
.component-navbar-image.layout-carousel:has(> .cg-inc-footer) .cg-inc-body,
.component-navbar-image.layout-carousel .cg-navbar-image-view:has(> .cg-inc-footer) > .cg-inc-body {
    padding-top: var(--cg-navbar-image-carousel-controls-h);
}

.component-navbar-image .cg-navbar-image-list {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-navbar-image-cell-min-width), 1fr));
    border-top:            var(--bs-border-width) solid var(--bs-border-color);
    border-left:           var(--bs-border-width) solid var(--bs-border-color);
}

.component-navbar-image .cg-navbar-image-item {
    display:          flex;
    align-items:      center;
    justify-content:  center;
    padding:          var(--cg-navbar-image-cell-padding);
    background-color: var(--cg-navbar-image-cell-bg);
    border-right:     var(--bs-border-width) solid var(--bs-border-color);
    border-bottom:    var(--bs-border-width) solid var(--bs-border-color);
}

.component-navbar-image .cg-navbar-image-link {
    /* position: relative anchors the absolutely-positioned hover image
       to the link box. isolation: isolate keeps the cross-fade z-index
       layer scoped to the link so a hover image never paints under a
       sibling logo's box-shadow / overlay. */
    position:        relative;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    isolation:       isolate;
}

.component-navbar-image .cg-navbar-image-logo {
    display:    block;
    max-height: var(--cg-navbar-image-logo-mh);
    width:      auto;
    transition: filter 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
}

@media (min-width: 992px) {
    .component-navbar-image .cg-navbar-image-logo {
        max-height: var(--cg-navbar-image-logo-mh-lg);
    }
}

/* ------------------------------------------------------------
   IMAGE EFFECTS — opt-in color + hover treatments for the navbar-image family
   (members grid, generic grid, sponsors carousel). The template puts
   cg-imgfx-color-<x> + cg-imgfx-hover-<y> on the component root; the effects
   are scoped to .layout-wrapper (the image surface) so hovering the header,
   footer, or carousel arrows does NOT trigger them.
     color : spotlight* | grayscale | grayscale-hover | color
     hover : grow*       | lift | flip | bounce | none
   Default (set by the templates): spotlight + grow — the surface dims to
   greyscale on hover while the logo under the cursor stays full color + grows.
   ------------------------------------------------------------ */

/* color: spotlight (default) — entering the image surface dims every logo to
   greyscale + 50% opacity; the hovered/focused link's logo is restored to full
   color (the "restore" pair shares specificity and wins by source order). */
.cg-imgfx-color-spotlight .layout-wrapper:hover        .cg-navbar-image-logo,
.cg-imgfx-color-spotlight .layout-wrapper:focus-within .cg-navbar-image-logo {
    filter:  grayscale(1);
    opacity: 0.5;
}
.cg-imgfx-color-spotlight .layout-wrapper:hover        .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-color-spotlight .layout-wrapper:focus-within .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    filter:  none;
    opacity: 1;
}

/* color: grayscale — always greyscale. */
.cg-imgfx-color-grayscale .cg-navbar-image-logo {
    filter: grayscale(1);
}

/* color: grayscale-hover — greyscale until a logo is hovered/focused. */
.cg-imgfx-color-grayscale-hover .cg-navbar-image-logo {
    filter: grayscale(1);
}
.cg-imgfx-color-grayscale-hover .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-color-grayscale-hover .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    filter: none;
}

/* color: color — full color always (no filter rule needed). */

/* hover transforms — applied to the hovered/focused logo (transition declared
   on .cg-navbar-image-logo above). */
.cg-imgfx-hover-grow .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-hover-grow .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    transform: scale(1.1);
}
.cg-imgfx-hover-lift .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-hover-lift .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    transform: translateY(-0.25rem);
}
.cg-imgfx-hover-flip .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-hover-flip .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    transform: rotateY(180deg);
}
.cg-imgfx-hover-bounce .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.cg-imgfx-hover-bounce .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    animation: cg-imgfx-bounce 0.6s ease;
}
@keyframes cg-imgfx-bounce {
    0%, 100% { transform: translateY(0); }
    30%      { transform: translateY(-0.4rem); }
    50%      { transform: translateY(0); }
    70%      { transform: translateY(-0.2rem); }
}
@media (prefers-reduced-motion: reduce) {
    .cg-imgfx-hover-grow .cg-navbar-image-link:hover .cg-navbar-image-logo,
    .cg-imgfx-hover-lift .cg-navbar-image-link:hover .cg-navbar-image-logo,
    .cg-imgfx-hover-flip .cg-navbar-image-link:hover .cg-navbar-image-logo,
    .cg-imgfx-hover-bounce .cg-navbar-image-link:hover .cg-navbar-image-logo {
        transform:  none;
        animation:  none;
    }
}

/* Carousel layout clips transforms: its .carousel-inner needs overflow:hidden
   for the slide effect, so a scaled / lifted / flipped / bouncing logo gets
   trimmed at the cell edges. Disable the transform-based hover effects for the
   carousel (the color/spotlight effects don't overflow, so they still apply).
   Higher specificity than the .cg-imgfx-hover-* rules above, so it wins
   regardless of the requested hoverEffect. */
.component-navbar-image.layout-carousel .cg-navbar-image-link:hover         .cg-navbar-image-logo,
.component-navbar-image.layout-carousel .cg-navbar-image-link:focus-visible .cg-navbar-image-logo {
    transform: none;
    animation: none;
}

/* Collapsible row — when .cg-navbar-image-list carries .collapse,
   the panel hides on mobile until the toggle button is clicked.
   Above lg, the toggle is hidden via d-lg-none and the panel is
   always visible regardless of .show state.

   The base .cg-navbar-image-list { display: grid } rule above has
   the same specificity as Bootstrap's .collapse:not(.show) { display:
   none } and loads after it, so on its own it would override
   Bootstrap and keep the panel visible on mobile too. The rule below
   reinstates the mobile hide with higher specificity, then the lg+
   media query forces grid back regardless of .show. */
.component-navbar-image .cg-navbar-image-list.collapse:not(.show) {
    display: none;
}

@media (min-width: 992px) {
    .component-navbar-image .cg-navbar-image-list.collapse:not(.show) {
        display: grid;
    }
}

/* Bootstrap-grid track + cell — SHARED by the generic grid
   (component-navbar-image-template, .layout-grid) and the sponsors
   carousel (component-navbar-image-carousel-template, .layout-carousel).
   Both lay logos out in a Bootstrap `.row` whose cells carry col-N /
   col-md-N / col-lg-N spans (span = 12 / per-row count, restricted to the
   clean divisors 1/2/3/4/6/12) — pure BS5 12-grid, no custom grid or
   breakpoint CSS. This only tunes the row gutter and centers each logo in
   its (borderless) column. The members variant uses the bordered auto-fit
   .cg-navbar-image-list above instead.

   Custom property hook (override in theme.css):
     --cg-navbar-image-track-gap   gutter between logos (row x + y)
   ============================================================ */
.component-navbar-image {
    --cg-navbar-image-track-gap: 1.5rem;
}

.component-navbar-image .cg-navbar-image-track {
    --bs-gutter-x: var(--cg-navbar-image-track-gap);
    --bs-gutter-y: var(--cg-navbar-image-track-gap);
}

.component-navbar-image .cg-navbar-image-cell {
    display:         flex;
    align-items:     center;
    justify-content: center;
    /* Minimum logo slot — keeps logos from shrinking below this even when
       the column count would otherwise make them smaller (the row wraps
       instead). Matches the members grid's auto-fit floor. Default 6rem. */
    min-width:       var(--cg-navbar-image-cell-min-width);
}

/* Cap a logo at its column. The logo is sized only by max-height, so a wide
   one (a wordmark) ran past its cell into the next logo (FED-23057). The
   link is a flex item (min-width:auto), so it needs the cap too. */
.component-navbar-image .cg-navbar-image-cell .cg-navbar-image-link {
    min-width: 0;
    max-width: 100%;
}

.component-navbar-image .cg-navbar-image-cell .cg-navbar-image-logo {
    max-width: 100%;
    height:    auto;
}


/* ============================================================
   COMPONENT: cg-component-navbar-banner
   Static inline banner row rendered by component-navbar-banner-template
   — the non-carousel sibling of the banner carousel. Full-color banner
   graphics laid out in a Bootstrap 12-grid `.row` (cols/colsMd/colsLg →
   col spans) that wraps; each banner is img-fluid (capped at its natural
   size, never upscaled) and centered in its cell. Optional caption band.

   Custom property hooks (override in theme.css):
     --cg-navbar-banner-gap            gutter between banners (row x + y)
     --cg-navbar-banner-radius         banner image corner radius
     --cg-navbar-banner-caption-bg     caption overlay background
     --cg-navbar-banner-caption-color  caption overlay text color
   ============================================================ */
.component-navbar-banner {
    --cg-navbar-banner-gap:    1rem;
    --cg-navbar-banner-radius: 0;
}

.component-navbar-banner .cg-navbar-banner-track {
    --bs-gutter-x: var(--cg-navbar-banner-gap);
    --bs-gutter-y: var(--cg-navbar-banner-gap);
}

.component-navbar-banner .cg-navbar-banner-cell {
    display:         flex;
    align-items:     center;
    justify-content: center;
}

.component-navbar-banner .cg-navbar-banner-link {
    position:        relative;
    display:         block;
    width:           100%;
    text-decoration: none;
}

.component-navbar-banner .cg-navbar-banner-image {
    display:       block;
    /* img-fluid behavior: cap at the container width, never upscale past the
       artwork's natural size. These are full banners, NOT logo icons, so the
       height is deliberately uncapped (unlike the .component-navbar-image
       logo grids, where the logo height is capped) — it follows the artwork's
       aspect at the available width. */
    max-width:     100%;
    height:        auto;
    max-height:    none;
    margin-inline: auto;
    border-radius: var(--cg-navbar-banner-radius);
}

.component-navbar-banner .cg-navbar-banner-caption {
    position:    absolute;
    left:        0;
    right:       0;
    bottom:      0;
    padding:     0.5rem 0.75rem;
    text-align:  center;
    font-weight: 600;
    color:       var(--cg-navbar-banner-caption-color, #fff);
    background:  var(--cg-navbar-banner-caption-bg, rgba(0, 0, 0, 0.5));
}

/* Hover-reveal caption (captionReveal=hover) — mirrors the carousel banner:
   a full-cover, centered panel hidden below the image until the banner is
   hovered/focused, then it slides up + fades in (fades back out on leave).
   Scoped to both the link (clickable banners) and the cell (unlinked ones, where
   the caption is a direct child of the cell) so the reveal works either way; the
   container clips the panel and provides the positioning context. */
.component-navbar-banner.caption-reveal-hover .cg-navbar-banner-link,
.component-navbar-banner.caption-reveal-hover .cg-navbar-banner-cell {
    position: relative;
    overflow: hidden;
}
.component-navbar-banner.caption-reveal-hover .cg-navbar-banner-caption {
    inset:           0;   /* full cover — overrides the base bottom-band offsets */
    display:         flex;
    align-items:     center;
    justify-content: center;
    opacity:         0;
    transform:       translateY(100%);
    transition:      opacity .25s ease, transform .25s ease;
}
.component-navbar-banner.caption-reveal-hover .cg-navbar-banner-cell:hover .cg-navbar-banner-caption,
.component-navbar-banner.caption-reveal-hover .cg-navbar-banner-cell:focus-within .cg-navbar-banner-caption {
    opacity:   1;
    transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
    .component-navbar-banner.caption-reveal-hover .cg-navbar-banner-caption {
        transition: opacity .25s ease;
        transform:  none;
    }
}


/* ============================================================
   COMPONENT: cg-component-members-cards
   Member-logo cards rendered by component-members-cards-template: a
   logo plus a detail body (the member's links + social). Cards sit in a
   Bootstrap 12-grid (cols/colsMd/colsLg → col spans; 2-up on phones by
   default).

   DEFAULT (non-collapsible): the body shows beneath the logo (normal card).

   COLLAPSIBLE: the body is an OVERLAY that covers the LOGO area ONLY. The card
   is a two-row grid — a logo row + a toggle-bar foot — and the overlay shares
   the logo row, so it stops above the toggle bar and its content never sits
   behind it (mirrors the scoreboard's event-panel slide). The logo row sizes
   to the TALLER of the logo and the panel content so the links always fit with
   no scrollbar. Clicking anywhere on the card slides the overlay UP from the
   bottom (a transform — compositor-friendly, so no height reflow and no
   stutter); the toggle bar stays visible below it and its icon swaps to × —
   re-clicking the toggle slides the overlay back, so there is NO separate close
   button. The toggle drives an invisible Bootstrap collapse "state" element
   (.cg-members-card-state); the sibling rule
   `.cg-members-card-state.show ~ .cg-members-card-panel` animates the overlay.
   So Bootstrap owns the click + ARIA and there is NO custom JS, while the
   motion is a pure transform. The overlay defaults to the card surface (white)
   with site-color links (--bs-link-*); themes recolor via the tokens below.

   Custom property hooks (override in theme.css):
     --cg-members-card-bg               card background
     --cg-members-card-border-color     card border color
     --cg-members-card-radius           card corner radius
     --cg-members-card-logo-pad         padding around the logo
     --cg-members-card-logo-min-height  logo area height (logos larger than header)
     --cg-members-card-logo-max-height  logo max height inside the card
     --cg-members-card-col-min          min card width in the colsLg="auto"
                                        wrapping auto-grid mode (lg)
     --cg-members-card-panel-bg         overlay (collapsible) background
     --cg-members-card-panel-color      overlay text / link color
     --cg-members-card-panel-pad        overlay padding
     --cg-members-card-transition       overlay slide timing
     --cg-members-card-body-bg          inline body (non-collapsible) background
     --cg-members-card-body-color       inline body text / link color
     --cg-members-card-body-pad         inline body padding
   ============================================================ */
.component-members-cards {
    --cg-members-card-bg:              var(--bs-card-bg, #ffffff);
    --cg-members-card-border-color:    var(--bs-border-color, #dee2e6);
    --cg-members-card-radius:          var(--bs-border-radius, 0.375rem);
    --cg-members-card-logo-pad:        1rem;
    --cg-members-card-logo-min-height: 7rem;
    --cg-members-card-logo-max-height: 5rem;
    --cg-members-card-col-min:         10rem;
    --cg-members-card-panel-bg:        var(--cg-members-card-bg, #ffffff);
    --cg-members-card-panel-color:     inherit;
    --cg-members-card-panel-pad:       1rem;
    --cg-members-card-transition:      0.35s ease;
    --cg-members-card-body-bg:         transparent;
    --cg-members-card-body-color:      inherit;
    --cg-members-card-body-pad:        0.75rem 1rem 1rem;
}

.component-members-cards .cg-members-cards-grid {
    --bs-gutter-x: 1rem;
    --bs-gutter-y: 1rem;
}

/* colsLg="auto" → at lg, fit as many logos per row as will fit at a readable
   size, then WRAP (no fixed per-row count, no squeezing, no horizontal scroll).
   The row becomes a CSS grid of auto-fit tracks each at least
   --cg-members-card-col-min wide (growing to share the row via 1fr), so cards
   never shrink below that floor and overflow onto the next line instead. Cell
   padding is zeroed and the gutter handled by the grid gap (and the row's
   negative margins reset). Below lg the cells keep their cols/colsSm/colsMd
   wrapping via the normal Bootstrap grid. */
@media (min-width: 992px) {
    .component-members-cards .cg-members-cards-grid--lg-auto {
        display:               grid;
        grid-template-columns: repeat(auto-fit, minmax(var(--cg-members-card-col-min, 10rem), 1fr));
        gap:                   var(--bs-gutter-x, 1rem);
        margin:                0;
    }
    .component-members-cards .cg-members-cards-grid--lg-auto > .cg-members-cards-cell {
        width:     auto;
        max-width: none;
        padding:   0;
    }
}

/* Card — position:relative + overflow:hidden so the overlay positions and
   clips against it. Inherits Bootstrap .card; these set the themeable
   surface. */
.component-members-cards .cg-members-card {
    position:         relative;
    overflow:         hidden;
    background-color: var(--cg-members-card-bg);
    border:           var(--bs-border-width, 1px) solid var(--cg-members-card-border-color);
    border-radius:    var(--cg-members-card-radius);
}

/* Collapsible card — a two-row grid: the LOGO row (1fr) and the toggle-bar
   FOOT (auto). The overlay shares the LOGO row only, so it covers the logo but
   stops above the toggle bar, which stays visible + clickable to close.
   .cg-members-card-main is display:contents so its children (the logo wrap +
   the actions bar) drop straight into the card grid and can be placed in
   separate rows. Because the overlay keeps its full layout height even while
   transformed out of view (transform doesn't shrink layout), the LOGO row — and
   so the card — sizes to the TALLER of the logo and the panel's content, so
   every card is tall enough to show all its links with no scrollbar, no fixed
   height to guess, no JS. */
.component-members-cards .cg-members-card--collapsible {
    display:               grid;
    grid-template-columns:  1fr;
    grid-template-rows:     1fr auto;
    grid-template-areas:   "logo"
                           "foot";
}
.component-members-cards .cg-members-card--collapsible .cg-members-card-main {
    display: contents;
}
.component-members-cards .cg-members-card--collapsible .cg-members-card-logo-wrap,
.component-members-cards .cg-members-card--collapsible .cg-members-card-panel {
    grid-area: logo;
    min-width: 0;
}
.component-members-cards .cg-members-card--collapsible .cg-members-card-actions {
    grid-area: foot;
    /* NO z-index here. The toggle sits in its own foot row and the overlay only
       covers the logo row, so the toggle is never under the overlay and stays
       clickable without one. Adding a z-index would make the actions bar a
       stacking context and lift the toggle's stretched-link ::after (which spans
       the whole card) ABOVE the z-index:2 overlay, blocking clicks on the panel
       links. With no z-index the ::after stays at z-index:1, below the panel. */
}

/* Collapsible stack — logo fills the cell, the toggle bar pins to the base. */
.component-members-cards .cg-members-card-main {
    display:        flex;
    flex-direction: column;
}

/* Logo area — the collapsible logo wrap and the plain link (non-collapsible)
   share the same centered logo box. flex:1 fills the card so the logo stays
   centered if the card is taller than the logo. */
.component-members-cards .cg-members-card-logo-wrap,
.component-members-cards .cg-members-card-link {
    position:        relative;
    flex:            1 1 auto;
    display:         flex;
    align-items:     center;
    justify-content: center;
    width:           100%;
    min-height:      var(--cg-members-card-logo-min-height);
    padding:         var(--cg-members-card-logo-pad);
    border:          0;
    background:      transparent;
    text-decoration: none;
}

.component-members-cards .cg-members-card-logo {
    display:    block;
    max-width:  100%;
    max-height: var(--cg-members-card-logo-max-height);
    width:      auto;
    height:     auto;
    object-fit: contain;
}

/* Base toggle BAR (collapsible) — a full-width block at the card foot. Its
   stretched-link covers the whole card, so a click anywhere opens the overlay
   (Bootstrap collapse). Mirrors the scoreboard's cg-event-actions-toggle: a
   link-button whose icon swaps (ellipsis → ×) — here keyed off Bootstrap's
   .collapsed class on the toggle (present while closed), so still no JS. */
.component-members-cards .cg-members-card-actions {
    border-top: var(--bs-border-width, 1px) solid var(--cg-members-card-border-color);
}
.component-members-cards .cg-members-card-toggle {
    display:         flex;
    align-items:     center;
    justify-content: center;
    gap:             0.4rem;
    width:           100%;
    padding:         0.25rem 0.75rem;
    border:          0;
    background:      transparent;
    color:           var(--bs-primary, var(--bs-link-color));
    font-size:       1rem;
    line-height:     1;
    text-decoration: none;
    cursor:          pointer;
    transition:      background-color 0.15s ease-in-out, color 0.15s ease-in-out;
}
.component-members-cards .cg-members-card-toggle:hover,
.component-members-cards .cg-members-card-toggle:focus-visible {
    background-color: var(--bs-gray-100, #f8f9fa);
    color:            var(--bs-primary-text-emphasis, var(--bs-primary));
}

/* Icon swap — ellipsis when closed, × when open. Driven by Bootstrap's
   .collapsed class on the toggle (present while collapsed). */
.component-members-cards .cg-members-card-actions-icon--close {
    display: none;
}
.component-members-cards .cg-members-card-toggle:not(.collapsed) .cg-members-card-actions-icon--open {
    display: none;
}
.component-members-cards .cg-members-card-toggle:not(.collapsed) .cg-members-card-actions-icon--close {
    display: inline-block;
}

/* Invisible Bootstrap collapse STATE holder — kept out of layout and AT; it
   only carries the .show class BS toggles. transition:none so BS treats its
   (0-height) collapse as instant and flips .show right away, letting the
   overlay's own transform transition own the motion. */
.component-members-cards .cg-members-card-state {
    position:   absolute;
    width:      0;
    height:     0;
    overflow:   hidden;
    transition: none;
}

/* Overlay detail panel — shares the card's LOGO row with the logo
   (grid-area:logo), so it stacks ON the logo but NOT the toggle foot, and
   contributes its height to the card (see .cg-members-card--collapsible
   above). Parked below (translateY 100%) and hidden from AT/tab until the
   state holder gains .show, then it slides up via transform (no height
   animation → no reflow/stutter). The visibility delay on close keeps it
   rendered through the slide-out. */
.component-members-cards .cg-members-card-panel {
    z-index:          2;
    display:          flex;
    flex-direction:   column;
    align-items:      center;
    justify-content:  center;
    gap:              0.25rem;
    padding:          var(--cg-members-card-panel-pad);
    overflow-y:       auto;
    text-align:       center;
    background-color: var(--cg-members-card-panel-bg);
    color:            var(--cg-members-card-panel-color);
    transform:        translateY(100%);
    visibility:       hidden;
    transition:       transform var(--cg-members-card-transition),
                      visibility 0s linear var(--cg-members-card-transition);
}
.component-members-cards .cg-members-card-state.show ~ .cg-members-card-panel {
    transform:  translateY(0);
    visibility: visible;
    transition: transform var(--cg-members-card-transition), visibility 0s;
}

/* Inline body (non-collapsible) — links + social shown beneath the logo,
   divided by a top border. flex:0 0 auto overrides Bootstrap's .card-body
   flex:1 so the logo box is the part that fills the card height. */
.component-members-cards .cg-members-card-body {
    flex:             0 0 auto;
    padding:          var(--cg-members-card-body-pad);
    text-align:       center;
    background-color: var(--cg-members-card-body-bg);
    color:            var(--cg-members-card-body-color);
    border-top:       var(--bs-border-width, 1px) solid var(--cg-members-card-border-color);
}

/* Content — member name, text links, social row (shared by the overlay panel
   and the inline body). The title inherits the surface text color; the links
   use the site link color (--bs-link-* — the cg- link convention), so on the
   default white surface they read as normal site links. */
.component-members-cards .cg-members-card-panel-title {
    margin-bottom: 0.25rem;
    color:         inherit;
}
/* Panel content renders as small text — both the member links and the social
   row. The social-link below inherits this (rather than a fixed size) so the
   icons scale with it. */
.component-members-cards .cg-members-card-links,
.component-members-cards .cg-members-card-social {
    font-size: 0.875rem;
}
.component-members-cards .cg-members-card-link-item {
    display:         inline-block;
    padding:         0.1rem 0;
    color:           var(--body-link, var(--bs-link-color));
    text-decoration: none;
}
.component-members-cards .cg-members-card-link-item:hover,
.component-members-cards .cg-members-card-link-item:focus-visible {
    color:           var(--body-link-hover, var(--bs-link-hover-color));
    text-decoration: underline;
}
.component-members-cards .cg-members-card-social-link {
    display:         inline-block;
    font-size:       1rem;
    color:           var(--body-link, var(--bs-link-color));
    text-decoration: none;
}
.component-members-cards .cg-members-card-social-link:hover,
.component-members-cards .cg-members-card-social-link:focus-visible {
    color: var(--body-link-hover, var(--bs-link-hover-color));
}

/* Reduced motion — no slide; the overlay just appears/disappears. */
@media (prefers-reduced-motion: reduce) {
    .component-members-cards .cg-members-card-panel {
        transition: visibility 0s;
    }
}


/* ============================================================
   COMPONENT: cg-card
   Entry-level card primitive rendered by card-template. Built
   on Bootstrap 5's .card (.card / .card-body / .card-img-top /
   .card-img-overlay / .ratio) — the cg-card classes are additive
   hooks for theming and BEM-style modifiers per variant.

   Variants (BEM modifiers, set by the template via cardType param):
     .cg-card--thumbnail (default)  image top, body below
     .cg-card--hero                 image fills, title + meta overlay
     .cg-card--compact              image left, body right (horizontal)
     .cg-card--text-only            no image, body only

   Custom property hooks (override in theme.css):
     --cg-card-title-color             title color
     --cg-card-title-size              title font-size
     --cg-card-meta-color              meta (date/category/author) color
     --cg-card-meta-gap                inline gap between meta items (no
                                       vertical gap when they wrap)
     --cg-card-border                  hairline for the card outline AND the
                                       rule above the date band (one token, so
                                       the two never drift apart)
     --cg-card-footer-bg               date band background
     --cg-card-footer-color            date band text color
     --cg-card-footer-border           date band rule color (defaults to
                                       --cg-card-border)
     --cg-card-footer-padding          date band padding
     --cg-card-image-radius            thumbnail corner radius (default the
                                       Bootstrap --bs-border-radius)
     --cg-card-leadin-color            leadin paragraph color
     --cg-card-leadin-size             leadin font-size
     --cg-card-hero-overlay-gradient   linear-gradient for hero overlay
     --cg-card-compact-image-width     width of compact's left image
   ============================================================ */
/* ============================================================
   CARD SURFACE — shared default background for the cg- "card" components
   (headlines cards, bio cards, schedule events). White by default, sourced
   from Bootstrap's --bs-card-bg so the surface color is defined in ONE place:
   set --bs-card-bg (e.g. in theme.css) and every card recolors together.
   (Not done by adding the literal .card class because .card forces
   flex-direction:column, which would break .cg-event's horizontal row.)
   ============================================================ */
.cg-card,
.component-bio-card .cg-bio-card,
.component-schedule .cg-event {
    background-color: var(--bs-card-bg, #fff);
}

.cg-card {
    --cg-card-title-color:    inherit;
    --cg-card-title-size:     1.25rem; /* larger than body so the title still reads as the card's anchor; prominence boosted by fw-bold in markup */
    --cg-card-meta-color:     inherit; /* inherits the theme's body text color, not a muted shade */
    --cg-card-meta-gap:       0.5rem;  /* inline gap between category/date; matches the former .gap-2 */
    --cg-card-image-radius:   var(--bs-border-radius, 0.375rem); /* rounded thumbnail, matches the bio cards + BS default */
    --cg-card-leadin-color:   inherit;
    --cg-card-leadin-size:    1rem;

    /* Hero overlay — bottom-up dark fade so title/meta text reads
       against any image. Override to flip direction, change opacity,
       or use a brand-coloured gradient. */
    --cg-card-hero-overlay-gradient:
        linear-gradient(to top,
                        rgba(0, 0, 0, 0.7) 0%,
                        rgba(0, 0, 0, 0)  60%);

    --cg-card-compact-image-width: 6rem;

    /* Date band — rendered by default (datePlacement=footer). The reference
       presentation is the card grid in generic/news-template, which uses
       "card-footer border-top rounded-bottom bg-white text-muted p-3".
       Reproduced through tokens rather than those literal utilities so the
       band tracks --bs-card-bg like the rest of the card surface: a theme
       that sets a dark --bs-card-bg gets a dark band instead of a white
       stripe, and the .border-top utility's !important is not spent here.
       Bootstrap's own .card-footer:last-child already rounds the bottom
       corners, so "rounded-bottom" has nothing left to add.

       The hairline reads from the SAME token cg-league-news-rss paints its
       grid lines with, so the outline round a headlines card and the rule
       above its date band match the "Around the League" table exactly. One
       token moves all three: set --bs-gray-bg and the two components stay in
       step. (--bs-gray-bg is not defined anywhere today, so all of them
       resolve to the #e9ecef fallback — the point is that they resolve
       TOGETHER.) */
    --cg-card-border:         var(--bs-gray-bg, #e9ecef);
    --cg-card-footer-bg:      var(--bs-card-bg, #fff);
    --cg-card-footer-color:   var(--bs-secondary-color, rgba(33, 37, 41, 0.75));
    --cg-card-footer-border:  var(--cg-card-border);
    --cg-card-footer-padding: 1rem;
}

.cg-card-footer {
    background-color: var(--cg-card-footer-bg);
    color:            var(--cg-card-footer-color);
    border-top:       var(--bs-border-width, 1px) solid var(--cg-card-footer-border);
    padding:          var(--cg-card-footer-padding);
}

/* Card outline — the thumbnail cards carry Bootstrap's .card (the .border-0
   that used to sit beside them in the markup is gone), and its border is
   repointed at the shared hairline so the card outline, the rule above the
   date band and the league-news grid beside it are all one colour.

   BOTH declarations are needed, and the second is the one that paints.
   generic/assets/css/global-theme-reset-bs5.css normalises border behaviour
   with a flat ".card { border-color: var(--bs-border-color) }" — a direct
   longhand, not the Bootstrap custom property — so setting
   --bs-card-border-color alone changes nothing: the token resolves correctly
   and is then overwritten by that reset (measured: token #e9ecef, painted
   border #dee2e6). This sheet is injected by cssloader AFTER the shared
   resets, so an equal-specificity border-color here wins on source order.
   The token line is kept so anything reading --bs-card-border-color — a
   theme override, a nested component — still sees the right value.

   Scoped to .cg-card--thumbnail: the hero is an image-overlay card and keeps
   its .border-0. */
.cg-card--thumbnail {
    --bs-card-border-color: var(--cg-card-border);
    border-color:           var(--cg-card-border);
}

.cg-card-title {
    color:       var(--cg-card-title-color);
    font-size:   var(--cg-card-title-size);
    line-height: 1.2;
}

/* Meta row — category + date sit inline, wrapping to a second line on a narrow
   card. The flex layout + gaps live here (not on a .gap-2 utility in the
   markup) so the two axes can differ: column-gap supplies the inline spacing,
   while row-gap is zeroed — a wrap must NOT open the large vertical gap the
   `gap` shorthand would otherwise apply to both axes. */
.cg-card-meta {
    color:       var(--cg-card-meta-color);
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    column-gap:  var(--cg-card-meta-gap, 0.5rem);
    row-gap:     0;
}

.cg-card-meta .cg-card-category {
    font-weight: 600;
}

.cg-card-leadin {
    color:       var(--cg-card-leadin-color);
    font-size:   var(--cg-card-leadin-size);
    line-height: 1.4;
}

/* Image — Bootstrap's .object-fit-cover utility handles crop. The
   rule below duplicates it on .cg-card-image so the crop survives
   any project build that strips the utility class. */
.cg-card-image {
    object-fit: cover;
    width:      100%;
    height:     100%;
}

/* HERO — image fills, gradient overlay for title readability. The
   gradient sits on the .card-img-overlay (BS5 absolutely-positioned
   layer) so it always covers the full image without re-layout. */
.cg-card--hero .cg-card-overlay {
    background-image: var(--cg-card-hero-overlay-gradient);
    padding:          1rem;
}

/* The .card-img inside .cg-card--hero is wrapped in a .ratio so the
   card's aspect is enforced. The img itself fills the ratio box. */
.cg-card--hero .cg-card-image-wrap {
    /* .ratio gives the wrapper its dimensions; this just ensures the
       image fills it on browsers that don't enforce min-content sizing. */
    inline-size: 100%;
}

.cg-card--hero .card-img {
    border-radius: inherit;
}

/* Hero text colors — text-bg-dark on the card sets white text, but
   the meta has a `text-body-secondary`-style override above. In a
   dark overlay context, push meta to a translucent white for contrast. */
.cg-card--hero .cg-card-meta {
    color: rgba(255, 255, 255, 0.85);
}

/* COMPACT — left image at fixed width, body fills remaining row. */
.cg-card--compact .cg-card-image-wrap {
    inline-size: var(--cg-card-compact-image-width);
}

/* Title link — uses the theme link color (--body-link, falling back to
   --bs-link-color) so card
   titles read as links and stay consistent with every other component
   link (and the base `a` rule) across the landing + internal pages,
   distinguishable from body text (ADA). The hero overlay variant is the
   exception: its .text-reset in markup (color: inherit !important)
   overrides this, keeping the light text over its photo. Underline on
   hover for accessibility (the stretched-link area itself is invisible
   so the visible cue lives on the title). */
.cg-card-title-link {
    color: var(--body-link, var(--bs-link-color));
}
.cg-card-title-link:hover,
.cg-card-title-link:focus-visible {
    text-decoration: underline;
}


/* ------------------------------------------------------------
   Card presentation refinements
   ------------------------------------------------------------ */

/* Equal-height cards in a CSS Grid row — when the parent is a grid
   (.cg-headlines-grid), tracks default to align-items: stretch so each
   grid item fills its row's max height. The .cg-card article inside
   then needs to fill the grid item; flex-direction:column on the wrapper
   + flex-grow on the body lets the card chrome (image) stay top-aligned
   and the body absorb the extra height. */
.cg-headlines-grid {
    align-items: stretch;
}

/* Grid-card size convention — title at 1rem (vs the 1.25rem card
   default), with meta + related rows kept at the `small` size via
   their markup helper class.

   The token is re-declared on .cg-card *itself* — not on the
   .cg-headlines-grid ancestor — on purpose: .cg-card sets
   --cg-card-title-size: 1.25rem on its own element, which shadows any
   value inherited from an ancestor. Setting it here (same element,
   higher specificity than the bare .cg-card rule) is what actually
   wins, and the value then flows into .cg-card-title / its
   .cg-card-title-link. Applies to grid cards in BOTH layout-card
   (More News) and layout-featured's thumb grid; the featured hero
   sits outside the grid, so it keeps the larger 1.25rem default. */
.cg-headlines-grid .cg-card {
    --cg-card-title-size: 1rem;
}

.cg-headlines-grid-item {
    display:        flex;
    flex-direction: column;
}

.cg-headlines-grid-item > .cg-card {
    flex:           1 1 auto;
    height:         100%;
    display:        flex;
    flex-direction: column;
}

.cg-headlines-grid-item > .cg-card > .card-body {
    flex: 1 1 auto;     /* body stretches to fill remaining height */
}

/* Date band (datePlacement=footer) sits below the stretched body, so it
   pins to the base of every card in the row and the dates line up across
   the track whatever the title and leadin heights are. */
.cg-headlines-grid-item > .cg-card > .cg-card-footer {
    flex: 0 0 auto;
    margin-top: auto;
}

/* Meta + related links — both rows render inline (flex-wrap so they
   wrap to a new line on narrow widths) with a flex gap between items.
   No visual separator characters; whitespace alone provides the rhythm. */
.cg-card-related {
    display:   flex;
    flex-wrap: wrap;
    gap:       0.5rem 1rem;     /* row-gap col-gap */
    /* Lift above the title's .stretched-link overlay (its ::after is z-index:1,
       covering the whole card) so these related links (Recap / Box Score / …)
       stay individually clickable. position:relative establishes the stacking. */
    position:  relative;
    z-index:   2;
}

.cg-card-related > li {
    display: inline-flex;
}

/* Hover state — subtle, signals the entire card is clickable
   (the .stretched-link already covers click; this gives visual feedback).
   Image zooms slightly inside its .ratio container (overflow:hidden
   on the wrap traps the overflow); title shifts to the link-hover color
   (--body-link-hover, falling back to --bs-link-hover-color), matching the base `a:hover`. */
.cg-card .cg-card-image-wrap {
    overflow:      hidden;
    border-radius: var(--cg-card-image-radius);   /* rounded thumbnail; overflow:hidden clips the image + blurred bg to it */
}

.cg-card .cg-card-image {
    transition: transform 0.4s ease;
}

/* Blurred backdrop — the same image as a cover layer behind the CONTAINED
   thumbnail (object-fit-contain in markup), so off-ratio art fills the box
   instead of leaving flat bars. A direct child of the .ratio frame, so
   Bootstrap absolute-fills it; source-ordered before the <img> so the sharp
   image sits on top. The hover-zoom transition lives on the image only, not
   this layer. NOT applied to .cg-card--hero (that stays a full-bleed cover).
   scale() hides the soft blur-faded edge that would reveal the box border. */
.cg-card-image-bg {
    background-image:    var(--cg-card-img);
    background-position: center;
    background-size:     cover;
    background-repeat:   no-repeat;
    filter:              blur(20px);
    transform:           scale(1.1);
}

.cg-card:hover .cg-card-image,
.cg-card:focus-within .cg-card-image {
    transform: scale(1.05);
}

.cg-card:hover .cg-card-title-link,
.cg-card:focus-within .cg-card-title-link {
    color: var(--body-link-hover, var(--bs-link-hover-color));
}

/* Image fallback — emitted by the templates when a story has no
   thumbnail. Keeps the card's aspect ratio reserved so layout is
   consistent across cards with/without images. Light grey gradient
   for the standard case; hero variant uses a darker gradient that
   reads correctly with the text-bg-dark overlay. */
.cg-card-image-wrap--placeholder {
    background-image: linear-gradient(135deg,
        var(--bs-gray-200, #e9ecef),
        var(--bs-gray-300, #dee2e6));
}

.cg-card--hero .cg-card-image-wrap--placeholder {
    background-image: linear-gradient(135deg,
        var(--bs-gray-700, #495057),
        var(--bs-gray-900, #212529));
}

/* ============================================================
   COMPONENT: cg-component-headlines (Themes 2.1)
   Per-layout templates render through card-template per entry,
   so the visual treatment of each card is controlled by .cg-card
   classes (above). Rules here own the LAYOUT shell — list / grid /
   slider wrappers and section header integration. The archive button
   is rendered by the shared component footer (see .cg-inc-footer).

   Custom property hooks (override in theme.css):
     --cg-headlines-list-gap         spacing between list items
   ============================================================ */
.component-headlines {
    --cg-headlines-list-gap: 1rem;
}

/* layout-list — vertical stack of cards. .card-template renders
   each item; rules here just handle inter-item spacing and optional
   dividers. */
.component-headlines.layout-list .cg-headlines-list {
    display:        flex;
    flex-direction: column;
}

.component-headlines.layout-list .cg-headlines-list-item:not(:first-child) {
    margin-top: var(--cg-headlines-list-gap);
}

/* Dividers — opt-in via .cg-headlines-list--dividers on the wrapper.
   Adds a top border + matching padding-top to every item after the
   first so the divider line sits between items, not before the first
   one. Uses Bootstrap's border tokens so the rule color tracks the
   theme's overall border scheme. */
.component-headlines.layout-list.cg-headlines-list--dividers .cg-headlines-list-item:not(:first-child) {
    padding-top: var(--cg-headlines-list-gap);
    border-top:  var(--bs-border-width) solid var(--bs-border-color);
}


/* ============================================================
   LAYOUT HELPERS — .sec-split
   Reusable two-column split for rows like 60/40 (Top Headlines |
   Schedule) and main+sidebar (Main Content | Sidebar). Stacks
   single-column below lg and switches to row layout at lg+, with
   the aside at a fixed width / percentage and the main column
   filling the remaining space.

   Sticky behavior is baked in: at lg+ BOTH columns are
   position:sticky with top:var(--sticky-offset), and align-items:start
   on the container lets the shorter column hold in view while the
   taller column scrolls — both exit the viewport base-aligned when the
   row bottom reaches the sticky offset.

   Both columns, not just the aside, because either can be the long
   one (a sidebar of quick links + standings + ads routinely outruns
   the main column). A sticky box can only travel inside its own
   container, so the taller column fills the row, has nowhere to
   travel, and scrolls normally; neither needs to know which it is.
   Same technique as the event-detail rail in global-reset.css.

   Markup pattern:
     <section class="sec sec-{name}">
       <div class="container">
         <div class="sec-split">
           <div class="sec-split-main">  ...flex-grow main column...  </div>
           <div class="sec-split-aside"> ...34% / fixed width...     </div>
         </div>
       </div>
     </section>

   Per-row tuning via the section's named class. Width and minimum
   width are independent tokens. The helper wires them to native CSS
   flex-basis / max-width / min-width — when min-width > max-width the
   CSS spec promotes max-width to match, so the aside auto-scales with
   the ratio on wide viewports and holds at the floor on narrow ones
   without needing math functions in the flex shorthand:

     Default — pure 34% with no floor (≈318px at the lg breakpoint,
     wide enough to host a 300px-wide graphic without a floor):
       .sec-top-content  { --sec-split-aside-width: 34%; }

     Fixed pixel sidebar:
       .sec-main-content { --sec-split-aside-width: 300px; }

     70/30 ratio with a 300px floor (e.g. ad-bearing sidebar):
       .sec-main-content {
           --sec-split-aside-width:     30%;
           --sec-split-aside-min-width: 300px;
       }

   Custom property hooks:
     --sec-split-gap              gap between columns (and between
                                  rows when stacked at mobile)
     --sec-split-aside-width      aside cross-axis size at lg+
                                  (percentage or pixel value)
     --sec-split-aside-min-width  hard floor below which the aside
                                  never shrinks (default: 0)
   ============================================================ */
.sec-split {
    --sec-split-gap:             1.5rem;
    --sec-split-aside-width:     34%;
    --sec-split-aside-min-width: 0px;

    display:        flex;
    flex-direction: column;
    flex-wrap:      nowrap;       /* explicit — never wrap aside onto a new row at lg+ */
    gap:            var(--sec-split-gap);
}

@media (min-width: 992px) {
    .sec-split {
        flex-direction: row;
        align-items:    flex-start;   /* prerequisite for sticky on the shorter column */
    }

    /* min-width: 0 lets the main column shrink below its content's
       intrinsic min-width — needed so long card titles, slider tracks,
       and wide tables don't blow out the column width. flex-basis: 0
       (rather than auto) so the main column doesn't try to claim its
       content's natural width before flex-grow distributes space.
       Sticky for the case where the aside is the taller column. */
    .sec-split-main {
        flex-grow:   1;
        flex-shrink: 1;
        flex-basis:  0;
        min-width:   0;
        position:    sticky;
        top:         var(--sticky-offset);
    }

    /* Aside width is locked via four native CSS properties:
         width         target size (used by some browsers in preference
                       to flex-basis when both are set)
         flex-basis    same target size (per spec, takes precedence
                       over width inside flex containers)
         max-width     ceiling — aside never exceeds the target
         min-width     floor — when min-width > max-width the CSS spec
                       promotes max-width to match min-width, which is
                       exactly the "30% with a 300px floor" outcome
                       without needing max() in flex shorthand.
       flex-shrink: 0 prevents the aside from collapsing when the main
       column's content is wide. */
    .sec-split-aside {
        flex-grow:   0;
        flex-shrink: 0;
        flex-basis:  var(--sec-split-aside-width);
        width:       var(--sec-split-aside-width);
        max-width:   var(--sec-split-aside-width);
        min-width:   var(--sec-split-aside-min-width);
        position:    sticky;
        top:         var(--sticky-offset);
    }
}


/* ============================================================
   COMPONENT-CATALOG PAGE: cg-catpage-*
   Rendered by generic-cld/component-catalog-page-template — the full-page
   "every layout for this content type" view. Built on .sec-split above:
   .cg-catpage-main is a .sec-split-main, .cg-catpage-toc a .sec-split-aside,
   so the sticky/offset/breakpoint behavior is inherited, not redeclared.
   ============================================================ */
.cg-catpage-split {
    /* Narrower than .sec-split's 34% default — the TOC holds short link
       labels, not the graphic/ad-shaped content the default width assumes,
       so the wider default just left it looking oversized. Per-row tuning
       via these tokens, same as .sec-split's own documented pattern. */
    --sec-split-aside-width:     26%;
    --sec-split-aside-min-width: 220px;
}

/* Page-level identity — the heading, and only the heading.

   It carried a collapsed Implementation details block for a while (content
   type + source node). Removed as REDUNDANT rather than restyled: every
   section prints the content type in its own facts list, and every section's
   include snippet opens with the source node, so a block at the top only
   restated what the page repeats below — while reading as a section's block
   with no section above it to own it. The empty state is the exception, and
   its notice names the node itself.

   The class exists so this margin and the sibling rule below can key off the
   heading; the <h1> has no other styling of its own. 2rem is what the block
   that used to sit here contributed, so the first group title still opens at
   the same distance. */
.cg-catpage-title {
    margin: 0 0 2rem;
}

.cg-catpage-empty {
    padding:       .9rem 1rem;
    border-radius: var(--bs-border-radius, .375rem);
    background:    var(--bs-warning-bg-subtle, #fff3cd);
    border:        1px solid var(--bs-warning-border-subtle, #ffe69c);
    color:         var(--bs-emphasis-color, #212529);
}

/* A group title opens a new run of sections, so IT carries the divider for that
   boundary — the section after it has its own zeroed (below).

   It previously carried none, which left the page's biggest break with LESS
   separation than the section-to-section breaks inside it: sections are ruled,
   and a group boundary was whitespace alone. The rule is the same weight as a
   section's; what marks this as the larger break is the space around it —
   3.5rem above and 2.5rem below, against a section's 1.75rem on each side. */
.cg-catpage-group-title {
    margin:      3.5rem 0 1rem;
    padding-top: 2.5rem;
    border-top:  var(--bs-border-width, 1px) solid var(--cg-catpage-divider, var(--bs-border-color, #dee2e6));
    font-size:  .8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color:      var(--cg-catpage-group-color, var(--bs-secondary-color, #6c757d));
}
/* The first group title opens the main column, so it takes no divider and no
   leading gap of its own — the space above it is the identity block's 2rem
   margin-bottom. All three properties are reset together: leaving the border
   behind would put a rule immediately under the page heading, which is the
   same stray rule the section reset below exists to avoid.

   The identity block sits INSIDE .sec-split-main so the sticky TOC starts at
   the top of the page, which means the first group title is no longer
   :first-child; the sibling selector covers that case, and :first-child is
   kept for any column that renders without the identity block. The sibling is
   the heading — it was .cg-catpage-meta, then briefly the page-level
   disclosure, and both of those are gone now. */
.cg-catpage-group-title:first-child,
.cg-catpage-title + .cg-catpage-group-title {
    margin-top:  0;
    padding-top: 0;
    border-top:  0;
}

.cg-catpage-section {
    padding-top: 1.75rem;
    margin-top:  1.75rem;
    border-top:  var(--bs-border-width, 1px) solid var(--cg-catpage-divider, var(--bs-border-color, #dee2e6));
}
/* The first section opens the run, so it takes no divider — whichever heading
   it follows. A page with two or more groups follows a group title; a
   single-group page prints no group title (the heading would only restate the
   <h1>, and on the standings and stat-leaders pages it printed the SAME words
   under two different <h1>s), so there the first section follows the <h1>
   itself. Without the second selector that page opened with a stray rule
   directly beneath the page heading. */
.cg-catpage-group-title + .cg-catpage-section,
.cg-catpage-group-note + .cg-catpage-section,
.cg-catpage-title + .cg-catpage-section { padding-top: 0; margin-top: 0; border-top: 0; }

/* Optional intro line under a group title — the navbar entry's own title, on
   component-catalog-category-template. It sits between the group title and the
   first section, so the reset above names it too: without it, that section
   would open with a divider right under the group's own intro. */
.cg-catpage-group-note {
    margin: -.5rem 0 1.5rem;
    color:  var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}

/* Head is now only the heading and its one-line description — the example
   follows immediately. This margin is the gap between that description and
   the layout it describes. */
.cg-catpage-section-head { margin-bottom: 1rem; }

/* Reference block, below the example: the collapsed Implementation details.
   Held closer to the example above it than to the next heading, so it reads
   as that section's footer and needs no divider of its own —
   .cg-catpage-section supplies a rule plus 3.5rem before the next heading.

   The value has moved twice with the trigger's weight. 1rem was too tight while
   the trigger was a bold full-width bar — several components end in a bordered
   control row, and the bar 16px under one read as cramped; 1.75rem fixed that.
   Now the trigger is a single small glyph, and 1.75rem left it adrift between
   the demo and the next section's rule. 0.75rem puts it back against the
   example it belongs to, while .cg-catpage-section still supplies a rule plus
   3.5rem before the next heading. */
.cg-catpage-section-foot { margin-top: .75rem; }
/* One size whatever the level. The heading is <h3> on a page with groups and
   <h2> on one without, and the theme's bare h2/h3 sizes made "Main menu" (h2,
   Navigation) render a full step larger than "Hero carousel" (h3, Heroes) for
   the same kind of heading. The level carries the outline; the class pins the
   look. (0,1,0) beats the theme's bare h2/h3 rules. */
.cg-catpage-section-title {
    margin:    0 0 .25rem;
    font-size: var(--cg-catpage-section-title-size, clamp(1.25rem, 1.15rem + .4vw, 1.5rem));
}
.cg-catpage-section-note {
    margin:    0 0 .35rem;
    color:     var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}
/* Facts list — template / content type / source node. Replaces the single
   .cg-catpage-src line, which carried only the template name and the override
   source; `types` joined it once the page started answering "what does this
   bind to?" as well as "what file is it?".

   A <dl> laid out as a two-column grid: the term column sizes to its longest
   label (max-content) and the value column takes the rest. Row-per-pair comes
   from the grid itself, so no <div> wrapper per pair is needed and the dl stays
   a plain dl for assistive tech. Collapses to a stacked list on a phone, where
   two columns would squeeze the values. */
.cg-catpage-facts {
    display:               grid;
    grid-template-columns: max-content 1fr;
    gap:                   .15rem .75rem;
    margin:                0;
    font-size:             .8rem;
    color:                 var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}
.cg-catpage-facts dt { font-weight: 600; }
.cg-catpage-facts dd { margin: 0; }
.cg-catpage-facts code,
.cg-catpage-snippet code,
.cg-catpage-params code,
.cg-catpage-dev-note code {
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
}

@media (max-width: 575.98px) {
    .cg-catpage-facts {
        grid-template-columns: 1fr;
        gap:                   0;
    }
    .cg-catpage-facts dd { margin-bottom: .35rem; }
}

/* ------------------------------------------------------------
   Implementation disclosure — the include snippet + parameter table, collapsed.

   This page serves two audiences at once: a site admin reviewing layouts, and a
   theme developer deciding what to build with. Everything in here is for the
   second one, so it sits behind a native <details> rather than in the flow —
   the first audience scrolls past one line instead of a table.
   ------------------------------------------------------------ */
/* COLLAPSED, THIS IS ONE GLYPH — not a bar. It was a full-width bordered,
   filled strip carrying the words "Implementation details", once per section:
   eleven times on the navigation page, thirty-one across the catalog. At that
   weight it read as page furniture competing with the examples, because a
   bordered full-width strip is exactly what a component's OWN controls look
   like — .cg-inc-footer-archive is one, and the two sat inches apart. The
   surface moved to the open body below, so the closed state costs one icon
   button at the trailing edge and nothing else.

   The glyph REPLACES the native disclosure marker rather than sitting beside
   it. A code icon was added alongside the triangle once before and reverted the
   same day: two glyphs on one short line competed instead of dividing the work.
   One is the point. .cg-catpage-dev itself now declares nothing, so it is gone
   rather than left as an empty rule. */
.cg-catpage-dev-summary {
    /* 2rem round target — the icon-button geometry the schedule's
       .cg-event-panel-link already uses, and clear of the 24px minimum target
       size. A fixed width plus an auto start-margin keeps the clickable area ON
       the glyph instead of spanning the whole row, so clicking empty space
       beside it does nothing surprising. */
    display:             flex;
    align-items:         center;
    justify-content:     center;
    width:               2rem;
    height:              2rem;
    margin-inline-start: auto;
    border-radius:       50%;
    cursor:              pointer;
    color:               var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
    transition:          color .15s ease, background-color .15s ease;
    /* Hide the native triangle. list-style covers Firefox and Chrome, the
       pseudo-element covers Safari. The display:flex above already drops
       display:list-item, so this is belt and braces rather than the mechanism. */
    list-style:          none;
}
.cg-catpage-dev-summary::-webkit-details-marker { display: none; }

/* Hover, focus and open share one treatment — the control is the same control
   in all three. The focus ring is the browser default, already visible here;
   only the radius had to follow the round target, which border-radius above
   already does. Open state is NOT carried by colour alone: the panel appearing
   below is the primary signal. */
.cg-catpage-dev-summary:hover,
.cg-catpage-dev-summary:focus-visible,
.cg-catpage-dev[open] > .cg-catpage-dev-summary {
    color:      var(--body-link-hover, var(--bs-link-hover-color));
    background: var(--cg-catpage-toc-bg, var(--bs-tertiary-bg, #f8f9fa));
}

/* The panel surface lives here now — it only exists while the disclosure is
   open, which is the whole change. */
.cg-catpage-dev-body {
    margin-top:    .5rem;
    padding:       .6rem .85rem .85rem;
    border:        var(--bs-border-width, 1px) solid var(--cg-catpage-divider, var(--bs-border-color, #dee2e6));
    border-radius: var(--bs-border-radius, .375rem);
    background:    var(--cg-catpage-toc-bg, var(--bs-tertiary-bg, #f8f9fa));
    font-size:     .84rem;
}

.cg-catpage-dev-title {
    margin:         1rem 0 .4rem;
    font-size:      .78rem;
    font-weight:    700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color:          var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}
.cg-catpage-dev-title:first-child { margin-top: 0; }

.cg-catpage-dev-note {
    margin:    .4rem 0 0;
    font-size: .8rem;
    color:     var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}

/* Include snippet — one long line by nature (node + template + params), so it
   scrolls in its own box rather than wrapping into something that no longer
   reads as a single copyable statement. */
.cg-catpage-snippet {
    margin:        0;
    padding:       .6rem .75rem;
    border-radius: var(--bs-border-radius-sm, .25rem);
    background:    var(--bs-body-bg, #fff);
    border:        var(--bs-border-width, 1px) solid var(--cg-catpage-divider, var(--bs-border-color, #dee2e6));
    overflow-x:    auto;
    font-size:     .8rem;
    line-height:   1.5;
}

/* Parameter table. Its scroll container is the wrapper, not the table, so the
   table keeps its own display type (and with it the row/column semantics
   assistive tech reads) while still never widening the page. */
.cg-catpage-params-scroll {
    overflow-x: auto;
}

.cg-catpage-params {
    width:           100%;
    border-collapse: collapse;
    font-size:       .8rem;
}
.cg-catpage-params th,
.cg-catpage-params td {
    padding:     .3rem .6rem .3rem 0;
    text-align:  left;
    vertical-align: top;
    border-bottom: var(--bs-border-width, 1px) solid var(--cg-catpage-divider, var(--bs-border-color, #dee2e6));
}
.cg-catpage-params th {
    font-weight: 700;
    white-space: nowrap;
}
.cg-catpage-params tbody tr:last-child th,
.cg-catpage-params tbody tr:last-child td {
    border-bottom: 0;
}

.cg-catpage-toc-inner {
    padding:       1rem 1.1rem;
    border-radius: var(--bs-border-radius, .375rem);
    background:    var(--cg-catpage-toc-bg, var(--bs-tertiary-bg, #f8f9fa));
    border:        1px solid var(--cg-catpage-toc-border, var(--bs-border-color, #dee2e6));
    max-height:    calc(100vh - var(--sticky-offset) - 2rem);
    overflow-y:    auto;
}
.cg-catpage-toc-title {
    margin:     0 0 .6rem;
    font-size:  .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color:      var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}
.cg-catpage-toc-group {
    margin:     .8rem 0 .3rem;
    font-size:  .68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color:      var(--cg-catpage-note-color, var(--bs-secondary-color, #6c757d));
}
.cg-catpage-toc-group:first-child { margin-top: 0; }
.cg-catpage-toc-list { gap: .1rem; }
.cg-catpage-toc-link {
    padding:       .25rem .5rem;
    border-radius: var(--bs-border-radius-sm, .25rem);
    font-size:     .84rem;
    color:         var(--cg-catpage-toc-link, var(--body-link, var(--bs-link-color)));
}
.cg-catpage-toc-link:hover {
    background: var(--cg-catpage-toc-hover-bg, var(--bs-tertiary-bg-rgb, rgba(0,0,0,.04)));
}
.cg-catpage-toc-link.active {
    font-weight: 600;
    background:  var(--cg-catpage-toc-active-bg, rgba(13,110,253,.1));
    color:       var(--cg-catpage-toc-active-color, var(--body-link, var(--bs-link-color)));
}

/* Category page sidebar (component-catalog-category-template): two cards, not
   one. "On this page" and "Other components" shared a single tinted card split
   by a rule, and reviewers read them as one list — the page's own sections
   and the rest of the catalog ran together. --stacked moves the card styling
   from the wrapper onto each <nav>, so each list is its own box: "On this
   page" keeps the tinted fill, "Other components" sits on the page background
   with only a border. The wrapper keeps the sticky max-height and scroll.
   component-catalog-page-template's single-list sidebar has no modifier and
   keeps the one-card look above. */
.cg-catpage-toc-inner--stacked {
    display:       flex;
    flex-direction: column;
    gap:           1rem;
    padding:       0;
    border:        0;
    border-radius: 0;
    background:    none;
}
.cg-catpage-toc-inner--stacked > nav {
    padding:       1rem 1.1rem;
    border-radius: var(--bs-border-radius, .375rem);
    background:    var(--cg-catpage-toc-bg, var(--bs-tertiary-bg, #f8f9fa));
    border:        1px solid var(--cg-catpage-toc-border, var(--bs-border-color, #dee2e6));
}
.cg-catpage-toc-inner--stacked > .cg-catpage-toc-other {
    background: var(--cg-catpage-toc-other-bg, var(--bs-body-bg, #fff));
}

/* "[icon] Text" entries in "Other components" — the same icons the header
   Components dropdown shows (component-catalog-category-nav-helper). Fixed
   width so the labels stay aligned; decorative, the text names the link. */
.cg-catpage-toc-icon {
    display:      inline-block;
    width:        1.25em;
    margin-right: .4rem;
    text-align:   center;
}

/* The current category in "Other components" — marked with aria-current, not
   .active: .active already means "the section in view" in "On this page", and
   two filled rows would compete. A bold weight alone was too quiet to find,
   so it takes an accent bar on its leading edge and the body text color
   instead: a "you are here" mark that cannot be mistaken for the filled
   scroll highlight. Not a link color, because it is not where you go next. */
.cg-catpage-toc-link[aria-current="page"] {
    font-weight:   600;
    color:         var(--cg-catpage-toc-current-color, var(--bs-emphasis-color, #212529));
    border-radius: 0 var(--bs-border-radius-sm, .25rem) var(--bs-border-radius-sm, .25rem) 0;
    box-shadow:    inset 3px 0 0 var(--cg-catpage-toc-current-bar, var(--bs-primary, #0d6efd));
}


/* ============================================================
   COMPONENT HEADER: cg-inc-header
   Bootstrap 5 component header rendered by component-inc-header.
   One flex row holding title (with optional FA icon prefix) + sponsor
   (inline beside the title) + optional thumbnail + optional archive
   link pushed to the row end. Leadin paragraph sits below the row.

   Two ways to get an icon before the title:
     1. Content-driven, cross-theme — a "[Key]" bracket prefix in the
        CMS title (resolved by icon-map-helper → .cg-inc-header-icon
        span). Same icon wherever that page renders.
     2. Theme-driven, per-section — set --cg-inc-header-icon on any
        ancestor (typically a theme's .sec-* wrapper in theme.css). A
        decorative ::before glyph on .cg-inc-header-title-text. Dormant
        by default (content:none) so it costs nothing until a theme
        opts a section in. Use one or the other, not both, per heading.

   Custom property hooks (override in theme.css):
     --cg-inc-header-title-color    title color (inherit by default)
     --cg-inc-header-title-size     title font-size
     --cg-inc-header-title-text-transform  title text-transform (default none)
     --cg-inc-header-icon-color     icon color (defaults to title color)
     --cg-inc-header-icon-size      icon glyph size (defaults to inherit)
     --cg-inc-header-icon           decorative ::before glyph (e.g. "\f456");
                                    unset → no glyph
     --cg-inc-header-icon-weight    decorative glyph weight (900 solid* / 400 regular)
     --cg-inc-header-icon-gap       gap between decorative glyph and title text
     --cg-inc-header-sponsor-img-mh sponsor logo max-height
     --cg-inc-header-thumb-mh       inline thumbnail max-height
     --cg-inc-header-archive-color  archive link color (BS link default)
   ============================================================ */
.cg-inc-header {
    --cg-inc-header-title-color:          inherit;
    --cg-inc-header-title-size:           1.5rem;
    --cg-inc-header-title-text-transform: none;
    --cg-inc-header-icon-color:     currentColor;
    --cg-inc-header-icon-size:      1em;
    --cg-inc-header-sponsor-img-mh: 2rem;
    --cg-inc-header-thumb-mh:       2.5rem;
    --cg-inc-header-archive-color:  var(--body-link, var(--bs-link-color));
}

.cg-inc-header-title {
    color:          var(--cg-inc-header-title-color);
    font-size:      var(--cg-inc-header-title-size);
    text-transform: var(--cg-inc-header-title-text-transform);
    line-height:    1.2;
}

.cg-inc-header-icon {
    color:     var(--cg-inc-header-icon-color);
    font-size: var(--cg-inc-header-icon-size);
    /* line-height: 1 keeps the icon's optical box tight against
       the title's cap-height when the FA glyph has built-in padding. */
    line-height: 1;
}

/* Theme-driven decorative icon (approach #2 above). Dormant unless a
   theme sets --cg-inc-header-icon on an ancestor: content falls back
   to `none`, which generates no pseudo-element box, so unstyled
   headers render exactly as before. Inherits the title's color/size;
   weight selects the FA face (900 solid, 400 regular, 300 light). */
.cg-inc-header-title-text::before {
    content:      var(--cg-inc-header-icon, none);
    font-family:  var(--cg-icon-font);
    font-weight:  var(--cg-inc-header-icon-weight, 900);
    margin-right: var(--cg-inc-header-icon-gap, 0.4em);
    color:        var(--cg-inc-header-icon-color);
    /* keep the glyph aligned with the title cap-height like the span variant */
    line-height:  1;
    -webkit-font-smoothing: antialiased;
}

.cg-inc-header-sponsor-img,
.cg-inc-header-thumb {
    /* Literal fallback so the rule survives OUTSIDE a .cg-inc-header
       ancestor: the token is declared on .cg-inc-header, and an unresolved
       var() here would be invalid at computed-value time, dropping
       max-height entirely and rendering the logo at full natural size.
       See .cg-inc-sponsor-foot below, which reuses this markup. */
    max-height: var(--cg-inc-header-sponsor-img-mh, 2rem);
    width:      auto;
    display:    block;
}

/* SPONSOR STRIP AT THE FOOT
   Where a component puts its sponsor when it is NOT rendering a heading band
   to host it. Sits after the component body and BEFORE .cg-inc-footer, so an
   archive link / carousel controls stay the last thing in the component. The
   strip reuses the .cg-inc-header-sponsor markup verbatim — one source of
   sponsor styling — and re-declares the logo max-height token because there
   is no .cg-inc-header ancestor here to inherit it from. */
.cg-inc-sponsor-foot {
    --cg-inc-header-sponsor-img-mh: 2rem;

    display:         flex;
    justify-content: center;
    margin-top:      var(--cg-inc-sponsor-foot-mt, 1.25rem);
}

.cg-inc-header-thumb {
    max-height: var(--cg-inc-header-thumb-mh);
    border-radius: var(--bs-border-radius, 0.375rem);
}

/* Block-level page image above the header row — the node's thumbnailURL,
   rendered full-width and centered above .cg-inc-header-row. Mirrors the
   primary-navigation dropdown panel header image (.dropdown-panel-header-img)
   so the page image reads consistently across every cg- component header.
   Distinct from the opt-in inline .cg-inc-header-thumb above. */
.cg-inc-header-image {
    text-align: center;
}
.cg-inc-header-image-img {
    max-width:     100%;
    height:        auto;
    display:       block;
    margin:        0 auto 0.75rem;
}

.cg-inc-header-archive {
    display:         inline-flex;
    align-items:     center;
    gap:             0.25rem;
    color:           var(--cg-inc-header-archive-color);
    font-weight:     600;
    text-decoration: none;
    white-space:     nowrap;
}

.cg-inc-header-archive:hover,
.cg-inc-header-archive:focus-visible {
    text-decoration: underline;
}

.cg-inc-header-archive-chevron {
    font-size:   1.1em;
    line-height: 1;
}


/* ============================================================
   COMPONENT BODY: cg-inc-body
   Optional content-region wrapper sitting between .cg-inc-header and
   .cg-inc-footer. Unlike the header/footer it is NOT a shared include
   — there's no reusable body logic to extract — it's a structural
   class each component wraps its own content in, so every
   .ps-component renders a consistent header / body / footer triad and
   themes get one predictable hook to recess / background / pad the
   content region as a unit. For carousel components it also groups the
   below-the-cards .cg-carousel-controls with the slides.

   Custom property hooks (override in theme.css):
     --cg-inc-body-padding   inset around the content region (default 0)
   ============================================================ */
.cg-inc-body {
    --cg-inc-body-padding: 0;
    padding: var(--cg-inc-body-padding);
}

/* Nested sub-heading reset — a .cg-inc-header that sits INSIDE a .cg-inc-body
   (e.g. a switcher nested in another component's body) is a sub-heading, not a
   top-level component band. Themes commonly paint the top-level .cg-inc-header
   as a band (background / padding / texture / link colours); this baseline
   neutralises those so the band can NEVER leak onto a nested header — in any
   theme. The descendant selector (0,2,0) out-specifies a theme's plain
   .cg-inc-header (0,1,0), so it wins without !important; only background /
   padding / colour / link tokens are touched (band-prone props), and a
   theme texture pseudo is killed via content:none. */
.cg-inc-body .cg-inc-header {
    margin-bottom:         0;   /* nested header manages its own spacing */
    padding:               0;
    background:            transparent;
    color:                 inherit;
    /* Reset the link tokens so a band's link color can't leak onto a nested
       header. cg- links read --body-link first (then --bs-link-color), so both
       families are reset. */
    --body-link:            inherit;
    --body-link-hover:      inherit;
    --bs-link-color:       inherit;
    --bs-link-hover-color: inherit;
}
.cg-inc-body .cg-inc-header::before {
    content: none;
}


/* ============================================================
   COMPONENT FOOTER: cg-inc-footer
   Bottom-of-component counterpart to .cg-inc-header, rendered by
   component-inc-footer. Hosts a full-width block "archive" button
   (e.g. "More Headlines" / "More Videos" / "View All") linking back to
   the component's powered-by file. Any cg- component can activate it.

   Custom property hooks (override in theme.css):
     --cg-inc-footer-mt              space above the footer
     --cg-inc-footer-gap             gap between archive button + controls
     --cg-inc-footer-archive-color   button text color
     --cg-inc-footer-archive-bg      button background
     --cg-inc-footer-archive-border  button border
     --cg-inc-footer-archive-radius  button corner radius
     --cg-inc-footer-archive-padding button padding
     --cg-inc-footer-archive-color-hover  hover/focus text color
     --cg-inc-footer-archive-bg-hover     hover/focus background
   ============================================================ */
.cg-inc-footer {
    --cg-inc-footer-mt:                  1rem;
    --cg-inc-footer-gap:                 0.75rem;
    --cg-inc-footer-archive-color:       var(--body-link, var(--bs-link-color));
    --cg-inc-footer-archive-bg:          transparent;
    --cg-inc-footer-archive-border:      var(--bs-border-width) solid var(--bs-border-color);
    --cg-inc-footer-archive-radius:      var(--bs-border-radius);
    --cg-inc-footer-archive-padding:     0.75rem 1rem;
    --cg-inc-footer-archive-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-inc-footer-archive-bg-hover:    var(--bs-tertiary-bg, rgba(0, 0, 0, 0.03));

    margin-top: var(--cg-inc-footer-mt);

    /* MOBILE-FIRST (< md): stack as a column. The carousel controls
       render on their OWN ROW, ABOVE the archive link, centered. The
       archive is DOM-first and the controls DOM-last, so column-reverse
       lifts the controls to the top; align-items:center centers the
       controls cluster (the archive overrides to stretch full-width
       below). */
    display:        flex;
    flex-direction: column-reverse;
    align-items:    center;
    gap:            var(--cg-inc-footer-gap);
}

/* DESKTOP (>= md): a single row. justify-content:center centers the
   controls when they're the ONLY child (archive-less components like the
   banner carousel). When an archive IS present it flex-grows to consume
   all the row's slack, leaving no free space for justify-content to
   distribute — so the controls settle flush at the far right. One rule
   handles both cases, no modifier class needed. */
@media (min-width: 768px) {
    .cg-inc-footer {
        flex-direction:  row;
        flex-wrap:       nowrap;
        align-items:     stretch;
        justify-content: center;
    }
}

/* Archive — full-width block button on mobile (its own row, below the
   controls via the column-reverse container); on desktop it grows to
   fill the row and push the controls cluster to the far right. */
.cg-inc-footer-archive {
    display:         flex;
    align-items:     center;
    justify-content: center;
    align-self:      stretch;     /* full cross-axis width in the mobile column */
    padding:         var(--cg-inc-footer-archive-padding);
    color:           var(--cg-inc-footer-archive-color);
    background:      var(--cg-inc-footer-archive-bg);
    border:          var(--cg-inc-footer-archive-border);
    border-radius:   var(--cg-inc-footer-archive-radius);
    font-weight:     600;
    text-decoration: none;
    transition:      color .2s ease, background-color .2s ease, border-color .2s ease;
}

@media (min-width: 768px) {
    .cg-inc-footer-archive {
        flex:      1 1 auto;      /* grow to fill the row, pushing controls right */
        min-width: 0;             /* allow the label to shrink/wrap before overflowing */
    }
}

.cg-inc-footer-archive:hover,
.cg-inc-footer-archive:focus-visible {
    color:           var(--cg-inc-footer-archive-color-hover);
    background:      var(--cg-inc-footer-archive-bg-hover);
    text-decoration: none;
}

/* CENTERED modifier — the archive button sits centered at its natural
   width instead of spanning the row. For footers where the button is a
   secondary affordance rather than the component's main call to action
   (the jumbotron's "view the source file" link). Only the ITEM is
   overridden: align-self beats the container's align-items in both the
   mobile column (stretch) and the desktop row (stretch), and the desktop
   row already centers its content once the button stops flex-growing.
   Two-class specificity so this wins regardless of source order. */
.cg-inc-footer--center .cg-inc-footer-archive {
    align-self: center;
    flex:       0 0 auto;
}

/* Carousel controls cluster — shared by the footer and the header band
   (component-inc-header's carousel slot). Adopts the archive button's
   look (bordered, matching padding + link colors) via the shared
   .cg-carousel-control token overrides, so the prev/next cluster reads as
   a sibling of the "More Headlines" archive button in whichever band
   hosts it. The cluster itself never grows, never wraps its arrows onto a
   second line, and shrinks only if the row truly runs out of room.
   Right/center positioning is owned by the host band — see below. */
.cg-inc-footer-carousel-controls,
.cg-inc-header-carousel-controls {
    --cg-carousel-control-color:         var(--body-link, var(--bs-link-color));
    --cg-carousel-control-hover:         var(--body-link-hover, var(--bs-link-hover-color));
    --cg-carousel-control-bg-hover:      var(--bs-tertiary-bg, rgba(0, 0, 0, 0.03));
    --cg-carousel-control-size:          1rem;
    --cg-carousel-control-padding:       var(--cg-inc-footer-archive-padding, 0.5rem 0.75rem);
    --cg-carousel-control-border-width:  var(--bs-border-width);
    --cg-carousel-control-border-style:  solid;
    --cg-carousel-control-border-color:  var(--bs-border-color);
    --cg-carousel-control-border-radius: var(--bs-border-radius);

    display:     inline-flex;
    align-items: stretch;
    flex:        0 1 auto;
    flex-wrap:   nowrap;
    white-space: nowrap;
    gap:         0.25rem;
}

/* Header band — the controls share a row with the section title (which
   does not flex-grow), so they need an auto start-margin to pin to the
   far right. The footer doesn't need this: its archive grows to push the
   controls right (desktop) and its container centers them when alone /
   stacks them on mobile. */
.cg-inc-header-carousel-controls {
    margin-inline-start: auto;
}


/* ============================================================
   COMPONENT: cg-component-banner / layout-carousel
   Pure-BS5 banner carousel rendered by component-navbar-banner-carousel-template.
   Each slide is one page of up to showSize banner graphics laid out in
   a responsive grid; the prev/next + pause controls live in the shared
   .cg-inc-footer (center-aligned by default). BS5 owns the slide
   mechanics (.carousel / .carousel-fade / data-bs-ride); this block
   only styles the banner grid + image.

   Custom property hooks (override in theme.css):
     --cg-banner-gap        gap between banners when showSize > 1
     --cg-banner-radius     image corner radius
     --cg-banner-cols       columns per slide (set inline from showSize;
                            do not hand-edit — the template forwards it)
     --cg-banner-button-*   the showButton pill (see its block below)
   With mobileShowSize the template renders the banners twice: the
   showSize view (.cg-banner-view, lg+) and the small view
   (.cg-banner-view--sm, below lg), each with its own carousel and
   controls; Bootstrap utilities on the wrappers do the switching.
   ============================================================ */
.component-banner.layout-carousel {
    --cg-banner-gap:    1rem;
    --cg-banner-radius: 0;
    --cg-banner-button-color:        currentColor;
    --cg-banner-button-bg:           transparent;
    --cg-banner-button-border-color: currentColor;
    --cg-banner-button-radius:       50rem;
    --cg-banner-button-padding:      0.25rem 1rem;
    --cg-banner-button-font-size:    0.875rem;
    --cg-banner-button-line-height:  1.5;
    --cg-banner-button-mt:           0.5rem;
}

/* One carousel page — up to showSize banners. minmax(0,1fr) lets the
   tracks shrink below their intrinsic image width instead of overflowing
   the slide. */
.component-banner .cg-banner-grid {
    display:               grid;
    grid-template-columns: repeat(var(--cg-banner-cols, 1), minmax(0, 1fr));
    gap:                   var(--cg-banner-gap);
    align-items:           stretch;
}

/* Banners are wide graphics — collapse to a single column on small
   viewports regardless of showSize so each stays legible. */
@media (max-width: 575.98px) {
    .component-banner .cg-banner-grid {
        grid-template-columns: 1fr;
    }
}

/* rotate=one -- a looping row that always shows showSize banners and slides
   by one (banner-ticker.js moves the track). Matches only that markup, so
   every page-mode carousel is untouched. Cards are the same width as the
   grid's: (row - (cols - 1) gaps) / cols. The script turns the transition
   off under prefers-reduced-motion. */
.component-banner .cg-banner-ticker-viewport {
    overflow: hidden;
}
.component-banner .cg-banner-ticker-track {
    display:    flex;
    gap:        var(--cg-banner-gap);
    transition: transform 0.6s ease;
    will-change: transform;
}
.component-banner .cg-banner-ticker-track > .cg-banner-item {
    flex: 0 0 calc((100% - (var(--cg-banner-cols, 1) - 1) * var(--cg-banner-gap)) / var(--cg-banner-cols, 1));
    min-width: 0;
}
@media (prefers-reduced-motion: reduce) {
    .component-banner .cg-banner-ticker-track {
        transition: none;
    }
}

.component-banner .cg-banner-item {
    position: relative;
}

.component-banner .cg-banner-link {
    display:         block;
    text-decoration: none;
}

.component-banner .cg-banner-image {
    display:       block;
    /* img-fluid behavior: cap at the container width but NEVER upscale past
       the image's natural size (width:100% was stretching small artwork and
       blurring it). */
    max-width:     100%;
    height:        auto;
    /* Center the image in its cell when it's narrower than the container. */
    margin-inline: auto;
    border-radius: var(--cg-banner-radius);
}

/* Optional caption overlay (showTitle=true) — centered band over the
   foot of the banner. Off by default; the artwork usually carries its
   own text. */
.component-banner .cg-banner-caption {
    position:   absolute;
    left:       0;
    right:      0;
    bottom:     0;
    padding:    0.75rem 1rem;
    text-align: center;
    color:      var(--cg-banner-caption-color, #fff);
    background: var(--cg-banner-caption-bg, rgba(0, 0, 0, 0.5));
    font-weight: 600;
}

/* Hover-reveal caption (captionReveal=hover): the caption becomes a full-cover
   panel with its text centered on both axes, hidden below the image and
   transparent by default. It slides up + fades in when the banner is hovered
   or focused, and fades/slides back out on leave. The item clips the panel so
   it stays hidden until revealed. (The default always-visible showTitle
   caption stays a bottom band.) */
.component-banner.caption-reveal-hover .cg-banner-item {
    overflow: hidden;
}
.component-banner.caption-reveal-hover .cg-banner-caption {
    inset:           0;   /* full cover — overrides the base bottom-band offsets */
    display:         flex;
    align-items:     center;
    justify-content: center;
    opacity:         0;
    transform:       translateY(100%);
    transition:      opacity .25s ease, transform .25s ease;
}
/* showButton: the title over the pill. Only a caption with the pill
   carries the --button modifier, so every other caption keeps the rule
   above as it was. */
.component-banner.caption-reveal-hover .cg-banner-caption--button {
    flex-direction: column;
}
.component-banner.caption-reveal-hover .cg-banner-item:hover .cg-banner-caption,
.component-banner.caption-reveal-hover .cg-banner-item:focus-within .cg-banner-caption {
    opacity:   1;
    transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
    .component-banner.caption-reveal-hover .cg-banner-caption {
        transition: opacity .25s ease;
        transform:  none;
    }
}

/* Call-to-action pill (showButton=true) -- a label inside the banner's
   one link, not a second link, under the caption title: an outline pill
   in the caption's ink, filled on hover / focus of the banner when a
   theme sets the -hover hooks.
     --cg-banner-button-color / -bg / -border-color   rest
     --cg-banner-button-radius / -padding             shape
     --cg-banner-button-font-size / -line-height      type
     --cg-banner-button-mt                            gap under the title
     --cg-banner-button-color-hover / -bg-hover /
     --cg-banner-button-border-color-hover            hover / focus
                                                      (default: unchanged) */
.component-banner .cg-banner-button {
    display:       block;
    width:         fit-content;
    max-width:     100%;
    margin:        var(--cg-banner-button-mt) auto 0;
    padding:       var(--cg-banner-button-padding);
    border:        1px solid var(--cg-banner-button-border-color);
    border-radius: var(--cg-banner-button-radius);
    background:    var(--cg-banner-button-bg);
    color:         var(--cg-banner-button-color);
    font-size:     var(--cg-banner-button-font-size);
    font-weight:   400;
    line-height:   var(--cg-banner-button-line-height);
    transition:    color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out;
}
.component-banner .cg-banner-link:hover .cg-banner-button,
.component-banner .cg-banner-link:focus-visible .cg-banner-button {
    color:        var(--cg-banner-button-color-hover, var(--cg-banner-button-color));
    background:   var(--cg-banner-button-bg-hover, var(--cg-banner-button-bg));
    border-color: var(--cg-banner-button-border-color-hover, var(--cg-banner-button-border-color));
}
@media (prefers-reduced-motion: reduce) {
    .component-banner .cg-banner-button {
        transition: none;
    }
}


/* ============================================================
   COMPONENT: cg-component-switcher
   Tabbed content switcher rendered by component-switcher-tabs-template.
   The layout defines the tabs (title + content source); each becomes a
   tab in a heading band, and the body holds BS5 tab panes, each ending in
   a "View More" footer (.cg-inc-footer).

   NAV VARIANTS — the strip carries .cg-switcher-tabs--<variant> (set by
   the template's `variant` param) plus, for tabs/pills, the native BS5
   class:
     underline (default) — heading-styled tabs on a baseline rail with an
                           active-tab underline. Each tab reads like a
                           section heading (.cg-inc-header-title — heading
                           font, ~1.5rem, weight 700). This is the cg-
                           equivalent of BS5.3's .nav-underline (which BS5.2
                           doesn't ship). Styled below; the others fall
                           through to Bootstrap's own .nav-tabs / .nav-pills.
     tabs                — classic bordered folder tabs (.nav-tabs).
     pills               — rounded pill buttons (.nav-pills).
   The `fill` param adds .nav-fill / .nav-justified (width distribution) and
   `align` adds .justify-content-* (when not filled) — both standard BS5.
   Neutral by default — themes recolor via the tokens below (Reign sets the
   heading font + accent colors). The tokens below drive the UNDERLINE look;
   tabs/pills inherit Bootstrap's nav theming.

   Custom property hooks (override in theme.css):
     --cg-switcher-rail-width         thickness of the baseline rail
     --cg-switcher-rail-color         baseline rail color
     --cg-switcher-indicator-width    active-tab underline thickness
     --cg-switcher-indicator-color    active-tab underline color
     --cg-switcher-tab-font-family    tab label font (match section headings)
     --cg-switcher-tab-font-size      tab label size (match section headings)
     --cg-switcher-tab-font-weight    tab label weight
     --cg-switcher-tab-gap            gap between tabs
     --cg-switcher-tab-padding-y      tab vertical padding
     --cg-switcher-tab-color          resting (inactive) tab label color
     --cg-switcher-tab-color-active   active tab label color — defaults to the
                                      RESTING color: in the underline variant the
                                      indicator already marks the active tab, so
                                      the label doesn't recolour (override for a
                                      distinct active label color)
     --cg-switcher-tab-color-hover    hover/focus tab label color
     --cg-switcher-content-pt         space above the pane content
   ============================================================ */
.component-switcher {
    --cg-switcher-rail-width:       var(--bs-border-width, 1px);
    --cg-switcher-rail-color:       var(--bs-border-color, #dee2e6);
    --cg-switcher-indicator-width:  3px;
    --cg-switcher-indicator-color:  var(--body-link-hover, var(--bs-link-hover-color));

    /* Tabs read like section headings (.cg-inc-header-title) — same font,
       size, and weight — so a tab strip matches every other component heading. */
    --cg-switcher-tab-font-family:  var(--bs-heading-font-family, inherit);
    --cg-switcher-tab-font-size:    var(--cg-inc-header-title-size, 1.25rem);
    --cg-switcher-tab-font-weight:  var(--bs-heading-font-weight, 500);
    --cg-switcher-tab-gap:          1.5rem;
    --cg-switcher-tab-padding-y:    0.5rem;
    --cg-switcher-tab-color:        var(--body-link, var(--bs-link-color));
    --cg-switcher-tab-color-active: var(--cg-switcher-tab-color);   /* = resting; the underline marks active */
    --cg-switcher-tab-color-hover:  var(--body-link-hover, var(--bs-link-hover-color));

    --cg-switcher-content-pt:       1.5rem;
}

/* Heading band — holds an optional title + the tab strip. Its background /
   color / padding come from the theme's shared .cg-inc-header styling (the band
   carries that class); no switcher-specific paint rule here, so it can't
   out-specify the theme override. */

/* Tab strip — base (shared by every variant). Grows so the alignment
   utility (.justify-content-*) is meaningful, and so .nav-fill /
   .nav-justified distribute across the full row. The visual style
   (rail, borders, pills) is layered on by the variant rules below. */
.component-switcher .cg-switcher-tabs {
    flex: 1 1 auto;
}

/* Tab button — base (shared). inline-flex so the optional FA icon and the
   label sit on one row with a small gap; the look (heading rail vs BS
   tabs/pills) comes from the variant rules. */
.component-switcher .cg-switcher-tab {
    display:     inline-flex;
    align-items: center;
    gap:         0.4rem;
}

/* When the tabs are stretched (fill / justified) BS5 gives each .nav-link
   width:100%; center the icon+label within that stretched button (our
   inline-flex button ignores BS's text-align:center). Applies to any
   variant. */
.component-switcher .cg-switcher-tabs.nav-fill .cg-switcher-tab,
.component-switcher .cg-switcher-tabs.nav-justified .cg-switcher-tab {
    justify-content: center;
}

/* ---- UNDERLINE variant (cg- default) ----
   Heading-styled tabs on a baseline rail with an active-tab underline.
   (BS5.2 has no .nav-underline — this is the cg- equivalent.) The strip
   carries the rail; align-items:flex-end seats every tab on it so the
   active underline lines up. */
.component-switcher .cg-switcher-tabs--underline {
    align-items:   flex-end;
    gap:           var(--cg-switcher-tab-gap);
    border:        0;
    border-bottom: var(--cg-switcher-rail-width) solid var(--cg-switcher-rail-color);
}

.component-switcher .cg-switcher-tabs--underline .nav-item {
    display: flex;
}

/* Underline tab button — styled like a section-heading title
   (.cg-inc-header-title). A transparent bottom border reserves the
   active-underline space and a negative margin overlaps the strip rail
   (the BS nav-tabs technique), so the active tab's indicator sits on the
   rail line. */
.component-switcher .cg-switcher-tabs--underline .cg-switcher-tab {
    padding:       var(--cg-switcher-tab-padding-y) 0;
    border:        0;
    border-bottom: var(--cg-switcher-indicator-width) solid transparent;
    margin-bottom: calc(-1 * var(--cg-switcher-rail-width));
    background:    transparent;
    color:         var(--cg-switcher-tab-color);
    font-family:   var(--cg-switcher-tab-font-family);
    font-size:     var(--cg-switcher-tab-font-size);
    font-weight:   var(--cg-switcher-tab-font-weight);
    line-height:   1.2;
    transition:    color .15s ease, border-color .15s ease;
}

.component-switcher .cg-switcher-tabs--underline .cg-switcher-tab:hover,
.component-switcher .cg-switcher-tabs--underline .cg-switcher-tab:focus-visible {
    color: var(--cg-switcher-tab-color-hover);
}

.component-switcher .cg-switcher-tabs--underline .cg-switcher-tab.active {
    color:               var(--cg-switcher-tab-color-active);
    border-bottom-color: var(--cg-switcher-indicator-color);
}

.component-switcher .cg-switcher-tab-icon {

}

.component-switcher .cg-switcher-content {
    padding-top: var(--cg-switcher-content-pt);
}

/* The tab strip now lives in the body (below the heading), not inline with
   the <h2>. Add a little space between a heading and the tab strip. */
.component-switcher .cg-switcher-header + .cg-switcher-body {
    margin-top: 0.75rem;
}


/* ============================================================
   COMPONENT: cg-component-switcher-select
   Select-dropdown content switcher (Standings, etc.). A heading band
   (styled like .cg-inc-header) carries the title + the <select>; the
   body holds swappable .tab-pane content; an optional footer hosts the
   archive button. The dropdown can also sit in the footer or at the top
   of the body (selectPlacement). Neutral by default — themes recolor the
   band (Reign paints it dark, per the Standings reference).

   Custom property hooks (override in theme.css):
     --cg-switcher-select-content-pt      space above the pane content
     --cg-switcher-select-min-width       min width of the <select>

   The heading band carries the shared .cg-inc-header class, so its
   background / color / padding come entirely from the theme's .cg-inc-header
   styling (theme.css). There is intentionally NO switcher-select-specific
   header paint rule here, so it can't out-specify the theme override.
   ============================================================ */
.component-switcher-select {
    --cg-switcher-select-content-pt:     1rem;
    --cg-switcher-select-min-width:      12rem;
}

/* Dropdown — sized to its content with a floor. */
.component-switcher-select .cg-switcher-select {
    width:     100%;
    margin:    0;
    min-width: var(--cg-switcher-select-min-width);
}

/* Titleless header — the select slot grows to fill the row and the dropdown
   spans it, so there's no dead space to the left of the control. */
.component-switcher-select .cg-switcher-select-header--full {
    flex: 1 1 auto;
}
.component-switcher-select .cg-switcher-select-header--full .cg-switcher-select {
    width:      100%;
    margin:     0;
}

.component-switcher-select .cg-switcher-select-bodytop {
    margin-top: var(--cg-switcher-select-content-pt);
}

.component-switcher-select .cg-switcher-select-content {
    padding-top: var(--cg-switcher-select-content-pt);
}

/* Footer area — wraps the archive button (and the select when
   selectPlacement=footer). Owns the top spacing so the inner
   .cg-inc-footer doesn't double it. */
.component-switcher-select .cg-switcher-select-footer {
    display:        flex;
    flex-direction: column;
    gap:            0.75rem;
    margin-top:     1rem;
}

.component-switcher-select .cg-switcher-select-footer .cg-inc-footer {
    margin-top: 0;
}


/* ============================================================
   COMPONENT: cg-component-standings
   Pure-BS5 standings table (narrowed "brief" view) rendered by
   component-standings-template. Built on Bootstrap's .table /
   .table-hover; the team column takes the row's slack while the stat
   columns hug their content. cg-specific child classes (.cg-standings-*)
   keep it isolated from the legacy standings.css. Branded treatment
   (dark header band, emphasized column, etc.) is left to theme.css via
   the hooks below + the per-cell data-field / data-group attributes.

   Custom property hooks (override in theme.css):
     --cg-standings-logo-size      team logo box
     --cg-standings-cell-py        vertical cell padding
     --cg-standings-head-color     header text color
     --cg-standings-head-transform header text-transform (default capitalize;
                                   applied to ALL header cells so the team
                                   column matches the stat columns)
     --cg-standings-emphasis-bg  background for an emphasized column —
                                 apply per field, e.g.
                                 .component-standings .cg-standings-col[data-field="record"]
                                 { background: var(--cg-standings-emphasis-bg); }
     --cg-standings-division-gap space above each division table after
                                 the first (divisions mode)
   ============================================================ */
.component-standings {
    --cg-standings-logo-size:      1.5rem;
    --cg-standings-cell-py:        0.75rem;
    --cg-standings-head-color:     var(--bs-emphasis-color, #212529);
    --cg-standings-head-transform: capitalize;
    --cg-standings-emphasis-bg:    var(--bs-tertiary-bg, rgba(0, 0, 0, 0.04));
    --cg-standings-division-gap:   1.5rem;
}

.component-standings .cg-standings-table {
    --bs-table-bg: transparent;
    margin-bottom: 0;
}

/* Divisions mode renders one table per division, each in its own
   .table-responsive wrapper. Space every table after the first so the
   divisions read as separate groups; the first keeps the component flush. */
.component-standings .table-responsive + .table-responsive {
    margin-top: var(--cg-standings-division-gap);
}

/* Header cells — same treatment for the team column and the stat columns,
   so the first heading (team) matches the others' casing regardless of
   the raw teamHeader / label values. */
.component-standings .cg-standings-table thead th {
    color:               var(--cg-standings-head-color);
    font-weight:         700;
    vertical-align:      middle;
    text-transform:      var(--cg-standings-head-transform);
    border-bottom-width: var(--bs-border-width, 1px);
}

.component-standings .cg-standings-table th,
.component-standings .cg-standings-table td {
    padding-block: var(--cg-standings-cell-py);
}

/* Natural, content-based column widths. Neither column is forced: the team
   column no longer grabs the whole row (was width:100%, which collapsed the
   rest) and the stat columns are no longer pinned to their minimum (was
   width:1%). With the table at width:100% and the default `table-layout:auto`,
   the browser distributes the row's width proportionally to each column's
   content — the team column still reads widest, while the stat columns grow to
   suit their values instead of all hugging their minimum. nowrap keeps stat
   values on one line so a column never wraps below its content. */
.component-standings .cg-standings-team-col {
    text-align: start;
}

.component-standings .cg-standings-col {
    white-space: nowrap;
}

/* Focus column — the template auto-shades the primary ranking column (points
   if the sport tracks it, else conference record) with the emphasis token so it
   reads as the sorted-by column. Applies to both its header and body cells. */
.component-standings .cg-standings-col--focus {
    background-color: var(--cg-standings-emphasis-bg);
}

.component-standings .cg-standings-team {
    min-width: 0;
}

.component-standings .cg-standings-logo {
    flex:       0 0 auto;
    width:      var(--cg-standings-logo-size);
    height:     var(--cg-standings-logo-size);
    object-fit: contain;
}

.component-standings .cg-standings-team-name {
    font-weight: 600;
}

.component-standings .cg-standings-team-link {
    color: inherit;
}

/* Narrow spacer column between groups (colGroups mode). */
.component-standings .cg-standings-sep {
    width:   0.5rem;
    padding: 0;
    border:  0;
}

.component-standings .cg-standings-group-head {
    font-weight: 700;
}

.component-standings .cg-standings-empty {
    margin: 0;
}


/* ============================================================
   COMPONENT: cg-component-parallax (headlines + navbar variants)
   Full-bleed parallax banner rendered by cg-component-parallax-
   headlines-template and component-parallax-navbar-template. The
   background image(s) are fixed and parallax on scroll; multiple images
   cross-fade on a timer (parallax.js, which also pauses on hover). A
   dark-scrim overlay carries the page thumbnail / title / leadin (or,
   in the clickable navbar variant, per-slide label links).

   Consolidated copy of generic/assets/css/parallax.css so the cg- themes
   serve every component's CSS from this one sheet. Same class contract
   (.component-parallax / .parallax / .parallax-bg / .overlay,
   .has-multiple-images, layout-standard|hero|clickable) the parallax.js
   module expects. The legacy parallax.css stays for the legacy template.
   ============================================================ */
.component-parallax .layout-wrapper {
    position:   relative;
    min-height: 50vh;
}

.component-parallax .parallax {
    position:        absolute !important;
    background:      none !important;
    left:   0;
    right:  0;
    top:    0;
    bottom: 0;
    clip:            rect(0, auto, auto, 0);
    display:         flex;
    align-items:     center;
    justify-content: center;
}

.component-parallax .parallax-bg {
    content:             "";
    position:            fixed;
    top:    0;
    bottom: 0;
    left:   0;
    right:  0;
    background-repeat:   no-repeat;
    background-position: 50% 50%;
    background-size:     cover;
    /* Grayscale the background image by default (override --cg-parallax-bg-
       filter to `none` or another filter). Safe on this fixed element —
       filter only breaks fixed positioning for an element's DESCENDANTS. */
    filter: var(--cg-parallax-bg-filter, grayscale(100%));
}

/* Disable the fixed-background (parallax-on-scroll) effect on touch
   devices — scoped to the component (the legacy sheet left this global). */
@media (pointer: coarse) {
    .component-parallax .parallax-bg {
        position: absolute !important;
    }
}

.component-parallax .overlay {
    max-width:       100%;
    padding:         2rem 0;
    z-index:         2;
    display:         flex;
    flex-direction:  column;
    justify-content: center;
    align-items:     center;
    color:           #fff;
    text-shadow:     0 0 1px rgba(0, 0, 0, 0.2);
}

.component-parallax .overlay::before {
    content:    "";
    position:   absolute;
    left:   0;
    right:  0;
    top:    0;
    bottom: 0;
    background: var(--cg-parallax-overlay-bg, rgba(0, 0, 0, 0.5));
}

.component-parallax.layout-standard .overlay {
    text-align: center;
}

/* Multi-image cross-fade — backgrounds are fixed (locked while scrolling)
   and fade between one another. */
.component-parallax.has-multiple-images .parallax-bg-carousel {
    position: fixed;
    top:    0;
    bottom: 0;
    left:   0;
    right:  0;
    z-index: 0;
    clip:    rect(0, auto, auto, 0);
}

.component-parallax.has-multiple-images .parallax-bg {
    position:            fixed;
    top:    0;
    bottom: 0;
    left:   0;
    right:  0;
    background-size:     cover;
    background-position: center;
    background-repeat:   no-repeat;
    opacity:             0;
    transition:          opacity 1s ease-in-out;
    pointer-events:      none;
}

.component-parallax.has-multiple-images .parallax-bg.active,
.component-parallax.has-multiple-images .parallax.active .parallax-bg {
    opacity: 1;
    z-index: 1;
}

.component-parallax.has-multiple-images .fade {
    opacity:    0;
    transition: opacity 1s ease-in-out;
    position:   absolute;
    top:    0;
    left:   0;
    right:  0;
    bottom: 0;
    z-index: 0;
}

.component-parallax.has-multiple-images .fade.active {
    opacity: 1;
    z-index: 1;
}

/* Hero layout — full-viewport height. */
.component-parallax.layout-hero .layout-wrapper {
    min-height: 100vh;
}

.component-parallax.layout-hero .overlay {
    text-align: center;
}

@media only screen and (min-width: 992px) {
    .component-parallax .overlay { max-width: 90%; }
}

@media only screen and (min-width: 1200px) {
    .component-parallax .overlay { max-width: 80%; }
}


/* ============================================================
   COMPONENTS: cg-component-gallery / cg-component-videos
   Media-tile grid (photos and videos). Both render .cg-media-* cards:
   an image / video thumbnail in a fixed-ratio frame with an optional
   caption that overlays the image (default) or sits below it. The
   videos variant adds a centered .cg-media-card-play badge to denote a
   video. Carousel + footer/header controls reuse the shared cg- chrome
   (.cg-inc-footer / .cg-inc-header), same as cg-component-headlines-card.

   Custom property hooks (override in theme.css):
     --cg-media-grid-min       min grid track width (forwarded inline from
                               the mediaMinWidth param; drives columns)
     --cg-media-grid-gap       gap between tiles
     --cg-media-radius         tile corner radius
     --cg-media-overlay-bg     overlay caption scrim/background
     --cg-media-overlay-color  overlay caption text color
     --cg-media-play-size      play badge diameter
     --cg-media-play-bg        play badge background (resting)
     --cg-media-play-bg-hover  play badge background (card hover)
     --cg-media-play-color     play glyph color
   ============================================================ */
.component-gallery,
.component-videos {
    --cg-media-grid-min:      20rem;
    --cg-media-grid-gap:      1rem;
    --cg-media-radius:        var(--bs-border-radius, 0.375rem);
    --cg-media-overlay-bg:    linear-gradient(to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.35) 45%, rgba(0, 0, 0, 0) 100%);
    --cg-media-overlay-color: #fff;
    --cg-media-play-size:     3.5rem;
    --cg-media-play-bg:       rgba(0, 0, 0, 0.55);
    --cg-media-play-bg-hover: var(--bs-primary, #0d6efd);
    --cg-media-play-color:    #fff;
}

/* auto-fit + minmax: tiles reflow from many → one column as the column
   (or viewport) narrows; showSize chunks the carousel pages above this. */
.component-gallery .cg-media-grid,
.component-videos .cg-media-grid {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-media-grid-min), 1fr));
    gap:                   var(--cg-media-grid-gap);
}

.component-gallery .cg-media-card,
.component-videos .cg-media-card {
    position:      relative;
    overflow:      hidden;
    border-radius: var(--cg-media-radius);
    background:    transparent;
}

.component-gallery .cg-media-card-link,
.component-videos .cg-media-card-link {
    display: block;
    color:   inherit;
}

.component-gallery .cg-media-card-image-wrap,
.component-videos .cg-media-card-image-wrap {
    overflow:      hidden;
    border-radius: var(--cg-media-radius);
    background:    var(--bs-gray-200, #e9ecef);
}

.component-gallery .cg-media-card-image,
.component-videos .cg-media-card-image {
    width:      100%;
    height:     100%;
    object-fit: cover;
    transition: transform 0.4s ease;
}

/* Hover zoom — the sharp tile scales slightly inside its overflow:hidden wrap
   (signals the whole card is clickable; matches .cg-card-image). The image
   only — never the blurred -bg backdrop or the play badge. */
.component-gallery .cg-media-card:hover .cg-media-card-image,
.component-gallery .cg-media-card:focus-within .cg-media-card-image,
.component-videos .cg-media-card:hover .cg-media-card-image,
.component-videos .cg-media-card:focus-within .cg-media-card-image {
    transform: scale(1.05);
}

/* Blurred backdrop — same image as a cover layer behind the CONTAINED tile
   (object-fit-contain in markup) so off-ratio media fills the frame. First
   child of the .ratio box (Bootstrap absolute-fills it), behind the image and
   the play badge / caption overlay. scale() hides the blur-faded edge. */
.component-gallery .cg-media-card-image-bg,
.component-videos .cg-media-card-image-bg {
    background-image:    var(--cg-media-img);
    background-position: center;
    background-size:     cover;
    background-repeat:   no-repeat;
    filter:              blur(20px);
    transform:           scale(1.1);
}

/* Play badge — centered over the thumbnail. Overrides Bootstrap's
   `.ratio > *` (top/left:0; width/height:100%) since it lives inside the
   .ratio frame. */
.component-gallery .cg-media-card-play,
.component-videos .cg-media-card-play {
    position:        absolute;
    top:             50%;
    left:            50%;
    width:           var(--cg-media-play-size);
    height:          var(--cg-media-play-size);
    transform:       translate(-50%, -50%);
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    border-radius:   50%;
    background:      var(--cg-media-play-bg);
    color:           var(--cg-media-play-color);
    font-size:       calc(var(--cg-media-play-size) * 0.38);
    pointer-events:  none;
    transition:      background-color .15s ease;
}

.component-videos .cg-media-card:hover .cg-media-card-play,
.component-videos .cg-media-card:focus-within .cg-media-card-play {
    background: var(--cg-media-play-bg-hover);
}

/* Caption stack — shared by overlay and below-image modes. */
.component-gallery .cg-media-card-caption,
.component-videos .cg-media-card-caption {
    display:        flex;
    flex-direction: column;
    gap:            0.15rem;
}

/* Overlay mode — pinned to the foot of the thumbnail over a scrim.
   Overrides Bootstrap's `.ratio > *` full-bleed positioning. */
.component-gallery .cg-media-card-caption--overlay,
.component-videos .cg-media-card-caption--overlay {
    position:     absolute;
    top:          auto;
    bottom:       0;
    height:       auto;
    inset-inline: 0;
    padding:      1.75rem 0.85rem 0.7rem;
    background:   var(--cg-media-overlay-bg);
    color:        var(--cg-media-overlay-color);
}

.component-gallery .cg-media-card-caption--overlay .cg-media-card-title,
.component-videos .cg-media-card-caption--overlay .cg-media-card-title {
    color: var(--cg-media-overlay-color);
}

.component-gallery .cg-media-card-date,
.component-videos .cg-media-card-date {
    font-size: 0.8125rem;
    opacity:   0.85;
}

.component-gallery .cg-media-card-title,
.component-videos .cg-media-card-title {
    font-weight: 600;
    line-height: 1.25;
}

.component-gallery .cg-media-card-text,
.component-videos .cg-media-card-text {
    font-size: 0.875rem;
    opacity:   0.9;
}

/* showCategory -- the date and the sport / category share one meta line;
   the category is the same size as the date, set apart by weight. */
.component-gallery .cg-media-card-meta,
.component-videos .cg-media-card-meta {
    display:     flex;
    flex-wrap:   wrap;
    align-items: baseline;
    column-gap:  0.5rem;
}
.component-gallery .cg-media-card-category,
.component-videos .cg-media-card-category {
    font-size:   0.8125rem;
    font-weight: 600;
}

/* mobileScroll -- below lg a single-page grid becomes a swipeable row:
   one card per view (two at md), snapping card by card, scrollbar hidden
   (the footer's prev / next are the visible affordance). At lg+ the grid
   above applies unchanged. The headlines card row's mechanics. */
@media (max-width: 991.98px) {
    .component-gallery .cg-media-grid--scroll,
    .component-videos .cg-media-grid--scroll {
        display:               flex;
        overflow-x:            auto;
        overscroll-behavior-x: contain;
        scroll-snap-type:      x mandatory;
        scrollbar-width:       none;
    }
    .component-gallery .cg-media-grid--scroll::-webkit-scrollbar,
    .component-videos .cg-media-grid--scroll::-webkit-scrollbar {
        display: none;
    }
    .component-gallery .cg-media-grid--scroll > .cg-media-card,
    .component-videos .cg-media-grid--scroll > .cg-media-card {
        flex:              0 0 100%;
        min-width:         0;
        scroll-snap-align: start;
    }
}
@media (min-width: 768px) and (max-width: 991.98px) {
    .component-gallery .cg-media-grid--scroll > .cg-media-card,
    .component-videos .cg-media-grid--scroll > .cg-media-card {
        flex-basis: calc((100% - var(--cg-media-grid-gap, 1rem)) / 2);
    }
}
/* The scroll row's arrows are disabled at either end of the row (only
   they carry data-cg-scroll-step; carousel arrows are untouched). */
.component-gallery .cg-inc-footer .cg-carousel-control[data-cg-scroll-step]:disabled,
.component-videos .cg-inc-footer .cg-carousel-control[data-cg-scroll-step]:disabled {
    opacity: 0.4;
    cursor:  default;
}


/* ============================================================
   COMPONENT: cg-component-headlines / layout-featured
   "Hero + grid" presentation rendered by cg-component-headlines-
   featured-template. First entry is a large hero card; remaining
   entries flow into a responsive auto-fit grid below.

   Custom property hooks (override in theme.css OR via inline style
   on the component wrapper — the template forwards the thumbMinWidth
   URL param into --cg-headlines-grid-min via a style="" attribute):
     --cg-headlines-hero-mb    gap between hero and grid
     --cg-headlines-grid-min   minimum track width in the grid (drives
                               column count via auto-fit)
     --cg-headlines-grid-gap   gap between thumbnail cards
   ============================================================ */
.component-headlines.layout-featured {
    --cg-headlines-hero-mb:   1.5rem;
    --cg-headlines-grid-min:  15rem;
    --cg-headlines-grid-gap:  1rem;
}

.component-headlines.layout-featured .cg-headlines-featured-stack {
    display:        flex;
    flex-direction: column;
}

.component-headlines.layout-featured .cg-headlines-hero {
    margin-bottom: var(--cg-headlines-hero-mb);
}

/* auto-fit + minmax means the row collapses tracks once they'd drop
   below --cg-headlines-grid-min, so columns shrink from many → 1 as
   the viewport (or parent column, e.g. .sec-split-main) narrows. */
.component-headlines.layout-featured .cg-headlines-grid {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-headlines-grid-min), 1fr));
    gap:                   var(--cg-headlines-grid-gap);
}


/* ============================================================
   COMPONENT: cg-component-headlines / layout-jumbotron
   Replica of the Bootstrap 5 "Jumbotron" example, rendered by
   component-headlines-jumbotron-template. First entry is the
   full-width jumbotron; the next two are equal-height panels beside
   each other below it.

   The template keeps the reference example's STRUCTURAL utilities in
   the markup (p-5, container-fluid py-5, row align-items-md-stretch,
   col-md-6, h-100, display-5, fs-4, btn-*). Only the panel SURFACE is
   tokenized here, standing in for the example's bg-light + border +
   rounded-3, so a theme can restyle without overriding the template.
   Defaults resolve to Bootstrap's own tertiary surface, which also
   means the panels follow a [data-bs-theme] color mode.

   The jumbotron and the two panels below it are tokenized SEPARATELY, and
   the panels alternate between two backgrounds, so a theme can make the
   second and third card read differently (Catalog paints them with the
   primary and secondary brand colors). Defaults keep all three on the same
   neutral surface, so the component ships as a faithful replica of the
   reference example until a theme says otherwise.

   Buttons carry no btn-* variant class — the template emits plain .btn and
   the tokens below are mapped onto Bootstrap's own --bs-btn-* vars. That is
   what lets a theme recolor them with no template override, and it is why
   Bootstrap's .btn-secondary is not used for the hero: its compiled rule
   carries a literal grey from Sass, not the site's brand token.

   Custom property hooks (override in theme.css — declare them on the
   component's own element, .component-headlines.layout-jumbotron, since a
   self-declared --cg- token shadows any ancestor value):
     --cg-jumbotron-lead-bg           jumbotron background
     --cg-jumbotron-lead-ink          jumbotron heading/body ink
     --cg-jumbotron-lead-ink-muted    jumbotron lead-paragraph ink
     --cg-jumbotron-panel-bg          background of the 1st, 3rd, ... panel
     --cg-jumbotron-panel-alt-bg      background of the 2nd, 4th, ... panel
     --cg-jumbotron-panel-ink         panel heading ink
     --cg-jumbotron-panel-ink-muted   panel paragraph ink
     --cg-jumbotron-panel-border      panel border (shorthand value)
     --cg-jumbotron-radius            corner radius, all surfaces
     --cg-jumbotron-row-gutter        gutter between the lower panels
     --cg-jumbotron-lead-btn-*        hero button colors (see block below)
     --cg-jumbotron-panel-btn-*       panel button colors
   ============================================================ */
.component-headlines.layout-jumbotron {
    /* Surfaces */
    --cg-jumbotron-lead-bg:      var(--bs-tertiary-bg, #f8f9fa);
    --cg-jumbotron-panel-bg:     var(--bs-tertiary-bg, #f8f9fa);
    --cg-jumbotron-panel-alt-bg: var(--cg-jumbotron-panel-bg);
    --cg-jumbotron-panel-border: var(--bs-border-width) solid var(--bs-border-color);
    --cg-jumbotron-radius:       var(--bs-border-radius-xl, 1rem);
    --cg-jumbotron-row-gutter:   1.5rem;

    /* Ink — inherits the page by default. A theme that paints a panel with a
       brand color sets these to a light ink to sit on it. */
    --cg-jumbotron-lead-ink:        inherit;
    --cg-jumbotron-lead-ink-muted:  inherit;
    --cg-jumbotron-panel-ink:       inherit;
    --cg-jumbotron-panel-ink-muted: inherit;

    /* Hero button — the theme's SECONDARY brand color. --bs-secondary is
       injected per site by generic/inc-vars, so this tracks the configured
       palette rather than a baked-in value. */
    --cg-jumbotron-lead-btn-color:        #fff;
    --cg-jumbotron-lead-btn-bg:           var(--bs-secondary, #2f6d83);
    --cg-jumbotron-lead-btn-border:       var(--cg-jumbotron-lead-btn-bg);
    --cg-jumbotron-lead-btn-color-hover:  #fff;
    --cg-jumbotron-lead-btn-bg-hover:     color-mix(in srgb, var(--cg-jumbotron-lead-btn-bg) 85%, #000);
    --cg-jumbotron-lead-btn-border-hover: var(--cg-jumbotron-lead-btn-bg-hover);
    --cg-jumbotron-lead-btn-focus-rgb:   var(--bs-secondary-rgb, 47, 109, 131);

    /* Panel buttons — bordered and transparent, so they read against
       whatever the panel background is. */
    --cg-jumbotron-panel-btn-color:        var(--bs-body-color);
    --cg-jumbotron-panel-btn-bg:           transparent;
    --cg-jumbotron-panel-btn-border:       var(--bs-border-color);
    --cg-jumbotron-panel-btn-color-hover:  var(--bs-body-color);
    --cg-jumbotron-panel-btn-bg-hover:     rgba(0, 0, 0, 0.05);
    --cg-jumbotron-panel-btn-border-hover: var(--bs-border-color);
    --cg-jumbotron-panel-btn-focus-rgb:    var(--bs-body-color-rgb, 33, 37, 41);
}

/* The jumbotron itself. Borderless in the reference example — only the
   two panels below carry a border — so the border token is deliberately
   not applied here. */
.component-headlines.layout-jumbotron .cg-jumbotron-lead {
    background:    var(--cg-jumbotron-lead-bg);
    border-radius: var(--cg-jumbotron-radius);
    color:         var(--cg-jumbotron-lead-ink);
}

.component-headlines.layout-jumbotron .cg-jumbotron-lead-text {
    color: var(--cg-jumbotron-lead-ink-muted);
}

.component-headlines.layout-jumbotron .cg-jumbotron-panel {
    background:    var(--cg-jumbotron-panel-bg);
    border:        var(--cg-jumbotron-panel-border);
    border-radius: var(--cg-jumbotron-radius);
    color:         var(--cg-jumbotron-panel-ink);
}

/* Every SECOND panel column takes the alternate background. The hero sits
   OUTSIDE this row, so column 1 is the component's second card and column 2
   its third — i.e. at the default pageSize of 3, card 2 gets -panel-bg and
   card 3 gets -panel-alt-bg. Raising pageSize keeps alternating. */
.component-headlines.layout-jumbotron .cg-jumbotron-row > *:nth-child(even) .cg-jumbotron-panel {
    background: var(--cg-jumbotron-panel-alt-bg);
}

.component-headlines.layout-jumbotron .cg-jumbotron-panel-text {
    color: var(--cg-jumbotron-panel-ink-muted);
}

.component-headlines.layout-jumbotron .cg-jumbotron-row {
    --bs-gutter-x: var(--cg-jumbotron-row-gutter);
    --bs-gutter-y: var(--cg-jumbotron-row-gutter);
}

/* Button token mapping. Bootstrap 5.3 buttons resolve every color from
   --bs-btn-* custom properties on the element, so re-pointing those at the
   component's tokens restyles the button with no override of Bootstrap's own
   rules and no !important. */
.component-headlines.layout-jumbotron .cg-jumbotron-lead-btn {
    --bs-btn-color:               var(--cg-jumbotron-lead-btn-color);
    --bs-btn-bg:                  var(--cg-jumbotron-lead-btn-bg);
    --bs-btn-border-color:        var(--cg-jumbotron-lead-btn-border);
    --bs-btn-hover-color:         var(--cg-jumbotron-lead-btn-color-hover);
    --bs-btn-hover-bg:            var(--cg-jumbotron-lead-btn-bg-hover);
    --bs-btn-hover-border-color:  var(--cg-jumbotron-lead-btn-border-hover);
    --bs-btn-active-color:        var(--cg-jumbotron-lead-btn-color-hover);
    --bs-btn-active-bg:           var(--cg-jumbotron-lead-btn-bg-hover);
    --bs-btn-active-border-color: var(--cg-jumbotron-lead-btn-border-hover);
    --bs-btn-focus-shadow-rgb:    var(--cg-jumbotron-lead-btn-focus-rgb);
}

.component-headlines.layout-jumbotron .cg-jumbotron-panel-btn {
    --bs-btn-color:               var(--cg-jumbotron-panel-btn-color);
    --bs-btn-bg:                  var(--cg-jumbotron-panel-btn-bg);
    --bs-btn-border-color:        var(--cg-jumbotron-panel-btn-border);
    --bs-btn-hover-color:         var(--cg-jumbotron-panel-btn-color-hover);
    --bs-btn-hover-bg:            var(--cg-jumbotron-panel-btn-bg-hover);
    --bs-btn-hover-border-color:  var(--cg-jumbotron-panel-btn-border-hover);
    --bs-btn-active-color:        var(--cg-jumbotron-panel-btn-color-hover);
    --bs-btn-active-bg:           var(--cg-jumbotron-panel-btn-bg-hover);
    --bs-btn-active-border-color: var(--cg-jumbotron-panel-btn-border-hover);
    --bs-btn-focus-shadow-rgb:    var(--cg-jumbotron-panel-btn-focus-rgb);
}



/* ============================================================
   COMPONENT: cg-component-headlines / layout-hero
   ============================================================ */
.component-headlines.layout-hero {
    --cg-headlines-hero-ratio:    16 / 9;
    --cg-headlines-hero-min-h:    20rem;
    --cg-headlines-hero-max-h:    38rem;
    --cg-headlines-hero-interval: 7000ms;

    --cg-headlines-hero-surface:  #000;
    --cg-headlines-hero-empty-bg: var(--bs-secondary-bg, #e9ecef);

    --cg-headlines-hero-scrim-solid: 12rem;
    --cg-headlines-hero-scrim-hold:  0.72;
    --cg-headlines-hero-scrim:
        linear-gradient(to top,
                        rgb(0, 0, 0)        0,
                        rgba(0, 0, 0, 0.92) 8%,
                        rgba(0, 0, 0, 0.72) 16%,
                        rgba(0, 0, 0, 0.55) 21%,
                        rgba(0, 0, 0, 0.22) 27%,
                        rgba(0, 0, 0, 0)    34%);

    --cg-headlines-hero-color: var(--bs-white, #fff);
    --cg-headlines-hero-muted: rgba(255, 255, 255, 0.78);

    --cg-headlines-hero-title-size:      clamp(1.25rem, 0.85rem + 1.6vw, 2rem);
    --cg-headlines-hero-title-weight:    700;
    --cg-headlines-hero-title-tracking:  0.01em;
    --cg-headlines-hero-title-leading:   1.05;

    --cg-headlines-hero-leadin-size: 1rem;

    --cg-headlines-hero-copy-max:    40rem;
    --cg-headlines-hero-copy-gap:    1rem;
    --cg-headlines-hero-pad-top:     2rem;
    --cg-headlines-hero-pad-bottom:  2rem;

    --cg-headlines-hero-btn-color:        var(--bs-white, #fff);
    --cg-headlines-hero-btn-bg:           transparent;
    --cg-headlines-hero-btn-border:       rgba(255, 255, 255, 0.65);
    --cg-headlines-hero-btn-color-hover:  #000;
    --cg-headlines-hero-btn-bg-hover:     var(--bs-white, #fff);
    --cg-headlines-hero-btn-border-hover: var(--bs-white, #fff);
    --cg-headlines-hero-btn-focus-rgb:    var(--bs-white-rgb, 255, 255, 255);
    --cg-headlines-hero-btn-radius:       10rem;
    --cg-headlines-hero-btn-padding:      0.625rem 1.75rem;

    --cg-headlines-hero-meta-size: 0.8125rem;

    --cg-headlines-hero-related-link:       var(--bs-white, #fff);
    --cg-headlines-hero-related-link-size:  0.9375rem;
    --cg-headlines-hero-related-underline-size:  1px;
    --cg-headlines-hero-related-underline-speed: 240ms;

    --cg-headlines-hero-linescore-bg:     rgba(0, 0, 0, 0.72);
    --cg-headlines-hero-linescore-border: rgba(255, 255, 255, 0.2);
    --cg-headlines-hero-linescore-total:  rgba(255, 255, 255, 0.12);
    --cg-headlines-hero-linescore-radius: 0.5rem;
    --cg-headlines-hero-linescore-max:    26rem;
    --cg-headlines-hero-linescore-size:   0.8125rem;
    --cg-headlines-hero-linescore-gap:    1.5rem;
    --cg-headlines-hero-linescore-head:   rgba(255, 255, 255, 0.72);

    --cg-headlines-hero-tab-gap:            1.5rem;
    --cg-headlines-hero-tab-bar-h:          0.25rem;
    --cg-headlines-hero-tab-track:          rgba(255, 255, 255, 0.28);
    --cg-headlines-hero-mark-track:         rgba(255, 255, 255, 0.42);
    --cg-headlines-hero-tab-fill:           var(--bs-white, #fff);
    --cg-headlines-hero-tab-eyebrow-color:  rgba(255, 255, 255, 0.6);
    --cg-headlines-hero-tab-eyebrow-size:   0.6875rem;
    --cg-headlines-hero-tab-title-color:    rgba(255, 255, 255, 0.6);
    --cg-headlines-hero-tab-title-active:   var(--bs-white, #fff);
    --cg-headlines-hero-tab-title-size:     0.8125rem;
    --cg-headlines-hero-tab-pad:            0.75rem 0 1.25rem;
    --cg-headlines-hero-tab-w:              11rem;
    --cg-headlines-hero-control-diameter:   2rem;
    --cg-headlines-hero-tab-fade:           3rem;

    /* indicators=thumbs — the thumbnail strip. */
    --cg-headlines-hero-thumb-aspect:       16 / 9;
    --cg-headlines-hero-thumb-dim:          0.45;
    --cg-headlines-hero-thumb-border:       2px;
    --cg-headlines-hero-thumb-active:       var(--cg-headlines-hero-tab-fill);

    --cg-headlines-hero-dot-size:   0.625rem;
    --cg-headlines-hero-dot-target: 0.4375rem;
    --cg-headlines-hero-pill-pad:   0.5rem 0.875rem;
    --cg-headlines-hero-next-gap:   0.75rem;
    --cg-headlines-hero-ring-size:  1.25rem;
    --cg-headlines-hero-ring-w:     0.125rem;
    --cg-headlines-hero-ring-track: rgba(255, 255, 255, 0.3);
    --cg-headlines-hero-copy-start: 0.8;
    --cg-headlines-hero-gutter:     0.75rem;
    --cg-headlines-hero-narrow-pad: clamp(1.5rem, 4cqi, 3rem);
    --cg-headlines-hero-next-max:   min(calc(100vw - 2 * var(--cg-headlines-hero-gutter)), 30rem);

    --cg-headlines-hero-zoom-scale:    1.08;
    --cg-headlines-hero-zoom-duration: 9s;
    --cg-headlines-hero-zoom-still-scale:    1.04;
    --cg-headlines-hero-zoom-still-duration: 4.5s;

    --cg-headlines-hero-focus-blur:     14px;
    --cg-headlines-hero-focus-scale:    1.06;
    --cg-headlines-hero-focus-duration: 1.1s;

    --cg-headlines-hero-copy-duration: 0.5s;
    --cg-headlines-hero-copy-shift:    2.5rem;

    --cg-headlines-hero-letter-blur:     8px;
    --cg-headlines-hero-letter-shift:    0.625rem;
    --cg-headlines-hero-letter-duration: 0.2s;
    --cg-headlines-hero-letter-delay:    0.01s;
    --cg-headlines-hero-word-delay:      0.045s;
    --cg-headlines-hero-leadin-word-delay: 0.006s;
    --cg-headlines-hero-seq-step:          0.03s;
    --cg-headlines-hero-seq-budget:        1s;

    container-type: inline-size;
}

@supports (width: 1cqi) {
    .component-headlines.layout-hero {
        --cg-headlines-hero-next-max: min(calc(100cqi - 2 * var(--cg-headlines-hero-gutter)), 30rem);
    }
}

.component-headlines.layout-hero > .cg-inc-body {
    background-color: var(--cg-headlines-hero-surface);
}

.component-headlines.layout-hero > .cg-inc-header {
    box-sizing:     border-box;
    width:          100%;
    max-width:      var(--cg-hero-container-w, none);
    margin-inline:  auto;
    padding-inline: var(--cg-headlines-hero-gutter);
}

@media (min-width: 576px)  { .component-headlines.layout-hero { --cg-hero-container-w: 540px; } }
@media (min-width: 768px)  { .component-headlines.layout-hero { --cg-hero-container-w: 720px; } }
@media (min-width: 992px)  { .component-headlines.layout-hero { --cg-hero-container-w: 960px; } }
@media (min-width: 1200px) { .component-headlines.layout-hero { --cg-hero-container-w: 1140px; } }
@media (min-width: 1400px) { .component-headlines.layout-hero { --cg-hero-container-w: var(--ps-site-width, 1320px); } }

@media (min-width: 768px) {
    .component-headlines.layout-hero {
        --cg-headlines-hero-min-h:      24rem;
        --cg-headlines-hero-pad-top:    3.5rem;
        --cg-headlines-hero-pad-bottom: 3rem;
    }
}

@media (min-width: 992px) {
    .component-headlines.layout-hero {
        --cg-headlines-hero-scrim:
            linear-gradient(to right,
                            rgb(0, 0, 0) var(--cg-headlines-hero-scrim-solid),
                            rgba(0, 0, 0, var(--cg-headlines-hero-scrim-hold)) 100%),
            linear-gradient(to top,
                            rgb(0, 0, 0)        0,
                            rgba(0, 0, 0, 0.92) 8%,
                            rgba(0, 0, 0, 0.7)  22%,
                            rgba(0, 0, 0, 0)    52%);
    }

    @supports (width: 1cqi) {
        .component-headlines.layout-hero {
            --cg-hero-copy-edge: calc(max((100cqi - var(--cg-hero-container-w)) / 2,
                                          var(--cg-headlines-hero-narrow-pad))
                                      + var(--cg-headlines-hero-gutter)
                                      + var(--cg-headlines-hero-copy-max) + 2rem);
            --cg-headlines-hero-scrim:
                linear-gradient(to right,
                                rgb(0, 0, 0) var(--cg-headlines-hero-scrim-solid),
                                rgba(0, 0, 0, var(--cg-headlines-hero-scrim-hold)) var(--cg-hero-copy-edge),
                                rgba(0, 0, 0, 0) calc(var(--cg-hero-copy-edge) + 16rem)),
                linear-gradient(to top,
                                rgb(0, 0, 0)        0,
                                rgba(0, 0, 0, 0.92) 8%,
                                rgba(0, 0, 0, 0.7)  22%,
                                rgba(0, 0, 0, 0)    52%);
        }
    }
}

.component-headlines.layout-hero .carousel-item.active,
.component-headlines.layout-hero .carousel-item-next,
.component-headlines.layout-hero .carousel-item-prev {
    display:               grid;
    grid-template-columns: minmax(0, 1fr);
}

.component-headlines.layout-hero .cg-headlines-hero-media {
    grid-area: 1 / 1;
    position:  relative;
    width:     100%;
    overflow:  hidden;
}

.component-headlines.layout-hero .cg-headlines-hero-media::before {
    content:      "";
    display:      block;
    width:        100%;
    aspect-ratio: var(--cg-headlines-hero-ratio, 16 / 9);
    min-height:   var(--cg-headlines-hero-min-h);
    max-height:   var(--cg-headlines-hero-max-h);
}

.component-headlines.layout-hero .cg-headlines-hero-image {
    position:   absolute;
    inset:      0;
    display:    block;
    width:      100%;
    height:     100%;
    object-fit: cover;
}

.component-headlines.layout-hero .cg-headlines-hero-image--empty {
    background: var(--cg-headlines-hero-empty-bg);
}

.component-headlines.layout-hero .cg-headlines-hero-scrim {
    position:       absolute;
    inset:          0;
    background:     var(--cg-headlines-hero-scrim);
    pointer-events: none;
}

.component-headlines.layout-hero .cg-headlines-hero-copy-wrap {
    grid-area:      1 / 1;
    position:       relative;
    padding-top:    var(--cg-headlines-hero-pad-top);
    padding-bottom: var(--cg-headlines-hero-pad-bottom);
}

@media (max-width: 991.98px) {
    .component-headlines.layout-hero .cg-headlines-hero-media {
        align-self: start;
    }

    .component-headlines.layout-hero .cg-headlines-hero-copy-wrap {
        padding-top: 0;
    }

    .component-headlines.layout-hero .cg-headlines-hero-copy-wrap::before {
        content:      "";
        display:      block;
        width:        calc(var(--cg-headlines-hero-copy-start) * 100%);
        aspect-ratio: var(--cg-headlines-hero-ratio, 16 / 9);
        min-height:   calc(var(--cg-headlines-hero-min-h) * var(--cg-headlines-hero-copy-start));
        max-height:   calc(var(--cg-headlines-hero-max-h) * var(--cg-headlines-hero-copy-start));
    }
}

.component-headlines.layout-hero .cg-headlines-hero-copy {
    display:        flex;
    flex-direction: column;
    align-items:    flex-start;
    gap:            var(--cg-headlines-hero-copy-gap);
    max-width:      var(--cg-headlines-hero-copy-max);
    color:          var(--cg-headlines-hero-color);
}

@media (min-width: 992px) and (max-width: 1199.98px) {
    @container (max-width: 959.98px) {
        .component-headlines.layout-hero .cg-headlines-hero-copy-wrap,
        .component-headlines.layout-hero .cg-headlines-hero-nav {
            padding-inline: var(--cg-headlines-hero-narrow-pad);
        }

        .component-headlines.layout-hero > .cg-inc-header {
            padding-inline: calc(var(--cg-headlines-hero-narrow-pad) + var(--cg-headlines-hero-gutter));
        }
    }
}

@media (min-width: 1200px) and (max-width: 1399.98px) {
    @container (max-width: 1139.98px) {
        .component-headlines.layout-hero .cg-headlines-hero-copy-wrap,
        .component-headlines.layout-hero .cg-headlines-hero-nav {
            padding-inline: var(--cg-headlines-hero-narrow-pad);
        }

        .component-headlines.layout-hero > .cg-inc-header {
            padding-inline: calc(var(--cg-headlines-hero-narrow-pad) + var(--cg-headlines-hero-gutter));
        }
    }
}

@media (min-width: 1400px) {
    @container (max-width: 1319.98px) {
        .component-headlines.layout-hero .cg-headlines-hero-copy-wrap,
        .component-headlines.layout-hero .cg-headlines-hero-nav {
            padding-inline: var(--cg-headlines-hero-narrow-pad);
        }

        .component-headlines.layout-hero > .cg-inc-header {
            padding-inline: calc(var(--cg-headlines-hero-narrow-pad) + var(--cg-headlines-hero-gutter));
        }
    }
}

.component-headlines.layout-hero .cg-headlines-hero-copy > * {
    max-width:     100%;
    margin:        0;
    overflow-wrap: break-word;
}

.component-headlines.layout-hero .cg-headlines-hero-title {
    color:          inherit;
    font-size:      var(--cg-headlines-hero-title-size);
    font-weight:    var(--cg-headlines-hero-title-weight);
    line-height:    var(--cg-headlines-hero-title-leading);
    letter-spacing: var(--cg-headlines-hero-title-tracking);
    text-wrap:      balance;
}

.component-headlines.layout-hero .cg-headlines-hero-title-link {
    color:           inherit;
    text-decoration: none;
}

.component-headlines.layout-hero .cg-headlines-hero-title-link:hover,
.component-headlines.layout-hero .cg-headlines-hero-title-link:focus-visible {
    text-decoration: underline;
}

.component-headlines.layout-hero .cg-headlines-hero-leadin {
    --cg-headlines-hero-word-delay: var(--cg-headlines-hero-leadin-word-delay);
}

.component-headlines.layout-hero .cg-headlines-hero-leadin-word {
    display: inline-block;
}

.component-headlines.layout-hero .cg-headlines-hero-title-split {
    display: inline;
}

.component-headlines.layout-hero .cg-headlines-hero-word {
    display:   inline-block;
    max-width: 100%;
}

.component-headlines.layout-hero .cg-headlines-hero-letter {
    display: inline-block;
}

.component-headlines.layout-hero .cg-headlines-hero-leadin {
    font-size: var(--cg-headlines-hero-leadin-size);
    color:     var(--cg-headlines-hero-muted);
}

.component-headlines.layout-hero .cg-headlines-hero-meta {
    display:        flex;
    flex-wrap:      wrap;
    align-items:    center;
    gap:            0.25rem 0.75rem;
    color:          var(--cg-headlines-hero-muted);
    font-size:      var(--cg-headlines-hero-meta-size);
    font-weight:    700;
    letter-spacing: 0.08em;
}

.component-headlines.layout-hero .cg-headlines-hero-sport + .cg-headlines-hero-date {
    padding-left: 0.75rem;
    border-left:  1px solid currentColor;
}

.component-headlines.layout-hero .cg-headlines-hero-btn {
    --bs-btn-color:               var(--cg-headlines-hero-btn-color);
    --bs-btn-bg:                  var(--cg-headlines-hero-btn-bg);
    --bs-btn-border-color:        var(--cg-headlines-hero-btn-border);
    --bs-btn-hover-color:         var(--cg-headlines-hero-btn-color-hover);
    --bs-btn-hover-bg:            var(--cg-headlines-hero-btn-bg-hover);
    --bs-btn-hover-border-color:  var(--cg-headlines-hero-btn-border-hover);
    --bs-btn-active-color:        var(--cg-headlines-hero-btn-color-hover);
    --bs-btn-active-bg:           var(--cg-headlines-hero-btn-bg-hover);
    --bs-btn-active-border-color: var(--cg-headlines-hero-btn-border-hover);
    --bs-btn-focus-shadow-rgb:    var(--cg-headlines-hero-btn-focus-rgb);
    --bs-btn-border-radius:       var(--cg-headlines-hero-btn-radius);
    --bs-btn-padding-x:           2rem;
    padding:                      var(--cg-headlines-hero-btn-padding);
    font-weight:                  600;
    letter-spacing:               0.04em;
}

.component-headlines.layout-hero .cg-headlines-hero-related-list {
    display:        flex;
    flex-direction: row;
    flex-wrap:      wrap;
    gap:            0.25rem 1.25rem;
    margin:         0;
    padding:        0;
    list-style:     none;
}

.component-headlines.layout-hero .cg-headlines-hero-related-link {
    position:        relative;
    display:         inline-block;
    color:           var(--cg-headlines-hero-related-link);
    font-size:       var(--cg-headlines-hero-related-link-size);
    text-decoration: none;
}

.component-headlines.layout-hero .cg-headlines-hero-related-link::after {
    content:          "";
    position:         absolute;
    left:             0;
    right:            0;
    bottom:           -0.125rem;
    height:           var(--cg-headlines-hero-related-underline-size);
    background-color: currentColor;
    transform:        scaleX(0);
    transform-origin: left center;
    transition:       transform var(--cg-headlines-hero-related-underline-speed) ease-out;
}

.component-headlines.layout-hero .cg-headlines-hero-related-link:hover::after,
.component-headlines.layout-hero .cg-headlines-hero-related-link:focus-visible::after {
    transform: scaleX(1);
}

.component-headlines.layout-hero .cg-headlines-hero-linescore {
    box-sizing:       border-box;
    width:            min(100%, var(--cg-headlines-hero-linescore-max));
    margin-top:       var(--cg-headlines-hero-linescore-gap);
    padding:          0.625rem 0.875rem;
    color:            var(--cg-headlines-hero-color);
    font-size:        var(--cg-headlines-hero-linescore-size);
    background-color: var(--cg-headlines-hero-linescore-bg);
    border:           1px solid var(--cg-headlines-hero-linescore-border);
    border-radius:    var(--cg-headlines-hero-linescore-radius);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter:         blur(8px);
}

/* An include that renders no table (no stats document yet) leaves no box. */
.component-headlines.layout-hero .cg-headlines-hero-linescore:not(:has(table)) {
    display: none;
}

/* brief-stats-template spaces its own wrappers with mb-3 and friends, which
   are !important; the panel's padding is the spacing here. */
.component-headlines.layout-hero .cg-headlines-hero-linescore .brief-stats,
.component-headlines.layout-hero .cg-headlines-hero-linescore .linescore,
.component-headlines.layout-hero .cg-headlines-hero-linescore .linebox,
.component-headlines.layout-hero .cg-headlines-hero-linescore .table {
    margin: 0 !important;
}

/* The table tokens live on .table itself, so they are set here on .table and
   not on the panel, where .table's own declarations would win. Some sports'
   brief-stats tables also carry bg-white / text-dark utilities, which are
   !important themselves; only an !important scoped here can undo them. */
.component-headlines.layout-hero .cg-headlines-hero-linescore .table {
    --bs-table-bg:           transparent;
    --bs-table-color:        var(--cg-headlines-hero-color);
    --bs-table-border-color: var(--cg-headlines-hero-linescore-border);
    --bs-table-active-bg:    var(--cg-headlines-hero-linescore-total);
    --bs-table-active-color: var(--cg-headlines-hero-color);
    --bs-table-hover-bg:     transparent;
    --bs-table-hover-color:  var(--cg-headlines-hero-color);
    color:            var(--cg-headlines-hero-color) !important;
    background-color: transparent !important;
    font-size:        inherit;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore .table > :not(caption) > * > * {
    padding:          0.375rem 0.5rem;
    color:            var(--cg-headlines-hero-color);
    background-color: transparent;
    white-space:      nowrap;
}

/* Themes paint thead on the brand colour with their own !important; the
   panel keeps one ground throughout, so this one has to match it. The
   header reads as an eyebrow - the status ("Final", or the clock) and the
   period numbers - rather than a second bold row. */
.component-headlines.layout-hero .cg-headlines-hero-linescore .table > thead > tr > th {
    background-color: transparent !important;
    border-bottom:    1px solid var(--cg-headlines-hero-linescore-border);
    color:            var(--cg-headlines-hero-linescore-head);
    font-size:        0.6875rem;
    font-weight:      700;
    letter-spacing:   0.08em;
    text-transform:   uppercase;
}

/* The panel's own padding closes the box; the last row needs no rule. */
.component-headlines.layout-hero .cg-headlines-hero-linescore .table > tbody > tr:last-child > td {
    border-bottom: 0;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore .team-name {
    font-weight: 500;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore .score {
    min-width:            2rem;
    font-variant-numeric: tabular-nums;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore .score.total {
    font-weight: 700;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore .team-record,
.component-headlines.layout-hero .cg-headlines-hero-linescore .gamebox {
    display: none;
}

.component-headlines.layout-hero .cg-headlines-hero-linescore a {
    color: inherit;
}

/* Once the COMPONENT is xl-wide the copy column (40rem) and the panel (26rem)
   fit side by side, so the panel leaves the flow and is pinned to the
   picture's lower right. A container query, not a viewport one: a hero in a
   60% split column at 1680px is narrower than a full-width hero at 1200px,
   and there the panel stays under the copy. The copy column's .container
   is the positioning box - it is stretched to the row so bottom: 0 is the
   foot of the picture (less the wrap's padding), and its own gutter is the
   right edge the copy already aligns to. */
@container (min-width: 1140px) {
    .component-headlines.layout-hero .cg-headlines-hero-copy-wrap > .container {
        position: relative;
        height:   100%;
    }

    .component-headlines.layout-hero .cg-headlines-hero-linescore {
        position:   absolute;
        right:      var(--cg-headlines-hero-gutter);
        bottom:     0;
        width:      var(--cg-headlines-hero-linescore-max);
        margin-top: 0;
    }
}

.component-headlines.layout-hero .cg-headlines-hero-carousel {
    display:        flex;
    flex-direction: column;
}

.component-headlines.layout-hero .cg-headlines-hero-nav {
    order: 1;
}

.component-headlines.layout-hero .cg-headlines-hero-nav-inner {
    display:         flex;
    align-items:     flex-start;
    justify-content: space-between;
    gap:             1rem;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs {
    position:        static;
    display:         flex;
    align-items:     stretch;
    justify-content: flex-start;
    gap:             var(--cg-headlines-hero-tab-gap);
    flex:            1 1 auto;
    margin:          0;
    padding:         0;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab {
    display:        flex;
    flex-direction: column;
    gap:            0.625rem;
    flex:           1 1 0;
    box-sizing:     border-box;
    width:          auto;
    height:         auto;
    margin:         0;
    padding:        var(--cg-headlines-hero-tab-pad);
    border:         0;
    background:     none;
    text-indent:    0;
    text-align:     left;
    opacity:        1;
    cursor:         pointer;
}

.component-headlines.layout-hero .cg-headlines-hero-tab-bar {
    display:       block;
    width:         100%;
    height:        var(--cg-headlines-hero-tab-bar-h);
    background:    var(--cg-headlines-hero-tab-track);
    overflow:      hidden;
    border-radius: 10rem;
}

.component-headlines.layout-hero .cg-headlines-hero-tab-fill {
    display:          block;
    width:            100%;
    height:           100%;
    background:       var(--cg-headlines-hero-tab-fill);
    transform:        translateX(-100%);
    transform-origin: left center;
}

.component-headlines.layout-hero .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
    transform: translateX(0);
}

.component-headlines.layout-hero .cg-headlines-hero-tab-text {
    display:        flex;
    flex-direction: column;
    gap:            0.25rem;
}

.component-headlines.layout-hero .cg-headlines-hero-tab-eyebrow {
    display:        block;
    color:          var(--cg-headlines-hero-tab-eyebrow-color);
    font-size:      var(--cg-headlines-hero-tab-eyebrow-size);
    font-weight:    700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.component-headlines.layout-hero .cg-headlines-hero-tab-title {
    color:          var(--cg-headlines-hero-tab-title-color);
    font-size:      var(--cg-headlines-hero-tab-title-size);
    font-weight:    700;
    line-height:    1.25;
    letter-spacing: 0.02em;
    transition:     color .2s ease;
}

.component-headlines.layout-hero .cg-headlines-hero-tab.active .cg-headlines-hero-tab-title,
.component-headlines.layout-hero .cg-headlines-hero-tab:hover .cg-headlines-hero-tab-title,
.component-headlines.layout-hero .cg-headlines-hero-tab:focus-visible .cg-headlines-hero-tab-title {
    color: var(--cg-headlines-hero-tab-title-active);
}

@media (min-width: 992px) {
    .component-headlines.layout-hero .cg-headlines-hero-tabs {
        --cg-hero-clip-start:  #000;
        --cg-hero-clip-end:    #000;
        min-width:             0;
        margin:                -0.25rem;
        padding:               0.25rem;
        overflow-x:            auto;
        overflow-y:            hidden;
        overscroll-behavior-x: contain;
        scroll-padding-inline: var(--cg-headlines-hero-tab-fade);
        scrollbar-width:       none;
        -webkit-mask-image: linear-gradient(to right,
                                var(--cg-hero-clip-start) 0,
                                #000 var(--cg-headlines-hero-tab-fade),
                                #000 calc(100% - var(--cg-headlines-hero-tab-fade)),
                                var(--cg-hero-clip-end) 100%);
        mask-image:         linear-gradient(to right,
                                var(--cg-hero-clip-start) 0,
                                #000 var(--cg-headlines-hero-tab-fade),
                                #000 calc(100% - var(--cg-headlines-hero-tab-fade)),
                                var(--cg-hero-clip-end) 100%);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs::-webkit-scrollbar {
        display: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs.cg-headlines-hero-tabs--clip-start {
        --cg-hero-clip-start: transparent;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs.cg-headlines-hero-tabs--clip-end {
        --cg-hero-clip-end: transparent;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--tabs .cg-headlines-hero-tab {
        flex: 0 0 var(--cg-headlines-hero-tab-w);
    }
}

.component-headlines.layout-hero .cg-headlines-hero-tab-next {
    display: none;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--bars .cg-headlines-hero-tab-text,
.component-headlines.layout-hero .cg-headlines-hero-tabs--dots .cg-headlines-hero-tab-text {
    display: none;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--bars,
.component-headlines.layout-hero .cg-headlines-hero-tabs--dots {
    --cg-headlines-hero-tab-track: var(--cg-headlines-hero-mark-track);
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--bars .cg-headlines-hero-tab {
    flex: 0 0 2.75rem;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--dots .cg-headlines-hero-tab {
    flex: 0 0 auto;
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--dots .cg-headlines-hero-tab-bar {
    width:  var(--cg-headlines-hero-dot-size);
    height: var(--cg-headlines-hero-dot-size);
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--dots .cg-headlines-hero-tab-fill {
    transform: scale(0);
}

.component-headlines.layout-hero .cg-headlines-hero-tabs--dots .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
    transform: scale(1);
}

/* indicators=thumbs — a row of story thumbnails at lg+, sharing the row
   equally; the inactive ones dimmed, the active one at full strength in a
   frame, with the progress bar under it. Below lg the thumbnail is hidden
   and the strip is the same dots pill as every other variant. */
.component-headlines.layout-hero .cg-headlines-hero-tab-thumb {
    display: none;
}

@media (min-width: 992px) {
    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab {
        flex: 1 1 0;
        min-width: 0;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab-text {
        display: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab-thumb {
        display:      block;
        aspect-ratio: var(--cg-headlines-hero-thumb-aspect);
        overflow:     hidden;
        border:       var(--cg-headlines-hero-thumb-border) solid transparent;
        transition:   border-color .2s ease;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab-thumb img {
        display:    block;
        width:      100%;
        height:     100%;
        object-fit: cover;
        opacity:    var(--cg-headlines-hero-thumb-dim);
        transition: opacity .2s ease;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab:is(.active, :hover, :focus-visible) .cg-headlines-hero-tab-thumb img {
        opacity: 1;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-thumb {
        border-color: var(--cg-headlines-hero-thumb-active);
    }
}

@media (prefers-reduced-motion: reduce) {
    .component-headlines.layout-hero .cg-headlines-hero-tabs--thumbs :is(.cg-headlines-hero-tab-thumb, .cg-headlines-hero-tab-thumb img) {
        transition: none;
    }
}

.component-headlines.layout-hero .cg-headlines-hero-buttons {
    display:     flex;
    align-items: center;
    gap:         0.25rem;
    flex:        0 0 auto;
    padding:     var(--cg-headlines-hero-tab-pad);
}

@media (min-width: 992px) {
    .component-headlines.layout-hero .cg-headlines-hero-buttons {
        order: 1;
    }
}

.component-headlines.layout-hero .cg-headlines-hero-arrow {
    --cg-carousel-control-color:         var(--cg-headlines-hero-color);
    --cg-carousel-control-hover:         #000;
    --cg-carousel-control-bg:            transparent;
    --cg-carousel-control-bg-hover:      var(--bs-white, #fff);
    --cg-carousel-control-size:          0.875rem;
    --cg-carousel-control-padding:       0;
    --cg-carousel-control-border-width:  var(--bs-border-width, 1px);
    --cg-carousel-control-border-style:  solid;
    --cg-carousel-control-border-color:  rgba(255, 255, 255, 0.35);
    --cg-carousel-control-border-radius: 50%;
    flex:                                0 0 auto;
    width:                               var(--cg-headlines-hero-control-diameter);
    height:                              var(--cg-headlines-hero-control-diameter);
}

@keyframes cg-headlines-hero-zoom {
    from { transform: scale(1); }
    to   { transform: scale(var(--cg-headlines-hero-zoom-scale)); }
}

@keyframes cg-headlines-hero-focus {
    from {
        transform: scale(var(--cg-headlines-hero-focus-scale));
        filter:    blur(var(--cg-headlines-hero-focus-blur));
    }
    to {
        transform: scale(1);
        filter:    blur(0);
    }
}

@keyframes cg-headlines-hero-copy-in {
    from { opacity: 0; transform: translateX(var(--cg-hero-copy-from, var(--cg-headlines-hero-copy-shift))); }
    to   { opacity: 1; transform: none; }
}

@keyframes cg-headlines-hero-copy-out-forward {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateX(calc(-1 * var(--cg-headlines-hero-copy-shift))); }
}

@keyframes cg-headlines-hero-copy-out-back {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateX(var(--cg-headlines-hero-copy-shift)); }
}

@property --cg-hero-ring {
    syntax:        '<percentage>';
    inherits:      false;
    initial-value: 0%;
}

@keyframes cg-headlines-hero-ring {
    from { --cg-hero-ring: 0%; }
    to   { --cg-hero-ring: 100%; }
}

@keyframes cg-headlines-hero-letter-in {
    from {
        opacity:    0;
        visibility: hidden;
        transform:  translateY(var(--cg-headlines-hero-letter-shift));
        filter:     blur(var(--cg-headlines-hero-letter-blur));
    }
    1% {
        visibility: visible;
    }
    to {
        opacity:    1;
        visibility: visible;
        transform:  none;
        filter:     blur(0);
    }
}

@keyframes cg-headlines-hero-row-in {
    from {
        opacity:   0;
        transform: translateY(var(--cg-headlines-hero-letter-shift));
        filter:    blur(var(--cg-headlines-hero-letter-blur));
    }
    to {
        opacity:   1;
        transform: none;
        filter:    blur(0);
    }
}

@keyframes cg-headlines-hero-progress {
    from { transform: translateX(-100%); }
    to   { transform: translateX(0); }
}

.component-headlines.layout-hero .cg-headlines-hero-carousel:not([data-bs-ride="carousel"]) {
    --cg-headlines-hero-zoom-scale:    var(--cg-headlines-hero-zoom-still-scale);
    --cg-headlines-hero-zoom-duration: var(--cg-headlines-hero-zoom-still-duration);
}

.component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item.active .cg-headlines-hero-image,
.component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item-next.carousel-item-start .cg-headlines-hero-image,
.component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item-prev.carousel-item-end .cg-headlines-hero-image {
    animation-name:            cg-headlines-hero-zoom;
    animation-duration:        var(--cg-headlines-hero-zoom-duration);
    animation-timing-function: ease-out;
    animation-fill-mode:       both;
}

.component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item.active .cg-headlines-hero-image,
.component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item-next.carousel-item-start .cg-headlines-hero-image,
.component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item-prev.carousel-item-end .cg-headlines-hero-image {
    animation-name:            cg-headlines-hero-focus;
    animation-duration:        var(--cg-headlines-hero-focus-duration);
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1);
    animation-fill-mode:       both;
}

.component-headlines.layout-hero .carousel-fade .carousel-item.active .cg-headlines-hero-copy,
.component-headlines.layout-hero .carousel-fade .carousel-item-next.carousel-item-start .cg-headlines-hero-copy,
.component-headlines.layout-hero .carousel-fade .carousel-item-prev.carousel-item-end .cg-headlines-hero-copy {
    animation-name:            cg-headlines-hero-copy-in;
    animation-duration:        var(--cg-headlines-hero-copy-duration);
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1);
    animation-fill-mode:       both;
}

.component-headlines.layout-hero .carousel-fade .carousel-item-prev.carousel-item-end .cg-headlines-hero-copy {
    --cg-hero-copy-from: calc(-1 * var(--cg-headlines-hero-copy-shift));
}

.component-headlines.layout-hero .carousel-fade .carousel-item.active.carousel-item-start .cg-headlines-hero-copy {
    animation-name: cg-headlines-hero-copy-out-forward;
}

.component-headlines.layout-hero .carousel-fade .carousel-item.active.carousel-item-end .cg-headlines-hero-copy {
    animation-name: cg-headlines-hero-copy-out-back;
}

.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur [data-cg-hero-seq]:not(.cg-headlines-hero-seq-run) .cg-headlines-hero-fx-unit {
    opacity:    0;
    visibility: hidden;
}

.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur .cg-headlines-hero-seq-run .cg-headlines-hero-fx-unit {
    animation-name:            cg-headlines-hero-letter-in;
    animation-duration:        var(--cg-headlines-hero-letter-duration);
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1);
    animation-fill-mode:       backwards;
    animation-delay:           calc(var(--cg-hero-seq-scale, 1) * (
        var(--cg-hero-seq-start, 0s) +
        var(--cg-hero-word-i, 0) * var(--cg-headlines-hero-word-delay) +
        var(--cg-hero-letter-i, 0) * var(--cg-headlines-hero-letter-delay)
    ));
}

.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur [data-cg-hero-seq]:not(.cg-headlines-hero-seq-run) > .cg-headlines-hero-fx-unit {
    visibility: visible;
}

.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur .cg-headlines-hero-seq-run > .cg-headlines-hero-fx-unit {
    animation-name: cg-headlines-hero-row-in;
}

.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur [data-cg-hero-seq] > .cg-headlines-hero-fx-unit:focus-within,
.component-headlines.layout-hero .cg-headlines-hero-titlefx-blur .cg-headlines-hero-title-link:focus-visible .cg-headlines-hero-fx-unit {
    visibility: visible !important;
    opacity:    1 !important;
    transform:  none !important;
    filter:     none !important;
}

.component-headlines.layout-hero .cg-headlines-hero-carousel[data-bs-ride="carousel"] .cg-headlines-hero-tabs:not(.cg-headlines-hero-tabs--dots) .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
    animation-name:            cg-headlines-hero-progress;
    animation-duration:        var(--cg-headlines-hero-interval);
    animation-timing-function: linear;
    animation-fill-mode:       both;
}

@media (max-width: 991.98px) {
    .component-headlines.layout-hero .cg-headlines-hero-nav {
        display:         flex;
        justify-content: flex-start;
        padding-left:    var(--cg-headlines-hero-gutter);
        padding-bottom:  1rem;
    }

    .component-headlines.layout-hero .cg-headlines-hero-nav:has(.cg-headlines-hero-tab:nth-child(2)) {
        padding-bottom: 2.75rem;
    }

    .component-headlines.layout-hero .cg-headlines-hero-nav-inner {
        position:                relative;
        width:                   auto;
        max-width:               100%;
        flex-wrap:               wrap;
        align-items:             center;
        justify-content:         flex-start;
        gap:                     0.75rem;
        margin:                  0;
        padding:                 var(--cg-headlines-hero-pill-pad);
        border:                  var(--bs-border-width, 1px) solid rgba(255, 255, 255, 0.22);
        border-radius:           100rem;
        background:              rgba(255, 255, 255, 0.1);
        backdrop-filter:         blur(14px) saturate(1.6);
        -webkit-backdrop-filter: blur(14px) saturate(1.6);
    }

    .component-headlines.layout-hero .cg-headlines-hero-buttons {
        padding: 0;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs {
        --cg-headlines-hero-tab-track: var(--cg-headlines-hero-mark-track);
        flex:      0 1 auto;
        flex-wrap: wrap;
        gap:       0;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab {
        flex:    0 0 auto;
        padding: var(--cg-headlines-hero-dot-target);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab-text {
        display: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab.active + .cg-headlines-hero-tab .cg-headlines-hero-tab-text,
    .component-headlines.layout-hero .cg-headlines-hero-tabs:has(.cg-headlines-hero-tab:nth-child(2)):has(.cg-headlines-hero-tab:last-child.active) .cg-headlines-hero-tab:first-child .cg-headlines-hero-tab-text {
        display:         flex;
        flex-direction:  row;
        align-items:     baseline;
        gap:             0.375rem;
        position:        absolute;
        top:             100%;
        left:            calc(-1 * var(--bs-border-width, 1px));
        margin-top:      var(--cg-headlines-hero-next-gap);
        width:           max-content;
        max-width:       var(--cg-headlines-hero-next-max);
        text-align:      left;
        pointer-events:  none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab-next {
        display:     inline;
        flex:        0 0 auto;
        color:       var(--cg-headlines-hero-tab-eyebrow-color);
        font-size:   var(--cg-headlines-hero-tab-eyebrow-size);
        font-weight: 700;
        letter-spacing: 0.08em;
        text-transform: uppercase;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab-next::after {
        content: ":";
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab-eyebrow {
        display: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab.active + .cg-headlines-hero-tab .cg-headlines-hero-tab-title,
    .component-headlines.layout-hero .cg-headlines-hero-tabs:has(.cg-headlines-hero-tab:last-child.active) .cg-headlines-hero-tab:first-child .cg-headlines-hero-tab-title {
        -webkit-line-clamp: 1;
        min-width:          0;
        flex:               0 1 auto;
        color:              var(--cg-headlines-hero-tab-title-active);
        text-transform:     none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tab-bar {
        width:  var(--cg-headlines-hero-dot-size);
        height: var(--cg-headlines-hero-dot-size);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab-fill {
        border-radius: 50%;
        transform:     scale(0);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
        transform: scale(1);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab-bar {
        position: relative;
        overflow: visible;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::before,
    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        content:        "";
        position:       absolute;
        top:            50%;
        left:           50%;
        width:          var(--cg-headlines-hero-ring-size);
        height:         var(--cg-headlines-hero-ring-size);
        margin:         calc(var(--cg-headlines-hero-ring-size) / -2) 0 0
                        calc(var(--cg-headlines-hero-ring-size) / -2);
        border-radius:  50%;
        pointer-events: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::before {
        border: var(--cg-headlines-hero-ring-w) solid var(--cg-headlines-hero-ring-track);
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        --cg-hero-ring: 100%;
        background-image: conic-gradient(from 0deg,
                              var(--cg-headlines-hero-tab-fill) var(--cg-hero-ring),
                              rgba(0, 0, 0, 0)                  var(--cg-hero-ring));
        -webkit-mask-image: radial-gradient(closest-side,
                              rgba(0, 0, 0, 0) calc(100% - var(--cg-headlines-hero-ring-w) - 0.5px),
                              #000             calc(100% - var(--cg-headlines-hero-ring-w) + 0.5px));
        mask-image: radial-gradient(closest-side,
                              rgba(0, 0, 0, 0) calc(100% - var(--cg-headlines-hero-ring-w) - 0.5px),
                              #000             calc(100% - var(--cg-headlines-hero-ring-w) + 0.5px));
    }

    .component-headlines.layout-hero .cg-headlines-hero-carousel[data-bs-ride="carousel"] .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        animation-name:            cg-headlines-hero-ring;
        animation-duration:        var(--cg-headlines-hero-interval);
        animation-timing-function: linear;
        animation-fill-mode:       both;
    }

    .component-headlines.layout-hero .cg-headlines-hero-carousel[data-bs-ride="carousel"] .cg-headlines-hero-tabs:not(.cg-headlines-hero-tabs--dots) .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
        animation-name: none;
        transform:      scale(1);
    }
}

.component-headlines.layout-hero .cg-headlines-hero-carousel[data-cg-carousel-paused] .cg-headlines-hero-tabs:not(.cg-headlines-hero-tabs--dots) .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
    animation-name: none;
}

@media (max-width: 991.98px) {
    .component-headlines.layout-hero .cg-headlines-hero-carousel[data-cg-carousel-paused] .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        animation-name: none;
    }
}

.component-headlines.layout-hero .cg-headlines-hero-carousel.cg-headlines-hero-fx-zoom[data-cg-carousel-paused] .cg-headlines-hero-image {
    animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
    .component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item.active .cg-headlines-hero-image,
    .component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item-next.carousel-item-start .cg-headlines-hero-image,
    .component-headlines.layout-hero .cg-headlines-hero-fx-zoom .carousel-item-prev.carousel-item-end .cg-headlines-hero-image,
    .component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item.active .cg-headlines-hero-image,
    .component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item-next.carousel-item-start .cg-headlines-hero-image,
    .component-headlines.layout-hero .cg-headlines-hero-fx-focus .carousel-item-prev.carousel-item-end .cg-headlines-hero-image,
    .component-headlines.layout-hero .carousel-fade .carousel-item.active .cg-headlines-hero-copy,
    .component-headlines.layout-hero .carousel-fade .carousel-item-next.carousel-item-start .cg-headlines-hero-copy,
    .component-headlines.layout-hero .carousel-fade .carousel-item-prev.carousel-item-end .cg-headlines-hero-copy,
    .component-headlines.layout-hero .carousel-fade .carousel-item.active.carousel-item-start .cg-headlines-hero-copy,
    .component-headlines.layout-hero .carousel-fade .carousel-item.active.carousel-item-end .cg-headlines-hero-copy,
    .component-headlines.layout-hero .cg-headlines-hero-carousel[data-bs-ride="carousel"] .cg-headlines-hero-tabs:not(.cg-headlines-hero-tabs--dots) .cg-headlines-hero-tab.active .cg-headlines-hero-tab-fill {
        animation-name: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-carousel[data-bs-ride="carousel"] .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        animation-name: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-tabs .cg-headlines-hero-tab.active .cg-headlines-hero-tab-bar::after {
        --cg-hero-ring: 100%;
    }

    .component-headlines.layout-hero .cg-headlines-hero-titlefx-blur .cg-headlines-hero-seq-run .cg-headlines-hero-fx-unit {
        animation-name: none;
    }

    .component-headlines.layout-hero .cg-headlines-hero-titlefx-blur [data-cg-hero-seq]:not(.cg-headlines-hero-seq-run) .cg-headlines-hero-fx-unit {
        opacity:    1;
        visibility: visible;
    }

    .component-headlines.layout-hero .cg-headlines-hero-related-link::after {
        transition-duration: 0s;
    }
}


/* ============================================================
   COMPONENT: cg-component-headlines / layout-card
   Card grid rendered by component-headlines-card-template. Every
   entry is a thumbnail card (.cg-card--thumbnail) in a responsive
   auto-fit grid (same engine as layout-featured's thumb grid). When
   pageSize > showSize the grid is chunked into Bootstrap carousel
   pages — one .cg-headlines-grid per slide. Carousel controls default
   into the component footer at the far right, beside the archive
   button (.cg-inc-footer / component-inc-footer); pass
   controlsPlacement=header to route them into the header band instead
   (component-inc-header's carousel slot — Reign does this). The
   archive button is also rendered by the shared component footer.

   Custom property hooks (override in theme.css; --cg-headlines-grid-min
   is also forwarded from the thumbMinWidth URL param via an inline
   style on the component wrapper):
     --cg-headlines-grid-min   min track width (drives columns)
     --cg-headlines-grid-gap   gap between cards
   ============================================================ */
.component-headlines.layout-card {
    --cg-headlines-grid-min:  15rem;
    --cg-headlines-grid-gap:  1rem;
}

/* Grid — same auto-fit behavior as layout-featured: tracks collapse
   once they'd drop below --cg-headlines-grid-min, so columns shrink
   many → 1 as the parent column narrows. */
.component-headlines.layout-card .cg-headlines-grid {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-headlines-grid-min), 1fr));
    gap:                   var(--cg-headlines-grid-gap);
}

/* Carousel — each slide carries one grid page. BS5 hides inactive
   .carousel-items; the small block padding keeps the card hover-zoom
   from clipping at the slide's edge. */
.component-headlines.layout-card .cg-headlines-card-carousel .carousel-item {
    padding-block: 0.25rem;
}

/* mobileScroll -- below lg a single-page grid becomes a swipeable row:
   one card per view (two at md), snapping card by card, scrollbar hidden
   (the footer's prev / next are the visible affordance). At lg+ the grid
   above applies unchanged. Cards keep equal heights: the row stretches
   its items and the item/card flex chain above fills them. */
@media (max-width: 991.98px) {
    .component-headlines.layout-card .cg-headlines-grid--scroll {
        display:               flex;
        overflow-x:            auto;
        overscroll-behavior-x: contain;
        scroll-snap-type:      x mandatory;
        scrollbar-width:       none;
    }
    .component-headlines.layout-card .cg-headlines-grid--scroll::-webkit-scrollbar {
        display: none;
    }
    .component-headlines.layout-card .cg-headlines-grid--scroll > .cg-headlines-grid-item {
        flex:              0 0 100%;
        min-width:         0;
        scroll-snap-align: start;
    }
}
@media (min-width: 768px) and (max-width: 991.98px) {
    .component-headlines.layout-card .cg-headlines-grid--scroll > .cg-headlines-grid-item {
        flex-basis: calc((100% - var(--cg-headlines-grid-gap, 1rem)) / 2);
    }
}
/* The scroll row's arrows are disabled at either end of the row (only
   they carry data-cg-scroll-step; carousel arrows are untouched). */
.component-headlines.layout-card .cg-inc-footer .cg-carousel-control[data-cg-scroll-step]:disabled {
    opacity: 0.4;
    cursor:  default;
}


/* ============================================================
   COMPONENT: cg-component-schedule / layout-list
   Bordered event cards rendered by cg-component-schedule-list-
   template. Each card: logo column (1-2 team logos with internal
   cell borders) | body (date+time, sport title, team names, notes)
   | kebab dropdown for event links.

   Custom property hooks (override in theme.css):
     --cg-schedule-gap                  vertical gap between event cards
     --cg-schedule-logo-col-width       width of the left logo column (also the
                                        side of each 1:1 logo cell)
     --cg-schedule-logo-watermark-opacity  opacity of the site-default-logo
                                        watermark shown when a team logo is
                                        unavailable
     --cg-schedule-body-padding         inner padding of the body region
     --cg-schedule-border               border around the card
     --cg-schedule-border-radius        corner radius
   ============================================================ */
.component-schedule {
    --cg-schedule-gap:              0.75rem;
    --cg-schedule-logo-col-width:   4rem;
    --cg-schedule-logo-watermark-opacity: 0.25;
    --cg-schedule-body-padding:     0.75rem 1rem;
    --cg-schedule-border:           var(--bs-border-width) solid var(--bs-border-color);
    --cg-schedule-border-radius:    var(--bs-border-radius, 0.375rem);
    --cg-schedule-card-bg:          var(--bs-body-bg, #fff);
}

.component-schedule.layout-list .cg-schedule-list {
    display:        flex;
    flex-direction: column;
    gap:            var(--cg-schedule-gap);
}

/* Event card — outer border + rounded corners. overflow: hidden
   clips the inner logo-column border-right at the card corners. */
.component-schedule .cg-event {
    border:         var(--cg-schedule-border);
    border-radius:  var(--cg-schedule-border-radius);
    overflow:       hidden;
}

/* Logo column — fixed width; border-right separates it from the body.
   space-evenly distributes equal space above / between / below the cells, so
   each logo sits centred in its own region (rather than the two squares
   clustering in the middle). The cells stay fixed 1:1 squares, so the leftover
   column height becomes that even spacing. position:relative anchors the
   centre divider (below). */
.component-schedule .cg-event-logos {
    position:        relative;
    width:           var(--cg-schedule-logo-col-width);
    border-right:    var(--cg-schedule-border);
    justify-content: space-evenly;
}

.component-schedule .cg-event-logo {
    flex:       0 0 auto;
    width:      100%;
    min-height: 0;
    padding:    0.75rem;
}

/* Two (or more) logos → each cell is an equal 1:1 square (side = the column
   width), so a matchup reads as a balanced, same-height stack. flex:0 0 auto
   keeps the square — letting it grow (flex:1) would stretch the height and
   break the ratio. min-height:0 (above) is essential: without it a flex item
   defaults to min-height:auto, so a tall logo's intrinsic height expands the
   cell past the 1:1 ratio (unequal cells, off-centre logos). A lone logo
   (:only-child) skips the square so its height collapses to the logo;
   space-evenly with one item still centres it. */
.component-schedule .cg-event-logo:not(:only-child) {
    aspect-ratio: 1 / 1;
}

/* Centre divider — with space-evenly the two squares sit apart, so a
   cell-to-cell border wouldn't land centred. Draw it as a fixed line at the
   column's vertical midpoint (which falls in the gap between the cells),
   decoupled from cell position. :has() limits it to the two-logo case so a
   lone logo gets no stray line. */
.component-schedule .cg-event-logos:has(.cg-event-logo + .cg-event-logo)::after {
    content:      "";
    position:     absolute;
    inset-inline: 0;
    top:          50%;
    border-top:   var(--cg-schedule-border);
}

/* The cell is a flex box (align-items/justify-content:center in the markup),
   so it centres the logo on both axes. Cap with max-width/height — NOT
   width/height:100% — because a percentage height against the aspect-ratio
   cell resolves as indefinite in some browsers and collapses, leaving tall
   logos off-centre. Natural size, contained, centred for any logo ratio. */
.component-schedule .cg-event-logo img {
    max-width:  100%;
    max-height: 100%;
    object-fit: contain;
}

/* Watermark — when a team logo is unavailable the cell shows the site default
   logo (the header logo) at a low opacity so it reads as a placeholder, not a
   real crest. Applied server-side (empty logo) and via the <img> onerror
   swap (broken logo). Override --cg-schedule-logo-watermark-opacity to tune. */
.component-schedule .cg-event-logo--placeholder img {
    opacity: var(--cg-schedule-logo-watermark-opacity, 0.25);
    filter: grayscale(1);
}

/* Body — content area between the logo column and the kebab. */
.component-schedule .cg-event-body {
    padding: var(--cg-schedule-body-padding);
}

.component-schedule .cg-event-when {
    color: var(--bs-secondary-color, #6c757d);
}

.component-schedule .cg-event-sport {

}

.component-schedule .cg-event-team + .cg-event-team {

}

/* Each team row: name on the left, its score (finished / in-progress events
   only) pushed to the right. */
.component-schedule .cg-event-team {
    display:         flex;
    align-items:     baseline;
    justify-content: space-between;
    gap:             0.75rem;
}
.component-schedule .cg-event-team-score {
    font-variant-numeric: tabular-nums;
}

/* Past-event result inherits body color (overrides the muted .when
   shade since it's a result, not a label). */
.component-schedule .cg-event--past .cg-event-result {
    color: var(--bs-body-color);
}

/* ------------------------------------------------------------------
   EVENT DETAILS LINK (FED-22765) — routes to the ?eid= landing page.

   Token is self-declared on the component element, not a wrapper: a
   --cg-* set on an ancestor is shadowed by the component's own
   declaration, so overriding it per-theme has to happen here.

   Colour resolves through --body-link first so a theme that recolours
   its body links recolours this too, falling back to the Bootstrap
   link token. Do NOT mirror this into --bs-link-color.
   ------------------------------------------------------------------ */
.component-schedule .cg-event-details {
    --cg-event-details-color:       var(--body-link, var(--bs-link-color));
    --cg-event-details-color-hover: var(--body-link-hover, var(--bs-link-hover-color));

    margin-top: 0.5rem;
}

.component-schedule .cg-event-details-link {
    color:           var(--cg-event-details-color);
    text-decoration: none;
}

/* The template gives this link position-relative + z-2 so it sits above
   the no-sport branch's .stretched-link. Without hover/focus feedback it
   would look like static text; :focus-visible matters most, since the
   stretched link makes the rest of the row a mouse-only target. */
.component-schedule .cg-event-details-link:hover,
.component-schedule .cg-event-details-link:focus-visible {
    color:           var(--cg-event-details-color-hover);
    text-decoration: underline;
}

/* Live indicator — the "LIVE" label and its dot. Colour is a token hook
   rather than Bootstrap's .text-danger so a theme can recolour it without
   !important. Self-declared on the element (see the note on
   .cg-event-details above), so a theme overrides it on
   .component-schedule .cg-event-status. Default stays the alert red.

   Only the dot moves (fa-beat-fade in the template, which Font Awesome
   stills under prefers-reduced-motion). The label used to pulse to 40%
   opacity as well, which dropped the text below AA for part of every
   cycle, so the label now holds its full colour. */
.component-schedule .cg-event-status {
    --cg-event-live-color: var(--bs-danger, #dc3545);

    color: var(--cg-event-live-color);
}

/* Actions toggle (kebab → close) — sits at the right edge of the
   card. position: relative + z-index above the stretched-link's so
   the button click lands on the toggle and not the card link below.
   When the event is open (.cg-event--actions-open) the ellipsis icon
   swaps to an X. Color is --bs-primary (matches the site link color)
   per the design call. */
.component-schedule .cg-event-actions {
    position:    relative;
    z-index:     3;
    align-self:  stretch;
    display:     flex;
    align-items: stretch;
    padding:     0;
    border-left: var(--cg-schedule-border);
}

.component-schedule .cg-event-actions-toggle {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    align-self:      stretch;
    padding:         0 0.25rem;
    border-radius:   0 var(--bs-border-radius, 0.375rem) var(--bs-border-radius, 0.375rem) 0;
    color:           var(--bs-primary, var(--bs-link-color));
    line-height:     1;
    text-decoration: none;
    transition:      background-color 0.15s ease-in-out, color 0.15s ease-in-out;
}

.component-schedule .cg-event-actions-toggle:hover,
.component-schedule .cg-event-actions-toggle:focus-visible {
    background-color: var(--bs-gray-100, #f8f9fa);
    color:            var(--bs-primary-text-emphasis, var(--bs-primary));
}

/* Icon swap — ellipsis when closed, X when open. Driven by the
   .cg-event--actions-open class on the parent .cg-event article. */
.component-schedule .cg-event-actions-icon--close {
    display: none;
}

.component-schedule .cg-event--actions-open .cg-event-actions-icon--open {
    display: none;
}

.component-schedule .cg-event--actions-open .cg-event-actions-icon--close {
    display: inline-block;
}

/* Sliding actions panel — absolutely positioned starting just past
   the logos column, slides over the body when the article carries
   .cg-event--actions-open. Background matches the card's body bg so
   the panel reads as a layered surface, not a popover. */
.component-schedule .cg-event-panel {
    position:         absolute;
    top:              0;
    bottom:           0;
    right:            0;
    left:             var(--cg-schedule-logo-col-width);
    background-color: var(--cg-schedule-card-bg, var(--bs-body-bg, #fff));
    transform:        translateX(100%);
    transition:       transform 0.25s ease;
    z-index:          2;
    padding:          var(--cg-schedule-body-padding);
    padding-inline-end: 3rem;       /* leave room so icons don't sit under the kebab */
    display:          flex;
    align-items:      center;
    pointer-events:   none;          /* not interactive while hidden */
}

.component-schedule .cg-event--actions-open .cg-event-panel {
    transform:      translateX(0);
    pointer-events: auto;
}

.component-schedule .cg-event-panel-list {
    gap: 0.5rem 1rem;
    width: 100%;
}

.component-schedule .cg-event-panel-link {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           2rem;
    height:          2rem;
    color:           var(--bs-primary, var(--bs-link-color));
    font-size:       1.1rem;
    text-decoration: none;
    border-radius:   50%;
    transition:      color 0.15s ease, background-color 0.15s ease;
}

.component-schedule .cg-event-panel-link:hover,
.component-schedule .cg-event-panel-link:focus-visible {
    color:            var(--bs-primary-text-emphasis, var(--bs-primary));
    background-color: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.1);
}

/* Text-mode related links (linkStyle=text). The template renders a visible
   .cg-event-panel-link-label instead of an FA glyph and tags the panel with
   .cg-event-panel--text. Undo the fixed 2rem circular icon-button treatment so
   the links read as text: auto size, pill padding, standard link font-size. */
.component-schedule .cg-event-panel--text .cg-event-panel-link {
    width:         auto;
    height:        auto;
    padding:       0.25rem 0.5rem;
    border-radius: var(--bs-border-radius, 0.375rem);
    font-size:     0.9375rem;
}
.component-schedule .cg-event-panel-link-label {
    white-space: nowrap;
}


/* ============================================================
   COMPONENT: component-schedule / layout-cards
   Event-card scoreboard rendered by component-schedule-cards-template:
   heading row (title + sponsor, Home / Away legend, sport select,
   Events | Results tabs), a horizontally scrolling row of equal event
   cards, and a footer with round prev / next either side of the archive
   pill. Mobile-first: one card per view, the select full width, the
   tabs and legend centred, the legend below the cards. At lg+ the
   legend, select and tabs join the title row and showSize cards fit
   the row (the template forwards it as --cg-event-card-cols-lg).

   Custom property hooks (override in theme.css, on the component):
     --cg-schedule-cards-row-gap        space between the stacked blocks
                                        (title row, cards, footer)
     --cg-event-card-gap                gap between cards
     --cg-event-card-bg                 card ground
     --cg-event-card-border             card outline AND the rule above
                                        the link row (one token)
     --cg-event-card-radius             card corner radius
     --cg-event-card-bar-bg / -color    date bar, home and default
     --cg-event-card-bar-bg-away / -color-away   date bar, away games
     --cg-event-card-bar-size           date bar font size
     --cg-event-card-bar-height         date bar minimum height
     --cg-event-card-bar-gap            space between date and time
     --cg-event-card-sport-bg / -color  sport bar
     --cg-event-card-sport-size / -weight / -height
     --cg-event-card-body-pad           padding around logo + text
     --cg-event-card-logo-size          logo box (square)
     --cg-event-card-matchup-size / -color
     --cg-event-card-meta-size / -color notes and location (keep the
                                        colour at 4.5:1 on the card ground)
     --cg-event-card-links-color / -hover / -size / -gap / -height
     --cg-event-legend-dot / -size      legend dot diameter, label size
     --cg-schedule-cards-tab-font / -size / -weight
     --cg-schedule-cards-tab-color / -color-active
     --cg-schedule-cards-tab-indicator / -indicator-color / -gap
     --cg-schedule-cards-select-width   select width at lg+
     --cg-event-card-cols-lg            cards per view at lg+ (set inline
                                        from showSize; do not hand-edit)
   ============================================================ */
.component-schedule.layout-cards {
    --cg-schedule-cards-row-gap:            1.125rem;
    --cg-event-card-cols:                   1;
    --cg-event-card-gap:                    1rem;
    --cg-event-card-bg:                     var(--bs-card-bg, var(--bs-body-bg, #fff));
    --cg-event-card-border:                 var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    --cg-event-card-radius:                 var(--bs-border-radius-sm, 0.25rem);
    --cg-event-card-bar-bg:                 var(--bs-primary, #0d6efd);
    --cg-event-card-bar-color:              var(--bs-white, #fff);
    --cg-event-card-bar-bg-away:            var(--bs-secondary, #6c757d);
    --cg-event-card-bar-color-away:         var(--bs-white, #fff);
    --cg-event-card-bar-size:               0.875rem;
    --cg-event-card-bar-height:             2rem;
    --cg-event-card-bar-gap:                1.125rem;
    --cg-event-card-sport-bg:               var(--bs-border-color, #dee2e6);
    --cg-event-card-sport-color:            var(--body-link, var(--bs-link-color));
    --cg-event-card-sport-size:             1rem;
    --cg-event-card-sport-weight:           700;
    --cg-event-card-sport-height:           1.875rem;
    --cg-event-card-body-pad:               1.5rem 1rem 1.25rem;
    --cg-event-card-logo-size:              5.5rem;
    --cg-event-card-matchup-size:           1rem;
    --cg-event-card-matchup-color:          var(--bs-body-color, #212529);
    --cg-event-card-meta-size:              0.875rem;
    --cg-event-card-meta-color:             var(--bs-secondary-color, #6c757d);
    --cg-event-card-links-color:            var(--body-link, var(--bs-link-color));
    --cg-event-card-links-hover:            var(--body-link-hover, var(--bs-link-hover-color));
    --cg-event-card-links-size:             1.125rem;
    --cg-event-card-links-gap:              1.125rem;
    --cg-event-card-links-height:           1.625rem;
    --cg-event-legend-dot:                  1rem;
    --cg-event-legend-size:                 0.875rem;
    --cg-schedule-cards-tab-font:           inherit;
    --cg-schedule-cards-tab-size:           1.125rem;
    --cg-schedule-cards-tab-weight:         700;
    --cg-schedule-cards-tab-color:          var(--bs-secondary-color, #6c757d);
    --cg-schedule-cards-tab-color-active:   var(--body-link, var(--bs-link-color));
    --cg-schedule-cards-tab-indicator:      2px;
    --cg-schedule-cards-tab-indicator-color: currentColor;
    --cg-schedule-cards-tab-gap:            0.875rem;
    --cg-schedule-cards-select-width:       12.375rem;

    /* Mobile: one stacked column; order puts the legend under the cards. */
    display:        flex;
    flex-direction: column;
    gap:            var(--cg-schedule-cards-row-gap);
}
/* Legend -- a dot per side plus its text label. */
.component-schedule.layout-cards .cg-event-legend {
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    gap:         0.25rem 1.125rem;
    margin:      0;
    padding:     0;
    font-size:   var(--cg-event-legend-size);
    line-height: 1.2;
}
.component-schedule.layout-cards .cg-event-legend-item {
    display:     inline-flex;
    align-items: center;
    gap:         0.375rem;
}
.component-schedule.layout-cards .cg-event-legend-dot {
    flex:          0 0 auto;
    width:         var(--cg-event-legend-dot);
    height:        var(--cg-event-legend-dot);
    border-radius: 50%;
    background:    var(--cg-event-card-bar-bg);
}
.component-schedule.layout-cards .cg-event-legend-item--away .cg-event-legend-dot {
    background: var(--cg-event-card-bar-bg-away);
}

/* Sport select -- compact, about 2rem tall: 1rem text on a 1.25 line below
   lg, because iPhone Safari zooms the whole page when a control with
   smaller text takes focus; 0.875rem on a 1.5 line from lg. */
.component-schedule.layout-cards .cg-schedule-cards-select {
    width:         100%;
    font-size:     1rem;
    line-height:   1.25;
    padding:       0.25rem 2.25rem 0.25rem 0.75rem;
    border-radius: var(--bs-border-radius-sm, 0.25rem);
}
@media (min-width: 992px) {
    .component-schedule.layout-cards .cg-schedule-cards-select {
        font-size:   0.875rem;
        line-height: 1.5;
    }
}

/* Tabs -- heading-weight text tabs with an underline the width of the
   label; no rail. Overrides the .nav-link box (padding, colour). */
.component-schedule.layout-cards .cg-schedule-cards-tabs {
    display:   flex;
    flex-wrap: nowrap;
    gap:       var(--cg-schedule-cards-tab-gap);
    margin:    0;
    padding:   0;
    border:    0;
}
.component-schedule.layout-cards .cg-schedule-cards-tab {
    padding:       0.125rem 0;
    border:        0;
    border-bottom: var(--cg-schedule-cards-tab-indicator) solid transparent;
    border-radius: 0;
    background:    none;
    color:         var(--cg-schedule-cards-tab-color);
    font-family:   var(--cg-schedule-cards-tab-font);
    font-size:     var(--cg-schedule-cards-tab-size);
    font-weight:   var(--cg-schedule-cards-tab-weight);
    line-height:   1.2;
}
.component-schedule.layout-cards .cg-schedule-cards-tab:hover,
.component-schedule.layout-cards .cg-schedule-cards-tab:focus-visible,
.component-schedule.layout-cards .cg-schedule-cards-tab.active {
    color: var(--cg-schedule-cards-tab-color-active);
}
.component-schedule.layout-cards .cg-schedule-cards-tab.active {
    border-bottom-color: var(--cg-schedule-cards-tab-indicator-color);
}

/* Placement -- after the element rules above so the lg+ margins win
   over their margin resets (same specificity, later in the file). */
.component-schedule.layout-cards > .cg-schedule-cards-header  { order: 1; margin: 0; }
.component-schedule.layout-cards > .cg-schedule-cards-filter  { order: 2; }
.component-schedule.layout-cards > .cg-schedule-cards-tabs    { order: 3; align-self: center; }
.component-schedule.layout-cards > .cg-schedule-cards-panes   { order: 4; min-width: 0; }
.component-schedule.layout-cards > .cg-event-legend           { order: 5; align-self: center; justify-content: center; }
.component-schedule.layout-cards > .cg-inc-footer             { order: 6; }

@media (min-width: 576px) {
    .component-schedule.layout-cards { --cg-event-card-cols: min(2, var(--cg-event-card-cols-lg, 5)); }
}
@media (min-width: 768px) {
    .component-schedule.layout-cards { --cg-event-card-cols: min(3, var(--cg-event-card-cols-lg, 5)); }
}

/* lg+: one wrapping row. The header grows, pushing legend | select | tabs
   to the right edge (flush with the cards); the cards and the footer each
   take a full line of their own. */
@media (min-width: 992px) {
    .component-schedule.layout-cards {
        --cg-event-card-cols: var(--cg-event-card-cols-lg, 5);

        flex-direction: row;
        flex-wrap:      wrap;
        align-items:    center;
        column-gap:     0;
    }
    .component-schedule.layout-cards > .cg-schedule-cards-header { flex: 1 1 auto; min-width: 0; }
    .component-schedule.layout-cards > .cg-event-legend          { order: 2; margin-inline-start: 1.5rem; }
    .component-schedule.layout-cards > .cg-schedule-cards-filter { order: 3; margin-inline-start: 2.75rem; width: var(--cg-schedule-cards-select-width); }
    .component-schedule.layout-cards > .cg-schedule-cards-tabs   { order: 4; margin-inline-start: 2.375rem; }
    .component-schedule.layout-cards > .cg-schedule-cards-panes,
    .component-schedule.layout-cards > .cg-inc-footer            { flex: 0 0 100%; }
    /* No heading band: the first control takes the slack instead. */
    .component-schedule.layout-cards > :first-child:not(.cg-schedule-cards-header) { margin-inline-start: auto; }
}

/* Track -- a scroll-snap row; the footer arrows page it one view at a
   time and swipe / trackpad scroll it directly. The scrollbar is hidden
   (the arrows are the visible affordance). Cards stretch to the tallest
   in the row, so the link rows line up. position: relative makes the
   track the containing block of the cards' absolutely positioned
   .visually-hidden labels, so its overflow clips them too -- without it
   an off-screen card's label escapes the track and widens the page
   (a horizontal scroll at every width below lg). */
.component-schedule.layout-cards .cg-schedule-cards {
    position:             relative;
    display:              flex;
    gap:                  var(--cg-event-card-gap);
    margin:               0;
    padding:              0;
    overflow-x:           auto;
    overscroll-behavior-x: contain;
    scroll-snap-type:     x mandatory;
    scrollbar-width:      none;
}
.component-schedule.layout-cards .cg-schedule-cards::-webkit-scrollbar {
    display: none;
}
.component-schedule.layout-cards .cg-schedule-cards-item {
    display:           flex;
    flex:              0 0 calc((100% - (var(--cg-event-card-cols) - 1) * var(--cg-event-card-gap)) / var(--cg-event-card-cols));
    min-width:         0;
    scroll-snap-align: start;
}
.component-schedule.layout-cards .cg-schedule-cards-empty {
    margin:        0;
    padding:       2rem 1rem;
    text-align:    center;
    color:         var(--cg-event-card-meta-color);
    border:        var(--cg-event-card-border);
    border-radius: var(--cg-event-card-radius);
}

/* Card */
.component-schedule.layout-cards .cg-event-card {
    display:          flex;
    flex-direction:   column;
    width:            100%;
    overflow:         hidden;
    text-align:       center;
    background-color: var(--cg-event-card-bg);
    border:           var(--cg-event-card-border);
    border-radius:    var(--cg-event-card-radius);
}
.component-schedule.layout-cards .cg-event-card-when {
    display:         flex;
    flex-wrap:       wrap;
    align-items:     center;
    justify-content: center;
    column-gap:      var(--cg-event-card-bar-gap);
    min-height:      var(--cg-event-card-bar-height);
    padding:         0.25rem 0.75rem;
    background:      var(--cg-event-card-bar-bg);
    color:           var(--cg-event-card-bar-color);
    font-size:       var(--cg-event-card-bar-size);
    line-height:     1.25;
}
.component-schedule.layout-cards .cg-event-card--away .cg-event-card-when {
    background: var(--cg-event-card-bar-bg-away);
    color:      var(--cg-event-card-bar-color-away);
}
.component-schedule.layout-cards .cg-event-card-datetime {
    display:    inline-flex;
    flex-wrap:  wrap;
    column-gap: var(--cg-event-card-bar-gap);
}
.component-schedule.layout-cards .cg-event-card-status {
    display:        inline-flex;
    align-items:    center;
    gap:            0.375rem;
    font-weight:    700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}
.component-schedule.layout-cards .cg-event-card-live-dot {
    font-size: 0.5em;
}
.component-schedule.layout-cards .cg-event-card-sport {
    display:         flex;
    align-items:     center;
    justify-content: center;
    min-height:      var(--cg-event-card-sport-height);
    padding:         0.125rem 0.75rem;
    background:      var(--cg-event-card-sport-bg);
    color:           var(--cg-event-card-sport-color);
    font-size:       var(--cg-event-card-sport-size);
    font-weight:     var(--cg-event-card-sport-weight);
    line-height:     1.2;
}
.component-schedule.layout-cards .cg-event-card-sport-link {
    color:           inherit;
    text-decoration: none;
}
.component-schedule.layout-cards .cg-event-card-sport-link:hover,
.component-schedule.layout-cards .cg-event-card-sport-link:focus-visible {
    text-decoration: underline;
}
.component-schedule.layout-cards .cg-event-card-body {
    display:        flex;
    flex:           1 1 auto;
    flex-direction: column;
    align-items:    center;
    padding:        var(--cg-event-card-body-pad);
}
.component-schedule.layout-cards .cg-event-card-logo {
    display:         flex;
    flex:            0 0 auto;
    align-items:     center;
    justify-content: center;
    width:           var(--cg-event-card-logo-size);
    height:          var(--cg-event-card-logo-size);
    margin-bottom:   0.5rem;
}
.component-schedule.layout-cards .cg-event-card-logo-img {
    max-width:  100%;
    max-height: 100%;
    object-fit: contain;
}
.component-schedule.layout-cards .cg-event-card-logo--placeholder .cg-event-card-logo-img {
    opacity: var(--cg-schedule-logo-watermark-opacity, 0.25);
}
.component-schedule.layout-cards .cg-event-card-matchup {
    margin:      0;
    color:       var(--cg-event-card-matchup-color);
    font-size:   var(--cg-event-card-matchup-size);
    line-height: 1.5;
}
.component-schedule.layout-cards .cg-event-card-matchup-link {
    color:           inherit;
    text-decoration: none;
}
.component-schedule.layout-cards .cg-event-card-matchup-link:hover,
.component-schedule.layout-cards .cg-event-card-matchup-link:focus-visible {
    text-decoration: underline;
}
.component-schedule.layout-cards .cg-event-card-result {
    margin:      0.125rem 0 0;
    font-weight: 700;
    line-height: 1.4;
}
.component-schedule.layout-cards .cg-event-card-notes,
.component-schedule.layout-cards .cg-event-card-location {
    margin:      0;
    color:       var(--cg-event-card-meta-color);
    font-size:   var(--cg-event-card-meta-size);
    line-height: 1.1;
}
.component-schedule.layout-cards :is(.cg-event-card-matchup, .cg-event-card-result) + :is(.cg-event-card-notes, .cg-event-card-location) {
    margin-top: 1.125rem;
}
.component-schedule.layout-cards .cg-event-card-notes + .cg-event-card-location {
    margin-top: 0.125rem;
}

/* Link row -- always present (empty when the event has no links) so the
   rule and the card height match across the row. 24px minimum targets. */
.component-schedule.layout-cards .cg-event-card-foot {
    display:         flex;
    align-items:     center;
    justify-content: center;
    min-height:      var(--cg-event-card-links-height);
    border-top:      var(--cg-event-card-border);
}
.component-schedule.layout-cards .cg-event-card-links {
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    justify-content: center;
    gap:         0 var(--cg-event-card-links-gap);
    margin:      0;
    padding:     0;
}
.component-schedule.layout-cards .cg-event-card-link {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    min-width:       1.5rem;
    min-height:      1.5rem;
    color:           var(--cg-event-card-links-color);
    font-size:       var(--cg-event-card-links-size);
    line-height:     1;
    text-decoration: none;
}
.component-schedule.layout-cards .cg-event-card-link:hover,
.component-schedule.layout-cards .cg-event-card-link:focus-visible {
    color: var(--cg-event-card-links-hover);
}

/* Footer -- prev | archive pill | next on one centred row at every width.
   The controls wrapper dissolves (display: contents) so the two arrows
   become footer items and the prev arrow is ordered before the pill. The
   component's own row gap spaces it, so the footer margin is dropped. */
.component-schedule.layout-cards > .cg-inc-footer {
    flex-direction:  row;
    flex-wrap:       nowrap;
    align-items:     center;
    justify-content: center;
    margin-top:      0;
}
.component-schedule.layout-cards > .cg-inc-footer > .cg-inc-footer-archive {
    flex:       0 1 auto;
    align-self: center;
}
.component-schedule.layout-cards > .cg-inc-footer > .cg-inc-footer-carousel-controls {
    display: contents;
}
.component-schedule.layout-cards > .cg-inc-footer .carousel-control-prev {
    order: -1;
}
.component-schedule.layout-cards > .cg-inc-footer .cg-carousel-control:disabled {
    opacity: 0.4;
    cursor:  default;
}

/* ============================================================
   COMPONENT: cg-component-schedule / layout-carousel
   Horizontal scoreboard rendered by component-schedule-carousel-template.
   Shares the .component-schedule base above (card surface, border, logo
   watermark, actions panel); everything layout-specific is scoped to
   .layout-carousel so the list is untouched.

   The track is a native scroll-snap row — no slide chunking — so the cards
   per view can change per breakpoint. The template writes the four counts
   as --cg-schedule-per-view(-md/-lg/-xl) on the component element.

   Tokens (set on .component-schedule.layout-carousel):
     --cg-schedule-carousel-gap       space between cards. MUST carry a unit
                                      (0px, not 0): it feeds a calc() with a
                                      percentage, and a unitless 0 there makes
                                      the card width invalid, so every card
                                      collapses into one view
     --cg-schedule-carousel-btn-size  prev/next button size
     --cg-schedule-carousel-btn-color prev/next glyph colour
     --cg-schedule-card-sport-color   sport label colour. Deliberately NOT
                                      declared here: its fallback (--body-link)
                                      must resolve on the link itself, so a
                                      theme that resets --body-link on the card
                                      (a white card on a dark section) is
                                      honoured. Declared here, it would freeze
                                      to the section's link colour
     --cg-schedule-card-sport-transform  sport label text-transform
     --cg-schedule-card-logo-size     team logo size in a row
     --cg-schedule-card-team-gap      space between a team logo and its name
     --cg-schedule-card-divider       rule between the date line and teams
   ============================================================ */
.component-schedule.layout-carousel {
    --cg-schedule-carousel-gap:        var(--cg-schedule-gap, 0.75rem);
    --cg-schedule-carousel-btn-size:   2.5rem;
    --cg-schedule-carousel-btn-color:  currentColor;
    --cg-schedule-card-sport-transform: uppercase;
    --cg-schedule-card-logo-size:      2rem;
    --cg-schedule-card-team-gap:       0.5rem;
    --cg-schedule-card-divider:        var(--cg-schedule-border);
    --cg-schedule-per-view-active:     var(--cg-schedule-per-view, 1);
}

@media (min-width: 768px) {
    .component-schedule.layout-carousel {
        --cg-schedule-per-view-active: var(--cg-schedule-per-view-md, 2);
    }
}

@media (min-width: 992px) {
    .component-schedule.layout-carousel {
        --cg-schedule-per-view-active: var(--cg-schedule-per-view-lg, 3);
    }
}

@media (min-width: 1200px) {
    .component-schedule.layout-carousel {
        --cg-schedule-per-view-active: var(--cg-schedule-per-view-xl, 4);
    }
}

/* Arrows flank the track; the track takes the middle column. */
.component-schedule.layout-carousel .cg-schedule-carousel {
    display:     flex;
    align-items: center;
    gap:         0.25rem;
}

.component-schedule.layout-carousel .cg-schedule-track {
    flex:                1 1 auto;
    min-width:           0;
    overflow-x:          auto;
    overscroll-behavior-x: contain;
    scroll-snap-type:    x mandatory;
    scrollbar-width:     none;
}

.component-schedule.layout-carousel .cg-schedule-track::-webkit-scrollbar {
    display: none;
}

.component-schedule.layout-carousel .cg-schedule-track:focus-visible {
    outline:        2px solid var(--bs-focus-ring-color, currentColor);
    outline-offset: 2px;
}

.component-schedule.layout-carousel .cg-schedule-slides {
    display: flex;
    gap:     var(--cg-schedule-carousel-gap);
}

/* Width per card = (track - the gaps between N cards) / N. */
.component-schedule.layout-carousel .cg-schedule-slide {
    flex:              0 0 calc((100% - (var(--cg-schedule-per-view-active) - 1) * var(--cg-schedule-carousel-gap)) / var(--cg-schedule-per-view-active));
    min-width:         0;
    scroll-snap-align: start;
    display:           flex;
}

.component-schedule.layout-carousel .cg-schedule-carousel-btn {
    flex:            0 0 auto;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           var(--cg-schedule-carousel-btn-size);
    height:          var(--cg-schedule-carousel-btn-size);
    padding:         0;
    border:          0;
    background:      transparent;
    color:           var(--cg-schedule-carousel-btn-color);
    font-size:       1.25rem;
}

.component-schedule.layout-carousel .cg-schedule-carousel-btn:disabled {
    opacity: 0.3;
}

/* Everything fits — no scrolling, so no arrows (the JS sets the class). */
.component-schedule.layout-carousel .cg-schedule-carousel--static .cg-schedule-carousel-btn {
    visibility: hidden;
}

/* ---- Card ---------------------------------------------------------------
   Vertical, not the list's logo-column row. */
.component-schedule.layout-carousel .cg-event--card {
    display:        flex;
    flex-direction: column;
    width:          100%;
}

.component-schedule.layout-carousel .cg-event--card .cg-event-body {
    flex:           1 1 auto;
    display:        flex;
    flex-direction: column;
    gap:            0.5rem;
    padding-inline-end: 2.5rem;   /* room for the kebab in the top corner */
}

.component-schedule.layout-carousel .cg-event-sport {
    font-weight:    700;
    line-height:    1.2;
    text-transform: var(--cg-schedule-card-sport-transform);
}

.component-schedule.layout-carousel .cg-event-sport .cg-event-link {
    color:           var(--cg-schedule-card-sport-color, var(--body-link, var(--bs-link-color)));
    text-decoration: none;
}

.component-schedule.layout-carousel .cg-event-sport .cg-event-link:hover,
.component-schedule.layout-carousel .cg-event-sport .cg-event-link:focus-visible {
    text-decoration: underline;
}

/* Date left, status/time right, then the divider. */
.component-schedule.layout-carousel .cg-event-when {
    display:         flex;
    justify-content: space-between;
    align-items:     baseline;
    gap:             0.5rem;
    padding-bottom:  0.5rem;
    margin-inline-end: -1.5rem;   /* reclaim the kebab gutter under the head */
    border-bottom:   var(--cg-schedule-card-divider);
    color:           inherit;
}

/* The list's .component-schedule .cg-event-status rule paints EVERY status
   in --cg-event-live-color (red) — in the list that class only ever carries
   LIVE. Here it also carries Final / Postponed / Canceled, so it is reset to
   the card ink; only --live (next rule) takes the live colour. */
.component-schedule.layout-carousel .cg-event-status,
.component-schedule.layout-carousel .cg-event-time {
    color:       inherit;
    font-weight: 700;
    text-align:  end;
}

.component-schedule.layout-carousel .cg-event-status--live {
    color: var(--cg-event-live-color, var(--bs-danger, #dc3545));
}

.component-schedule.layout-carousel .cg-event-status--disrupted {
    font-style: italic;
}

.component-schedule.layout-carousel .cg-event-teams {
    display:        flex;
    flex-direction: column;
    gap:            0.5rem;
    margin-inline-end: -1.5rem;
}

.component-schedule.layout-carousel .cg-event-team {
    display:     flex;
    align-items: center;
    gap:         var(--cg-schedule-card-team-gap);
    justify-content: flex-start;
}

.component-schedule.layout-carousel .cg-event-team-name {
    flex:      1 1 auto;
    min-width: 0;
}

.component-schedule.layout-carousel .cg-event-team-score {
    flex:        0 0 auto;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
}

.component-schedule.layout-carousel .cg-event-team--winner .cg-event-team-name,
.component-schedule.layout-carousel .cg-event-team--winner .cg-event-team-score {
    font-weight: 700;
}

.component-schedule.layout-carousel .cg-event-logo {
    flex:            0 0 auto;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           var(--cg-schedule-card-logo-size);
    height:          var(--cg-schedule-card-logo-size);
    padding:         0;
}

.component-schedule.layout-carousel .cg-event-notes,
.component-schedule.layout-carousel .cg-event-location {
    color: var(--bs-secondary-color, #6c757d);
}

/* Notes and location belong to the matchup, so they start on the team-name
   line, not under the logos. Only when the card actually shows logos
   (showLogos=false leaves the names flush left, and the notes with them). */
.component-schedule.layout-carousel .cg-event--card:has(.cg-event-logo) .cg-event-notes,
.component-schedule.layout-carousel .cg-event--card:has(.cg-event-logo) .cg-event-location {
    padding-inline-start: calc(var(--cg-schedule-card-logo-size) + var(--cg-schedule-card-team-gap));
}

/* Kebab: top-right corner of the card instead of a full-height column. */
.component-schedule.layout-carousel .cg-event--card .cg-event-actions {
    position:    absolute;
    top:         0.5rem;
    right:       0.25rem;
    border-left: 0;
}

.component-schedule.layout-carousel .cg-event--card .cg-event-actions-toggle {
    padding:       0.25rem 0.5rem;
    border-radius: var(--bs-border-radius, 0.375rem);
}

/* The panel covers the whole card (no logo column to clear). */
.component-schedule.layout-carousel .cg-event--card .cg-event-panel {
    left: 0;
}

.component-schedule.layout-carousel .cg-schedule-empty {
    padding: var(--cg-schedule-body-padding);
}

/* ============================================================
   COMPONENT: cg-component-countdown
   Centered hero for the next upcoming event: large title, meta
   row (date | time | location), oversized live countdown digits
   (colon-separated, no labels), and a row of action buttons
   sourced from the event's links.

   Custom property hooks (override in theme.css):
     --cg-next-event-padding         inner card padding
     --cg-next-event-teams-size      matchup (team-names) font-size
     --cg-next-event-meta-gap        horizontal gap between meta cells
     --cg-next-event-meta-color      meta row text color
     --cg-next-event-digit-size      countdown digit font-size
     --cg-next-event-digit-color     countdown digit color
     --cg-next-event-action-*        action-button color / border / padding / gap
   ============================================================ */
.component-countdown {
    --cg-next-event-padding:        3rem 1rem;
    --cg-next-event-row-gap:        0;
    --cg-next-event-teams-size:     1.25rem;  /* matches --cg-card-title-size */
    --cg-next-event-meta-gap:       0.75rem;
    --cg-next-event-logo-box-size:  4.5rem;
    --cg-next-event-logo-padding:   0.5rem;
    --cg-next-event-digit-size:     2.5rem;  /* phone default; bumped to 5rem at md+ below */
    --cg-next-event-digit-color:    var(--bs-emphasis-color, var(--bs-body-color));

    /* Action buttons — bordered style consistent with the logo box /
       the cg- footer-archive + carousel-control aesthetic. */
    --cg-next-event-action-gap:         0.5rem;
    --cg-next-event-action-color:       var(--body-link, var(--bs-link-color));
    --cg-next-event-action-bg:          transparent;
    --cg-next-event-action-border:      var(--bs-border-width) solid var(--bs-border-color);
    --cg-next-event-action-radius:      var(--bs-border-radius);
    --cg-next-event-action-padding:     0.5rem 1rem;
    --cg-next-event-action-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-next-event-action-bg-hover:    var(--bs-tertiary-bg, rgba(0, 0, 0, 0.03));

    /* Background texture — defaults to the site logo (set on :root by
       the default-decorator). Override --cg-next-event-bg-image at the
       theme or site level for a unique texture.
         --cg-next-event-bg-image     image URL (or `none` to disable)
         --cg-next-event-bg-height    background-size height; 400% means
                                      the image renders four times as
                                      tall as the component so only the
                                      blurred middle slice is visible
         --cg-next-event-bg-blur      blur radius applied to the image
         --cg-next-event-bg-opacity   image opacity
         --cg-next-event-bg-grayscale 0 = original color, 1 = full gray
         --cg-next-event-overlay-color  solid color the gradient fades
                                        to at the bottom of the card. */
    --cg-next-event-bg-image:       var(--site-logo-url, none);
    --cg-next-event-bg-height:      400%;
    --cg-next-event-bg-blur:        20px;
    --cg-next-event-bg-opacity:     0.3;
    --cg-next-event-bg-grayscale:   1;
    --cg-next-event-overlay-color:  var(--bs-gray-200, #e9ecef);

    /* position:relative + isolation:isolate scope the layered pseudo-
       elements; overflow:hidden clips the oversized texture image so
       only the centered slice shows. */
    position:         relative;
    overflow:         hidden;
    isolation:        isolate;
    /* Light-gray panel — the ::before logo texture and the ::after gradient
       (which fades to --cg-next-event-overlay-color, also gray-200) sit on
       top, so the panel reads as a subtle textured gray-200 surface. */
    background-color: var(--bs-gray-200, #e9ecef);
}

/* Oversized countdown digits on tablet and up. The phone default
   (2.5rem) is set on the token above; md+ scales it to the full
   5rem hero size. */
@media (min-width: 768px) {
    .component-countdown {
        --cg-next-event-digit-size: 5rem;
    }
}

/* Background texture — deepest layer. The image is centered and
   sized so its height is --cg-next-event-bg-height (default 400% of
   the component height); the auto-sized width is then clipped by the
   component's overflow:hidden. Blurred + grayscaled + dimmed so the
   site logo (or replacement) reads as a soft, abstract pattern rather
   than a recognisable mark. */
.component-countdown::before {
    content:             "";
    position:            absolute;
    inset:               0;
    background-image:    var(--cg-next-event-bg-image);
    background-repeat:   no-repeat;
    background-position: center;
    background-size:     auto var(--cg-next-event-bg-height);
    filter:              grayscale(var(--cg-next-event-bg-grayscale))
                         blur(var(--cg-next-event-bg-blur));
    opacity:             var(--cg-next-event-bg-opacity);
    pointer-events:      none;
    z-index:             -2;
}

/* Gradient overlay — sits between the texture and the content. Fades
   from transparent at the top (texture visible) to the solid bg color
   at the bottom (texture hidden). Lets content at the bottom of the
   component (countdown digits, action buttons) read against a clean
   surface while the top (logos, title, meta) sits over the texture. */
.component-countdown::after {
    content:        "";
    position:       absolute;
    inset:          0;
    background:     linear-gradient(
                        to bottom,
                        transparent 0%,
                        var(--cg-next-event-overlay-color) 100%
                    );
    pointer-events: none;
    z-index:        -1;
}

.component-countdown .cg-next-event {
    padding: var(--cg-next-event-padding);
}

/* Sport label — muted text above the matchup; the `small` helper class in the
   markup sets the size (matching the event-notes typography). The prominent
   line is the team matchup, wrapped in .cg-next-event-title below. */
.component-countdown .cg-next-event-sport {
    color: var(--bs-secondary-color, #6c757d);
}

/* Matchup wrapper — the prominent event "title" line. Sizes the team names so
   they scale together; base prominent size, bumped to 1.25em at lg+ (as before). */
.component-countdown .cg-next-event-title {
    font-size: var(--cg-next-event-teams-size);
}
@media (min-width: 992px) {
    .component-countdown .cg-next-event-title {
        font-size: 1.25em;
    }
}

/* Add vertical breathing room between the stacked rows via row
   margin. Universal selector with adjacent sibling so only items
   after the first get the space. The meta row's top margin is
   reset below — date/time read as a natural continuation of the
   title, not a separated block. */
.component-countdown .cg-next-event > .container > * + * {
    margin-top: var(--cg-next-event-row-gap);
}

/* Rows that should sit flush against the row above (no gap) — meta
   reads as a continuation of the title, teams reads as a continuation
   of the meta, and notes reads as supporting detail on the teams. */
.component-countdown .cg-next-event-title,
.component-countdown .cg-next-event-meta,
.component-countdown .cg-next-event-teams,
.component-countdown .cg-next-event-notes {
    margin-top: 0;
}

/* The countdown is the focal point: the only row with both a top and a
   bottom margin, isolating the oversized digits from the header above and
   the actions below. Selector depth matches the > * + * row rule above
   (same specificity, declared later) so these margins win. The header's
   own bottom spacing comes from this top margin. */
.component-countdown .cg-next-event > .container > .cg-next-event-countdown {
    margin-top:    2rem;
    margin-bottom: 2rem;
}

.component-countdown .cg-next-event-meta {
    column-gap: var(--cg-next-event-meta-gap);
    row-gap:    0.25rem;
}

/* Header — wraps the team logos and the event details. By default (phones,
   and any single-logo event) it wraps: the logo(s) sit centered on the
   first line and .cg-next-event-details, which takes the full row width,
   drops to the line below. A two-team matchup at lg+ switches to a no-wrap
   row that flanks the details with one logo on each side (the lg block
   further down). Parent .cg-next-event has text-center for the details. */
.component-countdown .cg-next-event-header {
    display:         flex;
    flex-wrap:       wrap;
    align-items:     center;
    justify-content: center;
}

/* Details takes the full row so it wraps below the logos; the gap from the
   logo row above is added here, and reset in the lg flank layout where the
   details sits inline between the two logos. (No logos → no top margin.) */
.component-countdown .cg-next-event-details {
    flex-basis: 100%;
}
.component-countdown .cg-next-event-header[data-logos="1"] .cg-next-event-details,
.component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-details {
    margin-top: 2rem;
}

/* Logos — individual bordered boxes so they can flank the details at lg.
   When two boxes sit adjacent (the stacked layout) the shared edge collapses
   into a single pill: the trail box drops its left border and the inner
   corners are squared, so the pair reads as one bordered group with an
   internal divider — matching the old joined-group treatment. */
.component-countdown .cg-next-event-logo {
    width:            var(--cg-next-event-logo-box-size);
    height:           var(--cg-next-event-logo-box-size);
    flex:             0 0 auto;
    display:          flex;
    align-items:      center;
    justify-content:  center;
    padding:          var(--cg-next-event-logo-padding);
    border:           var(--bs-border-width) solid var(--bs-border-color);
    border-radius:    var(--bs-border-radius, 0.375rem);
    overflow:         hidden;          /* clip the logo at the rounded corners */
    background-color: var(--bs-body-bg, #fff);
}

/* Joined-pill seam between two adjacent logo boxes (stacked layout). */
.component-countdown .cg-next-event-logo--lead + .cg-next-event-logo--trail {
    border-left:               0;
    border-top-left-radius:    0;
    border-bottom-left-radius: 0;
}
.component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-logo--lead {
    border-top-right-radius:    0;
    border-bottom-right-radius: 0;
}

.component-countdown .cg-next-event-logo img {
    max-width:  100%;
    max-height: 100%;
    width:      auto;
    height:     auto;
    object-fit: contain;
}

/* lg+ — a two-team matchup flanks the details with a logo on each side.
   Source order is lead, trail, details; `order` interleaves them visually
   to lead | details | trail. The joined-pill seam is undone so each box
   reads as its own standalone bordered box again. */
@media (min-width: 992px) {
    .component-countdown .cg-next-event-header[data-logos="2"] {
        flex-wrap:  nowrap;
        column-gap: 2.5rem;
    }
    /* Details don't grow (flex:0) — the lead | details | trail cluster sizes to
       its content and the header's justify-content:center pulls it to the middle,
       so the logos sit next to the details instead of pinned to the viewport edges. */
    .component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-details {
        flex:       0 1 auto;
        flex-basis: auto;
        order:      1;
        margin-top: 0;
    }
    /* Logos read 25% larger in the flanking presentation. */
    .component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-logo {
        width:  calc(var(--cg-next-event-logo-box-size) * 1.25);
        height: calc(var(--cg-next-event-logo-box-size) * 1.25);
    }
    .component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-logo--lead {
        border-top-right-radius:    var(--bs-border-radius, 0.375rem);
        border-bottom-right-radius: var(--bs-border-radius, 0.375rem);
    }
    .component-countdown .cg-next-event-header[data-logos="2"] .cg-next-event-logo--trail {
        order:                     2;
        border-left:               var(--bs-border-width) solid var(--bs-border-color);
        border-top-left-radius:    var(--bs-border-radius, 0.375rem);
        border-bottom-left-radius: var(--bs-border-radius, 0.375rem);
    }
}

/* Team names — single inline line "Team 1 vs Team 2", centered.
   vs/at separator inherits color subtly via the .cg-next-event-vsat
   span so it reads as a connector word between the two names. */
.component-countdown .cg-next-event-teams {
    font-weight: 700;
}

.component-countdown .cg-next-event-vsat {
    font-weight: 400;
    color:       var(--bs-secondary-color, #6c757d);
    margin:      0 0.25rem;
}

/* Countdown — uses the legacy component-countdown-digits-template's
   markup. Force the ul to inline-flex (overriding the helper's
   d-flex utility) so it sizes to the digits' width instead of
   stretching to fill the parent. Parent .cg-next-event already has
   .text-center so the inline-block-sized ul centers naturally.
   Labels hidden; colons inherit body font-size to stay subtle
   against the oversized digits. */
.component-countdown .countdown-container .countdown {
    display:    inline-flex !important;   /* override .d-flex from helper markup */
    flex-wrap:  nowrap;
    gap:        0.25em;
    margin:     0;
    padding:    0;
    line-height: 1;
}

.component-countdown .countdown-container .countdown-label {
    display: none;                  /* no labels in this layout */
}

/* Digit blocks — the helper renders each as
   <div class="countdown-digit fw-bold fs-2">…</div>. The .fs-2 utility
   carries !important, so the font-size override below needs !important
   to win. (Effectively removes the fs-2 sizing from this scope; the
   value is driven entirely by --cg-next-event-digit-size.) */
.component-countdown .countdown-container .countdown-digit {
    font-size:     var(--cg-next-event-digit-size) !important;
    font-weight:   800;
    line-height:   1;
    color:         var(--cg-next-event-digit-color);
    padding:       0;
    background:    transparent;
    border-radius: 0;
    display:       inline-flex;
    align-items:   baseline;
}

/* Colon between segments — standard (body) font-size so it reads as
   ordinary punctuation against the oversized digits. */
.component-countdown .countdown-container .countdown > li {
    flex:        0 0 auto;
    display:     inline-flex;
    align-items: center;
    width:       auto;
}

.component-countdown .countdown-container .countdown > li + li::before {
    content:     ":";
    font-size:   calc(var(--cg-next-event-digit-size) / 2);
    font-weight: 700;
    color:       inherit;
    margin:      0 0.1em;
}

/* Action buttons — a row of event links (Tickets, Watch, Preview, …).
   Consistent bordered button styling via the --cg-next-event-action-*
   tokens (matches the logo box / footer-archive border treatment).
   Responsive width so the row reflows cleanly:
     xs 100% (1/row) · sm 50% (2) · md 33% (3) · lg+ 25% (4).
   flex-basis subtracts each row's share of the gap so exactly N fit
   per row; justify-content:center (in the markup) keeps short rows
   centered. The --cg-next-event-action-gap token drives both the flex
   gap and the basis math, so they stay in sync. */
.component-countdown .cg-next-event-actions {
    gap: var(--cg-next-event-action-gap);
}

.component-countdown .cg-next-event-action {
    flex:            0 0 100%;
    max-width:       100%;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    padding:         var(--cg-next-event-action-padding);
    color:           var(--cg-next-event-action-color);
    background:      var(--cg-next-event-action-bg);
    border:          var(--cg-next-event-action-border);
    border-radius:   var(--cg-next-event-action-radius);
    font-weight:     600;
    text-decoration: none;
    transition:      color .2s ease, background-color .2s ease, border-color .2s ease;
}

.component-countdown .cg-next-event-action:hover,
.component-countdown .cg-next-event-action:focus-visible {
    color:      var(--cg-next-event-action-color-hover);
    background: var(--cg-next-event-action-bg-hover);
}

@media (min-width: 576px) {   /* sm / phone — 2 per row */
    .component-countdown .cg-next-event-action {
        flex-basis: calc(50% - (var(--cg-next-event-action-gap) / 2));
        max-width:  calc(50% - (var(--cg-next-event-action-gap) / 2));
    }
}

@media (min-width: 768px) {   /* md — 3 per row */
    .component-countdown .cg-next-event-action {
        flex-basis: calc((100% / 3) - (var(--cg-next-event-action-gap) * 2 / 3));
        max-width:  calc((100% / 3) - (var(--cg-next-event-action-gap) * 2 / 3));
    }
}

@media (min-width: 992px) {   /* lg+ / desktop — 4 per row */
    .component-countdown .cg-next-event-action {
        flex-basis: calc(25% - (var(--cg-next-event-action-gap) * 3 / 4));
        max-width:  calc(25% - (var(--cg-next-event-action-gap) * 3 / 4));
    }
}

/* On-load reveal — logos drop in from above, actions rise in from
   below. Both keyframes also fade in. `both` fill-mode keeps each
   element at its from-state before the animation starts (no flash of
   final state) and at its to-state after it ends. The component's
   overflow:hidden clips the off-stage start positions so the elements
   appear to enter from outside the card. */
.component-countdown .cg-next-event-logo {
    animation: cg-next-event-slide-down 0.6s ease-out both;
}

.component-countdown .cg-next-event-actions {
    animation: cg-next-event-slide-up 0.6s ease-out both;
}

@keyframes cg-next-event-slide-down {
    from { transform: translateY(-100%); opacity: 0; }
    to   { transform: translateY(0);     opacity: 1; }
}

@keyframes cg-next-event-slide-up {
    from { transform: translateY(100%); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}

/* Skip the entrance animations for users who've opted in to reduced
   motion via OS / browser preference. The element renders at its
   natural position immediately. */
@media (prefers-reduced-motion: reduce) {
    .component-countdown .cg-next-event-logo,
    .component-countdown .cg-next-event-actions {
        animation: none;
    }
}


/* ============================================================
   COMPONENT: cg-component-featured-bio
   Featured player bio rendered by component-featured-bio-template.
   Each entry is a card with headshot on the left and body on the right
   (name, sport, position/number/year meta, optional summary).

   Custom property hooks (override in theme.css):
     --cg-featured-bio-gap                  gap between headshot and body
     --cg-featured-bio-headshot-width       fixed width of the headshot column
     --cg-featured-bio-headshot-aspect      aspect-ratio of the headshot box
                                            (default 1 / 1 — square bio headshot)
     --cg-featured-bio-headshot-radius      border-radius on the headshot
     --cg-featured-bio-article-image-width  fixed width of the article (16:9)
                                            image column (sm+); the bio
                                            headshot uses -headshot-width
     --cg-featured-bio-name-size            name font-size

   Bio surface + logo texture (both .cg-featured-bio-list [single]
   and .cg-featured-bio-carousel [multi]):
     --cg-featured-bio-list-bg              panel background (default light gray)
     --cg-featured-bio-list-padding         padding (mobile)
     --cg-featured-bio-list-padding-lg      padding (lg+)
     --cg-featured-bio-bg-image             texture image (default site logo)
     --cg-featured-bio-bg-inset-right       right inset of the texture box (default
                                            50% = box covers the LEFT half; the
                                            image's right edge meets the surface center)
     --cg-featured-bio-bg-position          texture background-position (default
                                            "right center" — anchors the image's right
                                            edge to the box's right edge)
     --cg-featured-bio-bg-size              texture background-size (default "auto 150%"
                                            — image height = 150% of the surface)
     --cg-featured-bio-bg-grayscale         0 = original color, 1 = full gray
     --cg-featured-bio-bg-opacity           texture opacity
     --cg-featured-bio-bg-blur              texture blur radius (default 0 = off)
   ============================================================ */
.component-featured-bio {
    --cg-featured-bio-gap:                   1.25rem;
    --cg-featured-bio-headshot-width:        10rem;
    --cg-featured-bio-headshot-aspect:       3 / 4;
    --cg-featured-bio-headshot-radius:       var(--bs-border-radius, 0.375rem);
    --cg-featured-bio-article-image-width:   18rem;
    --cg-featured-bio-name-size:             1.5em;  /* em so it scales off the surface font-size */

    /* List surface + logo background texture. Mirrors the
       .component-countdown::before pattern: the site logo (set on
       :root as --site-logo-url by the default-decorator) is painted
       as a soft grayscale texture behind the bio content. The texture
       box spans only the LEFT half of the surface (its right edge sits
       at the horizontal center via --cg-featured-bio-bg-inset-right);
       the logo is anchored to that right edge and bleeds off the left,
       vertically centered, at 150% of the surface height. Not blurred. */
    --cg-featured-bio-list-bg:               var(--bs-gray-bg, #e9ecef);
    --cg-featured-bio-list-padding:          1rem;
    --cg-featured-bio-list-padding-lg:       2rem;
    --cg-featured-bio-bg-image:              var(--site-logo-url, none);
    --cg-featured-bio-bg-inset-right:        50%;
    --cg-featured-bio-bg-position:           right center;
    --cg-featured-bio-bg-size:               auto 300%;
    --cg-featured-bio-bg-grayscale:          1;
    --cg-featured-bio-bg-opacity:            0.15;
    --cg-featured-bio-bg-blur:               10px;
}

.component-featured-bio .cg-featured-bio {
    gap: var(--cg-featured-bio-gap);
}

/* Bio surface — padded light-gray panel wrapping the bio card(s).
   Shared by the single-bio wrapper (.cg-featured-bio-list) and the
   multi-bio carousel wrapper (.cg-featured-bio-carousel) so the panel
   + logo texture read the same in either mode. position:relative +
   overflow:hidden + isolation:isolate scope and clip the layered
   texture pseudo below. */
.component-featured-bio .cg-featured-bio-list,
.component-featured-bio .cg-featured-bio-carousel {
    position:         relative;
    overflow:         hidden;
    isolation:        isolate;
    padding:          var(--cg-featured-bio-list-padding);
}

/* Panel background lives on the single-bio surface only. The multi-bio
   carousel wrapper stays transparent so the section/body background shows
   through behind the slides. */
.component-featured-bio .cg-featured-bio-list {
    background-color: var(--cg-featured-bio-list-bg);
}

/* Featured (multi-bio) carousel — base text stays at the site default; only the
   player name is emphasized (--cg-featured-bio-name-size, 1.5em). Inherit keeps
   the surface from compounding the name size. */
.component-featured-bio .cg-featured-bio-carousel {
    font-size: inherit;
}

@media (min-width: 992px) {
    .component-featured-bio .cg-featured-bio-list,
    .component-featured-bio .cg-featured-bio-carousel {
        padding: var(--cg-featured-bio-list-padding-lg);
    }
}

/* Logo texture — deepest layer (z-index:-1 inside the isolated
   stacking context, so it paints over the panel background but
   under the bio content). Grayscaled and dimmed so the site logo
   reads as a soft texture; no blur by default.
   The pseudo box covers only the LEFT half of the surface — its
   right edge is pulled in to the horizontal center via the
   inset-right value (default 50%). The logo is anchored to that
   right edge (background-position: right center), so the image's
   right edge meets the surface's center and the remainder of the
   logo bleeds off the left edge, where the box clips it. */
.component-featured-bio .cg-featured-bio-list::before,
.component-featured-bio .cg-featured-bio-carousel::before {
    content:             "";
    position:            absolute;
    inset:               0 var(--cg-featured-bio-bg-inset-right) 0 0;
    background-image:    var(--cg-featured-bio-bg-image);
    background-repeat:   no-repeat;
    background-position: var(--cg-featured-bio-bg-position);
    background-size:     var(--cg-featured-bio-bg-size);
    filter:              grayscale(var(--cg-featured-bio-bg-grayscale))
                         blur(var(--cg-featured-bio-bg-blur));
    opacity:             var(--cg-featured-bio-bg-opacity);
    pointer-events:      none;
    z-index:             -1;
}

/* Body — vertically center its contents against the headshot column
   on sm+ (where the article is flex-row and the body stretches to the
   headshot height). On mobile (flex-column) this is a no-op. */
.component-featured-bio .cg-featured-bio-body {
    display:         flex;
    flex-direction:  column;
    justify-content: center;
    gap:             0.25rem;   /* row rhythm — child rows carry no bottom margins */
}

/* Headshot — fixed-width column on sm+, full width on mobile (the
   article switches to flex-column at <sm). Aspect-ratio reserves
   space so image-load doesn't shift the layout. */
/* Side-by-side (sm+) layout: the flex row defaults to align-items: stretch,
   which would stretch the image column to the body height and cancel its
   aspect-ratio. Pin ONLY the media columns to the top so the headshot keeps its
   1:1 box and the release/article image its 16:9 box. The body keeps the default
   stretch, so it grows to the row (headshot) height and its justify-content:
   center can balance the text vertically against the headshot. */
@media (min-width: 576px) {
    .component-featured-bio .cg-featured-bio-headshot,
    .component-featured-bio .cg-featured-bio-article-image {
        align-self: flex-start;
    }
}

.component-featured-bio .cg-featured-bio-headshot {
    position:      relative;
    width:         100%;
    aspect-ratio:  var(--cg-featured-bio-headshot-aspect);
    overflow:      hidden;
    border-radius: var(--cg-featured-bio-headshot-radius);
}

@media (min-width: 576px) {
    .component-featured-bio .cg-featured-bio-headshot {
        width: var(--cg-featured-bio-headshot-width);
    }
}

/* Blurred backdrop — the box now sizes the aspect ratio; the portrait is
   CONTAINED (full image, never cropped) over a blurred cover of itself, so a
   tall headshot fills the square box without losing the top/bottom. The box
   clips (overflow + radius), so the image and backdrop no longer carry their
   own radius. */
.component-featured-bio .cg-featured-bio-headshot-bg {
    position:            absolute;
    inset:               0;
    background-image:    var(--cg-bio-headshot-img);
    background-position: center;
    background-size:     cover;
    background-repeat:   no-repeat;
    filter:              blur(20px);
    transform:           scale(1.1);
}

.component-featured-bio .cg-featured-bio-headshot img {
    position:   absolute;
    inset:      0;
    width:      100%;
    height:     100%;
    object-fit: contain;
    display:    block;
    transition: transform 0.4s ease;
}

.component-featured-bio .cg-featured-bio-article-image img {
    transition: transform 0.4s ease;
}

/* Hover zoom — the sharp portrait / article thumb scales slightly inside its
   overflow:hidden box (matches .cg-card-image). The image only — never the
   blurred -bg backdrop. */
.component-featured-bio .cg-featured-bio:hover .cg-featured-bio-headshot img,
.component-featured-bio .cg-featured-bio:focus-within .cg-featured-bio-headshot img,
.component-featured-bio .cg-featured-bio:hover .cg-featured-bio-article-image img,
.component-featured-bio .cg-featured-bio:focus-within .cg-featured-bio-article-image img {
    transform: scale(1.05);
}

/* Article variant — a release/article AOTW entry (type="article"). The
   image is a 16:9 block (BS5 .ratio, which sizes the box and absolutely
   fills it with the <img>), in a wider column than the square bio
   headshot. Full width on mobile (the article switches to flex-column at
   <sm), fixed column on sm+. */
.component-featured-bio .cg-featured-bio-article-image {
    width: 100%;
}

@media (min-width: 576px) {
    .component-featured-bio .cg-featured-bio-article-image {
        width: var(--cg-featured-bio-article-image-width);
    }
}

.component-featured-bio .cg-featured-bio-article-image .ratio {
    border-radius: var(--cg-featured-bio-headshot-radius);
    overflow:      hidden;
}

/* Blurred backdrop — same image as a cover layer behind the CONTAINED article
   thumbnail (object-fit-contain in markup) so off-ratio art fills the 16:9
   box. First child of the .ratio frame (Bootstrap absolute-fills it), behind
   the image. scale() hides the blur-faded edge. The square bio HEADSHOT is
   intentionally left as a cover crop (faces frame better cropped). */
.component-featured-bio .cg-featured-bio-article-bg {
    background-image:    var(--cg-bio-article-img);
    background-position: center;
    background-size:     cover;
    background-repeat:   no-repeat;
    filter:              blur(20px);
    transform:           scale(1.1);
}

/* Article category label — matches the bio sport label weight so the
   sport/category reads consistently across both layouts. */
.component-featured-bio .cg-featured-bio-category {
    font-weight: 600;
}

/* Team line. Inter-row spacing comes from the body's `gap`, so no margins. */
.component-featured-bio .cg-featured-bio-team {
    line-height: 1.2;
}

/* Attribute block — stacks the jersey/class/position chip strip and the team
   logo in a left-aligned column. align-items:flex-start keeps each child
   shrunk to its content (the inline-flex chip strip would otherwise stretch
   to the full body width). */
.component-featured-bio .cg-featured-bio-attribute {
    display:        flex;
    flex-direction: column;
    align-items:    flex-start;
    gap:            0.5rem;
}

/* Team logo — bottom of the attribute block, height capped at 4rem. */
.component-featured-bio .cg-featured-bio-team-logo {
    max-height: 4rem;
    width:      auto;
    display:    block;
}

/* Player name — primary heading, fw-bold via markup. */
.component-featured-bio .cg-featured-bio-name {
    font-size:   var(--cg-featured-bio-name-size);
    line-height: 1.2;
}

/* Player name link — intentionally NO color / decoration override: it inherits
   the base `a` / `a:hover` styling (theme link color, darker on hover,
   underline). stretched-link still makes the whole card clickable. */

/* Sport / date eyebrow row colour comes from the markup's .text-body-secondary;
   no rule needed (spacing is the body's gap). */

/* Attribute meta row (jersey # / class / position) — a compact bordered chip
   strip, SHARED with the sidebar bio-card (.cg-bio-card-meta). One inline row
   framed in grey-md; cells collapse to their content with NO gap and a single
   divider line between each (the frame is the container border; each interior
   divider is a cell's leading border, so nothing doubles). Cells are
   transparent and inherit the text color, with minimal padding. The grey
   literal fallback keeps the border visible in themes that don't define
   --color-grey-mid. */
.component-featured-bio .cg-featured-bio-meta,
.component-bio-card .cg-bio-card-meta {
    display:     inline-flex;
    align-items: stretch;
    border:      1px solid var(--color-grey-mid, #cbcbcb);
}
.component-featured-bio .cg-featured-bio-meta > *,
.component-bio-card .cg-bio-card-meta > * {
    padding:          0.1rem 0.5rem;
    background-color: transparent;
    color:            inherit;
}
.component-featured-bio .cg-featured-bio-meta > * + *,
.component-bio-card .cg-bio-card-meta > * + * {
    border-inline-start: 1px solid var(--color-grey-mid, #cbcbcb);
}

.component-featured-bio .cg-featured-bio-summary {
    line-height: 1.5;
}

/* ============================================================
   SHARED — Carousel controls (.cg-carousel-control)
   Generic styling primitive for the prev/next arrow buttons and
   the optional play/pause toggle on every cg- carousel component
   (featured-bio, banner, and any future banner-style carousel).
   Pair this class with the BS5 control class
   (.carousel-control-prev / .carousel-control-next) on arrow
   buttons and with .cg-carousel-pause on the pause toggle. The
   visible glyph is a FontAwesome <span> inside the button.

   Predefined geometry — padding, margin, border-width, border-style,
   and border-radius are all in place at the resting state with
   border-color = transparent. To enable a visible border on every
   cg- carousel control theme-wide, override ONLY
   --cg-carousel-control-border-color (or border-color directly) in
   theme.css; no padding / sizing adjustment is needed because the
   transparent border was already reserving the layout box.

   Custom property hooks (override at :root in theme.css for one
   theme-wide control treatment, or scope to a component's wrapper
   for component-specific overrides):
     --cg-carousel-control-color          glyph color (resting)
     --cg-carousel-control-hover          glyph color (hover / focus)
     --cg-carousel-control-bg             background (resting)
     --cg-carousel-control-bg-hover       background (hover / focus)
     --cg-carousel-control-size           glyph font-size
     --cg-carousel-control-padding        padding shorthand
     --cg-carousel-control-margin         margin shorthand
     --cg-carousel-control-border-width   border thickness
     --cg-carousel-control-border-style   border style (solid, dashed, ...)
     --cg-carousel-control-border-color   border color
                                          (default transparent — set a real
                                          color to reveal the border without
                                          shifting layout)
     --cg-carousel-control-border-radius  border-radius
   ============================================================ */
:root {
    --cg-carousel-control-color:         var(--bs-secondary-color, #6c757d);
    --cg-carousel-control-hover:         var(--bs-body-color, #212529);
    --cg-carousel-control-bg:            transparent;
    --cg-carousel-control-bg-hover:      transparent;
    --cg-carousel-control-size:          1.25rem;
    --cg-carousel-control-padding:       0.5rem 0.75rem;
    --cg-carousel-control-margin:        0;
    --cg-carousel-control-border-width:  1px;
    --cg-carousel-control-border-style:  solid;
    --cg-carousel-control-border-color:  transparent;
    --cg-carousel-control-border-radius: 0;
}

.cg-carousel-control {
    /* BS5 resets — .carousel-control-prev / -next default to
       position:absolute, 15% width, full-height, opacity 0.5. The
       generic primitive is inline; the consuming layout (e.g.
       .cg-inc-header-carousel-controls / .cg-inc-footer-carousel-controls)
       positions it. */
    position:        static;
    width:           auto;
    opacity:         1;

    /* Predefined geometry — set --cg-carousel-control-border-color
       (or `border-color`) to a real value to reveal the border with
       no layout shift. */
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    padding:         var(--cg-carousel-control-padding);
    margin:          var(--cg-carousel-control-margin);
    border:          var(--cg-carousel-control-border-width)
                     var(--cg-carousel-control-border-style)
                     var(--cg-carousel-control-border-color);
    border-radius:   var(--cg-carousel-control-border-radius);
    background:      var(--cg-carousel-control-bg);
    color:           var(--cg-carousel-control-color);
    font-size:       var(--cg-carousel-control-size);
    line-height:     1;
    transition:      color .2s ease, background-color .2s ease, border-color .2s ease;
}

/* A mouse click focuses the arrow, and Bootstrap's .carousel-control-prev /
   -next :focus rule (white, 0.9 opacity) outranks the resting colours: white
   on a light ground once the pointer leaves (FED-23025). A click keeps the
   resting look; keyboard focus still takes the hover look below. */
.cg-carousel-control:focus {
    background: var(--cg-carousel-control-bg);
    color:      var(--cg-carousel-control-color);
    opacity:    1;
}

.cg-carousel-control:hover,
.cg-carousel-control:focus-visible {
    background: var(--cg-carousel-control-bg-hover);
    color:      var(--cg-carousel-control-hover);
}

/* Centered controls bar — generic placement for prev/next (+ optional
   pause) beneath a carousel. Components whose controls sit in a centered
   bar below the slides wrap their .cg-carousel-control buttons in this.
   (Note: component-headlines-card-template now defaults its controls
   into the footer/header bands, not this bar.) Tokens are var()-
   fallbacks (not self-declared on the element) so they can be overridden
   from any ancestor. */
.cg-carousel-controls {
    display:         flex;
    align-items:     center;
    justify-content: center;
    gap:             var(--cg-carousel-controls-gap, 0);
    margin-top:      var(--cg-carousel-controls-mt, 1rem);
}

/* ------------------------------------------------------------
   FEATURED BIO — Carousel mode
   Active only when the template renders 2+ bios. Single-bio
   output skips the carousel wrapper entirely (no chrome to style).

   The prev/next + pause controls are NOT styled here: they render in
   the shared component header band (.cg-inc-header-carousel-controls)
   or footer (.cg-inc-footer-carousel-controls) — see the
   .cg-carousel-control primitive and the header/footer control-cluster
   blocks above. This section owns only the carousel-item padding and
   the bar-indicator (dots) styling — dormant unless the template's
   `dots` param is reactivated (default is `false`); the rules stay
   here so flipping the flag back on Just Works. With the controls bar
   gone the dots render BS5-native, overlaid at the foot of the slide.

   Custom property hooks (override in theme.css):
     --cg-featured-bio-carousel-bar-width    indicator bar width
     --cg-featured-bio-carousel-bar-height   indicator bar height
     --cg-featured-bio-carousel-bar-radius   indicator bar corner radius
     --cg-featured-bio-carousel-bar-gap      gap between indicator bars
     --cg-featured-bio-carousel-bar-color    indicator bar color
   ------------------------------------------------------------ */
.cg-featured-bio-carousel {
    --cg-featured-bio-carousel-bar-width:     1.75rem;
    --cg-featured-bio-carousel-bar-height:    0.2rem;
    --cg-featured-bio-carousel-bar-radius:    0;
    --cg-featured-bio-carousel-bar-gap:       0.35rem;
    --cg-featured-bio-carousel-bar-color:     var(--bs-body-color, #212529);
}

/* Each slide's bio article fills the carousel-item box. */
.cg-featured-bio-carousel .carousel-item {
    /* Match the article's own padding so consecutive slides line up. */
    padding-block: 0.25rem;
}

/* Indicators (dots) — horizontal bars overlaid at the foot of the
   slide via BS5's native .carousel-indicators positioning. Dormant
   unless the template's `dots` param is reactivated (default `false`).
   Keep BS5's absolute bottom placement; only restyle the bars and the
   inter-bar gap (the flex gap replaces BS5's fixed button margins). */
.cg-featured-bio-carousel .carousel-indicators {
    margin-bottom: 0.5rem;
    gap:           var(--cg-featured-bio-carousel-bar-gap);
}

.cg-featured-bio-carousel .carousel-indicators [data-bs-target] {
    box-sizing:       border-box;
    width:            var(--cg-featured-bio-carousel-bar-width);
    height:           var(--cg-featured-bio-carousel-bar-height);
    margin:           0;
    border:           0;
    border-radius:    var(--cg-featured-bio-carousel-bar-radius);
    background-color: var(--cg-featured-bio-carousel-bar-color);
    opacity:          0.35;
    transition:       opacity .2s ease;
}

.cg-featured-bio-carousel .carousel-indicators .active {
    opacity: 1;
}

/* ============================================================
   COMPONENT: cg-league-news-rss
   "Around the League" — grid of news headlines aggregated from
   member-site RSS feeds (rendered by league-news-rss-template).
   Each card has the member-site logo on the left and a clamped
   headline link on the right; rendered as a 2-col table-like grid
   at md+ that drops to a single column on mobile.

   "Table" appearance — the thin shared border between every card
   is produced via the grid-background trick: the
   .cg-league-news-rss-grid container's background is painted with
   the border color, the grid uses gap:1px, and each card paints
   its own opaque background. The 1px gaps reveal the container's
   bg as the grid lines; the container's own border closes the
   outer edges. No :nth-child math needed, and the grid lines
   stay consistent at any column count.

   Custom property hooks (override in theme.css):
     --cg-league-news-rss-grid-color      border / grid-line color
     --cg-league-news-rss-card-bg         card background
     --cg-league-news-rss-card-padding    card inner padding
     --cg-league-news-rss-card-gap        gap between logo and body
     --cg-league-news-rss-logo-min-width  logo box min width (column align)
     --cg-league-news-rss-logo-max-width  logo box max width (≈48px)
     --cg-league-news-rss-logo-radius     logo border-radius
     --cg-league-news-rss-title-size      headline font-size
     --cg-league-news-rss-cols            grid columns at md+ (default 2)
   ============================================================ */
.component-league-news-rss {
    --cg-league-news-rss-grid-color:   var(--bs-gray-bg, #e9ecef);
    --cg-league-news-rss-card-bg:      #ffffff;
    --cg-league-news-rss-card-padding: 0.75rem 1rem;
    --cg-league-news-rss-card-gap:     0.875rem;
    --cg-league-news-rss-logo-min-width: 3rem; /* match max-width so the logo column is a fixed width and titles align */
    --cg-league-news-rss-logo-max-width: 3rem; /* ≈48px — keep in sync with .img-mh-3 on the <img> */
    --cg-league-news-rss-logo-radius:  0;
    --cg-league-news-rss-title-size:   0.95rem;
    --cg-league-news-rss-cols:         2;
}

/* Grid container — owns the table-like border appearance. */
.cg-league-news-rss-grid {
    display:               grid;
    grid-template-columns: 1fr;
    gap:                   1px;
    background-color:      var(--cg-league-news-rss-grid-color);
    border:                1px solid var(--cg-league-news-rss-grid-color);
}

@media (min-width: 768px) {
    .cg-league-news-rss-grid {
        grid-template-columns: repeat(var(--cg-league-news-rss-cols), minmax(0, 1fr));
    }
}

/* Each card — opaque background covers the grid color except in
   the 1px gaps, which read as the grid lines. */
.cg-league-news-rss-card {
    background-color: var(--cg-league-news-rss-card-bg);
    padding:          var(--cg-league-news-rss-card-padding);
    gap:              var(--cg-league-news-rss-card-gap);
}

/* Logo box — fixed-width column (min-width == max-width) so the
   logos render at a consistent width and every card's headline body
   starts at the same x-offset. The <img> carries the Bootstrap
   helpers .img-fluid (max-width:100%; height:auto) and .img-mh-3
   (max-height:3rem), so tall, wide, or square logos all scale down
   proportionally inside the box; the box itself holds its width
   regardless of the image's intrinsic ratio. */
.cg-league-news-rss-logo {
    min-width: var(--cg-league-news-rss-logo-min-width);
    max-width: var(--cg-league-news-rss-logo-max-width);
    text-align: center;
}

.cg-league-news-rss-logo img {
    border-radius: var(--cg-league-news-rss-logo-radius);
}

/* Headline — line-clamped via the .clamp-N utility (global-reset.css)
   set in the markup based on the titleLines param. */
.cg-league-news-rss-title {
    font-size: var(--cg-league-news-rss-title-size);
}

/* Title-link hover/focus surface — the link is .stretched-link,
   so the visible underline cue is the only affordance for the
   otherwise-transparent click area. */
.cg-league-news-rss-link:hover,
.cg-league-news-rss-link:focus-visible {
    text-decoration: underline;
}


/* ============================================================
   COMPONENT: cg-component-navbar-list (Quick Links)
   Vertical list of links from a text navbar; bracket-key labels get an
   FA icon. Tokens override in theme.css.
   ============================================================ */
.component-navbar-list {
    --cg-navbar-list-padding-y:        0.6rem;
    --cg-navbar-list-divider:          var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    --cg-navbar-list-link-color:       var(--body-link, var(--bs-link-color));
    --cg-navbar-list-link-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    /* Default the icon to the anchor's own color (link color, and the hover
       color on hover) so the glyph always matches the link text. Override
       this token for a distinct icon color. */
    --cg-navbar-list-icon-color:       inherit;
}

.component-navbar-list .cg-navbar-list-item + .cg-navbar-list-item {
    border-top: var(--cg-navbar-list-divider);
}

.component-navbar-list .cg-navbar-list-link {
    display:         flex;
    align-items:     center;
    gap:             0.6rem;
    padding-block:   var(--cg-navbar-list-padding-y);
    color:           var(--cg-navbar-list-link-color);
    text-decoration: none;
    transition:      color .15s ease;
}

.component-navbar-list .cg-navbar-list-link:hover,
.component-navbar-list .cg-navbar-list-link:focus-visible {
    color:           var(--cg-navbar-list-link-color-hover);
    text-decoration: none;
}

.component-navbar-list .cg-navbar-list-icon {
    flex:       0 0 auto;
    width:      1.25em;
    text-align: center;
    color:      var(--cg-navbar-list-icon-color);
}


/* ============================================================
   COMPONENT: cg-component-navbar-inline (footer / utility links)
   Horizontal, wrapping row of text links from a text navbar; bracket-key
   labels get an FA icon. The inline counterpart of cg-component-navbar-list.
   The row's flex / wrap / alignment come from Bootstrap utilities in the
   markup (d-flex flex-wrap justify-content-*); this block only sets the
   inter-link gap + link styling. Links use the site link tokens
   (--bs-link-*); override the --cg-navbar-inline-* tokens per context (e.g.
   a theme paints them white inside a colored footer). Tokens override in
   theme.css.
   ============================================================ */
.component-navbar-inline {
    --cg-navbar-inline-gap:              0.5rem 1.5rem; /* row-gap column-gap */
    --cg-navbar-inline-link-color:       var(--body-link, var(--bs-link-color));
    --cg-navbar-inline-link-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-navbar-inline-icon-color:       inherit;
}

.component-navbar-inline .cg-navbar-inline {
    gap: var(--cg-navbar-inline-gap);
}

.component-navbar-inline .cg-navbar-inline-link {
    display:         inline-flex;
    align-items:     center;
    gap:             0.4rem;
    color:           var(--cg-navbar-inline-link-color);
    text-decoration: none;
    transition:      color .15s ease;
}

.component-navbar-inline .cg-navbar-inline-link:hover,
.component-navbar-inline .cg-navbar-inline-link:focus-visible {
    color:           var(--cg-navbar-inline-link-color-hover);
    text-decoration: underline;
}

.component-navbar-inline .cg-navbar-inline-icon {
    flex:  0 0 auto;
    color: var(--cg-navbar-inline-icon-color);
}


/* ============================================================
   COMPONENT: cg-component-navbar-social (inline social icons)
   Icon-only links from a TEXT navbar (bracket-key → FA glyph), laid out
   as a wrapping row of round icon buttons.
   ============================================================ */
.component-navbar-social {
    --cg-navbar-social-gap:         0rem;
    --cg-navbar-social-size:        2.5rem;
    --cg-navbar-social-icon-size:   1.1rem;
    --cg-navbar-social-color:       var(--body-link, var(--bs-link-color));
    --cg-navbar-social-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-navbar-social-bg:          transparent;
    --cg-navbar-social-bg-hover:    var(--bs-tertiary-bg, rgba(0, 0, 0, 0.06));
}

.component-navbar-social .cg-navbar-social-list {
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    gap:         var(--cg-navbar-social-gap);
}

.component-navbar-social .cg-navbar-social-link {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           var(--cg-navbar-social-size);
    height:          var(--cg-navbar-social-size);
    border-radius:   50%;
    color:           var(--cg-navbar-social-color);
    background:      var(--cg-navbar-social-bg);
    font-size:       var(--cg-navbar-social-icon-size);
    text-decoration: none;
    transition:      color .15s ease, background-color .15s ease;
}

.component-navbar-social .cg-navbar-social-link:hover,
.component-navbar-social .cg-navbar-social-link:focus-visible {
    color:      var(--cg-navbar-social-color-hover);
    background: var(--cg-navbar-social-bg-hover);
}

.component-navbar-social .cg-navbar-social-text {
    font-size:   0.9rem;
    padding-inline: 0.25rem;
}


/* ============================================================
   COMPONENT: cg-component-navbar-social-grid (larger boxes)
   Social links as bordered, clickable boxes in an auto-fit grid —
   presented like the members logo grid, each box a large icon + optional
   platform label. The seamless grid borders use the same top/left-on-list
   + right/bottom-on-item technique as .component-navbar-image.

   Each box is a 1:1 square (aspect-ratio) — never wider than tall — and the
   auto-fit floor is just large enough to hold the icon, so a short row of
   icons packs onto a single line instead of wrapping. Boxes grow to fill the
   row (1fr) and may grow TALLER than square if a label needs the room.

   Custom property hooks (override in theme.css):
     --cg-social-grid-min-width   auto-fit minmax floor (smallest box width)
     --cg-social-grid-padding     inner box padding
     --cg-social-grid-bg / -border / -icon-size / -gap
     --cg-social-grid-color / -color-hover / -bg-hover
   ============================================================ */
.component-navbar-social-grid {
    --cg-social-grid-min-width:   4rem;
    --cg-social-grid-padding:     1rem;
    --cg-social-grid-bg:          var(--bs-body-bg, #fff);
    --cg-social-grid-border:      var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    --cg-social-grid-icon-size:   1.5rem;
    --cg-social-grid-gap:         0.5rem;
    --cg-social-grid-color:       var(--bs-body-color, #212529);
    --cg-social-grid-color-hover: var(--body-link-hover, var(--bs-link-hover-color));
    --cg-social-grid-bg-hover:    var(--color-grey-mid, rgba(0, 0, 0, 0.04));
}

/* Larger icon glyph on lg+ screens (default 1.5rem → 2rem). */
@media (min-width: 992px) {
    .component-navbar-social-grid {
        --cg-social-grid-icon-size: 2rem;
    }
}

.component-navbar-social-grid .cg-social-grid-list {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-social-grid-min-width), 1fr));
    border-top:            var(--cg-social-grid-border);
    border-left:           var(--cg-social-grid-border);
}

.component-navbar-social-grid .cg-social-grid-item {
    border-right:  var(--cg-social-grid-border);
    border-bottom: var(--cg-social-grid-border);
}

.component-navbar-social-grid .cg-social-grid-link {
    display:         flex;
    flex-direction:  column;
    align-items:     center;
    justify-content: center;
    gap:             var(--cg-social-grid-gap);
    width:           100%;
    aspect-ratio:    1 / 1;      /* 1:1 square block; grows taller if content needs it */
    padding:         var(--cg-social-grid-padding);
    color:           var(--cg-social-grid-color);
    background:      var(--cg-social-grid-bg);
    text-align:      center;
    text-decoration: none;
    transition:      color .15s ease, background-color .15s ease;
}

.component-navbar-social-grid .cg-social-grid-link:hover,
.component-navbar-social-grid .cg-social-grid-link:focus-visible {
    color:           var(--cg-social-grid-color-hover);
    background:      var(--cg-social-grid-bg-hover);
    text-decoration: none;
}

.component-navbar-social-grid .cg-social-grid-icon {
    font-size:   var(--cg-social-grid-icon-size);
    line-height: 1;
}

.component-navbar-social-grid .cg-social-grid-label {
    font-size:   0.85rem;
    font-weight: 600;
}

/* showImage -- a photo box: the entry's image fills the box (cover), a
   scrim rises from the foot, and the icon + name sit left-aligned on a bar
   along the foot, in the box's photo ink. The photo is decorative; the
   link keeps the platform name. Hooks (override in theme.css):
     --cg-social-grid-image-bg      ground behind the photo while it loads
     --cg-social-grid-image-scrim   the gradient laid over the photo
     --cg-social-grid-image-color   ink on the bar (clears the scrim)
     --cg-social-grid-bar-height    the foot bar's height
     --cg-social-grid-bar-padding   the foot bar's inset
     --cg-social-grid-bar-gap       icon-to-name gap
   Hover / focus: the photo eases in and the name underlines (the link
   stays discernible without a colour change on a photo). */
.component-navbar-social-grid {
    --cg-social-grid-image-bg:    var(--bs-gray-800, #343a40);
    --cg-social-grid-image-scrim: linear-gradient(to bottom, transparent 40%, rgba(0, 0, 0, 0.8) 90%);
    --cg-social-grid-image-color: #fff;
    --cg-social-grid-bar-height:  3.5rem;
    --cg-social-grid-bar-padding: 0 1rem;
    --cg-social-grid-bar-gap:     0.75rem;
}
.component-navbar-social-grid .cg-social-grid-link--image {
    position:        relative;
    isolation:       isolate;
    overflow:        hidden;
    justify-content: flex-end;
    align-items:     stretch;
    padding:         0;
    color:           var(--cg-social-grid-image-color);
    background:      var(--cg-social-grid-image-bg);
}
.component-navbar-social-grid .cg-social-grid-image {
    position:   absolute;
    inset:      0;
    z-index:    -2;
    width:      100%;
    height:     100%;
    object-fit: cover;
    transition: transform 0.4s ease;
}
.component-navbar-social-grid .cg-social-grid-link--image::before {
    content:    "";
    position:   absolute;
    inset:      0;
    z-index:    -1;
    background: var(--cg-social-grid-image-scrim);
}
.component-navbar-social-grid .cg-social-grid-bar {
    display:     flex;
    align-items: center;
    gap:         var(--cg-social-grid-bar-gap);
    min-height:  var(--cg-social-grid-bar-height);
    padding:     var(--cg-social-grid-bar-padding);
    text-align:  start;
}
.component-navbar-social-grid .cg-social-grid-link--image:hover,
.component-navbar-social-grid .cg-social-grid-link--image:focus-visible {
    color:      var(--cg-social-grid-image-color);
    background: var(--cg-social-grid-image-bg);
}
.component-navbar-social-grid .cg-social-grid-link--image:hover .cg-social-grid-image,
.component-navbar-social-grid .cg-social-grid-link--image:focus-visible .cg-social-grid-image {
    transform: scale(1.05);
}
.component-navbar-social-grid .cg-social-grid-link--image:hover .cg-social-grid-label,
.component-navbar-social-grid .cg-social-grid-link--image:focus-visible .cg-social-grid-label {
    text-decoration: underline;
}
@media (prefers-reduced-motion: reduce) {
    .component-navbar-social-grid .cg-social-grid-image {
        transition: none;
    }
}


/* ============================================================
   COMPONENT: cg-component-bio-card (Athlete of the Week, etc.)
   Bio cards from headlines — photo + name (+ meta/date). direction=column
   (photo on top) or direction=row (photo beside the body). Cards lay out
   N-per-row via BS col spans on the grid; views chunk into a carousel
   when pageSize > showSize (see the template).

   Custom property hooks (override in theme.css):
     --cg-bio-card-gap              grid gutter / row gap
     --cg-bio-card-radius           photo corner radius
     --cg-bio-card-row-thumb-width  photo width in direction=row
     --cg-bio-card-name-color       name color
     --cg-bio-card-summary-color    leadin / summary text color
   ============================================================ */
.component-bio-card {
    --cg-bio-card-gap:             1rem;
    --cg-bio-card-radius:          var(--bs-border-radius, 0.375rem);
    --cg-bio-card-row-thumb-width: 35%;
    --cg-bio-card-name-color:      inherit;
    --cg-bio-card-summary-color:   var(--bs-secondary-color, #6c757d);
}

.component-bio-card .cg-bio-card-grid {
    --bs-gutter-x: var(--cg-bio-card-gap);
    --bs-gutter-y: var(--cg-bio-card-gap);
}

.component-bio-card .cg-bio-card {
    position: relative;
    height:   100%;
    /* background comes from the shared card-surface rule (--bs-card-bg) */
}

/* mobileShowSize -- the small-screen carousel's own prev / pause / next row,
   centred under the card inside the body. The buttons are the shared
   .cg-carousel-control primitive; a theme re-points its tokens here. */
.component-bio-card .cg-bio-card-controls {
    display:         flex;
    justify-content: center;
    align-items:     center;
    gap:             0.5rem;
    margin-top:      1rem;
}

/* Player name link — intentionally NO color / decoration override: it inherits
   the base `a` / `a:hover` styling (theme link color, darker on hover,
   underline). A `color:` here (0,2,0) would beat `a:hover` (0,1,1) and stop the
   hover from darkening. The shared card-surface bg + stretched-link still make
   the whole card clickable. */

.component-bio-card .cg-bio-card-image-wrap {
    overflow:      hidden;
    border-radius: var(--cg-bio-card-radius);
    background:    var(--bs-gray-200, #e9ecef);
}

/* Blurred backdrop — the same image as a cover layer behind the contained
   photo, so off-ratio art fills the box instead of leaving flat bars. Both
   this and the <img> are direct children of .ratio, so Bootstrap stretches
   them to fill; z-index keeps the sharp image on top. scale() hides the soft
   blur-faded edges that would otherwise reveal the box border. */
.component-bio-card .cg-bio-card-image-bg {
    background-image:    var(--cg-bio-card-img);
    background-position: center;
    background-size:     cover;
    background-repeat:   no-repeat;
    filter:              blur(20px);
    transform:           scale(1.1);
}

/* The sharp photo is CONTAINED — the FULL image is always shown, never cropped
   or trimmed, no matter how its source ratio differs from the frame. It fills
   the frame box and object-fit:contain letter/pillarboxes it to its own ratio;
   the blurred backdrop (the cover layer behind it) fills whatever the contained
   image leaves, so off-ratio art reads cleanly with no flat bars. Holds in
   every combo: a portrait bio in a 1:1 or 16:9 frame, a 16:9 headline in a 1:1
   row frame, and a row frame stretched taller than the photo by a taller body. */
.component-bio-card .cg-bio-card-image {
    z-index:    1;
    width:      100%;
    height:     100%;
    object-fit: contain;
    transition: transform 0.4s ease;
}

/* Hover zoom — the sharp photo scales slightly inside its overflow:hidden wrap
   (matches .cg-card-image). The image only — never the blurred -bg backdrop. */
.component-bio-card .cg-bio-card:hover .cg-bio-card-image,
.component-bio-card .cg-bio-card:focus-within .cg-bio-card-image {
    transform: scale(1.05);
}

.component-bio-card .cg-bio-card-body {
    padding-inline: 0;
}

.component-bio-card .cg-bio-card-name {
    color:       var(--cg-bio-card-name-color);
    line-height: 1.25;
}

.component-bio-card .cg-bio-card-summary {
    color:     var(--cg-bio-card-summary-color);
    font-size: 0.9rem;
}

/* Row direction — photo to the side of the body (the card element is the flex
   container). align-items:stretch lets the thumb column grow to the body's
   height; the wrap's `.ratio` class supplies a BASE height (= the photo's own
   content ratio), and stretch only ever makes it taller, never shorter. */
.component-bio-card .cg-bio-card--row {
    flex-direction: row;
    align-items:    stretch;
    gap:            var(--cg-bio-card-gap);
}

.component-bio-card .cg-bio-card--row .cg-bio-card-image-wrap {
    flex:  0 0 var(--cg-bio-card-row-thumb-width);
    width: var(--cg-bio-card-row-thumb-width);
}

/* Body stretches to the card (thumb) height via the row's align-items:stretch;
   center its content vertically so the text reads balanced against the side
   photo rather than top-aligned beside a taller thumbnail. */
.component-bio-card .cg-bio-card--row .cg-bio-card-body {
    flex:            1 1 auto;
    display:         flex;
    flex-direction:  column;
    justify-content: center;
}


/* ============================================================
   COMPONENT: cg-component-stats-leaders
   Stat leaders rendered by component-stats-leaders-template — a responsive
   grid of leader cards, one per stat category. Each card: a stat-label header,
   a ranked list of top players (logo + name + team + value), and a "View All"
   link to the full sorted players list. Position groups (e.g. Hitting /
   Pitching) render as labeled bands. Typically hosted inside
   component-switcher-select-template (the sport dropdown).

   Custom property hooks (override in theme.css):
     --cg-leaders-gap            grid gap between cards
     --cg-leaders-card-min       min card width before the grid wraps
     --cg-leaders-card-bg        card background
     --cg-leaders-card-border    card border
     --cg-leaders-card-radius    card corner radius
     --cg-leaders-head-bg        stat-label header background
     --cg-leaders-head-color     stat-label header text color
     --cg-leaders-value-color    stat-value color
     --cg-leaders-logo-size      team-logo box size
     --cg-leaders-lead-logo-h    first record (leader) logo height
     --cg-leaders-lead-value-size  first record value font-size (vs row text)
   ============================================================ */
.component-stats-leaders {
    --cg-leaders-gap:         1rem;
    --cg-leaders-card-min:    16rem;
    --cg-leaders-card-bg:     var(--bs-body-bg, #fff);
    --cg-leaders-card-border: var(--bs-border-width) solid var(--bs-border-color);
    --cg-leaders-card-radius: var(--bs-border-radius, 0.375rem);
    --cg-leaders-head-bg:     var(--bs-gray-200, #e9ecef);
    --cg-leaders-head-color:  var(--bs-emphasis-color, var(--bs-body-color));
    --cg-leaders-value-color: var(--bs-emphasis-color, var(--bs-body-color));
    --cg-leaders-logo-size:   1.75rem;
    --cg-leaders-lead-logo-h:     4rem;   /* first record (leader) logo height */
    --cg-leaders-lead-value-size: 1.25em;  /* first record value — 1.5x the row text */
    --cg-leaders-row-hover-bg: rgba(0, 0, 0, 0.075);   /* matches Bootstrap .table-hover (standings) */
}

/* Group switcher — a full-width <select> that swaps the position-group panes
   (Hitting / Pitching, …). Block-level so it spans the whole component width;
   reuses the shared .cg-switcher-select behavior (switcher-select.js). */
.component-stats-leaders .cg-leaders-switcher {
    margin-bottom: 1rem;
}
.component-stats-leaders .cg-leaders-select {
    display: block;
    width:   100%;
}

/* Card grid — fits as many --cg-leaders-card-min columns as the width allows,
   the cards stretching to share the row; columns collapse many → 1 as the
   viewport narrows (auto-fit, matching the other cg- card carousels' responsive
   scale-down). Drives both the plain grid and each carousel slide. */
.component-stats-leaders .cg-leaders-grid {
    display:               grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--cg-leaders-card-min), 1fr));
    gap:                   var(--cg-leaders-gap);
}

.component-stats-leaders .cg-leaders-card {
    display:         flex;
    flex-direction:  column;
    background:      var(--cg-leaders-card-bg);
    border:          var(--cg-leaders-card-border);
    border-radius:   var(--cg-leaders-card-radius);
    overflow:        hidden;
}

.component-stats-leaders .cg-leaders-card-head {
    padding:     0.5rem 0.75rem;
    background:   var(--cg-leaders-head-bg);
    color:       var(--cg-leaders-head-color);
    font-weight: 700;
}

.component-stats-leaders .cg-leaders-list {
    list-style: none;
    margin:     0;
    padding:    0;
    flex:       1 1 auto;
    /* Flex column, rows top-aligned. The grid stretches cards to equal height
       and the list grows to fill, but the rows should sit at the top of that
       space (a shorter card just leaves room below) rather than float centered. */
    display:         flex;
    flex-direction:  column;
    justify-content: flex-start;
}

.component-stats-leaders .cg-leaders-row {
    position:    relative;          /* anchor for the player name's stretched-link */
    display:     flex;
    align-items: center;
    gap:         0.5rem;
    padding:     0.5rem 0.75rem;
}
.component-stats-leaders .cg-leaders-row + .cg-leaders-row {
    border-top: var(--bs-border-width) solid var(--bs-border-color);
}
/* Whole-row hover — light gray wash matching the standings table's
   Bootstrap .table-hover treatment (the entire row is the click target). */
.component-stats-leaders .cg-leaders-row:hover {
    background-color: var(--cg-leaders-row-hover-bg);
}

.component-stats-leaders .cg-leaders-player {
    flex:        1 1 auto;
    min-width:   0;          /* allow the name/team to ellipsis instead of overflow */
    display:     flex;
    align-items: center;
    gap:         0.5rem;
}

/* id + value wrapper — shared by every row. Normal rows: one line, name on the
   left, value pushed to the right (the value keeps its margin-left:auto). The
   first record overrides this to a left-aligned column so the two stack. */
.component-stats-leaders .cg-leaders-text {
    flex:        1 1 auto;
    min-width:   0;
    display:     flex;
    align-items: center;
    gap:         0.5rem;
}

.component-stats-leaders .cg-leaders-logo {
    flex:       0 0 var(--cg-leaders-logo-size);
    width:      var(--cg-leaders-logo-size);
    height:     var(--cg-leaders-logo-size);
    object-fit: contain;
}

.component-stats-leaders .cg-leaders-id {
    min-width: 0;
}
.component-stats-leaders .cg-leaders-name {
    display:         block;
    font-weight:     600;
    text-decoration: none;
    white-space:     nowrap;
    overflow:        hidden;
    text-overflow:   ellipsis;
}
.component-stats-leaders .cg-leaders-team {
    display:       block;
    font-size:     0.8rem;
    color:         var(--bs-secondary-color, #6c757d);
    text-decoration: none;
    white-space:   nowrap;
    overflow:      hidden;
    text-overflow: ellipsis;
}

.component-stats-leaders .cg-leaders-value {
    flex:                 0 0 auto;
    margin-left:          auto;
    font-variant-numeric: tabular-nums;   /* align digits across rows; weight/size stay the theme default */
    color:                var(--cg-leaders-value-color);
}

/* First record (the leader) — pronounced two-column treatment: the team logo
   sits in the left, taller (--cg-leaders-lead-logo-h), while the name/team and
   an enlarged bold value stack left-aligned beside it. The shared
   .cg-leaders-text wrapper switches to a column here; the player stays a flex
   row so the logo is centered against the stack. */
.component-stats-leaders .cg-leaders-list > .cg-leaders-row:first-child .cg-leaders-logo {
    flex:   0 0 auto;
    width:  auto;
    height: var(--cg-leaders-lead-logo-h);
}
.component-stats-leaders .cg-leaders-list > .cg-leaders-row:first-child .cg-leaders-text {
    flex-direction: column;
    align-items:    flex-start;
    gap:            0.1rem;
}
.component-stats-leaders .cg-leaders-list > .cg-leaders-row:first-child .cg-leaders-value {
    margin-left: 0;            /* override the base right-pushing margin */
    font-size:   var(--cg-leaders-lead-value-size);
    font-weight: 700;
    line-height: 1.1;
}

.component-stats-leaders .cg-leaders-more {
    display:         block;
    margin-top:      auto;          /* pin to the card foot when lists are short */
    padding:         0.5rem 0.75rem;
    border-top:      var(--bs-border-width) solid var(--bs-border-color);
    text-align:      center;
    font-weight:     600;
    text-decoration: none;
}

.component-stats-leaders .cg-leaders-empty {
    text-align: center;
    padding:    1rem;
}

/* Carousel layout (component-stats-leaders-carousel-template) — each
   position group's cards page through a Bootstrap-5 carousel; the grid inside
   every .carousel-item reuses .cg-leaders-grid above. Space it from the
   per-group footer (archive + prev/next) that follows. */
.component-stats-leaders.layout-carousel .cg-leaders-carousel {
    margin-bottom: 1rem;
}


/* ============================================================
   cg-layout-guide — super-user "component not powered" placeholder.
   Emitted by a layout-core's #implementationGuide macro (e.g. Reign) in the slot where
   an unpowered component would render. Each is a stock Bootstrap
   .alert.alert-warning naming the poweredby file an admin must create — so no
   custom skin is needed. Super-user only; nothing renders for a populated
   component or for normal visitors.

   Visibility tracks the page's wiki annotations: the CMS "Wiki: Show / Hide"
   control (generic/dropoff-scripts) toggles an `html.wiki-hidden` class, so the
   guides hide right along with every [edit]/[customize]/[params] link — no
   separate toggle. (The control only .hide()s anchor wiki-links, hence the
   class hook rather than rel="wiki-link".)
   ============================================================ */
.cg-layout-guide {
    display: block;
}
.wiki-hidden .cg-layout-guide {
    display: none;
}


/* ============================================================
   wiki-component-modal — superuser doc modal.
   A "[params]" wiki link (styled like the CMS [edit] /
   [customize] links via .code-link) opens a Bootstrap modal: a parameter
   table in code typography, an optional NOTES block after the table,
   and a Close button styled like the standard archive buttons.
   ============================================================ */

/* Trigger link — sits inline beside the CMS [edit]/[customize] wiki links and
   inherits their .code-link styling; this only guarantees a little spacing. */
.cg-wiki-link { margin-left: 0.25rem; }

/* Modal body typography — parameters / values render as inline code. */
.cg-wiki-modal .cg-wiki-intro { margin-bottom: 0.75rem; }
.cg-wiki-modal .cg-wiki-table th,
.cg-wiki-modal .cg-wiki-table td { vertical-align: top; }
.cg-wiki-modal .cg-wiki-table code,
.cg-wiki-modal .modal-title code {
    font-family: var(--bs-font-monospace, ui-monospace, "SFMono-Regular", monospace);
    font-size:   0.875em;
    color:       var(--bs-emphasis-color, var(--bs-body-color));
    background:  transparent;
    padding:     0;
}
.cg-wiki-modal .cg-wiki-legend {
    margin: 0;
    color:  var(--bs-secondary-color, #6c757d);
}

/* NOTES block — rendered after the table. */
.cg-wiki-notes {
    margin-top:  1rem;
    padding:     0.75rem 1rem;
    border-left: 3px solid var(--bs-border-color, #dee2e6);
    background:  var(--bs-gray-100, #f8f9fa);
}
.cg-wiki-notes-title { margin: 0 0 0.25rem; }
.cg-wiki-notes-body  { margin: 0; }
.cg-wiki-notes-body code {
    color:      var(--bs-emphasis-color, var(--bs-body-color));
    background: transparent;
    padding:    0;
}

/* Close button — mirrors the standard .cg-inc-footer-archive treatment. */
.cg-wiki-close {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    padding:         0.5rem 1.25rem;
    color:           var(--body-link, var(--bs-link-color));
    background:      transparent;
    border:          var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);
    border-radius:   var(--bs-border-radius, 0.375rem);
    font-weight:     600;
    text-decoration: none;
    cursor:          pointer;
    transition:      color .2s ease, background-color .2s ease, border-color .2s ease;
}
.cg-wiki-close:hover,
.cg-wiki-close:focus-visible {
    color:      var(--body-link-hover, var(--bs-link-hover-color));
    background: var(--bs-tertiary-bg, rgba(0, 0, 0, 0.03));
}

