/* ==========================================================================
   Canvas Compatibility: Plugin CSS Overrides
   ========================================================================== */

/* ── Mobile Heading Scale Restore ─────────────────────────────────────── */

/*
 * Global styles emit unconditional `hN { font-size: var(--cnvs-font-size-hN,
 * preset) }` rules AFTER style.css, which overrides style.css's own
 * `@media (max-width: 767.98px)` heading downscale (h1 1.75rem / h2 1.5rem /
 * h3 1.25rem / h4 1rem): mobile headings rendered at their desktop size.
 * Restore the downscale at (0,1,1) specificity (`body hN`) so it wins over
 * the (0,0,1) global-styles rules regardless of load order, while any
 * classed heading rule (heading-block, demo scopes) still wins over this.
 * The `--cnvs-font-size-hN-mobile` tokens are the override hook for
 * per-device sizes.
 *
 * Element selectors only. The `.hN` classes are also used as icon size
 * tokens (`<i class="bi-play-fill h3 icon-2x">`), where font-icons.css's
 * `.icon-Nx` must win: it does at its own (0,1,0) against style.css's
 * `.hN`, but not against a `body .hN` here. style.css already covers the
 * class form on mobile; global styles cannot outrank it.
 */
@media (max-width: 767.98px) {
	body h1 { font-size: var(--cnvs-font-size-h1-mobile, 1.75rem); }
	body h2 { font-size: var(--cnvs-font-size-h2-mobile, 1.5rem); }
	body h3 { font-size: var(--cnvs-font-size-h3-mobile, 1.25rem); }
	body h4 { font-size: var(--cnvs-font-size-h4-mobile, 1rem); }
}

/* ── Button Shadow ────────────────────────────────────────────────────── */

/*
 * style.css defines .button-shadow-dark and .button-shadow-contrast but not
 * .button-shadow-light. Add the missing light variant here.
 */
.button-shadow-light {
	--cnvs-btn-shadow-color: rgba(255, 255, 255, 0.6);
}

/* ── Button SVG Icon Support ──────────────────────────────────────────── */

/*
 * style.css targets <i> for button icon margin and effects. Icons may also
 * render as inline SVGs (e.g. SVG icon mode / editor re-renders). Mirror
 * every .button i rule to .button svg so both render identically.
 */

.button svg {
	display: inline-block;
	position: relative;
	margin-right: calc(var(--cnvs-btn-icon-margin) + var(--cnvs-btn-icon-margin-offset));
	width: 1em;
	height: 1em;
	vertical-align: -0.125em;
}

.button.text-end svg {
	margin: 0 0 0 calc(var(--cnvs-btn-icon-margin) + var(--cnvs-btn-icon-margin-offset));
}

/* Icon effect extra offset. Mirrors .button-icon-effect i */
.button-icon-effect svg {
	--cnvs-btn-icon-margin-offset: .25rem;
}

/* Flip-X: horizontal slide animation. Mirrors .button-icon-effect.button-icon-flip-x i */
.button-icon-effect.button-icon-flip-x svg {
	animation: buttonIconSlideBack 0.3s ease;
}
.button-icon-effect.button-icon-flip-x:hover svg {
	animation: buttonIconSlideFront 0.5s forwards;
}

/* Flip-Y: vertical slide animation. Mirrors .button-icon-effect.button-icon-flip-y i */
.button-icon-effect.button-icon-flip-y svg {
	animation: buttonIconSlideUp 0.3s ease;
}
.button-icon-effect.button-icon-flip-y:hover svg {
	animation: buttonIconSlideDown 0.5s forwards;
}

/* ── Custom Social Links ──────────────────────────────────────────────── */

/* Hover color from repeater brand color custom property */
.canvas-social-custom:hover {
	background-color: var(--cnvs-social-color) !important;
	color: #fff !important;
}

/* ── Toggle / Accordion: exotic variant styles ── */

/*
 * Defines ~46 rules for a set of "exotic" .toggle-* / .accordion-* variant
 * classes (Card, Left Accent Bar, Pill, Numbered/Step, Minimal, Outline
 * toggles; Card, Left Accent, Timeline/Step accordions). The block/shortcode
 * render layer can already emit any of these class names
 * on the root ".toggle"/".accordion" element (canvasStyle-style free text or
 * an extra-classes field), but style.css never defined them, so choosing
 * one of these variants rendered NO variant styling at all. Declarations
 * use literal `var(--bs-white)` since style.css defines `--bs-white: #fff`
 * exactly once with no dark-mode override: an exact, safe equivalent
 * (--cnvs-contrast-0 was NOT used for this since it flips to #000 in dark
 * mode and would break the always-white text-on-themecolor look these
 * active states rely on). All other var() references used here
 * (--bs-gray-200/300/700, --bs-primary-rgb/--bs-danger-rgb/
 * --bs-success-rgb, --bs-light, --bs-danger, --bs-success, --cnvs-themecolor,
 * --cnvs-toggle-title-color, --cnvs-toggle-header-bg) already exist in
 * style.css with identical names/semantics, so no mapping was needed there.
 * The two box-shadow values (0 2px 8px / 0 4px 16px rgba(0,0,0,.08/.14))
 * are close to but not exact matches of style.css's --cnvs-box-shadow-*
 * tokens, so they are kept as literals to preserve the exact visual result.
 */

