/* ═══════════════════════════════════════════════════════════════════════════
   WEBSITE RENDER - Layout Width Model + Block Breakout
   Loaded on both public website pages and admin editor previews.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Layout Width Model ───────────────────────────────────────────────────
   Themeable custom properties + utility classes for section bleed,
   inner content width, and content padding.
   ──────────────────────────────────────────────────────────────────────── */

:root {
	--rosterli-content-max-width: 1140px;
	--rosterli-content-narrow-max-width: 720px;
	--rosterli-padding-compact: 1rem;
	--rosterli-padding-default: 2.5rem;
	--rosterli-padding-spacious: 5rem;
}

/* Section bleed - applied to the outer .rosterli-layout wrapper.
   On the public site, `<body>` has no max-width, and every section
   (page layouts and the site `<header>`/`<footer>`) is a body-level
   block, so the parent is already viewport-width. `width:100%` is
   therefore equivalent to "full-bleed" visually, *without* the
   scrollbar-overshoot bug that the earlier `100vw + margin-inline:
   calc(-50vw + 50%)` recipe produced whenever the page had a vertical
   scrollbar (100vw is wider than clientWidth by the scrollbar's
   width - typically 15px - and showed up as a horizontal scrollbar).
   The old recipe also fought Bootstrap utility classes emitted by
   `compute_style_classes_and_styles()` like `m-0` (`margin: 0
   !important`), which neutralized the negative margin-inline and
   pinned the 100vw box at left:0 - full 15px overshoot on the right.
   `max-width:100%` guards against any descendant trying to widen the
   wrapper past its parent. */
.rosterli-section--contained {
	max-width: 100%;
}
.rosterli-section--full-bleed {
	width: 100%;
	max-width: 100%;
}

/* Video / random-video backgrounds carry an absolutely-positioned
   video container (`position:absolute; inset:0;`). `overflow-x: clip`
   prevents any minor overshoot from that child from leaking out, while
   not creating a scroll container (so descendants with `position:
   sticky` continue to work). */
.rosterli-layout.rosterli-bg-video {
	overflow-x: clip;
}

/* Content width - applied to the inner container div within .rosterli-layout */
.rosterli-content--contained {
	max-width: var(--rosterli-content-max-width);
	margin-inline: auto;
	width: 100%;
	padding-inline: var(--bs-gutter-x, 0.75rem);
}
.rosterli-content--narrow {
	max-width: var(--rosterli-content-narrow-max-width);
	margin-inline: auto;
	width: 100%;
	padding-inline: var(--bs-gutter-x, 0.75rem);
}
.rosterli-content--full {
	width: 100%;
}
.rosterli-content--inherit {
	width: 100%;
}

/* Content padding - vertical spacing applied to the outer .rosterli-layout wrapper */
.rosterli-padding--none {
	padding-block: 0;
}
.rosterli-padding--compact {
	padding-block: var(--rosterli-padding-compact);
}
.rosterli-padding--default {
	padding-block: var(--rosterli-padding-default);
}
.rosterli-padding--spacious {
	padding-block: var(--rosterli-padding-spacious);
}

/* ── Split Content Breakout ───────────────────────────────────────────────
   Block-level breakout behavior for the Split Content block.
   The outer wrapper controls overflow/width; the inner row holds the
   two-column media + text structure.
   ──────────────────────────────────────────────────────────────────────── */

.rosterli-split-breakout {
	position: relative;
}

.rosterli-split-breakout--none {
	/* Normal: stays inside the layout's resolved inner width */
}

.rosterli-split-breakout--media-left {
	width: 100vw;
	margin-inline-start: calc(-50vw + 50%);
	margin-inline-end: 0;
}

.rosterli-split-breakout--media-right {
	width: 100vw;
	margin-inline-start: 0;
	margin-inline-end: calc(-50vw + 50%);
}

.rosterli-split-breakout--full {
	width: 100vw;
	margin-inline: calc(-50vw + 50%);
}

/* Text column width constraint within a breakout */
.rosterli-split-text--inherit {
	/* Inherits the column width from the grid - no constraint */
}

.rosterli-split-text--contained {
	max-width: var(--rosterli-content-max-width);
}

.rosterli-split-text--narrow {
	max-width: var(--rosterli-content-narrow-max-width);
}

.rosterli-split-text--full {
	/* No constraint - fills available column space */
}

/*
 * Grid-aligned text inside directional breakout.
 *
 * When the breakout wrapper is 100vw, each Bootstrap column is 50vw.
 * The viewport-facing edge of the text column extends beyond the site
 * grid. Padding on that edge pushes the text inward so it aligns to
 * the contained (or narrow) grid boundary.
 *
 * calc(50vw - <grid-half-width>) = distance from the grid edge to
 * the viewport edge on the text side.
 */
.rosterli-split-breakout--media-left .rosterli-split-text--contained {
	max-width: none;
	padding-inline-end: max(var(--bs-gutter-x, 0.75rem), calc(50vw - var(--rosterli-content-max-width) / 2));
}
.rosterli-split-breakout--media-left .rosterli-split-text--narrow {
	max-width: none;
	padding-inline-end: max(var(--bs-gutter-x, 0.75rem), calc(50vw - var(--rosterli-content-narrow-max-width) / 2));
}

.rosterli-split-breakout--media-right .rosterli-split-text--contained {
	max-width: none;
	padding-inline-start: max(var(--bs-gutter-x, 0.75rem), calc(50vw - var(--rosterli-content-max-width) / 2));
}
.rosterli-split-breakout--media-right .rosterli-split-text--narrow {
	max-width: none;
	padding-inline-start: max(var(--bs-gutter-x, 0.75rem), calc(50vw - var(--rosterli-content-narrow-max-width) / 2));
}

/* ── Carousel block ───────────────────────────────────────────────────────
   Public + admin-preview rendering for the carousel block. These rules used to
   live only in admin_app/assets/css/signed_in.css, so the public site (which
   loads this shared sheet, not signed_in.css) had no object-fit on the slide
   image: the <img> carries `d-block w-100` plus an inline fixed height, so with
   no object-fit it stretched/squashed to that box. object-fit:cover crops to the
   fixed height while preserving the source aspect ratio (never distorts).
   ──────────────────────────────────────────────────────────────────────── */

.rosterli-carousel-slide-img {
	object-fit: cover;
	object-position: center;
}

/* Whole-image overlay layer. Positioning/stacking only - the background-color
   is set inline from a server-validated 0-100 opacity. pointer-events:none so
   it never blocks the caption's links/buttons (which sit above it at z-index 2). */
.rosterli-carousel-overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
}

