/* ============================================================
   Anello: "the fridge door"
   The household's real memory surface: a lived door with cream
   paper sheets pinned by melon and piscine magnets at mixed
   sizes and slight rotations, marine marker underlines and
   sweeps, soft paper-lift shadows, and office-stamp states.
   Nellie is the door that answers. The proportions are the
   palette: pale cream is the door (60), deep marine is ink and
   marker (30), melon the power magnets (5), piscine the clip
   layer (3), chili the stop (2).

   Every text pair clears WCAG AA (4.5:1) on the surface it
   sits on. Melon is too light for plain marine text (3.2:1),
   so melon tiles and buttons carry --melon-ink, a deep marine.
   Chili never carries text on the door (3.9:1) — only
   --alert-deep does.
   ============================================================ */

:root {
	/* Anello, 2026: the fridge door. Pale cream is the door itself; warm
	   cream sheets lift off it on soft shadows, pinned by melon and piscine
	   magnets. Deep marine is ink and marker. The phone demo hangs on the
	   door like the household's real command center. */
	--paper: #fcf6e9; /* pale cream: the door, the 60% */
	--paper-alt: #ebd8b2; /* deeper cream for banded surfaces and the foot */
	--surface: #f4e7ce; /* warm cream sheets, cards, and inputs */

	--ink: #284d5c; /* deep marine: ink, plate, rules — the 30%. 8.45:1 on paper */
	--ink-2: #35586a; /* secondary text; 7.08:1 on paper, 5.44:1 on the band */
	--ink-3: #3f6477; /* muted text; 5.91:1 on paper, 4.54:1 on the band */

	--melon: #f28b49; /* ripe melon, brand + "power" signal */
	--melon-hi: #f49d63; /* hover lift; carries --melon-ink at 5.6:1 */
	--melon-mid: #d97738; /* deeper melon for the panel's offset shadow */
	--melon-deep: #94420f; /* text-safe melon: small marks, links, focus ring */
	--melon-ink: #1b3948; /* deep marine; the only text that sits on melon (4.96:1) */
	--melon-tint: #f3cfa8;
	--melon-wash: #f9e5d2;

	--pisc: #9ec3d3; /* piscine, the "reference / code" layer */
	--pisc-deep: #27597b; /* text-safe piscine: inline code, links, chips */
	--pisc-tint: #e3eef4;

	/* Alert. Chili at full strength is 3.9:1 on the door, so it is borders
	   and decoration only; --alert-deep is the text-safe version. */
	--alert: #d84b20;
	--alert-deep: #a93411; /* text-safe on the wash and on paper */
	--alert-wash: #f9e3d7;

	/* Piscine carries the accents on the dark plate (chat head, close). */
	--pisc-on-dark: #c2dce7;

	--plate: #284d5c;
	--plate-ink: #f8f0de;

	--rule: #e0cfa6; /* hairline on the alt band (darker than either ground) */
	--line-strong: #c3a878; /* a bit more presence for input/secondary-button edges */
	--ink-rule: #284d5c; /* device and input edges */
	--frame: rgba(40, 77, 92, 0.25); /* door-frame hairlines for section rules */

	/* Paper lifting off the door: soft, offset, neutral — never a colored glow. */
	--pin: 0 1px 2px rgba(0, 0, 0, 0.1), 0 10px 28px rgba(0, 0, 0, 0.13);
	--pin-hi: 0 2px 4px rgba(0, 0, 0, 0.12), 0 18px 40px rgba(0, 0, 0, 0.17);

	--mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	--sans: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;

	--wrap: 1200px;
	--pad: clamp(1.25rem, 4vw, 3rem);

	/* Height the sticky topbar occupies, so anchor jumps can clear it. Grows
     when the nav wraps to its own row below 900px. */
	--topbar-h: 64px;
	--tr: 0.2s ease;
	--radius: 12px; /* paper sheet */
	--radius-sm: 8px; /* tiles, inputs, small chrome */
}

*,
*::before,
*::after {
	box-sizing: border-box;
}

/* The `hidden` attribute is only `display: none` in the UA stylesheet, so any
   author rule that sets `display` silently beats it — .signup-form's
   `display: grid` did exactly that, and the invite gate rendered its refusal
   with the form still sitting underneath it. !important rather than ordering,
   so a later `display` on any future component can't reintroduce the bug. */
[hidden] {
	display: none !important;
}

html {
	scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}
}

body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--sans);
	font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

.wrap {
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--pad);
}

a {
	color: inherit;
}

::selection {
	background: var(--melon);
	color: var(--melon-ink);
}

/* Target of both the skip link and the wordmark, so it gets the same clearance
   the bands do. */
#main {
	scroll-margin-top: var(--topbar-h);
}
#main:focus {
	outline: none;
}

:focus-visible {
	outline: 2px solid var(--melon-deep);
	outline-offset: 3px;
	border-radius: 2px;
}

.skip {
	position: absolute;
	left: -9999px;
	top: 0;
	background: var(--melon);
	color: var(--melon-ink);
	padding: 0.6rem 1rem;
	font: 700 0.8rem / 1 var(--sans);
	z-index: 50;
}
.skip:focus {
	left: 0;
}

/* ── shared type ─────────────────────────────────────────── */

/* Manrope carries the display voice as well as the body; the mono that
   remains (badges, code, small terminal furniture) is an accent, not the
   headline face. */
h1,
h2,
h3 {
	font-family: var(--sans);
	margin: 0;
	text-wrap: balance;
}

h1 {
	font-weight: 800;
	font-size: clamp(2.1rem, 1.3rem + 3.4vw, 4rem);
	line-height: 1.02;
	letter-spacing: -0.025em;
	margin-block: 0.6rem 0;
}
.hero h1 {
	line-height: 1.18;
}
h1 em {
	font-style: normal;
	color: var(--ink); /* ink over the swipe, like a real marker: melon-deep drops to 4.0:1 on the wash */
	white-space: nowrap; /* keep the marked phrase on one line */
	/* melon marker swipe under the words */
	background: linear-gradient(to top, rgba(242, 139, 73, 0.45) 24%, transparent 24%);
	padding-inline: 0.08em;
}

h2 {
	font-weight: 700;
	font-size: clamp(1.55rem, 1.15rem + 1.5vw, 2.15rem);
	line-height: 1.1;
	letter-spacing: -0.02em;
}

