/* Styles for the fact.ag landing page.
   Font URLs below are relative to this stylesheet rather than root-relative, so they resolve against the
   stylesheet's own location whatever path it is served from. */

/* Phonic and Recital are proprietary and are on no public CDN, so they ship with the page. Recital is the serif
   the design sets both headings in; it comes from the Intl. Farming kit rather than the FA/CT one. */
@font-face {
	font-family: Phonic;
	src: url("fonts/Phonic-Regular.otf") format("opentype");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: Recital;
	src: url("fonts/Recital-Regular.otf") format("opentype");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

/* Names and values are the Figma variables for this file, which are also src/theme.css's names in the SPA. */
:root {
	--color-algae: #294739;
	--color-mist: #f8f7f0;
	--color-fog: #ebebe2;
	--color-stone: #d3cfc2;
	--color-silt: #667064;
	--color-pistachio: #d1dbba;
	--color-citron: #eae27a;
	--color-negative: #dd2839;
	--color-positive: #acdd68;
	--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);

	/* The easing the FACTLandingAnimatedMockUp timeline uses for its fades (node 40:466). */
	--ease-in-out: cubic-bezier(0.5, 0, 0.5, 1);

	/* The design is drawn at 1728x1117: a 490px copy column and a 550px card, both centred. */
	--copy-width: 490px;
	--card-width: 550px;

	/* Open, the card's bottom sits 75px above the viewport floor; closed, only its heading strip shows. */
	--card-peek: 95px;
	--card-gap: 75px;
}

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

/* .page is fixed, so it is laid out against the small viewport: when a mobile browser's chrome retracts, or the
   view rubber-bands past its end, the strip that uncovers is the canvas rather than .page. Painting the canvas
   Algae means that strip matches the gradient's darkest stop instead of flashing white. */
html {
	background: var(--color-algae);
}

body {
	margin: 0;
	font-family:
		Phonic,
		system-ui,
		-apple-system,
		sans-serif;
	font-weight: 400;
	line-height: 26px;
	color: var(--color-mist);
	-webkit-font-smoothing: antialiased;
}

h1,
h2 {
	margin: 0;
	font-weight: 400;
}

p {
	margin: 0;
}

.glass-filters {
	position: absolute;
	width: 0;
	height: 0;
	overflow: hidden;
	pointer-events: none;
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* No border-radius here. An outline already follows the element's own radius, so setting one would reshape
   whatever is focused -- which flattened the pill inputs into rounded rectangles. */
:focus-visible {
	outline: 2px solid var(--color-mist);
	outline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   Splash
   --------------------------------------------------------------------------- */

/* overflow: clip, not hidden. A hidden box is still a scroll container -- it just has no scrollbars -- and this
   one overflows itself by a couple of hundred pixels, because the card sits below the fold by design. So
   anything that scrolls it slides the whole layer, the clip included, and the gradient appears along the
   bottom. What scrolls it is focus: opening the card focuses the first field, and Safari brings a focused
   element into view whether or not preventScroll was asked for. clip is the same painting with no scroll
   container at all, so there is nothing left to scroll. hidden stays first as the fallback. */
.page {
	position: fixed;
	inset: 0;
	overflow: hidden;
	overflow: clip;
	background: linear-gradient(160deg, #294739 0%, #3a6350 60%, #294739 100%);
}

/* The clip is supplied already blurred -- the softening is baked into the file, exactly as the Figma layer is
   composed. No CSS blur is applied on top, because that compounds into a much softer image than the design.
   If the clip is ever replaced with a sharp master, the blur belongs on a backdrop-filter layer over this
   element rather than a filter on it, so the video does not have to be oversized to hide a feathered edge. */
.bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	pointer-events: none;
}

.scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgb(0 0 0 / 10%) 37.44%, rgb(0 0 0 / 0%) 100%);
}

.stage {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100%;

	/* Bottom padding stops exactly at the closed card rather than 24px short of it. The extra used to be
	   breathing room, but it sat outside the flex content box, so the strip's auto margins split the space
	   evenly and then the padding added itself to the lower gap -- a constant 24px off centre. The strip's
	   own auto margin is the breathing room now. */
	padding: 24px clamp(20px, 5vw, 48px) var(--card-peek);
	transition: filter 0.45s var(--ease-out);
}