.rosterli-carousel-caption {
	z-index: 2;
}

/* Content-only overlay: the validated background/padding/radius live inline on
   this container, which hugs the slide content (inline-block) and stays centered
   inside the caption. Without an overlay it is an inert transparent wrapper. */
.rosterli-carousel-content--overlay {
	display: inline-block;
	max-width: 100%;
}

/* Trim the trailing margin of the last rich-text block so the overlay box (or
   caption) doesn't carry extra space under the final paragraph/heading. */
.rosterli-carousel-richtext > :last-child {
	margin-bottom: 0;
}

/* Image Width = Full Bleed: the whole .carousel breaks out to viewport width
   using the same recipe as the Split Content block's full breakout
   (.rosterli-split-breakout--full above), so slide images reach the browser
   edges with no side gutter. The carousel is position:relative, so the
   absolutely-positioned controls/caption stay anchored to this 100vw box. */
.rosterli-carousel-breakout {
	width: 100vw;
	margin-inline: calc(-50vw + 50%);
	max-width: 100vw;
}

/* Content Width = Contained (only meaningful while the image is full bleed):
   pull the overlay controls back to the centered content width so captions,
   buttons, and prev/next arrows stay aligned to the page grid even though the
   image bleeds edge-to-edge. max(0px, …) collapses the inset at and below the
   content breakpoint - where "contained" already equals full width - so mobile
   keeps controls at the edges and never introduces horizontal scroll. */
/* The constrained content widths share one inset recipe, parameterized by
   --rosterli-carousel-content-max: "contained" reuses the page container width
   (byte-identical to before), "narrow" ≈ 960px, "focused" reuses the existing
   narrow token (720px). "full_bleed" content sets no class and keeps Bootstrap's
   defaults. The max(…) guards collapse the inset below each width so mobile keeps
   controls at the edges and never introduces horizontal scroll. */
.rosterli-carousel--content-contained { --rosterli-carousel-content-max: var(--rosterli-content-max-width); }
.rosterli-carousel--content-narrow    { --rosterli-carousel-content-max: 960px; }
.rosterli-carousel--content-focused   { --rosterli-carousel-content-max: var(--rosterli-content-narrow-max-width); }

.rosterli-carousel--content-contained .carousel-control-prev,
.rosterli-carousel--content-narrow .carousel-control-prev,
.rosterli-carousel--content-focused .carousel-control-prev {
	left: max(0px, calc(50% - var(--rosterli-carousel-content-max) / 2));
}
.rosterli-carousel--content-contained .carousel-control-next,
.rosterli-carousel--content-narrow .carousel-control-next,
.rosterli-carousel--content-focused .carousel-control-next {
	right: max(0px, calc(50% - var(--rosterli-carousel-content-max) / 2));
}
.rosterli-carousel--content-contained .carousel-caption,
.rosterli-carousel--content-narrow .carousel-caption,
.rosterli-carousel--content-focused .carousel-caption {
	left: 50%;
	right: auto;
	transform: translateX(-50%);
	width: min(90%, var(--rosterli-carousel-content-max));
}
.rosterli-carousel--content-contained .carousel-indicators,
.rosterli-carousel--content-narrow .carousel-indicators,
.rosterli-carousel--content-focused .carousel-indicators {
	margin-inline: max(15%, calc(50% - var(--rosterli-carousel-content-max) / 2));
}

/* ── Timeline block ───────────────────────────────────────────────────────
   Public + admin-preview rendering for the timeline block. Like the carousel
   rules above, these used to live only in admin_app/assets/css/signed_in.css,
   so the public site (which loads this shared sheet, not signed_in.css) emitted
   the connector markup from shared/views/blocks/timeline.cfm but had no styles
   to size/colour it: the vertical/horizontal line <div>s collapsed to zero and
   the dots lost their fixed dimensions, so connectors were invisible publicly
   even with "Show Connectors" enabled. Keep in sync with signed_in.css. */

/* Timeline: shared */
.rosterli-timeline-dot {
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: var(--bs-primary);
	flex-shrink: 0;
}

.rosterli-timeline-dot--connector {
	z-index: 1;
	flex-shrink: 0;
	margin-top: 4px;
}

.rosterli-timeline-dot--h {
	width: 14px;
	height: 14px;
}

.rosterli-timeline-img {
	max-width: 100%;
	height: auto;
	max-height: 220px;
	object-fit: contain;
}

/* Milestone description is limited WYSIWYG HTML wrapped in .rosterli-timeline__desc.
   Trim the trailing margin of its last block so a single-paragraph description
   keeps the tight spacing the old plain-text `<p class="mb-0">` had. */
.rosterli-timeline__desc > :last-child {
	margin-bottom: 0;
}

