/* =========================================================================
   responsive.css — hand-written overrides layered on top of landing-website.css.
   Loaded last so it wins on specificity ties.

   landing-website.css started as a Figma export and is now ours: it has been
   renamed, its unreachable rules deleted, and Figma is a visual reference
   rather than a source of code. Do not regenerate it — edit it. The split
   between the two files is historical, not a rule; the only thing it still
   buys is that this file is where the responsive thinking lives.

   Phase 1: page shell (max 1440, centered) + responsive navigation.
   ========================================================================= */

/* Breakpoints used in this file, and only these:

     1199px  the export's own desktop edge; where the 120px hero inset and the
             five-across category row give up
      900px  where the five nav labels stop fitting beside the logo, so the
             hamburger appears. Its complement is written `width > 900px`
      700px  where two-column layouts become one column and the card rows turn
             into swipe carousels. Complement: `700px < width <= 1199px`
      640px  small-phone adjustments inside layouts that already collapsed
      480px / 340px  two isolated squeezes; see the rules that use them

   They are literals on purpose: `@media` is evaluated before custom
   properties resolve, so a breakpoint cannot be a `var()`. Listing them here
   is the substitute — one place to read the ladder, even though each query
   still has to spell its own number. Adding a seventh means asking first
   whether one of these six is the same idea. */

:root {
	--site-max-width: 1440px;

	/* The two families, with their fallbacks attached. The export names them
	   bare — `font-family: 'Playfair Display'` — so a browser that has not
	   finished loading the webfont falls back to whatever `initial` is rather
	   than to a serif. Anything we write uses these instead, which also means
	   the fallback stack is decided in one place instead of per rule. */
	--font-display: 'Playfair Display', Georgia, serif;
	--font-body: 'DM Sans', system-ui, sans-serif;

	/* Colours the export used as raw hex, named for the job they do here.
	   `--plum-900`/`--plum-700` are the two stops of the header gradient;
	   `--plum-seam` is that gradient's value 22% down, which is what the
	   mobile hero fades the photo into. They are not in global.css's token
	   set — the export inlined them — so they are declared here rather than
	   left as three unexplained hex codes in the middle of a gradient. */
	--surface-white: #fff;
	--plum-900: #281726;
	--plum-700: #492e45;
	--plum-seam: #3a2337;

	/* The hero photograph, in one place. It is painted twice — as a background
	   on `.hero` for desktop and as a `#hero::after` layer on phones, where
	   it needs a different crop — and those two were free to drift apart. */
	--hero-photo: url(../assets/hero-app-in-hands.png);

	/* The scrim the category cards use: the photo stays untouched down to 65%
	   and then darkens, so the caption sitting at the bottom stays readable
	   without dimming the image itself. Shared so every photo card in the page
	   gets the same treatment. */
	--photo-scrim: linear-gradient(180deg, rgba(71, 31, 67, 0) 65%, rgba(71, 31, 67, 0.8) 100%);
	--photo-veil: linear-gradient(rgba(71, 31, 67, 0.93), rgba(71, 31, 67, 0.93));
}

/* -------------------------------------------------------------------------
   Page shell
   ------------------------------------------------------------------------- */

body {
	/* The export made <body> the flex container and the `body` container-query
	   context. Both move to .site, which is the element that actually lays the
	   sections out. */
	display: block;
	container: none;
	/* The export left 19.6px here, which every element without a size of its
	   own then inherits — a fractional default on some 200 elements per page.
	   20px is the same size to the eye and a whole pixel. */
	font-size: 20px;
	background-color: var(--background-500-base);
	overflow-x: hidden;
}

/* Full width on purpose. The 1440 cap lives on each section's *content*
   (see "Full-bleed section backgrounds" below) rather than on the shell, so
   every section paints its background edge to edge while the content inside
   it stays centred and capped. Capping the shell instead would leave the
   page floating in a white box on wide screens. */
.site {
	container: body / inline-size;
	position: relative;
	width: 100%;
	display: flex;
	flex-direction: column;
	background-color: var(--surface-white);
	overflow: hidden; /* clips the leftover absolute blocks from the export */
}

/* <main> sits between .site and the sections, so it has to pass the column
   layout through rather than collapse them into one block. */
.site > main {
	display: flex;
	flex-direction: column;
}

/* -------------------------------------------------------------------------
   Full-bleed section backgrounds

   Each section spans the whole viewport so its background colour or photo
   reaches both edges, and the content is brought back to 1440 by padding
   instead of by a width cap. Padding keeps the section's own box full width
   — which is what the background paints — while `container-type: inline-size`
   measures the *content* box, so the export's `@container hero/about/
   categories/how-it-works/footer` rules keep seeing exactly the widths they
   saw before.

   The `max(0px, ...)` half-difference is 0 below 1440 and grows from there,
   so on phones and tablets this is just the gutter and nothing changes. The
   gutter is added on top of the centring padding rather than folded into it,
   so the content band on a 2560px screen is the same band as on a 1440px one
   instead of 48px wider.
   ------------------------------------------------------------------------- */

.site > main > section,
.site > main > .founding-cta, /* the "Join the first 100" banner is a bare div */
.site > footer {
	/* Per-section so the hero can keep the export's wider inset. */
	--gutter: 24px;
	padding-inline: calc(max(0px, (100% - var(--site-max-width)) / 2) + var(--gutter));
}

/* The hero and the "Join the first 100" banner are inset by 120px in the
   export rather than by the 24px every .section uses. This rule outranks the
   export's own breakpoint (it is more specific), so the collapse to a small
   gutter has to be restated here. 1199px rather than the export's 576px: a
   120px inset on a 700px screen leaves the content in a 460px column. */
.site > main > #hero,
.site > main > .founding-cta {
	--gutter: 120px;
}

@media (max-width: 1199px) {
	.site > main > #hero,
	.site > main > .founding-cta {
		--gutter: 24px;
	}
}

/* The hero photo. Only the image is overridden, not the whole shorthand: the
   export's `center / cover no-repeat` on `.hero` is still what we want, and
   restating it would mean two places to keep in step.

   The file the export names there (assets/col.png) has been deleted: this rule
   replaces the layer outright, so the browser never requests it. Same for the
   two photos on `.founder-left`/`.founder-right` below. Those three dead paths
   are still in landing-website.css; if one of these overrides is ever removed,
   the rule 404s — which is the loud failure we want, rather than silently
   getting the old artwork back. */
.hero {
	background-image: var(--hero-photo);
}

/* The two backdrop shapes sit behind the hero (z-index 2 and 3 against its 4),
   so they are only ever visible where they stick out past it. The export gave
   them a fixed 852px — the desktop hero's height — but on a phone the hero is
   100svh, which is shorter on most screens. The overhang painted a plum band
   between the bottom of the photo and the next section.

   Capping at the hero's own floor (`main > section` gives every section
   min-height: 100svh) makes the overhang impossible at any viewport size,
   without having to know how tall the hero actually ends up. */
.hero-backdrop {
	height: min(852px, 100svh);
}

/* -------------------------------------------------------------------------
   Navigation
   ------------------------------------------------------------------------- */

.site-nav {
	position: relative;
	z-index: 20;
	margin-left: 0;
	display: flex;
	/* Explicit: `@container hero (width < 1197px)` in the export sets
	   `.site-nav-row { flex-direction: column }`, and this nav still carries that
	   class. Same specificity, so we must declare the property to win it. */
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	align-self: stretch; /* the export centered .site-nav-row; we want full width */
}

/* Same container query adds `text-align: center` to every .site-nav-row child. */
.site-nav > * {
	text-align: left;
}

.site-nav-logo {
	display: inline-flex;
	flex-shrink: 0;
}

/* Driven by height, not width: the height is what has to line up with the nav
   links beside it. The export sized it as `width: 115px` on a 230x112 asset,
   which happened to land on 56px tall. */
.site-nav-logo .site-nav-logo-img {
	margin: 0;
	display: block;
	width: auto;
	height: 64px;
}

/* 56px below the same breakpoint where the nav collapses to the hamburger,
   so the logo and the menu change together rather than at two separate
   widths. */
@media (max-width: 900px) {
	.site-nav-logo .site-nav-logo-img {
		height: 56px;
	}
}

.site-nav-menu {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	align-items: center;
	gap: 32px;
}

/* Kill the 540px spacer the export used to push the links to the right;
   justify-content on .site-nav does that job now. */
.site-nav-menu .site-nav-link-first {
	margin-left: 0;
	margin-inline: 0;
}

.site-nav-menu .site-nav-link {
	margin-top: 0;
	margin-right: 0;
	display: block;
	padding: 8px 0;
	text-decoration: none;
	white-space: nowrap;
	transition: color 0.2s ease;
}

.site-nav-menu .site-nav-link:hover,
.site-nav-menu .site-nav-link:focus-visible {
	color: var(--secondary-500-base);
}

/* --- Hamburger button (hidden on desktop) --- */

.site-nav-toggle {
	display: none;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 44px;
	height: 44px;
	padding: 10px;
	border: 0;
	background: transparent;
	cursor: pointer;
}

.site-nav-toggle span {
	display: block;
	width: 100%;
	height: 2px;
	border-radius: 2px;
	background-color: var(--background-600);
	transition: transform 0.25s ease, opacity 0.2s ease;
}

/* Morph the three bars into an X while the panel is open. */
.site-nav.is-open .site-nav-toggle span:nth-child(1) {
	transform: translateY(7px) rotate(45deg);
}

.site-nav.is-open .site-nav-toggle span:nth-child(2) {
	opacity: 0;
}

.site-nav.is-open .site-nav-toggle span:nth-child(3) {
	transform: translateY(-7px) rotate(-45deg);
}

/* -------------------------------------------------------------------------
   Mobile navigation
   ------------------------------------------------------------------------- */

/* 900px: the point where the five nav labels stop fitting beside the logo.
   Kept as a literal — @media cannot read custom properties. */
