/* =======================================================================
   JVS' Dash page template

   Cosmetics only. This file deliberately contains NO scroll-animation
   rules: the page renders inside the shared #dedicated-product / .dp-page
   root, so the Apple-style fade (.dp-reveal -> .is-in-view / .is-out-above,
   including its prefers-reduced-motion handling) comes from
   css/dedicated-product.css exactly as it does on Dedicated Product,
   ARC-32, System and about-products. Duplicating it here would let JVS
   drift away from those pages.

   Everything is scoped under .dp-page--jvs-dash. Where the style guide and
   the shared tokens disagree, the token is re-pointed rather than the rule
   re-written, so the base .dp-page declarations keep producing the right
   result.
   ======================================================================= */

/* The canvas paints its own background, but the document behind it (and the
   area around the footer) would otherwise stay on the theme's default light
   ground and flash white on load. The DP templates get this from
   renewal_dp_inline_critical_css(); JVS is not in that gate, so it is declared
   here instead. */
body.jvs-dash-page {
	background: #080808;
}

#dedicated-product.dp-page--jvs-dash {
	/* --- shared tokens, re-pointed to the JVS palette --- */
	--dp-bg:        #080808;
	--dp-text:      #f5f5f7;
	--dp-muted:     #86868b;
	--dp-accent:    #e60012;
	--dp-border:    rgba(255, 255, 255, 0.08);

	/* --- JVS-only tokens --- */
	--jvs-card-bg:      rgba(22, 22, 24, 0.75);
	/* --dist-bg-alt on the Distributors page. */
	--jvs-announce-bg:  #1a1a1a;
	--jvs-text-soft:    #a1a1a6;
	--jvs-text-notice:  #d1d5db;
	--jvs-max-width:    980px;
	/* Content column, taken from .distributors-outer: 56rem capped, 1rem of
	   padding below 640px and 2rem above. That resolves to the same 832px
	   measure the region tabs/panels occupy on the Distributors page. */
	--jvs-outer-max:    56rem;
	--jvs-radius:       20px;
	--jvs-radius-sm:    12px;
	/* Vertical travel written by js/jvs-dash.js; 0 unless the editor enabled
	   the parallax toggle, so the hero is static by default. */
	--jvs-hero-parallax-offset: 0px;
	--jvs-hero-overscan: 0px;

	line-height: 1.5;
	padding-bottom: clamp(48px, 8vw, 80px);
}

/* ==========================================================================
   Hero
   ========================================================================== */
#dedicated-product.dp-page--jvs-dash .jvs-hero {
	position: relative;
	width: 100%;
	/* Solid black, matching .distributors-hero. The style guide's red centre
	   spotlight (radial-gradient at top centre) was removed on request. */
	background: #000000;
	padding: clamp(40px, 7vw, 60px) 20px clamp(28px, 5vw, 40px);
	text-align: center;
	overflow: hidden;
	isolation: isolate;
}

/* Background image layer. Absolutely positioned and centred with auto inline
   margins rather than a translate, so `transform` stays free for the parallax
   without the two fighting over the same property. */
#dedicated-product.dp-page--jvs-dash .jvs-hero__media {
	position: absolute;
	left: 0;
	right: 0;
	margin-inline: auto;
	/* Overscan (written by JS) gives the parallax somewhere to travel without
	   exposing an edge at either extreme. */
	top: calc(-1 * var(--jvs-hero-overscan));
	bottom: calc(-1 * var(--jvs-hero-overscan));
	z-index: 0;
	pointer-events: none;
	transform: translate3d(0, var(--jvs-hero-parallax-offset), 0);
}

#dedicated-product.dp-page--jvs-dash .jvs-hero[data-jvs-parallax] .jvs-hero__media {
	will-change: transform;
}

#dedicated-product.dp-page--jvs-dash .jvs-hero__image {
	width: 100%;
	height: 100%;
	object-position: center;
	opacity: 0.55;
}

/* "Fill Banner Space" checked: the image covers the whole banner, cropping
   whatever does not fit. */
#dedicated-product.dp-page--jvs-dash .jvs-hero--media-fill .jvs-hero__image {
	object-fit: cover;
}

/* Unchecked: the whole image stays visible, letterboxed rather than cropped.
   js/jvs-dash.js also zeroes the parallax overscan in this mode — sliding a
   letterboxed image only moves it away from its own empty space. */
