/* ============================================================================
   duxwrx.com - company site
   ----------------------------------------------------------------------------
   Charcoal and red, sampled off the logo artwork rather than invented:

       #505050  wordmark charcoal
       #A8A8A8  "DRIVING SUCCESS" gray
       #AB2D2D  THE LOGO RED - HSL(0, 58%, 42%)

   #AB2D2D is measured, not approximate: it is 91.5% of every red pixel in
   both the circuit-head mark and the full lockup. An earlier version of this
   file carried #A83030 and called it the mark's red. It never was.

   EVERY RED HERE IS DERIVED FROM #AB2D2D BY MOVING LIGHTNESS ONLY. Hue stays
   0 and saturation stays 58% throughout, and lightness moves only as far as a
   contrast requirement actually forces:

       #AB2D2D   42%   the logo red. Everything that reads as TEXT or a thin
                       graphic, in both themes: the emphasised phrase on the
                       band, the section rules, the hover arrows in the
                       capability list, and the circuit traces
       #8B2525   35%   -7pt. FILLS: buttons, and the slider track and thumb.
                       Deliberately darker than the logo red - a fill covers
                       far more area than a line of text does, and at that
                       size the logo red is louder than it should be
       #6D1D1D   27%   -15pt. Light-theme fill hover
       #5D1919   23%   -19pt. The hairline around a filled button, in both
                       themes. Darker than the fill it surrounds, so it shapes
                       the button rather than outlining it against the page
       #CB3939   51%   +9pt. Only two jobs, neither of them brand: --focus,
                       and link hover text on a dark surface

   So every red on the page IS the logo red, and the two derived values exist
   for functional reasons that are named where they are used.
   scratchpad/logo_red.py measures the source, derive_palette.py regenerates.

   THE CONTRAST BAND IS DARK IN BOTH THEMES. The hero, the sub-page hero and the
   footer are painted with --band. It was briefly light in light mode, and that is
   how the light palette ended up inside an 11 point lightness range with nothing
   reading as a section - hue cannot fix that, only lightness can. Everything drawn
   on the band still comes from an --on-band-* token; nothing there may hardcode a
   colour, because the band is the one place the two themes agree and a hardcoded
   value would be invisible the moment that stops being true.

   THE ACCENT IS THE LOGO RED IN BOTH THEMES. It measures only 2.43:1 on a dark
   surface by WCAG 2.x, and an earlier version lifted it +9pt on that basis. Three
   measures were then run against each other and only WCAG separated them:

       WCAG 2.x   logo 2.33:1 on the band, lift 3.10:1 - the only line drawn
       pop score  logo 83, lift 88 - CARMStudio/Utilities/Extensions.cs
                  GetVisualPopScore, which counts the SATURATION and HUE
                  separation that a pure luminance ratio cannot see
       APCA Lc    logo 20.4, lift 28.3, against a large-text threshold near 45.
                  BOTH FAIL, so the lift never bought real readability - it only
                  crossed a WCAG threshold

   Given that, the brand value wins. scratchpad/kelly_metrics.py ports both of
   the non-WCAG measures so this can be re-checked rather than re-argued.

   THE ONE EXCEPTION IS THE FOCUS RING, which keeps the lift. That is not brand -
   it is what a keyboard user needs in order to find where they are, and it is the
   only place on the site where getting this wrong costs somebody real access.

   KEEPING A RED FROM READING AS PINK TAKES TWO RULES, NOT ONE. Blue must not
   exceed green - that is the obvious half. The other half is LIGHTNESS: an
   earlier version used #E85A5A, which has B == G and still read as salmon,
   because HSL lightness 63% is simply too high for a red. Nothing in this
   palette goes above 51%, and where contrast would demand a lighter red -
   hover text on a dark surface, which needs 4.5:1 at body size - the answer is
   to stop using red there rather than to lighten it.

   RED FILLS ALWAYS CARRY A WHITE LABEL. If a fill is too light for white text
   then the fill is wrong - do not darken the label. Dark type on a light red
   was measurably compliant and genuinely hard to read.

   FILLS ARE ONE STEP DARKER THAN THE LOGO RED (#8B2525, -7pt), because a button
   is a large area of colour where a line of text is a thin one, and the logo red
   at that size reads louder than it should. The hairline around a fill is darker
   again (#5D1919) and identical in both themes.

   THE LOGO IS ARTWORK, NEVER RECOLORED, NEVER REDRAWN. Three files straight out of
   the brand kit, cropped to their content bounds and otherwise byte-for-byte as
   delivered:

       duxwrx-wordmark.png        #505050 letters, for light surfaces
       duxwrx-wordmark-light.png  #F0F0F0 letters, for the charcoal band
       duxwrx-mark.png            the circuit-head alone

   The cropping is not a liberty - the brand-kit exports are 250x110 canvases whose
   artwork fills only 42% of the height, so a 34px-tall <img> rendered letters about
   14px tall and the wordmark was unreadable in the header. Pixels were removed from
   the empty margin; not one pixel of artwork was touched. Do not re-export without
   cropping.

   ONE PLACE SETS THE NAME IN TYPE INSTEAD: the header, via .brand-word. That is a
   deliberate exception and the reason is repetition - the artwork was appearing in
   the header, again in the footer, and a third time as the circuit-head mark in the
   hero at several hundred pixels. The footer still places the real file and the hero
   still carries the mark, so the logo has not left the site. Anywhere else, place the
   file; do not reach for the typeface.

   CLASS NAMES ARE LOAD-BEARING. js/site.js wires .site-head / .nav / .menu-btn /
   .theme-toggle, and the three legal pages under /TOS/ /privacy/ /EULA/ are imported
   documents that use .page-hero, .breadcrumb, .prose, .legal-nav, .content,
   .sub-content and .number - so those keep their names here even where the styling
   changed completely. Renaming any of them silently unstyles a legal document.

   No stock photography anywhere. A two-person software company illustrated with
   strangers shaking hands reads as less credible, not more. The visual interest is
   type, space, the red, and the circuit motif that is already in the mark.
   ============================================================================ */

/* ------------------------------------------------------------------ reset */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, h4, h5, p, ul, ol, li, figure, blockquote, dl, dd { margin: 0; padding: 0; }
html {
	height: 100%;
	scroll-behavior: smooth;
	/* Always reserve the scrollbar, so a short page keeps the same viewport width as a
	   long one and centered content does not shift about 8px between pages. */
	overflow-y: scroll;
	-webkit-text-size-adjust: 100%;
}
img, svg { max-width: 100%; }
img { height: auto; border: 0; }

