/* ovos codesafe — landing page styles.
	Extracted from index.html; mirrors the tool's Rajdhani display face. */

/* Rajdhani (OFL) — codesafe's display/UI face, mirrored here so the
	landing page's chrome (logo, nav, buttons, headings, labels) reads as
	the same brand as the tool. Body prose stays in the system sans and
	code tokens in the mono stack. The four weights match the tool's set. */
@font-face
{
	font-family: 'Rajdhani';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url('fonts/rajdhani/rajdhani-400.woff2') format('woff2');
}

@font-face
{
	font-family: 'Rajdhani';
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url('fonts/rajdhani/rajdhani-500.woff2') format('woff2');
}

@font-face
{
	font-family: 'Rajdhani';
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url('fonts/rajdhani/rajdhani-600.woff2') format('woff2');
}

@font-face
{
	font-family: 'Rajdhani';
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url('fonts/rajdhani/rajdhani-700.woff2') format('woff2');
}

:root
{
	--bg: #0a0a12;
	--panel: #0d0d17;
	--panel-2: #11111c;
	--border: #26263a;
	--cyan: #00f0ff;
	--yellow: #fcee0a;
	--red: #ff003c;
	--muted: #8b8ba7;
	--text: #e8e8f0;
	--mono: Consolas, "Courier New", monospace;
	--sans: "Segoe UI", Helvetica, Arial, sans-serif;
	/* the tool's display face — chrome and headings, matching the app */
	--display: 'Rajdhani', 'Segoe UI', sans-serif;
}

*
{
	box-sizing: border-box;
}

/* themed scrollbar — standard scrollbar-color (no vendor prefixes) */
html
{
	scroll-behavior: smooth;
	scrollbar-color: color-mix(in srgb, var(--cyan) 60%, var(--border)) var(--panel);
	scrollbar-width: thin;
}

body
{
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--sans);
	font-size: 16px;
	line-height: 1.6;
	/* faint scanline texture */
	background-image: repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.014) 0 1px, transparent 1px 4px);
	overflow-x: hidden;
}

a
{
	color: var(--cyan);
	text-decoration: none;
}

a:hover
{
	text-decoration: underline;
}

.wrap
{
	max-width: 1120px;
	margin: 0 auto;
	/* horizontal gutter ONLY — a `padding: 0 24px` shorthand also sets vertical
		padding to 0, and .wrap (a class) then outranks element rules like
		`section { padding: 70px 0 }`, collapsing their vertical padding and
		sticking section border-tops to the surrounding content. padding-inline
		leaves each component's own vertical padding intact. */
	padding-inline: 24px;
}

.mono
{
	font-family: var(--mono);
}

.eyebrow
{
	font-family: var(--display);
	font-size: 12px;
	letter-spacing: 5px;
	color: var(--muted);
	text-transform: uppercase;
	margin-top: 6px;
}

.eyebrow .on
{
	color: var(--cyan);
}

/* top bar */
header
{
	border-bottom: 1px solid var(--border);
	background: rgba(10, 10, 18, 0.85);
	position: sticky;
	top: 0;
	z-index: 10;
	backdrop-filter: blur(6px);
}

.bar
{
	display: flex;
	align-items: center;
	justify-content: space-between;
	height: 60px;
}

.logo
{
	font-family: var(--display);
	font-weight: 700;
	/* 28px puts the capitals at the ovos mark's own height (both ~18px of ink,
	   measured) — the two read as one name, "ovos codesafe" */
	font-size: 28px;
	letter-spacing: 5px;
	display: inline-block;
	text-decoration: none;
}

.logo:hover
{
	text-decoration: none;
}

/* the product's name is "ovos codesafe": the ovos mark (the app footer's,
   same path data) and the CODESAFE lockup read as one name, no divider —
   one group, so the bar keeps its three parts (brand, nav, menu) */
.brand
{
	display: flex;
	align-items: center;
	gap: 10px;
}

.brand-ovos
{
	display: inline-flex;
	color: var(--text);
	transition: filter .25s ease;
}

/* full strength at rest; on hover it takes the lockup's glow in its own
   white — a drop-shadow, so the glow follows the letterforms, not a box */
.brand-ovos:hover,
.brand-ovos:focus-visible
{
	filter: drop-shadow(0 0 8px rgba(232, 232, 240, 0.75)) drop-shadow(0 0 22px rgba(232, 232, 240, 0.3));
}

.brand-ovos-art
{
	display: block;
	fill: currentColor;
}

/* the app's glow, value for value (variables.less --glow-mark/-act and
   their -strong forms): tight at rest, a brighter core and a bloom on hover */
.logo .con
{
	color: var(--yellow);
	text-shadow: 0 0 6px rgba(252, 238, 10, 0.55);
	transition: text-shadow .2s ease;
}

.logo .sole
{
	color: var(--cyan);
	text-shadow: 0 0 6px rgba(0, 240, 255, 0.55);
	transition: text-shadow .2s ease;
}

.logo:hover .con,
.logo:focus-visible .con
{
	text-shadow: 0 0 8px rgba(252, 238, 10, 0.75), 0 0 22px rgba(252, 238, 10, 0.3);
}

.logo:hover .sole,
.logo:focus-visible .sole
{
	text-shadow: 0 0 8px rgba(0, 240, 255, 0.75), 0 0 22px rgba(0, 240, 255, 0.3);
}

/* the brand mark ignites like a neon tube warming up — once per page load.
	Opacity-only, so the resting text-shadow glow is untouched (mirrors the
	app's .topbar-logo neon-ignite) */
@media (prefers-reduced-motion: no-preference)
{
	.logo
	{
		animation: neon-ignite 1.2s steps(1, jump-end);
	}
	
	@keyframes neon-ignite
	{
		0%
		{
			opacity: 0.35;
		}
		18%
		{
			opacity: 1;
		}
		26%
		{
			opacity: 0.5;
		}
		34%
		{
			opacity: 1;
		}
		41%
		{
			opacity: 0.75;
		}
		48%, 100%
		{
			opacity: 1;
		}
	}
}

.nav-links a
{
	/* Rajdhani's regular weight in the muted grey was a hairline on the dark
	   bar; 600 in a lighter tone reads, and the cyan hover still stands out */
	color: color-mix(in srgb, var(--text) 72%, var(--muted));
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 2px;
	margin-left: 18px;
	text-transform: uppercase;
	font-family: var(--display);
	white-space: nowrap;
}

.nav-links a:hover
{
	color: var(--cyan);
	text-decoration: none;
}

/* the landing page's bar carries the nav alone — the "ovos codesafe" lockup
   leads the hero instead — so the links sit centred and a size larger; the
   docs pages keep the lockup in their bar, their way home */
.bar-nav
{
	justify-content: center;
}

.bar-nav .nav-links a
{
	font-size: 15px;
	letter-spacing: 2.5px;
	margin-left: 26px;
}

.bar-nav .nav-links a:first-child
{
	margin-left: 0;
}

.bar-nav .nav-menu
{
	margin-left: auto;
}

/* the lockup at the head of the hero: the ovos mark at 95x22 and CODESAFE at
   33px on one baseline, the mark's top on the capitals' line and its round
   letters a pixel below the baseline */
.hero-brand
{
	display: inline-flex;
	/* baseline, not centre: a flex row puts the wordmark's baseline on the
	   svg's bottom edge, which is where the ovos letters stand — flush in
	   every browser, whatever the font's metrics */
	align-items: baseline;
	gap: 16px;
	margin-bottom: 6px;
}

.hero-brand .brand-ovos
{
	/* one pixel past the baseline, by eye (MG 2026-09-25): the mark's round
	   letters want the overshoot the capitals do not */
	transform: translateY(1px);
}

.hero-brand .logo
{
	font-size: 33px;
	letter-spacing: 6px;
}

/* 320-400px: the 33px lockup ran past the screen's edge — both halves step
	down together (the mark 95 -> 76px, the capitals 33 -> 26px), so they keep
	their one baseline and matched heights */