#dedicated-product.dp-page--jvs-dash .jvs-hero--media-contain .jvs-hero__image {
	object-fit: contain;
}

/* Dot pattern from the style guide, kept as a real element (rather than a
   ::before) so it can stack above the optional image but below the copy. */
#dedicated-product.dp-page--jvs-dash .jvs-hero__grain {
	position: absolute;
	inset: 0;
	z-index: 2;
	background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px);
	background-size: 24px 24px;
	opacity: 0.6;
	pointer-events: none;
}

/* Bottom-up fade to black. --jvs-hero-bleed-start is written inline from the
   "Hero Gradient Bleed Height (%)" field as (100 - percent), so 100% bleed
   starts the fade at the very top — identical to the Distributors overlay. */
#dedicated-product.dp-page--jvs-dash .jvs-hero__overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(
		to bottom,
		rgba(0, 0, 0, 0) 0%,
		rgba(0, 0, 0, 0) var(--jvs-hero-bleed-start, 0%),
		#000 100%
	);
	pointer-events: none;
}

#dedicated-product.dp-page--jvs-dash .jvs-hero__content {
	position: relative;
	/* Above both the image and the bleed overlay. */
	z-index: 3;
	max-width: var(--jvs-max-width);
	margin: 0 auto;
	/* Matches .distributors-hero__content (40px 24px as measured). */
	padding: 2.5rem 1.5rem;
	opacity: 1;
	transform: none;
	transition: none;
}

/* Typography lifted 1:1 from #distributors-page .distributors-hero__title.
   Verified against the live page: 48px @1440 / 32px @390, line-height 52.8px /
   35.2px, letter-spacing normal, text-shadow 0 10px 28px rgba(0,0,0,.4).

   font-weight MUST stay 400. 'Montserrat-Bold' is a single face registered
   under its own family name in style.css, so it is already bold at 400; asking
   for 700/800 makes the browser synthesise a second, heavier bold on top of it
   and the two pages stop matching. */
#dedicated-product.dp-page--jvs-dash .jvs-hero__title {
	font-family: 'Montserrat-Bold', Helvetica, Arial, sans-serif;
	font-size: clamp(2rem, 5vw, 3rem);
	font-weight: 400;
	line-height: 1.1;
	letter-spacing: normal;
	color: #ffffff;
	text-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
	margin: 0;
	/* Long JA headlines must break rather than force a horizontal scrollbar. */
	overflow-wrap: anywhere;
}

/* ==========================================================================
   Layout container
   ========================================================================== */
#dedicated-product.dp-page--jvs-dash .jvs-outer {
	max-width: var(--jvs-outer-max);
	margin: 0 auto;
	padding: 1rem;
}

@media (min-width: 640px) {
	#dedicated-product.dp-page--jvs-dash .jvs-outer {
		padding: 2rem;
	}
}

/* ==========================================================================
   Announcement
   ========================================================================== */
/* Explicit block padding owns the announcement's vertical rhythm. Rich-text
   child margins are reset below, so omitting the optional title cannot change
   the apparent top/bottom inset. */
#dedicated-product.dp-page--jvs-dash .jvs-announce {
	display: flex;
	width: 100%;
	max-width: 100%;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1rem;
	padding: 2.5rem 1.5rem;
	margin: 0 0 3rem;
	background: var(--jvs-announce-bg);
	border-left: 4px solid var(--dp-accent);
	border-radius: 0 var(--jvs-radius-sm) var(--jvs-radius-sm) 0;
	text-align: center;
}

/* One copy block does not need the full two-line announcement inset. Keep the
   title-only and description-only states deliberately compact on every axis. */
#dedicated-product.dp-page--jvs-dash .jvs-announce--single-copy {
	gap: 0.75rem;
	padding: 1rem;
}

@media (min-width: 640px) {
	#dedicated-product.dp-page--jvs-dash .jvs-announce {
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		justify-content: center;
		gap: 1rem;
	}
}

/* Mirrors .distributors-notice__text: shrink-to-fit rather than filling the
   row, so the button stays beside the copy instead of being pushed away. */
#dedicated-product.dp-page--jvs-dash .jvs-announce__body {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	flex: 0 1 auto;
	min-width: 0;
	margin: 0;
	text-align: center;
}

#dedicated-product.dp-page--jvs-dash .jvs-announce__title {
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--dp-text);
	margin: 0;
}