/* ------------------------------------------------------------------ tokens */
:root {
	color-scheme: light;

	/* EVERY NEUTRAL BELOW IS THE DARK THEME'S, WITH ITS LIGHTNESS REFLECTED. Not picked.
	   Each one was converted to CIE Lab, its L* replaced by 100 - L*, and a* / b* kept
	   exactly - so the light theme has the same spacing between surfaces and the same faint
	   blue tint as the dark theme, running the other way. scratchpad/invert_palette.py is
	   the generator and prints the whole table; re-run it rather than nudging a value here,
	   or the two themes stop being the same design.

	   WHY, given the light theme had already been hand-assembled twice: both attempts read
	   as a weaker copy of the dark one. Deciding each surface on its own merits produced a
	   set with no relationships in it, and no amount of adjusting individual values fixes
	   that - the structure is the thing that was missing, and the dark theme already has it.

	   THE STACK COMES OUT UPSIDE DOWN, WHICH IS INHERENT AND NOT A BUG. In dark, --surface
	   is the LIGHTEST surface and --band the darkest, so cards lift off the canvas. Reflected,
	   --surface is the DARKEST and --band the lightest, so cards sit recessed into it. That is
	   the one relationship a lightness flip cannot carry, because "lifting" means catching
	   light from above and that has a direction. If it reads wrong, the fix is to swap
	   --surface and --band rather than to abandon the derivation.

	   REDS DO NOT INVERT. #AB2D2D is the logo red and is deliberately identical in both
	   themes; reflecting it produces a pink. Only neutrals transform. */
	--page-bg:       #E4E8ED;
	--surface:       #D7DCE1;
	--panel:         #CED4DA;
	--border:        #B6BFC9;
	--border-strong: #A6AFBA;

	--text:          #16181A;
	--text-strong:   #080B0C;
	--text-muted:    #3D454B;
	--text-faint:    #545C64;

	--accent:        #AB2D2D;   /* THE LOGO RED, verbatim, in BOTH themes */
	/* FILLS ARE DARKER THAN THE LOGO RED, on purpose. A button is a large area of colour
	   where a line of text is a thin one, and the logo red at button size reads louder than
	   it should. Text keeps --accent. */
	--accent-fill:   #8B2525;
	--focus:         #AB2D2D;   /* see the note about the focus ring in the header */
	/* The hairline around a FILLED button. Darker than the fill, and the SAME in both themes -
	   it shapes the fill rather than separating it from the page, so it does not need to lift
	   on dark. HSL(0, 58%, 23%), the next step down the ladder from --btn-hover. */
	--btn-ring:      #5D1919;
	/* --accent-ink IS NOT A TEXT COLOUR AT BODY SIZE. See the note by the `a` rule: red type on
	   a light surface was the "somewhat-redish with black text is not an easy read" problem. It
	   is only a hover colour and the underline under a link. */
	--accent-ink:    #8B2525;   /* link hover text on a light surface */
	--btn-hover:     #6D1D1D;   /* on a light surface a fill hovers DARKER still */
	--accent-soft:   #AB2D2D1A;
	--accent-line:   #AB2D2D40;

	/* THE CONTRAST BAND backs the hero, the sub-page hero and the footer. Reflected from dark
	   like everything else, which makes it the LIGHTEST surface in this theme where it is the
	   darkest in the other - the inversion described at the top of the block. It still does
	   the same job: it is the surface furthest from the cards, so the top and bottom of the
	   page still read as a frame around the content rather than as more of it.

	   Everything drawn on the band comes from an --on-band-* token. Those are now DARK-ON-LIGHT
	   here and light-on-dark in the other theme, so nothing on the band may hardcode a colour -
	   a literal that looked right in one theme is now guaranteed to be invisible in the other. */
	--band:              #EBEFF3;
	--band-2:            #F2F4F6;   /* the footer's bottom strip, one step further again */

	/* THE HEADER IS A SHADE DARKER THAN THE HERO BENEATH IT, which is the reflection of dark
	   mode's header being a shade LIGHTER than its band. It falls out of the derivation rather
	   than being chosen, and it happens to be the relationship that was asked for directly.

	   These keep their own tokens instead of borrowing --on-band-*: the header and the band sit
	   at different depths in both themes, so one set cannot serve both. F2 is the 95% alpha the
	   backdrop blur needs to have anything to blur. */
	--menu-bg:       #D7DCE1F2;
	--menu-solid:    #D7DCE1;   /* the phone dropdown - see the note on it in the media query */
	--head-text:     #080B0C;
	--head-nav:      #3D454B;
	--head-nav-bg:   #CED4DA;
	--head-line:     #B6BFC9;
	--on-band:           #16181A;
	--on-band-strong:    #080B0C;
	--on-band-mute:      #495158;
	--on-band-faint:     #646D73;
	--on-band-line:      #00000040; /* outline-button border on the band */
	--on-band-line-lit:  #000000A6; /* the same, hovered */
	--on-band-wash:      #0000000F; /* the faint fill an outline button gets on hover */
	--on-band-hairline:  #0000001F; /* the rule between the footer and its bottom strip */
	/* The red fade behind the hero art, and the ONE value the reflection cannot produce. A glow
	   is a normal-blend overlay, so it is a straight mix with whatever is under it: the logo red
	   at .34 over a dark band gives #451C1E, red light in a dark room, but over this light band
	   it gives #D4ABAD - a dusty pink. Light plus red IS pink, and no alpha makes it not be, so
	   the only lever is how little of it there is. .12 reads as a warm cast behind the art
	   instead of a colour in its own right. */
	--bloom:             rgba(171, 45, 45, .12);
	--mark-shadow:       0 4px 18px rgba(14, 16, 18, .16);

	--shadow-sm: 0 1px 2px rgba(22, 24, 26, .06), 0 1px 3px rgba(22, 24, 26, .08);
	--shadow-md: 0 2px 6px rgba(22, 24, 26, .07), 0 8px 20px rgba(22, 24, 26, .07);
	--shadow-lg: 0 4px 12px rgba(22, 24, 26, .09), 0 18px 42px rgba(22, 24, 26, .11);

	--radius:     10px;
	--radius-lg:  16px;
	--maxw:       1180px;
	--maxw-prose: 760px;

	/* Aliases for css/forms.css, which is copied VERBATIM from carmstudio.com and uses
	   that site's token names. Aliasing here rather than editing the copy is what keeps
	   forms.css diffable against the original, so a fix made there drops straight in.
	   Defining them once is enough - custom properties substitute at computed-value
	   time, so --accent-plum follows whichever --accent the live theme set. */
	--accent-plum: var(--accent);
	--panel-bg:    var(--panel);
}
html[data-theme="dark"] {
	color-scheme: dark;

	--page-bg:       #15181B;
	--surface:       #1D2125;
	--panel:         #22272B;
	--border:        #303840;
	--border-strong: #3D454E;

	/* Byte-for-byte what the header already rendered before it got its own tokens:
	   --text-strong, --text-muted, --panel and --border as they stand above. */
	--menu-bg:       #1D2125F2;
	--menu-solid:    #1D2125;   /* = --surface, which is what the dropdown used before */
	--head-text:     #F5F7F8;
	--head-nav:      #A7B0B8;
	--head-nav-bg:   #22272B;
	--head-line:     #303840;

	--text:          #E4E8EB;
	--text-strong:   #F5F7F8;
	--text-muted:    #A7B0B8;
	--text-faint:    #8C959D;

	/* The logo red, unlifted, exactly as in light theme. The rules, the fact borders and the
	   hover arrows are all drawn in it at 2.43:1 against --surface - low by WCAG, 85 by the
	   pop score, and these are 3px graphics rather than text. */
	--accent:        #AB2D2D;
	--accent-fill:   #8B2525;   /* same darker fill as light theme */
	/* The focus ring is the exception: it keeps the +9pt lift, at 3.55:1 against --page-bg. */
	--focus:         #CB3939;
	--btn-ring:      #5D1919;   /* same in both themes - see the note in :root */
	/* Hover text on a dark surface needs 4.5:1 at body size, and any red light enough to do that
	   is back in salmon territory. So hover goes to the strong text colour and the red arrives
	   through the underline instead. NOT used as a fill - see --btn-hover. */
	--accent-ink:    #F2F4F6;
	/* On a dark surface a fill hovers LIGHTER, which is the conventional direction there. It
	   lifts to exactly the logo red, so hovering brings the button up to brand rather than
	   past it. */
	--btn-hover:     #AB2D2D;
	--accent-soft:   #AB2D2D2E;
	--accent-line:   #AB2D2D66;

	--band:              #101316;
	--band-2:            #0C0E10;
	--on-band:           #E4E8EB;
	--on-band-strong:    #FFFFFF;
	--on-band-mute:      #99A2AA;
	--on-band-faint:     #7B848B;
	--on-band-line:      #FFFFFF40;
	--on-band-line-lit:  #FFFFFFA6;
	--on-band-wash:      #FFFFFF0F;
	--on-band-hairline:  #FFFFFF1F;
	/* The dark band takes the logo red at full strength - on near-black this reads as red
	   light rather than as a tint. See the long note in :root for why the two themes cannot
	   share one value. */
	--bloom:             rgba(171, 45, 45, .34);
	--mark-shadow:       0 6px 26px rgba(0, 0, 0, .55);

	--shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
	--shadow-md: 0 2px 6px rgba(0, 0, 0, .45), 0 8px 22px rgba(0, 0, 0, .4);
	--shadow-lg: 0 4px 14px rgba(0, 0, 0, .5), 0 20px 46px rgba(0, 0, 0, .45);
}