@media (max-width: 900px) {
	.site-nav-toggle {
		display: flex;
	}

	/* A full-bleed sheet hanging off the bar, not a floating card.

	   `left: 0; right: 0` would only span the nav, which is inset by the
	   page gutter on both sides. The nav is centred in the viewport, so
	   pinning the panel's left edge to the nav's midpoint and pulling it
	   back half its own width puts it on the screen's edges whatever that
	   gutter turns out to be — and it works the same under the hero's
	   padding as under .doc-header's.

	   The pull-back is `translate` rather than a negative margin because
	   the export's `@container hero (width < 1197px)` rule sets
	   `margin-left: unset !important` on every .site-nav-row child, which
	   silently eats a margin here — and only on index.html, where the nav
	   sits inside the hero container, so it would have looked correct on
	   every other page. `translate` is also its own property, so it
	   composes with the open/close `transform` below instead of fighting it.

	   It also sits flush against the bar now: the old 16px gap was the
	   breathing room a rounded card needed to read as separate, and an
	   edge-to-edge sheet reads as attached instead. The shadow stays — it
	   is what lifts the panel off the content it covers. */
	.site-nav-menu {
		position: absolute;
		top: 100%;
		left: 50%;
		right: auto;
		width: 100vw;
		translate: -50%;
		flex-direction: column;
		align-items: stretch;
		gap: 4px;
		padding: 12px;
		border-radius: 0;
		background-color: var(--primary-500-base);
		box-shadow: 0 16px 40px rgba(40, 23, 38, 0.35);

		/* Animate open/close without display:none, so the transition runs. */
		opacity: 0;
		visibility: hidden;
		transform: translateY(-8px);
		transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
	}

	.site-nav.is-open .site-nav-menu {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
	}

	.site-nav-menu .site-nav-link {
		padding: 14px 16px;
		border-radius: 10px;
		font-size: 17px;
	}

	.site-nav-menu .site-nav-link:hover,
	.site-nav-menu .site-nav-link:focus-visible {
		background-color: var(--primary-400);
		color: var(--surface-white);
	}
}

@media (prefers-reduced-motion: reduce) {
	.site-nav-menu,
	.site-nav-toggle span {
		transition: none;
	}
}

/* =========================================================================
   Phase 2: content rows

   The export sized every row for a 1440px canvas: fixed pixel widths paired
   with `flex-shrink: 0`, spaced by huge gaps and nudged with negative margins.
   That combination cannot shrink, so anything below 1440px overflows sideways.

   The fix is the same shape everywhere: replace the fixed-width flex row with
   an auto-fitting grid, and neutralise the pixel widths and negative margins
   the export used for optical alignment.
   ========================================================================= */

/* Images never drive the layout wider than their column. */
img {
	max-width: 100%;
	height: auto;
}

/* --- "Discover / Choose / Book the Look" (3 feature columns) --- */

.steps {
	margin-left: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 48px;
	align-items: start;
	width: 100%;
	max-width: 1000px;
	margin-inline: auto;
}

.steps > .step-left,
.steps > .step-center,
.steps > .step-right {
	margin: 0;
	width: auto;
	min-width: 0; /* lets a grid item shrink below its content's intrinsic size */
}

.steps .step {
	margin-block: 0;
}

/* The third step is `column-a col-right1` in the markup now, like the other
   two, but the export still gives `.step-right` its own 9px gap — and since
   both selectors weigh (0,1,0), the one written later in landing-website.css
   wins, which is that one. Hence restating the shared gap here rather than
   relying on `.step` to carry it.

   9px against the others' 16px is what pulled "Book the Look" and its
   description up a few pixels: two gaps above the copy instead of one, so the
   drift compounded down the column. */
.steps > .step-right {
	gap: 16px;
}

/* The export centred these columns optically with per-element nudges. */
.steps .step-shot,
.steps .step-center .step-shot-inner {
	margin: 0;
	width: 176px;
}

/* The screenshots are RGBA with the rounded frame and its pale background
   already painted in — the corners of the file are transparent. The export
   drew a second 5px ring around that with `border` + `border-radius`, which
   is the double edge. The artwork owns the shape, so nothing is needed here.

   Dropping the border also gives back the 10px it was eating: box-sizing is
   border-box (reset.css:28), so the 176px box was rendering a 166px image. */
.step-shot {
	border: 0;
	border-radius: 0;
}

.steps .step-title,
.steps .step-left .step-title {
	margin-left: 0;
	/* Full column width, not the 176px of the screenshot above it. Capping the
	   heading to the image made "Book the Look" wrap onto a second line while
	   "Discover" and "Choose" stayed on one, which pushed that column's
	   description out of line with its neighbours. The column is ~300px, so all
	   three titles fit on one line and the three descriptions start level. */
	width: 100%;
	max-width: none;
	/* The heading is a flex box, so `text-align` alone would not move a single
	   line inside it — the line box is only as wide as the words. */
	justify-content: center;
}

/* .step-right was the only column without centring; the export relied on its
   fixed width instead. */
.steps > * {
	align-items: center;
}

/* Every step is a centred stack: screenshot, title, description.

   Two things fight this in the export. The headings carry `text-align: left`
   (landing-website.css:257 and :289) — Figma writes down where the text
   happened to sit, not that it was meant to hang off the left of a column
   that is otherwise centred. And `@container about (width < 1002px)` resets
   `.steps > .step-left > *` and friends to `text-align: unset`, which throws
   away the `center` that `.step-text` sets — so below 1002px the descriptions
   go left too, which is the misalignment that shows up in practice.

   `.about` in front of the selector is only there for weight: it takes this to
   (0,3,1), above the container query's (0,2,1). Being later in the file is not
   enough on its own, since specificity is compared before source order and
   at-rules do not enter into it. */
.about .steps > * > * {
	text-align: center;
}

/* Dots are built by JS for every track, so they must stay out of the way on
   the desktop grids, where every slide is visible at once. */
.carousel-dots {
	display: none;
}

/* --- Horizontal carousels on small screens ---------------------------------
   Two rows share this behaviour: the "Discover / Choose / Book the Look"
   steps and the category cards. Scroll snapping does the paging natively, so
   no JS is involved in the movement itself — touch flicks, trackpads and
   keyboard scrolling all behave the way the OS already does. JS only mirrors
   the position into the dots. */

@media (max-width: 700px) {
	#what-is-glamgo .steps,
	#categories .category-row {
		display: flex;
		/* The export's own container queries (`col1`/`col9 width < N`) stack
		   these into a column. A media query does not outrank a container
		   query, so the direction has to be restated here explicitly. */
		flex-direction: row;
		align-items: stretch;
		grid-template-columns: none;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		/* Hide the scrollbar: the dots are the affordance here. */
		scrollbar-width: none;
		/* Cancel the section's side padding so the track spans edge to edge. */
		margin-inline: -24px;
		width: calc(100% + 48px);
		max-width: none;
	}

	#what-is-glamgo .steps::-webkit-scrollbar,
	#categories .category-row::-webkit-scrollbar {
		display: none;
	}

	#what-is-glamgo .steps > *,
	#categories .category-row > * {
		/* flex-basis alone is not enough: flex items shrink by default, so
		   they would all squeeze onto one screen instead of scrolling. */
		flex-shrink: 0;
		scroll-snap-align: center;
	}

	/* One step per screen: they are text blocks, and a partial one is just
	   truncated copy. */
	#what-is-glamgo .steps {
		gap: 0;
	}

	#what-is-glamgo .steps > * {
		flex-basis: 100%;
		padding-inline: 24px;
	}

	/* The category cards are 226/352 portraits — full width would make each
	   one taller than the screen. A narrower slide also leaves the next card
	   peeking in, which advertises that the row scrolls. */
	#categories .category-row {
		gap: 12px;
		padding-inline: 24px;
		/* Without this the snapped card sits flush against the viewport edge
		   instead of aligned with the rest of the section's content. */
		scroll-padding-inline: 24px;
	}

	#categories .category-row > * {
		flex-basis: 68%;
		max-width: 260px;
	}

	#what-is-glamgo .carousel-dots,
	#categories .carousel-dots {
		display: flex;
		justify-content: center;
		gap: 10px;
		margin-top: 20px;
	}

	.carousel-dot {
		width: 9px;
		height: 9px;
		padding: 0;
		border: 0;
		border-radius: 50%;
		background-color: var(--primary-200);
		opacity: 0.4;
		cursor: pointer;
		transition: opacity 0.2s, background-color 0.2s;
	}

	.carousel-dot[aria-current='true'] {
		background-color: var(--primary-500-base);
		opacity: 1;
	}

	.carousel-dot:focus-visible {
		outline: 2px solid var(--primary-500-base);
		outline-offset: 3px;
	}
}

/* --- "Meet the Founders" (2 cards) --- */

.founders-row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 26px;
}

.founders-row > .founder {
	width: auto;
	min-width: 0;
}

/* The export put a flat rgba(145, 100, 140, 0.6) wash over the whole photo on
   top of the bottom fade, which is why these two read as muted next to the
   category cards. Drop the wash and keep only the scrim.
   The export's `background-size: auto auto, auto auto, cover` listed one size
   per layer, so removing a layer would have left the photo at `auto auto`
   (its intrinsic pixel size) instead of cover — hence the explicit reset. */
/* The photo hangs on the front face, not on .founder. On the card itself it
   would stay put while the text panel turned in front of it, and the middle of
   every flip showed a still portrait with nothing on it. */
.founder-left,
.founder-right {
	background-image: none;
}

.founder-front,
.founder-back {
	background-size: cover;
	background-position: center;
}

.founder-left .founder-front {
	background-image: var(--photo-scrim), url(../assets/audience-clients.png);
}

.founder-right .founder-front {
	background-image: var(--photo-scrim), url(../assets/audience-providers.png);
}

/* The reverse keeps the same portrait under a near-opaque veil rather than
   going flat: it holds the card's colour through the turn, and the bio still
   reads at AA against it. A flat panel makes the back look like a different
   component that swapped in. */
.founder-left .founder-back {
	background-image: var(--photo-veil), url(../assets/audience-clients.png);
}

.founder-right .founder-back {
	background-image: var(--photo-veil), url(../assets/audience-providers.png);
}

/* --- founder flip ---
   Two faces on one card: the photo and title block, and the bio behind it.
   Turned by hover on a mouse, and by the More button everywhere — the button
   is what makes the bio reachable from a keyboard, and the only affordance a
   touch screen has, since it has no hover state to discover the card with.

   The rotation is on .founder-inner rather than .founder because .founder is
   already a reveal target: `data-reveal-group` in the markup hands each card a
   `transform: translate3d(...)` entrance, and a second rule setting `transform`
   on the same element replaces it rather than adding to it. */
.founder {
	position: relative;
	padding: 0;
	perspective: 1200px;
}