/* Timeline: vertical */
.rosterli-timeline__line {
	width: 3px;
	background: var(--bs-primary-bg-subtle, #dee2e6);
}

.rosterli-timeline__line--left {
	left: 18px;
}

.rosterli-timeline__connector-column {
	width: 36px;
}

/* Timeline: horizontal */
.rosterli-timeline--horizontal .rosterli-timeline__scroll-container {
	overflow-x: auto;
	overflow-y: visible;
	padding-bottom: 0.5rem;
	scroll-snap-type: x mandatory;
}

.rosterli-timeline__h-track {
	display: flex;
	align-items: center;
	padding: 0 130px;
	margin-bottom: 0.75rem;
}

.rosterli-timeline__h-track-cell {
	display: flex;
	align-items: center;
	flex: 1 1 0;
	justify-content: center;
	min-width: 0;
}

.rosterli-timeline__h-track-cell:first-child .rosterli-timeline__h-line:first-child,
.rosterli-timeline__h-track-cell:last-child .rosterli-timeline__h-line:last-child {
	visibility: hidden;
}

.rosterli-timeline__h-line {
	flex: 1 1 0;
	height: 3px;
	background: var(--bs-primary-bg-subtle, #dee2e6);
	min-width: 12px;
}

.rosterli-timeline__h-cards {
	display: flex;
	gap: 1rem;
}

.rosterli-timeline__horizontal-card {
	width: 260px;
	min-width: 220px;
	flex-shrink: 0;
	scroll-snap-align: start;
}

/* ── Hero Banner Content Width ────────────────────────────────────────────
   Block-level content width for the Hero Banner block.
   Applied to the .rosterli-hero-banner__content wrapper inside the hero flex
   container. Reuses the shared width custom properties.
   ──────────────────────────────────────────────────────────────────────── */

.rosterli-hero-content--contained {
	max-width: var(--rosterli-content-max-width);
	margin-inline: auto;
}

.rosterli-hero-content--narrow {
	max-width: var(--rosterli-content-narrow-max-width);
	margin-inline: auto;
}

/* ── Contact Information Block Icons ─────────────────────────────────────
   Icons inherit the block's text color so they remain visible on both
   light and dark backgrounds.  Opacity provides the subdued look that
   Bootstrap's text-muted would give on a light background.
   ──────────────────────────────────────────────────────────────────────── */

.rosterli-contact-block__icon {
	color: inherit;
	opacity: 0.65;
}

/* ── Block-level Font Size Scale ─────────────────────────────────────────
   Applied to the .rosterli-block envelope by website_renderer_service when
   style.typography.font_size is set to a non-md value. The wrapper-level
   font-size cascades to text content inside the block; Bootstrap children
   that hard-code their own font-size (accordion-button, nav-link, table)
   are forced to inherit so the cascade actually reaches them. Default md
   intentionally emits no class so existing blocks render unchanged.
   ──────────────────────────────────────────────────────────────────────── */

.rosterli-block.rosterli-block-font-size-xs  { font-size: 0.8rem; }
.rosterli-block.rosterli-block-font-size-sm  { font-size: 0.9rem; }
.rosterli-block.rosterli-block-font-size-lg  { font-size: 1.15rem; }
.rosterli-block.rosterli-block-font-size-xl  { font-size: 1.3rem; }
.rosterli-block.rosterli-block-font-size-xxl { font-size: 1.5rem; }

/* Rich-text inline typography classes. Emitted by the TipTap custom
   Paragraph node when the editor format dropdown picks Small or Fine print.
   Both are em-based so they scale with the parent block font-size. */
.rosterli-text-small      { font-size: 0.875em; }
.rosterli-text-fine-print { font-size: 0.75em; color: var(--bs-secondary-color, #6c757d); }

/* Inline text-size classes. Emitted by the TipTap RosterliTextSize mark when an
   author sizes only part of a paragraph/heading/list item/table cell. Distinct
   from the block-level .rosterli-text-small above: these apply to an inline
   <span> and only affect font-size (em-based so they scale with the parent
   typography). No colour, weight, family, margin, or line-height changes. */
.rosterli-text-size-small   { font-size: 0.875em; }
.rosterli-text-size-large   { font-size: 1.25em; }
.rosterli-text-size-x-large { font-size: 1.5em; }

.rosterli-block[class*="rosterli-block-font-size-"] .accordion-button,
.rosterli-block[class*="rosterli-block-font-size-"] .accordion-body,
.rosterli-block[class*="rosterli-block-font-size-"] .nav-link,
.rosterli-block[class*="rosterli-block-font-size-"] .navbar-nav .nav-link,
.rosterli-block[class*="rosterli-block-font-size-"] table,
.rosterli-block[class*="rosterli-block-font-size-"] .table {
	font-size: inherit;
}

/* ── Gallery block lightbox ─────────────────────────────────────────── */
.rosterli-gallery-lightbox-trigger { cursor: zoom-in; }
.rosterli-gallery-lightbox-content { background-color: rgba(0, 0, 0, 0.92); }
.rosterli-gallery-lightbox .modal-body { overflow: hidden; }
.rosterli-gallery-lightbox .carousel,
.rosterli-gallery-lightbox .carousel-inner,
.rosterli-gallery-lightbox .carousel-item { height: 100%; }
.rosterli-gallery-lightbox-img {
	max-width: 100%;
	max-height: calc(100vh - 8rem);
	width: auto;
	height: auto;
	object-fit: contain;
	display: block;
}
.rosterli-gallery-lightbox-caption {
	max-width: 60ch;
	font-size: 0.95rem;
}
.rosterli-gallery-lightbox .carousel-control-prev,
.rosterli-gallery-lightbox .carousel-control-next { width: 8%; }

/* ── Gallery block layout: photo + caption alignment ──────────────── */
.rosterli-gallery-block .rosterli-gallery-item {
	display: flex;
	flex-direction: column;
	height: 100%;
}
.rosterli-gallery-block .rosterli-gallery-item > .figure {
	display: flex;
	flex-direction: column;
	height: 100%;
}
.rosterli-gallery-block .rosterli-gallery-media {
	display: flex;
	width: 100%;
}
.rosterli-gallery-block .rosterli-gallery-media > a,
.rosterli-gallery-block .rosterli-gallery-media > img {
	width: 100%;
}
.rosterli-gallery-photo-align-top    .rosterli-gallery-media { align-items: flex-start; }
.rosterli-gallery-photo-align-middle .rosterli-gallery-media { align-items: center; }
.rosterli-gallery-photo-align-bottom .rosterli-gallery-media { align-items: flex-end; }
.rosterli-gallery-photo-align-middle .rosterli-gallery-media > a,
.rosterli-gallery-photo-align-middle .rosterli-gallery-media > img,
.rosterli-gallery-photo-align-bottom .rosterli-gallery-media > a,
.rosterli-gallery-photo-align-bottom .rosterli-gallery-media > img {
	align-self: inherit;
}
.rosterli-gallery-caption-bottom .figure-caption { margin-top: auto; }

/* ── Multi-Column Row: Configurable Gap + Optional Vertical Rule ───────────
   Applied to every multi-column row wrapper across blocks, page/header/footer
   layout regions, and footer navigation columns. Maps a named gap scale to
   Bootstrap's gutter custom properties so the standard grid keeps working.
   Vertical rules render only between columns (never on outer edges) and only
   at the md breakpoint or wider, so stacked mobile layouts do not show an
   awkward border. Uses theme-aware Bootstrap border variables.

   Selectors qualify on `.row` so the rules carry (0,2,0) specificity that
   beats the inline theme-generated `.row { --bs-gutter-x: <theme-spacing> }`
   declaration emitted by website_theme_service.generate_css(). That rule
   loads inside a <style> tag after this stylesheet, so a bare `.rosterli-row`
   selector would tie on specificity (0,1,0) and lose on source order - the
   column padding then stays pinned to the theme default no matter which gap
   class is set.
   ──────────────────────────────────────────────────────────────────────── */
.row.rosterli-row {
	--rosterli-col-gap: 1.5rem;
	--bs-gutter-x: var(--rosterli-col-gap);
	--bs-gutter-y: var(--rosterli-col-gap);
}
.row.rosterli-col-gap-none { --rosterli-col-gap: 0; }
.row.rosterli-col-gap-xs   { --rosterli-col-gap: 0.25rem; }
.row.rosterli-col-gap-sm   { --rosterli-col-gap: 0.5rem; }
.row.rosterli-col-gap-md   { --rosterli-col-gap: 1.5rem; }
.row.rosterli-col-gap-lg   { --rosterli-col-gap: 2.5rem; }
.row.rosterli-col-gap-xl   { --rosterli-col-gap: 4rem; }
.row.rosterli-col-gap-xxl  { --rosterli-col-gap: 6rem; }

@media (min-width: 768px) {
	.rosterli-col-rule > [class*="col-"]:not(:first-child) {
		border-left: var(--bs-border-width, 1px) solid var(--bs-border-color);
	}
}

/* ── Responsive visibility (platform-owned) ───────────────────────────────
   Layout- and column-level "hide on breakpoint" controls in the page builder
   emit these `rl-hide-<bp>` classes (never free-form Bootstrap display
   utilities, which would fight the layout's own d-flex/d-block state). Each
   class hides its element ONLY within that breakpoint's band, so hiding on an
   arbitrary subset of breakpoints composes cleanly. `display: none` also drops
   the element out of the flex row, so a hidden column leaves no empty gap.
   Bands follow Bootstrap 5.3: xs <576, sm 576-767, md 768-991, lg 992-1199,
   xl 1200-1399, xxl ≥1400.
   ──────────────────────────────────────────────────────────────────────── */
@media (max-width: 575.98px) {
	.rl-hide-xs { display: none !important; }
}
@media (min-width: 576px) and (max-width: 767.98px) {
	.rl-hide-sm { display: none !important; }
}
@media (min-width: 768px) and (max-width: 991.98px) {
	.rl-hide-md { display: none !important; }
}
@media (min-width: 992px) and (max-width: 1199.98px) {
	.rl-hide-lg { display: none !important; }
}
@media (min-width: 1200px) and (max-width: 1399.98px) {
	.rl-hide-xl { display: none !important; }
}
@media (min-width: 1400px) {
	.rl-hide-xxl { display: none !important; }
}

/* ── Navigation hamburger toggle ──────────────────────────────────────────
   Bootstrap's default `.navbar-toggler-icon` is a background-image SVG that
   isn't reliably colorable. The navigation renderer rewrites the toggle
   markup to use the .hamburger / .hamburger-box / .hamburger-inner
   structure shared with the admin preview so the bars paint with
   `currentColor`. Setting `color` on `.navbar-toggler` via the per-nav
   scoped <style> emitted by website_renderer_service.wrap_navigation_with_style
   then drives the bar color (and ::before / ::after) for the mobile menu
   icon. Animation transforms only apply when the toggler also carries
   `.is-active`; without the optional JS shim that mirrors aria-expanded
   onto `.is-active`, the icon still renders and toggles the menu open
   correctly - only the squeeze/cross animation goes unrendered.
   ──────────────────────────────────────────────────────────────────────── */
.hamburger { display: inline-flex; align-items: center; justify-content: center; padding: 4px; cursor: pointer; background: transparent; border: 0; color: inherit; outline: none; box-shadow: none; }
.navbar-toggler.hamburger { border: 0; }
.navbar-toggler.hamburger:focus { outline: none; box-shadow: none; }
.hamburger-box { width: 30px; height: 24px; display: inline-block; position: relative; transition: transform 0.3s ease; }
.hamburger-inner,
.hamburger-inner::before,
.hamburger-inner::after { width: 100%; height: 3px; background-color: currentColor; border-radius: 2px; position: absolute; left: 0; transition: all 0.2s ease; }
.hamburger-inner { top: 50%; margin-top: -1.5px; }
.hamburger-inner::before { content: ''; top: -8px; }
.hamburger-inner::after { content: ''; bottom: -8px; }
.hamburger--sm .hamburger-box { width: 24px; height: 18px; }
.hamburger--sm .hamburger-inner,
.hamburger--sm .hamburger-inner::before,
.hamburger--sm .hamburger-inner::after { height: 2px; }
.hamburger--sm .hamburger-inner { margin-top: -1px; }
.hamburger--sm .hamburger-inner::before { top: -6px; }
.hamburger--sm .hamburger-inner::after { bottom: -6px; }
.hamburger--squeeze.is-active .hamburger-inner { background-color: transparent; }
.hamburger--squeeze.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--squeeze.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--spin.is-active .hamburger-box { transform: rotate(180deg); }
.hamburger--spin.is-active .hamburger-inner { background-color: transparent; }
.hamburger--spin.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--spin.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--spin-r.is-active .hamburger-box { transform: rotate(-180deg); }
.hamburger--spin-r.is-active .hamburger-inner { background-color: transparent; }
.hamburger--spin-r.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--spin-r.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--elastic .hamburger-inner,
.hamburger--elastic .hamburger-inner::before,
.hamburger--elastic .hamburger-inner::after { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); transition-duration: 0.3s; }
.hamburger--elastic.is-active .hamburger-inner { background-color: transparent; }
.hamburger--elastic.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--elastic.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--arrow.is-active .hamburger-inner { background-color: transparent; }
.hamburger--arrow.is-active .hamburger-inner::before { top: 0; width: 50%; transform: rotate(-40deg); transform-origin: 0 50%; }
.hamburger--arrow.is-active .hamburger-inner::after { bottom: 0; width: 50%; transform: rotate(40deg); transform-origin: 0 50%; }
.hamburger--arrow-r.is-active .hamburger-inner { background-color: transparent; }
.hamburger--arrow-r.is-active .hamburger-inner::before { top: 0; width: 50%; left: 50%; transform: rotate(40deg); transform-origin: 100% 50%; }
.hamburger--arrow-r.is-active .hamburger-inner::after { bottom: 0; width: 50%; left: 50%; transform: rotate(-40deg); transform-origin: 100% 50%; }
.hamburger--collapse .hamburger-inner::before { transition: top 0.15s 0.15s ease, transform 0.15s ease; }
.hamburger--collapse .hamburger-inner::after { transition: bottom 0.15s 0.15s ease, transform 0.15s ease; }
.hamburger--collapse.is-active .hamburger-inner { background-color: transparent; }
.hamburger--collapse.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); transition: top 0.15s ease, transform 0.15s 0.15s ease; }
.hamburger--collapse.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); transition: bottom 0.15s ease, transform 0.15s 0.15s ease; }
.hamburger--vortex .hamburger-box { transition: transform 0.4s ease; }
.hamburger--vortex.is-active .hamburger-box { transform: rotate(360deg); }
.hamburger--vortex.is-active .hamburger-inner { background-color: transparent; }
.hamburger--vortex.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--vortex.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--slider .hamburger-inner::before { transition: top 0.15s 0.15s ease, background-color 0.15s ease; }
.hamburger--slider .hamburger-inner::after { transition: bottom 0.15s 0.15s ease, transform 0.15s ease; }
.hamburger--slider.is-active .hamburger-inner { transform: rotate(45deg); }
.hamburger--slider.is-active .hamburger-inner::before { top: 0; background-color: transparent; transition: top 0.15s ease, background-color 0.15s 0.15s ease; }
.hamburger--slider.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-90deg); transition: bottom 0.15s ease, transform 0.15s 0.15s ease; }
.hamburger--minus.is-active .hamburger-inner::before { top: 0; background-color: transparent; }
.hamburger--minus.is-active .hamburger-inner::after { bottom: 0; background-color: transparent; }
.hamburger--flipx { perspective: 80px; }
.hamburger--flipx .hamburger-box { transition: transform 0.3s ease; }
.hamburger--flipx.is-active .hamburger-box { transform: rotateX(180deg); }
.hamburger--flipx.is-active .hamburger-inner { background-color: transparent; }
.hamburger--flipx.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--flipx.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }
.hamburger--flipy { perspective: 80px; }
.hamburger--flipy .hamburger-box { transition: transform 0.3s ease; }
.hamburger--flipy.is-active .hamburger-box { transform: rotateY(180deg); }
.hamburger--flipy.is-active .hamburger-inner { background-color: transparent; }
.hamburger--flipy.is-active .hamburger-inner::before { top: 0; transform: rotate(45deg); }
.hamburger--flipy.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-45deg); }