body {
	background: var(--page-bg);
	color: var(--text);
	font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
	             "Helvetica Neue", Arial, "Noto Sans", sans-serif;
	font-size: 17px;
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	display: flex;
	flex-direction: column;
	min-height: 100vh;
}
/* main grows to keep the footer at the bottom of a short page. It is ALSO a flex column so that
   the leftover height can be handed to a chosen section - see .section.stretch. Without that, the
   slack collects after the last section, which reads as that section being oddly tall on a tall
   desktop window while looking correct on a phone, where there is no slack to collect. */
main {
	flex: 1 0 auto;
	display: flex;
	flex-direction: column;
}

/* ------------------------------------------------------------------ type */
h1, h2, h3, h4, h5 {
	color: var(--text-strong);
	line-height: 1.14;
	letter-spacing: -0.018em;
	font-weight: 700;
}
h1 { font-size: clamp(2.15rem, 1.35rem + 3.2vw, 3.7rem); }
h2 { font-size: clamp(1.55rem, 1.2rem + 1.5vw, 2.25rem); }
h3 { font-size: clamp(1.12rem, 1.05rem + 0.35vw, 1.3rem); }
h4 { font-size: 1.05rem; }

p { margin-bottom: 1rem; }
p:last-child { margin-bottom: 0; }

/* RED IS A GRAPHIC COLOUR HERE, NOT A TEXT COLOUR.
   Links used to be set in #8C2226 and so did the eyebrows, the card footers and the numbered
   headings in the legal documents. A dark desaturated maroon at 14-15px clears 4.5:1 on white
   and still reads muddy - it is not quite red and not quite black, and next to charcoal body
   copy the page looks like it cannot decide. So reading text is charcoal, and the red arrives
   as the UNDERLINE and on hover, where it marks the link without being the thing you read.
   Red still carries the buttons, the section rules, the circuit traces, the focus ring and the
   one emphasised phrase in the hero - all places where it is a shape, not a sentence. */
a {
	color: var(--text-strong);
	text-decoration: underline;
	text-decoration-color: var(--accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 2px;
}
a:hover { color: var(--accent-ink); text-decoration-color: var(--accent-ink); }

strong { color: var(--text-strong); font-weight: 650; }

.eyebrow {
	display: block;
	font-size: .76rem;
	font-weight: 700;
	letter-spacing: .17em;
	text-transform: uppercase;
	color: var(--text-faint);
	margin-bottom: .7rem;
}
.lede {
	font-size: clamp(1.05rem, 1rem + 0.4vw, 1.2rem);
	color: var(--text-muted);
	max-width: 62ch;
}
.center { text-align: center; }
.center .lede { margin-left: auto; margin-right: auto; }
.note { color: var(--text-faint); font-size: .87rem; }

/* Always visible on both themes, and never color alone. Uses --focus rather than --accent:
   the accent is the logo red, which is 2.43:1 on a dark surface, and a focus ring is the one
   red on this site that has to be found rather than admired. */
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 3px; }

/* Chrome and navigation opt OUT of the underline. The underline is there to mark a link inside
   a sentence; on a header, a footer list, a breadcrumb or a card that is entirely a link, it is
   just noise. Listed in one place so a new link cannot quietly miss it. */
.brand a,
.nav a,
.btn,
a.card,
.cap-list a,
.legal-nav a,
.breadcrumb a,
.foot-col a,
.foot-brand a,
.foot-bottom-inner a,
.skip-link {
	text-decoration: none;
}
.foot-col a:hover,
.foot-bottom-inner a:hover,
.breadcrumb a:hover { text-decoration: underline; text-decoration-color: currentColor; }

/* ------------------------------------------------------------------ layout */
.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 1.5rem; }
/* SECTIONS ARE CARDS ON THE DARK CANVAS. The <section> is now only the GAP around a card -
   all the breathing room that used to be section padding moved inside .container, which is
   the card itself. The markup did not change: every section on the site is already
   <section class="section"><div class="container">, so the card had a box to be. */
.section { padding: clamp(.9rem, .7rem + .9vw, 1.6rem) 1.5rem; }
.section.tight { padding-block: clamp(.5rem, .4rem + .5vw, .9rem); }
.section > .container {
	position: relative;   /* the beam is an ::before on this */
	background: var(--surface);
	/* The border earns its place in DARK, where the card is #1D2125 on a #101316 canvas and
	   needs an edge to be a card at all. In light it is a faint halo around a white card and
	   costs nothing, so one rule covers both. */
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-md);
	padding: clamp(2.25rem, 1.75rem + 2vw, 3.5rem) clamp(1.5rem, 1rem + 2vw, 3rem);
}
/* Sections whose CONTENT is already a card: the three legal documents wrap .prose, and the
   contact form wraps .form-wrap. Carding the section too would put a card inside a card. */
.section.bare { padding-block: clamp(2.25rem, 1.75rem + 2vw, 3.5rem); }
.section.bare > .container {
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	/* ZERO, not the .container default of 0 1.5rem. The section already supplies that gutter, so
	   keeping the container's own padding would inset .prose and .form-wrap by 3rem while every
	   carded section's card starts at 1.5rem - the cards would not line up down the page. */
	padding: 0;
}
/* .alt used to mean "the white one" in the alternating model. Every card is --surface now, so
   it decides nothing - kept only because the markup carries it and removing it from four
   files buys nothing. Do not hang new meaning on it without renaming it. */