@media (max-width: 400px)
{
	.hero-brand
	{
		gap: 12px;
	}
	
	.hero-brand .brand-ovos-art
	{
		width: 76px;
		height: auto;
	}
	
	.hero-brand .logo
	{
		font-size: 26px;
		letter-spacing: 4.5px;
	}
}

/*	the phone navigation. Eight links do not fit a 360px bar on one row, and
	two wrapped rows would make a sticky header eat a third of the screen — so
	below 720px the row becomes a disclosure. The label is a word, not a bare
	glyph, and <details> opens it with no JavaScript at all; nav.js only adds
	the three closing manners (a link chosen, a tap outside, Escape). */
.nav-menu
{
	display: none;
	position: relative;
}

.nav-menu > summary
{
	list-style: none;
	cursor: pointer;
	font-family: var(--display);
	/* the .btn cure: bold, a size up — the regular cut was a hairline */
	font-size: 15px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--cyan);
	padding: 8px 14px;
	border: 1px solid var(--border);
	user-select: none;
}

.nav-menu > summary::-webkit-details-marker
{
	display: none;
}

/*	the caret is drawn, not typed: Rajdhani has no arrow glyph and a fallback
	face renders a different size at every weight */
.nav-menu > summary::after
{
	content: '';
	display: inline-block;
	margin-left: 9px;
	vertical-align: 2px;
	border: 4px solid transparent;
	border-top-color: currentColor;
	border-bottom: 0;
	transition: transform .15s;
}

.nav-menu[open] > summary
{
	border-color: var(--cyan);
}

.nav-menu[open] > summary::after
{
	transform: rotate(180deg);
}

/*	the panel hangs off the summary, so its top edge lands on the header's own
	bottom border whatever height the bar is */
.nav-menu-panel
{
	position: absolute;
	top: calc(100% + 12px);
	right: 0;
	min-width: 210px;
	display: flex;
	flex-direction: column;
	background: var(--panel-2);
	border: 1px solid var(--border);
	box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6);
	/* a phone held sideways is ~360px tall: eight rows must scroll, not run
		off the bottom of the screen */
	max-height: calc(100dvh - 84px);
	overflow-y: auto;
}

.nav-menu-panel a
{
	font-family: var(--display);
	font-size: 14px;
	/* the desktop nav's weight (.nav-links a) */
	font-weight: 600;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--text);
	padding: 12px 18px;
	border-bottom: 1px solid var(--border);
}

.nav-menu-panel a:last-child
{
	border-bottom: 0;
}

.nav-menu-panel a:hover,
.nav-menu-panel a:focus-visible
{
	color: var(--cyan);
	background: var(--panel);
	text-decoration: none;
}

/* ten links and the "ovos codesafe" lockup fit one row from 1180px (measured
   with the 28px wordmark); below that the row becomes the menu rather than
   wrapping under the brand */
/* the bar's own CODESAFE: hidden while the nav row fills the bar, shown
	once the row becomes the menu — the phone bar held MENU alone, and the
	first screen scrolls the hero's lockup away (MG 2026-09-25) */
.bar-logo
{
	display: none;
	font-size: 22px;
	letter-spacing: 4px;
}

@media (max-width: 1179px)
{
	.nav-links
	{
		display: none;
	}
	
	.nav-menu
	{
		display: block;
	}
	
	.bar-nav
	{
		justify-content: space-between;
	}
	
	.bar-logo
	{
		display: inline-block;
	}
}

/* buttons */
.btn
{
	position: relative;
	isolation: isolate;
	display: inline-block;
	font-family: var(--display);
	/* Rajdhani's regular weight at 13px was a hairline in the cyan/yellow on
		the dark ground (MG 2026-09-25, "hardly readable") — the nav's cure:
		the bold cut, a size up */
	font-size: 15px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	padding: 12px 22px;
	color: var(--cyan);
	background: transparent;
	/* Notched frame drawn as the 1px gap between two clipped fills. A real
		border under clip-path loses its right/bottom edge at fractional
		widths (DPR=1 rounding); this renders reliably at any size. The inner
		fill must match the surface behind the button (see --btn-bg). */
	--btn-bg: var(--bg);
	--btn-line: var(--cyan);
	transition: color .15s;
}

.btn::before,
.btn::after
{
	content: '';
	position: absolute;
	z-index: -1;
	clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}

.btn::before
{
	inset: 0;
	background: var(--btn-line);
}

.btn::after
{
	inset: 1px;
	background: var(--btn-bg);
	/* inherits ::before's 10px chamfer, inset 1px → a crisp 1px frame on retina. */
	transition: background .15s;
}

/* Non-retina (DPR 1) anti-aliases the 45° chamfer across pixels, so the diagonal
	frame reads far fainter than the crisp axis-aligned edges and the cut corners
	look broken. Widen the inner cut there (12px vs the outer 10px) so the diagonal
	band is ~2.8px and rasterises as solid as the 1px straight edges. Retina keeps
	the thin inherited chamfer (it renders the 1px frame sharply). */
@media (max-resolution: 1.5dppx)
{
	.btn::after
	{
		clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
	}
}

.btn:hover
{
	color: #04141a;
	text-decoration: none;
}

.btn:hover::after
{
	background: var(--btn-line);
}

.btn.yellow
{
	color: var(--yellow);
	--btn-line: var(--yellow);
}

.btn.yellow:hover
{
	color: #141200;
}

.dual .panel .btn
{
	--btn-bg: var(--panel-2);
}

/* hero */
.hero
{
	/* padding-block, never the shorthand: .hero sits on the same element as
		.wrap, and a shorthand would zero its 24px side gutter — which is how the
		headline, the lead and the stat all ended up flush against the edge of a
		360px screen (the same trap .doc fell into) */
	padding-block: 90px 70px;
	text-align: center;
	position: relative;
}

/* phones: 90px of air above the lockup is a desktop proportion. This rule
	sat ABOVE the base one for a while and lost to it on order, so phones kept
	the full 90px (101px of air to the mark's ink); it lives after it now */
@media (max-width: 720px)
{
	.hero
	{
		padding-block: 32px 48px;
	}
}

.hero h1
{
	font-family: var(--display);
	font-size: clamp(34px, 6vw, 62px);
	line-height: 1.05;
	margin: 22px 0 18px;
	font-weight: 700;
	letter-spacing: 0;
}

.hero h1 .g-cyan
{
	color: var(--cyan);
	text-shadow: 0 0 26px rgba(0, 240, 255, 0.4);
}

.hero h1 .g-yellow
{
	color: var(--yellow);
	text-shadow: 0 0 26px rgba(252, 238, 10, 0.4);
}

.hero p.lead
{
	font-size: clamp(16px, 2.4vw, 20px);
	color: var(--muted);
	max-width: 680px;
	margin: 0 auto 34px;
}

.hero-cta
{
	display: flex;
	gap: 16px;
	justify-content: center;
	flex-wrap: wrap;
}

.hero-shot
{
	margin: 60px auto 0;
	max-width: 100%;
	border: 1px solid var(--border);
	box-shadow: 0 0 60px rgba(0, 240, 255, 0.08), 0 20px 60px rgba(0, 0, 0, 0.6);
	display: block;
	cursor: zoom-in;
}

/* sections */
section
{
	padding: 48px 0;
	border-top: 1px solid var(--border);
}

h2
{
	font-family: var(--display);
	font-size: clamp(24px, 4vw, 34px);
	letter-spacing: 0;
	margin: 8px 0 12px;
}

.section-intro
{
	color: var(--muted);
	max-width: 640px;
	margin-bottom: 40px;
}

/* feature grid */
.features
{
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 18px;
}

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

.card
{
	position: relative;
	isolation: isolate;
	padding: 24px;
}

/* Notched frame drawn as the 1px gap between two clipped fills — a real border
	under clip-path loses its chamfered edges (same technique as .btn). ::before
	is the frame line, ::after the surface inset 1px with a 1px-smaller chamfer. */
.card::before,
.card::after
{
	content: '';
	position: absolute;
	z-index: -1;
	inset: 0;
	background: var(--border);
	clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
}