.founder-inner {
	display: grid;
	flex: 1;
	transform-style: preserve-3d;
	transition: transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Both faces in the same grid cell, so they stack without leaving the flow.
   Absolute positioning would collapse the card to its min-height and clip the
   longer bio on a narrow phone; stacked in one cell the card takes the height
   of its tallest face, and the row's grid stretch keeps the pair level. */
.founder-front,
.founder-back {
	grid-area: 1 / 1;
	min-width: 0;
	border-radius: 16px;
	padding: 18px 26px;
	/* Safari still needs the prefix for the property that hides the reverse
	   side of a turned face; without it both faces paint at once mid-turn. */
	-webkit-backface-visibility: hidden;
	backface-visibility: hidden;
}

.founder-front {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	gap: 1px;
}

/* Centred rather than bottom-aligned like the front: the bio is a block of
   running text with nothing under it, so it should sit in the panel instead
   of hanging off its floor. Its ground is set with the photos further down. */
.founder-back {
	display: flex;
	align-items: center;
	transform: rotateY(180deg);
	/* Clears the toggle sitting in the top-right corner. */
	padding-top: 58px;
}

.founder-bio {
	color: var(--background-400);
	font-size: 15px;
	line-height: 1.55;
	text-align: left;
	margin: 0;
}

/* Outside .founder-inner on purpose: a control that rides the face it turns
   would spin away from the pointer that is trying to press it. */
.founder-toggle {
	position: absolute;
	top: 16px;
	right: 16px;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	background-color: rgba(255, 251, 252, 0.16);
	color: var(--background-400);
	font-family: 'DM Sans';
	font-size: 14px;
	font-weight: 500;
	line-height: 1;
	border: 1px solid rgba(255, 251, 252, 0.5);
	border-radius: 999px;
	cursor: pointer;
	padding: 8px 16px;
	transition: background-color 0.2s ease;
	-webkit-backdrop-filter: blur(4px);
	backdrop-filter: blur(4px);
}

.founder-toggle:hover {
	background-color: rgba(255, 251, 252, 0.3);
}

.founder-toggle:focus-visible {
	outline: 2px solid var(--background-400);
	outline-offset: 2px;
}

.founder-toggle-back {
	display: none;
}

.founder.is-flipped .founder-inner {
	transform: rotateY(180deg);
}

.founder.is-flipped .founder-toggle-more {
	display: none;
}

.founder.is-flipped .founder-toggle-back {
	display: inline;
}

/* Hover only where there is a real pointer. `(hover: hover)` alone is true on
   some touch devices that emulate a hover on first tap, which would turn the
   card on a tap meant to scroll past it. */
@media (hover: hover) and (pointer: fine) {
	.founder:hover .founder-inner {
		transform: rotateY(180deg);
	}

	/* The label has to follow the hover turn too, or the card reads "More"
	   while it is already showing the bio. */
	.founder:hover .founder-toggle-more {
		display: none;
	}

	.founder:hover .founder-toggle-back {
		display: inline;
	}
}

/* A card that rotates in depth is exactly the kind of motion this setting is
   asking us to drop, so the same two states arrive as a cross-fade instead. */
@media (prefers-reduced-motion: reduce) {
	.founder-inner,
	.founder:hover .founder-inner,
	.founder.is-flipped .founder-inner {
		transform: none;
	}

	.founder-back {
		transform: none;
		opacity: 0;
		transition: opacity 0.2s ease;
	}

	.founder-front {
		transition: opacity 0.2s ease;
	}

	.founder.is-flipped .founder-back {
		opacity: 1;
	}

	.founder.is-flipped .founder-front {
		opacity: 0;
	}
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
	.founder:hover .founder-back {
		opacity: 1;
	}

	.founder:hover .founder-front {
		opacity: 0;
	}
}

@media (max-width: 640px) {
	.founders {
		padding: 24px var(--margin-sm) 32px;
	}
}

/* --- "Explore Beauty by Category" (5 image cards) --- */

.category-row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 16px;
	width: 100%;
}

/* Five cards divide badly on tablet: auto-fit picks 4 columns and strands the
   fifth alone. Pin it to 3 (3+2) until there is room for all five in a row. */
@media (700px < width <= 1199px) {
	.category-row {
		grid-template-columns: repeat(3, 1fr);
	}
}

/* `.category`/`.category-nails`/`.category-lashes` all shared a 226px width plus 300px of
   top padding to push the caption to the bottom. An aspect ratio plus flex
   end-alignment does the same thing at any width. */
.category-row > * {
	width: auto;
	min-width: 0;
	min-height: 0;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	aspect-ratio: 226 / 352;
	padding: 24px 20px 28px;
	/* The export gave each card a different text-align + asymmetric padding
	   (61/45/68px) to fake centring at its fixed width. Centre them for real. */
	text-align: center;
}

/* --- "Join the first 100" banner --- */

/* -201px pulled the paragraph left to overlap the heading's column at 1440px.
   Once .founding-cta stacks (container query in the export), it drags text offscreen. */
.founding-cta-text {
	margin: 0;
}

.founding-cta-body {
	max-width: 100%;
}

/* The banner is a headline on the left and a pitch answering it from the
   right — the outward pull is what makes the two read as one line of thought
   rather than two stacked paragraphs. Both are restated here rather than left
   to the export, since they are now a requirement instead of a leftover of
   where Figma happened to place the boxes. */
.founding-cta-title {
	text-align: left;
}

.founding-cta-text {
	text-align: right;
	/* Fixed, unlike every other supporting line on the page: this one sits
	   against a 48px headline in a dark band, and the h1 support ramp's 22px
	   made it compete with the headline instead of answering it. */
	font-size: var(--type-body-lg);
}

/* The CTA became an <a> so it can navigate. Everything the export drew still
   applies — `.btn1` already makes it a flex box — but an anchor brings its own
   underline and its own colour, which `.body-md1` would otherwise lose to the
   user-agent's link styling. */
.founding-cta-btn {
	text-decoration: none;
	color: var(--background-400);
}

@media (max-width: 640px) {
	.founding-cta {
		padding-block: 40px;
	}

	/* Once the band is one narrow column, opposite edges stop reading as a
	   pair and just look misaligned, so both centre. */
	.founding-cta-title,
	.founding-cta-body,
	.founding-cta-text {
		text-align: center;
	}

	.founding-cta-btn {
		width: 100%;
		align-self: stretch;
	}
}

/* =========================================================================
   Phase 3: Founding Perks

   Replaces .row2 / .row3 / .row4 from the export. Those were a flat row of
   six siblings (icon column, text column, icon column, ...) plus a seventh
   perk stranded outside, with the header image pinned at `left: 818px`.

   Now: three self-contained groups, each with an image header and its own
   list, so wrapping never separates an icon from its text.
   ========================================================================= */

.perks {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: 24px;
	align-items: start;
	width: 100%;
	max-width: 1200px;
	margin-inline: auto;
	padding-inline: var(--margin-md);
}

.perk-group {
	display: flex;
	flex-direction: column;
	gap: 20px;
	min-width: 0;
}

/* Image header: same gradient + photo treatment the export gave .financial,
   but sized by aspect-ratio instead of 258px of top padding. */
.perk-group-title {
	display: flex;
	align-items: flex-end;
	justify-content: center;
	/* The photos are 768x656. Any other ratio here makes `cover` crop them,
	   so the box follows the asset rather than the other way round. */
	aspect-ratio: 768 / 656;
	margin: 0;
	padding: 20px 16px 24px;
	border-radius: 16px;
	color: var(--background-400);
	font-size: 28px;
	font-family: var(--font-display);
	font-weight: 600;
	line-height: 1.1;
	text-align: center;
	background-position: center;
	background-size: cover;
	background-repeat: no-repeat;
}

.perk-group-financial {
	background-image:
		var(--photo-scrim),
		url(../assets/perk-financial.png);
}

.perk-group-operations {
	background-image:
		var(--photo-scrim),
		url(../assets/perk-operations.png);
}

.perk-group-access {
	background-image:
		var(--photo-scrim),
		url(../assets/perk-access.png);
}

