/*
 * ==========================================================================
 * FAXDIRECT.CA — LUXE LAYER
 *
 * Loads after style.css and never replaces it. The base sheet keeps every
 * component's structure, contrast pairing and accessibility work; this file
 * changes the material the site is made of and adds the scroll narrative.
 *
 * Two materials, one lamp:
 *   VOID    #081726  the application's own navy, taken down until white type
 *           clears 15:1. Full-bleed; photography shows through it.
 *   PAPER   #FFFFFF / #F7FBFF / #EBF3FD  the surfaces the application itself
 *           uses — body, list, selected row. Cool, never cream.
 *   CHROME  #1A4F8A  the product's title bar, unchanged.
 *   ACTION  #0078D4  unchanged. The only chroma that takes a click.
 *
 * Everything here degrades. With JavaScript off the photographs are simply
 * there, unmasked, and the page is the base theme in the application's own
 * colours. Under prefers-reduced-motion nothing drifts, nothing plays, and the
 * whole interaction layer stands down.
 *
 * CONTENTS
 *   L1.  Tokens — material overrides
 *   L2.  Type — one family, three weights
 *   L3.  Vignette, grain, paper grain
 *   L4.  Surfaces — acts and paper bands
 *   L5.  Chrome — header, utility, nav, footer
 *   L6.  Eyebrows, section heads and act numerals
 *   L7.  Components — buttons, cards, console, timeline, chips, drawers,
 *        pricing, feature browser, forms
 *   L8.  Hero act
 *   L9.  The seal — signature element
 *   L15. Plates — photography
 *   L16. Ambient motion and footage
 *   L17. Flourish — hover and state
 *   L11. Stage controls (sound, motion)
 *   L12. Story states — split headlines, act reveals
 *   L13. Reduced motion, no-JS, print
 *   L14. Responsive
 * ==========================================================================
 */


/* ==========================================================================
   L1. TOKENS — MATERIAL OVERRIDES
   Every pairing below is contrast-measured against its own surface, the way
   the base sheet does it. The palette is sampled from the application rather
   than invented, so the site and the product are made of the same paint.
   ========================================================================== */

:root {
	/* ── The two materials, taken from the application ─────────────────
	   Sampled from the product itself rather than invented: the title bar
	   is #1A4F8A, the list sits on #F7FBFF, a selected row is #EBF3FD and
	   the body is white. The site is the front of that application, so it
	   is built from the same paint.

	   Void is that navy taken down until white type clears 15:1 on it. It
	   is not black and must not become black — the hue is the whole point,
	   and it is what stops the dark bands reading as a generic dark theme. */
	--void: #081726;
	--void-2: #0E2438;
	--void-3: #16324A;

	/* The application's own chrome, unchanged. Used where the site should
	   look like the product rather than like a page. */
	--chrome: #1A4F8A;

	/* Light surfaces, straight from the app: white body, the list's cool
	   white, and the selected-row blue. Cool, not warm — a warm cream
	   beside a blue application reads as a different product. */
	--paper: #FFFFFF;
	--paper-2: #F7FBFF;
	--paper-3: #EBF3FD;

	/* Hairlines. On void, a pale blue at low alpha; on paper, ink. */
	--edge: rgba(160, 200, 240, 0.16);
	--edge-hi: rgba(160, 200, 240, 0.32);
	--edge-ink: rgba(8, 23, 38, 0.13);

	/* ── Base tokens, re-pointed ───────────────────────────────────────
	   These are the variables the base sheet already draws with, so
	   re-pointing them re-skins every component at once. */
	--night: #081726;
	--night-2: #0E2438;
	--night-3: #16324A;
	--night-4: #1A4F8A;

	--white: #FFFFFF;
	--glacier: #F7FBFF;
	--glacier-2: #EBF3FD;

	/* Text on paper — measured on --glacier #F7FBFF */
	--ink: #08172B;          /* 16.4:1 */
	--body: #33465C;         /* 8.86:1 */
	--muted: #566A80;        /* 5.42:1 */
	--faint: #93A3B4;        /* decorative only */
	--line: #D3E1F0;
	--line-2: #E4EDF7;

	/* Text on void — measured on #081726 */
	--on-night: #E8F1FA;     /* 15.62:1 */
	--on-night-muted: #A8BED2; /* 8.31:1 */
	--line-dark: rgba(168, 190, 210, 0.12);
	--line-dark-2: rgba(168, 190, 210, 0.20);

	/* Action Blue, Info Cyan, Success, Metric, Alert and Navy are all
	   inherited unchanged from the base sheet. The site still has to match
	   the application it fronts, and Navy #1A4F8A is the product's own
	   title bar — see DESIGN.md §4, Console. Info Cyan improves to 9.70:1
	   on the deeper void, which is the only ratio this file changes. */
	--accent-wash: #E7F0FB;
	--info-wash: #E4F2F8;
	--success-wash: #E3F0E8;
	--metric-wash: #FBF2DF;
	--alert-wash: #FAEAE6;
	--amber-wash: #FBF2DF;

	/* The colour a heading takes on a dark band. The reference site sets its
	   headings in a bright blue against near-black, and the application's own
	   action colour is the honest equivalent — so headlines on void lift into
	   Action Blue's brighter sibling rather than staying paper white. 7.1:1 on
	   --void, well past the 3:1 a headline needs. */
	--display-on-night: #4BA3F0;

	/* ── Radius — engraved, not rounded ────────────────────────────────
	   The base sheet uses 8px because three generated sources agreed on
	   it. This theme is a printed record, and print has corners: 3px is
	   the largest radius that still reads as a cut edge rather than a
	   soft one. Pills survive, because a status chip is a stamp. */
	--r-sm: 2px;
	--r: 3px;
	--r-lg: 4px;
	--r-xl: 6px;

	/* Shadow tinted with void rather than navy, and deeper: paper needs to
	   sit above a dark volume, not above a light page. */
	--shadow-color: 8, 23, 38;
	--sh-xs: 0 1px 2px rgba(var(--shadow-color), 0.10);
	--sh-sm: 0 2px 10px rgba(var(--shadow-color), 0.12);
	--sh: 0 8px 28px rgba(var(--shadow-color), 0.18);
	--sh-card: 0 4px 14px rgba(var(--shadow-color), 0.10);
	--sh-hover: 0 16px 40px rgba(var(--shadow-color), 0.20);
	--sh-lg: 0 26px 70px rgba(var(--shadow-color), 0.34);
	--sh-xl: 0 44px 120px rgba(var(--shadow-color), 0.52);

	/* ── Motion — pace is the luxury signal ───────────────────────────
	   expo.out. Nothing overshoots, nothing bounces; things arrive and
	   settle. Hover stays quick enough to read as feedback (320ms), the
	   scroll reveals take almost a second, and the act transitions take
	   longer than any of them. */
	--ease: cubic-bezier(0.16, 1, 0.30, 1);
	--ease-out: cubic-bezier(0.16, 1, 0.30, 1);
	--ease-p2: cubic-bezier(0.16, 1, 0.30, 1);
	--ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
	--fast: 180ms;
	--dur: 320ms;
	--slow: 900ms;
	--act: 1400ms;

	/* Vertical rhythm opens up. One value, as before. */
	--section-y: clamp(4.5rem, 9vw, 9.5rem);

	/* Type — one family, three jobs.
	   The Didone that used to sit here was the last thing pulling in a third
	   direction: an engraved serif against a blue Windows application and a
	   reference site set in a heavy geometric sans. Plex Sans is the
	   application's own face, so headlines, interface and data are now the
	   same voice at different weights. It also costs one fewer web font —
	   the base sheet already loads 400 through 700. */
	--display: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

	/* Stage */
	--stage-h: 100vh;
}