.card::after
{
	inset: 1px;
	background: var(--panel);
	clip-path: polygon(13px 0, 100% 0, 100% calc(100% - 13px), calc(100% - 13px) 100%, 0 100%, 0 13px);
}

/* same DPR-1 anti-aliasing fix as .btn — widen the inner cut on non-retina so the
	diagonal frame reads as solid as the straight edges (retina keeps the thin cut) */
@media (max-resolution: 1.5dppx)
{
	.card::after
	{
		clip-path: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
	}
}

.card .tag
{
	font-family: var(--display);
	font-size: 11px;
	letter-spacing: 3px;
	color: var(--yellow);
	text-transform: uppercase;
}

.card:nth-child(even) .tag
{
	color: var(--cyan);
}

.card h3
{
	font-family: var(--display);
	font-size: 18px;
	letter-spacing: 0.3px;
	margin: 10px 0 8px;
}

.card p
{
	color: var(--muted);
	font-size: 15px;
	margin: 0;
}

/* flow */
.flow
{
	display: flex;
	align-items: stretch;
	gap: 0;
	flex-wrap: wrap;
	font-family: var(--display);
}

.flow .step
{
	flex: 1 1 160px;
	background: var(--panel);
	border: 1px solid var(--border);
	padding: 20px 18px;
	position: relative;
}

.flow .step .n
{
	color: var(--yellow);
	font-size: 12px;
	letter-spacing: 2px;
}

.flow .step h4
{
	color: var(--cyan);
	margin: 8px 0 6px;
	font-size: 15px;
	letter-spacing: 1px;
}

.flow .step p
{
	color: var(--muted);
	font-size: 14px;
	margin: 0;
	font-family: var(--sans);
}

.flow .arrow
{
	display: flex;
	align-items: center;
	color: var(--border);
	font-size: 22px;
	padding: 0 6px;
}

@media (max-width: 860px)
{
	.flow .arrow
	{
		display: none;
	}
	
	.flow .step
	{
		flex-basis: 100%;
	}
}

/* gallery */
.gallery
{
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 22px;
}

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

.shot
{
	border: 1px solid var(--border);
	background: var(--panel);
}

.shot img
{
	width: 100%;
	display: block;
	border-bottom: 1px solid var(--border);
	cursor: zoom-in;
}

.shot .cap
{
	padding: 14px 18px;
}

.shot .cap b
{
	color: var(--text);
	font-family: var(--display);
	letter-spacing: 1px;
	font-size: 14px;
}

.shot .cap > span
{
	color: var(--muted);
	font-size: 14px;
	display: block;
}

/* The live demo (2026-09-14) — the one thing in this section that is not a
	picture of the product. It stands above the gallery because it finishes the
	line above it: the screens come from a running instance, and the running
	instance is open. A left rule in the accent, the corner cut the panels
	wear, and the facts in two columns so the block stays one screenful. */
.demo-cta
{
	background: var(--panel-2);
	border: 1px solid var(--border);
	border-left: 2px solid var(--cyan);
	padding: 28px 32px 30px;
	margin: 0 0 38px;
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%);
}

.demo-live
{
	display: flex;
	align-items: center;
	gap: 9px;
	font-family: var(--display);
	font-size: 13px;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--cyan);
}

.demo-dot
{
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--cyan);
	box-shadow: 0 0 10px var(--cyan);
}

/* it says LIVE, so it breathes — but only where motion is welcome */
@media (prefers-reduced-motion: no-preference)
{
	.demo-dot
	{
		animation: demo-pulse 2.4s ease-in-out infinite;
	}
	
	@keyframes demo-pulse
	{
		50%
		{
			opacity: 0.3;
		}
	}
}

.demo-cta h3
{
	font-family: var(--display);
	font-size: clamp(22px, 3vw, 28px);
	margin: 10px 0 8px;
}

.demo-cta > p
{
	color: var(--muted);
	max-width: 640px;
	margin: 0 0 20px;
}

.demo-url
{
	font-family: var(--mono);
	letter-spacing: 0.5px;
}

.demo-facts
{
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 14px 32px;
	list-style: none;
	padding: 0;
	margin: 26px 0 0;
	color: var(--muted);
	font-size: 15px;
}

.demo-facts b
{
	color: var(--text);
	font-family: var(--display);
	letter-spacing: 0.4px;
}

@media (max-width: 720px)
{
	.demo-cta
	{
		padding: 24px 22px 26px;
	}
	
	.demo-facts
	{
		grid-template-columns: 1fr;
	}
}

/* narrow phones: a slimmer panel gutter, and the address on ONE line — at
	15px it broke as CODESAFE- / DEMO.OVOS.AT */
@media (max-width: 400px)
{
	.demo-cta
	{
		padding: 22px 16px 24px;
	}
	
	.demo-url
	{
		font-size: 13px;
		padding-inline: 16px;
		white-space: nowrap;
	}
}

/* click-to-zoom lightbox — the shots are captured at full desktop res, too
	detailed to read in the grid; click one to view it fit-to-screen */
.lightbox
{
	position: fixed;
	inset: 0;
	z-index: 100;
	display: none;
	align-items: center;
	justify-content: center;
	padding: 24px;
	background: rgba(5, 5, 10, 0.92);
	cursor: zoom-out;
}

.lightbox.open
{
	display: flex;
}

.lightbox img
{
	max-width: 100%;
	max-height: 100%;
	width: auto;
	border: 1px solid var(--cyan);
	box-shadow: 0 0 44px rgba(0, 240, 255, 0.25);
}

.mobile-head
{
	margin: 48px 0 6px;
	font-family: var(--display);
	letter-spacing: 1px;
}

/* the phone shots are a centred row, not a left-aligned grid: three in a
   840px lane inside a wider column read as a fourth one missing, and on two
   columns the third sits under the middle instead of alone on the left */
.mobile-gallery
{
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	max-width: 840px;
	margin-inline: auto;
}

.mobile-gallery .shot
{
	flex: 0 1 calc((100% - 44px) / 3);
}

@media (max-width: 860px)
{
	.mobile-gallery .shot
	{
		flex-basis: calc((100% - 22px) / 2);
	}
}

@media (max-width: 560px)
{
	.mobile-gallery
	{
		max-width: 340px;
	}
	
	.mobile-gallery .shot
	{
		flex-basis: 100%;
	}
}

/* use cases */
.uses
{
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 18px;
}

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

.use
{
	border-left: 2px solid var(--cyan);
	padding: 4px 0 4px 20px;
}

/* the accent cycles cyan / yellow / red down the grid */
.use:nth-child(3n + 2)
{
	border-color: var(--yellow);
}

.use:nth-child(3n)
{
	border-color: var(--red);
}

.use h3
{
	font-family: var(--display);
	letter-spacing: 0.3px;
	margin: 0 0 6px;
	font-size: 17px;
}

.use p
{
	color: var(--muted);
	margin: 0;
	font-size: 15px;
}

/* dual CTA */
.dual
{
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 22px;
}

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

.dual .panel
{
	background: var(--panel-2);
	border: 1px solid var(--border);
	padding: 34px;
	text-align: center;
	clip-path: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
}

.dual .panel h3
{
	font-family: var(--display);
	letter-spacing: 0.3px;
	font-size: 22px;
	margin: 0 0 10px;
}

.dual .panel p
{
	color: var(--muted);
	min-height: 66px;
	margin: 0 0 22px;
}

footer
{
	border-top: 1px solid var(--border);
	/* extra room below so the page doesn't end flush against the footer line;
		.wrap supplies the 24px side gutter via padding-inline */
	padding: 30px 0 64px;
	color: var(--muted);
	font-size: 13px;
	font-family: var(--display);
	display: flex;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 10px;
}

/* ---------------------------------------------------------------------------
   THE FULL CIRCLE — the six arcs, and the pulse that closes them.

   The ring is an SVG so it scales to any width without reflowing; the prose
   lives in the HTML list beside it, so on a phone the words stay readable
   instead of shrinking to 6px with the drawing. Motion is CSS (offset-path),
   not SMIL, for exactly one reason: prefers-reduced-motion can switch it off.
--------------------------------------------------------------------------- */
.fc {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: 40px;
	align-items: center;
	margin-top: 32px;
}