/* ── Split navbar (centered brand) ────────────────────────────────────────
   The navbar_split preset renders three children inside .navbar-collapse:
   the left item group, a centered brand slot, and the right item group. On
   desktop (>=992px, Bootstrap's navbar-expand-lg breakpoint) a three-column
   grid keeps the brand horizontally centered regardless of how the items
   divide left/right - uneven counts never pull the logo off-center. The
   side columns each take an equal flexible share (minmax(0, 1fr)) so long
   labels wrap/shrink instead of overlapping the auto-width center column.
   Below the breakpoint the collapsed menu falls back to normal stacked flow
   so mobile keeps Bootstrap's default hamburger/stacked behavior. The empty
   brand slot collapses to zero width when Show logo / group name are off, so
   logo-off split navs lay out exactly as a left/right split.
   Mobile: the centered slot lives inside .navbar-collapse, so below lg it is
   hidden (d-none d-lg-flex) and a bar-level twin slot
   (.split-navbar-logo-mobile, d-lg-none, outside the collapse) carries the
   same brand - hamburger left, brand right (margin-left: auto backs up the
   navbar container's space-between), vertically centered by the bar's own
   align-items. The expanded menu therefore never repeats the logo. */
.split-navbar-logo { display: flex; align-items: center; }
.split-navbar-logo .navbar-brand { margin-right: 0; }
.split-navbar-logo-mobile { margin-left: auto; min-width: 0; flex: 1 1 0%; justify-content: flex-end; }
/* Text-brand fallback: Bootstrap's .navbar-brand is white-space: nowrap, so a
   long group name would overflow a narrow bar. flex-basis 0 above keeps the
   slot on the toggler's row (it absorbs the remaining space instead of
   flex-wrapping below it) and the name wraps inside the right-aligned slot.
   Logos are unaffected (fixed-height <img>). */