/* ==========================================================================
   L2. TYPE — ONE FAMILY, THREE WEIGHTS
   One face now, which is the application's own:
     IBM Plex Sans 700  headlines. Set large and tracked tight, so the mass
                        comes from weight and size rather than from a
                        different typeface.
     IBM Plex Sans 400–600  everything interface.
     IBM Plex Mono      every number, ID, timestamp and label. Unchanged.
   A display serif used to sit on top of this and it was the last thing
   pulling against the product: an engraved Didone in front of a blue Windows
   application reads as two companies. Contrast now comes from weight and
   scale, which is what the reference site does too.
   ========================================================================== */

body {
	background: var(--void);
	color: var(--body);
}

h1,
h2,
.display {
	font-family: var(--display);
	font-weight: 700;
	font-synthesis: none;
	text-wrap: balance;
}

/*
 * A grotesque needs closing up where a Didone needed opening: Plex Sans is
 * drawn for interface sizes, and its sidebearings read as gaps once a headline
 * is 90px. Tracking is tighter than the serif's was for that reason, and the
 * sizes come down a touch because Plex has the larger x-height of the two and
 * sets visually bigger at the same nominal value.
 */
h1 {
	font-size: clamp(2.75rem, 5.9vw, 5.75rem);
	line-height: 1.0;
	letter-spacing: -0.035em;
}

h2 {
	font-size: clamp(2rem, 3.9vw, 3.5rem);
	line-height: 1.06;
	letter-spacing: -0.03em;
}

/*
 * Headlines lift into blue on the dark bands, the way the reference site sets
 * its section headings against near-black. On paper they stay ink: blue on
 * white at display size is legible but it turns every band into a link, and
 * the accent has to keep meaning "this takes a click".
 */
.section--night h2,
.section--night h1,
.hero h1,
.cta-band h2 {
	color: var(--display-on-night);
}

/* The hero is the exception to the exception. It sits on a photograph, and a
   mid-blue on a bright frame is the one place this palette can lose its
   contrast — so the opening headline stays the near-white. */
.hero h1 {
	color: var(--on-night);
}

/* h3 stays Plex Sans. A page of serif subheads under serif headlines reads
   as a book, and this is a control panel with a cover. */
h3 {
	letter-spacing: -0.012em;
}

.lead {
	font-size: clamp(1.0625rem, 0.5vw + 0.95rem, 1.3125rem);
	line-height: 1.58;
	max-width: 62ch;
}

/* Every figure keeps tabular alignment, as in the base sheet. */
.mono,
.eyebrow,
.chip,
.timeline__time {
	font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   L3. STAGE — CANVAS, VIGNETTE, GRAIN
   One canvas for the page lifetime, fixed behind everything. Paper bands are
   opaque and cover it; void acts are transparent and let it through. That
   compositing rule is also the motion budget: the stage is only ever visible
   where no component is animating.
   ========================================================================== */

/*
 * The stage is gone.
 *
 * Up to 3.1.0 a three.js point cloud rendered behind the whole page. It was
 * built when nothing else on the site was a picture, and once real photography
 * was scanning in behind every act it stopped reading as a document and
 * started reading as a field of molecules — an unrelated science graphic
 * sitting behind a fax company. It has been removed rather than dimmed, and
 * three.js with it: about 645 KB, one WebGL context, and the largest single
 * source of motion on the page.
 *
 * What replaced it is in §L15 and §L16: the pictures themselves move.
 */

/* A vignette still belongs here — it frames the page rather than the stage,
   and it is what keeps a full-bleed raster from running flat into the edges
   of the window. */
.fdcl-vignette {
	position: fixed;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(125% 95% at 50% 42%, transparent 42%, rgba(8, 23, 38, 0.42) 80%, rgba(8, 23, 38, 0.78) 100%);
}

/* Film grain over the whole page — 3%, and it does not move. A grain that
   animates is a filter; a grain that sits still is stock. */
.fdcl-grain {
	position: fixed;
	inset: 0;
	z-index: 3;
	pointer-events: none;
	opacity: 0.05;
	mix-blend-mode: overlay;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
	background-size: 180px 180px;
}

/* Everything the reader touches sits above the stage. */
.site-header,
#main,
.site-footer,
.to-top {
	position: relative;
	z-index: 1;
}

.site-header {
	z-index: 40;
}


/* ==========================================================================
   L4. SURFACES — ACTS AND PAPER BANDS
   Declared in cascade order: .section is paper, the glacier variants are
   heavier paper, and .section--night is a transparent act. Same specificity
   throughout, so source order decides and the file stays readable.
   ========================================================================== */

.section {
	background: var(--paper);
}

.section--glacier {
	background: var(--paper-2);
}

.section--glacier-2 {
	background: var(--paper-3);
}

/* An act. Transparent, so the stage is the background, and given room —
   the camera needs travel and the type needs air. */
.section--night {
	background: transparent;
	padding-block: clamp(6rem, 13vh, 11rem);
}

/*
 * The scrim.
 *
 * Type over a point cloud is type over noise, so every act carries a
 * directional scrim behind its content: opaque where the words are, clearing
 * to nothing where the stage should be seen. Acts set their heads left (§L6),
 * so the scrim is left-weighted, and the composition becomes text on a clean
 * field with the document visible in the space beside it.
 *
 * z-index -1 inside #main, which is itself z-index 1 — so it sits above the
 * fixed canvas and below everything in the section.
 */
.section--night::after,
.cta-band::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background: linear-gradient(
		100deg,
		rgba(8, 23, 38, 0.95) 0%,
		rgba(8, 23, 38, 0.88) 30%,
		rgba(8, 23, 38, 0.42) 58%,
		rgba(8, 23, 38, 0) 78%
	);
}