.fc-ring {
	width: 100%;
	height: auto;
	overflow: visible;
}

.fc-node circle {
	fill: var(--panel-2);
	stroke: var(--cyan);
	stroke-width: 2;
}

.fc-node text {
	fill: var(--text);
	font-family: var(--display);
	font-size: 21px;
	font-weight: 700;
	letter-spacing: .08em;
	text-anchor: middle;
}

.fc-node .fc-i {
	fill: var(--muted);
	font-family: var(--mono);
	font-size: 13px;
	font-weight: 400;
	letter-spacing: .12em;
}

/* the two arcs that close the loop read yellow, like the ring behind them */
.fc-node-close circle {
	stroke: var(--yellow);
}

.fc-hub {
	fill: var(--muted);
	font-family: var(--display);
	font-size: 20px;
	letter-spacing: .22em;
	text-anchor: middle;
}

.fc-hub-2 {
	font-family: var(--sans);
	font-size: 14px;
	letter-spacing: .04em;
}

/* the arcs draw themselves, then the dash keeps drifting */
.fc-arc {
	fill: none;
	stroke: var(--cyan);
	stroke-width: 3;
	stroke-linecap: round;
	stroke-dasharray: 9 11;
	animation: fc-drift 1.6s linear infinite;
}

.fc-arc-close {
	stroke: var(--yellow);
	animation-duration: 2.4s;
}

@keyframes fc-drift {
	to { stroke-dashoffset: -20; }
}

.fc-pulse {
	offset-path: path("M 360 100 A 200 200 0 1 1 360 500 A 200 200 0 1 1 360 100");
	animation: fc-orbit 9s linear infinite;
	filter: drop-shadow(0 0 6px var(--cyan));
}

.fc-pulse-2 { animation-delay: -.45s; }
.fc-pulse-3 { animation-delay: -.9s; }

@keyframes fc-orbit {
	from { offset-distance: 0%; }
	to   { offset-distance: 100%; }
}

.fc-steps {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 18px;
}

.fc-steps h4 {
	margin: 0 0 4px;
	font-family: var(--display);
	font-size: 17px;
	letter-spacing: .1em;
	color: var(--cyan);
}

.fc-steps h4 span {
	font-family: var(--mono);
	font-size: 12px;
	color: var(--muted);
	margin-right: 8px;
	letter-spacing: .1em;
}

.fc-steps .close h4 {
	color: var(--yellow);
}

.fc-steps p {
	margin: 0;
	color: var(--muted);
	font-size: 15px;
	line-height: 1.55;
}

.fc-steps strong {
	color: var(--text);
}

.fc-steps li {
	border-left: 2px solid var(--border);
	padding-left: 16px;
}

.fc-steps li.close {
	border-left-color: color-mix(in srgb, var(--yellow) 55%, var(--border));
}

.lead-2 {
	margin-top: 14px;
}

@media (max-width: 900px) {
	.fc {
		grid-template-columns: minmax(0, 1fr);
		gap: 28px;
	}
	
	/* the drawing is the summary on a phone, the list is the detail */
	.fc-ring {
		max-width: 460px;
		margin: 0 auto;
	}
}

@media (prefers-reduced-motion: reduce) {
	.fc-arc,
	.fc-pulse {
		animation: none;
	}
	
	/* without the orbit the pulses would stack at the start of the path */
	.fc-pulse-2,
	.fc-pulse-3 {
		display: none;
	}
}

/*	=====================================================================
	THE VISUAL LAYER — background field, reveals, stats, the attack
	panel, the capability tiles. Everything below is decoration that
	carries meaning; the reduced-motion block at the end stops all of it
	and leaves the page composed and still.
	===================================================================== */

/* the field: requests raining down, errors caught on the ingest line */
#field
{
	position: fixed;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: -3;
	pointer-events: none;
}

/* a slow grid under the field — codesafe's own chrome, drifting */
body::before
{
	content: '';
	position: fixed;
	inset: -60px;
	z-index: -4;
	pointer-events: none;
	opacity: .5;
	background-image:
		linear-gradient(rgba(0, 240, 255, 0.045) 1px, transparent 1px),
		linear-gradient(90deg, rgba(0, 240, 255, 0.045) 1px, transparent 1px);
	background-size: 56px 56px, 56px 56px;
	animation: grid-drift 26s linear infinite;
}

@keyframes grid-drift
{
	from { transform: translate3d(0, 0, 0); }
	to { transform: translate3d(0, 56px, 0); }
}

/* a vignette so the field never competes with the text over it */
.veil
{
	position: fixed;
	inset: 0;
	z-index: -2;
	pointer-events: none;
	background:
		radial-gradient(120% 70% at 50% 0%, rgba(0, 240, 255, 0.07), transparent 60%),
		radial-gradient(90% 60% at 50% 100%, rgba(252, 238, 10, 0.04), transparent 60%),
		linear-gradient(180deg, rgba(10, 10, 18, 0.35) 0%, rgba(10, 10, 18, 0.86) 45%, rgba(10, 10, 18, 0.95) 100%);
}

/* sections arrive as they are reached */
.reveal
{
	opacity: 0;
	transform: translateY(18px);
	transition: opacity .7s ease, transform .7s cubic-bezier(.2, .7, .3, 1);
}

.reveal.in
{
	opacity: 1;
	transform: none;
}

/* --- hero numbers ---------------------------------------------------- */

.hero p.lead-strong
{
	color: var(--text);
	margin-bottom: 30px;
}

/* the setup line, then what codesafe does as four steps SIDE BY SIDE — the
	hero is read in glances, and five forced lines made a tall narrow column
	(MG 2026-09-25, "no ideas to make it wider?"). The row shares its width with
	the figure box under it, so the two read as one block */
.hero p.lead-setup
{
	margin-bottom: 18px;
}

.hero-steps
{
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	max-width: 900px;
	margin: 0 auto 34px;
	padding: 0;
	list-style: none;
	text-align: left;
}

.hero-steps li
{
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 12px 18px 4px;
	border-top: 1px solid color-mix(in srgb, var(--cyan) 40%, var(--border));
}

.hero-steps li + li
{
	border-left: 1px solid var(--border);
}

.hero-steps b
{
	font-family: var(--display);
	font-weight: 700;
	font-size: 15px;
	letter-spacing: .18em;
	color: var(--cyan);
}

.hero-steps span
{
	font-size: 15px;
	line-height: 1.5;
	color: var(--muted);
}

/* two by two, then one column; the rules follow the grid */
@media (max-width: 860px)
{
	.hero-steps
	{
		grid-template-columns: repeat(2, minmax(0, 1fr));
		row-gap: 14px;
	}
	
	.hero-steps li:nth-child(3)
	{
		border-left: 0;
	}
}

@media (max-width: 480px)
{
	.hero-steps
	{
		grid-template-columns: minmax(0, 1fr);
	}
	
	/* one column: no neighbour to keep apart from, so the text takes the
		full width under its rule */
	.hero-steps li
	{
		padding-inline: 0;
	}
	
	.hero-steps li + li
	{
		border-left: 0;
	}
}

.stats
{
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 18px;
	max-width: 760px;
	margin: 0 auto 34px;
}

.stat
{
	border: 1px solid var(--border);
	border-top-color: color-mix(in srgb, var(--cyan) 40%, var(--border));
	background: linear-gradient(180deg, rgba(0, 240, 255, 0.05), rgba(13, 13, 23, 0.6));
	padding: 16px 12px 14px;
}

.stat-n
{
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(30px, 5vw, 46px);
	line-height: 1;
	color: var(--cyan);
	text-shadow: 0 0 24px rgba(0, 240, 255, 0.35);
}

.stat:nth-child(2) .stat-n
{
	color: var(--yellow);
	text-shadow: 0 0 24px rgba(252, 238, 10, 0.3);
}

.stat-l
{
	margin-top: 8px;
	font-size: 12px;
	line-height: 1.45;
	color: var(--muted);
}