#dedicated-product.dp-page--jvs-dash .jvs-announce__text {
	font-size: 0.875rem;
	font-weight: 400;
	color: var(--jvs-text-notice);
	margin: 0;
}

#dedicated-product.dp-page--jvs-dash .jvs-announce__title + .jvs-announce__text {
	margin-top: 4px;
}

#dedicated-product.dp-page--jvs-dash .jvs-announce__text > :first-child {
	margin-top: 0;
}

#dedicated-product.dp-page--jvs-dash .jvs-announce__text > :last-child {
	margin-bottom: 0;
}

@media (min-width: 640px) {
	#dedicated-product.dp-page--jvs-dash .jvs-announce__title,
	#dedicated-product.dp-page--jvs-dash .jvs-announce__text {
		font-size: 1rem;
	}
}

/* ==========================================================================
   Buttons
   ========================================================================== */
/* Geometry replicated 1:1 from #distributors-page .distributors-notice__btn —
   measured on the live page: padding 12px 20px (10px 16px under 40em),
   min-height 44px, radius 8px, 2px transparent border, gap 10px, 1.05rem /
   weight 500 Montserrat-Bold, shadow 0 8px 18px rgba(3,3,3,.18).

   Declared on the base class, not per modifier: the announcement and content
   CTAs are the same button and differ only in icon size, so sharing one rule is
   what stops them drifting apart.

   The 2px border is present but transparent at rest. It has to exist at rest or
   the hover outline would add 4px to the box and shove the row on hover. */
#dedicated-product.dp-page--jvs-dash .jvs-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	max-width: min(100%, 22rem);
	min-height: 44px;
	padding: 0.75rem 1.25rem;
	gap: 0.625rem;
	background: var(--dp-accent);
	color: #ffffff;
	font-family: 'Montserrat-Bold', Helvetica, Arial, sans-serif;
	font-size: 1.05rem;
	font-weight: 500;
	line-height: var(--renewal-cta-line-height, 1.2);
	letter-spacing: var(--renewal-cta-letter-spacing, normal);
	text-transform: none;
	text-align: center;
	text-decoration: none;
	white-space: normal;
	border-radius: var(--renewal-cta-radius, 8px);
	border: var(--renewal-cta-border-width-compact, 2px) solid transparent;
	box-shadow: var(--renewal-cta-shadow, 0 8px 18px rgba(3, 3, 3, 0.18));
	/* No transform in the transition: these buttons deliberately do not lift on
	   hover, unlike the Distributors button they take their geometry from. */
	transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease,
	            box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Red fill -> white fill, with the label AND the icon flipping to red. The icon
   is drawn with fill/stroke: currentColor, so it follows `color` on its own. */
#dedicated-product.dp-page--jvs-dash .jvs-btn:hover,
#dedicated-product.dp-page--jvs-dash .jvs-btn:focus-visible {
	background: #ffffff;
	color: var(--dp-accent);
	border-color: var(--dp-accent);
	box-shadow: 0 10px 24px rgba(230, 0, 18, 0.18);
}

/* Touch devices have no hover: without this the button would stick in its
   hovered state after a tap on iOS. */
@media (hover: none) {
	#dedicated-product.dp-page--jvs-dash .jvs-btn:hover {
		background: var(--dp-accent);
		color: #ffffff;
		border-color: transparent;
		box-shadow: var(--renewal-cta-shadow, 0 8px 18px rgba(3, 3, 3, 0.18));
	}

	#dedicated-product.dp-page--jvs-dash .jvs-btn:active {
		background: #ffffff;
		color: var(--dp-accent);
		border-color: var(--dp-accent);
	}
}

@media (max-width: 40em) {
	#dedicated-product.dp-page--jvs-dash .jvs-btn {
		padding: 0.625rem 1rem;
	}
}

#dedicated-product.dp-page--jvs-dash .jvs-btn__icon {
	flex: none;
	width: 16px;
	height: 16px;
	fill: currentColor;
}

/* The only difference between the two CTAs. */
#dedicated-product.dp-page--jvs-dash .jvs-btn--primary .jvs-btn__icon {
	width: 20px;
	height: 20px;
}

/* Outline icons are drawn with a stroke and an explicit fill="none". That is a
   presentation attribute, which CSS outranks — so the rule above would flood
   the envelope solid. Restore it here rather than dropping the shared fill,
   which the solid GitHub mark needs. */