h3 {
	font-weight: 600;
	font-size: 1.02rem;
	line-height: 1.35;
	letter-spacing: -0.01em;
}

p {
	margin: 0;
}

.eyebrow {
	font: 700 0.72rem / 1 var(--sans);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--melon-deep);
}

/* The bird sits inline with the homepage brand label. */
.hero .eyebrow {
	display: flex;
	align-items: center;
	gap: 0.7rem;
}
.eyebrow-bird {
	width: 48px;
	height: 32px;
	object-fit: contain;
	flex: none;
}

/* ── buttons ─────────────────────────────────────────────── */

/* Flat marine primary buttons with cream text. */
.btn {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	font: 600 0.9rem / 1 var(--sans);
	letter-spacing: 0.005em;
	text-decoration: none;
	padding: 0.78rem 1.35rem;
	border: 1.5px solid transparent;
	border-radius: 9px;
	cursor: pointer;
	transition:
		background-color var(--tr),
		color var(--tr),
		border-color var(--tr),
		box-shadow var(--tr),
		transform var(--tr);
}
.btn:active {
	transform: translateY(1px);
}

.btn-solid {
	background: #284d5c;
	color: #fcf6e9;
}
.btn-solid:hover {
	background: #1b3948;
}

.btn-ghost {
	background: var(--surface);
	color: var(--ink);
	border-color: var(--line-strong);
}
.btn-ghost:hover {
	background: var(--paper-alt);
	border-color: var(--ink-3);
}

.btn-lg {
	padding: 0.95rem 1.7rem;
	font-size: 0.98rem;
}

/* Touch pointers get a comfortable 44px button target. */
@media (pointer: coarse) {
	.btn {
		min-height: 44px;
	}
}
@media (prefers-reduced-motion: reduce) {
	.btn:active {
		transform: none;
	}
	.card,
	.card:hover {
		transform: none;
		transition: none;
	}
}

/* ── topbar ──────────────────────────────────────────────── */

.topbar {
	position: sticky;
	top: 0;
	z-index: 20;
	background: rgba(252, 246, 233, 0.96);
	backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--frame);
}

/* Permanent signup banner below the topbar, reusing the invite banner's piscine band. */
.code-banner {
	background: var(--pisc-tint);
	border-bottom: 1px solid var(--frame);
}
.code-banner[hidden] {
	display: none;
}
.code-banner-inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem 1.5rem;
	flex-wrap: wrap;
	padding-top: 0.85rem;
	padding-bottom: 0.85rem;
}
.code-banner-text {
	margin: 0;
	font-weight: 600;
	color: var(--ink);
	font-size: 1.02rem;
}
.code-banner-text a {
	display: inline;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}
@media (max-width: 620px) {
	.code-banner-text a {
		display: block;
	}
}
.code-banner .btn-solid {
	flex: none;
}

.topbar-inner {
	display: flex;
	align-items: center;
	gap: 1.5rem;
	min-height: 64px;
}

.mark {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	text-decoration: none;
	margin-right: auto;
}
.brand-logo {
	display: block;
	transform: translateX(-1px); /* Align the antialiased visible edge, not just the image box. */
	width: 164px;
	height: auto;
	max-width: 100%;
}
/* Preserve the live header's original artwork scale and 7px bottom inset,
   without the PNG's former 5px left inset. Footer branding stays tightly cropped. */
.mark .brand-logo {
	width: 158.5px;
	padding-bottom: 7px;
}
.foot-mark {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex-wrap: wrap;
}

.topnav {
	display: flex;
	gap: 1.5rem;
}
.topnav a {
	font: 600 0.82rem / 1 var(--sans);
	color: var(--ink-2);
	text-decoration: none;
	padding-block: 0.4rem;
	border-bottom: 2px solid transparent;
	white-space: nowrap;
}
.topnav a:hover {
	color: var(--melon-deep);
	border-bottom-color: var(--melon-deep);
}

.topbar-cta {
	padding: 0.5rem 0.9rem;
}

.topbar-link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	flex: none;
	min-height: 44px;
	font: 600 0.82rem / 1 var(--sans);
	color: var(--ink);
	text-decoration: none;
	text-underline-offset: 0.25em;
	white-space: nowrap;
}
.topbar-link:hover {
	color: var(--melon-deep);
	text-decoration: underline;
}
.topbar-link svg {
	width: 1rem;
	height: 1rem;
	flex: none;
}

/* The nav moves to its own row rather than disappearing, so phone visitors
   aren't left scrolling the whole page to reach Setup. */
@media (max-width: 900px) {
	:root {
		--topbar-h: 104px;
	}

	/* row-gap has to be zeroed explicitly: the base rule sets `gap: 1.5rem`,
     which would otherwise open 24px of dead space above the strip. */
	.topbar-inner {
		flex-wrap: wrap;
		column-gap: 1rem;
		row-gap: 0;
		padding-block: 0.5rem 0;
	}

	.topnav {
		order: 3;
		width: 100%;
		gap: 1.5rem;
		overflow-x: auto;
		overscroll-behavior-inline: contain;
		border-top: 1px solid var(--rule);
		/* Bleed the strip to the viewport edges while its items stay on the
       same left edge as the wordmark above them. */
		margin-inline: calc(var(--pad) * -1);
		padding-inline: var(--pad);
		scrollbar-width: none;
	}
	.topnav::-webkit-scrollbar {
		width: 0;
		height: 0;
	}

	.topnav a {
		flex: none;
		display: inline-flex;
		align-items: center;
		min-height: 44px;
		padding-block: 0;
	}
}

/* ── hero ────────────────────────────────────────────────── */

.hero {
	padding-block: clamp(3.5rem, 7vw, 6.5rem) clamp(3.5rem, 6vw, 6rem);
	border-bottom: 1px solid var(--frame);
	background: var(--paper);
	overflow-x: clip;
}

.hero-bird {
	display: block;
	width: 100px;
	height: auto;
	margin: 0 auto 1rem;
}

.hero-grid {
	display: grid;
	grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
	gap: clamp(2.5rem, 5vw, 4.5rem);
	align-items: center;
}

.lede {
	margin-top: 1.5rem;
	max-width: 34em;
	color: var(--ink-2);
	font-size: 1.08rem;
	line-height: 1.62;
}

/* 600 and a step darker rather than a full 700 bold. The lede is five sentences
   and the emphasis only has to mark where the questions stop and the answer
   starts; a hard bold at this size turns into a slab and pulls the eye off the
   headline above it. */