.perk-list {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.perk {
	display: flex;
	align-items: flex-start;
	gap: 14px;
}

.perk-check {
	flex: none;
	width: 32px;
	margin-top: 2px;
}

.perk-text {
	min-width: 0;
	margin: 0;
	color: var(--primary-500-base);
	font-size: var(--type-body-lg);
	font-family: var(--font-body);
	font-weight: 500;
	line-height: 1.333;
	text-align: left;
}

@media (max-width: 640px) {
	.perks {
		padding-inline: var(--margin-sm);
	}

	.perk-group-title {
		/* Only the type shrinks here. Overriding the aspect ratio made `cover`
		   crop a third of the photo's height away on phones, which is where
		   the header is widest and the crop most visible. */
		font-size: 24px;
	}
}

/* =========================================================================
   Phase 4: "See GlamGo in Action" demo cards

   The export drew the first card with ten absolutely-positioned pieces at
   literal Figma coordinates (`.cliennt-demo1 { top: 43px; left: 577px }`),
   plus a `.rect2` div standing in for the card background.

   Both cards are now the same two-column flow layout, mirrored, sharing one
   `.demo-media` stack for the phone-over-ornament composition.
   ========================================================================= */

.demo-card {
	display: flex;
	align-items: center;
	gap: 48px;
	width: 100%;
	max-width: 995px;
	margin-inline: auto;
	padding: 56px 64px;
	border-radius: 24px;
	background-color: var(--background-400);
	/* The ornament is wider than its column by design; clip it to the card's
	   rounded edge instead of letting it push the page sideways. */
	overflow: hidden;
}

.demo-card-body {
	flex: 1 1 340px;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* These three were absolutely positioned; they are now plain flow children. */
.demo-card .demo-title-clients,
.demo-card .demo-lede-clients {
	position: static;
	min-height: 0;
}

.demo-steps {
	margin: 20px 0 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 18px;
}

.demo-step {
	display: flex;
	align-items: flex-start;
	gap: 10px;
}

.demo-step-check {
	flex: none;
	width: 24px;
	margin-top: 3px;
}

.demo-step-text {
	min-width: 0;
	margin: 0;
	text-align: left;
}

/* --- Match the "For Clients" card's type to the "For Beauty Providers" one ---

   The two cards are the same component shown twice, but the export gave them
   different text classes: the clients side got the fluid `display-web-xl2` /
   `body-web-lg1` / `body-md2`, which interpolate with the viewport, while the
   providers side got the fixed `display-web-xl1` / `body-web-lg3` / `row-text`.
   They only agree at 1440, where the fluid scales top out; anywhere below that
   the clients card is visibly smaller than the card right beneath it — 27.8px
   against 32px at a 1086px viewport, for instance.

   The providers card is the reference, so these are its computed values,
   pinned. Line heights already match (both cards use the same ratios), and so
   do the colours; only size — and the step text's weight — were out of step.

   The heading and its supporting line have since moved to the site-wide
   heading scale at the bottom of this file, which drives both cards from one
   pair of selectors; only the step text's weight is still pinned here. */
.demo-card-clients .demo-step-text {
	font-size: var(--type-body);
	font-weight: 400;
}

/* --- Shared media stack: phone mockup over the decorative ornament --- */

.demo-media {
	position: relative;
	flex: 0 1 380px;
	/* Reset the export's fixed 380x575 box on .demo-media-providers. */
	width: auto;
	height: auto;
	aspect-ratio: 380 / 575;
	display: flex;
	align-items: center;
	justify-content: center;
}

.demo-media-phone {
	position: relative;
	z-index: 1;
	display: block;
	width: 72%;
	max-width: 268px;

	/* The clips are 296x640. Pinning their ratio means the card reserves the
	   right space before the video's metadata arrives, so nothing jumps, and
	   the video sits at very close to its native shape. `cover` absorbs what is
	   left: the ~1% the poster PNG differs by (536x1148), and the couple of
	   percent the border costs, since box-sizing is border-box and the ratio
	   therefore describes the border box rather than the video inside it. */
	aspect-ratio: 296 / 640;
	object-fit: cover;

	/* Unlike the poster, video frames are square-cornered, so the rounding has
	   to come from CSS here. Two percentages rather than one length: 16% of
	   the width and 7.5% of the height are both ~43px at full size and stay
	   circular as the card shrinks, where a fixed 43px would not. */
	border-radius: 16% / 7.5%;

	/* The recordings are raw screen captures with no device frame of their
	   own, so this ring is the phone's bezel. The inner radius follows from
	   the outer one minus the border on its own — no second value needed. */
	border: 5px solid var(--background-600);
}

/* Purely decorative; sits behind and slightly below the phone. The export
   pinned it at left:-99px, which is what pushed it outside the card. */
.demo-media-layer {
	position: absolute;
	z-index: 0;
	top: 58%;
	left: 50%;
	width: 165%;
	max-width: none;
	transform: translate(-50%, -50%);
	pointer-events: none;
}

/* --- Mirrored card below (For Beauty Providers) --- */

.demo-card-providers {
	align-items: center;
	gap: 48px;
	width: 100%;
	max-width: 995px;
	margin-inline: auto;
	padding: 56px 64px;
	overflow: hidden;
}

.demo-card-body-providers {
	flex: 1 1 340px;
	min-width: 0;
	margin-top: 0;
	width: auto;
}

/* The export nudged each row's text with negative right margins sized for a
   fixed-width column (-155px, -51px, -21px); they overflow once it is fluid. */
.demo-card-providers .demo-row,
.demo-card-providers .demo-row-3 {
	margin: 0;
	width: auto;
	max-width: none;
	align-self: stretch;
}

.demo-card-providers .demo-row-text,
.demo-card-providers .demo-row-text-last {
	margin: 0;
	min-width: 0;
}

.demo-card-providers .demo-lede-providers {
	margin: 0;
}

/* The column is `align-items: center` in the export, which shrink-wraps its
   children to their text and then centres the box. That hides `text-align`
   entirely — a box only as wide as its words has nothing to align inside.
   Stretching them to the column makes the alignment below a real choice
   rather than a side effect of how long the sentence happens to be. */
.demo-card-providers .demo-title-providers,
.demo-card-providers .demo-lede-providers {
	align-self: stretch;
	text-align: left;
}

.demo-card-providers .demo-row-1 {
	margin-top: 20px;
}

@media (max-width: 900px) {
	.demo-card,
	.demo-card-providers {
		flex-direction: column;
		gap: 40px;
		padding: 40px var(--margin-md);
	}

	/* Text first, phone second, on both cards. */
	.demo-card .demo-media,
	.demo-card-providers .demo-media {
		order: 2;
		flex: none;
		width: 100%;
		max-width: 340px;
	}

	.demo-card-body,
	.demo-card-body-providers {
		order: 1;
		flex: none;
		width: 100%;
	}

	/* Both cards' heading and supporting line centre once the card stacks.
	   Side by side, the text column has the phone beside it and its own left
	   edge to read down, so the heading belongs on that edge. Stacked, the
	   text column *is* the card, and a left-aligned heading over a narrow
	   phone-width block reads as an accident rather than a choice.

	   The checklist below them stays left in both layouts: a list of steps
	   reads down a common left edge at any width.

	   `.demo-title-clients` is a flex box in the export, so it needs
	   `justify-content` too — `text-align` alone would not move a line box
	   that is only as wide as its words. */
	.demo-card .demo-title-clients,
	.demo-card .demo-lede-clients,
	.demo-card-providers .demo-title-providers,
	.demo-card-providers .demo-lede-providers {
		text-align: center;
		justify-content: center;
	}
}

@media (max-width: 640px) {
	.demo-card,
	.demo-card-providers {
		padding: 32px var(--margin-sm);
	}
}

/* --- Buttons: let the hero CTAs wrap instead of overflowing --- */

.hero-actions {
	flex-wrap: wrap;
}

@media (max-width: 480px) {
	.hero-actions {
		align-self: stretch;
	}

	.hero-actions > .hero-btn-primary,
	.hero-actions > .hero-btn-secondary {
		width: 100%;
	}
}

/* ---------------------------------------------------------------------------
   Tabs (audience toggles) + FAQ accordion
   The export baked the *state* into the markup: the selected option was a
   <button>, the unselected one a <p>, and open/closed FAQ entries were four
   different structures. Both are one shape here, and CSS reads the ARIA
   attribute instead of a separate class, so the a11y state and the visual
   state cannot drift apart.
--------------------------------------------------------------------------- */

.toggle {
	align-items: center;
	gap: 4px;
	padding: 6px;
	justify-content: center;

	/* One row, always. Two things used to break the pill onto two lines on a
	   phone: `@container about (width < 428px)` in the export switches this to
	   `flex-direction: column`, and the labels wrapped anyway because at a
	   fixed 16px with 28px of padding a side the pill needs ~364px of the
	   ~342px a 390px screen actually offers. The sizing below fixes the second
	   half; this line has to restate the direction because the rule above only
	   wins the properties it declares — the container query still supplies
	   whatever this rule leaves unsaid. */
	flex-direction: row;
	flex-wrap: nowrap;
}

.toggle-option {
	flex: 0 1 auto;
	min-width: 0;
	border: 0;
	cursor: pointer;
	font-family: var(--font-body);

	/* Fluid so the two labels always share one line. Measured intrinsic widths
	   are 78.5px and 157.1px at 16px, and the pill costs 4 paddings + 4px of
	   gap + 12px of its own padding on top of those. At the narrow end (320px
	   screen, 272px usable) that budget only closes if both the text and the
	   padding give: 13px and 12px land at ~259px, with room to spare. The
	   upper bounds are the design's own values, reached at ~433px and up. */
	font-size: clamp(13px, 3.7vw, 16px);
	padding: 9px clamp(12px, 4vw, 28px);
	/* The labels are two- and three-word phrases; breaking one mid-phrase to
	   save a few pixels reads worse than the slightly smaller type does. */
	white-space: nowrap;

	font-weight: 500;
	line-height: 1.25;
	text-align: center;
	color: var(--primary-500-base);
	background-color: transparent;
	border-radius: 9999px;
	transition: background-color 0.2s ease, color 0.2s ease;
}

.toggle-option[aria-selected='true'] {
	color: var(--background-400);
	background-color: var(--primary-500-base);
}

.toggle-option:focus-visible {
	outline: 2px solid var(--primary-300);
	outline-offset: 2px;
}

.toggle-panel {
	width: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
}

/* --- accordion --- */

.faq-item {
	padding: 0;
	overflow: hidden;
}

.faq-question {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	text-align: left;
	background: none;
	border: 0;
	cursor: pointer;
	padding: 20px 22px;
}

.faq-question-text {
	min-width: 0;
}

.faq-question:focus-visible {
	outline: 2px solid var(--primary-300);
	outline-offset: -2px;
}

/* Icon drawn in CSS rather than shipping faq-plus.png plus a .minus/.minus-line
   pair: one element that rotates its vertical bar away to become a minus. */
.faq-icon {
	position: relative;
	flex: none;
	width: 20px;
	height: 20px;
}

.faq-icon::before,
.faq-icon::after {
	content: '';
	position: absolute;
	inset: 50% 0 auto 0;
	height: 2px;
	background-color: var(--primary-500-base);
	transform: translateY(-50%);
	transition: transform 0.2s ease, opacity 0.2s ease;
}

.faq-icon::after {
	transform: translateY(-50%) rotate(90deg);
}

.faq-question[aria-expanded='true'] .faq-icon::after {
	transform: translateY(-50%) rotate(0deg);
	opacity: 0;
}

.faq-answer {
	padding: 0 22px 20px;
}

.faq-answer > p {
	margin: 0;
}

/* .toggle-panel declares display:flex, which would beat the UA sheet's
   [hidden] { display: none }. Restore it for every element we hide. */
[hidden] {
	display: none !important;
}

/* ---------------------------------------------------------------------------
   Hero copy column
   --------------------------------------------------------------------------
   The copy used to be a direct child of the flex column, so it stretched the
   full width and ran across the photo. It now lives in a two-column grid
   whose second column is intentionally empty: that keeps the right half of
   the background clear without hard-coding a width for the text.
--------------------------------------------------------------------------- */

.hero-body {
	display: grid;
	grid-template-columns: 1fr;
	width: 100%;
}

.hero-copy {
	display: flex;
	flex-direction: column;
	/* The gap .hero gave these three blocks when they were its own
	   children; the wrapper has to reproduce it. */
	gap: 24px;
	align-items: flex-start;
	min-width: 0;
}

/* Two columns only once there is room for both to be useful. Splitting at
   700px left the copy in a 295px column — the headline broke to two words a
   line and the hero grew past the viewport. This is the complement of the
   900px nav breakpoint, written as `width > 900px` rather than as
   `min-width: 901px`: viewport widths are fractional, so 900/901 left 900.5px
   matching neither rule. Range syntax makes the pair exact by construction. */
@media (width > 900px) {
	.hero-body {
		grid-template-columns: 1fr 1fr;
		gap: 48px;
	}
}

/* =========================================================================
   Hero background on small screens
   The photo is 1500x903 and the phone — the thing worth showing — sits at
   roughly 72% of its width, with the left third being plum backdrop. `cover`
   on a narrow viewport crops around the centre, so the phone falls off the
   right edge entirely and all that is left is a hand.

   Two changes: move the focal point right so the phone survives the crop,
   and lay a gradient over the top of the image so the headline and the
   paragraph keep their contrast instead of sitting on a pale forearm.
--------------------------------------------------------------------------- */

@media (max-width: 700px) {
	#hero {
		/* No extra padding: the section is exactly one screen tall (the
		   global min-height: 100svh) and the photo lives in whatever space
		   the copy leaves over. Backgrounds clip to the element on their
		   own, so anything that does not fit simply is not painted. */
		padding-bottom: 0;
		justify-content: flex-start;

		/* Just the export's header gradient. The photo is no longer one of
		   this element's background layers — see #hero::after below. */
		background-image: linear-gradient(180deg, var(--plum-900) 0%, var(--plum-700) 100%);
		background-position: top center;
		background-size: cover;
		background-repeat: no-repeat;
	}

	/* The export pushed the headline down 149px to sit clear of the desktop
	   navigation. On a phone that space is what the photo needs. */
	#hero .hero-title {
		margin-top: 24px;
	}

	/* The photo, as the last flex item of the hero column rather than a
	   background layer on the hero itself.

	   As a background it had to be given a fixed height (46% of the section),
	   which meant that on a short screen the copy simply overlapped it — the
	   two were sized independently and nothing reconciled them. As a flex item
	   with `flex: 1`, it takes exactly the space the copy leaves over, so the
	   overlap cannot happen at any viewport size.

	   `auto 100%` fills that space by height and lets the width overflow; the
	   background clips to the box, so anchoring it bottom-right crops the
	   image up and to the left, which is the part worth showing. */
	#hero::after {
		content: '';
		flex: 1 1 0;
		min-height: 0;
		align-self: stretch;
		/* Cancel the section's gutter so the photo reaches both screen edges.
		   Below 1440 the centring half-difference is 0, so the gutter is the
		   whole padding and negating it is exact. */
		margin-inline: calc(-1 * var(--gutter));
		background-image:
			/* Softens the seam where the photo meets the gradient above it.
			   --plum-seam is that gradient's colour at this point. */
			linear-gradient(180deg, var(--plum-seam) 0%, rgba(58, 35, 55, 0) 22%),
			var(--hero-photo);
		background-position: top center, right bottom;
		background-size: cover, auto 100%;
		background-repeat: no-repeat;
	}
}