#dedicated-product.dp-page--jvs-dash .jvs-btn__icon--outline {
	fill: none;
	stroke: currentColor;
}

/* ==========================================================================
   Main content card
   ========================================================================== */
#dedicated-product.dp-page--jvs-dash .jvs-card {
	background: var(--jvs-card-bg);
	border: 1px solid var(--dp-border);
	backdrop-filter: blur(30px);
	-webkit-backdrop-filter: blur(30px);
	border-radius: var(--jvs-radius);
	padding: clamp(28px, 4vw, 48px);
	box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
}

#dedicated-product.dp-page--jvs-dash .jvs-card__header {
	margin-bottom: 24px;
}

#dedicated-product.dp-page--jvs-dash .jvs-card__title {
	font-size: clamp(1.5rem, 3.5vw, 2rem);
	font-weight: 700;
	letter-spacing: -0.01em;
	color: #ffffff;
	margin-bottom: 12px;
	overflow-wrap: anywhere;
}

#dedicated-product.dp-page--jvs-dash .jvs-card__desc {
	font-size: 1.125rem;
	font-weight: 300;
	line-height: 1.6;
	color: var(--jvs-text-soft);
}

#dedicated-product.dp-page--jvs-dash .jvs-card__desc > :first-child { margin-top: 0; }
#dedicated-product.dp-page--jvs-dash .jvs-card__desc > :last-child  { margin-bottom: 0; }

#dedicated-product.dp-page--jvs-dash .jvs-card__desc p {
	margin: 0 0 0.75em;
}

/* Restore the native editor's heading rhythm inside this rich-text field.
   dedicated-product.css resets every heading margin to zero for component
   headings, which made author-entered rows disappear in published content. */
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h1,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h2,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h3,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h4,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h5,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc h6 {
	margin: 1em 0;
}

#dedicated-product.dp-page--jvs-dash .jvs-card__desc ul,
#dedicated-product.dp-page--jvs-dash .jvs-card__desc ol {
	margin: 0 0 0.75em;
	padding-left: 1.4em;
}

#dedicated-product.dp-page--jvs-dash .jvs-card__desc li {
	margin-bottom: 0.4em;
}

#dedicated-product.dp-page--jvs-dash .jvs-card__desc a {
	color: var(--dp-accent);
	text-decoration: underline;
}

/* ==========================================================================
   Feature media
   ========================================================================== */
#dedicated-product.dp-page--jvs-dash .jvs-media__image {
	display: block;
	width: 100%;
	height: auto;
	margin: clamp(24px, 4vw, 36px) 0;
	-webkit-user-drag: none;
	user-select: none;
}

/* Defensive layer for lightbox plugins that decorate editor HTML after the
   template has stripped image links server-side. */
#dedicated-product.dp-page--jvs-dash .jvs-card__desc a:has(img) {
	pointer-events: none;
	cursor: default;
}

/* ==========================================================================
   Action row
   ========================================================================== */
#dedicated-product.dp-page--jvs-dash .jvs-card__action {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 20px;
	padding-top: 24px;
}

/* The I/O Board section uses the About Products component and assets directly.
   Keep its source full-width rail while the earlier JVS content remains capped. */
#dedicated-product.dp-page--jvs-dash .jvs-io-boards {
	/* The source page adds 4rem before every carousel. Here the carousel follows
	   the Content card directly, so remove those two 2rem spacing rows. The
	   Content wrapper gutter and carousel's own 2rem inset remain intact. */
	margin-top: 0;
}

#dedicated-product.dp-page--jvs-dash .jvs-io-boards .exa-slider {
	width: 100%;
	max-width: none;
	margin-inline: 0;
}

/* dedicated-product.css deliberately normalizes every heading/link inside its
   root. Restore the source component's more specific typography and CTA states. */
#dedicated-product.dp-page--jvs-dash .jvs-io-boards .exa-carousel__heading,
#dedicated-product.dp-page--jvs-dash .jvs-io-boards .exa-accessory-card__title {
	font-family: 'Montserrat-Bold', Helvetica, Arial, sans-serif;
}

#dedicated-product.dp-page--jvs-dash .jvs-io-boards .exa-carousel__heading {
	line-height: 1.4;
}

#dedicated-product.dp-page--jvs-dash .jvs-io-boards .exa-accessory-card__title {
	margin: 0 0 0.5rem;
	line-height: 1.35;
}