/* Paper grain: a printed band should not be a flat fill. 2%, static.
   The conversion band is excluded — it is an act, not paper. */
.section:not(.section--night):not(.cta-band)::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	opacity: 0.022;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23p)'/%3E%3C/svg%3E");
}

/* The boundary trace, kept from the base sheet — the seam where the surface
   changes is a path the document travels. Repainted in paper rather than
   Action Blue: on the void it is a fold in the stock, not a wire. main.js
   still draws it in with .is-traced, so JS-off gets a flat edge as before. */
.section--glacier::before,
.section--glacier-2::before,
.section--night::before {
	background: linear-gradient(90deg, transparent, var(--edge-hi) 24%, rgba(200, 220, 240, 0.55) 50%, var(--edge-hi) 76%, transparent);
	opacity: 1;
	height: 1px;
}

/* Where paper meets void, the paper gets a lit top edge so the band reads as
   a sheet with thickness rather than a colour change. */
.section--night + .section:not(.section--night),
.hero + .section:not(.section--night) {
	box-shadow: 0 -1px 0 var(--edge-hi), var(--sh-lg);
}

/* Consecutive paper bands: hairline, no shadow, so a run of them reads as
   pages in one file. */
.section:not(.section--night) + .section:not(.section--night) {
	border-top: 1px solid var(--line-2);
}

/* Interior page heroes are the same act as the homepage hero — transparent,
   with the volume behind them. In the base sheet they carry an opaque Night
   fill, which on this theme would read as a flat black band with the stage
   hidden behind it. */
.page-hero {
	background: transparent;
	padding-block: clamp(4.5rem, 10vh, 8rem) clamp(3.5rem, 8vh, 6rem);
}

/*
 * The editorial templates — page, single, index, archive, search, 404 — put
 * their content in a bare .container with no .section wrapper, so on a dark
 * body they would be dark type on the void. Every one of them uses
 * .content-area, so that is where the paper goes: a sheet laid on the desk,
 * with the volume showing in the margins. The article was always the one
 * place on this site where the document metaphor could be literal.
 */
.content-area {
	background: var(--paper);
	padding: clamp(2rem, 4vw, 3.5rem) clamp(1.25rem, 3vw, 3rem);
	margin-block: clamp(2.5rem, 5vw, 5rem);
	border-radius: var(--r-lg);
	box-shadow: var(--sh-lg), 0 0 0 1px var(--edge);
	position: relative;
}


/* ==========================================================================
   L5. CHROME — HEADER, UTILITY, NAV, FOOTER
   ========================================================================== */

/*
 * The base sheet's header is a light bar: white fill, Glacier utility strip,
 * dark type. On this theme it sits over the void at the top of every page, so
 * the whole thing inverts. This is the largest single re-colour in the file,
 * and it is all of it — nothing below the header needed the same treatment,
 * because the base sheet already ships .on-dark variants for components.
 */
.site-header {
	background: transparent;
	color: var(--on-night-muted);
	border-bottom: 0;
}

/*
 * The header is the application's own arrangement, and the two bars are
 * measured against different surfaces because they now are different surfaces.
 *
 *   UTILITY   the product's title bar, #1A4F8A. It carries the four things a
 *             visitor may want before they have read anything — residency,
 *             status, help, language — and putting them on the chrome colour
 *             is the same move the application makes with its own title bar.
 *   MASTHEAD  white. The navigation is the most-read thing on the site and it
 *             belongs on the most legible surface there is.
 *
 * The whole header is therefore light now, which means the base sheet's own
 * treatment is right again and most of what used to be here has gone rather
 * than been re-pointed.
 */
.utility {
	background: var(--chrome);
	border-bottom: 0;
	color: #FFFFFF;
}

.utility a,
.utility span {
	color: rgba(255, 255, 255, 0.88);  /* 5.7:1 on #1A4F8A */
}

.utility a:hover,
.utility a:focus-visible {
	color: #FFFFFF;
}

.utility svg {
	color: rgba(255, 255, 255, 0.72);
}

/*
 * The status dot goes back to the mint. --success #059669 was chosen when this
 * strip was white; on the chrome blue it drops to 1.6:1 and disappears, while
 * --success-hi #34D399 reads at 4.6:1. Same trap as before, opposite direction
 * — the dot follows the surface, not the palette.
 */
.utility .pulse {
	background: var(--success-hi);
}

.lang-switch a {
	color: rgba(255, 255, 255, 0.80);
}

.lang-switch a[aria-current=true] {
	color: #FFFFFF;
	background: rgba(255, 255, 255, 0.18);
}

/*
 * The masthead is white and stays white. It used to be transparent over the
 * hero and materialise on scroll, which only made sense while the page behind
 * it was the void; against a white bar there is nothing to materialise into,
 * so the only thing scroll changes now is the shadow that lifts it off the
 * content passing underneath.
 */
.masthead {
	background: var(--paper);
	border-bottom: 1px solid var(--line);
	transition: box-shadow var(--dur) var(--ease);
}

.is-scrolled .masthead {
	box-shadow: var(--sh-sm);
}

.brand__text {
	color: var(--ink);
}

.brand__text em {
	font-family: var(--display);
	font-style: italic;
	font-weight: 600;
	color: var(--accent);
}

/* The bundled dark-on-light logo is correct again, so the reversed swap this
   file used to apply is gone. Nothing to declare. */

.nav a {
	color: var(--body);
}

.nav a:hover,
.nav a:focus-visible {
	color: var(--accent);
	background: var(--accent-wash);
}

.nav .current-menu-item > a,
.nav .current_page_item > a {
	color: var(--accent);
}

.nav ul ul {
	background: var(--paper);
	border-color: var(--line);
	border-radius: var(--r);
	box-shadow: var(--sh-lg);
}

.masthead .btn--quiet {
	color: var(--ink);
}

.masthead .btn--quiet:hover {
	color: var(--accent);
}

.nav-toggle {
	color: var(--ink);
	border-color: var(--line);
	border-radius: var(--r-sm);
}

.nav-toggle:hover {
	background: var(--paper-3);
}

.site-footer {
	background: transparent;
	border-top: 1px solid var(--edge);
}

.site-footer__col h4 {
	font-family: var(--mono);
	font-size: var(--t-xs);
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--on-night-muted);
}