.is-open .stage {
	filter: blur(6px);
}

/* Ranged left with the copy, not centred over it: the column's left edge is the design's alignment axis. */
.mark {
	display: block;
	width: 117px;
	height: 40px;
	margin-bottom: 78px;
}

/* The column is centred but its text is ranged left, which is what gives the design its editorial feel. */
.copy {
	width: 100%;
	max-width: var(--copy-width);

	/* Third auto margin: see .mq. Together they split the stage's free space into equal thirds. */
	margin-top: auto;
}

.copy h1 {
	margin-bottom: 30px;
	font-family: Recital, Georgia, serif;
	font-size: 20px;
	letter-spacing: -0.4px;
}

.copy .body-copy {
	font-size: 16px;
	letter-spacing: -0.32px;
}

.copy .body-copy + .body-copy {
	margin-top: 30px;
}

.connect {
	display: flex;
	font-size: 16px;
	letter-spacing: -0.32px;
	flex-wrap: wrap;
	gap: 10px;
	align-items: center;
	margin-top: 35px;
}

.signin,
.pill {
	display: inline-flex;
	align-items: center;
	padding: 2px 14px;
	font-size: 14px;
	color: var(--color-mist);
	text-decoration: none;
	white-space: nowrap;
	letter-spacing: -0.28px;
	background: rgb(234 226 122 / 30%);
	border: 1px solid rgb(248 247 240 / 26%);
	border-radius: 50px;

	/* Two declarations, deliberately. The first is the floor every browser gets. The second adds the liquid
	   displacement and is simply dropped by engines that will not take a url() here */
	backdrop-filter: blur(3.2px) saturate(1.12);
	backdrop-filter: url("#liquid-control") blur(3.2px) saturate(1.12);
	transition:
		background 0.2s,
		border-color 0.2s;
}

/* Only the referral button is positioned, and only it blurs on its own account -- the pills sit inside .stage,
   which the open card blurs wholesale. */
.signin {
	position: absolute;
	top: 24px;
	right: clamp(20px, 5vw, 48px);
	z-index: 2;
	transition:
		background 0.2s,
		border-color 0.2s,
		filter 0.45s var(--ease-out);
}

.signin[hidden] {
	display: none;
}

.signin:hover,
.pill:hover {
	background: rgb(234 226 122 / 42%);
	border-color: rgb(248 247 240 / 42%);
}

.signin:focus-visible,
.pill:focus-visible {
	outline: 2px solid var(--color-citron);
	outline-offset: -4px;
}

/* Blurs with the stage: opening the card must not leave one sharp element floating over a blurred page. */
.is-open .signin {
	filter: blur(6px);
}

/* ---------------------------------------------------------------------------
   Signup card
   --------------------------------------------------------------------------- */

/* Anchored to the middle of the viewport and pushed down until only its heading shows. Opening it removes that
   push, so one transform drives the whole movement. */

/* Translating by its own height keeps the closed offset correct however tall the card grows on a narrow screen,
   which a fixed pixel offset would not. */

/* will-change on the element that actually animates. Safari was reported to drag the background clip along with
   this card as it slides, which is what a late layer promotion does: the transition starts, Safari rebuilds the
   layer tree around it, and a neighbour gets caught up in the card's layer. Declaring the transform up front
   means the layer already exists when the transition begins, so there is nothing to regroup. Promoting the clip
   instead was tried first and did not help -- it is this element that moves. */
.card {
	position: absolute;
	will-change: transform;
	bottom: var(--card-gap);
	left: 50%;
	z-index: 2;
	width: min(var(--card-width), calc(100% - 40px));
	min-height: 317px;
	padding: 25px 30px 30px;
	color: var(--color-algae);
	background: var(--color-mist);
	border: 1px solid var(--color-stone);
	border-radius: 15px;
	box-shadow: 0 10px 15px 0 rgb(0 0 0 / 10%);
	transform: translate(-50%, calc(100% - var(--card-peek) + var(--card-gap)));
	transition: transform 0.5s var(--ease-out);
}

.is-open .card {
	transform: translate(-50%, 0);
}

.card-head h2 {
	font-family: Recital, Georgia, serif;
	font-size: 20px;
	letter-spacing: -0.4px;
}