.lede strong {
	font-weight: 600;
	color: var(--ink);
}

.cta-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.85rem;
	margin-top: 2rem;
}

.hero-cta {
	flex-direction: column;
	align-items: center;
	margin-bottom: 2rem;
}

.testimonial-carousel {
	display: grid;
	grid-template-columns: 32px minmax(0, 1fr) 32px;
	column-gap: 0.75rem;
	align-items: center;
	margin-bottom: 1rem;
	border-block: 1px solid var(--rule);
}

.testimonial-carousel + .hero-learn {
	margin-top: 0;
}

.testimonials {
	grid-column: 2;
	grid-row: 1;
	margin-bottom: 0;
	padding: 1.5rem 0.75rem;
	-webkit-mask-image: linear-gradient(to right, transparent, #000 0.75rem, #000 calc(100% - 0.75rem), transparent);
	mask-image: linear-gradient(to right, transparent, #000 0.75rem, #000 calc(100% - 0.75rem), transparent);
	display: flex;
	align-items: center;
	gap: 1.5rem;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-padding-inline: 0.75rem;
}

.testimonial,
.testimonial blockquote,
.testimonial p {
	margin: 0;
}

.testimonial {
	flex: 0 0 100%;
	min-width: 0;
	scroll-snap-align: start;
}

.testimonial blockquote {
	font-style: italic;
	font-size: 1rem;
	line-height: 1.6;
	color: var(--ink);
}

.testimonials:focus-visible {
	outline-offset: -2px;
}

.testimonial-arrow {
	display: none;
}

.testimonial-arrow:not([hidden]) {
	display: grid;
	place-items: center;
	width: 32px;
	min-height: 44px;
	padding: 0;
	border: 0;
	background: transparent;
	color: var(--ink);
	font-size: 1.25rem;
	opacity: 0.65;
	cursor: pointer;
}

.testimonial-arrow:active {
	opacity: 1;
}

.testimonial figcaption {
	margin-top: 0.65rem;
	font-size: 0.85rem;
	color: var(--ink-2);
}

.hero-learn-more {
	text-align: center;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.hero-cta .btn,
#signup-text {
	width: min(100%, 24rem);
	justify-content: center;
	text-align: center;
}

.demo-link-mobile {
	display: none;
}

#demo {
	scroll-margin-top: calc(var(--topbar-h) + 1rem);
}

.hero-facts {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.5rem;
	margin: 2.25rem 0 0;
	padding: 1.5rem 0 0;
	border-top: 1px solid var(--rule);
}
.hero-learn + .hero-facts {
	margin-top: 1rem;
	padding-top: 1rem;
}

.hero-facts li {
	font: 600 0.76rem / 1.4 var(--sans);
	color: var(--ink-3);
	display: flex;
	align-items: center;
	gap: 0.5rem;
}
.hero-facts .hero-fact-compatibility {
	flex-basis: 100%;
	align-items: baseline;
}
.hero-facts li::before {
	content: "";
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--pisc);
	box-shadow: inset 0 -1px 2px rgba(0, 0, 0, 0.25);
	flex: none;
}

/* ── the conversation (signature) ────────────────────────── */

/* The supplied textured phone frames the live conversation. */
.demo {
	min-width: 0;
	display: grid;
	gap: 1rem;
	justify-items: center;
	position: relative;
}
/* Back-lit paper warmth: light diffusing through the sheet behind the phone,
   a soft melon wash, not a glow. */
.demo::before {
	content: "";
	position: absolute;
	inset: 12% -4% 6%;
	background: radial-gradient(closest-side, rgba(242, 139, 73, 0.2), rgba(242, 139, 73, 0.07) 62%, transparent 78%);
	z-index: 0;
	pointer-events: none;
}
.demo > * {
	position: relative;
	z-index: 1;
}

/* The bird and nest sit beneath the phone without covering its controls. */
.demo { padding-bottom: 6.5rem; isolation: isolate; }
.demo > .phone-frame {
	position: absolute;
	inset: 4rem 0 0;
	width: min(100%, 360px);
	margin-inline: auto;
	z-index: 0;
	pointer-events: none;
}
.phone-frame img {
	position: absolute;
	width: 125px;
	height: 125px;
	object-fit: contain;
}
.phone-frame .frame-nest {
	bottom: 0;
	left: 50%;
	transform: translateX(-50%);
	width: 190px;
	height: 95px;
}

.demo-tabs {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.4rem;
}
.demo-tab {
	font: 600 0.76rem / 1 var(--sans);
	letter-spacing: 0.01em;
	color: var(--ink-2);
	background: transparent;
	border: 1.5px solid transparent;
	border-radius: var(--radius);
	padding: 0.5rem 0.7rem;
	cursor: pointer;
	transition:
		color var(--tr),
		border-color var(--tr),
		background-color var(--tr);
}
.demo-tab:hover {
	color: var(--ink);
	border-color: var(--rule);
}
.demo-tab[aria-selected="true"] {
	color: var(--ink);
	background: var(--surface);
	border-color: var(--line-strong);
	font-weight: 700;
}
@media (pointer: coarse) {
	.demo-tab {
		min-height: 44px;
	}
}

/* Three tabs on one row is the whole point of a tablist — wrapped, the odd one
   out reads as a different control. At the labels' current lengths the row
   needs about 411px of viewport, which is wider than every common phone, so
   these two steps tighten the type and padding just enough to keep it in line:
   the first covers 375px and up, the second 360px, which is where most Android
   sits. The 44px tap target is set on min-height and is unaffected by either.

   Below 360px it wraps, and that is the right place to stop — the type would
   have to go under 10px to hold the line, and two rows of legible tabs beat
   one row nobody can read. flex-wrap is the backstop, here and for whatever
   longer label comes later. */
@media (max-width: 30rem) {
	.demo-tabs {
		gap: 0.3rem;
	}
	.demo-tab {
		font-size: 0.68rem;
		padding: 0.5rem 0.55rem;
	}
}
@media (max-width: 23rem) {
	.demo-tabs {
		gap: 0.25rem;
	}
	.demo-tab {
		font-size: 0.63rem;
		padding: 0.5rem 0.42rem;
	}
}