/* Card / Shadow Toggles */
.toggle-card {
	border-radius: 0.5rem;
	box-shadow: 0 2px 8px rgba(0,0,0,0.08);
	overflow: hidden;
	margin-bottom: 1rem !important;
	transition: box-shadow .3s ease;
}
.toggle-card .toggle-header {
	padding: 1rem 1.25rem;
}
.toggle-card.toggle-active {
	box-shadow: 0 4px 16px rgba(0,0,0,0.14);
}
.toggle-card .toggle-content {
	padding: 0 1.25rem 1rem;
}

/* Left Accent Bar Toggles */
.toggle-accent {
	border-left: 4px solid var(--bs-gray-300);
	padding-left: 0;
	margin-bottom: 0.5rem !important;
	transition: border-color .3s ease;
}
.toggle-accent .toggle-header {
	padding: 0.75rem 1rem;
}
.toggle-accent .toggle-content {
	padding: 0.5rem 1rem 0.75rem;
}
.toggle-accent.toggle-active {
	border-left-color: var(--cnvs-themecolor);
}
.toggle-accent-danger.toggle-active {
	border-left-color: var(--bs-danger);
}
.toggle-accent-success.toggle-active {
	border-left-color: var(--bs-success);
}

/* Pill / Rounded Toggles */
.toggle-pill .toggle-header {
	border-radius: 50px;
	padding: 0.75rem 1.5rem;
}
.toggle-pill .toggle-content {
	padding: 0.75rem 1.5rem;
}
.toggle-pill-primary .toggle-header {
	background-color: rgba(var(--bs-primary-rgb), 0.1);
	--cnvs-toggle-title-color: var(--bs-primary);
}
.toggle-pill-danger .toggle-header {
	background-color: rgba(var(--bs-danger-rgb), 0.1);
	--cnvs-toggle-title-color: var(--bs-danger);
}
.toggle-pill-success .toggle-header {
	background-color: rgba(var(--bs-success-rgb), 0.1);
	--cnvs-toggle-title-color: var(--bs-success);
}

/* Numbered / Step Toggles */
.toggle-numbered {
	margin-bottom: 0.5rem !important;
}
.toggle-numbered .toggle-header {
	padding: 0.75rem 0;
	border-bottom: 1px solid var(--bs-gray-200);
}
.toggle-numbered .toggle-step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	background: var(--bs-gray-200);
	color: var(--bs-gray-700);
	font-weight: 600;
	font-size: 0.8rem;
	margin-right: 0.75rem;
	flex-shrink: 0;
	transition: all .3s ease;
}
.toggle-numbered.toggle-active .toggle-step {
	background: var(--cnvs-themecolor);
	color: var(--bs-white);
}
.toggle-numbered .toggle-content {
	padding: 0.75rem 0 0.75rem 2.75rem;
}

/* Minimal / Clean Toggles */
.toggle-minimal {
	margin-bottom: 0 !important;
}
.toggle-minimal .toggle-header {
	padding: 1rem 0;
	border-bottom: 1px solid var(--bs-gray-200);
}
.toggle-minimal .toggle-icon i {
	transition: transform .3s ease;
}
.toggle-minimal.toggle-active .toggle-icon .toggle-open {
	transform: rotate(180deg);
}
.toggle-minimal .toggle-content {
	padding: 0.75rem 0 0.25rem;
}

/* Outline / Border Fill Toggles */
.toggle-outline {
	margin-bottom: 0.5rem !important;
}
.toggle-outline .toggle-header {
	border: 2px solid var(--bs-gray-300);
	border-radius: 0.5rem;
	padding: 0.75rem 1rem;
	transition: all .3s ease;
}
.toggle-outline.toggle-active .toggle-header {
	border-color: var(--cnvs-themecolor);
	background: var(--cnvs-themecolor);
	--cnvs-toggle-title-color: var(--bs-white);
	color: var(--bs-white);
}
.toggle-outline.toggle-active .toggle-header .toggle-icon i {
	color: var(--bs-white);
}
.toggle-outline .toggle-content {
	padding: 0.75rem 1rem;
}
.toggle-outline-danger .toggle-header {
	border-color: rgba(var(--bs-danger-rgb), 0.4);
}
.toggle-outline-danger.toggle-active .toggle-header {
	border-color: var(--bs-danger);
	background: var(--bs-danger);
}
.toggle-outline-success .toggle-header {
	border-color: rgba(var(--bs-success-rgb), 0.4);
}
.toggle-outline-success.toggle-active .toggle-header {
	border-color: var(--bs-success);
	background: var(--bs-success);
}