/* ---------------------------------------------------------------------------
   Full-viewport sections
   Every section is at least one screen tall and centres its content.
   min-height, not height: the tall sections (FAQ, categories on mobile) still
   grow past it, so nothing is ever clipped or unreachable.
--------------------------------------------------------------------------- */

main > section {
	min-height: 100vh;
	/* svh = the viewport with the mobile browser chrome *shown*. Plain vh
	   measures it hidden, so on a phone the section overflows by the height
	   of the URL bar until you scroll. */
	min-height: 100svh;
	justify-content: center;
}

/* ---------------------------------------------------------------------------
   Anchor alignment for #what-is-glamgo
   The export gave .about margin-top: -61px, tucking it under the hero, and
   padding-top: 145px to push the content back down. The overlap is invisible
   while scrolling (the hero has z-index 4 and paints over it), but an anchor
   link lands on the section's real top edge — 61px up, still behind the hero.

   Removing the margin and taking the same 61px off the padding leaves the
   content at the identical position and lets the anchor land flush.
--------------------------------------------------------------------------- */

#what-is-glamgo {
	margin-top: 0;
	padding-top: 84px; /* 145 - 61 */
}


/* =========================================================================
   Phase 8: one heading scale for the whole page

   The brief is four sizes, each with a desktop and a mobile value:

                        desktop   mobile
     h1                  48px      32px
     support under h1    22px      18px
     h2                  32px      28px
     support under h2    18px      16px

   These are ramps rather than a breakpoint switch. The export already thinks
   that way — `--interpolate` in global.css interpolates every fluid class
   between a 430px and a 1440px viewport — so the same two anchors are reused
   here, which means the numbers above are exact at both ends and the type
   grows smoothly in between instead of jumping when a tablet rotates.

   Written as plain `clamp()` rather than by setting `--min-font-size` /
   `--max-font-size`, because those two feed a shared `--interpolate` declared
   on `*`: every element re-computes it, so an element that inherits a stray
   min/max from an ancestor silently picks up the wrong ramp. A variable that
   already holds the finished `clamp()` cannot be half-overridden that way.

   Why the whole page needs restating: the export mixes fluid and fixed type
   inside the same role. Section headings alone arrive as `.section-title` (27→48),
   `display-web-h2` (27→48), `display-web-h3` (fixed 48) and, on the demo
   cards, `display-web-xl2` (20→32) against `display-web-xl1` (fixed 32). All
   of those agree at 1440 — the width the design was drawn at — and disagree
   everywhere else, which is why the mismatch was invisible in Figma. */
:root {
	/* Two sizes per role, not a ramp between them. These were `clamp()` ramps
	   interpolating across --fluid-min-vw..--fluid-span, which hit 32 and 48
	   exactly at the anchors but drew every width in between at a fraction —
	   47.4297px at a 1288px viewport. Headings now land on whole pixels at
	   every width, at the cost of stepping rather than growing when the
	   viewport crosses 900px. The phone values are the defaults; the desktop
	   block below overrides them. */
	--type-h1: 32px;
	--type-h1-support: 18px;
	--type-h2: 28px;
	--type-h2-support: 16px;

	/* Body type does not ramp: below the headings the export draws everything
	   at one of three fixed sizes, and prose that resizes with the viewport is
	   harder to read, not easier. Named by role so the sizes stop being
	   repeated as bare pixels in a dozen unrelated rules. */
	--type-body: 16px;
	--type-body-lg: 18px;
	--type-body-sm: 14px;
}

/* 900px: the site's main breakpoint, the same one the nav collapses at. Kept
   as a literal because @media cannot read custom properties — see the
   breakpoint ladder at the top of this file. */
@media (width > 900px) {
	:root {
		--type-h1: 48px;
		--type-h1-support: 22px;
		--type-h2: 32px;
		--type-h2-support: 18px;
	}
}

/* h1 — the hero and every section title. `.section-title` alone covers four of the
   sections (Founding Provider, Categories, See GlamGo in Action, FAQ); What is
   GlamGo? and Let's Connect carry one-off classes from the export.

   `.founding-cta-title` is the "Join the first 100" banner. Not a section
   title, but the page's other full-bleed headline, and the export already
   sized it like the hero (fixed 48px), so it rides the same ramp.

   These stay `<h2>` in the markup. One `<h1>` per page is what screen readers
   and search engines expect the document outline to say, and the outline is a
   separate question from how large the text is drawn. */
#hd-hero,
.section-title,
.about-title,
.contact-title,
.founding-cta-title {
	font-size: var(--type-h1);
}

/* The lede under each section title. Size was already restated here; weight
   and line-height have to be too, or each section keeps whichever value the
   export's own class carried — 400/normal on the hero, 500/1.2 on four
   sections, 500/1.333 on Let's Connect — and the same role reads differently
   down the page.

   Colour is deliberately left out: the hero draws its lede white over the
   dark band and the rest draw it on the light ground, so that one genuinely
   is per-section rather than an inconsistency.

   These stay <p>. A lede is not a heading; promoting it to <h3> to make the
   styling uniform would announce a subsection that does not exist, for the
   same reason the section titles above stay <h2>. */
.hero-lede,
.section-lede,
.about-lede,
.contact-lede {
	/* The support step, not --type-h1. The export drew these at the same size
	   as the title above them, so the lede competed with the heading instead
	   of introducing it, and any sentence past a dozen words became a wall
	   before the section had started. This is the same size .doc-intro uses on
	   the document pages, so both halves of the site agree. */
	font-size: var(--type-h1-support);
	font-weight: 500;
	line-height: 1.4;
}

/* h2 — the headings *inside* a section: the founders block and the two demo
   cards. One step down from the section title above them, which is what makes
   the nesting readable at a glance. */
.founders-title,
.demo-title-clients,
.demo-title-providers {
	font-size: var(--type-h2);
}

.founders-lede,
.demo-lede-clients,
.demo-lede-providers {
	font-size: var(--type-h2-support);
}

/* `display-web-xl1` and `display-web-xl2` set `line-height: 0.75` — a ratio
   that only clears the descenders because 32px happened to be the drawn size.
   At 28px it clips, and it leaves the two demo headings sitting on different
   baselines from every other h2, so the heading scale sets its own. */
.demo-title-clients,
.demo-title-providers {
	line-height: 1.2;
}


/* =========================================================================
   Forms

   Shared by the Let's Connect form on the landing page and the Founding 100
   application on apply.html. These classes were called `contact-*` while there
   was only one form; the application needs the same boxes, so they say what
   they are instead of where they first appeared.

   The export drew the contact form as four separate shapes — two inputs, a
   <div> pretending to be a dropdown with a caret <img> beside it, and a
   <textarea> faded to `opacity: 0.5` to imitate placeholder text. The dropdown
   could not be operated and the fade dimmed real answers as much as the prompt.

   Now every control is the same control: one `.form-field` carries the box —
   width, padding, border, type — and the <select> adds only the things a
   <select> needs. That is what keeps them the same size; nothing restates a
   measurement.
   ========================================================================= */

.form {
	width: 100%;
	max-width: 587px;
	display: flex;
	flex-direction: column;
	gap: 16px;
}

/* Each control plus the space its error message will need. The message is
   `hidden` until it has something to say, so the row is just the control
   until then. */