/* one number carries more than three: wider box, bigger figure — and a ROW,
	the figure on the left and what it means beside it, so the box stands wide
	under the hero's text rather than a tall narrow column (MG 2026-09-25) */
.stats-one
{
	grid-template-columns: minmax(0, 900px);
	max-width: 900px;
	justify-content: center;
}

.stats-one .stat
{
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: 28px;
	align-items: center;
	padding: 22px 28px 20px;
	text-align: left;
}

.stats-one .stat-n
{
	grid-row: 1 / 3;
	font-size: clamp(44px, 7vw, 64px);
}

.stats-one .stat-l
{
	margin-top: 0;
	font-size: 14px;
}

/* phones: back to the column, centred, the figure over its words */
@media (max-width: 620px)
{
	.stats-one .stat
	{
		display: block;
		padding: 22px 20px 20px;
		text-align: center;
	}
	
	.stats-one .stat-l
	{
		margin-top: 8px;
	}
}

/* the consequence, under the number: the figure alone answers "so what?" badly */
.stat-note
{
	margin-top: 10px;
	padding-top: 10px;
	border-top: 1px solid var(--border);
	font-family: var(--display);
	font-weight: 600;
	font-size: 16px;
	color: var(--yellow);
	opacity: .92;
}

@media (max-width: 620px)
{
	.stats
	{
		grid-template-columns: minmax(0, 1fr);
	}
}

/* --- the attack panel ------------------------------------------------ */

.atk
{
	display: grid;
	grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
	gap: 28px;
	align-items: stretch;
	margin-top: 10px;
}

.atk-wall
{
	display: flex;
	flex-direction: column;
	border: 1px solid var(--border);
	background: rgba(13, 13, 23, 0.75);
	overflow: hidden;
}

.atk-wall-head
{
	font-family: var(--mono);
	font-size: 11px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--muted);
	padding: 10px 14px;
	border-bottom: 1px solid var(--border);
	background: rgba(255, 0, 60, 0.06);
}

/* the log fills whatever the grid row gives the wall (the text column beside
	it stands taller on desktop), and never less than 290px when stacked */