/* Alternate Toggles: style-2 / style-3 / border-alt (ported from source demo <style>) */
.toggle-border-alt.toggle-active,
.toggle-border-alt.toggle-active .toggle-header {
	border-color: var(--cnvs-themecolor);
	transition: all .3s ease;
}
.toggle-border-alt.toggle-active .toggle-header {
	--cnvs-toggle-title-color: var(--cnvs-contrast-0);
	background-color: var(--cnvs-themecolor);
}
.toggle-style-2.toggle-active,
.toggle-style-2.toggle-active .toggle-header {
	border-color: var(--cnvs-themecolor);
	transition: all .3s ease;
}
.toggle-style-2.toggle-active .toggle-header {
	--cnvs-toggle-title-color: var(--cnvs-contrast-0);
	background-color: var(--cnvs-themecolor);
}
.toggle-style-3 {
	--cnvs-toggle-header-bg: var(--bs-light);
}
.toggle-style-3.toggle-active,
.toggle-style-3.toggle-active .toggle-header {
	border-color: var(--cnvs-themecolor);
	transition: all .3s ease;
}
.toggle-style-3.toggle-active .toggle-header {
	--cnvs-toggle-title-color: var(--cnvs-contrast-0);
	background-color: var(--cnvs-themecolor);
}

/* Card / Separated Accordion */
.accordion-card .accordion-header {
	border-radius: 0.5rem;
	padding: 1rem 1.25rem;
	box-shadow: 0 2px 8px rgba(0,0,0,0.08);
	margin-bottom: 0.75rem;
	border: none;
	transition: box-shadow .3s ease;
}
.accordion-card .accordion-header:hover {
	box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.accordion-card .accordion-content {
	padding: 0 1.25rem 1rem;
}

/* Left Accent Accordion */
.accordion-accent .accordion-header {
	border-left: 4px solid var(--bs-gray-300);
	padding: 0.85rem 1rem;
	transition: border-color .3s ease;
}
.accordion-accent .accordion-header.accordion-active,
.accordion-accent .accordion-active .accordion-header {
	border-left-color: var(--cnvs-themecolor);
}
.accordion-accent .accordion-content {
	padding: 0.5rem 1rem 0.75rem 1.25rem;
}

/* Timeline / Step Accordion */
.accordion-timeline {
	position: relative;
	padding-left: 3rem;
}
.accordion-timeline::before {
	content: "";
	position: absolute;
	left: 0.95rem;
	top: 1.25rem;
	bottom: 1.25rem;
	width: 2px;
	background: var(--bs-gray-300);
}
.accordion-timeline .accordion-header {
	position: relative;
	padding: 0.75rem 0;
	border: none;
}
.accordion-timeline .accordion-step {
	position: absolute;
	left: -3rem;
	top: 50%;
	transform: translateY(-50%);
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	background: var(--bs-gray-200);
	color: var(--bs-gray-700);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 600;
	font-size: 0.75rem;
	z-index: 1;
	transition: all .3s ease;
}
.accordion-timeline .accordion-active .accordion-step {
	background: var(--cnvs-themecolor);
	color: var(--bs-white);
}
.accordion-timeline .accordion-content {
	padding: 0.25rem 0 1rem;
}

/* ── Display Conditions: Hide on Device ──────────────────────────────────── */

/*
 * Canvas_Core\Blocks\DisplayConditions injects canvas-hide-{desktop,tablet,
 * mobile} onto a block's root element for the "Hide On" Advanced option.
 * Breakpoints follow the theme's Bootstrap scale (defined in this
 * file: `--bs-breakpoint-md: 768px` / `--bs-breakpoint-lg: 992px`, and the
 * matching max-width boundaries already used throughout this stylesheet:
 * 767.98px / 991.98px):
 *   - mobile  = below md            (max-width: 767.98px)
 *   - tablet  = md up to below lg   (768px to 991.98px)
 *   - desktop = lg and up           (min-width: 992px)
 *
 * Scoped with :where() to keep specificity at (0,1,0), no !important arms
 * race with other utility classes, and guarded to skip the block editor's
 * write-view iframe (body.block-editor-iframe__body) so authors can still
 * see and select a "hidden" block while editing; the rule only takes effect
 * on the real front end (and previews rendered outside that iframe).
 */
@media (max-width: 767.98px) {
	:where(body:not(.block-editor-iframe__body)) .canvas-hide-mobile {
		display: none !important;
	}
}
@media (min-width: 768px) and (max-width: 991.98px) {
	:where(body:not(.block-editor-iframe__body)) .canvas-hide-tablet {
		display: none !important;
	}
}
@media (min-width: 992px) {
	:where(body:not(.block-editor-iframe__body)) .canvas-hide-desktop {
		display: none !important;
	}
}

/*
 * Editor-canvas companions to the three canvas-hide-* rules above. The Header
 * Root block's "Preview device visibility" inspector toggle (ephemeral editor
 * state, never saved) adds .canvas-preview-conditions to the editor canvas
 * iframe's <body>; while present, the same Hide-on-Device conditions engage
 * INSIDE the canvas. The canvas iframe resizes with the editor's
 * desktop/tablet/mobile device preview, so the identical max-width boundaries
 * used on the front end apply naturally. Every selector requires
 * body.block-editor-iframe__body (a class only the block editor's write-view
 * iframe body ever carries) so these rules can NEVER match a front-end body:
 * front-end output and styling are untouched.
 */
@media (max-width: 767.98px) {
	:where(body.block-editor-iframe__body.canvas-preview-conditions) .canvas-hide-mobile {
		display: none !important;
	}
}
@media (min-width: 768px) and (max-width: 991.98px) {
	:where(body.block-editor-iframe__body.canvas-preview-conditions) .canvas-hide-tablet {
		display: none !important;
	}
}
@media (min-width: 992px) {
	:where(body.block-editor-iframe__body.canvas-preview-conditions) .canvas-hide-desktop {
		display: none !important;
	}
}

/* ----------------------------------------------------------------
	Canvas Slider: static layer (persistent overlay across all slides).
	Rendered once inside .slider-inner from a slider's
	`_canvas_slider_static_content` block markup (e.g. a fixed phone /
	contact line or badge, like a Revolution-Slider static layer). It
	fills the slider but only its OWN children take pointer events, so it
	never blocks swipe/nav; the content positions itself (e.g. top-right).
-----------------------------------------------------------------*/
.slider-element .slider-static-layer {
	position: absolute;
	inset: 0;
	z-index: 6;
	pointer-events: none;
}
.slider-element .slider-static-layer > * { pointer-events: auto; }

/* ----------------------------------------------------------------
	FSE post-content top margin.
	style.css gives .entry-content a top margin
	(--cnvs-post-content-margin: 2rem) designed for blog-entry cards,
	where the content follows an entry title/meta block. In FSE
	templates the post content IS the first child of .content-wrap
	(#content > .content-wrap > .entry-content), so that margin opens
	a spurious gap above every page. Zero the token for exactly that
	direct-child case; entry cards inside grids/feeds are never direct
	children of .content-wrap, so they keep the designed spacing.

	A template may also put layout containers between .content-wrap and
	the post content (a .container, a Bootstrap row, a main.postcontent
	column beside a sidebar). The margin then escapes through margin
	collapse and opens the same gap one level up, so the second selector
	covers it: the FSE post-content block (.wp-block-post-content, a
	class no entry card carries) leading its container is the page's
	content root, not content that follows an entry title.
-----------------------------------------------------------------*/
.content-wrap > .entry-content,
.content-wrap .entry-content.wp-block-post-content:first-child {
	--cnvs-post-content-margin: 0;
}

/* A design that sets `.entry-content { margin-top: … }` outright rather than
   through the token is not reached by the rule above, and its stylesheet loads
   after this one. Three classes outrank that single-class rule wherever it is
   declared, and the selector still names only the FSE content root. */
.content-wrap > .entry-content.wp-block-post-content {
	margin-top: 0;
}

/* ----------------------------------------------------------------
	Page-title rules that style.css only expresses through an id.

	Nearly every page-title rule in style.css is the paired selector
	`#page-title, .page-title`, so a class-only section (which is what
	the page-title part and the classic components render) gets it. Two
	rules have no `.page-title` companion and were therefore dead:

	  1. `.sticky-footer #page-title { z-index: 2 }`, without it a
	     sticky footer paints over the page-title bar.
	  2. `#page-title #portfolio-navigation { … }`: the mobile
	     reposition of portfolio prev/next arrows shown inside the bar.

	Restated here keyed on the CLASS rather than adding `id="page-title"` to the
	markup: matching the shared base block via its `#page-title` half raises it
	to ID specificity, which outranks every `.page-title-parallax` / `-mini` /
	`-center` variant overriding the same `--cnvs-page-title-*` properties.
	Class-side, the variants keep winning.
-----------------------------------------------------------------*/
@media (min-width: 992px) {
	.sticky-footer .page-title {
		z-index: 2;
	}
}

@media (max-width: 767.98px) {
	.page-title #portfolio-navigation {
		position: relative;
		top: 0;
		left: 0;
		margin: 1rem auto 0;
	}
}