/* ---------------------------------------------------------------- the beam */
/* A lit segment running the card's border, the same idea as the header separator and the
   hero traces: same #E8442E, same 11s period.

   It is a conic gradient rotated behind the card and masked down to the 1px ring, which is
   the only pure-CSS way to light a ROUNDED rectangle's edge - a background-position slide
   like the header's works on a straight line and cannot turn a corner.

   The angle has to be an @property or it will not interpolate; a plain custom property is a
   string to the animation engine and jumps 0 -> 360 in one frame. Where @property is missing
   the beam simply sits still and the ordinary border is still there underneath, so nothing
   breaks - it just stops moving.

   HONEST CAVEAT: a conic sweep is angular, not perimeter-based, so the lit point covers the
   short edges faster than the long ones. On a 1180x600 card the unevenness is visible if you
   look for it. Fixing it properly needs an SVG path per card size, which is JS this site
   does not otherwise need. */
@property --beam-angle {
	syntax: '<angle>';
	initial-value: 0deg;
	inherits: false;
}
.section > .container::before {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: inherit;
	padding: 1px;   /* the ring thickness the mask leaves behind */
	pointer-events: none;
	background: conic-gradient(from var(--beam-angle),
		transparent 0turn, transparent .78turn,
		#E8442E .88turn, #E8442E .92turn,
		transparent 1turn);
	/* Punch the middle out: paint the padding box, subtract the content box, leaving the ring. */
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	        mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	        mask-composite: exclude;
	animation: beam-run 11s linear infinite;
}
/* Out of phase on purpose. In step they read as one mechanism firing; staggered they read as
   several things quietly alive. The offsets are just thirds of the period, negated so every
   card is already mid-run on load rather than waiting to start. */
.section:nth-of-type(2) > .container::before { animation-delay: -3.7s; }
.section:nth-of-type(3) > .container::before { animation-delay: -7.3s; }
.section.bare > .container::before { content: none; }
@keyframes beam-run {
	to { --beam-angle: 360deg; }
}
/* The section that should absorb any leftover viewport height on a short page. Put this on the
   section carrying the most substantial content - on contact.html that is the form. Only one
   section per page should have it, or they will split the slack between them. */
.section.stretch { flex: 1 0 auto; }

/* A hairline rule with a short red leader, used above section headings. It replaces the
   decorative-icon habit: one line of brand color, no clip art. */
.rule {
	display: block;
	height: 3px;
	width: 46px;
	border-radius: 2px;
	background: var(--accent);
	margin-bottom: 1.25rem;
}

/* ------------------------------------------------------------------ header */
/* The sticky lives on the WRAPPER, not on .site-head. js/site.js injects the header partial into
   <div id="site-header">, so .site-head's containing block is that div - a box exactly its own
   height, which gives a sticky element no distance to travel. It scrolled away with its parent.
   Putting it here makes the scroll container the containing block, which is what sticky needs. */
#site-header {
	position: sticky;
	top: 0;
	z-index: 60;
}
.site-head {
	position: relative;
	background: var(--menu-bg);
	backdrop-filter: saturate(140%) blur(10px);
	/* The separator is THE LOGO RED rather than a neutral hairline - it is the same line the
	   circuit traces are drawn in, so the header reads as part of the same drawing. */
	border-bottom: 1px solid var(--accent);
}

/* A lit segment travelling the separator, built to the same rules as the pulses on the hero
   traces so the two read as one idea rather than two effects: the same #E8442E (hue 7, 79%
   saturation - orange-leaning, which is what keeps a bright red off the salmon end), the same
   tight 4px glow, and the same 11s period. Slow on purpose - alive, not animated.
   drop-shadow follows alpha, so the glow lands only on the lit segment and not along the whole
   transparent strip, which is the same separation the hero gets from a dedicated pulse layer. */
.site-head::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
	height: 2px;
	pointer-events: none;
	background: linear-gradient(90deg, transparent, #E8442E 42%, #E8442E 58%, transparent) no-repeat;
	background-size: 200px 100%;
	filter: drop-shadow(0 0 4px rgba(232, 68, 46, .55));
	animation: head-trace 11s linear infinite;
}
@keyframes head-trace {
	from { background-position: -200px 0; }
	to   { background-position: calc(100% + 200px) 0; }
}
.site-head .bar {
	max-width: var(--maxw);
	margin: 0 auto;
	padding: 0 1.5rem;
	min-height: 70px;
	display: flex;
	align-items: center;
	gap: 1rem;
}
/* THE HEADER NAME IS TYPE, NOT THE WORDMARK FILE - the one place on the site where that is true,
   and a deliberate exception to the rule further up this file. The artwork was appearing three
   times above the fold: header, footer, and the circuit-head mark in the hero at several hundred
   pixels. The footer still places the real file and the hero still carries the mark.

   It is set to EVOKE the lockup rather than imitate it: the real wordmark is wide-tracked caps, so
   this is too, at a size that matches the 34px the image occupied. It is not trying to pass for
   the logo - a typographic name and a placed logo are different things, and only one of them is
   the brand. No theme swap is needed because type takes a colour. */
.brand { display: flex; align-items: center; margin-right: auto; }
.brand a { display: block; text-decoration: none; }
.brand-word {
	display: block;
	/* 2rem. Started at 1.2rem, where it was barely larger than the nav links beside it and read as
	   another menu item rather than as the identity; 1.7rem matched the old 190x34 image footprint;
	   this goes past it deliberately. The binding constraint is the phone, where the name has to
	   clear the theme toggle and the burger - at this size there is still about 50px between them
	   at 375px wide, so there is room left if it wants to grow again. */
	font-size: 2rem;
	font-weight: 700;
	line-height: 1;
	color: var(--head-text);
	white-space: nowrap;

	/* WIDER LETTERS AT THE SAME HEIGHT.
	   font-stretch and font-variation-settings 'wdth' are the proper tools for this and neither is
	   available here: the stack resolves to Segoe UI on Windows, which ships no expanded width and
	   exposes no width axis, so both would be silently ignored. scaleX is what is left.

	   letter-spacing drops from .2em to .18em because the transform scales the TRACKING as well as
	   the glyphs - .18 x 1.12 lands back on .2, so the gaps between letters stay where they were
	   and only the letterforms grow. That is what was asked for: wider letters, not a wider gap.

	   The honest caveat: scaleX widens vertical strokes while leaving horizontals alone, so the
	   stems thicken slightly relative to the crossbars. At 1.12 on a geometric sans it is not
	   visible; much past about 1.15 it starts to look stretched rather than wide. */
	letter-spacing: .18em;
	transform: scaleX(1.12);
	transform-origin: left center;

	/* The tracking adds space after the final X too, which pushes the optical left edge right of
	   everything below it. Pulling it back lines the name up with the content column. */
	margin-right: -.18em;
}
.brand a:hover .brand-word { color: var(--accent); }