.atk-scroll
{
	position: relative;
	flex: 1 0 290px;
	overflow: hidden;
	mask-image: linear-gradient(180deg, transparent, #000 12%, #000 84%, transparent);
}

/* the track holds the list three times, so a third is one seamless turn —
	two copies left a gap under the last row once the log stood taller than
	one turn (342px) */
.atk-track
{
	animation: atk-roll 22s linear infinite;
}

@keyframes atk-roll
{
	from { transform: translateY(0); }
	to { transform: translateY(calc(-100% / 3)); }
}

.atk-list
{
	margin: 0;
	padding: 0;
	list-style: none;
	font-family: var(--mono);
	font-size: 12px;
}

.atk-list li
{
	padding: 7px 14px;
	color: var(--muted);
	border-bottom: 1px solid rgba(38, 38, 58, 0.5);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.atk-ip
{
	color: color-mix(in srgb, var(--red) 70%, var(--muted));
	margin-right: 10px;
}

.atk-list li.hit
{
	color: var(--text);
	background: rgba(255, 0, 60, 0.12);
	border-bottom-color: color-mix(in srgb, var(--red) 45%, var(--border));
	animation: atk-hit 2.4s ease-in-out infinite;
}

.atk-list li.hit b
{
	margin-left: 10px;
	padding: 1px 6px;
	font-weight: 600;
	letter-spacing: .1em;
	color: var(--bg);
	background: var(--red);
}

@keyframes atk-hit
{
	0%, 100% { background: rgba(255, 0, 60, 0.10); }
	50% { background: rgba(255, 0, 60, 0.26); }
}

.atk-said
{
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.atk-line
{
	margin: 0 0 10px;
	font-family: var(--display);
	font-weight: 600;
	font-size: clamp(18px, 2.4vw, 26px);
	line-height: 1.25;
	color: var(--text);
}

.atk-line.atk-hot
{
	color: var(--yellow);
	text-shadow: 0 0 22px rgba(252, 238, 10, 0.25);
}

/* three plain percentages under the claim — the proof, not the prose */
.atk-figs
{
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 14px;
	margin: 18px 0 4px;
}

.atk-figs .fig
{
	border-top: 2px solid var(--border);
	padding-top: 10px;
}

.atk-figs .fig.hot
{
	border-top-color: var(--yellow);
}

.atk-figs b
{
	display: block;
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(26px, 3.4vw, 36px);
	line-height: 1;
	color: var(--cyan);
}

.atk-figs .fig.hot b
{
	color: var(--yellow);
}

.atk-figs span
{
	display: block;
	margin-top: 6px;
	font-size: 12px;
	line-height: 1.45;
	color: var(--muted);
}

@media (max-width: 560px)
{
	.atk-figs
	{
		grid-template-columns: minmax(0, 1fr);
	}
}

.atk-note b
{
	color: var(--text);
	font-weight: 600;
}

.atk-note
{
	margin: 14px 0 0;
	padding-top: 14px;
	border-top: 1px solid var(--border);
	font-size: 14px;
	line-height: 1.6;
	color: var(--muted);
}

@media (max-width: 860px)
{
	.atk
	{
		grid-template-columns: minmax(0, 1fr);
	}
}

/* --- the example cards (SHIELD, PROPOSALS) ---------------------------- */

/* the probe log's frame, holding a record instead of a scroll: a Shield
   rule or a proposal as the console shows it, marked as an example */
.rec
{
	display: flex;
	flex-direction: column;
	align-self: start;
}

.rec-cool .atk-wall-head
{
	background: rgba(0, 240, 255, 0.06);
}

.rec-tag
{
	float: right;
	color: var(--yellow);
	letter-spacing: .12em;
}

.rec-rows
{
	margin: 0;
	font-family: var(--mono);
	font-size: 12.5px;
}

.rec-rows div
{
	display: grid;
	grid-template-columns: 86px minmax(0, 1fr);
	gap: 12px;
	padding: 9px 14px;
	border-bottom: 1px solid rgba(38, 38, 58, 0.5);
}

.rec-rows dt
{
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: .1em;
	font-size: 11px;
	padding-top: 1px;
}

.rec-rows dd
{
	margin: 0;
	color: var(--text);
	overflow-wrap: anywhere;
}

/* narrow phones: the 86px label column left the values a third of the card
	and three lines each — the label goes above its value instead */
@media (max-width: 400px)
{
	.rec-rows div
	{
		grid-template-columns: minmax(0, 1fr);
		gap: 2px;
	}
}

.rec-rows code
{
	color: color-mix(in srgb, var(--red) 70%, var(--text));
}

.rec-was
{
	color: var(--muted);
	text-decoration: line-through;
}

.rec-now
{
	color: var(--yellow);
	letter-spacing: .08em;
	text-transform: uppercase;
}

.rec-ok
{
	color: var(--cyan);
	font-weight: 600;
}

.rec-foot
{
	padding: 10px 14px;
	font-family: var(--mono);
	font-size: 11px;
	letter-spacing: .08em;
	color: var(--muted);
}

.rec-acts
{
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	padding: 12px 14px;
}

.rec-btn
{
	font-family: var(--mono);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .12em;
	padding: 5px 10px;
	border: 1px solid color-mix(in srgb, var(--cyan) 55%, var(--border));
	color: var(--cyan);
}

.rec-btn + .rec-btn
{
	border-color: var(--border);
	color: var(--muted);
}

/* the steps sit beside the card: the list keeps the circle's type, the
   note under it keeps the attack panel's */
.atk-said .fc-steps
{
	margin-bottom: 4px;
}

/* --- the before/after bars ------------------------------------------- */

.bars
{
	margin-top: 34px;
	border: 1px solid var(--border);
	background: rgba(13, 13, 23, 0.6);
	padding: 22px 24px;
	display: grid;
	gap: 18px;
}

.bar-row
{
	display: grid;
	grid-template-columns: 200px minmax(0, 1fr) 120px;
	gap: 18px;
	align-items: center;
}

.bar-key
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 17px;
	color: var(--text);
	line-height: 1.15;
}

.bar-key span
{
	display: block;
	font-family: var(--sans);
	font-weight: 400;
	font-size: 12px;
	color: var(--muted);
	margin-top: 3px;
}

.bar-track
{
	height: 18px;
	background: rgba(38, 38, 58, 0.4);
	border: 1px solid var(--border);
	overflow: hidden;
}

.bar-fill
{
	height: 100%;
	width: 0;
	transition: width 1.3s cubic-bezier(.2, .8, .3, 1);
}

.bars.in .bar-fill
{
	width: var(--to);
}

.bar-flat
{
	background: repeating-linear-gradient(90deg, var(--border) 0 6px, transparent 6px 12px);
}

.bar-hot
{
	background: linear-gradient(90deg, color-mix(in srgb, var(--red) 70%, var(--bg)), var(--yellow));
	box-shadow: 0 0 26px rgba(252, 238, 10, 0.25);
}

.bar-val
{
	font-family: var(--mono);
	font-size: 13px;
	color: var(--muted);
	text-align: right;
}

.bar-row:last-child .bar-val
{
	color: var(--yellow);
}

@media (max-width: 700px)
{
	.bar-row
	{
		grid-template-columns: minmax(0, 1fr) 96px;
	}
	
	.bar-key
	{
		grid-column: 1 / -1;
	}
}

/* --- what it does: tiles with a working diagram ---------------------- */

.tiles
{
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 18px;
}

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

@media (max-width: 560px)
{
	.tiles
	{
		grid-template-columns: minmax(0, 1fr);
	}
}

.tile
{
	border: 1px solid var(--border);
	background: linear-gradient(180deg, rgba(17, 17, 28, 0.9), rgba(13, 13, 23, 0.7));
	padding: 18px 18px 20px;
	transition: border-color .25s ease, transform .25s ease;
}

.tile:hover
{
	border-color: color-mix(in srgb, var(--cyan) 45%, var(--border));
	transform: translateY(-3px);
}

.tile h3
{
	font-family: var(--display);
	font-weight: 700;
	font-size: 19px;
	line-height: 1.2;
	margin: 14px 0 8px;
	color: var(--text);
}

.tile p
{
	margin: 0;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--muted);
}

.dia
{
	display: block;
	width: 120px;
	height: 60px;
	overflow: visible;
}

.dia-tag
{
	font-family: var(--mono);
	font-size: 9px;
	fill: var(--muted);
}

/* 1 — ingest: marks flying into the stream */
.dia-line
{
	stroke: var(--cyan);
	stroke-width: 2;
	opacity: .6;
}

.dia-dot
{
	fill: var(--cyan);
	animation: dia-fly 2.6s linear infinite;
}

.dia-dot.d2
{
	animation-delay: .85s;
	fill: var(--red);
}

.dia-dot.d3
{
	animation-delay: 1.7s;
}

@keyframes dia-fly
{
	0% { transform: translateX(0); opacity: 0; }
	12% { opacity: 1; }
	82% { opacity: 1; }
	100% { transform: translateX(92px); opacity: 0; }
}

/* 2 — grouping: five rows collapsing into one */
.dia-bar
{
	fill: var(--muted);
	opacity: .55;
	animation: dia-collapse 3.4s ease-in-out infinite;
}

.dia-bar.c1 { animation-delay: 0s; --shift: 20px; }
.dia-bar.c2 { animation-delay: .05s; --shift: 10px; }
.dia-bar.c4 { animation-delay: .05s; --shift: -10px; }
.dia-bar.c5 { animation-delay: 0s; --shift: -20px; }

.dia-bar.c3
{
	fill: var(--cyan);
	opacity: .9;
	animation-name: dia-keep;
}

@keyframes dia-collapse
{
	0%, 25% { transform: translateY(0); opacity: .55; }
	60%, 80% { transform: translateY(var(--shift)); opacity: .12; }
	100% { transform: translateY(0); opacity: .55; }
}

@keyframes dia-keep
{
	0%, 100% { opacity: .9; }
	60%, 80% { opacity: 1; }
}

/* 3 — severity: the tallest step is the one that wakes someone */
.dia-step
{
	fill: var(--border);
}

.dia-step.hot
{
	fill: var(--red);
	animation: dia-flash 2.2s ease-in-out infinite;
}

@keyframes dia-flash
{
	0%, 100% { opacity: .55; }
	50% { opacity: 1; }
}

/* 4 — the patch: a diff writing itself */
.dia-code
{
	fill: rgba(0, 0, 0, 0.25);
	stroke: var(--border);
}

.dia-del
{
	fill: var(--red);
	opacity: .5;
}

.dia-add
{
	fill: var(--cyan);
	transform-origin: 12px 0;
	animation: dia-type 3s ease-in-out infinite;
}

.dia-add.a2
{
	animation-delay: .4s;
}

@keyframes dia-type
{
	0%, 10% { transform: scaleX(0); }
	45%, 90% { transform: scaleX(1); }
	100% { transform: scaleX(0); }
}

/* 5 — verify: the check is drawn, not asserted */
.dia-ring
{
	fill: none;
	stroke: var(--border);
	stroke-width: 2;
	stroke-dasharray: 6 8;
	transform-origin: 60px 30px;
	animation: dia-spin 9s linear infinite;
}

.dia-check
{
	fill: none;
	stroke: var(--yellow);
	stroke-width: 3;
	stroke-linecap: square;
	stroke-dasharray: 40;
	stroke-dashoffset: 40;
	animation: dia-draw 3s ease-in-out infinite;
}

@keyframes dia-spin
{
	to { transform: rotate(360deg); }
}

@keyframes dia-draw
{
	0%, 10% { stroke-dashoffset: 40; }
	45%, 85% { stroke-dashoffset: 0; }
	100% { stroke-dashoffset: 40; }
}

/* 6 — monitors: a heartbeat that must not stop */
.dia-ecg
{
	fill: none;
	stroke: var(--cyan);
	stroke-width: 2;
	stroke-dasharray: 40 300;
	animation: dia-beat 3.2s linear infinite;
}

@keyframes dia-beat
{
	from { stroke-dashoffset: 340; }
	to { stroke-dashoffset: 0; }
}

/* 7 — MCP: an agent asking codesafe */
.dia-box
{
	fill: rgba(0, 240, 255, 0.06);
	stroke: var(--cyan);
	stroke-width: 1;
}

.dia-agent
{
	fill: var(--yellow);
	animation: dia-pulse 2.4s ease-in-out infinite;
}

.dia-wire
{
	stroke: var(--yellow);
	stroke-width: 2;
	stroke-dasharray: 5 7;
	animation: dia-wire 1.6s linear infinite;
}

@keyframes dia-pulse
{
	0%, 100% { opacity: .5; }
	50% { opacity: 1; }
}

@keyframes dia-wire
{
	to { stroke-dashoffset: -24; }
}

/* 8 — sources */
.dia-chips rect
{
	fill: rgba(0, 240, 255, 0.05);
	stroke: var(--border);
}

.dia-chips text
{
	font-family: var(--mono);
	font-size: 10px;
	fill: var(--muted);
}

.dia-chips rect:nth-of-type(1) { animation: dia-chip 5s ease-in-out infinite; }
.dia-chips rect:nth-of-type(2) { animation: dia-chip 5s ease-in-out 1.2s infinite; }
.dia-chips rect:nth-of-type(3) { animation: dia-chip 5s ease-in-out 2.4s infinite; }
.dia-chips rect:nth-of-type(4) { animation: dia-chip 5s ease-in-out 3.6s infinite; }

@keyframes dia-chip
{
	0%, 80%, 100% { stroke: var(--border); }
	20%, 40% { stroke: var(--cyan); }
}

/* --- the shift: three lanes, one race ------------------------------- */

.race
{
	display: grid;
	gap: 14px;
	border: 1px solid var(--border);
	background: rgba(13, 13, 23, 0.6);
	padding: 22px 24px;
}

.race-row
{
	display: grid;
	grid-template-columns: 200px minmax(0, 1fr) 92px;
	gap: 18px;
	align-items: center;
}

.race-key
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 17px;
	color: var(--text);
}