/*-----------------------------------------------------------------
	Event cards: stacked variant keeps its image gap
	-----------------------------------------------------------------
	`blog.css` styles the blog List-2 HORIZONTAL card with
	`.grid-inner.row.g-0 .entry-image { margin-bottom: 0; height: 100% }`
	so the image fills its `col-md-4` column. The event card family
	shares that same `.grid-inner.row.g-0` chain, so the rule also hit
	the STACKED event card: whose media column is `.col-12`, and whose
	design keeps the image at its natural height with the standard gap
	below it. Scoping on the `.col-12` media column restores the stacked
	card without touching the horizontal one, which stays on `.col-md-4`.
-----------------------------------------------------------------*/
.grid-inner.row.g-0 > .col-12 > .entry-image {
	margin-bottom: var(--cnvs-post-image-mb);
	height: auto;
}

.grid-inner.row.g-0 > .col-12 > .entry-image > img {
	height: auto;
}

/*-----------------------------------------------------------------
	`.collapse.dont-collapse-sm`
	-----------------------------------------------------------------
	A Bootstrap collapse that is only ever collapsible on small
	screens: a sidebar filter panel or an in-page index that must be
	permanently open from md upwards, with the toggle button hidden
	there. Bootstrap ships the collapse behaviour but no such
	modifier, so without this rule the panel stays collapsed at every
	width and the content is simply invisible on desktop, while every
	class in the markup is present and correct.
-----------------------------------------------------------------*/
@media (min-width: 768px) {
	.collapse.dont-collapse-sm {
		display: block;
		height: auto !important;
		visibility: visible;
	}
}