.form-row {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.form-field {
	width: 100%;
	box-sizing: border-box;
	/* `font: inherit` is not enough: form controls in every browser ship their
	   own family and size, and `inherit` on the shorthand would also drag in
	   the section's line-height. These are the export's values. */
	color: var(--primary-500-base);
	font-size: var(--type-body-sm);
	font-family: var(--font-body);
	font-weight: 500;
	/* A number, not the export's `normal`: `normal` is the font's own metric
	   and a <select> resolves it 2px shorter than an <input> does, so the three
	   boxes came out different heights. Pinning it makes the height a function
	   of font-size and padding, which they now share. */
	line-height: 1.5;
	letter-spacing: 0px;
	text-align: left;
	background-color: var(--inactive-surface);
	padding: 16px 13px;
	border: 1px solid var(--grey-10);
	border-radius: 8px;
}

.form-field::placeholder {
	color: var(--primary-300);
	opacity: 0.6;
}

.form-field:focus-visible {
	outline: 2px solid var(--primary-300);
	outline-offset: 2px;
}

/* The caret the export shipped as an <img>, moved into the control itself so
   the arrow cannot end up beside the wrong box. `appearance: none` removes the
   platform arrow that would otherwise sit next to ours; `padding-right` keeps
   a long option from running underneath it. */
.form-select {
	appearance: none;
	background-image: url(../assets/icon-caret-down.png);
	background-repeat: no-repeat;
	background-position: right 13px center;
	background-size: 16px;
	padding-right: 40px;
}

/* The prompt is a disabled placeholder option, so it reads as unanswered
   rather than as a fourth choice. It is styled to match ::placeholder. */
.form-select:has(option[value='']:checked) {
	color: var(--primary-300);
	opacity: 0.6;
}

.form-textarea {
	min-height: 84px;
	resize: vertical;
}

/* Only after a field has been checked once — otherwise every control is red
   before the visitor has typed anything. The JS sets aria-invalid. */
.form-field[aria-invalid='true'] {
	border-color: #b3261e;
}

.form-error {
	margin: 0;
	color: #b3261e;
	font-size: var(--type-body-sm);
	font-family: var(--font-body);
	line-height: 1.3;
}

.form-status {
	margin: 0;
	font-size: var(--type-body);
	font-family: var(--font-body);
	line-height: 1.4;
	text-align: center;
}

.form-status[data-state='ok'] {
	color: var(--primary-500-base);
	font-weight: 600;
}

.form-status[data-state='error'] {
	color: #b3261e;
}

/* .btn-primary is a fixed 240px pill, so in the form's flex column it does
   not stretch — where it sits is entirely `align-self`. Centred is the
   mobile default: on a narrow screen the form fills the width and a button
   pinned to one edge reads as stray rather than as the end of the form.

   The desktop half is written as `width > 900px` rather than
   `min-width: 901px` for the reason the nav breakpoint documents: viewport
   widths are fractional, and 900/901 leaves 900.5px matching neither rule. */
.form-submit {
	align-self: center;
}

@media (width > 900px) {
	.form-submit {
		align-self: flex-end;
	}
}

.form-submit[disabled] {
	opacity: 0.6;
	cursor: progress;
}

/* -------------------------------------------------------------------------
   Choice questions

   A question with options is a <fieldset>: the <legend> is the question, and
   the browser reads it out before each option, which is what makes "Other"
   mean something on its own. Radios and checkboxes share every rule here —
   the only difference between them is the shape the browser draws, so the
   markup differs by `type` and nothing else.
   ------------------------------------------------------------------------- */

.form-question {
	margin: 0;
	padding: 0;
	border: 0;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.form-legend {
	/* `float: left` + a clearing sibling is the standard way to make a <legend>
	   behave like a normal block: unfloated, it is laid out by the fieldset and
	   ignores width and margins in several browsers. */
	float: left;
	width: 100%;
	padding: 0;
	color: var(--primary-500-base);
	font-size: var(--type-body);
	font-family: var(--font-body);
	font-weight: 600;
	line-height: 1.35;
}

.form-question::after {
	content: '';
	display: table;
	clear: both;
}

.form-options {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/* The whole row is the label, so the text is part of the hit target rather
   than something to aim past on the way to a 16px box. */
.form-option {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	color: var(--primary-500-base);
	font-size: var(--type-body-sm);
	font-family: var(--font-body);
	font-weight: 500;
	line-height: 1.5;
	cursor: pointer;
}

.form-option input {
	/* `flex-shrink: 0` so a two-line option does not squash the box, and the
	   margin lines it up with the first line of text rather than the middle of
	   the block. */
	flex-shrink: 0;
	width: 18px;
	height: 18px;
	margin: 3px 0 0;
	accent-color: var(--primary-400);
	cursor: pointer;
}

/* The follow-up box for "Other". It is indented under the option it belongs to
   and only exists while that option is checked — the JS removes the `hidden`
   attribute and adds `required`, so an unanswered "Other" is caught the same
   way an empty Name is. */
.form-other {
	margin-left: 28px;
}

.form-other[hidden] {
	display: none;
}

/* The three contact boxes at the end of the application. Two columns where
   there is room, one where there is not. */
.form-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 16px;
}

.form-label {
	margin-bottom: 6px;
	display: block;
	color: var(--primary-500-base);
	font-size: var(--type-body-sm);
	font-family: var(--font-body);
	font-weight: 600;
	line-height: 1.4;
}

.form-optional {
	color: var(--primary-300);
	font-weight: 500;
}

/* Reachable by a screen reader, invisible on screen. Not `display: none`,
   which would remove it from the accessibility tree along with everything
   else. */
.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}


/* =========================================================================
   Phase 9: footer

   The export laid this out as five sibling rows inside one flex column, with
   every horizontal position expressed as a margin measured at 1440px:
   `.footer-brand { margin-left: 428px }`, `.footer-png-clipart { margin-left:
   326px }`, `.footer-text-privacy-policy { margin-left: 764px }`. Below that
   width the three groups slide over each other; above it they stay put while
   the footer grows. They are three real columns now.
   ========================================================================= */

.footer {
	align-items: stretch; /* .section centres its children; the columns need the full width */
	gap: 0;
}

.footer-cols {
	display: grid;
	/* Three tracks that share the row equally, so the middle one is centred on
	   the footer itself rather than on whatever the left column happens to be
	   wide. `minmax(0, 1fr)` instead of `1fr`: a grid track's default minimum
	   is its content, which would let a long link push its column wider. */
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 40px;
	width: 100%;
	max-width: var(--site-max-width);
	margin-inline: auto;
}

.footer-col {
	display: flex;
	flex-direction: column;
	gap: 12px;
	min-width: 0;
}

/* Each column reads toward its own side of the page: the links anchor the
   left edge, the stores the right, and the social block sits between them.
   `align-items` as well as `text-align`, because these are flex columns —
   text-align alone would leave the icon rows sitting at the start. */
.footer-col-links {
	align-items: flex-start;
	text-align: left;
}

.footer-col-social {
	align-items: center;
	text-align: center;
}

.footer-col-apps {
	align-items: flex-end;
	text-align: right;
}

.footer-link {
	color: #fff;
	font-family: var(--font-body);
	font-size: var(--type-body-sm);
	font-weight: 500;
	line-height: 1.4;
	text-decoration: none;
	transition: color 0.2s ease;
}

.footer-link:hover,
.footer-link:focus-visible {
	color: var(--secondary-500-base);
}

.footer-col-title {
	/* The export's -8px/-7px pulled the heading onto the row above it. */
	margin: 0 0 4px;
}

/* The column centres its children, which would centre each link on its own and
   leave the two icons at different x. Centring the group instead — `max-content`
   so the box is as wide as the longer handle — lines the icons up with each
   other while the block as a whole stays under the heading. */
.footer-social {
	width: max-content;
	max-width: 100%;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 8px;
	text-align: left;
}

/* Icon and handle are one link, not an icon beside some text: the whole thing
   is the target, so it is comfortably tappable on a phone without a 36px icon
   being the only thing to aim at. */
.footer-social-link {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	color: #fff;
	font-family: var(--font-body);
	font-size: var(--type-body-sm);
	font-weight: 500;
	line-height: 1.4;
	text-decoration: none;
	transition: color 0.2s ease;
}

.footer-social-link:hover,
.footer-social-link:focus-visible {
	color: var(--secondary-500-base);
}

.footer-social-icon {
	width: 24px;
	height: 24px;
	flex-shrink: 0;
	/* The icons are stroked, not filled, so their weight is a line width rather
	   than baked into the artwork. 24px drawn at stroke-width 2 is Tabler's own
	   proportion — scaling the box without scaling this would thin them out. */
	stroke-width: 2;
}

.footer-badges {
	display: flex;
	flex-direction: column;
	align-items: inherit; /* follow the column's own side */
	gap: 12px;
}

/* The App Store badge is a link while it points at TestFlight, which makes the
   anchor the flex item rather than the image. Give it the badge's own box, or
   `align-items: stretch` would spread the click target across the whole column
   while the artwork inside stayed 210px — a hit area wider than the thing it
   appears to be. */
.footer-badge-link {
	display: block;
	width: 210px;
	max-width: 100%;
	border-radius: 6px;
}

/* Both badges are the same asset shape now: 581x183 with an 8px transparent
   margin all round, so the artwork is 564x167. The box is widened by exactly
   that margin — 204 x 581/564 — so the *visible* badge still prints at the
   204px the export drew, rather than the padding eating 3% of it. */
.footer-badge {
	display: block;
	width: 210px;
	max-width: 100%;
	height: auto;
	border-radius: 6px;
}

.footer-rule {
	margin: 40px 0 20px;
	width: 100%;
	max-width: var(--site-max-width);
	margin-inline: auto;
	align-self: center;
}

.footer-bottom {
	margin: 0 auto;
	width: 100%;
	max-width: var(--site-max-width);
	display: flex;
	/* Restated: `@container footer (width < 1293px)` in the export switches this
	   to `column`, and the rule above only wins the properties it declares. */
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	/* The row holds only the copyright now — Privacy Policy and Terms moved up
	   into the links column, where they sit with the rest of the navigation
	   instead of being fine print that happens to be clickable. A lone item
	   under `space-between` would sit hard against the left edge. */
	justify-content: center;
	text-align: center;
	gap: 8px 22px;
	align-self: center;
}

.footer-copyright {
	margin-block: 0;
}

/* The export gave the copyright `--primary-300`, a dark purple sitting on the
   dark plum footer — roughly 1.9:1 against its own background. The two links
   beside it are `--background-500-base`, so it also read as a different kind
   of thing. All three are one row of fine print; they should look like it. */
.footer-copyright-type {
	color: var(--background-500-base);
}

/* One column on phones, everything centred: three columns pointing three
   different directions only reads as deliberate while they sit side by side.
   900px matches the nav and the demo cards, so the page reflows at one width
   rather than at four. */
@media (max-width: 900px) {
	.footer-cols {
		grid-template-columns: minmax(0, 1fr);
		gap: 32px;
		justify-items: center;
	}

	.footer-col-links,
	.footer-col-apps {
		align-items: center;
		text-align: center;
	}
}

/* =========================================================================
   Phase 10: document pages (terms, privacy policy)

   These two were standalone pages pointing at a `/styles.css` and a
   `/logo_ciruela.png` that do not exist in this repo, so they rendered as
   unstyled browser default text. They now load the same four stylesheets as
   the landing page and reuse its shell, nav and footer verbatim — the only
   new thing here is the prose column itself.

   The header is a plain dark band rather than the hero's photo: `.site-nav-link` is
   `--background-600` because the export drew it over the dark hero, so giving
   the band the same ground keeps every nav rule working untouched instead of
   forking a light-nav variant.
   ========================================================================= */

.doc-header {
	position: relative;
	z-index: 20;
	background-color: var(--primary-500-base);
	padding-block: 16px;
	/* The same centring + gutter every section gets; `.doc-header` is not a
	   child of <main>, so the shared rule above does not reach it. */
	--gutter: 24px;
	padding-inline: calc(max(0px, (100% - var(--site-max-width)) / 2) + var(--gutter));
}

.doc {
	background-color: var(--background-500-base);
	padding-block: 56px 80px;
}

/* Prose wants a measure, not the full 1440 band: past roughly 75 characters
   the eye loses the start of the next line on the way back. */
.doc-body {
	width: 100%;
	max-width: 820px;
	margin-inline: auto;
	color: var(--primary-500-base);
	font-family: var(--font-body);
}

.doc-title {
	margin: 0 0 8px;
	color: var(--primary-500-base);
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h1);
	line-height: 1.15;
}

/* Set from the page's own <title> family rather than a date string: nothing
   in the repo records when these documents were last revised.

   Sized at the support scale rather than --type-h1: at h1 size the lead
   competed with the title above it instead of introducing it, and any
   sentence longer than a dozen words became a wall before the document had
   started. --type-h1-support is the site's own step for a line that reads
   under a heading. */
.doc-intro {
	margin: 0 0 40px;
	color: var(--primary-300);
	font-size: var(--type-h1-support);
	line-height: 1.5;
}

.doc-body h2 {
	margin: 40px 0 12px;
	color: var(--primary-500-base);
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h2);
	line-height: 1.25;
}

.doc-body h3 {
	margin: 28px 0 8px;
	color: var(--primary-400);
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h2-support);
	line-height: 1.3;
}

/* `:not(.doc-intro)` is load-bearing, not decoration: `.doc-body p` is one
   class more specific than `.doc-intro`, so without it the 16px body size
   would quietly win over the lead paragraph's h1-support size. */