/* ==========================================================================
   L6. EYEBROWS, SECTION HEADS AND ACT NUMERALS
   The homepage order is the sales argument — claim, credibility, mechanism,
   tools, residency, capability, fit, migration, price, security, support,
   objections, conversion. That is a real sequence, so numbering it carries
   information rather than decorating it. The numerals are a CSS counter on
   the eyebrow: nothing is added to the document, and they vanish with the
   stylesheet.
   ========================================================================== */

body {
	counter-reset: act;
}

.section {
	counter-increment: act;
}

.eyebrow {
	font-size: var(--t-xs);
	letter-spacing: 0.18em;
	gap: 0.7rem;
	color: var(--muted);
}

.eyebrow::before {
	width: 42px;
	transition: width var(--slow) var(--ease);
}

.on-dark .eyebrow,
.section--night .eyebrow {
	color: var(--on-night-muted);
}

/* The rule is drawn as the section arrives, by story.js. Scoped to
   .fdcl-story, which that file only sets once it has GSAP in hand — so a
   blocked script leaves a drawn rule rather than an invisible one. */
.fdcl-story .section-head .eyebrow::before {
	width: 0;
}

.fdcl-story .section-head.is-drawn .eyebrow::before {
	width: 42px;
}

/* The numeral. Roman, engraved, ahead of the label it belongs to.
   Only on .section-head: the page heroes sit outside the counter, where the
   numeral would read "0". */
.section-head .eyebrow::after {
	content: counter(act, upper-roman);
	font-family: var(--display);
	font-size: 1.2em;
	font-weight: 500;
	letter-spacing: 0.04em;
	color: var(--faint);
	order: -1;
	margin-right: 0.1rem;
}

.on-dark .section-head .eyebrow::after,
.section--night .section-head .eyebrow::after {
	color: var(--edge-hi);
}

/* Acts are left-aligned and wide; paper bands stay centred. No two adjacent
   sections share a composition, which is the base sheet's own rule. */
.section--night .section-head--center {
	margin-inline: 0;
	text-align: left;
	max-width: 78ch;
}

.section--night .section-head--center .eyebrow {
	justify-content: flex-start;
}

.section-head {
	margin-bottom: clamp(2.5rem, 4.5vw, 4rem);
}

.section-head p {
	max-width: 58ch;
}

.section--night .section-head--center p {
	margin-inline: 0;
}


/* ==========================================================================
   L7. COMPONENTS
   The base sheet's structure is untouched. What changes here is material:
   corners, hairlines, weight of shadow, and where warmth sits.
   ========================================================================== */

/* ── Buttons ───────────────────────────────────────────────────────────
   Plex Sans, uppercase mono-ish tracking on the small ones, 3px corners.
   Movement on press only, as before. */
.btn {
	letter-spacing: 0.01em;
	border-radius: var(--r);
}

.btn--primary {
	box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset, 0 6px 18px rgba(0, 120, 212, 0.22);
}

.btn--ghost {
	border-color: var(--edge-hi);
	color: var(--on-night);
}

.btn--ghost:hover {
	background: rgba(200, 220, 240, 0.07);
	border-color: var(--on-night);
}

.btn--secondary {
	background: var(--paper);
	border-color: var(--line);
}

/* ── Cards ────────────────────────────────────────────────────────────
   Paper above paper. The lift is smaller than the base sheet's 3px because
   the shadow is doing more work; a card that jumps reads cheap. */
.card {
	background: var(--paper);
	border-color: var(--line);
	border-radius: var(--r-lg);
	box-shadow: var(--sh-xs);
}

.card:hover {
	transform: translateY(-2px);
	box-shadow: var(--sh-card);
	border-color: var(--muted);
}

.card--dark {
	background: rgba(200, 220, 240, 0.035);
	border-color: var(--line-dark);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
}

.card--dark:hover {
	background: rgba(200, 220, 240, 0.06);
	border-color: var(--edge-hi);
}

.card__icon {
	border-radius: var(--r-sm);
}

/* ── Sector tiles ─────────────────────────────────────────────────────── */
.sector {
	background: var(--paper);
	border-color: var(--line);
	border-radius: var(--r-lg);
}

/* ── Console and application window ───────────────────────────────────
   Both stay light and product-accurate: DESIGN.md §4 settles that the frame
   representing the application is the colour of the application, and the
   Navy #1A4F8A title bar is the product's own. What luxe adds is the frame
   around it — a hairline and a deep shadow, so the product floats in the
   volume the camera is looking into instead of sitting on the page. */
.console,
.appwin {
	border-radius: var(--r-lg);
	box-shadow: var(--sh-lg), 0 0 0 1px var(--edge);
}

.section--night .console,
.on-dark .console {
	box-shadow: var(--sh-xl), 0 0 0 1px var(--edge-hi);
}

.appwin {
	box-shadow: var(--sh-xl), 0 0 0 1px var(--edge-hi);
}

/* ── Timeline ─────────────────────────────────────────────────────────
   Unchanged in behaviour and in colour: Action Blue rail, green only on the
   delivered chip. Only the dot's easing slows to the luxe curve. */
.timeline__dot {
	transition-duration: var(--dur);
}

/* ── Status chips ─────────────────────────────────────────────────────── */
.chip {
	letter-spacing: 0.1em;
	border-radius: var(--r-pill);
}

/* ── Trust drawers ────────────────────────────────────────────────────── */
.drawer {
	border-radius: var(--r);
	border-color: var(--line-dark);
}

.on-dark .drawer,
.section--night .drawer {
	background: rgba(200, 220, 240, 0.03);
}

/* ── Pricing ──────────────────────────────────────────────────────────
   The UNLIMITED chip keeps Info Cyan on a dark chip — legal at 9.70:1 on
   the deeper void, and still the one place cyan appears on a card. */
.plan {
	background: var(--paper);
	border-color: var(--line);
	border-radius: var(--r-lg);
}

.plan__price,
.plan__amount {
	font-variant-numeric: tabular-nums;
}

/* ── Feature browser ──────────────────────────────────────────────────── */
.fb__tab,
.fb__panel {
	border-radius: var(--r);
}

/* ── Forms ────────────────────────────────────────────────────────────── */
.field input,
.field select,
.field textarea,
.checker input {
	border-radius: var(--r);
	background: var(--paper);
	border-color: var(--line);
}

.field input:focus,
.field select:focus,
.field textarea:focus,
.checker input:focus {
	border-color: var(--info-ink);
}

/* ── Conversion band ──────────────────────────────────────────────────
   The last act. Transparent so the seal has the stage behind it, with a
   hairline frame instead of a fill. */
.cta-band {
	background: transparent;
	border-top: 1px solid var(--edge);
	border-bottom: 1px solid var(--edge);
	padding-block: clamp(5rem, 11vh, 9rem);
}