/* ---------------------------------------------------------------------------
   Event list card: let the TEXT column govern the card height again.

   The design's horizontal event card sizes its picture from the text beside
   it: `<a class="entry-image h-100"><img class="h-100 object-cover">`, so the
   image fills whatever height the date / title / excerpt / location produce
   and object-fit crops the overflow.

   WordPress emits width/height attributes on a featured image, and an
   intrinsically sized image inside a `height: 100%` chain contributes its
   NATURAL height to the row instead of taking the row's, so the card grows to
   whatever the picture's aspect ratio makes it.

   Taking the image out of flow removes that contribution; the anchor is already
   `h-100`, so the image still fills the column.
   Only from md up, where the picture and the text are side by side and the row
   therefore HAS a height, below that the card stacks and the image must keep
   its own natural height, exactly as the design renders it.
--------------------------------------------------------------------------- */
@media (min-width: 768px) {

	.entry.event .entry-image.h-100 {
		position: relative;
		display: block;
	}

	.entry.event .entry-image.h-100 > img.h-100 {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
	}
}

/* ---------------------------------------------------------------------------
   Portfolio grid: equal, fixed-height cards.

   A grid of project cards whose photographs differ in aspect ratio lays out
   with a ragged bottom edge: each card is as tall as its own picture. The
   design's answer is to give every card ONE height and let the pictures crop to
   it, and it marks that treatment on the grid itself with `align-items-stretch`:
   the class the Posts block's "Same Heights" option already emits.

   Until now that option emitted the class and nothing else: Bootstrap's own
   `align-items-stretch` stretches the COLUMN, not the card inside it, so the
   pictures kept their natural heights and the setting did nothing visible.
   These are the rules that make it mean what it says.

   Deliberately scoped to `.portfolio.align-items-stretch` rather than shipped
   as the design writes them (bare `.portfolio-image > a`, `.fslider`,
   `.flex-viewport`): unscoped, they would force every portfolio image, slider
   and slide on the site to 100% height and crop it, including the grids that
   are supposed to keep their natural shape.

   `!important` is the design's own, and it is load-bearing here: the slider
   plugin writes inline heights onto `.flex-viewport` and `.slide` at runtime,
   and nothing but `!important` outranks an inline style.
--------------------------------------------------------------------------- */

.portfolio.align-items-stretch .portfolio-item {
	height: 500px;
}

.portfolio.align-items-stretch .grid-inner,
.portfolio.align-items-stretch .portfolio-image > a,
.portfolio.align-items-stretch .portfolio-image img,
.portfolio.align-items-stretch .portfolio-image .fslider,
.portfolio.align-items-stretch .portfolio-image .flexslider,
.portfolio.align-items-stretch .portfolio-image .flex-viewport,
.portfolio.align-items-stretch .portfolio-image .slider-wrap,
.portfolio.align-items-stretch .portfolio-image .slide,
.portfolio.align-items-stretch .portfolio-image .slide a {
	height: 100% !important;
}

.portfolio.align-items-stretch .portfolio-image img {
	object-fit: cover;
}

/* ---------------------------------------------------------------------------
   PORTFOLIO CARD SHAPE: `pf-ratio-<w>x<h>` on `.portfolio-image`
   ---------------------------------------------------------------------------
   The other half of a masonry grid. A column class decides how WIDE a card is;
   this decides how TALL, as a ratio of that width, so a grid of ordinary
   same-shaped photographs can still pack into varied card heights.

   A ratio and not a height: the card is a percentage of a fluid container, so
   a pixel height is correct at exactly one viewport. `aspect-ratio` scales with
   the column at every breakpoint and needs no media queries.

   `object-fit: cover` is what makes it work on photographs that were never
   cropped for the shape: the picture fills the frame and is trimmed, rather
   than being squashed. `.portfolio-item .portfolio-image` already carries
   `overflow: hidden`, so nothing new is needed to contain it.

   The height chain and its `!important` mirror the `.align-items-stretch` rule
   above, and for the same reason: the slider plugin writes inline heights onto
   `.flex-viewport` and `.slide` at runtime, and only `!important` outranks an
   inline style. One trigger, one mechanism: the two blocks stay in step.

   Emitted only when a grid asks for it (the Posts block's Card Shape field, or
   a project's own Card Shape once the grid is set to per-project), so nothing
   here reaches a grid that has not opted in.
--------------------------------------------------------------------------- */

.portfolio-item .portfolio-image[class*="pf-ratio-"] > a,
.portfolio-item .portfolio-image[class*="pf-ratio-"] img,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .fslider,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .flexslider,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .flex-viewport,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .slider-wrap,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .slide,
.portfolio-item .portfolio-image[class*="pf-ratio-"] .slide a {
	height: 100% !important;
}

.portfolio-item .portfolio-image[class*="pf-ratio-"] img {
	object-fit: cover;
}

/* Widest to tallest. These twelve are the shape census of the design's own 52
   masonry pages: one rule per token, and adding a shape means adding one line
   here and one entry to PortfolioMeta::card_aspect_choices(). */