/* Default is a readable vertical stack: with no JS you get three complete
   conversations rather than three overlapping ones. app.js adds .is-tabbed,
   which collapses them into a single cell so switching never changes the
   hero's height. */
/* Widths are definite the whole way down on purpose. Letting the stage
   shrink-wrap the panel, the panel shrink-wrap the phone, and the phone size
   itself with min(20rem, 100%) makes the 100% resolve against an indefinite
   width — the chain is circular and the phone settles off-centre. */
.demo-stage {
	width: 100%;
	display: grid;
	gap: 1.75rem;
	justify-items: center;
	min-width: 0;
}
.demo-panel {
	width: min(24rem, 100%);
	min-width: 0;
}
/* With JS but before app.js takes over, only the first panel shows. Otherwise
   all three paint stacked and then collapse — the other half of the flash.
   Once .is-tabbed lands, the hidden attribute drives it instead. */
.has-js .demo-stage:not(.is-tabbed) .demo-panel:nth-child(n + 2) {
	display: none;
}
.demo-stage.is-tabbed {
	gap: 0;
}
.demo-stage.is-tabbed .demo-panel {
	grid-area: 1 / 1;
}

/* Match the supplied 1024×1536 artwork; screen bounds stay proportional. */
.phone {
	position: relative;
	width: 100%;
	aspect-ratio: 2 / 3;
	background: url("/assets/optimized/phone-768.webp") center / 100% 100% no-repeat;
	/* The frame is at most 384 CSS px wide. Keep a 2x fallback for browsers
	   without image-set; high-density screens can use the original pixel width. */
	background-image: image-set(
		url("/assets/optimized/phone-384.webp") 1x,
		url("/assets/optimized/phone-768.webp") 2x,
		url("/assets/optimized/phone-1024.webp") 2.667x
	);
	border-radius: 1rem;
}

.phone.phone-group {
	background-image: url("/assets/optimized/phone-group-768.webp");
	background-image: image-set(
		url("/assets/optimized/phone-group-384.webp") 1x,
		url("/assets/optimized/phone-group-768.webp") 2x,
		url("/assets/optimized/phone-group-1024.webp") 2.667x
	);
}
.phone-screen {
	position: absolute;
	inset: 15% 12.5% 14%;
	background: transparent;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	contain: layout paint;
}

/* Reserve the former name line (0.7rem × 1.6) below the artwork controls. */
.phone .thread-head {
	padding: 1.12rem 0 calc(0.5rem + 6px);
	background: transparent;
	backdrop-filter: none;
	border: 0;
}
.phone .chat-log {
	padding: 0.5rem 0.4rem;
	overflow-y: auto;
	justify-content: safe flex-end;
}
.phone .chat-log:focus-visible {
	outline: 2px solid var(--ink);
	outline-offset: -2px;
}
.phone .bubble { font-size: clamp(0.7rem, 2.8vw, 0.83rem); }

/* The thread header, as iOS draws it: translucent, hairline, name centred. */
.thread-head {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.25rem;
	padding: 1.5rem 1rem 0.5rem;
	background: rgba(249, 249, 249, 0.94);
	backdrop-filter: blur(12px);
	border-bottom: 1px solid #dcdcdf;
	flex: none;
}
/* Group threads: incoming bubbles carry a byline, because the grey side can
   be more than one person. Nellie keeps her melon; it stays put while the
   typing dots show, like iOS holds the name through the ellipsis. */
.msg-sender {
	font: 600 0.62rem / 1 var(--sans);
	color: var(--melon-deep);
	padding: 0 0.3rem;
}

.chat-log {
	list-style: none;
	margin: 0;
	padding: 0.75rem 0.7rem 1rem;
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	overflow: hidden;
	flex: 1;
	justify-content: flex-end;
}

/* Grid rather than flex so the row itself can collapse to nothing: a staged
   message is 0fr tall, and expanding it to 1fr is what pushes the thread
   upward as each new bubble lands. Flex has no equivalent you can transition. */