/* ==========================================================================
   L8. HERO ACT
   Act I. The application window over the volume, with the stage showing the
   document it is about to receive. The base sheet's signal-grid texture is
   retired here: the stage is the texture now, and two backgrounds competing
   is exactly the noise the base DESIGN.md warns about.
   ========================================================================== */

.hero {
	background: transparent;
	padding-block: clamp(4.5rem, 11vh, 9rem) clamp(5rem, 12vh, 10rem);
	min-height: min(100svh, 940px);
	display: flex;
	align-items: center;
	overflow: visible;
}

/*
 * The base sheet's signal-grid texture is retired and its layer reused as the
 * hero's scrim — the stage is the texture now, and two backgrounds competing
 * is the noise DESIGN.md §1 warns about. main.js still writes --hero-par to
 * this element, so the scrim drifts on scroll inside the preset's 5–15% band.
 */
.hero::before {
	display: block;
	background-image: linear-gradient(
		100deg,
		rgba(8, 23, 38, 0.96) 0%,
		rgba(8, 23, 38, 0.90) 28%,
		rgba(8, 23, 38, 0.44) 55%,
		rgba(8, 23, 38, 0) 76%
	);
	background-size: auto;
	-webkit-mask-image: none;
	mask-image: none;
}

.hero::after {
	display: none;
}

.hero__inner {
	width: 100%;
	gap: clamp(2.5rem, 5vw, 5rem);
}

.hero h1 {
	color: var(--on-night);
	max-width: 22ch;
}

.hero__sub {
	max-width: 48ch;
	color: var(--on-night-muted);
}

.hero-badge {
	background: rgba(200, 220, 240, 0.05);
	border-color: var(--edge);
	border-radius: var(--r-pill);
	font-family: var(--mono);
	font-size: var(--t-xs);
	letter-spacing: 0.06em;
}