.portfolio-item .portfolio-image.pf-ratio-8x3   { aspect-ratio: 8 / 3; }
.portfolio-item .portfolio-image.pf-ratio-21x9  { aspect-ratio: 21 / 9; }
.portfolio-item .portfolio-image.pf-ratio-16x9  { aspect-ratio: 16 / 9; }
.portfolio-item .portfolio-image.pf-ratio-3x2   { aspect-ratio: 3 / 2; }
.portfolio-item .portfolio-image.pf-ratio-4x3   { aspect-ratio: 4 / 3; }
.portfolio-item .portfolio-image.pf-ratio-5x4   { aspect-ratio: 5 / 4; }
.portfolio-item .portfolio-image.pf-ratio-1x1   { aspect-ratio: 1 / 1; }
.portfolio-item .portfolio-image.pf-ratio-17x20 { aspect-ratio: 17 / 20; }
.portfolio-item .portfolio-image.pf-ratio-4x5   { aspect-ratio: 4 / 5; }
.portfolio-item .portfolio-image.pf-ratio-3x4   { aspect-ratio: 3 / 4; }
.portfolio-item .portfolio-image.pf-ratio-2x3   { aspect-ratio: 2 / 3; }
.portfolio-item .portfolio-image.pf-ratio-9x16  { aspect-ratio: 9 / 16; }

/*-----------------------------------------------------------------
	Bootstrap card media: keep the picture on its own aspect ratio.
	-----------------------------------------------------------------
	Bootstrap stretches `.card-img*` to `width: 100%`, and the base
	stylesheet only declares `img { max-width: 100% }`. WordPress
	serves every attachment with intrinsic `width` and `height`
	attributes, so in a card wider than the file the attribute pins
	the box and the picture renders shorter than its own ratio while
	every class in the markup is present and correct.
-----------------------------------------------------------------*/
.card-img,
.card-img-top,
.card-img-bottom {
	height: auto;
}

/*-----------------------------------------------------------------
	Icon metrics: restore the shared icon utilities after the packs.
	-----------------------------------------------------------------
	font-icons.css declares `display: inline-flex` and `line-height:
	inherit` on every icon `::before`. Each pack re-declares
	`line-height: 1` and `display: inline-block`, and the packs are
	enqueued as DEPENDENTS of canvas-font-icons, so they always print
	afterwards and always win: equal specificity, later source order.
	The reference has the opposite order, importing the packs at the top
	of its own icon stylesheet, so restoring these two properties
	reproduces the reference cascade rather than overriding it.

	Selectors and values are copied verbatim from font-icons.css so the
	two cannot drift. This also covers third-party packs registered via
	`canvas_enqueue_icon_packs`, which print after the base for the same
	reason.

	The icon element carries the same restore: font-icons.css gives it
	`line-height: inherit` and Font Awesome's own `line-height: 1` prints
	over it. Its two size utilities are restated afterwards because
	font-icons.css declares them after that element rule, without them
	the restore would flatten a large or stacked icon's box.
-----------------------------------------------------------------*/
[class*="fa-"]::before,
[class*=" fa-"]::before,
[class*="bi-"]::before,
[class*=" bi-"]::before,
[class*="uil-"]::before,
[class*=" uil-"]::before {
	display: inline-flex;
	line-height: inherit;
}

[class*="fa-"],
[class*=" fa-"],
[class*="bi-"],
[class*=" bi-"],
[class*="uil-"],
[class*=" uil-"] {
	line-height: inherit;
}

.icon-lg {
	line-height: 0.75em;
}

.icon-stacked {
	line-height: 2em;
}

/* ── Sticky product description column ───────────────────────────────── */

/*
 * `.position-lg-sticky` ships `position: sticky` with no threshold, which
 * behaves exactly like `static`. The design supplies the threshold per
 * element: an inline `top` on the event single pages, a page rule of
 * `top: 80px` on the sticky product single. Restated here on the product
 * column's own class pair so the offset ships with the template instead of
 * with one page's content, and so the utility stays a no-op everywhere the
 * design leaves it one.
 */
@media (min-width: 992px) {
	.product-desc.position-lg-sticky {
		top: 80px;
	}
}

/* ── Assistive-only price text in the title band ──────────────────────── */

/*
 * WooCommerce writes a sale price as an aria-hidden `<del>`/`<ins>` pair plus
 * a `.screen-reader-text` sentence that carries the same information in words.
 * The sentence is meant for a screen reader only; the theme ships no rule for
 * the class, so inside the page-title band it reads out as visible copy next
 * to the figures it duplicates.
 */
.page-title .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── Prices in the title band ─────────────────────────────────────────── */

/*
 * `.page-title-content span` is the subtitle rule: block, its own colour, its
 * own size, pushed away from the title. WooCommerce writes a price as nested
 * spans, so in the shop title layout that rule breaks a price onto one line
 * per part: currency symbol above the figure, old price above new. The
 * reference sets its prices inline; restated here so the subtitle rule stops
 * at the subtitle.
 */
.page-title-content .woocommerce-Price-amount,
.page-title-content .woocommerce-Price-currencySymbol {
	display: inline;
	margin-top: 0;
	color: inherit;
	font-size: inherit;
}

/* ── Carousel video items ─────────────────────────────────────────────── */

/*
 * A video item has no intrinsic height until its player is opened, so the
 * carousel would collapse to nothing before the first click. `.item-video` is
 * emitted only by a carousel slide of the Video Link kind, so the height is
 * inert everywhere else.
 */
.item-video {
	height: 500px;
}

/* ── Carousel arrows always visible ───────────────────────────────────── */