.msg {
	display: grid;
	grid-template-rows: 1fr;
	justify-items: end;
	transition:
		grid-template-rows 0.55s cubic-bezier(0.25, 0.8, 0.25, 1),
		opacity 0.46s ease-out;
}
.msg.is-bax {
	justify-items: start;
}
.msg .msg-body {
	min-height: 0;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 0.15rem;
	max-width: 78%;
	transition: transform 0.55s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.msg.is-bax .msg-body {
	align-items: flex-start;
}

/* Bubbles. Sent is blue and right, received is grey and left, which is the
   arrangement everyone already knows how to read. The blue is two steps
   deeper than Apple's #007AFF so white text clears 4.5:1 instead of 4.0. */
.bubble {
	max-width: 100%;
	padding: 0.44rem 0.75rem;
	border-radius: 1.15rem;
	font-family: var(--sans);
	font-size: 0.83rem;
	line-height: 1.38;
	background: #0a6ce0;
	color: #fff;
	border-bottom-right-radius: 0.35rem;
}
.msg.is-bax .bubble {
	background: #e9e9eb;
	color: #1c1c1e;
	border-bottom-right-radius: 1.15rem;
	border-bottom-left-radius: 0.35rem;
}
/* Only the last bubble of a run gets the tail; the rest stay fully round. */
.msg .bubble:not(:last-child) {
	border-bottom-right-radius: 1.15rem;
}
.msg.is-bax .bubble:not(:last-child) {
	border-bottom-left-radius: 1.15rem;
}

.bubble-media {
	padding: 0;
	overflow: hidden;
	background: #d9d9dd;
	line-height: 0;
}
.bubble-photo {
	display: block;
	width: 7.5rem;
	height: auto;
	border-radius: inherit;
}

.msg-body {
	min-width: 0;
}

.msg-h {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0.45rem;
	margin-bottom: 0.15rem;
}
.msg-h b {
	font-family: var(--mono);
	font-weight: 600;
	font-size: 0.9rem;
}
.msg-h span {
	font: 400 0.68rem / 1 var(--mono);
	color: var(--ink-3);
}

/* Staged reveal. Keyed on has-js, set by an inline script in <head>, so this
   applies on the first paint — app.js adds its class several frames later and
   the demo would flash fully-rendered before collapsing. The markup stays
   complete for anyone without JS, which is the point of doing it this way.

   (was: app.js adds .is-staged only when motion is welcome and the
   script is actually running, so all three conversations are complete without
   it. Enter from below, the way a message actually arrives. */
/* A staged row has no height at all, so the thread sits at the bottom of the
   screen and grows upward, one message at a time, the way a real one does.
   The bubble rides up the last few pixels on its own transform so it arrives
   rather than simply appearing. */
.has-js .chat-log .msg {
	grid-template-rows: 0fr;
	opacity: 0;
}
.has-js .chat-log .msg > .msg-body {
	overflow: hidden;
	transform: translateY(7px);
}
.has-js .chat-log .msg.is-in {
	grid-template-rows: 1fr;
	opacity: 1;
}
.has-js .chat-log .msg.is-in > .msg-body {
	transform: none;
}

/* Typing indicator, drawn the way iOS draws it: its own small grey bubble
   rather than dots floating over the reply. The real bubbles are removed from
   flow while it shows, so the thread grows when the reply lands. The screen is
   a fixed height with messages anchored to the bottom, so that growth reads as
   a conversation filling up instead of the panel resizing. */
.typing-dots {
	display: none;
	align-items: center;
	gap: 4px;
	padding: 0.62rem 0.8rem;
	background: #e9e9eb;
	border-radius: 1.15rem;
	border-bottom-left-radius: 0.35rem;
}
.msg.is-typing .msg-body > p {
	display: none;
}
.msg.is-typing .typing-dots {
	display: flex;
}
.typing-dots i {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: #8e8e93;
	animation: tdot 1.15s ease-in-out infinite;
}
.typing-dots i:nth-child(2) {
	animation-delay: 0.15s;
}
.typing-dots i:nth-child(3) {
	animation-delay: 0.3s;
}
@keyframes tdot {
	0%,
	60%,
	100% {
		opacity: 0.25;
	}
	30% {
		opacity: 1;
	}
}

@media (prefers-reduced-motion: reduce) {
	/* Must reset grid-template-rows as well as opacity: staging collapses the
	   row to 0fr, so resetting only opacity would leave every message present,
	   fully opaque, and zero pixels tall. */
	.has-js .chat-log .msg {
		grid-template-rows: 1fr;
		opacity: 1;
	}
	.has-js .chat-log .msg > .msg-body {
		transform: none;
	}
	.msg,
	.msg .msg-body {
		transition: none;
	}
	.typing-dots i {
		animation: none;
	}
}

@media (max-width: 980px) {
	.hero-grid {
		grid-template-columns: 1fr;
	}
	.demo-link-desktop {
		display: none;
	}
	.demo-link-mobile {
		display: inline-flex;
	}
}

/* ── band scaffolding ────────────────────────────────────── */

.band {
	padding-block: clamp(3.5rem, 6vw, 6rem);
	border-bottom: 1px solid var(--frame);
	scroll-margin-top: var(
		--topbar-h
	); /* clears the sticky topbar on anchor jumps */
}
.band-alt {
	background: var(--paper-alt);
}

.band-grid {
	display: grid;
	grid-template-columns: 190px minmax(0, 1fr);
	gap: clamp(1.75rem, 4vw, 3.5rem);
}

.band-label {
	font: 600 0.85rem / 1.3 var(--sans);
	letter-spacing: normal;
	text-transform: none;
	color: var(--melon-deep);
	display: flex;
	align-items: center;
	gap: 0.7rem;
	height: fit-content;
	padding-top: 0.5rem;
}
.tick {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	background: url('/assets/dot.png') center / contain no-repeat;
	flex: none;
}

/* Grid items default to min-width:auto, which refuses to shrink below their
   content, so the panes inside these columns would push the page wide. */
.band-body,
.hero-copy {
	min-width: 0;
}

.band-lede {
	margin-top: 1.1rem;
	max-width: 58ch;
	color: var(--ink-2);
}
@media (max-width: 860px) {
	.band-grid {
		grid-template-columns: 1fr;
		gap: 1.5rem;
	}
	.band-label {
		padding-top: 0;
	}
}

/* ── ability cards ───────────────────────────────────────── */

.cards {
	margin-top: 2.75rem;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
	gap: 2rem 2.5rem;
}
/* Cream paper cards with slight rotations and a faint edge shadow. */
.card {
	position: relative;
	padding: 1.6rem 1.5rem 1.7rem;
	background: var(--surface);
	border-radius: var(--radius);
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 3px 8px rgba(0, 0, 0, 0.05);
	transition: transform var(--tr), box-shadow var(--tr);
}
.card::before {
	content: "";
	display: block;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: url('/assets/dot.png') center / contain no-repeat;
	margin: 0 auto 1.1rem;
}
.card:nth-child(odd) {
	transform: rotate(-0.6deg);
}
.card:nth-child(even) {
	transform: rotate(0.5deg);
}
.card:hover {
	transform: rotate(0deg) translateY(-3px);
	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), 0 6px 16px rgba(0, 0, 0, 0.07);
}
@media (prefers-reduced-motion: reduce) {
	.card:nth-child(n), .card:hover {
		transform: none;
		transition: none;
	}
}
.page-illustration {
	display: block;
	max-width: 100%;
	height: auto;
	pointer-events: none;
}
.signup-dog { margin: 0 0 1rem; }

/* Homepage illustration study: replace card pins with household objects. */
#what .card::before { content: none; }
#what .card h3 {
	display: flex;
	align-items: center;
	gap: 0.75rem;
}
.feature-art {
	flex: none;
	width: 48px;
	height: 48px;
	object-fit: contain;
}
.privacy-plant {
	float: right;
	width: 220px;
	height: 200px;
	object-fit: contain;
	margin: 0 0 1rem 1.5rem;
}
#why-nellie .band-body { display: flow-root; }
@media (max-width: 540px) {
	.hero { padding-top: 1.5rem; }
	/* The illustrations include breathing room inside their image bounds. */
	#why-nellie { padding-top: 2rem; }
	#signup { padding-top: 2.25rem; }
	.privacy-plant {
		float: none;
		width: 180px;
		height: 160px;
		margin: 0 0 1rem;
	}
}
.card h3 {
	color: var(--ink);
}
.card p {
	margin-top: 0.6rem;
	font-size: 0.92rem;
	line-height: 1.58;
	color: var(--ink-2);
}

/* ── security plate ──────────────────────────────────────── */

/* A spec sheet, deliberately quiet: one column, so each rule gets a full
   line to state itself. */