.doc-body p:not(.doc-intro),
.doc-body li {
	font-size: var(--type-body);
	font-weight: 400;
	/* 1.65 rather than the 1.25–1.4 the landing page uses: those are short
	   marketing lines, this is a wall of clauses somebody has to read through. */
	line-height: 1.65;
	color: var(--primary-400);
}

.doc-body p:not(.doc-intro) {
	margin: 0 0 12px;
}

.doc-body ul {
	margin: 0 0 12px;
	padding-left: 22px;
	list-style: disc;
}

.doc-body li {
	margin-bottom: 6px;
}

.doc-body a {
	color: var(--primary-500-base);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.doc-body a:hover,
.doc-body a:focus-visible {
	color: var(--secondary-500-base);
}

/* -------------------------------------------------------------------------
   Apply page

   The Founding 100 form. It used to be an embedded Google Form; it is our own
   markup now, so everything under "Forms" above already styles it and only the
   page-level spacing lives here.
   ------------------------------------------------------------------------- */

.doc-apply .doc-body {
	/* Narrower than the legal pages: those are prose that wants a measure, this
	   is a single column of form controls that should not float in an 820px
	   column. */
	max-width: 640px;
}

.apply-form {
	margin-top: 32px;
	/* Wider than the contact form's 587px would allow — this one is the page,
	   not a block inside a section. */
	max-width: none;
	/* Questions need more air between them than the contact form's four boxes
	   do, or a legend reads as belonging to the options above it. */
	gap: 32px;
}

/* Set apart from the questions: this is the closing section, not a ninth
   question. */
.apply-contact {
	padding-top: 8px;
	border-top: 1px solid var(--grey-10);
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.apply-contact-title {
	margin: 16px 0 0;
	color: var(--primary-500-base);
	font-size: var(--type-h1);
	font-family: var(--font-display);
	font-weight: 600;
	line-height: 1.25;
}

.apply-contact-lede {
	margin: 0 0 8px;
	color: var(--primary-300);
	font-size: var(--type-body);
	font-family: var(--font-body);
	font-weight: 500;
	line-height: 1.4;
}

@media (max-width: 900px) {
	.doc {
		padding-block: 40px 56px;
	}
}

/* =========================================================================
   Phase 11: Browse Looks (/looks)

   Reuses the document-page shell — same `.doc` band, `.doc-title`,
   `.doc-intro`, `.form-field` — and adds only what a grid of photographs
   needs. The one thing it deliberately does not reuse is `.doc-body`'s 820px
   measure: that width exists so prose does not outrun the eye, and it would
   squeeze three 9:16 tiles into columns barely wider than a phone.
   ========================================================================= */

.looks-body {
	width: 100%;
	max-width: 1120px;
	margin-inline: auto;
	color: var(--primary-500-base);
	font-family: var(--font-body);
}

/* -------------------------------------------------------------------------
   Filters
   ------------------------------------------------------------------------- */

.looks-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px 24px;
	margin: 0 0 24px;
}

.looks-scope,
.looks-services {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

/* One pill serves the scope radios and the service toggles. They differ only
   in which ARIA attribute carries the state — `aria-checked` for a radio,
   `aria-pressed` for a toggle — so the selected rule reads both. Styling off
   ARIA rather than a class is what keeps the announced state and the visible
   state from drifting apart. */
.looks-chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 10px 18px;
	color: var(--primary-400);
	font-family: var(--font-body);
	font-size: var(--type-body-sm);
	font-weight: 500;
	line-height: 1.25;
	background-color: var(--inactive-surface);
	border: 1px solid var(--grey-10);
	border-radius: 9999px;
	cursor: pointer;
	transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

.looks-chip:hover {
	border-color: var(--primary-200);
	color: var(--primary-500-base);
}

.looks-chip[aria-checked='true'],
.looks-chip[aria-pressed='true'] {
	background-color: var(--primary-500-base);
	border-color: var(--primary-500-base);
	color: var(--background-400);
}

.looks-chip:focus-visible {
	outline: 2px solid var(--primary-300);
	outline-offset: 2px;
}

.looks-chip-icon {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
}

.looks-zip {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	/* Its own full-width row: it appears and disappears, and letting it sit
	   inline would shuffle the chips sideways every time it did. */
	flex-basis: 100%;
}

/* The chip above already says "ZIP Code", so the label would be the same word
   twice on screen — but the input still needs a name for a screen reader. */
.looks-zip-label {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.looks-zip-input {
	width: 180px;
}

.looks-zip-submit {
	width: auto;
	padding: 13px 28px;
	cursor: pointer;
}

.looks-sort {
	display: flex;
	align-items: center;
	gap: 8px;
	/* Pushed to the end of the row: it orders the results rather than choosing
	   them, so it should not sit among the filters that do. */
	margin-left: auto;
}

.looks-sort-label {
	color: var(--primary-300);
	font-size: var(--type-body-sm);
	font-weight: 500;
	white-space: nowrap;
}

.looks-sort-select {
	width: auto;
	padding-block: 10px;
}

.looks-status {
	margin: 0 0 16px;
	color: var(--primary-300);
	font-size: var(--type-body);
	line-height: 1.4;
}

.looks-status[data-state='error'] {
	color: #b3261e;
}

/* -------------------------------------------------------------------------
   Grid
   ------------------------------------------------------------------------- */

.looks-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 20px;
}

/* The infinite-scroll sentinel. `min-height` is not decoration: with both
   children hidden this collapses to nothing, and a zero-height box is a
   fragile thing to ask an IntersectionObserver about. */
.looks-foot {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-height: 32px;
	padding-top: 24px;
}

.looks-loading {
	margin: 0;
	color: var(--primary-300);
	font-size: var(--type-body-sm);
	font-weight: 500;
	line-height: 1.4;
}

.looks-more {
	width: auto;
	padding-inline: 40px;
	cursor: pointer;
}

/* -------------------------------------------------------------------------
   Look card

   `aspect-ratio: 9 / 16` on the tile rather than on the image: the box then
   holds its place in the grid before a single photo has loaded, so a screenful
   of results does not reflow as they arrive. The LQIP that JS sets as the
   background-image sits in that reserved box until the real one paints over it.
   ------------------------------------------------------------------------- */

.look-card {
	position: relative;
	/* Now an <a> — see cardMarkup in js/looks.js. It carries no link colour or
	   underline because the whole photograph is the target, and the caption
	   sitting on it is white by design. */
	display: block;
	color: inherit;
	text-decoration: none;
	aspect-ratio: 9 / 16;
	overflow: hidden;
	border-radius: 16px;
	background-color: var(--background-600);
	background-size: cover;
	background-position: center;
	isolation: isolate;
}

.look-card-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	/* Fades over the blurred placeholder underneath instead of snapping in. */
	animation: look-card-fade 320ms ease both;
}

@keyframes look-card-fade {
	from { opacity: 0; }
	to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
	.look-card-img {
		animation: none;
	}
}

/* The same wash the category cards on the landing page use: transparent for
   most of the height, then plum at the bottom. It is what makes white text
   legible over a photograph whose bottom edge we cannot predict — without it
   a name lands on a pale manicure and disappears. */
.look-card-overlay {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 12px;
	padding: 16px;
	background-image: linear-gradient(180deg, rgba(71, 31, 67, 0) 55%, rgba(71, 31, 67, 0.9) 100%);
}

.look-card-text {
	min-width: 0;
}

.look-card-name {
	margin: 0;
	color: #fff;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h2-support);
	line-height: 1.2;
}

.look-card-meta {
	margin: 4px 0 0;
	color: #fff;
	font-family: var(--font-body);
	font-size: var(--type-body-sm);
	font-weight: 500;
	line-height: 1.3;
	/* The address can be long and the tile is narrow; two lines is the most it
	   may take before it starts eating the photograph. */
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	overflow: hidden;
}

.look-card-rating {
	margin: 0;
	display: flex;
	align-items: center;
	gap: 4px;
	flex-shrink: 0;
	color: #fff;
	font-family: var(--font-body);
	font-size: var(--type-body-sm);
	font-weight: 600;
	line-height: 1.3;
}

.look-card-star {
	width: 16px;
	height: 16px;
}

@media (max-width: 900px) {
	.looks-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* On a narrow screen the sort control is the widest thing in the row, and
	   `margin-left: auto` would strand it alone on a line of its own. */
	.looks-sort {
		margin-left: 0;
		flex-basis: 100%;
	}

	.looks-sort-select {
		flex: 1;
	}
}

@media (max-width: 600px) {
	.looks-grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 16px;
	}

	.looks-zip-input,
	.looks-zip-submit,
	.looks-more {
		flex: 1;
		width: 100%;
	}
}

/* =========================================================================
   Phase 12: Not Found (404.html)

   The one page in the site whose content is shorter than the viewport, so
   it is also the only one that has to say what "centred" means. `.site` is
   already a flex column, so the whole trick is a page-level modifier that
   makes that column at least a screen tall and lets <main> absorb the
   slack — the message then centres inside the space left over between the
   nav band and the footer, whatever their heights turn out to be.
   ========================================================================= */

.site-fill {
	min-height: 100vh;
}

.site-fill > main {
	flex: 1;
	display: flex;
}

.notfound {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: center;
	/* Keeps the block off the nav and footer on a short window, where the
	   flex slack runs out before the padding does. */
	padding-block: 80px;
}

.notfound-body {
	width: 100%;
	max-width: 680px;
	margin-inline: auto;
}

/* The mark, not a number: the brief is to tell a person the page is gone,
   not to quote them the status line the browser already knows. */
.notfound-mark {
	display: block;
	width: 72px;
	height: 72px;
	margin: 0 auto 24px;
	color: var(--primary-300);
}

.notfound-actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 12px;
	margin-top: 32px;
}

/* Same reason as `.hero-btn-primary`: `.btn-primary` is a flex row, and an
   <a> does not centre its own label the way a <button> does. */
.notfound-actions > .btn-primary {
	min-height: 56px;
	align-items: center;
	flex-shrink: 0;
}

@media (max-width: 900px) {
	.notfound {
		padding-block: 56px;
	}

	.notfound-actions > .btn-primary {
		flex: 1;
		width: 100%;
	}
}

/* Second action deliberately reads as a link rather than a second pill: two
   equal buttons would make the reader choose, and there is a right answer
   here — go home. */