/*
 * The base arrow is `opacity: 0` and the carousel's own `:hover` rule reveals
 * it, so a design that treats the arrows as part of the composition rather
 * than as a hover affordance has no way to say so. `owl-nav-always` is that
 * switch (the Arrows Always Visible field on every carousel-capable block)
 * and it reaches nothing until a carousel asks for it.
 */
.owl-carousel.owl-nav-always .owl-nav [class*="owl-"] {
	opacity: 1;
}

/* ── Flat button tint ─────────────────────────────────────────────────── */

/*
 * The flat button is a tinted plate rather than a filled one: the tint is the
 * theme colour at 10% behind the label, and the label keeps that colour on
 * hover instead of inverting to white on a solid fill. style.css still carries
 * the earlier filled-on-hover definition, so both the tint token and the hover
 * pair are restated here.
 */
.button-flat {
	--cnvs-btn-color-flat: var(--cnvs-themecolor);
}

.button-flat::before {
	content: '';
}

.button-flat:hover {
	background-color: inherit;
	color: var(--cnvs-btn-color-flat);
}

/**
 * Core block-library overrides.
 *
 * Both rules below undo `:where()` declarations from wp-block-library that
 * over-reach into theme markup. `:where()` carries zero specificity, so a
 * plain selector wins without `!important`.
 */

/* Core matches the `style` attribute as a substring, so a custom property
   whose NAME contains "border-width" (every Bootstrap `--bs-*-border-width`
   token) is forced to `border-style: solid` and paints the initial `medium`
   width. Restore the initial value; elements that want a border set one. */
html [style*="border-width"]:not([style*="border-style"]) {
	border-style: none;
}

/* Core clamps library images to their container width. `fit-content` is not a
   valid `min()` argument, so the natural width goes on `width` and `max-width`
   stays as the clamp: intrinsic size when it fits, container width when not. */
html img[class*="wp-image-"] {
	height: auto;
	width: fit-content;
	max-width: 100%;
}

/* The Archives block emits `<ul><li><a>`, but the tag-cloud component styles
   the links as the flex items of `.tagcloud`. Making each row a flex container
   hands the link back its block box, so a generated year cloud measures exactly
   like an authored one. */
.tagcloud > li {
	display: flex;
}

/* ---------------------------------------------------------------------------
   Reduced motion

   The two most intrusive motion features (the full-screen page-transition
   overlay and the cursor that replaces the system pointer) are switched off
   for a visitor whose operating system asks for reduced motion. Handled here
   rather than behind a setting: the visitor has already expressed the
   preference, and this layer loads on every request so it applies at first
   paint, before either script has run.
--------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {

	/* Show the destination immediately instead of animating it in. */
	body.page-transition #wrapper:not(.animated),
	body.page-transition #wrapper.animated {
		opacity: 1;
		animation: none;
	}

	.page-transition-wrap,
	body.page-transition .page-transition-wrap {
		display: none;
	}

	/* Give the system pointer back. The `cursor: none` rules are emitted with
	   the feature itself, and are lifted in the same place. */
	.cnvs-cursor {
		display: none;
	}
}

/* ---------------------------------------------------------------------------
   Go To Top: progress-ring variant

   The button's Progress Ring style (Theme Options → Misc → Go To Top) renders
   an SVG ring in place of the icon glyph. These declarations give that ring a
   visible stroke on any site; every value reads a `--cnvs-gotoTop-*` custom
   property, so a design that sets its own keeps it. Written through `:where()`
   so the selector carries no id weight and any stylesheet targeting
   `#gotoTop circle` wins outright, whatever the load order.
--------------------------------------------------------------------------- */
:where(#gotoTop) svg {
	width: 100%;
	height: 100%;
	overflow: visible;
}

:where(#gotoTop) circle {
	fill: none;
	stroke: var(--cnvs-gotoTop-stroke-color, var(--cnvs-themecolor));
	stroke-width: var(--cnvs-gotoTop-stroke-width, 4px);
	stroke-dasharray: 255 255;
	stroke-dashoffset: var(--cnvs-gotoTop-scroll, 0);
	transform: rotate(-90deg);
	transform-origin: center;
}

/* Bootstrap only applies the pagination radius to the end items; the circle
   and rounded styles expect it on every link. */
.pagination.pagination-circle .page-item .page-link,
.pagination.pagination-rounded .page-item .page-link {
	border-radius: var(--bs-pagination-border-radius);
}

/* The widget list styles anticipate <ul>; the Latest Comments block emits an <ol>. */
.widget ol.wp-block-latest-comments {
	margin: 0;
	padding-left: 0;
}