.plate {
	margin: 2.5rem 0 0;
	border-top: 1px solid var(--frame);
}
.plate > div {
	display: grid;
	grid-template-columns: 16rem minmax(0, 1fr);
	gap: 1.5rem;
	padding-block: 1rem;
	border-bottom: 1px solid var(--rule);
	align-items: baseline;
}
/* The term is what you scan, so it has to outweigh the prose explaining it. */
.plate dt {
	font-family: var(--sans);
	font-weight: 700;
	font-size: 0.92rem;
	line-height: 1.4;
	color: var(--ink);
}
.plate dd {
	margin: 0;
	font-size: 0.92rem;
	line-height: 1.55;
	color: var(--ink-2);
}

@media (max-width: 620px) {
	.plate > div {
		grid-template-columns: 1fr;
		gap: 0.35rem;
	}
}

/* ── close ───────────────────────────────────────────────── */

/* The page's second dark moment: the close picks up the chat window's title
   bar (marine plate, piscine accents) so the scroll ends on a peak instead of
   fading out. */
.close {
	padding-block: clamp(4rem, 8vw, 7rem);
	background: var(--plate);
	color: var(--plate-ink);
}
.close .band-label {
	color: var(--pisc-on-dark);
}
.close .tick {
	background: var(--pisc-on-dark);
}
.close h2 {
	color: var(--plate-ink);
}
.close-inner p {
	margin-top: 1rem;
	color: rgba(248, 240, 222, 0.8);
}
.close :focus-visible {
	outline-color: var(--pisc-on-dark);
}
.close .btn {
	margin-top: 2.25rem;
}

/* ── footer ──────────────────────────────────────────────── */

.foot {
	border-top: 1px solid var(--frame);
	padding-block: 1.75rem;
	background: var(--paper-alt);
}
.foot-inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.6rem 1.5rem;
	font: 500 0.78rem / 1.5 var(--sans);
	color: var(--ink-3);
}
.foot-mark {
	font-family: var(--sans);
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--ink);
	font-size: 1rem;
}
/* It links home, but it should still read as the wordmark rather than as a
   third footer link: no rule under it, and the dark ink kept. Specificity has
   to beat `.foot a`, which would otherwise underline it and lighten it. */
.foot a.foot-mark {
	color: var(--ink);
	text-decoration: none;
}
.foot a.foot-mark:hover {
	color: var(--melon-deep);
}
.foot-brand {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.6rem;
	flex-basis: 100%; /* Footer links begin their own row below the brand and subtitle. */
}
.foot-dim {
	margin-right: auto;
}
/* Underlined: an undecorated link beside grey footer text is easy to miss. */
.foot a {
	color: var(--ink-2);
	text-decoration: underline;
	text-decoration-color: var(--rule);
	text-underline-offset: 0.25em;
}
.foot a:hover {
	color: var(--melon-deep);
	text-decoration-color: var(--melon-deep);
}

/* ── legal / long-form prose ─────────────────────────────── */

/* The rest of the site is one scannable page; this is the opposite: thirteen
   sections someone reads top to bottom, or lands in the middle of from a table
   of contents. So: a narrow measure, real paragraph rhythm (the global
   `p { margin: 0 }` is a landing-page convenience that prose can't use), and
   section headings dialed well down from the display h2. */

.legal {
	padding-block: clamp(3rem, 6vw, 5rem) clamp(4rem, 8vw, 7rem);
	background: var(--paper);
}
.legal-inner {
	max-width: 44rem;
}

.legal h1 {
	font-size: clamp(1.9rem, 1.4rem + 2vw, 2.9rem);
}

.legal-dates {
	margin-top: 1rem;
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 1.5rem;
	font: 500 0.78rem / 1.5 var(--sans);
	color: var(--ink-3);
}

.legal .lede {
	max-width: none;
}

.legal p {
	margin-top: 1rem;
}
.legal p,
.legal li {
	color: var(--ink-2);
}

/* Numbered headings, so "§2" in the prose points somewhere findable. The rule
   above each is the section break; without it thirteen headings read as a wall. */
.legal h2 {
	margin-top: 3rem;
	padding-top: 1.4rem;
	border-top: 1px solid var(--frame);
	font-size: 1.32rem;
	color: var(--ink);
}
.founder-photo {
	display: block;
	position: relative;
	width: 10rem;
	max-width: 100%;
	margin-top: 2.5rem;
	border-radius: 12px;
}
.founder-photo img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: inherit;
}
.founder-linkedin {
	position: absolute;
	right: 0.5rem;
	bottom: 0.5rem;
}
.legal .founder-name {
	margin-top: 1rem;
	padding-top: 0;
	border-top: 0;
}

.legal h3 {
	margin-top: 1.9rem;
	color: var(--ink);
}
.legal h3 + p {
	margin-top: 0.6rem;
}

/* Anchor targets sit under the sticky topbar otherwise. */
.legal :is(h2, h3)[id] {
	scroll-margin-top: calc(var(--topbar-h) + 1.5rem);
}

/* Exclude buttons without raising specificity above the TOC link styles. */
.legal a:where(:not(.btn)) {
	color: var(--pisc-deep);
	text-decoration: underline;
	text-decoration-color: var(--pisc-tint);
	text-underline-offset: 0.2em;
	transition: text-decoration-color var(--tr);
}
.legal a:where(:not(.btn)):hover {
	text-decoration-color: var(--pisc-deep);
}

.legal-toc {
	margin-top: 2.25rem;
	padding: 1.25rem 1.5rem;
	background: var(--surface);
	border-radius: var(--radius);
	box-shadow: var(--pin);
}
.legal-toc ol {
	margin: 0;
	padding-left: 1.6rem;
	columns: 2;
	column-gap: 2rem;
	font: 500 0.85rem / 1.9 var(--sans);
}
.legal-toc li {
	break-inside: avoid;
}
.legal-toc a {
	text-decoration: none;
	color: var(--ink-2);
}
.legal-toc a:hover {
	color: var(--melon-deep);
}

@media (max-width: 560px) {
	.legal-toc ol {
		columns: 1;
	}
}

.legal-list {
	margin-top: 1rem;
	padding-left: 1.25rem;
}
.legal-list li {
	margin-top: 0.5rem;
}
.legal-list strong {
	color: var(--ink);
}

/* Tighter term column than the homepage's 16rem: this measure is half as wide. */
.legal .plate {
	margin-top: 1.75rem;
}
.legal .plate > div {
	grid-template-columns: 11rem minmax(0, 1fr);
	gap: 1.25rem;
}