.race-lane
{
	position: relative;
	height: 12px;
	border-bottom: 1px dashed var(--border);
}

.race-dot
{
	position: absolute;
	top: 2px;
	left: 0;
	width: 10px;
	height: 10px;
	border-radius: 50%;
}

.race-dot::after
{
	content: '';
	position: absolute;
	right: 8px;
	top: 4px;
	width: 46px;
	height: 2px;
	opacity: .55;
}

.dot-red
{
	background: var(--red);
	box-shadow: 0 0 14px rgba(255, 0, 60, 0.7);
	animation: race-run 2.6s cubic-bezier(.4, 0, .5, 1) infinite;
}

.dot-red::after
{
	background: linear-gradient(90deg, transparent, var(--red));
}

.dot-grey
{
	background: var(--muted);
	animation: race-run 9s linear infinite;
}

.dot-grey::after
{
	background: linear-gradient(90deg, transparent, var(--muted));
	opacity: .3;
}

.dot-cyan
{
	background: var(--cyan);
	box-shadow: 0 0 14px rgba(0, 240, 255, 0.7);
	animation: race-run 3s cubic-bezier(.4, 0, .5, 1) infinite;
}

.dot-cyan::after
{
	background: linear-gradient(90deg, transparent, var(--cyan));
}

@keyframes race-run
{
	0% { left: 0; opacity: 0; }
	8% { opacity: 1; }
	92% { opacity: 1; }
	100% { left: calc(100% - 10px); opacity: 0; }
}

.race-val
{
	font-family: var(--mono);
	font-size: 13px;
	color: var(--muted);
	text-align: right;
}

.race-ours .race-key
{
	color: var(--cyan);
}

.race-ours .race-val
{
	color: var(--cyan);
}

.era-note
{
	margin: 18px 0 0;
	font-size: 14px;
	line-height: 1.65;
	color: var(--muted);
	max-width: 900px;
}

.era-note b
{
	color: var(--text);
	font-weight: 600;
}

.era-note a
{
	color: var(--muted);
	text-decoration-color: color-mix(in srgb, var(--cyan) 50%, transparent);
}

.era-note a:hover
{
	color: var(--cyan);
}

@media (max-width: 700px)
{
	.race-row
	{
		grid-template-columns: minmax(0, 1fr) 78px;
	}
	
	.race-key
	{
		grid-column: 1 / -1;
		font-size: 15px;
	}
}

/* --- motion off: the page must still read complete ------------------- */

@media (prefers-reduced-motion: reduce)
{
	.reveal
	{
		opacity: 1;
		transform: none;
		transition: none;
	}
	
	.bar-fill
	{
		width: var(--to);
		transition: none;
	}
	
	body::before,
	.race-dot,
	.atk-track,
	.atk-list li.hit,
	.dia-dot,
	.dia-bar,
	.dia-step.hot,
	.dia-add,
	.dia-ring,
	.dia-check,
	.dia-ecg,
	.dia-agent,
	.dia-wire,
	.dia-chips rect
	{
		animation: none;
	}
	
	.dia-check
	{
		stroke-dashoffset: 0;
	}
	
	.dia-ecg
	{
		stroke-dasharray: none;
	}
	
	.tile:hover
	{
		transform: none;
	}
}

/*	=====================================================================
	THE DOCS PAGES — site/docs/*.html, generated by
	tools-dev/build-site-docs.php out of the repository's own markdown.
	Same chrome as the product page, tuned for reading a contract: a
	measure that stays under 80 characters, tables that scroll rather
	than squeeze, and code that is the loudest thing on the page.
	===================================================================== */

.doc-body
{
	background: var(--bg);
}

/* the docs run wider than the product page: a contract is read beside its
	code, and an 80-column JSON sample in a 400px column is unreadable */
.doc-body .wrap
{
	max-width: 1360px;
}

.doc
{
	/* padding-block, never the shorthand: .doc sits on the same element as
		.wrap and a shorthand would zero its 24px side gutter — which is how
		the text ended up flush against the edge of a 320px screen */
	padding-block: 40px 70px;
}

.doc h1
{
	font-family: var(--display);
	font-weight: 700;
	font-size: clamp(30px, 4.6vw, 46px);
	line-height: 1.05;
	margin: 8px 0 10px;
}

.doc-lead
{
	font-size: 17px;
	line-height: 1.6;
	color: var(--muted);
	max-width: 760px;
	margin: 0 0 34px;
}

.doc-grid
{
	display: grid;
	grid-template-columns: 230px minmax(0, 1fr);
	gap: 48px;
	align-items: start;
}

/* a grid item is min-content wide by default, so a wide <pre> stretches the
	column instead of scrolling inside it — this is the whole 320px bug */
.doc-grid > *
{
	min-width: 0;
}

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

/* --- the table of contents ------------------------------------------- */

.doc-toc
{
	position: sticky;
	top: 82px;
	border-left: 2px solid var(--border);
	padding-left: 16px;
	font-size: 13px;
}

@media (max-width: 900px)
{
	.doc-toc
	{
		position: static;
	}
}

.doc-toc b
{
	display: block;
	font-family: var(--display);
	font-weight: 600;
	letter-spacing: .16em;
	text-transform: uppercase;
	font-size: 11px;
	color: var(--muted);
	margin-bottom: 10px;
}

.doc-toc ul
{
	list-style: none;
	margin: 0;
	padding: 0;
}

.doc-toc li
{
	margin: 0 0 7px;
}

.doc-toc a
{
	color: var(--text);
	text-decoration: none;
	line-height: 1.35;
	display: block;
}

.doc-toc a:hover
{
	color: var(--cyan);
}

/* --- the document itself --------------------------------------------- */

.doc-article
{
	font-size: 15.5px;
	line-height: 1.7;
	color: var(--text);
}

/* prose keeps a readable measure; code, tables and figures take the width */
.doc-article > p,
.doc-article > ul,
.doc-article > ol,
.doc-article > blockquote
{
	max-width: 82ch;
}

.doc-article h2
{
	font-family: var(--display);
	font-weight: 700;
	font-size: 27px;
	line-height: 1.15;
	margin: 42px 0 14px;
	padding-top: 18px;
	border-top: 1px solid var(--border);
	scroll-margin-top: 86px;
}

.doc-article h3
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 20px;
	margin: 28px 0 10px;
	color: var(--cyan);
	scroll-margin-top: 86px;
}

.doc-article h4,
.doc-article h5,
.doc-article h6
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 16px;
	letter-spacing: .04em;
	margin: 22px 0 8px;
	color: var(--text);
	scroll-margin-top: 86px;
}

.doc-article p
{
	margin: 0 0 16px;
	color: var(--muted);
}

.doc-article strong
{
	color: var(--text);
	font-weight: 600;
}

.doc-article a
{
	color: var(--cyan);
	text-decoration-color: color-mix(in srgb, var(--cyan) 40%, transparent);
}

.doc-article ul,
.doc-article ol
{
	margin: 0 0 18px;
	padding-left: 22px;
	color: var(--muted);
}

.doc-article li
{
	margin: 0 0 7px;
}

.doc-article hr
{
	border: 0;
	border-top: 1px solid var(--border);
	margin: 30px 0;
}

.doc-article blockquote
{
	margin: 0 0 18px;
	padding: 2px 0 2px 18px;
	border-left: 2px solid var(--yellow);
}

.doc-article blockquote p
{
	margin: 0;
	color: var(--text);
}

/* code is the point of these pages */
.doc-article code
{
	font-family: var(--mono);
	font-size: 13px;
	color: var(--cyan);
	background: rgba(0, 240, 255, 0.07);
	border: 1px solid rgba(38, 38, 58, 0.9);
	padding: 1px 5px;
	/* an inline span may be a whole redaction list: let it wrap rather than
		widen the page on a phone */
	white-space: normal;
	overflow-wrap: anywhere;
}