/* Core Search block fields match the framework's form styling. */
.widget .wp-block-search__input {
	padding: 0.375rem 0.75rem;
	border: 1px solid var(--bs-border-color, #dee2e6);
	border-radius: var(--bs-border-radius, 0.375rem);
	background-color: var(--bs-body-bg, #fff);
	color: var(--bs-body-color, #212529);
	font-size: 1rem;
	line-height: 1.5;
}
.widget .wp-block-search__button {
	margin-left: 0.5rem;
	padding: 0.375rem 0.75rem;
	border: 1px solid var(--bs-secondary, #6c757d);
	border-radius: var(--bs-border-radius, 0.375rem);
	background-color: var(--bs-secondary, #6c757d);
	color: #fff;
	font-size: 1rem;
	line-height: 1.5;
	transition: background-color 0.3s ease-in-out;
}
.widget .wp-block-search__button:hover {
	background-color: var(--cnvs-themecolor);
	border-color: var(--cnvs-themecolor);
}

/* ---------------------------------------------------------------------------
   Nested comment avatars
   ---------------------------------------------------------------------------
   The avatar is absolutely positioned to overhang the card, and the card's
   left padding is the gutter it hangs into. At depth 1 that balances: a 70px
   avatar at -35px reaches 35px into a 32px gutter. The nested rules move the
   avatar inward to -25px AND cut the gutter to 20px, so the same 70px avatar
   reaches 46px into 20px and lands on the text. Avatars are 60px at every
   depth, so nested is given the depth-1 geometry; margin-left still carries
   the indent. */
.commentlist li .children .comment-avatar {
	left: -35px;
}

.commentlist ul .comment-wrap {
	padding-left: 2rem;
}

html[dir="rtl"] .commentlist li .children .comment-avatar {
	left: auto;
	right: -35px;
}

html[dir="rtl"] .commentlist ul .comment-wrap {
	padding-left: 1.25rem;
	padding-right: 2rem;
}

/* ---------------------------------------------------------------------------
   404: body class vs design class
   ---------------------------------------------------------------------------
   style.css styles the oversized "404" glyph with `.error404 { font-size:
   18vw; font-weight: 700; color: #ddd; letter-spacing: 4px; line-height: 1 }`.
   Core also puts `error404` on the BODY of every 404 request, so the same
   declarations cascade to the whole document: measured 259.2px body text at a
   1440 viewport against the theme's own 16px. Restate the body values (the
   tokens style.css's own `body` rules use) at (0,1,1) so they win, leaving the
   glyph element itself untouched. */
body.error404 {
	font-size: var(--cnvs-font-size-body);
	font-weight: var(--bs-body-font-weight);
	line-height: 1.5;
	letter-spacing: normal;
	color: var(--bs-body-color);
}

/* ---------------------------------------------------------------------------
   Long unbroken strings
   ---------------------------------------------------------------------------
   A title with no spaces (a taxon name, a hash, a German compound) is a single
   unbreakable word. `.page-title-row` and the Bootstrap content row are flex
   containers whose items keep `min-width: auto`, so the item grows to the
   word's min-content width and pushes the document wider than the viewport.
   `anywhere` (not `break-word`) is required: only `anywhere` lets the soft wrap
   opportunity count towards min-content, which is the size the flex item is
   resolved from.

   The same word can appear anywhere in an article - a heading, a paragraph, a
   comment, the title of the neighbouring post in the prev/next links (an
   `inline-flex` anchor, measured 768px wide inside a 390px viewport, the
   remainder cut off by `.content-wrap`'s `overflow: hidden`). Cover the page
   title, the article and the comment thread rather than each component's own
   class; the prev/next navigation has no component class to target. */
.page-title-content,
.breadcrumb-item,
.entry,
#comments {
	overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   Aligned figures
   ---------------------------------------------------------------------------
   style.css writes the alignment rules for `img.` and `div.` only, but core
   has wrapped captions in a `figure` since 4.4 and gives the figure the
   pixel width of the image (`<figure class="wp-caption" style="width:1200px">`).
   Two consequences in classic post content, both measured: alignleft /
   alignright captions do not float and aligncenter is not centred (they only
   look right on a page, where core's `is-layout-flow` rules happen to cover
   them), and the figure keeps its pixel width in a narrower column so the
   picture is clipped by `.content-wrap`. Mirror the design's own alignment
   margins onto the figure and cap its width. Block images keep core's
   treatment: `.wp-block-image.alignleft` outranks these. */
figure.alignleft {
	float: left;
	margin: 5px 20px 13px 0;
}

figure.alignright {
	float: right;
	margin: 5px 0 13px 20px;
}

figure.alignnone,
figure.aligncenter {
	float: none;
	margin: 10px 0;
}

figure.aligncenter {
	margin-left: auto;
	margin-right: auto;
	clear: both;
}

.wp-caption,
figure.alignleft,
figure.alignright,
figure.alignnone,
figure.aligncenter {
	max-width: 100%;
}

.wp-caption img {
	height: auto;
}

/* ---------------------------------------------------------------------------
   Sticky posts
   ---------------------------------------------------------------------------
   A post pinned to the front of the blog is ordered first and otherwise
   identical to the posts under it, so nothing tells a reader it was pinned.
   Mark the card with the theme accent rather than a badge: no string to
   translate, and it survives every blog layout because it hangs off the card's
   inner wrapper instead of the grid column. */
.entry.sticky > .grid-inner {
	border-inline-start: 3px solid var(--cnvs-themecolor);
	padding-inline-start: 1.25rem;
}

/* ---------------------------------------------------------------------------
   Pingbacks and trackbacks
   ---------------------------------------------------------------------------
   A ping has no commenter, so get_avatar() returns nothing and the portrait
   slot renders as an empty bordered circle overhanging the card. Drop the slot
   when it holds no portrait; the card's own gutter keeps the trail aligned
   with the comments around it. */
.commentlist .comment-avatar:not(:has(img)) {
	display: none;
}