#dedicated-product.dp-page--jvs-dash .jvs-io-boards a.exa-shop-cta {
	color: #ffffff;
}

#dedicated-product.dp-page--jvs-dash .jvs-io-boards a.exa-shop-cta:hover,
#dedicated-product.dp-page--jvs-dash .jvs-io-boards a.exa-shop-cta:focus-visible {
	color: #d90500;
}

/* ==========================================================================
   Content card — staggered entrance
   ==========================================================================
   The card's header, image and CTA cascade in 90ms apart instead of arriving
   together. 90ms is the step the shared engine already uses for the DP features
   grid (js/dedicated-product.js), kept identical so this reads the same.

   Driven off the CARD's own .is-in-view, not by marking each child .dp-reveal.
   Marking them was tried and is wrong: the shared observer watches every
   .dp-reveal individually, and this card is taller than the observer's root
   band, so each child crossed it at a different SCROLL position — the image and
   CTA simply had not entered yet while the header played, and the delays below
   did nothing. It is the same fault js/dedicated-product.js documents for
   carousel rails, and the same fix: reveal the group when its container
   appears. It also keeps the cascade pure CSS, with nothing added to the
   shared observer's workload.

   Wrapped in the same prefers-reduced-motion guard as the base .dp-reveal
   rules. Without it a reduced-motion visitor would get the offsets with no
   animation to resolve them, leaving the contents displaced and invisible. */
@media (prefers-reduced-motion: no-preference) {

	/* The parent card is already travelling 48px. Inheriting another 48px would
	   compound to ~96px and visibly throw the contents past the card's own edges
	   mid-entrance, so the children rise a shorter distance. */
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal .jvs-card__header,
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal .jvs-media__image,
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal .jvs-card__action {
		opacity: 0;
		transform: translate3d(0, 16px, 0);
		transition: opacity .9s cubic-bezier(.2, .65, .2, 1),
		            transform .9s cubic-bezier(.2, .65, .2, 1);
	}

	/* Settling to `none` rather than translate3d(0,0,0): an identity transform
	   still promotes a composited layer, and the card's image would then be
	   GPU-resampled for the life of the page. Same reason the base .dp-reveal
	   rule in css/dedicated-product.css settles to none. */
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-card__header,
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-media__image,
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-card__action {
		opacity: 1;
		transform: none;
	}

	/* Delays sit on the entrance only, so the card fades out as one piece
	   instead of unwinding backwards on the way up. */
	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-card__header {
		transition-delay: 0ms;
	}

	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-media__image {
		transition-delay: 90ms;
	}

	#dedicated-product.dp-page--jvs-dash .jvs-card.dp-reveal.is-in-view .jvs-card__action {
		transition-delay: 180ms;
	}
}

/* ==========================================================================
   Responsive
   ========================================================================== */
/* The announcement bar is not listed here: it stacks at its own 640px
   breakpoint, matching .distributors-notice__inner. A second rule at 768px
   would override that and leave it stacked across the 640-768px band where
   Distributors has already gone to a row. */
@media (max-width: 768px) {
	/* The CTA stays centred at its own width rather than stretching, so the
	   22rem cap and the pill geometry survive on narrow screens. */
	#dedicated-product.dp-page--jvs-dash .jvs-card__action {
		flex-direction: column;
		align-items: center;
		text-align: center;
	}

	#dedicated-product.dp-page--jvs-dash .jvs-btn {
		justify-content: center;
	}
}

/* Tall portrait displays (9:16 kiosks / vertical monitors). The 56rem column
   already fits, but the hero reads squat against that much height, so give it
   a viewport-proportional block padding instead of the fixed clamp. */
@media (orientation: portrait) and (min-height: 900px) {
	#dedicated-product.dp-page--jvs-dash .jvs-hero {
		padding-block: clamp(60px, 9svh, 140px) clamp(40px, 6svh, 96px);
	}
}

/* The parallax listener already bails on reduced motion; neutralise the
   inherited custom property too, so nothing is left mid-travel if the
   preference flips after the offset was written. */
@media (prefers-reduced-motion: reduce) {
	#dedicated-product.dp-page--jvs-dash .jvs-hero__media {
		--jvs-hero-parallax-offset: 0px;
		--jvs-hero-overscan: 0px;
		will-change: auto;
	}
}