@media (max-width: 620px) {
	.legal .plate > div {
		grid-template-columns: 1fr;
		gap: 0.35rem;
	}
}

/* The three disclosures carriers audit for (frequency, rates, and the
   non-sharing pledge) plus the restatement of the pledge. Boxed so a reviewer
   skimming for them finds them without reading the section, and so they survive
   being quoted out of context. */
.legal-callout {
	margin-top: 1.25rem;
	padding: 1rem 1.25rem;
	background: var(--melon-wash);
	border: 1.5px solid var(--melon-tint);
	border-radius: var(--radius-sm);
	color: var(--ink-2);
}
.legal-callout strong {
	color: var(--ink);
}

.legal-pledge {
	background: var(--surface);
	border-left-color: var(--ink-rule);
	font-size: 0.95rem;
	line-height: 1.62;
}

/* ── invite form + consent disclosure ────────────────────── */

/* The page captures an email; the SMS consent form lives behind it. Fields are
   boxed in the same hard border as everything else: squared corners, offset
   shadow on focus rather than a glow, so inputs read as part of the chassis. */
.signup-form {
	margin-top: 2rem;
	max-width: 30rem;
	display: grid;
	gap: 1.1rem;
}
.field {
	display: grid;
	gap: 0.4rem;
}
.field label {
	font: 700 0.74rem / 1 var(--sans);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--ink-2);
}
.field input {
	font: 500 1rem / 1.4 var(--sans);
	color: var(--ink);
	background: var(--surface);
	border: 1.5px solid var(--line-strong);
	border-radius: var(--radius-sm);
	padding: 0.7rem 0.85rem;
	width: 100%;
	transition:
		box-shadow var(--tr),
		border-color var(--tr);
}
.field input::placeholder {
	color: var(--ink-3);
}
.field input:focus-visible {
	outline: none;
	border-color: var(--melon-deep);
	box-shadow: 0 0 0 3px rgba(242, 139, 73, 0.35);
}
/* :user-invalid only fires after the field has been touched, so the empty
   form doesn't shout on load. (The old :not(:placeholder-shown) guard never
   matched, since these inputs have no placeholders, and did exactly that.) */
.field input:user-invalid {
	border-color: var(--melon-deep);
}

.signup-form .btn-solid {
	justify-self: start;
}

/* Short consent note under a form. The carrier-vetting wall this replaced had
   to be exhaustive; this only has to be honest and quick to read. */
.signup-note {
	margin-top: 1.4rem;
	max-width: 34rem;
	font-size: 0.9rem;
	line-height: 1.65;
	color: var(--ink-3);
}

#signup-text {
	margin-top: 1.5rem;
}

#signup-phone-link {
	color: inherit;
	text-decoration: none;
}

#signup-phone {
	font-size: 1.5rem;
	font-weight: 700;
	color: var(--ink);
}

@media (max-width: 620px) {
	.signup-cta,
	#signup-phone {
		text-align: center;
	}

	#signup-phone {
		max-width: none;
	}
}

.signup-note-top {
	margin-top: .5rem;
	max-width: 34rem;
	font-size: 0.9rem;
	line-height: 1.65;
	color: var(--ink-3);
}

.signup-note a {
	color: var(--pisc-deep);
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.field-hint {
	margin-top: 0.1rem;
	font-size: 0.82rem;
	line-height: 1.5;
	color: var(--ink-3);
}

/* The box is a control, not a bullet, so it sits with the first line of the
   label rather than floating above the block. */
.consent {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 0.7rem;
	align-items: start;
	max-width: 34rem;
}
.consent input[type="checkbox"] {
	width: 1.15rem;
	height: 1.15rem;
	margin: 0.15rem 0 0;
	accent-color: var(--melon-deep);
	cursor: pointer;
}
.consent label {
	font-size: 0.9rem;
	line-height: 1.6;
	color: var(--ink-2);
	cursor: pointer;
	text-transform: none;
	letter-spacing: normal;
	font-family: var(--sans);
	font-weight: 400;
}

/* Invite gate on join.html: shown when ?key= is missing, malformed, unsigned,
   or expired. Every one of those is a dead end, so it takes the alert red
   rather than a neutral note — this is the only thing on the page, and it
   should read as "stop" at a glance.

   Same hard offset shadow the buttons and the chat panel use, in red. It pops
   because the chassis is already built that way, not because it's louder. */
/* The invite code, sitting above the waiting list. Mono and uppercased on
   sight so a code read off a card looks like the thing on the card; the value
   is uppercased again on the way out, and /api/join does it a third time. */
.code-form {
	margin-top: 2rem;
	max-width: 30rem;
}
.code-row {
	display: flex;
	align-items: stretch;
	gap: 0.6rem;
}
/* The border lives on the wrapper so the prefix and the field read as one
   control; the input inside is stripped bare and the focus ring is raised to
   the wrapper with :focus-within. */
.code-input {
	display: flex;
	align-items: center;
	background: var(--surface);
	border: 1.5px solid var(--line-strong);
	border-radius: var(--radius-sm);
	padding: 0.7rem 0.85rem;
	transition:
		box-shadow var(--tr),
		border-color var(--tr);
}
.code-input:focus-within {
	border-color: var(--melon-deep);
	box-shadow: 0 0 0 3px rgba(242, 139, 73, 0.35);
}
.code-input input {
	font: 400 1rem / 1.4 var(--mono);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink);
	background: none;
	border: 0;
	border-radius: 0;
	padding: 0;
	width: 6.5ch;
	min-width: 0;
}
.code-input input:focus-visible {
	outline: none;
	box-shadow: none;
}
.code-input input::placeholder {
	color: var(--ink-3);
	text-transform: none;
	opacity: 0.65;
}
.code-row .btn {
	flex: none;
}
.code-row .btn[disabled] {
	opacity: 0.55;
	cursor: default;
}

/* The waiting list, demoted to the fallback under the code field. The rule is
   the divider: what's above it is for people who already have an invite, what's
   below is for everyone else. */
.signup-alt {
	margin-top: 2.75rem;
	padding-top: 1.75rem;
	border-top: 1px solid var(--rule);
	color: var(--ink);
}
.signup-alt-lede {
	margin-top: 0.7rem;
	max-width: 58ch;
	color: var(--ink-2);
}