.nav { display: flex; align-items: center; gap: .2rem; }
.nav a {
	display: block;
	padding: .5rem .8rem;
	border-radius: 8px;
	font-size: .93rem;
	font-weight: 550;
	color: var(--head-nav);
	text-decoration: none;
	white-space: nowrap;
}
.nav a:hover { color: var(--head-text); background: var(--head-nav-bg); text-decoration: none; }
.nav a.active { color: var(--head-text); }
.nav a.active::after {
	content: "";
	display: block;
	height: 2px;
	margin-top: 3px;
	border-radius: 2px;
	background: var(--accent);
}
.nav .nav-cta {
	margin-left: .35rem;
	color: #fff;
	background: var(--accent-fill);
	padding: .5rem 1.05rem;
	box-shadow: 0 0 0 2px var(--btn-ring);
}
.nav .nav-cta:hover { background: var(--btn-hover); color: #fff; }

.head-actions { display: flex; align-items: center; gap: .35rem; }

.theme-toggle {
	width: 38px; height: 38px; padding: 0;
	display: inline-flex; align-items: center; justify-content: center;
	background: transparent;
	border: 1px solid var(--head-line);
	border-radius: 9px;
	color: var(--head-nav);
	cursor: pointer;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--accent-line); }
.theme-toggle svg { width: 18px; height: 18px; }
.theme-toggle .icon-sun { display: none; }
html[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
html[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

.menu-btn {
	display: none;
	width: 38px; height: 38px; padding: 0;
	align-items: center; justify-content: center;
	background: transparent;
	border: 1px solid var(--head-line);
	border-radius: 9px;
	color: var(--on-band);   /* #E4E8EB in both themes - the value dark's --text already was */
	cursor: pointer;
}
.menu-btn span { display: block; width: 17px; height: 2px; border-radius: 2px; background: currentColor; position: relative; }
.menu-btn span::before, .menu-btn span::after {
	content: ""; position: absolute; left: 0; width: 17px; height: 2px; border-radius: 2px; background: currentColor;
}
.menu-btn span::before { top: -5px; }
.menu-btn span::after { top: 5px; }

/* ------------------------------------------------------------------ buttons */
.btn {
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	padding: .78rem 1.45rem;
	border-radius: 9px;
	font-size: .97rem;
	font-weight: 600;
	text-decoration: none;
	border: 1px solid transparent;
	cursor: pointer;
	transition: transform .12s ease, background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn .arw { transition: transform .15s ease; }
.btn:hover .arw { transform: translateX(3px); }
/* WHY A FILLED LABEL GETS ITS OWN WEIGHT AND SMOOTHING.
   The label is already #FFFFFF - there is no whiter white - so a label that reads as dull grey
   is not a colour problem, it is a stroke problem. body sets -webkit-font-smoothing: antialiased,
   which is greyscale AA, and greyscale AA THINS strokes. Thin white letters on a dark fill lose
   coverage and the eye reads that as less white. So filled labels opt back into the platform's
   normal smoothing and carry a heavier weight - more ink on the same colour. */
.btn-primary,
.nav .nav-cta,
.skip-link,
.legal-nav a[aria-current="page"] {
	font-weight: 700;
	-webkit-font-smoothing: auto;
	-moz-osx-font-smoothing: auto;
}

/* THE FILL IS ONE STEP DARKER THAN THE LOGO RED, and the 1px ring is one step darker again.
   An earlier version drew the ring in --focus, which lifts to #CB3939 on dark so it could give
   the button an edge against the near-black page. It did that, and it read as a bright outline.
   Going darker instead means one value for both themes, and the button's edge on dark is
   carried by the white label at 8.9:1 rather than by the ring.

   2px, not 1px. --btn-ring sits only 12 lightness points below the fill, which at a single
   pixel is invisible - the ring was already there and simply could not be seen. A box-shadow
   ring draws OUTSIDE the border box, so widening it costs no layout. */
.btn-primary {
	background: var(--accent-fill);
	color: #fff;
	box-shadow: 0 0 0 2px var(--btn-ring);
}
.btn-primary:hover { background: var(--btn-hover); color: #fff; }
.btn-ghost { background: transparent; color: var(--text-strong); border-color: var(--border-strong); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent-ink); }
/* The outline button that sits ON the band. Named for where it lives, not for a colour - the
   band is light in light mode now, so every value here has to come from a token. */
.btn-on-dark {
	background: transparent;
	color: var(--on-band-strong);
	border-color: var(--on-band-line);
}
.btn-on-dark:hover {
	border-color: var(--on-band-line-lit);
	color: var(--on-band-strong);
	background: var(--on-band-wash);
}
.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2rem; }
.btn-row.center { justify-content: center; }

/* ============================================================== hero
   The mark is the hero, at full size and full color - not a 12% watermark. The circuit
   traces that run out of it are CSS/SVG, drawn to continue the ones already inside the
   artwork, and a single faint pulse travels each trace on a long stagger. Slow on
   purpose: the point is that it is alive, not that it is animated.
   ============================================================================ */
.hero {
	position: relative;
	overflow: hidden;
	background: var(--band);
	color: var(--on-band);
	border-bottom: 1px solid var(--border);
}
/* Red bloom behind the mark, so the artwork sits in its own light instead of on a flat
   rectangle. Pure CSS - no image, nothing to 404.

   ANCHORED TO THE CONTENT, NOT TO THE VIEWPORT. This used to be positioned `at 76% 46%`, and a
   percentage is a percentage of the BAND, which is full width - so on a wide monitor the bloom
   slid away to the right while the mark stayed put inside the centred 1180px column. The offset
   is now measured from the centre line, which is where the column is anchored, so the two track
   each other at any width.

   Where the +356px comes from: the container is 1180 wide with 24px of padding each side, so
   1132 of usable width, and .hero-inner puts the art in a 420px right-hand column. That column
   starts 712px into the container and its centre is 210px further, at 922px. The container's own
   left edge is calc(50% - 590px + 24px), so the art centre lands at calc(50% + 356px). The radii
   are in px for the same reason - a percentage would stretch the bloom across a large display. */
.hero::after {
	content: "";
	position: absolute; inset: 0;
	pointer-events: none;
	background: radial-gradient(ellipse 400px 330px at calc(50% + 356px) 50%,
	                            var(--bloom), transparent 70%);
}
/* Below 940px .hero-inner stacks to one column and the art moves under the copy, centred, so the
   bloom has to follow it there. */
@media (max-width: 940px) {
	.hero::after {
		background: radial-gradient(ellipse 300px 300px at 50% 74%,
		                            var(--bloom), transparent 70%);
	}
}
.hero .container { position: relative; z-index: 1; }
.hero-inner {
	padding: clamp(3.25rem, 2.25rem + 5vw, 6.5rem) 0;
	display: grid;
	gap: clamp(2rem, 1rem + 4vw, 4rem);
	grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
	align-items: center;
}
.hero h1 { color: var(--on-band-strong); }
.hero h1 .accent { color: var(--accent); }
.hero .lede {
	color: var(--on-band-mute);
	margin-top: 1.2rem;
	font-size: clamp(1.05rem, 1rem + .45vw, 1.22rem);
	max-width: 46ch;
}
/* The company line above the headline. Not an "eyebrow" in accent red here - on the band
   that would fight the headline's red emphasis, so it is quiet gray with a red tick. */
.hero-kicker {
	display: inline-flex;
	align-items: center;
	gap: .6rem;
	font-size: .76rem;
	font-weight: 700;
	letter-spacing: .17em;
	text-transform: uppercase;
	color: var(--on-band-mute);
	margin-bottom: 1.1rem;
}
.hero-kicker::before {
	content: "";
	width: 26px; height: 2px;
	border-radius: 2px;
	background: var(--accent);
	flex: none;
}

/* width:100% is not decoration. Both children are absolutely positioned, so this box has no
   in-flow content to size against - if its inline size ever resolves to auto it shrink-to-fits
   to ZERO, and aspect-ratio then hands back zero height, and the logo silently disappears.
   That is exactly what happened at mobile widths, where the auto inline margins used to center
   it took priority over justify-self:stretch and un-stretched the item. Keep the width
   definite and center with justify-self, never with auto margins. */
/* width:100% is not decoration. Both children are absolutely positioned, so this box has no
   in-flow content to size against - if its inline size ever resolves to auto it shrink-to-fits
   to ZERO, and aspect-ratio then hands back zero height, and the logo silently disappears.
   That is exactly what happened at mobile widths, where the auto inline margins used to center
   it took priority over justify-self:stretch and un-stretched the item. Keep the width
   definite and center with justify-self, never with auto margins. */
.hero-art { position: relative; width: 100%; aspect-ratio: 1 / 1; }
.hero-art .circuit { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

/* ====== DO NOT CHANGE width, left, top OR transform HERE WITHOUT REDOING THE PATHS ======
   The SVG traces in index.html continue the traces that run out of the logo artwork, and they
   are anchored to MEASURED pixel positions inside duxwrx-mark.png. That mapping depends on
   exactly this placement:

       viewBox 420 x 420, image at width:50% centred
       -> image is 210.00 x 223.39 at (105.00, 98.31)
       -> file px to viewBox units = x 0.836653

   Change the width and every join moves and the continuations stop lining up. The derivation
   is in the comment above the <svg> in index.html.
   ======================================================================================== */
.hero-art .hero-mark {
	position: absolute;
	left: 50%; top: 50%;
	transform: translate(-50%, -50%);
	width: 50%;
	filter: drop-shadow(var(--mark-shadow));
}

/* The continuation traces are drawn at the ARTWORK'S OWN gauge and colour, measured off the
   file: every trace in the mark is 3px wide, which at this scale is 3 x 0.836653 = 2.51 viewBox
   units, and the crimson samples as #AB2D2D. Full opacity, not a faded tint - the whole point is
   that the line leaving the logo and the line crossing the page are the same line. A thinner or
   lighter stroke is what made the previous version read as decoration parked near the logo.
   #AB2D2D is only about 2.5:1 on the band, which is correct: this is decorative graphics, not
   text and not a UI control, so it is out of scope for contrast minimums - and matching the
   artwork matters more than standing out. Legibility is carried by the pulse below. */
.circuit .traces path {
	fill: none;
	stroke: #AB2D2D;
	stroke-width: 2.51;
	stroke-linecap: butt;
}
/* The mark's own terminal dots measure 7px across -> r 2.93. Same value, so a continuation that
   ends in a dot ends in the same dot the artwork uses. */
.circuit .traces circle { fill: #AB2D2D; }

/* Travelling pulse: a lit segment sliding along each trace.
   Two things were wrong with the first version and both showed up in a screenshot rather than
   in motion. It used a SHORT dash, which freeze-frames as a stub of line floating in a gap; and
   its colour (#F2705C, a salmon) reads as tan next to #AB2D2D rather than as the same line lit
   up. So: a long dash with round caps and a glow, in a colour that is unmistakably the same red
   only brighter. Any still frame now reads as "this stretch of the line is lit". #E8442E sits
   at hue 7 with 79% saturation - it leans ORANGE rather than light, which is what keeps a bright
   red off the salmon end. The glow is deliberately tight and weak: a wide soft halo of light red
   over a dark background is itself a way to end up looking pink. */
.circuit .pulses {
	filter: drop-shadow(0 0 4px rgba(232, 68, 46, .55));
}
.circuit .pulses path {
	fill: none;
	stroke: #E8442E;
	stroke-width: 2.51;
	stroke-linecap: round;
	stroke-dasharray: 26 1000;
	stroke-dashoffset: 0;
	animation: trace-flow 11s linear infinite;
}
/* Staggered so the eight traces never fire together, which would read as a strobe. Prime-ish
   spacing across the 11s period keeps the pattern from settling into a visible rhythm. */
.circuit .pulses path:nth-child(1) { animation-delay: 0s; }
.circuit .pulses path:nth-child(2) { animation-delay: 4.1s; }
.circuit .pulses path:nth-child(3) { animation-delay: 7.3s; }
.circuit .pulses path:nth-child(4) { animation-delay: 2.2s; }
.circuit .pulses path:nth-child(5) { animation-delay: 9.4s; }
.circuit .pulses path:nth-child(6) { animation-delay: 5.6s; }
.circuit .pulses path:nth-child(7) { animation-delay: 1.3s; }
.circuit .pulses path:nth-child(8) { animation-delay: 8.2s; }
@keyframes trace-flow {
	/* Sweeping the full dasharray period guarantees the dash has left the path completely
	   before it re-enters, whatever that individual path's length happens to be. */
	from { stroke-dashoffset: 26; }
	to   { stroke-dashoffset: -1000; }
}

/* The four left-hand terminals breathe, so a trace that ends in a dot looks like it arrives
   somewhere rather than just stopping. */
.circuit .nodes circle {
	fill: #AB2D2D;
	animation: node-pulse 6.5s ease-in-out infinite;
}
.circuit .nodes circle:nth-child(2) { animation-delay: 1.6s; }
.circuit .nodes circle:nth-child(3) { animation-delay: 3.2s; }
.circuit .nodes circle:nth-child(4) { animation-delay: 4.8s; }
@keyframes node-pulse {
	0%, 100% { fill: #AB2D2D; r: 2.93; }
	50%      { fill: #E8442E; r: 3.8; }
}

/* Sub-page band. Same charcoal, shorter. Used by contact.html and all three legal pages. */
.page-hero {
	position: relative;
	overflow: hidden;
	background: var(--band);
	color: var(--on-band);
	border-bottom: 1px solid var(--border);
}
/* Light spilling from the top edge of the band, CENTRED. This used to sit `at 88% 10%`, and
   because a percentage is a percentage of the full-width band, on a wide monitor it drifted off
   to the top right and read as an unrelated smudge - the heading is inside a centred 1180px
   column and the two had nothing to do with each other. There is no artwork on these pages for a
   bloom to belong to, so it is centred: symmetric, deliberate, and identical at every width.
   Radii in px rather than percentages so it does not stretch across a large display. */
.page-hero::after {
	content: "";
	position: absolute; inset: 0;
	pointer-events: none;
	background: radial-gradient(ellipse 760px 300px at 50% 0%,
	                            var(--bloom), transparent 72%);
}
.page-hero .container { position: relative; z-index: 1; }
.page-hero-inner { padding: clamp(2.25rem, 1.75rem + 2.5vw, 3.75rem) 0; }
.page-hero h1 { color: var(--on-band-strong); }
.page-hero .lede { color: var(--on-band-mute); margin-top: .85rem; }
.breadcrumb { font-size: .85rem; color: var(--on-band-mute); margin-bottom: .9rem; }
.breadcrumb a { color: var(--on-band-mute); }
.breadcrumb a:hover { color: var(--on-band-strong); }

/* ============================================================== the product
   Everything factual here comes off carmstudio.com. The nine capability names are that
   site's own section headings and each one links to its own page there - the detail lives
   on the product site, and this page's job is to hand people over to it, not to re-pitch.
   ============================================================================ */
/* One column, generous measure. There is no boxed spec panel any more - platform, database,
   scope, founding year and the licence terms all came off. None of that is why anybody arrives
   at a company page, and the licence terms are a contract-negotiation document. */
.product { max-width: 74ch; }

/* The product name, set in the site's own type rather than shown as its logo - see the long
   note in index.html for why. Bigger than a normal h2 because it is standing in for a logotype
   and has to carry the same weight in the layout. */
.product-name {
	font-size: clamp(2rem, 1.4rem + 2.4vw, 3.1rem);
	line-height: 1.06;
	margin: .9rem 0 1.15rem;
}

.product-body { margin-top: 1rem; color: var(--text-muted); }

/* Capability list. Two columns of real feature names, hairline separated, with an arrow
   that slides on hover. No icons - nine invented glyphs would be nine small lies. */
.cap-list {
	list-style: none;
	display: grid;
	gap: 0 2.5rem;
	grid-template-columns: 1fr 1fr;
	margin-top: 1.75rem;
	border-top: 1px solid var(--border);
}
.cap-list li { border-bottom: 1px solid var(--border); }
.cap-list a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: .72rem .25rem;
	font-size: .95rem;
	font-weight: 550;
	color: var(--text);
	text-decoration: none;
}
.cap-list a:hover { color: var(--accent-ink); text-decoration: none; }
.cap-list a::after {
	content: "\2192";
	color: var(--accent);
	opacity: 0;
	transform: translateX(-6px);
	transition: opacity .15s ease, transform .15s ease;
}
.cap-list a:hover::after { opacity: 1; transform: translateX(0); }

/* ============================================================== statement */
/* A single paragraph block, set larger than body text and marked with a red bar. This is
   the only place on the site that talks about the company in its own voice, so it gets
   room rather than being chopped into three cards of invented virtues. */
.statement {
	/* NOT in ch. `ch` resolves against THIS element's font-size, which is the inherited 17px,
	   while the paragraphs inside render at about 20.5px from their own clamp - so a 62ch measure
	   delivered only about 49 characters of the actual rendered text and the block sat at half the
	   column width, reading as a squeezed strip rather than a statement. 46rem gives roughly 63
	   characters at the size the text is really set in. */
	max-width: 46rem;
	margin-top: 1.25rem;
	border-left: 3px solid var(--accent);
	padding-left: clamp(1.25rem, 1rem + 1vw, 2rem);
}
.statement p {
	font-size: clamp(1.08rem, 1rem + .5vw, 1.28rem);
	line-height: 1.55;
	color: var(--text);
}
.statement p + p { margin-top: 1rem; }
.statement .who {
	display: block;
	margin-top: 1.5rem;
	font-size: .8rem;
	font-weight: 700;
	letter-spacing: .13em;
	text-transform: uppercase;
	color: var(--text-faint);
}

/* ------------------------------------------------------------------ facts */
/* A fact whose value runs to several lines, or is an address or an email - the one-line
   size is too heavy for those and a mailing address needs its own line breaks. */

/* ------------------------------------------------------------------ cards */

/* ============================================================== CTA */
/* On the charcoal band rather than a white box, so the page closes the way it opened. */

/* ------------------------------------------------------------------ prose (legal) */
.prose {
	max-width: var(--maxw-prose);
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	padding: clamp(1.5rem, 1.25rem + 1.5vw, 2.75rem);
	box-shadow: var(--shadow-sm);
	margin-inline: auto;
}
.prose h2 {
	font-size: 1.14rem;
	text-transform: uppercase;
	letter-spacing: .05em;
	margin-top: 2.25rem;
	margin-bottom: .8rem;
	padding-bottom: .5rem;
	border-bottom: 1px solid var(--border);
}
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: 1.02rem; margin-top: 1.5rem; margin-bottom: .5rem; }
.prose p, .prose li { color: var(--text-muted); font-size: .96rem; }
.prose ul, .prose ol { margin: 0 0 1rem 1.25rem; }
.prose li { margin-bottom: .45rem; }
.prose a { text-decoration: underline; }
/* The "Last updated: ..." line is always the first paragraph inside the imported legal
   fragment. Styling it in place, rather than lifting it into its own element, is what
   lets that HTML stay byte-for-byte identical to the document it came from - so the
   re-skin can be verified by plain equality instead of trusted. */
.prose .updated,
.prose > p:first-child {
	display: inline-block;
	font-size: .79rem;
	font-weight: 600;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--text-faint);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 999px;
	padding: .25rem .8rem;
	margin-bottom: 1.75rem;
}
/* The legal text was authored as nested numbered tables; keep that shape readable. */
.prose table { width: 100%; border-collapse: collapse; }
.prose td { vertical-align: top; padding: 0 0 .55rem 0; color: var(--text-muted); font-size: .96rem; }
.prose td.number { width: 2.6rem; padding-right: .5rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.prose td.number.main-section { font-weight: 700; color: var(--text-strong); }
/* The three documents were authored with these classes on their nested numbering tables, and
   each page carried its own <style> block defining them. Re-skinning the pages dropped that
   block, so the rules have to live here - without them the sub-clauses lose their indent and
   their numbers sit at the wrong weight. Kept minimal: the original hardcoded 16/24/32px font
   sizes and absolute line-heights to line numbers up against a fixed-size heading, which this
   stylesheet does with the table cell instead. */
.prose td.content { vertical-align: top; }
.prose .sub-content { padding-left: 10px; }
.prose td.number.sub-section { font-weight: 400; }
.prose td.number.sub-section.h3-align { font-weight: 700; color: var(--text); }

.legal-nav { display: flex; flex-wrap: wrap; gap: .5rem; max-width: var(--maxw-prose); margin: 0 auto 1.5rem; }
.legal-nav a {
	font-size: .89rem; font-weight: 600;
	padding: .4rem .9rem;
	border: 1px solid var(--border);
	border-radius: 999px;
	background: var(--surface);
	color: var(--text-muted);
	text-decoration: none;
}
.legal-nav a:hover { border-color: var(--accent-line); color: var(--accent-ink); text-decoration: none; }
.legal-nav a[aria-current="page"] { background: var(--accent-fill); border-color: var(--btn-ring); color: #fff; }

/* ------------------------------------------------------------------ footer */
.site-foot {
	margin-top: auto;
	background: var(--band);
	color: var(--on-band-mute);
	border-top: 1px solid var(--border);
}
.foot-top {
	max-width: var(--maxw);
	margin: 0 auto;
	padding: clamp(2.25rem, 2rem + 1.5vw, 3.25rem) 1.5rem;
	display: grid;
	gap: 2rem;
	grid-template-columns: 1.4fr repeat(3, 1fr);
}
/* Two files, same as the header: the letter colours are baked into the artwork, so a footer
   that follows the theme has to swap the file. The footer used to be permanently dark and
   hardcoded the white-lettered one, which would be invisible on a light footer. */
/* The badge is a 300x300 source scaled down here rather than resized on disk. It carries its own
   opaque white disc, so unlike the wordmark it needs no light/dark pair. */
.foot-brand img { display: block; width: 225px; height: auto; }
.foot-brand p { font-size: .91rem; max-width: 34ch; }

/* Taken out of flow so the badge does not set the row height. The band stays the height the link
   columns give it and the badge overflows into the padding, which is the point - it should read as
   larger than the columns rather than stretch the band to fit itself.
   Only above the 940px breakpoint, where the brand still has a column of its own. Below that the
   footer stacks and an absolute badge would sit on top of the columns. */
@media (min-width: 941px) {
	.foot-brand { position: relative; }
	.foot-brand img { position: absolute; top: 50%; transform: translateY(-50%); }
}
.foot-col .title {
	font-size: .74rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
	color: var(--on-band-strong); margin-bottom: .85rem;
}
.foot-col ul { list-style: none; display: grid; gap: .5rem; }
.foot-col a { color: var(--on-band-mute); font-size: .92rem; }
.foot-col a:hover { color: var(--on-band-strong); }
.foot-bottom { border-top: 1px solid var(--on-band-hairline); background: var(--band-2); }
.foot-bottom-inner {
	max-width: var(--maxw);
	margin: 0 auto;
	padding: 1.1rem 1.5rem;
	display: flex; flex-wrap: wrap; gap: .75rem 1.5rem;
	align-items: center; justify-content: space-between;
	font-size: .85rem;
}
.foot-bottom-inner .legal-links { display: flex; flex-wrap: wrap; gap: 1rem; }

/* ------------------------------------------------------------------ misc */
.visually-hidden {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
	position: absolute; left: 1rem; top: -3rem; z-index: 200;
	background: var(--accent-fill); color: #fff;
	padding: .6rem 1rem; border-radius: 0 0 8px 8px;
	transition: top .15s ease;
}
.skip-link:focus { top: 0; text-decoration: none; }

/* Reveal on scroll. Opt in per element with .reveal; js/site.js adds .seen when it enters
   the viewport. Anything without JS is simply visible - see the .no-js rule in site.js's
   companion block below, and note the reduced-motion override at the bottom of this file.
   The animation is never load-bearing. */
.reveal { opacity: 0; transform: translateY(16px); transition: opacity .55s ease, transform .55s ease; }
.reveal.seen { opacity: 1; transform: none; }

/* ------------------------------------------------------------------ responsive */
@media (max-width: 1040px) {
	.hero-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 340px); }
}
@media (max-width: 940px) {
	.nav {
		position: absolute;
		top: 100%; left: 0; right: 0;
		flex-direction: column;
		align-items: stretch;
		gap: .15rem;
		/* The dropdown is part of the header, so it takes the header's colour - otherwise a dark
		   bar unrolls a white panel, which is the exact collision this change removes.

		   OPAQUE, not --menu-bg. The bar can be translucent because it is a thin strip over
		   whatever scrolls beneath it, but this panel covers half the screen and its 5% of
		   bleed-through put a legible ghost of the page heading behind the nav items - 5% of
		   white is plenty against a near-black. It was solid before this change and stays solid. */
		background: var(--menu-solid);
		border-bottom: 1px solid var(--head-line);
		box-shadow: var(--shadow-lg);
		padding: .6rem 1rem 1rem;
		display: none;
	}
	.nav.open { display: flex; }
	.nav a { padding: .7rem .8rem; font-size: 1rem; }
	.nav a.active::after { display: none; }
	.nav .nav-cta { margin-left: 0; margin-top: .4rem; justify-content: center; display: flex; }
	.menu-btn { display: inline-flex; }
	.site-head .bar { position: relative; }
	/* Art below the copy, centered and smaller. It stays - it is the logo, and dropping the
	   logo on phones to save layout trouble is the wrong trade. */
	.hero-inner { grid-template-columns: 1fr; }
	.hero-art { max-width: 320px; justify-self: center; order: 2; }
	.foot-top { grid-template-columns: 1fr 1fr; }
	.cap-list { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
	body { font-size: 16px; }
	.foot-top { grid-template-columns: 1fr; gap: 1.5rem; }
	.btn { width: 100%; justify-content: center; }
	.btn-row { flex-direction: column; }
	.hero-art { max-width: 250px; }
}
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
	}
	.btn:hover { transform: none; }
	/* The reveal must not be left mid-fade when its transition is neutered. */
	.reveal { opacity: 1; transform: none; }
	/* And the pulses must not freeze as a stripe halfway down a trace. */
	.circuit .pulses { display: none; }
	/* Same for the card beam - neutering the animation parks it at 360deg, which is 0deg, so
	   it would sit as a permanent red arc on one corner of every card. */
	.section > .container::before { display: none; }
	/* Same reasoning for the header separator - a frozen lit segment is a stray orange smear
	   sitting on the line. The red separator itself stays; only the travelling part goes. */
	.site-head::after { display: none; }
	.circuit .nodes circle { opacity: .55; }
}
@media print {
	/* Somebody reading a legal document in dark mode and hitting Ctrl+P was getting white
	   headings on white paper. Print always uses the light palette, whatever the screen shows. */
	:root, html[data-theme="dark"] {
		color-scheme: light;
		--page-bg: #FFFFFF;
		--surface: #FFFFFF;
		--panel: #F2F2F2;
		--border: #CCCCCC;
		--border-strong: #999999;
		--text: #1A1A1A;
		--text-strong: #000000;
		--text-muted: #333333;
		--text-faint: #555555;
		--accent: #AB2D2D;
		--accent-fill: #AB2D2D;
		--focus: #AB2D2D;
		--accent-ink: #8B2525;
		/* The band tokens too, or a page printed while dark mode is active puts a solid black
		   slab where the sub-page hero is. */
		--band: #F2F2F2;
		--band-2: #E8E8E8;
		--on-band: #1A1A1A;
		--on-band-strong: #000000;
		--on-band-mute: #333333;
		--on-band-faint: #555555;
		--bloom: transparent;
	}
	.site-head, .site-foot, .legal-nav, .skip-link { display: none !important; }
	.prose { border: 0; box-shadow: none; padding: 0; max-width: none; }
	/* The cards are a screen device. On paper they waste a margin and print a grey box. */
	.section > .container { border: 0; box-shadow: none; border-radius: 0; padding: 0; background: none; }
	.section > .container::before { display: none; }
	.section { padding-inline: 0; }
	body { background: #fff; color: #000; font-size: 11pt; }
	.reveal { opacity: 1 !important; transform: none !important; }
}
