.hp-heartbeat {
	overflow: hidden;
	background: #fbfaff;
	color: var(--hp-color-text);
}

.hp-heartbeat__shell {
	width: min(1180px, calc(100% - 40px));
	margin-inline: auto;
}

.hp-heartbeat__hero {
	position: relative;
	overflow: hidden;
	/* Owner directive (2026-08-14): the complete hero + radar must fit a
	 * normal desktop first viewport (1366x768 up to 1920x1080) without
	 * scrolling. vh-aware clamps here (and on the eyebrow/intro/promises/
	 * ambient/radar rules below) shrink together on short viewports and
	 * relax on tall ones, rather than one fixed height tuned to a single
	 * screen size. */
	padding: clamp(18px, 3vh, 36px) 0 clamp(20px, 3vh, 32px);
	background:
		radial-gradient(circle at 50% 54%, rgba(130, 75, 246, .16), transparent 31%),
		radial-gradient(circle at 12% 24%, rgba(209, 194, 255, .34), transparent 22%),
		linear-gradient(180deg, #fff 0%, #faf8ff 48%, #f3efff 100%);
	text-align: center;
}

.hp-heartbeat__hero::before,
.hp-heartbeat__hero::after {
	position: absolute;
	border-radius: 50%;
	content: "";
	filter: blur(2px);
	pointer-events: none;
}

.hp-heartbeat__hero::before {
	top: 22%;
	left: -140px;
	width: 360px;
	height: 360px;
	background: rgba(219, 207, 255, .28);
}

.hp-heartbeat__hero::after {
	right: -110px;
	bottom: -130px;
	width: 320px;
	height: 320px;
	background: rgba(238, 231, 255, .66);
}

/*
 * Owner directive (2026-08-15), item 11: the hero's own ::before/::after
 * glow circles get clipped in a dead-straight line by `overflow: hidden`
 * wherever they cross the hero's bottom edge -- exactly the "harsh
 * straight line" complaint. This sits in normal flow behind the shell
 * (see z-index below) and dissolves the last stretch of the hero into
 * the page's own base colour, so the lavender atmosphere fades rather
 * than cuts off. Real DOM element, not a third pseudo-element, so it can
 * carry its own z-index independent of ::before/::after's stacking.
 */
.hp-heartbeat__hero-fade {
	position: absolute;
	z-index: 1;
	right: 0;
	bottom: 0;
	left: 0;
	height: 130px;
	background: linear-gradient(180deg, rgba(251, 250, 255, 0) 0%, #fbfaff 82%);
	pointer-events: none;
}

.hp-heartbeat__hero .hp-heartbeat__shell {
	position: relative;
	z-index: 2;
}

.hp-heartbeat__eyebrow,
.hp-heartbeat__section-heading p {
	margin: 0 0 6px;
	color: var(--hp-color-primary-600);
	font-size: .8rem;
	font-weight: 850;
	letter-spacing: .13em;
	text-transform: uppercase;
}

.hp-heartbeat h1 {
	margin: 0;
	/* Was clamp(2.55rem, 5.7vw, 5rem) -- at most desktop widths this hit
	 * (or nearly hit) its 5rem/80px ceiling and wrapped the title to two
	 * lines, alone costing more vertical space than the whole rest of the
	 * hero combined. Still a large, deliberate display size -- just no
	 * longer the single biggest contributor to the hero overflowing a
	 * normal viewport. */
	font-size: clamp(2.1rem, 4vw, 3.4rem);
	line-height: 1.08;
	letter-spacing: -.045em;
}

.hp-heartbeat__tagline {
	margin: 3px 0 0;
	color: var(--hp-color-primary-700);
	font-size: clamp(1.05rem, 2.1vw, 1.3rem);
	font-weight: 750;
	letter-spacing: -.01em;
}

.hp-heartbeat__intro {
	max-width: 720px;
	margin: clamp(4px, 1vh, 10px) auto 0;
	color: var(--hp-color-text-muted);
	font-size: clamp(1rem, 2vw, 1.1rem);
	line-height: 1.55;
}

/*
 * Owner directive (2026-08-15), item 4: was 3 identical rectangle cards
 * (the "generic 3-column grid" complaint) -- now a slim inline pill
 * strip. Each pill's width is driven purely by its own label+detail
 * text length, so the strip reads as organically shaped rather than
 * three equal boxes, while keeping the exact same three facts visible
 * (nothing removed, just recomposed).
 */
.hp-heartbeat-trust {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px 10px;
	max-width: 1100px;
	margin: clamp(10px, 1.8vh, 16px) auto 0;
	padding: 0;
	list-style: none;
}

.hp-heartbeat-trust li {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 7px 15px 7px 11px;
	border: 1px solid rgba(124, 58, 237, .12);
	border-radius: 999px;
	background: rgba(255, 255, 255, .72);
	box-shadow: 0 8px 22px rgba(67, 38, 119, .05);
	color: #211537;
	font-size: .78rem;
	white-space: nowrap;
	backdrop-filter: blur(10px);
}

.hp-heartbeat-trust i {
	width: 6px;
	height: 6px;
	flex: 0 0 auto;
	border-radius: 50%;
	background: #7c3aed;
	box-shadow: 0 0 0 4px rgba(124, 58, 237, .1);
	animation: hp-heartbeat-dot-pulse 2.6s ease-in-out infinite;
}

@keyframes hp-heartbeat-dot-pulse {
	0%, 100% { box-shadow: 0 0 0 4px rgba(124, 58, 237, .1); }
	50% { box-shadow: 0 0 0 7px rgba(124, 58, 237, .16); }
}

.hp-heartbeat-trust strong { font-weight: 800; }

.hp-heartbeat-trust span {
	color: var(--hp-color-text-muted);
	font-weight: 600;
}

.hp-heartbeat-trust span::before { content: "\2014\00a0"; }

/*
 * Owner directive (2026-08-14): ambient hierarchy redesign -- was 4 flex
 * pills with nowrap content, which forced each one wide enough to hold
 * its full label+detail on one line, so they stacked into a plain
 * telemetry-text column instead of reading as distinct cards. Now a row
 * of fixed-width icon+text tiles (matching the node cards' own visual
 * language), each independently sized so 3-4 sit on one row on desktop.
 */
.hp-heartbeat-ambient {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px;
	max-width: 920px;
	margin: clamp(4px, 0.8vh, 8px) auto 0;
}

.hp-heartbeat-ambient__chip {
	display: block;
	width: 272px;
	padding: 10px 15px;
	border: 1px solid rgba(124, 58, 237, .12);
	border-radius: 16px;
	transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, border-radius .18s ease;
	background: rgba(255, 255, 255, .82);
	box-shadow: 0 10px 26px rgba(67, 38, 119, .07);
	backdrop-filter: blur(12px);
	text-align: left;
	text-decoration: none;
	color: inherit;
}

/* Owner directive (2026-08-15), item 4: the freshness badge and the
 * community badge read as pills (echoing the central heart in
 * miniature) while weather/sunset stay rounded rectangles -- genuine
 * shape variety instead of four identical tiles. Once a pill chip is
 * expanded (2-3 lines of real content, not 1), a true stadium radius
 * leaves an oddly generous curve of empty space under the last line --
 * settle to a normal rounded-rect radius while [open] so the extra
 * height reads as "a card with more in it," not "a pill with a gap".
 */
.hp-heartbeat-ambient__chip--pulse,
.hp-heartbeat-ambient__chip--community { padding-left: 18px; border-radius: 999px; }
.hp-heartbeat-ambient__chip--pulse[open] { padding-left: 15px; border-radius: 22px; }

.hp-heartbeat-ambient__chip summary,
.hp-heartbeat-ambient__chip--community {
	display: flex;
	align-items: center;
	gap: 10px;
	cursor: pointer;
	list-style: none;
}

.hp-heartbeat-ambient__chip summary::-webkit-details-marker { display: none; }
.hp-heartbeat-ambient__chip summary::marker { content: ""; }

/* Owner directive (2026-08-15), item 6: every chip that looks like a
 * dashboard component now behaves like one -- hover lift, category-
 * tinted glow and an icon reaction, on ALL four chips, not just one. */
.hp-heartbeat-ambient__chip:hover,
.hp-heartbeat-ambient__chip:focus-within {
	transform: translateY(-2px);
	border-color: rgba(124, 58, 237, .3);
	box-shadow: 0 16px 36px rgba(67, 38, 119, .14);
}

.hp-heartbeat-ambient__chip:hover .hp-heartbeat-ambient__icon,
.hp-heartbeat-ambient__chip:focus-within .hp-heartbeat-ambient__icon {
	transform: scale(1.08);
}

.hp-heartbeat-ambient__icon {
	display: grid;
	flex: 0 0 auto;
	width: 32px;
	height: 32px;
	place-items: center;
	border-radius: 10px;
	background: #f1eaff;
	color: #7c3aed;
	transition: transform .2s ease;
}

.hp-heartbeat-ambient__icon svg {
	width: 17px;
	height: 17px;
}

.hp-heartbeat-ambient__icon--photo {
	overflow: hidden;
	border-radius: 50%;
	background: #fff;
}

.hp-heartbeat-ambient__icon--photo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.hp-heartbeat-ambient__body {
	display: block;
	min-width: 0;
	flex: 1;
}

.hp-heartbeat-ambient__body strong {
	display: block;
	overflow: hidden;
	color: #211537;
	font-size: .82rem;
	font-weight: 800;
	line-height: 1.2;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.hp-heartbeat-ambient__body small {
	display: block;
	overflow: hidden;
	margin-top: 1px;
	color: var(--hp-color-text-muted);
	font-size: .68rem;
	font-weight: 600;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.hp-heartbeat-ambient__chevron {
	display: grid;
	flex: 0 0 auto;
	width: 12px;
	height: 16px;
	margin-left: auto;
	place-items: center;
	color: rgba(124, 58, 237, .4);
}

.hp-heartbeat-ambient__chevron::before {
	width: 6px;
	height: 6px;
	margin-top: -3px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	content: "";
	transform: rotate(45deg);
	transition: transform .2s ease;
}

details[open] .hp-heartbeat-ambient__chevron::before { transform: rotate(-135deg); margin-top: 2px; }

.hp-heartbeat-ambient__reveal {
	margin: 8px 0 1px;
	padding-top: 8px;
	border-top: 1px dashed rgba(124, 58, 237, .18);
	color: var(--hp-color-text-muted);
	font-size: .72rem;
	font-weight: 600;
	line-height: 1.4;
	white-space: normal;
}

/* Owner directive (2026-08-15), item 3: weather=blue, sunset=warm coral,
 * intelligence=green (unchanged), community=magenta -- the same small
 * category palette as the radar nodes below, so colour itself signals
 * "this belongs to that category" consistently across the whole page. */
.hp-heartbeat-ambient__chip--weather .hp-heartbeat-ambient__icon { background: #eaf2ff; color: #2563eb; }
.hp-heartbeat-ambient__chip--sunset .hp-heartbeat-ambient__icon { background: #fff1e8; color: #ea580c; }
.hp-heartbeat-ambient__chip--pulse .hp-heartbeat-ambient__icon { background: #ebfaf3; color: #16865b; }
.hp-heartbeat-ambient__chip--community .hp-heartbeat-ambient__icon { background: #fdf0fa; color: #c026d3; }

.hp-heartbeat-ambient__chip--weather:hover,
.hp-heartbeat-ambient__chip--weather:focus-within { border-color: rgba(37, 99, 235, .32); box-shadow: 0 16px 36px rgba(37, 99, 235, .15); }
.hp-heartbeat-ambient__chip--sunset:hover,
.hp-heartbeat-ambient__chip--sunset:focus-within { border-color: rgba(234, 88, 12, .32); box-shadow: 0 16px 36px rgba(234, 88, 12, .15); }
.hp-heartbeat-ambient__chip--pulse:hover,
.hp-heartbeat-ambient__chip--pulse:focus-within { border-color: rgba(22, 134, 91, .32); box-shadow: 0 16px 36px rgba(22, 134, 91, .15); }
.hp-heartbeat-ambient__chip--community:hover,
.hp-heartbeat-ambient__chip--community:focus-visible { border-color: rgba(192, 38, 212, .32); box-shadow: 0 16px 36px rgba(192, 38, 212, .15); }

.hp-heartbeat-ambient__chip--pulse-live .hp-heartbeat-ambient__icon {
	animation: hp-heartbeat-pulse-icon 2.2s ease-in-out infinite;
}

@keyframes hp-heartbeat-pulse-icon {
	0%, 100% { box-shadow: 0 0 0 0 rgba(22, 134, 91, .22); }
	50% { box-shadow: 0 0 0 6px rgba(22, 134, 91, 0); }
}

/* Owner directive (2026-08-15), item 9: one restrained connector -- a
 * small dot travelling down a soft line from the ambient row toward
 * the radar -- suggesting everything feeds into the central heart
 * without turning into a sci-fi HUD. */
.hp-heartbeat-connector {
	position: relative;
	width: 2px;
	height: clamp(14px, 2.2vh, 24px);
	margin: 2px auto 0;
	background: linear-gradient(180deg, rgba(124, 58, 237, .3), rgba(124, 58, 237, .02));
	border-radius: 2px;
}

.hp-heartbeat-connector::after {
	position: absolute;
	top: 0;
	left: 50%;
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: #8b5cf6;
	box-shadow: 0 0 8px rgba(124, 58, 237, .5);
	content: "";
	transform: translateX(-50%);
	animation: hp-heartbeat-connector-flow 2.8s ease-in-out infinite;
}

@keyframes hp-heartbeat-connector-flow {
	0% { top: 0; opacity: 0; }
	18% { opacity: 1; }
	82% { opacity: 1; }
	100% { top: 100%; opacity: 0; }
}

.hp-heartbeat-radar {
	position: relative;
	display: grid;
	width: min(920px, 100%);
	/* vh-driven so the radar itself shrinks on short viewports (1366x768)
	 * and relaxes on tall ones (1920x1080) instead of a single fixed
	 * height. Floor of 400px keeps the 4 corner .hp-heartbeat-node cards
	 * (top:13%/41%, bottom:5%, ~83px tall each) clear of each other --
	 * checked against this exact geometry, not just eyeballed. */
	min-height: clamp(400px, 52vh, 480px);
	margin: clamp(2px, 0.4vh, 6px) auto 0;
	place-items: center;
}

.hp-heartbeat-radar__grid {
	position: absolute;
	inset: 3% 7% 9%;
	border-radius: 44px;
	background-image: radial-gradient(rgba(124, 58, 237, .15) 1px, transparent 1px);
	background-size: 18px 18px;
	mask-image: radial-gradient(circle at center, #000 8%, transparent 67%);
	opacity: .58;
}

/*
 * Owner directive (2026-08-15): the mobile-only wrapper introduced
 * around signal-state + location (.hp-heartbeat-radar__status) has no
 * desktop rule of its own, so it defaulted to a normal display:block
 * box -- a real (if empty) grid item next to the heart in the radar's
 * single implicit desktop grid cell, which pushed the heart down/off-
 * centre and made the whole radar read as oversized and sparse. This
 * is the actual desktop regression. display:contents removes the
 * wrapper from the box tree entirely on desktop -- its children keep
 * their own real, independent position:absolute placement exactly as
 * before this wrapper existed. The mobile media query already
 * overrides this back to a real flex box for its own layout.
 */
.hp-heartbeat-radar__status { display: contents; }

.hp-heartbeat-radar__signal-state {
	position: absolute;
	z-index: 4;
	top: 1px;
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0;
	padding: 8px 13px;
	border: 1px solid rgba(124,58,237,.12);
	border-radius: 999px;
	background: rgba(255,255,255,.88);
	box-shadow: 0 10px 28px rgba(55,31,99,.08);
	color: #5b3b86;
	font-size: .72rem;
	font-weight: 800;
	backdrop-filter: blur(12px);
}

.hp-heartbeat-radar__signal-state i {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: #a78bfa;
	box-shadow: 0 0 0 5px rgba(167,139,250,.12);
}

.hp-heartbeat-radar--active .hp-heartbeat-radar__signal-state i {
	background: #22c55e;
	box-shadow: 0 0 0 5px rgba(34,197,94,.12);
	animation: hp-heartbeat-signal 1.8s ease-out infinite;
}

.hp-heartbeat-radar__rings {
	position: absolute;
	inset: 11% 29%;
	border: 1px solid rgba(124, 58, 237, .2);
	border-radius: 50%;
	/*
	 * Owner fix (2026-08-19): measured live against the real page --
	 * .hp-heartbeat__hero (the deliberately viewport-height-budgeted
	 * container per the 2026-08-14 "must fit one screen, no scrolling"
	 * directive) has overflow:hidden and sits only ~71-73px below this
	 * ring's own box at typical desktop heights. The outermost ring's old
	 * 114px spread overshot that by ~40px, so its bottom arc was hard-
	 * clipped in a straight line -- exactly the cut-off the owner
	 * screenshotted. Top/left/right all had ample room; only the bottom
	 * was short. Scaled the whole ring set down to a 62px max (safely
	 * under the ~71px budget with real margin, including the breathe
	 * animation's +1.5% peak scale) rather than growing the hero, which
	 * would risk reintroducing the scrolling the owner already fixed once.
	 */
	box-shadow:
		0 0 0 22px rgba(124, 58, 237, .055),
		0 0 0 44px rgba(124, 58, 237, .038),
		0 0 0 62px rgba(124, 58, 237, .022),
		0 28px 80px rgba(105, 54, 198, .14);
	animation: hp-heartbeat-breathe 5s ease-in-out infinite;
}

.hp-heartbeat-radar__rings::before,
.hp-heartbeat-radar__rings::after {
	position: absolute;
	left: 50%;
	width: 260px;
	height: 1px;
	background: linear-gradient(90deg, transparent, rgba(124, 58, 237, .3), transparent);
	content: "";
	transform: translateX(-50%);
}

.hp-heartbeat-radar__rings::before { top: 20%; }
.hp-heartbeat-radar__rings::after { bottom: 20%; }

.hp-heartbeat-radar__heart {
	position: relative;
	z-index: 3;
	display: grid;
	width: 164px;
	height: 164px;
	place-items: center;
	border: 1px solid rgba(124, 58, 237, .18);
	border-radius: 50%;
	background: rgba(255, 255, 255, .94);
	box-shadow: 0 24px 72px rgba(80, 42, 150, .2), inset 0 0 30px rgba(135, 86, 245, .06);
	color: inherit;
	text-decoration: none;
	animation: hp-heartbeat-lubdub 2.4s ease-in-out infinite;
	transition: transform .35s cubic-bezier(.2,.8,.3,1), box-shadow .35s ease, border-color .35s ease;
}

.hp-heartbeat-radar__halo {
	position: absolute;
	inset: -17px;
	z-index: -1;
	border: 1px solid rgba(124, 58, 237, .16);
	border-radius: 50%;
	background: radial-gradient(circle, rgba(255,255,255,.12), rgba(124,58,237,.05));
	box-shadow: 0 0 44px rgba(124, 58, 237, .12);
}

.hp-heartbeat-radar__heart img {
	width: 82px;
	height: 82px;
	object-fit: contain;
	filter: drop-shadow(0 10px 16px rgba(97, 46, 190, .24));
}

.hp-heartbeat-radar__heart > span:last-child {
	margin-top: -25px;
	color: var(--hp-color-primary-700);
	font-size: .72rem;
	font-weight: 850;
	letter-spacing: .1em;
	text-transform: uppercase;
}

.hp-heartbeat-radar__heart:hover,
.hp-heartbeat-radar__heart:focus-visible {
	border-color: rgba(124,58,237,.42);
	box-shadow: 0 30px 92px rgba(80,42,150,.32), inset 0 0 48px rgba(135,86,245,.15), 0 0 0 16px rgba(124,58,237,.04);
	animation: hp-heartbeat-lubdub-live 1.08s ease-in-out infinite;
}

.hp-heartbeat-radar__heart:hover .hp-heartbeat-radar__halo,
.hp-heartbeat-radar__heart:focus-visible .hp-heartbeat-radar__halo {
	box-shadow: 0 0 58px rgba(124,58,237,.26), 0 0 0 28px rgba(124,58,237,.04), 0 0 0 56px rgba(124,58,237,.02);
	transform: scale(1.06);
}

.hp-heartbeat-node {
	--hp-heartbeat-node-rgb: 124, 58, 237;
	position: absolute;
	z-index: 2;
	display: flex;
	align-items: center;
	gap: 13px;
	min-width: 190px;
	padding: 15px 17px;
	border: 1px solid rgba(124, 58, 237, .14);
	border-radius: 21px;
	background: rgba(255, 255, 255, .9);
	box-shadow: 0 18px 46px rgba(43, 26, 80, .09);
	text-align: left;
	text-decoration: none;
	backdrop-filter: blur(14px);
	transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
}

.hp-heartbeat-node:hover,
.hp-heartbeat-node:focus-visible {
	border-color: rgba(124,58,237,.42);
	background: linear-gradient(145deg, rgba(255,255,255,.98), rgba(244,237,255,.98));
	box-shadow: 0 26px 62px rgba(70,35,132,.2), 0 0 0 5px rgba(124,58,237,.04);
	transform: translateY(-5px) scale(1.025);
}

.hp-heartbeat-node:hover > i,
.hp-heartbeat-node:focus-visible > i { background: #7c3aed; box-shadow: 0 10px 28px rgba(92,43,180,.24); color: #fff; transform: scale(1.06); }
.hp-heartbeat-node > i { transition: transform .25s ease, background .25s ease, color .25s ease, box-shadow .25s ease; }
.hp-heartbeat-node:hover::after,
.hp-heartbeat-node:focus-visible::after { background: linear-gradient(90deg, rgba(124,58,237,.72), rgba(124,58,237,.08)); box-shadow: 0 0 9px rgba(124,58,237,.3); }

.hp-heartbeat-node::after {
	position: absolute;
	top: 50%;
	width: clamp(50px, 9vw, 112px);
	height: 1px;
	background: linear-gradient(90deg, rgba(var(--hp-heartbeat-node-rgb), .4), transparent);
	content: "";
}

.hp-heartbeat-node--one::after,
.hp-heartbeat-node--three::after,
.hp-heartbeat-node--five::after { left: 100%; }
.hp-heartbeat-node--two::after,
.hp-heartbeat-node--four::after,
.hp-heartbeat-node--six::after { right: 100%; transform: rotate(180deg); }

.hp-heartbeat-node > i {
	display: grid;
	flex: 0 0 auto;
	width: 38px;
	height: 38px;
	place-items: center;
	border-radius: 13px;
	background: rgba(var(--hp-heartbeat-node-rgb), .1);
	box-shadow: 0 8px 22px rgba(90,44,170,.11);
	color: rgb(var(--hp-heartbeat-node-rgb));
}

.hp-heartbeat-node > i svg { width: 19px; height: 19px; }

.hp-heartbeat-node > span {
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: baseline;
	column-gap: 8px;
}

.hp-heartbeat-node b {
	grid-column: 1 / -1;
	color: #241638;
	font-size: .83rem;
}

.hp-heartbeat-node strong {
	color: var(--hp-color-primary-700);
	font-size: 1.55rem;
	line-height: 1.15;
}

.hp-heartbeat-node small {
	color: var(--hp-color-text-muted);
	font-size: .72rem;
}

/* Owner enhancement (2026-08-20): "Next up nearby" reuses the <strong> slot
   (normally a short number, styled large) for a real event title, which can
   run much longer than "128" -- a smaller size and a 2-line clamp keep it
   from overpowering the card or forcing an awkward reflow, while still
   reading as the card's headline fact.
   Real bug found (2026-08-20), confirmed via computed style: this rule's
   font-size never actually applied -- `.hp-heartbeat-node strong` (a class
   + element selector, specificity 0-1-1) is numerically more specific than
   this single-class selector (0-1-0) and always won regardless of source
   order, so the title rendered at the base 1.55rem the whole time. That's
   also why the node was still oversized even after the width fix below:
   two 1.55rem lines are far taller than two 1rem lines. Qualifying with the
   parent class (0-2-0) fixes it for real. */
.hp-heartbeat-node .hp-heartbeat-node__event-title {
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-size: 1rem;
	line-height: 1.25;
}

/* Owner enhancement (2026-08-20): confirmed live -- the card's default grid
   (b spans both columns; strong/small then auto-place into columns 1/2 of
   the SAME row, correct for a short count+descriptor pair like "128" +
   "upcoming") put the event title and its "Today · 4:00pm · Stevenage"
   detail line side by side instead of stacked, clipping the detail line at
   the card edge. This variant's title and detail both need the full row. */
.hp-heartbeat-node--variant-next_up_nearby > span > strong,
.hp-heartbeat-node--variant-next_up_nearby > span > small {
	grid-column: 1 / -1;
}

/* Owner enhancement (2026-08-20): measured live -- this variant's title
   (unlike every other node's short number+word pair) had no width limit, so
   the box grew as wide as the longest event title needed (up to 391px vs
   204-259px for its five siblings), breaking the radar's symmetric hexagon.
   Capping width to the widest sibling ("Businesses", 259px) restores that
   symmetry; the existing 2-line clamp on the title absorbs the rest. */
.hp-heartbeat-node--variant-next_up_nearby {
	max-width: 260px;
}

/* Owner follow-up (2026-08-20): "match the businesses one opposite it...
   so it looks symmetrical" -- width already matched (260px vs Businesses'
   259px) but height was still 128px vs Businesses' 80px, since this
   variant genuinely carries one more line of information (label + title +
   date/time/town vs Businesses' label + number + word). Clamping the
   title to 1 line (was 2 -- most real event titles fit on one line at this
   width anyway, per live content) and tightening this variant's own
   padding/margins closes nearly all of the remaining gap without dropping
   any of the three pieces of information. */
.hp-heartbeat-node--variant-next_up_nearby {
	padding-block: 11px;
}
.hp-heartbeat-node--variant-next_up_nearby > span > small {
	margin-top: 0;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Owner directive (2026-08-15), item 4: real count + short context word
 * (or an honest CTA when the count is genuinely zero) -- mobile-only,
 * desktop keeps its existing count+descriptor pair unchanged. */
.hp-heartbeat-node__adaptive { display: none; }

.hp-heartbeat-node--one { top: 13%; left: 0; }
.hp-heartbeat-node--two { top: 13%; right: 0; }
.hp-heartbeat-node--three { top: 41%; left: -4%; }
.hp-heartbeat-node--four { top: 41%; right: -4%; }
.hp-heartbeat-node--five { bottom: 5%; left: 0; }
.hp-heartbeat-node--six { right: 0; bottom: 5%; }

/* Subject colour belongs to the subject, not whichever of the six stable
 * radar positions it currently occupies. AUTO can therefore suppress an
 * empty product without making the remaining subjects change identity. */
.hp-heartbeat-node--subject-whats_on { --hp-heartbeat-node-rgb: 124, 58, 237; }
.hp-heartbeat-node--subject-local_updates { --hp-heartbeat-node-rgb: 22, 134, 91; }
.hp-heartbeat-node--subject-businesses { --hp-heartbeat-node-rgb: 37, 99, 235; }
.hp-heartbeat-node--subject-roads_travel { --hp-heartbeat-node-rgb: 14, 116, 144; }
.hp-heartbeat-node--subject-consultations { --hp-heartbeat-node-rgb: 109, 40, 217; }
.hp-heartbeat-node--subject-environment { --hp-heartbeat-node-rgb: 5, 150, 105; }
.hp-heartbeat-node--subject-community { --hp-heartbeat-node-rgb: 192, 38, 211; }
.hp-heartbeat-node--subject-volunteering { --hp-heartbeat-node-rgb: 168, 85, 110; }
.hp-heartbeat-node--subject-hertsoffers,
.hp-heartbeat-node--subject-hertsjobs { --hp-heartbeat-node-rgb: 217, 119, 6; }
/* Owner enhancement (2026-08-20): "Fresh today" (warm/energetic -- new
   activity) and "Next up nearby" (forward-looking -- what's coming) get
   their own distinct accents, same convention as every subject above. */
.hp-heartbeat-node--subject-fresh_today { --hp-heartbeat-node-rgb: 234, 88, 12; }
.hp-heartbeat-node--subject-next_up_nearby { --hp-heartbeat-node-rgb: 79, 70, 229; }

/* Owner directive (2026-08-15), item 3/9: each node's own connecting
 * line picks up its category colour at rest -- amplifies the colour
 * system and doubles as a subtle "this is what's feeding the heart"
 * connectivity cue. Hover still lights every line brand-purple (see
 * the shared :hover rule above) as the "activation" moment. */
.hp-heartbeat-node::after { background: linear-gradient(90deg, rgba(var(--hp-heartbeat-node-rgb), .38), transparent); }

/* Preserve the approved six-position radar while balancing a smaller AUTO
 * set. The central heart/rings/status remain untouched. */
@media (min-width: 761px) {
	.hp-heartbeat-radar[data-subject-count="1"] .hp-heartbeat-node--one,
	.hp-heartbeat-radar[data-subject-count="3"] .hp-heartbeat-node--three,
	.hp-heartbeat-radar[data-subject-count="5"] .hp-heartbeat-node--five {
		top: auto;
		right: auto;
		bottom: 14%;
		left: 50%;
		transform: translateX(-50%);
	}
	.hp-heartbeat-radar[data-subject-count="1"] .hp-heartbeat-node--one:hover,
	.hp-heartbeat-radar[data-subject-count="1"] .hp-heartbeat-node--one:focus-visible,
	.hp-heartbeat-radar[data-subject-count="3"] .hp-heartbeat-node--three:hover,
	.hp-heartbeat-radar[data-subject-count="3"] .hp-heartbeat-node--three:focus-visible,
	.hp-heartbeat-radar[data-subject-count="5"] .hp-heartbeat-node--five:hover,
	.hp-heartbeat-radar[data-subject-count="5"] .hp-heartbeat-node--five:focus-visible {
		transform: translateX(-50%) translateY(-5px) scale(1.025);
	}
	.hp-heartbeat-radar[data-subject-count="1"] .hp-heartbeat-node--one::after,
	.hp-heartbeat-radar[data-subject-count="3"] .hp-heartbeat-node--three::after,
	.hp-heartbeat-radar[data-subject-count="5"] .hp-heartbeat-node--five::after { display: none; }

	.hp-heartbeat-radar[data-subject-count="4"] .hp-heartbeat-node--three {
		top: auto;
		bottom: 5%;
		left: 0;
	}
	.hp-heartbeat-radar[data-subject-count="4"] .hp-heartbeat-node--four {
		top: auto;
		right: 0;
		bottom: 5%;
	}
}

.hp-heartbeat-radar__location {
	position: absolute;
	z-index: 4;
	bottom: 16px;
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 10px 15px;
	border: 1px solid rgba(124,58,237,.12);
	border-radius: 999px;
	background: rgba(255,255,255,.94);
	box-shadow: 0 12px 34px rgba(57,33,99,.1);
	color: #2b1b48;
	font-size: .85rem;
	font-weight: 800;
}

.hp-heartbeat-radar__location > span { color: #22c55e; font-size: .62rem; }
.hp-heartbeat-radar__location small { color: var(--hp-color-text-muted); font-weight: 600; }
/* Owner follow-up (2026-08-21): a subtle, secondary "based in Stevenage,
 * expands outward" clarifier -- deliberately quieter than the "Reviewed
 * local signals" text it follows (opacity, not a competing bold label),
 * so the primary "Hertfordshire" framing this pill was corrected to use
 * on 2026-08-20 stays the headline. */
.hp-heartbeat-radar__location-base { opacity: .72; font-weight: 500; }
.hp-heartbeat-radar__location-base::before { content: " \00b7 "; }

@keyframes hp-heartbeat-lubdub {
	0%, 58%, 100% { transform: scale(1); }
	64% { transform: scale(1.045); }
	70% { transform: scale(1); }
	76% { transform: scale(1.025); }
}

@keyframes hp-heartbeat-lubdub-live {
	0%, 46%, 100% { transform: scale(1); }
	54% { transform: scale(1.08); }
	62% { transform: scale(1.01); }
	70% { transform: scale(1.045); }
}

@keyframes hp-heartbeat-breathe {
	0%, 100% { opacity: .78; transform: scale(.985); }
	50% { opacity: 1; transform: scale(1.015); }
}

@keyframes hp-heartbeat-signal {
	0% { box-shadow: 0 0 0 0 rgba(34,197,94,.24); }
	100% { box-shadow: 0 0 0 10px rgba(34,197,94,0); }
}

/*
 * HP-CLAUDE-057 (owner refinement B, 2026-09-14): make Heartbeat read as
 * genuinely live without becoming a UI that is constantly moving.
 *
 * Two additions only, both built from elements that already exist rather than
 * new decoration:
 *
 *  1. An OCCASIONAL signal ripple from the halo. The visible burst lasts about
 *     2.3 seconds of a 9-second cycle, so roughly three quarters of the time
 *     nothing is happening at all. That rhythm is the point: a signal arriving
 *     now and then reads as "receiving", where a continuous pulse reads as
 *     decoration. It animates transform and opacity only, so it composites on
 *     the GPU and costs no layout or paint.
 *
 *  2. A slow travelling highlight along the radar's two existing scan lines.
 *     They were already gradient rules sitting static; giving the gradient a
 *     position and moving it makes them read as sweeps. The two are offset so
 *     they never fire together. Each is a 260x1px box, so animating
 *     background-position on them is trivial work despite not being composited.
 *
 * Both sit inside .hp-heartbeat, so the existing pause control already stops
 * them (.hp-pulse-paused *::before/::after sets animation-play-state:paused),
 * and both are listed in the prefers-reduced-motion block below.
 */
.hp-heartbeat-radar__halo::after {
	position: absolute;
	inset: -6px;
	border: 1px solid rgba(124, 58, 237, .5);
	border-radius: 50%;
	content: "";
	opacity: 0;
	transform: scale(1);
	animation: hp-heartbeat-live-ripple 9s cubic-bezier(.2, .7, .3, 1) infinite;
	pointer-events: none;
}

@keyframes hp-heartbeat-live-ripple {
	0% { opacity: 0; transform: scale(1); }
	4% { opacity: .34; }
	26%, 100% { opacity: 0; transform: scale(1.85); }
}

.hp-heartbeat-radar__rings::before,
.hp-heartbeat-radar__rings::after {
	background-repeat: no-repeat;
	background-size: 55% 100%;
	animation: hp-heartbeat-scanline 7.5s ease-in-out infinite;
}

.hp-heartbeat-radar__rings::after { animation-delay: 3.4s; }

@keyframes hp-heartbeat-scanline {
	0% { background-position: -60% 0; opacity: .2; }
	50% { opacity: .8; }
	100% { background-position: 165% 0; opacity: .2; }
}

.hp-experience-dark .hp-heartbeat-radar__halo::after { border-color: rgba(206, 168, 255, .45); }


.hp-heartbeat__section { padding: clamp(58px, 7vw, 92px) 0; }
.hp-heartbeat__section--map { background: linear-gradient(180deg, #f6f2ff 0%, #fbf9ff 100%); }

.hp-heartbeat__section-heading {
	display: flex;
	align-items: end;
	justify-content: space-between;
	gap: 24px;
	margin-bottom: 28px;
}

.hp-heartbeat__section-heading h2 {
	margin: 0;
	font-size: clamp(2rem, 4vw, 3.3rem);
	letter-spacing: -.04em;
}

.hp-heartbeat__section-heading a {
	color: var(--hp-color-primary-600);
	font-weight: 800;
	text-decoration: none;
}

.hp-heartbeat-insights { margin: -4px 0 22px; }

.hp-heartbeat-insight {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: 16px;
	padding: 18px 20px;
	border: 1px solid rgba(124, 58, 237, .15);
	border-radius: 20px;
	background: linear-gradient(110deg, #fff, #f8f3ff);
	box-shadow: 0 16px 42px rgba(62, 35, 110, .07);
	color: inherit;
	text-decoration: none;
	transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}

.hp-heartbeat-insight:hover,
.hp-heartbeat-insight:focus-visible {
	border-color: rgba(124, 58, 237, .36);
	box-shadow: 0 20px 52px rgba(62, 35, 110, .13);
	transform: translateY(-2px);
}

.hp-heartbeat-insight__icon {
	display: grid;
	width: 46px;
	height: 46px;
	place-items: center;
	border-radius: 15px;
	background: #efe8ff;
	color: var(--hp-color-primary-600);
}

.hp-heartbeat-insight__icon svg { width: 22px; height: 22px; }
.hp-heartbeat-insight__body { display: grid; gap: 2px; }
.hp-heartbeat-insight__body small { color: var(--hp-color-primary-600); font-size: .7rem; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
.hp-heartbeat-insight__body strong { color: #211537; font-size: 1.08rem; }
.hp-heartbeat-insight__body em { color: var(--hp-color-text-muted); font-size: .75rem; font-style: normal; }
.hp-heartbeat-insight__link { color: var(--hp-color-primary-600); font-size: .82rem; font-weight: 850; white-space: nowrap; }

.hp-heartbeat-feed {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 18px;
}

.hp-heartbeat-feed__item {
	padding: 24px;
	border: 1px solid rgba(124,58,237,.12);
	border-radius: 22px;
	background: #fff;
	box-shadow: 0 18px 46px rgba(62,35,110,.07);
	animation: hp-heartbeat-card-arrive .42s cubic-bezier(.2,.75,.3,1) both;
}

.hp-heartbeat-feed__item:nth-child(2) { animation-delay: .06s; }
.hp-heartbeat-feed__item:nth-child(3) { animation-delay: .12s; }
.hp-heartbeat-feed__item:nth-child(4) { animation-delay: .18s; }
.hp-heartbeat-feed__item:nth-child(5) { animation-delay: .24s; }
.hp-heartbeat-feed__item:nth-child(6) { animation-delay: .3s; }

.hp-heartbeat-feed__head {
	display: flex;
	align-items: center;
	gap: 12px;
}

/* Wave 2 (2026-08-17, owner accessibility refinement): this is now the
   wrapping <a> (a hidden duplicate link to the item's canonical page --
   tabindex="-1" aria-hidden="true", keyboard/AT users still get exactly one
   real link, the title) rather than the <img> itself, so the thumbnail is
   genuinely clickable/tappable for mouse and touch users. Sizing/radius
   moves here unchanged; overflow:hidden is what makes the radius actually
   clip the child <img> now that they're no longer the same element. */
.hp-heartbeat-feed__thumb {
	display: block;
	flex-shrink: 0;
	width: 48px;
	height: 48px;
	border-radius: 14px;
	overflow: hidden;
}

.hp-heartbeat-feed__thumb img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Genuine gap found (2026-08-20) while surveying image-cropping coverage:
   the Discover page's "What's happening now" teaser reuses this thumbnail
   for events/local updates, which never got the same real-aspect-ratio
   logo detection already proven on the Directorist archive grid and
   homepage rail -- see assets/js/discovery-media-fixes.js, which applies
   this class the same way. Lower risk than the business-icon case (these
   are event/update photos, not logos) but still a real, currently-uncovered
   surface, so covered for consistency. */
.hp-heartbeat-feed__thumb.hp-thumb-logo-safe img { object-fit: contain; padding: 4px; background: var(--hp-color-white); }

.hp-heartbeat-feed__head > p:first-child {
	flex: 1;
	display: flex;
	justify-content: space-between;
	gap: 12px;
	color: var(--hp-color-text-muted);
	font-size: .8rem;
}

.hp-heartbeat-feed__head > p:first-child span {
	color: var(--hp-color-primary-600);
	font-weight: 850;
	text-transform: uppercase;
}

.hp-heartbeat-feed__item h3 { margin: 16px 0 10px; font-size: 1.25rem; }
.hp-heartbeat-feed__item h3 a { color: inherit; text-decoration: none; }
.hp-heartbeat-feed__item small { color: var(--hp-color-text-muted); }

.hp-heartbeat__empty {
	position: relative;
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: 22px;
	overflow: hidden;
	padding: 28px;
	border: 1px solid rgba(124,58,237,.14);
	border-radius: 24px;
	background: linear-gradient(110deg, #fff, #fbf8ff);
	box-shadow: 0 18px 50px rgba(62,35,110,.07);
}

.hp-heartbeat__empty::after {
	position: absolute;
	right: 12%;
	bottom: -46px;
	width: 160px;
	height: 100px;
	border: 1px solid rgba(124,58,237,.1);
	border-radius: 50%;
	box-shadow: 0 0 0 24px rgba(124,58,237,.025);
	content: "";
}

.hp-heartbeat__empty > span {
	display: grid;
	width: 58px;
	height: 58px;
	place-items: center;
	border-radius: 18px;
	background: #efe8ff;
	color: var(--hp-color-primary-600);
	font-size: 2rem;
}

.hp-heartbeat__empty h3,
.hp-heartbeat__empty p { margin: 0; }
.hp-heartbeat__empty p { margin-top: 6px; color: var(--hp-color-text-muted); line-height: 1.6; }