.card-head p {
	font-size: 16px;
	color: var(--color-silt);
}

/* z-index because the panel's entrance animation puts a transform on .card-head, which makes it paint as if it
   were positioned -- and it comes after this button in the markup, so it was covering the lower half of the
   arrow. The heading has no background, so nothing looked wrong; the clicks just stopped landing. */
.toggle {
	position: absolute;
	z-index: 1;
	top: 15px;
	right: 15px;
	display: grid;
	place-items: center;
	width: 30px;
	height: 30px;
	font: inherit;
	font-size: 16px;
	color: var(--color-silt);
	cursor: pointer;
	background: var(--color-fog);
	border: 0;
	border-radius: 8px;
}

/* The arrow is 30px, which is a small thing to hit. This grows the target to 50px without moving or resizing
   anything on screen. */
.toggle::before {
	position: absolute;
	inset: -10px;
	content: "";
}

.toggle:hover {
	color: var(--color-algae);
}

.arrow {
	display: block;
	line-height: 1;
	transition: transform 0.4s var(--ease-out);
}

.is-open .arrow {
	transform: rotate(180deg);
}

/* ---------------------------------------------------------------------------
   Form
   --------------------------------------------------------------------------- */

form {
	display: grid;
	gap: 15px;
	margin-top: 20px;
}

.row2 {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 15px;
}

input {
	width: 100%;
	padding: 13px 20px;
	font: inherit;
	font-size: 14px;
	line-height: 28px;
	color: var(--color-algae);
	letter-spacing: -0.28px;
	background: var(--color-fog);
	border: 1px solid transparent;
	border-radius: 50px;
}

input::placeholder {
	color: var(--color-silt);
}

/* A ring that follows the pill, rather than a border-colour change: on a filled input a 1px shift is too quiet
   to read as focus. box-shadow is used instead of outline so the radius is followed everywhere, and it matches
   what Tailwind's ring utility draws on the FarmAdvisor side. */

/* Everything focusable inside the card sits on Mist, where the splash's light outline is invisible, so the whole
   card shares one ring that hugs each control's own radius.

   Stone is a design decision, not an oversight: it is 1.45:1 against Mist and 1.30:1 against the Fog inputs,
   where WCAG 2.2 SC 1.4.11 asks 3:1 of a focus indicator, so the ring is deliberately quiet. Algae was 9.52:1.
   Raise it with the design, not in passing. */
.card :focus-visible {
	outline: 0;
	box-shadow: 0 0 0 2px var(--color-stone);
}

/* The thank-you panel is given focus so the keyboard lands on the new content, but it is a container rather
   than a control, and the shared ring above would draw a 2px outline around the whole card. Equal specificity
   to that rule, so this has to stay below it. */
.panel:focus-visible {
	box-shadow: none;
}

.hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.submit {
	justify-self: start;
	width: 125px;
	padding: 13px 20px;
	font: inherit;
	font-size: 14px;
	line-height: 28px;
	color: var(--color-mist);
	text-align: center;
	letter-spacing: -0.28px;
	cursor: pointer;
	background: var(--color-algae);
	border: 0;
	border-radius: 50px;
	transition:
		opacity 0.2s,
		box-shadow 0.27s var(--ease-in-out);
}

/* The primary button is Algae itself, so its ring needs a Mist gap or it merges into the button. */
.submit:focus-visible {
	box-shadow:
		0 0 0 2px var(--color-mist),
		0 0 0 4px var(--color-algae);
}

/* The halo is the Submit track of FACTLandingAnimatedMockUp (node 40:492): rgba(172, 221, 104, 0.75) -- that is
   --color-positive at 75% -- blurred 25px, with no offset and no spread, so it sits evenly around the pill rather
   than falling below it like a shadow. Figma writes the blur as 12.5px; CSS box-shadow takes twice that.

   Not on [disabled]: the button spends most of its life disabled, and a glow on a control that cannot be pressed
   reads as an invitation. The three rules run in ascending specificity, which is what decides the overlaps --
   :not() makes this one (0,3,0), so it would outrank the ring above whatever the source order. */
.submit:hover:not([disabled]) {
	opacity: 0.9;
	box-shadow: 0 0 25px 0 rgb(172 221 104 / 75%);
}