.notfound-link {
	display: inline-flex;
	align-items: center;
	min-height: 56px;
	padding-inline: 16px;
	color: var(--primary-300);
	font-size: 16px;
	font-weight: 500;
	line-height: 1.25;
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* =========================================================================
   Phase 13: floating nav

   The header scrolls away with the page; this is how the menu stays
   reachable after it does. js/site-nav.js builds it by cloning the header's
   own <ul>, so it inherits `.site-nav-menu` and every mobile rule attached
   to that class — the full-bleed sheet included. This block comes last in
   the file, which is what lets a plain `.floating-nav-menu` selector take
   those back at equal specificity.

   It is a right-anchored card at every width, not a full-width sheet on
   phones: the sheet reads as attached to a full-width bar, and there is no
   bar here to attach to — only a 48px button in the corner.
   ========================================================================= */

.floating-nav {
	/* The button's own geometry, named because the mobile sheet has to sit
	   exactly below it and would otherwise repeat the sum as a magic 72px. */
	--floating-nav-inset: 16px;
	--floating-nav-size: 48px;
	--floating-nav-gap: 8px;

	position: fixed;
	top: var(--floating-nav-inset);
	right: var(--floating-nav-inset);
	z-index: 60;

	/* Hidden until the real nav leaves the screen. `visibility` rather than
	   `display` so the fade has something to animate, and so the button is
	   out of the tab order while it is invisible.

	   The slide that goes with the fade is on the button, not here: a
	   transform on this element would make it the containing block for its
	   own `position: fixed` descendants, and the mobile sheet below is
	   fixed precisely so it can span the viewport rather than the 48px
	   button. It would have collapsed to the button's width instead. */
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.2s ease, visibility 0.2s;
}

.floating-nav.is-visible {
	opacity: 1;
	visibility: visible;
}

.floating-nav.is-visible .floating-nav-toggle {
	transform: translateY(0);
}

/* Unlike the header's hamburger, this one sits over whatever section
   happens to be scrolled past — photos, plum bands, pale prose. It carries
   its own ground so it is legible over all of them. */
.floating-nav-toggle {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: var(--floating-nav-size);
	height: var(--floating-nav-size);
	padding: 13px;
	border: 0;
	border-radius: 50%;
	background-color: var(--primary-500-base);
	box-shadow: 0 6px 20px rgba(40, 23, 38, 0.3);
	cursor: pointer;

	/* The slide half of the reveal; see .floating-nav above for why it is
	   here and not on the wrapper. */
	transform: translateY(-8px);
	transition: transform 0.2s ease;
}

.floating-nav-toggle span {
	display: block;
	width: 100%;
	height: 2px;
	border-radius: 2px;
	background-color: var(--surface-white);
	transition: transform 0.25s ease, opacity 0.2s ease;
}

.floating-nav.is-open .floating-nav-toggle span:nth-child(1) {
	transform: translateY(7px) rotate(45deg);
}

.floating-nav.is-open .floating-nav-toggle span:nth-child(2) {
	opacity: 0;
}

.floating-nav.is-open .floating-nav-toggle span:nth-child(3) {
	transform: translateY(-7px) rotate(-45deg);
}

.floating-nav-menu {
	position: absolute;
	top: calc(100% + var(--floating-nav-gap));
	right: 0;
	left: auto;
	translate: none; /* undoes the mobile sheet's -50% pull-back */
	width: max-content;
	min-width: 220px;
	max-width: min(320px, calc(100vw - 32px));
	flex-direction: column;
	align-items: stretch;
	gap: 4px;
	padding: 8px;
	border-radius: 16px;
	background-color: var(--primary-500-base);
	box-shadow: 0 16px 40px rgba(40, 23, 38, 0.35);

	opacity: 0;
	visibility: hidden;
	transform: translateY(-8px);
	transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}

.floating-nav.is-open .floating-nav-menu {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

/* Restated rather than inherited: the header gives these links the desktop
   inline treatment above 900px, and inside a dropdown they want the padded
   pill at every width. */
.floating-nav-menu .site-nav-link {
	padding: 12px 16px;
	border-radius: 10px;
	color: var(--background-600);
	font-size: 16px;
}

.floating-nav-menu .site-nav-link:hover,
.floating-nav-menu .site-nav-link:focus-visible {
	background-color: var(--primary-400);
	color: var(--surface-white);
}

/* Same full-bleed sheet as the header's menu, and the same reasoning: at
   this width a card floating clear of every edge is a smaller tap target
   for no gain. `position: fixed` rather than absolute because the panel has
   to span the viewport, and its offset parent is a 48px button pinned in
   the corner. */
@media (max-width: 900px) {
	.floating-nav-menu {
		position: fixed;
		top: calc(var(--floating-nav-inset) + var(--floating-nav-size) + var(--floating-nav-gap));
		left: 0;
		right: 0;
		width: 100%;
		min-width: 0;
		max-width: none;
		padding: 12px;
		border-radius: 0;
	}

	.floating-nav-menu .site-nav-link {
		padding: 14px 16px;
		font-size: 17px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.floating-nav,
	.floating-nav-menu,
	.floating-nav-toggle,
	.floating-nav-toggle span {
		transition: none;
	}
}

/* =========================================================================
   Phase 12: Look detail (/looks/{id})

   Two columns that are really one: the photograph keeps the same 9:16 crop it
   had in the grid, so opening a look is a zoom rather than a recomposition,
   and the facts sit beside it on a wide screen and under it on a narrow one.
   The image column is capped rather than fluid — a 9:16 photo given half of a
   1120px band would stand taller than any laptop viewport.
   ========================================================================= */

.look-detail-body {
	width: 100%;
	max-width: 1120px;
	margin-inline: auto;
	color: var(--primary-500-base);
	font-family: var(--font-body);
}

.look-detail-back {
	display: inline-block;
	margin: 0 0 24px;
	color: var(--primary-400);
	font-size: var(--type-body-sm);
	font-weight: 500;
	text-decoration: none;
}

.look-detail-back:hover {
	color: var(--primary-500-base);
	text-decoration: underline;
}

.look-detail-status {
	margin: 0;
	font-size: var(--type-body);
	color: var(--primary-400);
}

.look-detail-status[data-state="error"] {
	color: var(--primary-500-base);
	font-weight: 500;
}

.look-detail-card {
	display: grid;
	grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
	gap: 40px;
	align-items: start;
}

.look-detail-media {
	position: relative;
	aspect-ratio: 9 / 16;
	overflow: hidden;
	border-radius: 16px;
	background-color: var(--background-600);
	background-size: cover;
	background-position: center;
}

.look-detail-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	animation: look-card-fade 320ms ease both;
}

@media (prefers-reduced-motion: reduce) {
	.look-detail-img {
		animation: none;
	}
}

.look-detail-name {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h1);
	line-height: 1.15;
}

/* "{business} by {provider}", or whichever half of it the record has — see
   formatByline in js/look-detail.js. */
.look-detail-provider {
	margin: 8px 0 0;
	color: var(--primary-400);
	font-size: var(--type-body);
	font-weight: 500;
}

.look-detail-price {
	margin: 20px 0 0;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h2-support);
	line-height: 1.2;
}

.look-detail-description {
	margin: 20px 0 0;
	/* The one place on this page that holds prose, so it gets the measure the
	   document pages use rather than the full column. */
	max-width: 60ch;
	font-size: var(--type-body);
	line-height: 1.6;
	white-space: pre-line;
}

.look-detail-address {
	margin: 20px 0 0;
	color: var(--primary-400);
	font-size: var(--type-body-sm);
	font-weight: 500;
}

/* -------------------------------------------------------------------------
   More from this provider

   Three tiles across the full band, at the same size and gap they have in the
   grid at /looks: a tile that changes size between the two views reads as a
   different kind of thing, and these are the same thing. It does mean a 9:16
   tile stands taller than the capped photograph above it — the section is
   below the fold either way, so it is the tiles that are being scrolled to,
   not the look. Only the gap tightens on a phone.
   ------------------------------------------------------------------------- */

.look-detail-more {
	margin: 56px 0 0;
}

.look-detail-more-title {
	margin: 0 0 16px;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--type-h2-support);
	line-height: 1.2;
}

.look-detail-more-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 20px;
}

@media (max-width: 600px) {
	.look-detail-more-grid {
		gap: 8px;
	}

	/* At a third of a phone's width the caption would cover the photograph it
	   is captioning, and the name alone carries the tile. */
	.look-detail-more-grid .look-card-meta,
	.look-detail-more-grid .look-card-rating {
		display: none;
	}

	.look-detail-more-grid .look-card-name {
		font-size: var(--type-body-sm);
	}

	.look-detail-more-grid .look-card-overlay {
		padding: 10px;
	}
}

@media (max-width: 900px) {
	.look-detail-card {
		grid-template-columns: minmax(0, 1fr);
		gap: 24px;
	}

	/* Stacked, a full-width 9:16 image would push every fact below the fold on
	   a phone. Capping the column keeps the photo and its name on one screen. */
	.look-detail-media {
		max-width: 320px;
		margin-inline: auto;
	}
}

/* -------------------------------------------------------------------------
   Help & Troubleshooting page

   Built on the same `.doc` shell as the legal pages, so headings, measure and
   body copy come for free. What it adds is the accordion — and unlike the
   landing page's FAQ, which is a button plus a hidden panel driven by
   js/site-nav.js, this one is a plain <details>. With 80-odd entries that is
   the cheaper trade: open/close, keyboard handling and find-in-page all come
   from the browser, and the page ships no script of its own.
   ------------------------------------------------------------------------- */

.doc-support .support-item {
	/* card-white1 supplies the surface, shadow and side padding; the rest is
	   the vertical rhythm a stack of them needs. */
	margin-bottom: 12px;
	padding-block: 0;
	overflow: hidden;
}

.doc-support .support-q {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 18px 0;
	cursor: pointer;
	color: var(--primary-500-base);
	font-family: var(--font-body);
	font-weight: 500;
	font-size: var(--type-body);
	line-height: 1.4;
	/* Drops the UA triangle so the CSS plus/minus below is the only marker. */
	list-style: none;
}

.doc-support .support-q::-webkit-details-marker {
	display: none;
}

.doc-support .support-q:focus-visible {
	outline: 2px solid var(--primary-300);
	outline-offset: -2px;
}

/* The landing FAQ's plus/minus, redrawn on the summary itself: one bar that
   rotates away to leave a minus when the entry is open. */
.doc-support .support-q::after {
	content: '';
	flex: none;
	width: 14px;
	height: 14px;
	background-color: var(--primary-500-base);
	transition: clip-path 0.2s ease;
	clip-path: polygon(0 43%, 43% 43%, 43% 0, 57% 0, 57% 43%, 100% 43%,
	                   100% 57%, 57% 57%, 57% 100%, 43% 100%, 43% 57%, 0 57%);
}

.doc-support .support-item[open] .support-q::after {
	clip-path: polygon(0 43%, 100% 43%, 100% 57%, 0 57%);
}

.doc-support .support-a {
	padding-bottom: 18px;
}

.doc-support .support-a p:last-child,
.doc-support .support-a ol:last-child,
.doc-support .support-a ul:last-child {
	margin-bottom: 0;
}

.doc-support .support-a ol {
	margin: 0 0 12px;
	padding-left: 22px;
	list-style: decimal;
}

/* A section heading has to read as the start of a new group of cards, not as
   a label on the card above it. */
.doc-support .doc-body h2 {
	margin-top: 56px;
}

.doc-support .doc-body h3 {
	margin-top: 32px;
	margin-bottom: 12px;
}