.doc-article pre
{
	/* the sources are tab-indented; 8 columns turns a class body into a canyon */
	tab-size: 4;
	max-width: 100%;
	margin: 0 0 20px;
	padding: 16px 18px;
	overflow-x: auto;
	background: rgba(8, 8, 14, 0.85);
	border: 1px solid var(--border);
	border-left: 2px solid color-mix(in srgb, var(--cyan) 55%, var(--border));
}

.doc-article pre code
{
	font-size: 12.5px;
	line-height: 1.6;
	color: var(--text);
	background: none;
	border: 0;
	padding: 0;
	white-space: pre;
}

/* --- codesafe's own labels, in codesafe's own colours ----------- */

/* lifted from the app's theme (themes/codesafe/theme/variables.less), so a
	status code or a priority reads in the docs exactly as it does in the grid */
.doc-body
{
	--prio-0: #ff003c;
	--prio-1: #ff2e55;
	--prio-2: #ff5e3a;
	--prio-3: #ff9e2c;
	--prio-4: #fcee0a;
	--prio-5: #00f0ff;
	--prio-6: #3aa0b8;
	--prio-7: #5a5f72;
	--badge-ink: #0a0a0f;
	--ink-good: #53fb53;
	--ink-alarm: #ff3d6a;
}

.doc-article code.pill
{
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .04em;
	padding: 1px 7px;
	border: 1px solid transparent;
	background: none;
}

.doc-article code.pill-ok
{
	color: var(--badge-ink);
	background: var(--ink-good);
}

.doc-article code.pill-warn
{
	color: var(--badge-ink);
	background: var(--prio-4);
}

.doc-article code.pill-bad
{
	color: var(--text);
	background: var(--ink-alarm);
}

/* the three kinds, as the TYPE column paints them */
.doc-article code.pill-kind-error
{
	color: var(--cyan);
	border-color: var(--cyan);
	background: rgba(0, 240, 255, 0.08);
}

.doc-article code.pill-kind-404
{
	color: var(--prio-3);
	border-color: var(--prio-3);
	background: rgba(255, 158, 44, 0.1);
}

.doc-article code.pill-kind-security
{
	color: var(--ink-alarm);
	border-color: var(--ink-alarm);
	background: rgba(255, 61, 106, 0.1);
}

/* a syslog level wears the grid's PRIO fill */
.doc-article code.pill-prio
{
	color: var(--badge-ink);
}

.doc-article code.pill-prio-0 { background: var(--prio-0); }
.doc-article code.pill-prio-1 { background: var(--prio-1); }
.doc-article code.pill-prio-2 { background: var(--prio-2); }
.doc-article code.pill-prio-3 { background: var(--prio-3); }
.doc-article code.pill-prio-4 { background: var(--prio-4); }
.doc-article code.pill-prio-5 { background: var(--prio-5); }
.doc-article code.pill-prio-6 { background: var(--prio-6); }
.doc-article code.pill-prio-7 { background: var(--prio-7); color: var(--text); }

/* --- syntax colouring (built at publish time, five token kinds) ------- */

.doc-article pre code .tok-c
{
	color: color-mix(in srgb, var(--muted) 78%, transparent);
	font-style: italic;
}

.doc-article pre code .tok-s
{
	color: color-mix(in srgb, var(--yellow) 62%, var(--text));
}

.doc-article pre code .tok-n
{
	color: color-mix(in srgb, var(--red) 45%, var(--yellow));
}

.doc-article pre code .tok-k
{
	color: var(--cyan);
}

/* a JSON or YAML key: the half of the pair a reader scans for */
.doc-article pre code .tok-p
{
	color: color-mix(in srgb, var(--cyan) 55%, var(--text));
	font-weight: 600;
}

/* --- tables: scroll, never squeeze ----------------------------------- */

.doc-table
{
	overflow-x: auto;
	margin: 0 0 22px;
	border: 1px solid var(--border);
}

.doc-table table
{
	border-collapse: collapse;
	width: 100%;
	font-size: 13.5px;
}

.doc-table th
{
	text-align: left;
	font-family: var(--display);
	font-weight: 600;
	letter-spacing: .1em;
	text-transform: uppercase;
	font-size: 11px;
	color: var(--muted);
	padding: 10px 14px;
	background: rgba(0, 240, 255, 0.05);
	border-bottom: 1px solid var(--border);
	white-space: nowrap;
}

.doc-table td
{
	padding: 10px 14px;
	vertical-align: top;
	color: var(--muted);
	border-bottom: 1px solid rgba(38, 38, 58, 0.55);
}

.doc-table tr:last-child td
{
	border-bottom: 0;
}

/* a cell may hold a 60-character endpoint or a whole vocabulary: let it break
	rather than push the column past the screen (that is what produced rows two
	screens tall, with the text pushed out of sight) */
.doc-table td code
{
	overflow-wrap: anywhere;
}

.doc-table table
{
	table-layout: auto;
}

.doc-table td:first-child
{
	white-space: normal;
	min-width: 180px;
}

/* diagrams: full width, never wider than the column */
.doc-figure
{
	margin: 0 0 22px;
	padding: 16px;
	border: 1px solid var(--border);
	background: rgba(8, 8, 14, 0.6);
	text-align: center;
}

.doc-figure img
{
	max-width: 100%;
	height: auto;
}

/* --- tabs: one task, every runtime ------------------------------------ */

.tabs
{
	margin: 0 0 24px;
	border: 1px solid var(--border);
	background: rgba(8, 8, 14, 0.5);
}

.tab-strip
{
	display: flex;
	flex-wrap: wrap;
	gap: 2px;
	padding: 8px 8px 0;
	border-bottom: 1px solid var(--border);
}

.tab-strip button
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 13px;
	letter-spacing: .06em;
	color: var(--muted);
	background: none;
	border: 1px solid transparent;
	border-bottom: 0;
	padding: 7px 13px;
	cursor: pointer;
}

.tab-strip button:hover
{
	color: var(--text);
}

.tab-strip button[aria-selected="true"]
{
	color: var(--cyan);
	background: rgba(0, 240, 255, 0.07);
	border-color: var(--border);
}

.tab-panel
{
	padding: 16px 16px 2px;
}

.tab-panel pre
{
	margin-bottom: 14px;
}

/* the label above each panel is the no-JavaScript fallback: once the script
	has run the strip does that job, so it goes away */
.tabs.ready .tab-label
{
	display: none;
}

.tab-label
{
	font-family: var(--mono);
	font-size: 11px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--muted);
	margin: 0 0 8px;
}

/* an anchor you can copy, shown on hover */
.doc-anchor
{
	margin-left: 10px;
	font-size: .6em;
	color: var(--border);
	text-decoration: none;
	opacity: 0;
	transition: opacity .15s ease;
}

h2:hover > .doc-anchor,
h3:hover > .doc-anchor,
.doc-anchor:focus
{
	opacity: 1;
	color: var(--cyan);
}

/* --- the docs index --------------------------------------------------- */

.doc-cards
{
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 18px;
	margin-bottom: 26px;
}

@media (max-width: 700px)
{
	.doc-cards
	{
		grid-template-columns: minmax(0, 1fr);
	}
}

.doc-card
{
	display: block;
	text-decoration: none;
	border: 1px solid var(--border);
	background: linear-gradient(180deg, rgba(17, 17, 28, 0.9), rgba(13, 13, 23, 0.7));
	padding: 20px;
	transition: border-color .25s ease, transform .25s ease;
}

.doc-card:hover
{
	border-color: color-mix(in srgb, var(--cyan) 45%, var(--border));
	transform: translateY(-3px);
}

.doc-card h3
{
	font-family: var(--display);
	font-weight: 700;
	font-size: 20px;
	margin: 0 0 8px;
	color: var(--cyan);
}

.doc-card p
{
	margin: 0;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--muted);
}

.doc-kicker
{
	font-family: var(--display);
	font-weight: 600;
	font-size: 19px;
	color: var(--text);
	margin: 0 0 20px;
}

.doc-note
{
	font-size: 14px;
	line-height: 1.65;
	color: var(--muted);
	max-width: 760px;
	border-top: 1px solid var(--border);
	padding-top: 16px;
}

.doc-note a
{
	color: var(--cyan);
}