/* Hovering a focused button therefore has to restate the ring, or the glow alone would replace it. */
.submit:focus-visible:hover:not([disabled]) {
	box-shadow:
		0 0 0 2px var(--color-mist),
		0 0 0 4px var(--color-algae),
		0 0 25px 0 rgb(172 221 104 / 75%);
}

.submit[disabled] {
	cursor: not-allowed;
	opacity: 0.5;
}

.field {
	position: relative;
}

.field input {
	padding-right: 50px;
}

.mark-valid,
.mark-invalid {
	position: absolute;
	top: 50%;
	right: 20px;
	display: none;
	translate: 0 -50%;
}

.field.is-valid .mark-valid,
.field.is-invalid .mark-invalid {
	display: block;
	animation:
		mark-fade 0.22s var(--ease-in-out) both,
		mark-rise 0.39s cubic-bezier(0, 0.763, 0.58, 1) both;
}

@keyframes mark-fade {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes mark-rise {
	from {
		transform: translateY(17px);
	}

	to {
		transform: translateY(0);
	}
}

.panel[hidden] {
	display: none;
}

.panel > * {
	animation:
		panel-fade 0.52s var(--ease-in-out) both,
		panel-rise 0.72s cubic-bezier(0, 0.62, 0.3, 1) both;
}

@keyframes panel-fade {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes panel-rise {
	from {
		transform: translateY(37px);
	}

	to {
		transform: translateY(0);
	}
}

.panel-done {
	display: flex;
	flex-direction: column;
	gap: 15px;
	align-items: center;
	justify-content: center;
	min-height: 262px;
	text-align: center;
}

.panel-done h2 {
	font-family: Recital, Georgia, serif;
	font-size: 20px;
	letter-spacing: -0.4px;
}

.panel-done p {
	margin-top: -11px;
	font-size: 16px;
	color: var(--color-silt);
}

/* Overrides the lift that .panel-done > * gives its siblings: node 70:515 fades on the same curve with no
   movement under it. Equal specificity to that rule, so it is source order that settles it -- this rule has to
   stay below it. */
.again {
	margin-top: 18px;
	padding: 13px 20px;
	font: inherit;
	font-size: 14px;
	line-height: 28px;
	color: var(--color-silt);
	letter-spacing: -0.28px;
	cursor: pointer;
	background: var(--color-fog);
	border: 0;
	border-radius: 50px;
	animation: panel-fade 0.52s var(--ease-in-out) both;
}

.again:hover {
	color: var(--color-algae);
}

.status {
	display: none;
	font-size: 14px;
}

.status[data-state="ok"] {
	display: block;
	color: var(--color-algae);
}

.status[data-state="err"] {
	display: block;
	color: var(--color-negative);
}

/* The card is taller than a short viewport once open, so let it scroll rather than clip the submit button.

   dvh over vh, with vh left underneath for anything that does not know the unit: on mobile Safari `vh` means
   the *large* viewport, the one with the URL bar retracted, so calc(100vh - 32px) in landscape resolves taller
   than what is actually on screen and clips the very button this rule exists to keep reachable. */
@media (max-height: 560px) {
	.is-open .card {
		max-height: calc(100vh - 32px);
		max-height: calc(100dvh - 32px);
		overflow-y: auto;
	}
}

/* The subtitle wraps to two lines in a narrow card, so the closed strip has to be taller or it clips the line
   that explains what the form is for. */
@media (max-width: 560px) {
	:root {
		--card-peek: 128px;
	}
}

/* iOS Safari zooms the viewport whenever a focused input's text is under 16px. Now on touch 
	pointers only, leaving the desktop card at 14px. */
@media (pointer: coarse) {
	input {
		font-size: 16px;
	}
}

@media (max-width: 460px) {
	.row2 {
		grid-template-columns: 1fr;
	}

	.mark {
		margin-bottom: 48px;
	}
}

/* Brand marquee ------------------------------------------------------------------------------------------

   Full bleed without a margin hack: .stage already centres its children, so a child wider than the content
   box overflows evenly on both sides, and .page's overflow: clip keeps that from becoming a scrollbar. */

/* Auto margins, not a fixed one. The strip sits in the clear space between the copy and the closed card,
   and that space changes with every viewport height -- a fixed margin pinned the gap above it and let the
   gap below absorb the difference, which ran to 89px on a tall window. Three auto margins (one above the
   copy, one either side of the strip) split the free space into equal thirds, so the strip is centred in
   its gap at every height and the copy keeps matching breathing room above it. */
.mq {
	width: 100vw;
	overflow: hidden;
	flex-shrink: 0;
	margin-block: auto;
}


/* Sentence case in the serif, not the uppercase mono strapline CropTrak uses -- the design sets it as a
   quiet line of the same voice as the headline. */
.mq-l {
	margin: 0 0 14px;
	font-family: Recital, Georgia, serif;
	font-size: 15px;
	line-height: 1.2;
	text-align: center;
	color: rgb(248 247 240 / 80%);
}

/* padding-right needed to make the loop seamless */

/* The track is viewport width, never wider. Logos are positioned absolutely inside it and main.js moves
   them, so anything scrolled off is clipped instead of painted -- which is the whole point. The previous
   version animated one 5060px element holding 44 logos and Safari progressively stopped painting them. */
.mq-t {
	position: relative;
	height: 40px;
}

.mq-t img {
	position: absolute;
	top: 50%;
	left: 0;
	width: auto;
	height: calc(var(--logo-h) * var(--logo-scale, 1));
	opacity: 0.7;
	transition: opacity 0.3s var(--ease-out);
}

.mq-t img:hover {
	opacity: 1;
}

.b-del-monte {
	--logo-h: 40px;
}

.b-tropicana {
	--logo-h: 30px;
}

.b-pepsico {
	--logo-h: 31px;
}

.b-corteva {
	--logo-h: 22px;
}

.b-yara {
	--logo-h: 38px;
}

.b-limagrain {
	--logo-h: 26px;
}

.b-organic-girl {
	--logo-h: 32px;
}

.b-walther-farms {
	--logo-h: 38px;
}

.b-lipman {
	--logo-h: 40px;
}

.b-pivot-bio {
	--logo-h: 20px;
}

.b-weaver {
	--logo-h: 20px;
}

/* The page is one fixed viewport with no scroll, so the strip has to fit between the copy and the closed
   card rather than push anything. Mobile Safari is the tight case and not for the reason a desktop window
   suggests: its chrome takes about 160pt, so an iPhone 17 reports a 714px viewport, not 874. At full size
   the strip clears the card there by 3px. So it shrinks rather than disappears -- one scale factor drives
   all eleven heights, since they are each a variable rather than a literal. */
@media (max-height: 730px) {
	/* The closed card reserves 128px on a narrow screen, but the two-line subtitle it is sized around only
	   reaches 109 -- the rest is slack. Reclaiming part of it lets the card sit lower and gives the strip
	   somewhere to be, without touching the desktop layout or clipping the line that explains the form. */
	:root {
		--card-peek: 114px;
	}

	.mq-l {
		margin-bottom: 7px;
		font-size: 12px;
	}

	/* Same logos, smaller. main.js measures whatever they render at, so nothing else needs telling. */
	.mq-t {
		--logo-scale: 0.7;

		height: 28px;
	}
}

/* Below this no arrangement fits, and the copy matters more than the logos. 655px is measured, not guessed:
   clearance to the card falls about a pixel for every two the viewport loses, and with the reclaimed peek it
   reaches single digits just under 655. Note the number that decides this is the browser viewport, not the
   phone -- Safari's chrome takes about 160pt, so an iPhone 17 is 714px here and a 14 is 664px. */
@media (max-height: 655px) {
	.mq {
		display: none;
	}

	/* Without the strip there is nothing below the copy to balance against, and a lone margin-top: auto
	   would push it onto the card. Hand the centring back to justify-content. */
	.copy {
		margin-top: 0;
	}

	/* And give the 24px back. It was removed so the strip could centre against the card exactly, but with
	   no strip the taller content box just lets the centred copy sit 12px lower, into the card. */
	.stage {
		padding-bottom: calc(var(--card-peek) + 24px);
	}
}

@media (prefers-reduced-motion: reduce) {
	.card,
	.stage,
	.arrow,
	.pill,
	.submit,
	.signin {
		transition: none;
	}

	.mark-valid,
	.mark-invalid,
	.panel > *,
	.mq-t {
		animation: none;
	}
}