.gate {
	margin-top: 2rem;
	max-width: 34rem;
	padding: 1.15rem 1.35rem;
	background: var(--alert-wash);
	border: 2px solid var(--alert);
	border-radius: var(--radius-sm);
	box-shadow: var(--pin);
	transform: rotate(-0.4deg);
}
.gate p {
	font-size: 0.95rem;
	line-height: 1.65;
	color: var(--alert-deep);
}

/* The household name becomes an address, so it is set in the mono face here to
   match how it will look when they type it. */
.field-hint code {
	font-family: var(--mono);
	font-size: 0.95em;
	color: var(--pisc-deep);
}

/* "optional" beside a field label. Marking the one optional field is clearer
   than starring the four required ones: the exception is what people need
   told, and asterisks make a form look more demanding than it is. */
.field-optional {
	margin-left: 0.5rem;
	font-weight: 400;
	letter-spacing: 0.04em;
	text-transform: none;
	color: var(--ink-3);
}

/* The household address inside a card. Same mono treatment the join form uses
   for it, so the one string that appears on both pages looks like one thing. */
.card code {
	font-family: var(--mono);
	font-size: 0.88em;
	color: var(--pisc-deep);
	white-space: nowrap;
}

/* A locked field still reads as a field, just not one you can argue with. The
   invite decides this address, and the server overrides whatever is submitted,
   so an editable box would only mislead. */
.field input.is-locked {
	background: var(--paper-alt);
	color: var(--ink-2);
	cursor: default;
}

/* ── 404 ─────────────────────────────────────────────────── */

/* Vertically centred, because there is nothing below it to scroll to. The
   min-height accounts for the sticky topbar so the block sits in the middle of
   what's actually visible rather than the middle of the document. */
.lost {
	display: grid;
	align-content: center;
	min-height: calc(100svh - var(--topbar-h) - 6rem);
	padding-block: clamp(3rem, 8vw, 6rem);
	background: var(--paper);
}
.lost-inner {
	max-width: 34rem;
}

/* The hero's bubbles outside a phone. .chat-log can't be reused here: it is
   flex-end inside a fixed-height screen, which is meaningless on a page. */
.lost-thread {
	list-style: none;
	margin: 1.9rem 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	max-width: 30rem;
}
.lost-thread .msg-body {
	max-width: 88%;
}

.lost-cta {
	margin-top: 2.25rem;
}

/* Hero signature. Separated from the facts above it by a hairline rather than
   another bullet: those are product facts, this is a person putting their name
   to it, and they shouldn't read as the same kind of statement. Quiet on
   purpose — provenance stops working the moment it looks like a badge. */
.hero-sig {
	margin-top: 1.5rem;
	padding-top: 1.1rem;
	border-top: 1px solid var(--rule);
	max-width: 26rem;
	font: 500 0.8rem / 1.5 var(--sans);
	color: var(--ink-3);
}

/* The address has to clear 4.5:1 against paper, which --ink-3 around it does
   not; the deeper melon carries the link on its own without turning a quiet
   signature into a call to action. */
.hero-sig a {
	color: var(--melon-deep);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	text-decoration-thickness: 1px;
}
.hero-sig a:hover {
	color: var(--ink);
}

/* Quiet context links and a visible free-plan note in the hero. */
.hero-learn {
	margin-top: 1rem;
	font: 600 0.9rem / 1.4 var(--sans);
}
.learn-link {
	font: 600 0.9rem / 1.4 var(--sans);
	color: var(--melon-deep);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	text-decoration-thickness: 1px;
}
.learn-link:hover {
	color: var(--ink);
}
.hero-pricing {
	margin-top: 0.6rem;
	font: 600 0.95rem / 1.5 var(--sans);
}

/* Pricing keeps both plans on one comparison sheet, including on a phone. */
.pricing { padding: clamp(2.5rem, 6vw, 5rem) 0; }
.pricing-inner { max-width: 1000px; }
.pricing-intro { max-width: 43rem; margin-bottom: 2.5rem; }
.pricing-intro h1 { font-size: clamp(2.2rem, 5vw, 3.7rem); line-height: 1.08; letter-spacing: -0.025em; }
.pricing-intro .lede { margin: 1.2rem 0 1.5rem; }
.pricing-sheet { background: var(--surface); border-radius: var(--radius); box-shadow: var(--pin); padding: clamp(0.8rem, 3vw, 2rem); }
.pricing-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-variant-numeric: tabular-nums; }
.pricing-feature { width: 46%; }
.pricing-table caption { text-align: left; padding: 0 0 1.4rem; color: var(--ink-2); font-weight: 600; }
.pricing-table th, .pricing-table td { padding: 1rem 0.65rem; border-bottom: 1px solid var(--rule); vertical-align: middle; overflow-wrap: break-word; }
.pricing-table th { text-align: left; font-weight: 600; }
.pricing-table thead th { font-size: 1.12rem; font-weight: 800; }
.pricing-table thead th:not(:first-child), .pricing-table td { text-align: center; }
.pricing-table td { color: var(--ink-2); }
.pricing-table tbody tr:last-child > * { border-bottom: 0; }
.pricing-cost strong { display: block; color: var(--ink); font-size: clamp(1.7rem, 4vw, 2.7rem); font-weight: 800; line-height: 1.15; }
.pricing-cost span { display: block; margin-top: 0.4rem; font-size: 0.85rem; }
.pricing-credits { background: var(--melon-wash); }
.pricing-credits td { color: var(--ink); font-size: 1.25rem; font-weight: 800; }
.pricing-details { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; margin-top: 3rem; }
.pricing-details h2 { font-size: 1.35rem; line-height: 1.25; margin-bottom: 0.8rem; }
.pricing-details p + p { margin-top: 0.8rem; }
.pricing-details a { color: var(--melon-deep); text-decoration: underline; text-underline-offset: 0.2em; }
@media (max-width: 620px) {
	.topnav { gap: 0.9rem; }
	.pricing-details { grid-template-columns: 1fr; gap: 2rem; }
	.pricing-table { font-size: 0.85rem; }
	.pricing-table th, .pricing-table td { padding: 0.85rem 0.3rem; }
	.pricing-table thead th { font-size: 0.95rem; }
	.pricing-table caption { font-size: 0.95rem; }
	.pricing-feature { width: 44%; }
	.pricing-credits td { font-size: 1.1rem; }
}