.hero-badge__tag {
	border-radius: var(--r-sm);
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

/* Retired with the stage that drove it. The scan head now belongs to the
   picture (§L16) rather than to the section, which is where it always should
   have been — it is the machine's lamp, not a rule across the layout. */
.hero__scan {
	display: none;
}


/* ==========================================================================
   L9. THE SEAL — SIGNATURE ELEMENT
   Every send leaves a receipt; the receipt is the product's proof artifact.
   The seal is that artifact drawn once, in the conversion band, at the end of
   the narrative: a debossed ring with the transmission legend set around it
   in mono, struck when the last act resolves. It is decorative to assistive
   tech — the receipt itself is described in the copy beside it.
   ========================================================================== */

.fdcl-seal {
	position: absolute;
	right: clamp(1.5rem, 5vw, 5rem);
	top: 50%;
	width: clamp(132px, 15vw, 208px);
	aspect-ratio: 1;
	transform: translateY(-50%) rotate(-8deg);
	pointer-events: none;
	opacity: 0;
	z-index: 0;
}

.fdcl-seal svg {
	width: 100%;
	height: 100%;
	overflow: visible;
}

.fdcl-seal__ring {
	fill: none;
	stroke: var(--edge-hi);
	stroke-width: 1;
}

.fdcl-seal__ring--inner {
	stroke: var(--edge);
	stroke-dasharray: 2 4;
}

.fdcl-seal__legend {
	font-family: var(--mono);
	font-size: 7px;
	letter-spacing: 0.34em;
	text-transform: uppercase;
	fill: var(--on-night-muted);
}

.fdcl-seal__mark {
	font-family: var(--display);
	font-size: 30px;
	font-weight: 500;
	fill: var(--on-night);
}

.fdcl-seal__tick {
	stroke: var(--success-hi);
	stroke-width: 1.5;
	fill: none;
	stroke-linecap: square;
}

@media (max-width: 900px) {
	.fdcl-seal {
		position: static;
		transform: rotate(-8deg);
		margin: 2.5rem 0 0 auto;
		width: 128px;
	}
}


/* ==========================================================================
   L11. STAGE CONTROLS
   Fixed, quiet, and always reachable: a motion switch and a sound switch.
   Sound is off until asked for — an infrastructure site that makes noise at
   a stranger has misread the room — and the choice is remembered.
   ========================================================================== */

.fdcl-controls {
	position: fixed;
	left: max(1rem, env(safe-area-inset-left));
	bottom: max(1rem, env(safe-area-inset-bottom));
	z-index: 45;
	display: flex;
	gap: 0.5rem;
	align-items: center;
}

.fdcl-ctl {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.45rem;
	min-height: 44px;
	min-width: 44px;
	padding: 0 0.85rem;
	background: rgba(8, 23, 38, 0.78);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	border: 1px solid var(--edge);
	border-radius: var(--r-pill);
	color: var(--on-night-muted);
	font-family: var(--mono);
	font-size: var(--t-xs);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	cursor: pointer;
	transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.fdcl-ctl:hover {
	color: var(--on-night);
	border-color: var(--edge-hi);
}

.fdcl-ctl[aria-pressed="true"] {
	color: var(--info);
	border-color: rgba(0, 194, 255, 0.42);
}

/* Four bars that stand still until sound is on, then move — the only
   animated affordance on the page that reports a state rather than a
   process. */
.fdcl-ctl__bars {
	display: inline-flex;
	align-items: flex-end;
	gap: 2px;
	height: 11px;
}

.fdcl-ctl__bars i {
	width: 2px;
	height: 3px;
	background: currentColor;
	transition: height var(--dur) var(--ease);
}

.fdcl-ctl[aria-pressed="true"] .fdcl-ctl__bars i {
	animation: fdcl-bars 1100ms var(--ease-inout) infinite alternate;
}

.fdcl-ctl[aria-pressed="true"] .fdcl-ctl__bars i:nth-child(2) { animation-delay: 140ms; }
.fdcl-ctl[aria-pressed="true"] .fdcl-ctl__bars i:nth-child(3) { animation-delay: 280ms; }
.fdcl-ctl[aria-pressed="true"] .fdcl-ctl__bars i:nth-child(4) { animation-delay: 420ms; }

@keyframes fdcl-bars {
	from { height: 3px; }
	to { height: 11px; }
}

.fdcl-ctl__label {
	display: inline-block;
}

@media (max-width: 600px) {
	.fdcl-ctl__label {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	.fdcl-ctl {
		padding: 0;
	}
}


/* ==========================================================================
   L12. STORY STATES
   Two mechanisms, both additive:
     • The base sheet's [data-reveal] system is kept and retuned — longer,
       further, slower. It is IntersectionObserver-driven in main.js and
       needs no GSAP, so content is never dependent on the story script.
     • Headlines are split into lines and wiped up from behind a mask. The
       split is done by story.js, so with JS off the headline is just text.
   ========================================================================== */

[data-reveal] {
	transition-duration: var(--slow);
	transition-timing-function: var(--ease);
}

.js-enabled [data-reveal]:not(.is-revealed) {
	transform: translateY(30px);
}

.js-enabled [data-reveal="left"]:not(.is-revealed) {
	transform: translate3d(-34px, 0, 0);
}

.js-enabled [data-reveal="right"]:not(.is-revealed) {
	transform: translate3d(34px, 0, 0);
}

/* Split headline. The mask is on the line, the motion is on the inner span,
   so nothing is clipped once it has arrived. */
.fdcl-ln {
	display: block;
	overflow: hidden;
	padding-bottom: 0.06em;
	margin-bottom: -0.06em;
}

.fdcl-ln__i {
	display: block;
	will-change: transform;
}

/* Prepared but not yet played: held below the mask. Only ever applied by
   story.js after it has confirmed GSAP is present. */
.fdcl-split .fdcl-ln__i {
	transform: translateY(102%);
}

/* The current act is still a class on <html>, written by motion.js — the
   sound layer mixes its bed differently under each one, and the vignette
   closes a little on the acts that are meant to feel enclosed. */
.fdcl-act-vault .fdcl-vignette,
.fdcl-act-receipt .fdcl-vignette {
	background:
		radial-gradient(115% 88% at 50% 44%, transparent 30%, rgba(8, 23, 38, 0.52) 74%, rgba(8, 23, 38, 0.88) 100%);
}


/* ==========================================================================
   L15. PLATES — PHOTOGRAPHY
   A plate is a photograph and is shown as one. Up to 3.2.0 every picture here
   was rendered as a one-bit fax raster; it read as a field of dots rather than
   as photography, and the whole apparatus — the canvas, the error diffusion,
   the scan head, the standard/fine resolve — has been removed. Grading is
   baked into the shipped file so nothing is filtered at paint time.
   ========================================================================== */

.plate {
	position: relative;
	display: block;
	margin: 0;
	overflow: hidden;
	/* The reveal opens this. Set here rather than by script so the picture is
	   masked from first paint and never flashes in whole before animating. */
	clip-path: inset(0% 0% 0% 0%);
}

.fdcl-plates .plate:not(.is-shown) {
	clip-path: inset(0% 0% 100% 0%);
}

.plate__src {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* transform only, so the reveal and the drift both stay on the compositor
	   and neither ever triggers a repaint of the image itself. */
	will-change: transform;
}

.plate__cap {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.5rem clamp(1rem, 2vw, 1.75rem) clamp(0.85rem, 1.6vw, 1.25rem);
	background: linear-gradient(rgba(8, 23, 38, 0), rgba(8, 23, 38, 0.72));
	font-size: var(--t-xs);
	color: var(--on-night-muted);
	z-index: 3;
}

.plate__label {
	letter-spacing: 0.01em;
}

/* The status line is set the way the machine prints it. */
.plate__meta {
	letter-spacing: 0.08em;
	text-transform: uppercase;
	white-space: nowrap;
	opacity: 0.8;
}


/* -- Beds: a plate filling a section ------------------------------------- */

.bed {
	position: absolute;
	inset: 0;
	z-index: -2;
	overflow: hidden;
	pointer-events: none;
}

.bed .plate,
.bed .plate__src {
	width: 100%;
	height: 100%;
}

/*
 * The raster is pulled to the side the copy is not on, and faded out under the
 * words. The scrim in §L4 still does the contrast work; this only stops the
 * picture being cropped dead centre behind a headline.
 */
.bed--copy-left .plate {
	-webkit-mask-image: linear-gradient(100deg, transparent 4%, rgba(0, 0, 0, 0.25) 26%, #000 62%);
	mask-image: linear-gradient(100deg, transparent 4%, rgba(0, 0, 0, 0.25) 26%, #000 62%);
}

.bed--copy-right .plate {
	-webkit-mask-image: linear-gradient(260deg, transparent 4%, rgba(0, 0, 0, 0.25) 26%, #000 62%);
	mask-image: linear-gradient(260deg, transparent 4%, rgba(0, 0, 0, 0.25) 26%, #000 62%);
}

/*
 * A bed is a background. At full strength a photograph this contrasty wins
 * every argument with the type on top of it, and the point of the act is the
 * sentence, not the picture — so the plate is held back and the scrim below
 * finishes the job. The hero is the exception and keeps full strength: there
 * the picture *is* the thesis.
 */
.plate--bed {
	opacity: 0.72;
}

.hero .plate--bed {
	opacity: 1;
}

/*
 * The scrim in §L4 is left-weighted because acts set their heads left. A
 * section whose copy sits right needs it the other way round, or the picture
 * is darkened where nothing is written and left bright underneath the words.
 * Keyed off the bed's own side so the two can never disagree.
 */
.section--night:has(.bed--copy-right)::after,
.cta-band:has(.bed--copy-right)::after {
	background: linear-gradient(
		260deg,
		rgba(8, 23, 38, 0.95) 0%,
		rgba(8, 23, 38, 0.88) 30%,
		rgba(8, 23, 38, 0.42) 58%,
		rgba(8, 23, 38, 0) 78%
	);
}


/* -- Tiles: a shelf of sectors ------------------------------------------- */

.tiles {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(0.7rem, 1.3vw, 1.15rem);
}

.tile {
	position: relative;
	display: block;
	overflow: hidden;
	background: var(--void-2);
	border: 1px solid var(--edge);
	border-radius: var(--r);
	color: inherit;
	text-decoration: none;
	transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

a.tile:hover,
a.tile:focus-visible {
	border-color: var(--edge-hi);
	transform: translateY(-2px);
}

.tile__plate {
	display: block;
}

/* The sector name runs up the trailing edge, the way a name runs up the spine
   of a file. It is the tile's label, so it is set at reading size and never
   below it — a rotated caption is still a caption. */
.tile__name {
	position: absolute;
	right: clamp(0.6rem, 1.2vw, 0.95rem);
	bottom: clamp(0.85rem, 1.6vw, 1.25rem);
	writing-mode: vertical-rl;
	transform: rotate(180deg);
	font-family: var(--display);
	font-weight: 600;
	font-size: clamp(1.05rem, 1.5vw, 1.4rem);
	letter-spacing: -0.01em;
	color: var(--on-night);
	text-shadow: 0 1px 10px rgba(8, 23, 38, 0.85);
	z-index: 3;
}

.tile__line {
	position: absolute;
	left: clamp(0.85rem, 1.6vw, 1.25rem);
	bottom: clamp(0.85rem, 1.6vw, 1.25rem);
	max-width: 62%;
	font-size: var(--t-xs);
	line-height: 1.4;
	color: var(--on-night-muted);
	text-shadow: 0 1px 10px rgba(8, 23, 38, 0.9);
	z-index: 3;
}

/* The gradient a tile's caption sits on. Held low and made to fall away fast:
   any earlier and it turns the bottom half of the page grey, and the page is
   the reason the tile is there. */
.tile::after {
	content: "";
	position: absolute;
	inset: 56% 0 0;
	background: linear-gradient(
		rgba(8, 23, 38, 0) 0%,
		rgba(8, 23, 38, 0.30) 46%,
		rgba(8, 23, 38, 0.86) 84%
	);
	pointer-events: none;
	z-index: 2;
}


/* -- The hero, when it has a bed ----------------------------------------- */

.hero--bed .hero__inner {
	display: block;
}

.hero--bed .hero__copy {
	max-width: min(100%, 58ch);
}

.hero--bed h1 {
	max-width: 15ch;
}

.hero--bed .hero__sub {
	max-width: 42ch;
}


/* -- Where beds live ------------------------------------------------------
   A bed is absolutely positioned against its section and painted behind the
   scrim. Isolating each act keeps that ordering local: without it the beds and
   scrims of every section share #main's stacking context and their order
   depends on document position rather than on intent.

   .cta-band must be positioned for any of that to mean anything. The base
   sheet leaves it static, so the scrim §L4 gives it was resolving against
   #main and stretching over the whole document — harmless while it sat at
   z-index -1 behind everything, and an opaque veil over the entire page the
   moment the band became a stacking context. Both halves of that are fixed
   here rather than by dropping the isolation, because the band does need its
   own context: it carries a bed and the seal. */

.hero,
.section--night,
.cta-band {
	isolation: isolate;
}

.cta-band {
	position: relative;
}


/* -- Plates set on paper -------------------------------------------------- */

.plate--framed {
	border: 1px solid var(--line-2);
	border-radius: var(--r);
	box-shadow: var(--sh-lg);
	margin-bottom: clamp(1rem, 2vw, 1.5rem);
}

/* On paper the caption is ink, not a wash over a photograph. */
.plate--receive .plate__cap {
	background: linear-gradient(rgba(250, 247, 240, 0), rgba(250, 247, 240, 0.92) 55%);
	color: var(--muted);
}


/* -- Terse spec lists ------------------------------------------------------
   The same component as the base sheet with the second line removed: where a
   working component alongside already demonstrates the point, the item only
   has to name it. Tighter, because a list of single lines set at the spacing
   meant for two reads as four unrelated statements. */

.speclist--terse li {
	align-items: center;
	padding-block: 0.5rem;
}

.speclist--terse li strong {
	font-weight: 500;
	color: var(--on-night);
}

.section:not(.section--night) .speclist--terse li strong {
	color: var(--ink);
}


/* -- The application, given the room to be looked at ---------------------- */

.appstage {
	max-width: 1020px;
	margin: clamp(2.5rem, 5vw, 4rem) auto clamp(3rem, 6vw, 5rem);
	perspective: 1800px;
}

.appstage .appwin {
	transform: rotateX(1.6deg);
	transform-origin: 50% 100%;
	box-shadow: 0 60px 120px -40px rgba(0, 0, 0, 0.85), 0 0 0 1px var(--edge);
}


/* ==========================================================================
   L16. AMBIENT MOTION
   All of it, and there is deliberately not much. The hero drifts very slowly
   under its own mask so a still frame reads as held film rather than as a
   screenshot; footage, when any is set, plays over it and takes the job over.
   Everything else on the page moves only when it is scrolled to.
   ========================================================================== */

/*
 * Footage.
 *
 * Sits over the still and fades up only once it is genuinely rendering, so a
 * file that stalls or a browser that refuses autoplay leaves the photograph
 * in place rather than a black rectangle.
 */
.plate__vid {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0;
	transition: opacity 700ms var(--ease);
	pointer-events: none;
	z-index: 1;
}

.plate.is-live .plate__vid {
	opacity: 1;
}


/* -- The product film ----------------------------------------------------
   Scrubbed, not played: the section pins and the scrollbar becomes the
   transport. It is the one pinned region on the page, which is the ceiling the
   ui-ux-pro-max scrollytelling preset sets.

   The frame is a device rather than a bleed. This film is bright and full of
   interface, and run edge to edge on the void it would read as a hole cut in
   the page; sat inside a bezel it reads as a screen, which is what it is. */

.film {
	position: relative;
	width: min(100%, 1120px);
	margin: clamp(2.5rem, 5vw, 4rem) auto clamp(3rem, 6vw, 5rem);
}

.film__frame {
	position: relative;
	overflow: hidden;
	border-radius: var(--r-lg);
	border: 1px solid var(--edge-hi);
	background: var(--void-2);
	box-shadow: 0 60px 120px -40px rgba(0, 0, 0, 0.85);
	aspect-ratio: 16 / 9;
}

.film__vid {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* No controls, no interaction: the scrollbar is the only transport, and a
	   pointer landing on it should not be able to start anything. */
	pointer-events: none;
}

/*
 * While the film is pinned the section is doing one job, so the act's scrim is
 * pulled back — it exists to protect type from a picture, and there is no type
 * over this one.
 */
.film.is-ready {
	--film-lit: 1;
}

/* Nothing loaded, or nothing allowed to load: the poster is the picture. It is
   already painted by the video element, so there is nothing to swap in. */
.film.is-static .film__frame {
	box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.7);
}


/* ==========================================================================
   L17. FLOURISH — HOVER AND STATE
   Everything the pointer gets an answer from. CSS wherever CSS can do it, so
   the only work JavaScript keeps is the two things that need the cursor's
   position (§ flourish.js). All of it is transform and opacity, which stays on
   the compositor and never triggers layout.
   ========================================================================== */

/* -- Reading progress ----------------------------------------------------- */

.fdcl-progress {
	position: fixed;
	inset: 0 0 auto;
	height: 2px;
	z-index: 60;
	background: linear-gradient(90deg, var(--accent), var(--info));
	transform: scaleX(0);
	transform-origin: 0 50%;
	pointer-events: none;
}


/* -- Tiles ----------------------------------------------------------------
   The picture pushes in, the scrim deepens so the label keeps its contrast
   against a now-brighter page, and the sector name steps forward. One
   gesture, three parts, all on the same curve. */

.tile {
	transform-style: preserve-3d;
	will-change: transform;
}

.tile .plate__src {
	transition: transform var(--slow) var(--ease);
}

a.tile:hover .plate__src,
a.tile:focus-visible .plate__src,
.tile:hover .plate__src {
	transform: scale(1.055);
}

.tile::after {
	transition: opacity var(--dur) var(--ease);
}

.tile:hover::after {
	opacity: 1.15;
}

.tile__name,
.tile__line {
	transition: transform var(--dur) var(--ease), color var(--dur) var(--ease);
}

.tile:hover .tile__name {
	transform: rotate(180deg) translateY(6px);
	color: var(--display-on-night);
}

.tile:hover .tile__line {
	transform: translateY(-3px);
	color: var(--on-night);
}

/* Below the breakpoint the name is set horizontally (§L14), so the rotation
   in the rule above would flip it upside down. */
@media (max-width: 600px) {
	.tile:hover .tile__name {
		transform: translateY(-3px);
	}
}


/* -- Buttons --------------------------------------------------------------
   A sweep crosses the fill and the arrow travels. The sweep is a pseudo
   element rather than a background-position animation, because animating a
   gradient's position repaints the whole button every frame. */

.btn {
	position: relative;
	overflow: hidden;
	isolation: isolate;
}

.btn--primary::before,
.btn--dark::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, 0.22) 50%, transparent 80%);
	transform: translateX(-100%);
	transition: transform var(--slow) var(--ease);
}

.btn--primary:hover::before,
.btn--primary:focus-visible::before,
.btn--dark:hover::before,
.btn--dark:focus-visible::before {
	transform: translateX(100%);
}

.btn svg {
	transition: transform var(--dur) var(--ease);
}

.btn:hover svg,
.btn:focus-visible svg {
	transform: translateX(3px);
}


/* -- Cards ----------------------------------------------------------------
   A card lifts and its edge takes the action colour. The icon turns with it,
   so the whole cell reads as one object rather than a box with a badge. */

.card {
	transition:
		transform var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

.card:hover {
	transform: translateY(-3px);
	border-color: var(--accent);
}

.card__icon {
	transition: transform var(--dur) var(--ease), color var(--dur) var(--ease);
}

.card:hover .card__icon {
	transform: scale(1.08);
	color: var(--accent);
}


/* -- The section rule draws --------------------------------------------- */

.section-head .eyebrow::before {
	transition: width var(--slow) var(--ease);
}


/* ==========================================================================
   L13. REDUCED MOTION, NO-JS, PRINT
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	/* The interaction layer stands down whole: no progress bar, no lift, no
	   sweep, no tilt. flourish.js never initialises either, so nothing is
	   left half-applied. */
	.fdcl-progress {
		display: none;
	}

	.tile,
	.tile .plate__src,
	.card,
	.card__icon,
	.btn svg {
		transition: none !important;
		transform: none !important;
	}

	.btn--primary::before,
	.btn--dark::before {
		display: none;
	}

	/* Nothing drifts, nothing plays, and no picture is masked on arrival —
	   plate.js and motion.js both check this before starting anything. The
	   photographs are simply there. */
	.plate__vid {
		display: none;
	}

	.fdcl-plates .plate,
	.fdcl-plates .plate:not(.is-shown) {
		clip-path: inset(0% 0% 0% 0%);
	}

	.plate__src {
		transform: none !important;
	}

		display: none;
	}

	.fdcl-ctl[aria-pressed="true"] .fdcl-ctl__bars i {
		animation: none;
		height: 7px;
	}

	.fdcl-seal {
		opacity: 1;
	}

	.fdcl-split .fdcl-ln__i {
		transform: none;
	}

	.hero__scan {
		display: none;
	}
}

/* The mask that hides a picture before its reveal is only ever applied under
   .fdcl-plates, which plate.js adds. With the script blocked the photographs
   are simply visible — nothing here depends on JavaScript to appear. */

@media print {
	.fdcl-vignette,
	.fdcl-grain,
	.fdcl-controls,
	.fdcl-seal,
	.plate__vid {
		display: none !important;
	}

	.fdcl-plates .plate,
	.fdcl-plates .plate:not(.is-shown) {
		clip-path: none;
	}

	.plate__src {
		opacity: 1 !important;
		transform: none !important;
	}

	.bed {
		display: none !important;
	}

	body,
	.section,
	.section--night,
	.hero {
		background: #fff !important;
		color: #000 !important;
	}
}


/* ==========================================================================
   L14. RESPONSIVE
   ========================================================================== */

@media (max-width: 1023px) {
	.nav.is-open {
		background: rgba(8, 23, 38, 0.98);
		-webkit-backdrop-filter: blur(16px);
		backdrop-filter: blur(16px);
		border-color: var(--edge);
	}
}

@media (max-width: 900px) {
	/* One column, so the scrim turns with it: the type runs full width and the
	   stage is left the bottom of the frame rather than the right of it. */
	.hero::before,
	.section--night::after,
	.cta-band::after {
		background-image: linear-gradient(
			180deg,
			rgba(8, 23, 38, 0.95) 0%,
			rgba(8, 23, 38, 0.86) 55%,
			rgba(8, 23, 38, 0.30) 88%,
			rgba(8, 23, 38, 0) 100%
		);
	}

	.hero {
		min-height: 0;
		display: block;
	}

	.section--night {
		padding-block: clamp(4rem, 9vh, 6rem);
	}

	/* The act numeral is chrome, and chrome is the first thing to go when
	   the measure gets tight. */
	.section-head .eyebrow::after {
		display: none;
	}

	/* One column means the copy sits on top of the picture rather than beside
	   it, so the bed is masked upward instead of sideways. */
	.bed--copy-left .plate,
	.bed--copy-right .plate {
		-webkit-mask-image: linear-gradient(180deg, transparent 2%, rgba(0, 0, 0, 0.3) 34%, #000 74%);
		mask-image: linear-gradient(180deg, transparent 2%, rgba(0, 0, 0, 0.3) 34%, #000 74%);
	}

	.tiles {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 600px) {
	h1 {
		font-size: clamp(2.25rem, 9vw, 3rem);
		line-height: 1.04;
	}

	h2 {
		font-size: clamp(1.75rem, 7vw, 2.25rem);
	}

	/* A rotated label needs height to be legible; below this the tile is
	   wider than it is tall and the name goes back under the picture. */
	.tiles {
		grid-template-columns: 1fr;
	}

	.tile__name {
		writing-mode: horizontal-tb;
		transform: none;
		right: auto;
		left: clamp(0.85rem, 1.6vw, 1.25rem);
		bottom: 2.4rem;
	}

	.tile__line {
		max-width: 90%;
	}
}