.split-navbar-logo-mobile .navbar-brand { min-width: 0; white-space: normal; text-align: right; }
@media (min-width: 992px) {
	.split-navbar-inner { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; width: 100%; }
	.split-navbar-left { justify-content: flex-start; }
	.split-navbar-logo { justify-self: center; }
	.split-navbar-right { justify-content: flex-end; }
}

/* ── Calendar block ───────────────────────────────────────────────────────
   Static layout/visual CSS for the website Calendar block. Previously emitted
   as a per-instance inline <style> from shared/views/blocks/calendar.cfm - the
   identical rules were re-sent in the page source for every calendar block and
   never cached. The block view now carries only the dynamic per-event color
   inline style attributes (style="background-color: …"); everything below is
   static, so it lives here and is served once from this cached stylesheet
   across every public page and admin editor preview. The Bootstrap-default
   hex colors below are intentional literals (focus rings, borders, muted
   text) - they were hard-coded in the original block and are reproduced
   verbatim so rendering is byte-identical.
   ──────────────────────────────────────────────────────────────────────── */
.rosterli-calendar-block { font-size: 0.95rem; box-sizing: border-box; }
.rosterli-calendar-block *, .rosterli-calendar-block *::before, .rosterli-calendar-block *::after { box-sizing: inherit; }
.rosterli-calendar-block-title { margin: 0 0 0.5rem 0; font-size: 1.25rem; font-weight: 600; }
.rosterli-calendar-block-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
.rosterli-calendar-block-header-label-group { display: inline-flex; flex-direction: column; gap: 0.25rem; margin-right: auto; min-width: 0; }
.rosterli-calendar-block-window-label { margin: 0; font-size: 1rem; color: #6c757d; font-weight: 600; text-align: left; }
.rosterli-calendar-block-month-nav { display: inline-flex; gap: 0.25rem; }
.rosterli-calendar-block-month-nav a { display: inline-flex; align-items: center; justify-content: center; min-width: 2rem; height: 2rem; padding: 0 0.5rem; border: 1px solid #dee2e6; border-radius: 0.25rem; text-decoration: none; color: #495057; background: #fff; transition: background-color 0.15s ease, border-color 0.15s ease; }
.rosterli-calendar-block-month-nav a:hover { background: #f1f3f5; border-color: #adb5bd; }
.rosterli-calendar-block-month-nav a:focus-visible { outline: 2px solid #0d6efd; outline-offset: 2px; }
.rosterli-calendar-block-empty { padding: 1rem; text-align: center; color: #6c757d; }
.rosterli-calendar-block-legend { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; margin-bottom: 0.75rem; font-size: 0.85rem; }
.rosterli-calendar-block-legend-item { display: inline-flex; align-items: center; gap: 0.35rem; }
.rosterli-calendar-block-color-swatch { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 50%; flex-shrink: 0; }

.rosterli-calendar-block-layout { display: flex; align-items: stretch; }
.rosterli-calendar-block-main { flex: 1 1 100%; min-width: 0; transition: flex-basis 0.25s ease; }
.rosterli-calendar-block-side { flex: 0 0 0; min-width: 0; max-width: 0; padding-left: 0; overflow: hidden; opacity: 0; transition: flex-basis 0.25s ease, max-width 0.25s ease, padding-left 0.25s ease, opacity 0.2s ease; }
.rosterli-calendar-block--has-selected-event.rosterli-calendar-block--details-side .rosterli-calendar-block-main { flex-basis: 66.6667%; }
.rosterli-calendar-block--has-selected-event.rosterli-calendar-block--details-side .rosterli-calendar-block-side { flex-basis: 33.3333%; min-width: 14rem; max-width: 100%; padding-left: 1rem; opacity: 1; overflow: visible; }
.rosterli-calendar-block-below { margin-top: 1rem; }
.rosterli-calendar-block-below[hidden] { display: none; }

.rosterli-calendar-block-list { margin: 0; padding: 0; list-style: none; }
.rosterli-calendar-block-list-day { padding: 0.5rem 0; border-bottom: 1px solid #e9ecef; }
.rosterli-calendar-block-list-day:last-child { border-bottom: 0; }
.rosterli-calendar-block-list-date { font-weight: 600; margin-bottom: 0.35rem; }
.rosterli-calendar-block-list-event-row { display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.35rem 0.4rem; width: 100%; background: transparent; border: 1px solid transparent; border-radius: 0.25rem; text-align: left; cursor: pointer; color: inherit; font: inherit; transition: background-color 0.15s ease, border-color 0.15s ease; }
.rosterli-calendar-block-list-event-row:hover { background: #f8f9fa; }
.rosterli-calendar-block-list-event-row:focus-visible { outline: 2px solid #0d6efd; outline-offset: 2px; }
.rosterli-calendar-block-list-event-row[aria-expanded="true"] { background: #e7f1ff; border-color: #b6d4fe; }
.rosterli-calendar-block-list-event-time { color: #6c757d; flex-shrink: 0; font-variant-numeric: tabular-nums; min-width: 7rem; }
.rosterli-calendar-block-list-event-title { font-weight: 500; word-break: break-word; }
.rosterli-calendar-block-list-event-location { color: #6c757d; font-size: 0.85em; word-break: break-word; }

.rosterli-calendar-block-inline-detail { max-height: 0; overflow: hidden; transition: max-height 0.25s ease; }
.rosterli-calendar-block-inline-detail[data-rosterli-open="true"] { max-height: 32rem; }
.rosterli-calendar-block-inline-detail-inner { padding: 0.5rem 0.75rem 0.75rem 1.25rem; }

.rosterli-calendar-block-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.rosterli-calendar-block-grid th, .rosterli-calendar-block-grid td { border: 1px solid #dee2e6; padding: 0.25rem; vertical-align: top; }
.rosterli-calendar-block-grid th { background: #f8f9fa; text-align: center; font-weight: 600; font-size: 0.85rem; padding: 0.4rem 0.25rem; }
.rosterli-calendar-block-grid-day-number { font-size: 0.8rem; color: #6c757d; margin-bottom: 0.15rem; }
.rosterli-calendar-block-grid-day { height: 5.5rem; }
.rosterli-calendar-block-grid-day-out { background-color: #fafafa; }
.rosterli-calendar-block-grid-day-out .rosterli-calendar-block-grid-day-number { opacity: 0.45; }
/* Past day cells: dimmer than in-month cells but still visible so the
   calendar grid stays complete. The day number is muted; events on the
   day are rendered as non-interactive spans (.is-past). */
.rosterli-calendar-block-grid-day-past { background-color: #f5f5f5; }
.rosterli-calendar-block-grid-day-past .rosterli-calendar-block-grid-day-number { opacity: 0.55; }
.rosterli-calendar-block-grid-event { display: block; width: 100%; font-size: 0.78rem; line-height: 1.2; padding: 0.1rem 0.3rem; margin-bottom: 0.1rem; border-radius: 0.2rem; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; border: 0; cursor: pointer; }
.rosterli-calendar-block-grid-event:focus-visible { outline: 2px solid #0d6efd; outline-offset: 1px; }
.rosterli-calendar-block-grid-event[aria-pressed="true"] { box-shadow: 0 0 0 2px #0d6efd; }
.rosterli-calendar-block-grid-event.is-past { opacity: 0.55; cursor: default; pointer-events: none; }
.rosterli-calendar-block-list-event-row.is-past { opacity: 0.55; cursor: default; pointer-events: none; }
.rosterli-calendar-block-grid-more { display: block; font-size: 0.75rem; color: #6c757d; padding: 0 0.2rem; }

.rosterli-calendar-block-detail-bank { display: none; }
/* The detail panel uses Bootstrap .card / .card-body / .card-img-top / .card-title /
   .card-text / .btn / .btn-close. Only namespaced sizing + spacing overrides live
   here so the block does not redefine Bootstrap's card visual treatment. */
.rosterli-calendar-block-detail-title { word-break: break-word; }
.rosterli-calendar-block-detail-row { display: flex; gap: 0.5rem; font-size: 0.9rem; margin-bottom: 0.25rem; flex-wrap: wrap; }
.rosterli-calendar-block-detail-label { color: #6c757d; min-width: 5rem; }
.rosterli-calendar-block-detail-summary { white-space: pre-wrap; word-break: break-word; }
.rosterli-calendar-block-detail-meta { display: inline-flex; align-items: center; gap: 0.35rem; }
.rosterli-calendar-block-detail-banner { width: 100%; height: auto; max-height: 16rem; object-fit: cover; }

.rosterli-calendar-block-list-only { display: none; }
.rosterli-calendar-block-grid-only { display: block; }
.rosterli-calendar-block--mode-list .rosterli-calendar-block-grid-only { display: none; }
.rosterli-calendar-block--mode-list .rosterli-calendar-block-list-only { display: block; }

@media (max-width: 480px) {
	.rosterli-calendar-block-grid-only { display: none; }
	.rosterli-calendar-block-list-only { display: block; }
	.rosterli-calendar-block-side { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
	.rosterli-calendar-block, .rosterli-calendar-block * { transition: none !important; }
}

/* ── Card hover lift ───────────────────────────────────────────────────────
   Applied to the Card / Card Builder blocks when "hover effect" is enabled.
   The .rosterli-card-hover class is added by block_renderer_service ONLY when
   hover_effect is true, so a single global rule reproduces exactly what the
   per-instance, id-scoped inline <style> emitted from card.cfm / card_builder.cfm
   used to do - without duplicating the identical rule into the page source for
   every hover-enabled card grid.
   ──────────────────────────────────────────────────────────────────────── */
.rosterli-card-hover { transition: transform 0.2s ease, box-shadow 0.2s ease; }
.rosterli-card-hover:hover { transform: translateY(-4px); box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .12) !important; }

/* ── Testimonial carousel ───────────────────────────────────────────────────
   testimonial_quote block, layout = "carousel". Progressive enhancement: the
   server renders the first slide active (display:block); testimonial_carousel.js
   rotates slides and wires the prev / play-pause / next controls. Everything is
   class-namespaced so it cannot leak into other blocks, and the controls use
   Bootstrap CSS variables so they stay legible on light, dark, and brand-coloured
   sections.
   ──────────────────────────────────────────────────────────────────────── */
.rosterli-testimonial-carousel { position: relative; }
.rosterli-tc-viewport { position: relative; overflow: hidden; }
.rosterli-tc-slide { display: none; }
.rosterli-tc-slide.is-active { display: block; }

@media (prefers-reduced-motion: no-preference) {
	.rosterli-tc-slide.is-active { animation: rosterli-tc-fade 0.4s ease; }
}
@keyframes rosterli-tc-fade {
	from { opacity: 0; }
	to { opacity: 1; }
}

.rosterli-tc-controls {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	margin-top: 1rem;
}
.rosterli-tc-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;
	height: 2.5rem;
	padding: 0;
	border-radius: 50%;
	border: 1px solid var(--bs-border-color, rgba(0, 0, 0, 0.15));
	background-color: var(--bs-body-bg, #fff);
	color: var(--bs-body-color, #212529);
	box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15);
	cursor: pointer;
	line-height: 1;
	transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.rosterli-tc-btn:hover { background-color: var(--bs-secondary-bg, #e9ecef); }
.rosterli-tc-btn:focus-visible {
	outline: 2px solid var(--bs-primary, #0d6efd);
	outline-offset: 2px;
}

/* ──────────────────────────────────────────────────────────────────────────
 * System-managed legal links bar (Rosterli Terms & Privacy).
 *
 * Rendered by website_renderer_service.render_legal_bar and emitted on every
 * public page by the document wrappers. Non-optional compliance layer - it is
 * intentionally independent of the editable footer section. Kept visually
 * subordinate (a thin, muted strip at the very bottom) so it does not compete
 * with a site's own footer.
 * ────────────────────────────────────────────────────────────────────────── */
.rosterli-legal-bar {
	border-top: 1px solid var(--bs-border-color, rgba(0, 0, 0, 0.1));
	background-color: var(--bs-body-bg, #fff);
	padding: 0.625rem 1rem;
}
.rosterli-legal-bar__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.5rem 1.25rem;
	margin: 0;
	padding: 0;
	list-style: none;
}
.rosterli-legal-bar__item {
	display: inline-flex;
	align-items: center;
}
.rosterli-legal-bar__item + .rosterli-legal-bar__item::before {
	content: "";
	width: 1px;
	height: 0.75rem;
	margin-right: 1.25rem;
	background-color: var(--bs-border-color, rgba(0, 0, 0, 0.2));
}
.rosterli-legal-bar__link {
	font-size: 0.8125rem;
	color: var(--bs-secondary-color, #6c757d);
	text-decoration: none;
}
.rosterli-legal-bar__link:hover,
.rosterli-legal-bar__link:focus-visible {
	color: var(--bs-body-color, #212529);
	text-decoration: underline;
}
.rosterli-legal-bar__link:focus-visible {
	outline: 2px solid var(--bs-primary, #0d6efd);
	outline-offset: 2px;
}

/* ── Pricing block ──────────────────────────────────────────────────────────── */

/* Base pricing card - builds on Bootstrap .card; .h-100 + d-flex flex-column
   on .card-body + .mt-auto on the CTA handle equal-height. */
.rosterli-pricing-card {
	transition: box-shadow 0.2s ease, transform 0.2s ease;
	position: relative;
}

/* Accent-colour hook: a 3px top bar drawn as an ::before OVERLAY (not a
   border-top), so it never changes the card's border box. A real border-top
   width would push that card's content down relative to its siblings and break
   the cross-card alignment of plan names and prices. Also tints the price. */
.rosterli-pricing-card[style*="--rosterli-pricing-accent"]::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 3px;
	background: var(--rosterli-pricing-accent);
	border-top-left-radius: inherit;
	border-top-right-radius: inherit;
	pointer-events: none;
}
.rosterli-pricing-card[style*="--rosterli-pricing-accent"] .rosterli-pricing-value {
	color: var(--rosterli-pricing-accent);
}

/* Featured card: prominence via primary border colour + an inset ring + drop
   shadow. The ring is a box-shadow and the border WIDTH is left at the card's
   default (same as every sibling) - using border-width:2px (or the accent bar
   above) changes the border box and pushes the card's title/price down, which
   misaligns them across cards. No transform: scale() either (it made the card
   taller than its equal-height siblings and broke CTA bottom-alignment). */
.rosterli-pricing-card--featured {
	border-color: var(--bs-primary) !important;
	box-shadow: inset 0 0 0 2px var(--bs-primary), 0 0.5rem 1rem rgba(var(--bs-primary-rgb, 13 110 253) / 0.18);
}

/* Muted card: visually de-emphasised but still readable */
.rosterli-pricing-card--muted {
	opacity: 0.72;
}
.rosterli-pricing-card--muted:hover,
.rosterli-pricing-card--muted:focus-within {
	opacity: 1;
}

/* Price display */
.rosterli-pricing-amount {
	display: inline-flex;
	align-items: baseline;
	gap: 0.1em;
	line-height: 1.1;
}
.rosterli-pricing-value {
	font-size: 2rem;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--bs-body-color);
}
.rosterli-pricing-prefix {
	font-size: 1.1rem;
	font-weight: 600;
	align-self: flex-start;
	margin-top: 0.3em;
}
.rosterli-pricing-suffix {
	font-size: 0.875rem;
	color: var(--bs-secondary-color, #6c757d);
	margin-left: 0.2em;
}

/* Feature list */
.rosterli-pricing-features {
	text-align: left;
}
.rosterli-pricing-check-icon {
	color: var(--bs-success, #198754);
	font-size: 0.85em;
}

/* Section background helpers */
.rosterli-pricing-section {
	padding: 1.5rem;
	border-radius: var(--bs-border-radius-lg, 0.5rem);
}

/* Compact layout: tighter card-body padding */
.rosterli-pricing--compact .card-body {
	padding: 1rem;
}
.rosterli-pricing--compact .rosterli-pricing-value {
	font-size: 1.5rem;
}

/* Dark / brand contrast helpers */
.rosterli-pricing--dark .rosterli-pricing-value,
.rosterli-pricing--brand .rosterli-pricing-value {
	color: var(--bs-body-color); /* inherits from text-bg-* */
}
.rosterli-pricing--dark .rosterli-pricing-card,
.rosterli-pricing--brand .rosterli-pricing-card {
	--bs-card-bg: rgba(255, 255, 255, 0.1);
	--bs-card-border-color: rgba(255, 255, 255, 0.2);
	color: var(--bs-body-color);
}
.rosterli-pricing--dark .rosterli-pricing-card--featured,
.rosterli-pricing--brand .rosterli-pricing-card--featured {
	--bs-card-bg: rgba(255, 255, 255, 0.18);
	border-color: rgba(255, 255, 255, 0.7) !important;
}

/* ── Public forms (Request Form + Mailing List Signup) ───────────────── */
.rosterli-public-form {
	/* Fill the block's column. The form's width is limited by the page-layout
	   column it sits in (single/two/three-column) - not by a hard cap here. */
	width: 100%;
}
.rosterli-public-form__intro {
	color: var(--bs-secondary-color, #6c757d);
}
.rosterli-public-form__submit {
	min-width: 10rem;
}
/* Date/time slot picker: tile the time buttons at equal widths. The btn-check
   inputs are position:absolute (Bootstrap) so they don't occupy grid cells -
   only the labels do, each stretching to fill its track. */
.rosterli-public-form__slottimes {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
	gap: 0.5rem;
}
.rosterli-public-form__slottimes > .btn {
	width: 100%;
}
/* Honeypot: removed from view and from layout but still present + focusable-
   skipped in the DOM so bots fill it. Not display:none (some bots skip hidden
   fields) - pushed off-screen instead. */
.rosterli-pf-hp {
	position: absolute !important;
	left: -10000px !important;
	top: auto;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ── Article block ──────────────────────────────────────────────────────────
   Blog/news-style article. The readable body column width is set per block via
   Bootstrap col classes (.col-lg-8 offset-lg-2 etc.); these rules only handle
   typography polish and keep header/embedded images responsive and contained. */
.rosterli-article-header-figure {
	margin-bottom: 1.5rem;
}
.rosterli-article-header-image {
	width: 100%;
	height: auto;
	object-fit: cover;
}
.rosterli-article-media-video {
	display: block;
	width: 100%;
	height: auto;
}
.rosterli-article-title {
	line-height: 1.2;
}
.rosterli-article-subheadline {
	font-weight: 400;
}
.rosterli-article-body {
	font-size: 1.0625rem;
	line-height: 1.7;
}
.rosterli-article-body img {
	max-width: 100%;
	height: auto;
}
.rosterli-article-body figure {
	margin: 1.5rem 0;
}
.rosterli-article-body figure.figure {
	display: block;
}
.rosterli-article-body .figure-caption {
	margin-top: 0.5rem;
}
.rosterli-article-body > :last-child {
	margin-bottom: 0;
}

/* Article tables (authored in the Article body WYSIWYG; Article block only -
   scoped under .rosterli-article-body so admin grids, Bootstrap .table uses,
   and other blocks are untouched). Clean, light default; on narrow screens the
   table scrolls horizontally inside its own box instead of breaking the page
   layout (GitHub-style display:block + max-content pattern). */
.rosterli-article-body table {
	display: block;
	width: max-content;
	max-width: 100%;
	overflow-x: auto;
	border-collapse: collapse;
	margin: 1.25rem 0;
	font-size: 0.9375rem;
	line-height: 1.5;
}
.rosterli-article-body table caption {
	caption-side: top;
	color: var(--bs-secondary-color, #6c757d);
	font-size: 0.875rem;
	text-align: left;
	padding: 0 0 0.35rem 0;
}
.rosterli-article-body th,
.rosterli-article-body td {
	border: 1px solid var(--bs-border-color, #dee2e6);
	padding: 0.5rem 0.75rem;
	vertical-align: top;
	min-width: 3rem;
}
.rosterli-article-body th {
	background-color: var(--bs-secondary-bg, #f8f9fa);
	font-weight: 600;
}
.rosterli-article-body th p,
.rosterli-article-body td p {
	margin-bottom: 0.35rem;
}
.rosterli-article-body th p:last-child,
.rosterli-article-body td p:last-child {
	margin-bottom: 0;
}

/* Article inline code + code blocks (Article block body only) */
.rosterli-article-body code {
	font-family: var(--bs-font-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace);
	font-size: 0.9em;
}
.rosterli-article-body :not(pre) > code {
	background-color: var(--bs-secondary-bg, #f1f3f5);
	color: var(--bs-code-color, #d63384);
	padding: 0.15em 0.4em;
	border-radius: 0.25rem;
	word-break: break-word;
}
.rosterli-article-body pre {
	background-color: #f6f8fa;
	color: #24292f;
	border: 1px solid var(--bs-border-color, #d0d7de);
	padding: 1rem 1.1rem;
	border-radius: 0.5rem;
	overflow-x: auto;
	margin: 1.25rem 0;
	font-size: 0.875rem;
	line-height: 1.5;
}
.rosterli-article-body pre code {
	background: none;
	color: inherit;
	padding: 0;
	font-size: inherit;
	white-space: pre;
	word-break: normal;
}
/* Wrapper added by article_code_copy.js to host the copy button */
.rosterli-article-code {
	position: relative;
}
.rosterli-article-code > pre {
	padding-top: 2.5rem;
}
.rosterli-article-code-copy {
	position: absolute;
	top: 0.5rem;
	right: 0.5rem;
	z-index: 2;
	background-color: #fff;
	border: 1px solid var(--bs-border-color, #d0d7de);
	color: var(--bs-body-color, #24292f);
	padding: 0.1rem 0.6rem;
	font-size: 0.78rem;
	line-height: 1.5;
	border-radius: 0.25rem;
	opacity: 0.9;
}
.rosterli-article-code-copy:hover {
	opacity: 1;
}
.rosterli-article-code-copied {
	color: var(--bs-success, #198754);
	border-color: var(--bs-success, #198754);
}

/* Decorative Font Awesome icons inserted into rich-text / WYSIWYG content via
   the editor icon picker (RosterliIcon TipTap node → sanitizer-approved
   <i class="… rosterli-inline-icon" aria-hidden="true">). Kept intentionally
   minimal so the icon inherits the surrounding text's colour and size and flows
   inline with prose. The -nodeview wrapper exists only inside the editor. */
.rosterli-inline-icon {
	display: inline-block;
	line-height: inherit;
	vertical-align: baseline;
}
.rosterli-inline-icon-nodeview {
	display: inline-block;
	line-height: inherit;
}
