/* ═══════════════════════════════════════════════════════════════════════════
   Raylux — marketing site, Daylight (RAYLUX-UI-DESIGN-LANGUAGE-V2, ADR-170)

   Daylight is the default; Night follows the visitor's system setting (and a
   saved choice, see theme.js). Tokens are the spec's section 3.1 values.
   Geist and Geist Mono are self-hosted (OFL-1.1, /licenses/fonts/): no font
   CDN, so the CSP's style-src and font-src stay 'self'.

   WHAT IS DELIBERATE, so a future edit does not undo it:
     - Radius 8 / 12 / 18 / pill, one soft shadow, translucent masthead.
     - The beam (--beam) is for brand moments only: the hero rule, the Iris
       entry point, the recommended choice. Never status, never a chart pen.
       It is drawn still under prefers-reduced-motion.
     - Tabular numerals on every figure in a column.
     - Alarm and chart colors are not Daylight tokens and do not change.

   Contrast: the spec's --raylux-muted (#868C9B) measures 3.2:1 on the page
   backdrop, below the 4.5:1 floor this site holds for text (WCAG 1.4.3), and
   it is used here as caption and table-header text. --muted below is the same
   hue one step darker (Night's #707685 already clears it on #0A0B0E).
   ═══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: "Geist";
  src: url("/assets/fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --f-ui: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --f-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Consolas, monospace;

  --bg: #f4f5f7;
  --paper: #ffffff;
  --surface-2: #ffffff;
  --sunken: #ecedf2;
  --fill: #eceef2;
  --input: #ffffff;
  --border: #e0e3e9;
  --border-subtle: #eceef2;
  --border-strong: #d3d7de;
  --rule: #0c0e13;
  --text: #1b2030;
  --text-bright: #0c0e13;
  --secondary: #4b5262;
  --muted: #5e6575;
  --accent: #1f5fff;
  --accent-fill: #1f5fff;
  --accent-hover: #1a50db;
  --accent-soft: #e7eeff;
  --accent-wash: rgba(31, 95, 255, 0.08);
  --accent-ink: #ffffff;
  --link: #1f5fff;
  --success: #17795a;
  --warning: #f57c00;
  --error: #e12d39;
  --error-text: #c9202c;
  --hover: rgba(12, 14, 19, 0.04);
  --band: #ffffff;
  /* Chart series (the pricing over-time chart): checked against --bg/--band. */
  --series-current: #d9531e;
  --series-raylux: #1f5fff;
  /* A form control's edge has to clear 3:1 on its fill (WCAG 1.4.11). */
  --input-border: #7d8794;

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-full: 999px;
  --shadow: 0 1px 2px rgb(12 14 19 / 0.05), 0 10px 30px rgb(12 14 19 / 0.07);
  --shadow-lg: 0 2px 4px rgb(12 14 19 / 0.05), 0 30px 60px rgb(12 14 19 / 0.12);
  --glass: rgba(255, 255, 255, 0.72);
  --beam: linear-gradient(90deg, #cb1eff, #2a42ff 16%, #15e4ff 32%, #06aa00 48%, #fff805 64%, #ff5502 80%, #ff0000);
}

/* Night. Alarm colors do NOT change between themes (HMI color standard). */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0a0b0e;
  --paper: #13151a;
  --surface-2: #1a1d24;
  --sunken: #0a0b0e;
  --fill: #1d2027;
  --input: #0a0b0e;
  --border: #262a33;
  --border-subtle: #1d2027;
  --border-strong: #323743;
  --rule: #f1f2f5;
  --text: #d8dbe2;
  --text-bright: #f1f2f5;
  --secondary: #a9aebb;
  --muted: #8b91a0;
  --accent: #5c8dff;
  --accent-fill: #3b6fe8;
  --accent-hover: #7aa2ff;
  --accent-soft: #18213a;
  --accent-wash: rgba(92, 141, 255, 0.12);
  --accent-ink: #06080d;
  --link: #7aa2ff;
  --success: #3bd186;
  --error-text: #ff7b7b;
  --hover: rgba(255, 255, 255, 0.05);
  --band: #13151a;
  --series-current: #e86a36;
  --series-raylux: #5c8dff;
  --input-border: #6b7280;
  --shadow: 0 1px 2px rgb(0 0 0 / 0.4), 0 10px 30px rgb(0 0 0 / 0.35);
  --shadow-lg: 0 2px 4px rgb(0 0 0 / 0.4), 0 30px 60px rgb(0 0 0 / 0.5);
  --glass: rgba(26, 29, 36, 0.72);
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
}

/* 16px base, not the console's 13px. App density is why the old page felt like
   a tool rather than something to read. */
body {
  min-height: 100vh;
  margin: 0;
  color: var(--text);
  background: var(--bg);
  font-family: var(--f-ui);
  font-size: 16px;
  line-height: 1.6;
  overflow-wrap: break-word;
}

button,
input {
  font: inherit;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

a {
  color: var(--link);
}

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

/* A placeholder is a hint, never a value: muted and italic, so an example
   company name cannot be mistaken for something already filled in. */
::placeholder {
  color: var(--muted);
  font-style: italic;
  opacity: 1;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  padding: 10px 16px;
  color: #ffffff;
  background: var(--accent-fill);
  font-weight: 700;
}

.skip-link:focus {
  left: 0;
}

h1:focus {
  outline: none;
}

/* A spec table's value column has no visible header — the label column already
   says what the table is. Screen readers still need one. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Every measured quantity is monospace and tabular — a port, a version, a
   poll rate, a level. Prose is not. That split is the datasheet's whole
   typographic argument, so keep it. */
.mono,
code,
.spec td:last-child,
.figure figcaption b {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------- page frame ---------- */

.wrap {
  width: min(1180px, 100% - 48px);
  margin-inline: auto;
}

/* ---------- masthead ----------
   A document header, not an app topbar: wordmark, contents, status. */

.masthead {
  border-bottom: 1px solid var(--border);
  background: var(--paper);
}

/* ROW ONE — identity and utilities. The nav is NOT here; it is the contents
   rule below. A document masthead states who published it, then lists what is
   inside. That is the same move every page already makes with an eyebrow above
   a heading, so the header stops looking like an app topbar bolted onto a
   datasheet. */
.masthead-in {
  display: flex;
  align-items: center;
  gap: 24px;
  min-height: 56px;
}

/* ROW TWO — the contents rule. The border lives on the full-width wrapper so
   it spans the viewport like the masthead's own bottom border, rather than
   stopping at the .wrap gutter. */
.masthead-rule {
  border-top: 1px solid var(--border);
}

.masthead-contents {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 42px;
}

.contents-label {
  margin-right: 12px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Mobile only. Hidden until main.js marks the header enhanced — see the
   .is-enhanced note in the media query at the bottom of this section. */
.nav-burger {
  display: none;
  /* Never the thing that gives way on a narrow row — without this it was
     squeezed to ~22px wide at 360px (found 2026-09-25 enlarging the logo). */
  flex: 0 0 auto;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  /* 44px: the smallest target a gloved hand on a plant-floor laptop can hit
     reliably (WCAG 2.5.5). Was 40. */
  width: 44px;
  height: 44px;
  margin-left: auto;
  padding: 0 11px;
  background: none;
  border: 1px solid var(--border-strong);
  cursor: pointer;
}

.nav-burger i {
  display: block;
  height: 1.5px;
  background: var(--text-bright);
  transition: transform 140ms ease, opacity 140ms ease;
}

.nav-burger[aria-expanded="true"] i:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.nav-burger[aria-expanded="true"] i:nth-child(2) { opacity: 0; }
.nav-burger[aria-expanded="true"] i:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Shown only inside the mobile drawer. */
.nav-group-label,
.masthead-drawer-foot {
  display: none;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* The phone logo is 32px tall; the link around it is a 44px target. */
  min-height: 44px;
  margin: 0;
  color: var(--text-bright);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
}

.wordmark span {
  color: var(--muted);
  white-space: nowrap;
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.masthead nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

/* 44px tall (was ~33): the contents row grows from 42px to 44px, and the
   current page's underline sits on the row's foot like a tab. */
.masthead nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 10px;
  color: var(--secondary);
  font-size: 14px;
  text-decoration: none;
  border-bottom: 2px solid transparent;
}

.masthead nav a:hover {
  color: var(--text-bright);
}

.masthead nav a[aria-current="page"] {
  color: var(--text-bright);
  border-bottom-color: var(--accent);
}

/* ---------- products disclosure ----------
   Four product pages behind one nav item. The bar was ten items and wrapped to
   two rows before the real logo has even been placed, and the four component
   names are the obvious group: they are the only items that are parts of one
   product rather than sections of one site.

   NO-JAVASCRIPT PATH: without main.js the button does nothing, so the four
   product pages would be unreachable FROM THE BAR. They stay reachable because
   the site footer lists all four on every page — that is why the footer nav is
   not trimmed to match this one. Check the footer before removing a link from
   it.

   The panel is a plain absolutely-positioned block rather than a <dialog> or a
   popover: it has to sit under a 68px bar on a page that scrolls, and the
   simplest thing that does that has no failure mode when scripting is off. */
.nav-group {
  position: relative;
  display: inline-flex;
}

.nav-disclosure {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 0 10px;
  color: var(--secondary);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.nav-disclosure:hover,
.nav-disclosure[aria-expanded="true"] {
  color: var(--text-bright);
}

/* A product page is "current" at the group, since the item itself is inside. */
.nav-disclosure[data-current="true"] {
  color: var(--text-bright);
  border-bottom-color: var(--accent);
}

/* Rotates on open. Drawn rather than a glyph so it inherits color and needs
   no icon font. */
.nav-disclosure::after {
  content: "";
  width: 6px;
  height: 6px;
  margin-top: -3px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 120ms ease;
}

.nav-disclosure[aria-expanded="true"]::after {
  margin-top: 2px;
  transform: rotate(-135deg);
}

.nav-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  z-index: 40;
  min-width: 260px;
  padding: 6px;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: 0 12px 28px rgba(13, 21, 33, 0.14);
}

:root[data-theme="dark"] .nav-menu {
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
}

/* Overrides .masthead nav a — these are rows in a panel, not items in a bar. */
.masthead .nav-menu a {
  display: block;
  padding: 9px 12px;
  border-bottom: 0;
  color: var(--text);
  font-size: 14px;
}

.masthead .nav-menu a:hover,
.masthead .nav-menu a:focus-visible {
  color: var(--text-bright);
  background: var(--sunken);
}

.masthead .nav-menu a[aria-current="page"] {
  color: var(--text-bright);
  font-weight: 600;
  background: var(--sunken);
}

.nav-menu b {
  display: block;
  font-size: 14px;
  font-weight: 600;
}

.nav-menu span {
  display: block;
  margin-top: 2px;
  color: var(--secondary);
  font-size: 12.5px;
  line-height: 1.45;
}

/* ---------- masthead logo slot ----------
   The approved Raylux logo (lettering + star + rainbow beam, approved
   2026-09-25; source kit is Raylux-SCADA brand/logo/) REPLACES the word
   "Raylux" in the <a class="wordmark"> on every page, so each img carries
   alt="Raylux" — it is the company name, not decoration beside it.

   Two imgs, one per theme: raylux-logo-light.svg (black lettering, for the
   light default) and raylux-logo-dark.svg (white lettering). Switched on
   :root[data-theme="dark"] — NOT prefers-color-scheme — because theme.js
   writes data-theme from the saved toggle choice, and the toggle must win
   over the OS. display:none also drops the hidden one from the accessibility
   tree, so a screen reader says "Raylux" once.

   width/height attributes (216x54, the artwork's ~4:1) reserve the box before
   the SVG loads: no layout shift. 54px fills the 56px row without growing it
   (160x40 read as too small on the live site, 2026-09-25). Below 900px the
   CSS scales it to 160x40, and to 128x32 at 440px and under —
   same ratio, so the reserved box still matches, and the logo, the
   "SCADA / HMI" tag (hidden at 400px and under, below), the theme toggle and
   the burger stay on one 56px row down to 320px. Change the attributes and every size together. Bump ?v= when either
   file changes. */
.wordmark-logo {
  display: block;
  width: 216px;
  height: 54px;
  flex: 0 0 auto;
  object-fit: contain;
}

.wordmark-logo-dark,
:root[data-theme="dark"] .wordmark-logo-light {
  display: none;
}

:root[data-theme="dark"] .wordmark-logo-dark {
  display: block;
}

/* Product symbols: black star on Daylight, white star on Night (same swap as the wordmark). */
:root:not([data-theme="dark"]) .sym-dark,
:root[data-theme="dark"] .sym-light {
  display: none;
}

/* Product pages: the product's own symbol (the star with its single-color
   halo — Nexus violet, Studio blue, Spectrum magenta, Lens teal) takes the
   place of the status square in the hero's designation line. Only the
   white-star ("dark") artwork ships: .hero-feature is a fixed navy panel in
   BOTH themes, so a theme switch would put a black star on navy. alt="" —
   the h1 and the line itself already name the product. */
@media (max-width: 900px) {
  .wordmark-logo {
    width: 160px;
    height: 40px;
  }
}

/* Phones (440px and under): 128x32, so logo, tag, theme toggle and burger
   still fit one row at 401-440px, where the tag is still shown. */
@media (max-width: 440px) {
  .wordmark-logo {
    width: 128px;
    height: 32px;
  }
}

/* 400px and under: a tighter gap between logo and tag. */
@media (max-width: 400px) {
  .wordmark {
    gap: 8px;
  }
}

/* 400px and under (every phone): with the 128x32 logo the row needs ~331px
   for logo, tag, theme toggle and burger — 4px more than a 390px phone has,
   and 18px more than 360. The tag is the
   thing that gives way — visually hidden, since the logo still names the
   company, and kept in the accessibility tree. (Until 2026-09-25 this was
   340px, and the burger silently squeezed to ~22px wide at 360 instead.)
   A separate block from the 400px one above only so each keeps its reason. */
@media (max-width: 400px) {
  .wordmark span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

.eyebrow-product::before {
  display: none;
}

.product-symbol {
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  margin: -8px 2px -8px -4px;
}

/* The release stated as a document status line. It belongs to the datasheet
   language rather than being a marketing banner bolted on. */
.status-line {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.status-line::before {
  content: "";
  width: 7px;
  height: 7px;
  background: var(--warning);
}

/* The masthead's version line is a released-and-running status, so its
   square is the console's healthy green rather than the amber the other
   status-line labels (eyebrows, form labels) keep. --success is themed. */
.masthead-utility .status-line::before {
  background: var(--success);
}

/* ---------- theme toggle and sign-in: the masthead's two small boxes ----------
   Each is a 44px-tall target (WCAG 2.5.5; gloves on field laptops) whose
   visible box is drawn by ::before, inset 5px top and bottom, so the bar
   looks as it did (a ~34px mono box) while the hit area is the full 44.
   isolation + z-index -1 keeps the box behind the label. Hover and focus act
   on the drawn box; the focus ring sits just outside it. */
.theme-toggle,
.signin {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 10px;
  background: transparent;
  border: 0;
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.theme-toggle::before,
.signin::before {
  content: "";
  position: absolute;
  inset: 5px 0;
  z-index: -1;
  border: 1px solid var(--border-strong);
}

.theme-toggle:focus-visible,
.signin:focus-visible {
  outline-offset: -3px;
}

/* One pattern (2026-10-02): the label names what a press does ("Dark" in the
   light theme, "Light" in the dark), and main.js sets aria-label "Switch to
   dark theme" / "Switch to light theme" to match. No aria-pressed: a pressed
   state under a label naming the other theme said two different things. */
.theme-toggle {
  color: var(--secondary);
  cursor: pointer;
}

.theme-toggle:hover {
  color: var(--text-bright);
}

.theme-toggle:hover::before {
  border-color: var(--accent);
}

/* ---------- partner sign-in ----------
   Sits in the masthead's utility cluster with the status line and the theme
   toggle rather than in <nav>, because it is an action on a different host
   (portal.rayluxscada.com) and not a section of this site. Same bordered mono
   box as .theme-toggle so the cluster reads as one group; accent-colored
   because it is the only thing in that group anyone is meant to click on
   purpose.

   Deliberately NOT target="_blank": this is the start of a sign-in flow, and
   dropping a partner into a detached tab is how you end up authenticated in a
   window with no history back to where you were. */
.signin {
  /* Text use of --accent fails in light mode (3.43:1); --link is themed
     correctly per-palette (light: 5.36:1, dark: 6.50:1; dark --link IS dark
     --accent, so dark mode is unchanged). --accent-fill would have fixed
     light but broken dark (its dark value is only 3.69:1 as text). The box
     stays --accent: borders only need the 3:1 UI threshold, which --accent
     clears in both palettes. Other text uses of the accent point here. */
  color: var(--link);
  text-decoration: none;
}

.signin::before {
  border-color: var(--accent);
}

.signin:hover {
  color: #ffffff;
}

.signin:hover::before {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
}

/* The masthead's right-hand group. Keeps the status line, sign-in and theme
   toggle together when the bar wraps, instead of the three of them breaking
   apart onto separate rows.

   margin-left:auto is what pushes it to the right edge. It used to be a
   margin-right:auto on .masthead nav, which stopped working the moment the nav
   moved down to the contents rule and left the utilities bunched against the
   wordmark. */
.masthead-utility {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  margin-left: auto;
  flex-wrap: wrap;
}

/* ---------- masthead, small screens ----------
   The old header wrapped to three stacked blocks and stood 252px tall on a
   375x812 phone — 31% of the screen before a single word of content, with no
   way to collapse it. Below 900px the contents rule becomes a drawer behind
   one button and the bar lands at 56px.

   PROGRESSIVE ENHANCEMENT, and the direction matters: the drawer is OPEN by
   default in CSS and main.js collapses it by adding .is-enhanced. With
   scripting off the header is tall but every link works, which is the failure
   we want. Hiding it by default and revealing it with JS would leave a
   no-script phone with a burger that does nothing.

   The theme toggle deliberately STAYS in the top bar rather than moving into
   the drawer: it is the only way to change theme, and burying a control that
   changes the whole page behind a menu is worse than the 40px it costs. The
   status line and sign-in do move — sign-in reappears at the foot of the
   drawer as a full-width target. */
@media (max-width: 900px) {
  .masthead-in {
    gap: 12px;
    min-height: 56px;
  }

  .masthead-utility {
    margin-left: auto;
    gap: 10px;
  }

  /* Long text and a duplicate of the drawer's sign-in. Hidden rather than
     removed so exactly one of the two sign-in links is in the accessibility
     tree at a time. */
  .masthead-utility .status-line,
  .masthead-utility .signin {
    display: none;
  }

  .masthead.is-enhanced .nav-burger {
    display: inline-flex;
  }

  .masthead.is-enhanced .masthead-utility {
    margin-left: auto;
  }

  .masthead.is-enhanced .nav-burger {
    margin-left: 0;
  }

  .masthead-rule {
    border-top: 0;
  }

  .masthead-contents {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-top: 6px;
    padding-bottom: 14px;
    border-top: 1px solid var(--border);
  }

  .masthead.is-enhanced .masthead-contents {
    display: none;
  }

  .masthead.is-enhanced .masthead-contents.is-open {
    display: flex;
  }

  .contents-label {
    display: none;
  }

  .masthead-contents nav {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .masthead-contents nav > a {
    padding: 12px 2px;
    font-size: 15px;
    border-bottom: 0;
  }

  .masthead-contents nav > a[aria-current="page"] {
    font-weight: 600;
    border-bottom: 0;
  }

  /* Products is a flat labeled group in the drawer, not a second disclosure.
     Four product pages behind two taps is worse than a slightly longer list. */
  .nav-group {
    flex-direction: column;
    align-items: stretch;
  }

  .nav-disclosure {
    display: none;
  }

  .nav-group-label {
    display: block;
    margin: 10px 0 2px;
    color: var(--muted);
    font-family: var(--f-mono);
    font-size: 12px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  /* Overrides the [hidden] main.js sets for the desktop dropdown — in the
     drawer these links are always shown. */
  .nav-menu,
  .nav-menu[hidden] {
    display: block;
    position: static;
    min-width: 0;
    padding: 0;
    border: 0;
    box-shadow: none;
    background: none;
  }

  .masthead .nav-menu a {
    padding: 11px 2px 11px 16px;
    font-size: 15px;
  }

  .masthead .nav-menu a:hover,
  .masthead .nav-menu a[aria-current="page"] {
    background: none;
  }

  .nav-menu b {
    font-weight: 400;
  }

  .masthead .nav-menu a[aria-current="page"] b {
    font-weight: 600;
  }

  /* Descriptions earn their place in a desktop dropdown and only add height
     in a drawer where the reader has already committed to browsing. */
  .nav-menu span {
    display: none;
  }

  .masthead-drawer-foot {
    display: block;
    margin-top: 12px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
  }

  .masthead-drawer-foot .signin {
    display: flex;
    min-height: 46px;
    padding: 0 12px;
    font-size: 12px;
  }

  /* Full-width in the drawer: the drawn box is the whole target. */
  .masthead-drawer-foot .signin::before {
    inset: 0;
  }
}

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

.hero {
  padding: 88px 0 64px;
  border-bottom: 2px solid var(--rule);
}

.hero h1 {
  max-width: 18ch;
  margin: 0 0 24px;
  color: var(--text-bright);
  font-size: clamp(40px, 6.2vw, 76px);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.02em;
}

.hero-lede {
  max-width: 62ch;
  margin: 0 0 32px;
  color: var(--secondary);
  font-size: clamp(17px, 1.7vw, 20px);
  line-height: 1.55;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

/* ---------- home page: the feature hero ----------
   The one dark band on the site, and only on the home page — every other
   page keeps the light instrument face, and the compliance pages in
   particular must not change character. It is a control-room panel set
   into the datasheet: a fixed navy in BOTH themes (so it reads as a panel,
   not as "dark mode"), a slow trend grid behind the copy, and a trend
   faceplate beside the headline.

   Colors here are fixed rather than themed on purpose. Measured on
   --hero-bg (#0d1a2b): --hero-text #e8edf4 is 15.4:1, --hero-secondary
   #aeb9c8 is 9.0:1, --hero-muted #93a1b3 is 6.6:1 — all clear 4.5:1. */
.hero-feature {
  --hero-bg: #0d1a2b;
  --hero-text: #e8edf4;
  --hero-secondary: #aeb9c8;
  --hero-muted: #93a1b3;
  --hero-line: rgba(147, 161, 179, 0.28);
  --hero-pen: #4a9eff;
  position: relative;
  padding: 84px 0 60px;
  color: var(--hero-text);
  background: var(--hero-bg);
  border-bottom: 0;
}

.hero-grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 380px);
  gap: 32px 56px;
  align-items: center;
}

.hero-grid .proof {
  grid-column: 1 / -1;
  margin-top: 20px;
}

.hero-feature h1 {
  color: #ffffff;
  font-size: clamp(40px, 5vw, 64px);
  font-weight: 700;
}

.hero-feature .hero-lede {
  color: var(--hero-secondary);
}

.hero-feature .hero-trust li,
.hero-feature .proof-label {
  color: var(--hero-muted);
}

.hero-feature .hero-trust li::before {
  background: var(--hero-pen);
}

.hero-feature .proof li {
  border-top-color: var(--hero-line);
}

.hero-feature .proof-value {
  color: #ffffff;
}

/* On navy the quiet button's --text-bright/--border-strong would vanish in
   dark mode and glare in light, so it gets the panel's own pair. */
.hero-feature .button-quiet {
  color: var(--hero-text);
  border-color: rgba(232, 237, 244, 0.45);
}

.hero-feature .button-quiet:hover {
  color: #ffffff;
  border-color: var(--hero-pen);
}

.hero-feature :focus-visible {
  outline-color: #ffffff;
}

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

}

/* ---------- landing page: the argument ----------
   The home page has to sell, and these are the components that let it. They
   stay inside the datasheet grammar on purpose: a key-figures band is what an
   instrument catalog prints on its first page, and a "conventional stack"
   comparison is a spec table with an opinion. No cards, no radius, no shadow,
   no gradient — the persuasion is carried by the claims and the figures, not
   by chrome. If that reads as restrained next to a SaaS landing page, good;
   the buyer is a controls engineer reading a datasheet.

   EVERY figure these components display is already stated elsewhere on this
   site — /pricing, /platforms, /downloads. They are a summary, never a new
   claim. If one of those pages changes its numbers, change them here too.

   No competitor is named anywhere, deliberately. The comparison below is
   against the conventional way a SCADA stack is assembled and licensed, which
   is an industry norm rather than any one vendor's price list. Do not "sharpen
   it up" by putting a product name in it. */

/* Terms stated under the hero buttons, where the objection actually forms,
   instead of being left for the visitor to find on /pricing. */
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}

.hero-trust li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
}

/* The same 7px square the masthead status line uses, so a stated term reads
   as the same kind of object as a stated status. */
.hero-trust li::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  /* text-adjacent use of --accent; see .signin above. */
  background: var(--link);
}

/* A band of headline figures. Each sits under its own heavy rule rather than
   between vertical dividers, because that survives wrapping to two rows
   without the separators landing in the wrong places. */
.proof {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 30px 44px;
  margin: 44px 0 0;
  padding: 0;
  list-style: none;
}

.proof li {
  padding-top: 14px;
  border-top: 2px solid var(--rule);
}

/* Mono and tabular: these are measured quantities and prices, which is
   exactly the split the typography rule near the top of this file draws. */
.proof-value {
  display: block;
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: clamp(27px, 3.3vw, 35px);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* A unit set beside a figure, the way an instrument label prints one. Mono
   gives every character the same advance, so a plain space between "38" and
   "MB" opens to a full character width at this size and the two read as two
   separate figures. The unit carries its own tighter gap instead — write it
   with NO space in the markup: 38<span class="proof-unit">MB</span>. */
.proof-unit {
  margin-left: 0.22em;
  font-size: 0.62em;
  letter-spacing: 0.02em;
}

.proof-label {
  display: block;
  margin-top: 9px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}

/* The licensing line on a row of the module index — what this part costs or
   why it does not, said where the part is described. A catalog prints the
   price next to the part number; so does this. */
.module-terms {
  display: block;
  margin-top: 10px;
  /* text use of --accent; see .signin above. */
  color: var(--link);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
}

/* A two-way comparison: how a SCADA stack is conventionally assembled, beside
   what Raylux does instead. Deliberately NOT built on .spec-compare — that one
   is sized for three tier columns and forces a 720px scroller on small
   screens, which is far more room than two columns need. It repeats the two
   overrides it needs rather than inheriting them, so neither table can break
   the other later.

   Nothing in these cells is a measured quantity, so nothing in them is mono —
   same reasoning as .spec-compare. */
.spec-versus td:last-child,
.spec-versus td:nth-child(2) {
  font-family: inherit;
  font-variant-numeric: normal;
}

.spec-versus td:first-child {
  width: 22%;
}

.spec-versus th:not(:first-child),
.spec-versus td:not(:first-child) {
  width: auto;
  padding-right: 24px;
}

.spec-versus th:last-child,
.spec-versus td:last-child {
  padding-right: 0;
}

/* The conventional column is reported; the Raylux column is the answer. The
   weight difference is the whole point of the table, so it is typographic
   rather than a colored panel — a filled column would be the card this
   design does not have. */
.spec-versus td:nth-child(2) {
  color: var(--muted);
}

.spec-versus td:last-child {
  color: var(--text-bright);
}

.spec-versus th:last-child {
  /* text use of --accent; see .signin above. */
  color: var(--link);
}

@media (max-width: 900px) {
  /* Two value columns need far less than .spec-compare's 720px before they
     stop fitting, so the scroller starts much later. */
  .spec-versus {
    min-width: 560px;
  }
}

/* On a phone even 560px hides the Raylux column — the one that makes the
   argument — behind a sideways scroll. Stack each row instead: the item,
   then the two answers labeled. The labels repeat the column headers. */
@media (max-width: 600px) {
  .spec-versus {
    min-width: 0;
  }

  .spec-versus thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .spec-versus tr,
  .spec-versus td {
    display: block;
    width: auto !important;
    padding-right: 0 !important;
  }

  .spec-versus tr {
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
  }

  .spec.spec-versus tbody td {
    padding: 2px 0;
    border: 0;
  }

  .spec-versus td:nth-child(2)::before,
  .spec-versus td:last-child::before {
    display: block;
    font-family: var(--f-mono);
    font-size: 12px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .spec-versus td:nth-child(2)::before {
    content: "Conventional stack";
  }

  .spec-versus td:last-child::before {
    content: "Raylux";
    margin-top: 6px;
    color: var(--link);
  }
}

/* The closing ask, as a solid band in the accent. This is the one colored
   slab on the site, and it is earned: it is the last thing before the
   footer and the only place the page asks for anything outright. White on
   --accent-fill is 5.75:1 (light) and 4.85:1 (dark). Text here stays SOLID
   white: 90% white drops the dark pair to 4.25:1, under the 4.5:1 floor. */
.cta-band {
  padding: 68px 0 74px;
  color: #ffffff;
  background: var(--accent-fill);
}

.cta-band .cta-eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.cta-band .cta-eyebrow::after {
  content: "";
  width: 40px;
  height: 2px;
  background: #ffffff;
}

.cta-band h2 {
  max-width: 21ch;
  margin: 0 0 18px;
  color: #ffffff;
  font-size: clamp(30px, 4vw, 48px);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -0.02em;
}

.cta-band p {
  max-width: 60ch;
  margin: 0 0 30px;
  color: #ffffff;
  font-size: 17px;
  line-height: 1.55;
}

.cta-band .button {
  color: var(--accent-fill);
  background: #ffffff;
  border-color: #ffffff;
}

.cta-band .button:hover {
  color: #ffffff;
  background: transparent;
}

.cta-band .button-quiet {
  color: #ffffff;
  background: transparent;
  border-color: rgba(255, 255, 255, 0.6);
}

.cta-band .button-quiet:hover {
  border-color: #ffffff;
}

.cta-band .hero-trust li {
  color: #ffffff;
}

.cta-band .hero-trust li::before {
  background: #ffffff;
}

.cta-band :focus-visible {
  outline-color: #ffffff;
}

/* The band is its own edge; the footer's heavy rule under it read as a stray
   hairline (and as a bright bar in dark mode). */
main:has(> .cta-band:last-child) + .site-footer {
  border-top: 0;
}

/* ---------- section numbering ----------
   Sections are numbered like clauses in a datasheet. The number is content,
   not decoration, so it lives in the markup. */

.section {
  padding: 72px 0;
  border-bottom: 1px solid var(--border);
}

.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  /* baseline, not end: with a long side note, end-alignment dropped the h2
     to the note's last line and left a gap under the eyebrow. */
  align-items: baseline;
  gap: 12px 32px;
  margin-bottom: 36px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--rule);
}

/* The clause number, set as an eyebrow over the heading with a short rule
   after it — the label-then-headline order a panel nameplate uses. */
.section-no {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 12px;
  /* text use of --accent; see .signin above. */
  color: var(--link);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
}

.section-no::after {
  content: "";
  width: 40px;
  height: 2px;
  background: var(--accent);
}

.section-head h2 {
  margin: 0;
  color: var(--text-bright);
  font-size: clamp(27px, 3.4vw, 42px);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
}

.section-head p {
  margin: 0;
  max-width: 46ch;
  color: var(--muted);
  font-size: 14px;
}

/* Home page rhythm: every other section sits on the paper tone, so the long
   page reads as a run of distinct panels rather than one gray sheet. */
.config-hint.config-hint-gap {
  margin-top: 18px;
}

.section-alt {
  background: var(--band);
}

/* /news: release sections are generated by tools/build-news.py, so their
   alternation is here rather than a class the generator would overwrite.
   The hero is the first section, so odd-of-type = the 2nd, 4th... release. */
main > section[aria-labelledby^="rel-"]:nth-of-type(odd) {
  background: var(--band);
}

/* A heading's own rule already separates it from what follows, so a block
   that opens with a rule of its own would draw a second one 36px lower. */
.section-head + .modules,
.section-head + .clause {
  border-top: 0;
}

/* ---------- spec tables ----------
   The workhorse. Label left, value right, hairline between rows. No zebra
   striping, no rounded container, no card. */

.spec {
  width: 100%;
  border-collapse: collapse;
}

.spec th {
  padding: 0 0 10px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: left;
  border-bottom: 1px solid var(--rule);
}

.spec td {
  padding: 13px 0;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.spec td:first-child {
  width: 34%;
  padding-right: 24px;
  color: var(--text-bright);
  font-weight: 600;
}

.spec td:last-child {
  color: var(--secondary);
  font-size: 15px;
}

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

/* A COMPARISON table — one row per capability, one column per tier. Not the
   same object as a two-column spec table, and it has to undo two rules that
   are correct there and wrong here:

   1. `.spec td:last-child` is monospace because in a two-column table the last
      column IS the measured value. Across three tier columns the last one is
      prose like every other, and monospacing only the rightmost column makes
      it read as a different kind of thing. The datasheet split in styles.css
      line ~150 stays intact — nothing in these cells is a measured quantity,
      so nothing in them is mono.
   2. `.spec td:first-child { width: 34% }` leaves three tier columns sharing
      66%, so the rightmost wraps to four lines while the label column has
      room to spare. Here the label column is narrower and the tier columns
      are equal.

   An em dash is the "not at this tier" mark rather than a cross, matching how
   the rest of the site writes an absent value. */
.spec-compare td:last-child {
  font-family: inherit;
  font-variant-numeric: normal;
}

.spec-compare td:first-child {
  width: 26%;
}

.spec-compare th:not(:first-child),
.spec-compare td:not(:first-child) {
  width: auto;
  padding-right: 20px;
}

.spec-compare th:last-child,
.spec-compare td:last-child {
  padding-right: 0;
}

@media (max-width: 900px) {
  /* Below this the four columns stop fitting. .table-scroll already gives it a
     horizontal scroller, so hold a usable minimum rather than letting every
     cell wrap to three lines. */
  .spec-compare {
    min-width: 720px;
  }

  /* Three columns (label plus two) need far less room than five. */
  .spec-compare:not(:has(th:nth-child(4))) {
    min-width: 520px;
  }
}

@media (max-width: 600px) {
  /* On a phone a percentage label column claimed a third of a scroller that
     is wider than the screen, so the values started off-screen. Automatic
     table layout ignores a narrower hint, so the phone layout is fixed: the
     label gets a set width and the value columns share the rest. */
  .spec-compare {
    table-layout: fixed;
  }

  .spec-compare th:first-child,
  .spec-compare td:first-child {
    width: 7.25rem;
    /* A word too long for the column hyphenates rather than breaking
       mid-letter (Platinum) or spilling into the next column (commitment). */
    overflow-wrap: normal;
    hyphens: auto;
  }

  /* Three-column tables have room to spare for longer labels. */
  .spec-compare:not(:has(th:nth-child(4))) th:first-child,
  .spec-compare:not(:has(th:nth-child(4))) td:first-child {
    width: 8.75rem;
  }
}

/* Two spec tables side by side on wide screens. */
.spec-pair {
  display: grid;
  /* min(340px, 100%): below a 340px column (a 320px phone) the fixed floor
     was wider than the page and pushed every .spec-pair into a sideways
     scroll. Identical to the old rule anywhere 340px fits. */
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  /* start: a short table no longer stretches its rows to its neighbor's
     height. The row gap keeps a stacked table's caption off the previous
     table's last row on phones. */
  align-items: start;
  gap: 32px 56px;
}

/* ---------- partner tier marks ----------
   The approved symbol in each tier's finish (Raylux-SCADA brand/tiers/, made
   by brand/source/tier_marks.py; approved 2026-09-30). Always beside the
   tier's name, so the mark is decoration (alt="", aria-hidden) and color is
   never the only thing telling tiers apart. One img per theme, switched on
   data-theme exactly like .wordmark-logo, because theme.js's choice must win
   over the OS setting. The portal shows the same files. */
.tier-cell {
  white-space: nowrap;
}

.tier-mark {
  display: inline-flex;
  width: 28px;
  height: 28px;
  margin: -4px 10px -6px 0;
  vertical-align: middle;
}

.tier-mark img {
  width: 28px;
  height: 28px;
}

.tier-mark-dark,
:root[data-theme="dark"] .tier-mark-light {
  display: none;
}

:root[data-theme="dark"] .tier-mark-dark {
  display: block;
}

/* ---------- module index ----------
   The four products as a ruled list with a leading designation, the way a
   catalog indexes part numbers. Not feature cards. */

.modules {
  border-top: 1px solid var(--rule);
}

.module {
  display: grid;
  grid-template-columns: 92px minmax(0, 1.15fr) minmax(0, 1.6fr) auto;
  align-items: baseline;
  gap: 28px;
  padding: 26px 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
}

.module:hover {
  background: var(--hover);
}

.module-no {
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
}

.module h3 {
  margin: 0;
  color: var(--text-bright);
  font-size: 21px;
  font-weight: 600;
}

.module h3 small {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

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

.module-go {
  /* text use of --accent; see .signin above. */
  color: var(--link);
  font-family: var(--f-mono);
  font-size: 12px;
  white-space: nowrap;
}

/* ---------- figures ----------
   Numbered, captioned, full width, hairline border. A screenshot is evidence,
   so it is presented as a figure with a reference — not a floating card. */

.figure {
  margin: 0 0 44px;
}

.figure:last-child {
  margin-bottom: 0;
}

.figure img {
  display: block;
  width: 100%;
  border: 1px solid var(--border-strong);
  background: var(--sunken);
}

.figure figcaption {
  display: flex;
  gap: 14px;
  margin-top: 12px;
  padding-top: 10px;
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.5;
  border-top: 1px solid var(--border);
}

.figure figcaption b {
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* A figure slot with nothing in it yet. Visible on purpose: a page that
   silently omits a screenshot looks finished when it is not. */
.figure-pending {
  display: grid;
  place-items: center;
  min-height: 220px;
  padding: 28px;
  color: var(--muted);
  background: var(--sunken);
  border: 1px dashed var(--border-strong);
  text-align: center;
  font-size: 14px;
}

/* ---------- prose ---------- */

.prose-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 40px 56px;
}

/* Four blocks laid out three-across left one alone on a second row. */
@media (min-width: 700px) {
  .prose-pair:has(> :nth-child(4):last-child) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.prose h3 {
  margin: 0 0 10px;
  color: var(--text-bright);
  font-size: 17px;
  font-weight: 600;
}

.prose p {
  margin: 0 0 12px;
  color: var(--secondary);
  font-size: 15px;
}

.prose p:last-child {
  margin-bottom: 0;
}

.prose ul {
  margin: 0 0 12px;
  padding-left: 18px;
  color: var(--secondary);
  font-size: 15px;
}

.prose strong {
  color: var(--text-bright);
  font-weight: 600;
}

/* ---------- buttons ---------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: 0 22px;
  color: #ffffff;
  background: var(--accent-fill);
  border: 1px solid var(--accent-fill);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.button:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.button-quiet {
  color: var(--text-bright);
  background: transparent;
  border-color: var(--border-strong);
}

.button-quiet:hover {
  color: var(--accent);
  background: transparent;
  border-color: var(--accent);
}

/* ---------- signup ---------- */

.notify-form {
  max-width: 560px;
}

.notify-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
}

/* On a phone the button took most of the row and clipped the placeholder. */
@media (max-width: 480px) {
  .notify-row {
    grid-template-columns: minmax(0, 1fr);
  }

  .notify-row input {
    border-right: 1px solid var(--border-strong);
    border-bottom: 0;
  }
}

.notify-row input {
  min-width: 0;
  height: 46px;
  padding: 0 14px;
  color: var(--text-bright);
  background: var(--input);
  border: 1px solid var(--input-border);
  border-right: 0;
  outline: none;
}

.notify-row input:focus-visible {
  /* outline: none above suppressed the browser default with nothing to
     replace it — :focus-visible only changed border-color and offset, so a
     keyboard user got no visible focus indicator at all. Restores the same
     2px solid var(--accent) ring used site-wide (see the global
     :focus-visible rule near the top of this file), offset inward since the
     input already has a 1px border. */
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

.notify-status {
  margin: 12px 0 0;
  color: var(--secondary);
  font-size: 14px;
}

.notify-status.is-ok {
  color: var(--success);
}

.notify-status.is-error {
  color: var(--error-text);
}

/* Honeypot: off-screen for people, still fillable by bots. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- contact ----------
   The address is TEXT first and a link second, because mailto: does nothing on
   a machine with no mail client and the visitor is then stuck. */
.contact-email {
  margin: 0 0 16px;
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: clamp(20px, 3vw, 30px);
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
  user-select: all;
}

.contact-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 10px;
}

/* The address under the contact form: the alternative, so it sits a clear
   step below the form rather than competing with its button. */
.contact-form + .section-sub {
  margin-top: 56px;
}

.contact-postal {
  margin: 0 0 14px;
  color: var(--secondary);
  font-size: 15px;
  font-style: normal;
  line-height: 1.7;
}

/* ---------- pricing configurator ----------
   Controls on the left, a live quote on the right. Kept to the same tables and
   tokens as the rest of the site: this is a spec sheet you can change, not a
   different kind of page. */
.config-grid {
  display: grid;
  gap: 28px;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
}

.config-set {
  margin: 0 0 20px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  background: var(--paper);
}

.config-set legend {
  padding: 0 6px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.config-set label {
  display: flex;
  gap: 10px;
  align-items: center;
  margin: 8px 0;
  color: var(--text);
  font-size: 15px;
  cursor: pointer;
}

.config-set input[type="checkbox"]:disabled + *,
.config-set label:has(input:disabled) { color: var(--muted); cursor: not-allowed; }

/* ── Configurator option cards ───────────────────────────────────────
   Selectable cards, not a list of bare radios. The inputs are still REAL
   radios and checkboxes - visually hidden but focusable and in the tab order -
   because they carry the keyboard behavior (arrow keys within a radio group),
   the screen-reader semantics, and the "pick one" versus "pick any"
   distinction. pricing.js also queries them directly.

   State comes from `input:checked + .opt` sibling combinators, which is why
   the input sits BESIDE the label (linked by for=/id) rather than inside it.
   `.opt:has(input:checked)` with the input nested would read more naturally;
   the combinator is chosen because it works everywhere, while :has() needs
   Chrome 105 / Safari 15.4 and a panel PC's browser is often neither.

   Worth recording, because it cost an hour and would cost the next person the
   same: while checking this in dark mode, an automated browser reported the
   selected card keeping its light --accent-wash and --accent after the theme
   toggle, which looks exactly like a custom-property invalidation bug. It is
   not. Inserting a fresh clone of the same card into the live DOM reported the
   correct dark values while the existing element still reported light ones -
   the pre-existing element simply was not being re-styled, because the
   automation pane was hidden and skipping style recalc. Both versions of this
   CSS were always correct. Do not "fix" a theme bug here that is really a
   headless-measurement artifact; verify with a freshly inserted element, or in
   a real visible browser, before believing it.

   Square corners on purpose: the site's language is the datasheet - .spec
   tables, mono section numbers, hard 1px rules. Rounded pill cards would read
   as a different product.

   Selectors are scoped under .config-set to outrank the pre-existing
   `.config-set label` rule (0-1-1), which sets display:flex and margin:8px 0.
   A bare `.opt` (0-1-0) loses to it and the cards silently render as the old
   flat rows. */

.icon-sprite { display: none; }

/* Two-tone icons, colored from the stylesheet rather than from attributes -
   a var() inside an SVG presentation attribute does not resolve. */
.ic-s  { stroke: currentColor; fill: none; }
.ic-f  { fill: currentColor; stroke: none; }
.ic-as { stroke: var(--accent); fill: none; }
.ic-af { fill: var(--accent); stroke: none; }

.opt-grid { display: grid; gap: 8px; }

/* Hidden, never display:none - that would drop it from the tab order and from
   the accessibility tree, which is the whole reason for keeping a real input. */
.config-set .opt-row > input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.config-set .opt {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  background: var(--paper);
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}

.config-set .opt:hover { border-color: var(--border-strong); background: var(--hover); }

.config-set input:checked + .opt {
  border-color: var(--accent);
  background: var(--accent-wash);
}

/* Focus lives on the card because the input it belongs to is invisible.
   Without this a keyboard user has no idea where they are. */
.config-set input:focus-visible + .opt {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The control itself, redrawn. Circle for radio, square for checkbox - the
   shape is the only cue that says "one of these" or "any of these", so it is
   kept rather than replaced with a generic tick. */
.opt-mark {
  width: 16px; height: 16px;
  border: 1.5px solid var(--input-border);
  background: var(--input);
  position: relative;
  flex: none;
}
.config-set input[type="radio"] + .opt .opt-mark { border-radius: 50%; }

.config-set input:checked + .opt .opt-mark {
  border-color: var(--accent-fill);
  background: var(--accent-fill);
}

/* Radio: a dot. Checkbox: a tick. */
.config-set input[type="radio"]:checked + .opt .opt-mark::after {
  content: "";
  position: absolute;
  inset: 3.5px;
  border-radius: 50%;
  background: var(--paper);
}
.config-set input[type="checkbox"]:checked + .opt .opt-mark::after {
  content: "";
  position: absolute;
  left: 4.5px; top: 1px;
  width: 4px; height: 8px;
  border: solid var(--paper);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.opt-icon {
  width: 26px; height: 26px;
  flex: none;
  color: var(--secondary);
  transition: color 120ms ease;
}
.config-set input:checked + .opt .opt-icon { color: var(--text-bright); }

.opt-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.opt-name { color: var(--text-bright); font-size: 15px; font-weight: 600; }
.opt-sub  { color: var(--muted); font-size: 13px; line-height: 1.4; }

.opt-price {
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: 14px;
  white-space: nowrap;
}

/* SMS and voice ride on alarm notification. pricing.js sets .disabled on the
   input; the card has to look unavailable or the reader just sees a card that
   ignores clicks. */
.config-set input:disabled + .opt {
  cursor: not-allowed;
  background: var(--sunken);
  border-color: var(--border-subtle);
}
.config-set input:disabled + .opt .opt-name,
.config-set input:disabled + .opt .opt-price { color: var(--muted); }
.config-set input:disabled + .opt .opt-icon { color: var(--muted); opacity: 0.55; }

/* A channel is subordinate to the module above it, and the indent says so
   without needing a sentence. */
.config-set .opt-child { margin-left: 22px; }

.config-set .opt-inline { grid-template-columns: auto minmax(0, 1fr) auto; }

.clients-head { display: flex; align-items: flex-start; gap: 12px; }
.clients-head .opt-icon { margin-top: 2px; color: var(--secondary); }
.clients-head .config-hint { margin-top: 0; }

@media (max-width: 520px) {
  /* The icon is decoration; the price is not. Drop the icon first. */
  .config-set .opt { grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px; }
  .opt-icon { display: none; }
  .config-set .opt-child { margin-left: 12px; }
}

.config-hint { margin-top: 0; }

@media (max-width: 520px) {
  /* The icon is decoration; the price is not. Drop the icon first. */
  .config-set .opt { grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px; }
  .opt-icon { display: none; }
  .config-set .opt-child { margin-left: 12px; }
}

.config-hint {
  margin: 2px 0 12px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
}

.config-slider {
  display: flex;
  gap: 14px;
  align-items: center;
  margin: 10px 0 6px;
}

.config-slider input[type="range"] { flex: 1; accent-color: var(--accent); }

.config-slider output {
  min-width: 78px;
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: 14px;
  text-align: right;
}

.config-quote {
  align-self: start;
  padding: 18px;
  border: 1px solid var(--border-strong);
  background: var(--sunken);
}

.config-quote .button { width: 100%; margin-top: 14px; text-align: center; }

/* The site-wide 34% label column wrapped the quote's line items to six lines
   while the amount column sat half empty. */
.config-quote .spec td:first-child { width: auto; }
.config-quote .spec td:last-child { white-space: nowrap; text-align: right; }

.quote-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.quote-total b {
  color: var(--text-bright);
  font-size: 26px;
  letter-spacing: -0.01em;
}

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

/* ---------- legal documents (/eula, /privacy) ----------
   A license agreement is published verbatim, so it is a long single column of
   running text rather than the sectioned marketing layout the rest of the site
   uses. Measure is capped for readability; everything else reuses the tokens. */
.legal-doc .wrap { max-width: 760px; }

.legal-doc h1 {
  margin: 0 0 8px;
  font-size: 30px;
  line-height: 1.2;
  color: var(--text-bright);
  letter-spacing: -0.01em;
}

.legal-doc h2 {
  margin: 34px 0 10px;
  padding-top: 14px;
  border-top: 1px solid var(--border-subtle);
  font-size: 18px;
  color: var(--text-bright);
}

.legal-doc p,
.legal-doc li {
  margin: 0 0 12px;
  color: var(--secondary);
  font-size: 15px;
  line-height: 1.62;
}

.legal-doc ul { margin: 0 0 16px; padding-left: 20px; }

.legal-doc strong { color: var(--text-bright); }

.legal-doc h3 {
  margin: 24px 0 8px;
  color: var(--text-bright);
  font-size: 15px;
}

.legal-doc hr {
  margin: 26px 0;
  border: 0;
  border-top: 1px solid var(--border-subtle);
}

/* The EULA's Markdown puts a --- before every section, and the h2 already
   draws its own rule — two hairlines 35px apart. The h2's rule stays. */
.legal-doc hr:has(+ h2) {
  display: none;
}

.legal-doc blockquote {
  margin: 0 0 16px;
  padding: 12px 16px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
}

.legal-doc blockquote p { margin: 0; }

.legal-doc table { margin: 0 0 20px; }

.legal-doc .meta {
  margin: 0 0 24px;
  color: var(--muted);
  font-size: 13px;
}

/* ---------- footer ---------- */

.site-footer {
  padding: 40px 0;
  color: var(--muted);
  border-top: 2px solid var(--rule);
  font-size: 14px;
}

.site-footer-in {
  display: flex;
  gap: 24px;
  justify-content: space-between;
  flex-wrap: wrap;
}

/* Footer links are 44px-tall targets, so wrapped rows stack with no extra
   row gap: the targets themselves are the spacing. */
.site-footer nav {
  display: flex;
  gap: 0 20px;
  flex-wrap: wrap;
}

.site-footer a {
  color: var(--secondary);
  text-decoration: none;
}

/* Left-aligned inside the 44px minimum, so a wrapped row's first link lines
   up with the row above; a short word ("Lens") just carries a little air. */
.site-footer nav a {
  display: inline-flex;
  align-items: center;
  min-width: 44px;
  min-height: 44px;
}

/* The mailto in the address is inline in a sentence: inline padding makes it
   44px tall without moving the line. */
.footer-legal address a {
  padding-block: 14px;
}

.site-footer a:hover {
  color: var(--accent);
}

/* ---------- 404 ---------- */

.notfound {
  padding: 120px 0;
}

.notfound-code {
  margin: 0 0 12px;
  color: var(--accent);
  font-family: var(--f-mono);
  font-size: 13px;
  letter-spacing: 0.16em;
}

.notfound h1 {
  margin: 0 0 16px;
  color: var(--text-bright);
  font-size: clamp(32px, 5vw, 52px);
  font-weight: 600;
  line-height: 1.05;
}

.notfound p {
  max-width: 56ch;
  color: var(--secondary);
}

/* ---------- responsive ---------- */

@media (max-width: 860px) {
  .wrap {
    width: min(1180px, 100% - 32px);
  }

  .hero {
    padding: 56px 0 44px;
  }

  .section {
    padding: 48px 0;
  }

  /* The four-column part-number row collapses rather than crushing. The
     designation and the arrow are chrome, so they go first. */
  .module {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }

  .module-no,
  .module-go {
    display: none;
  }

  .section-head {
    grid-template-columns: minmax(0, 1fr);
  }

  .spec td:first-child {
    width: 42%;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   COMPLIANCE PAGES — /source and /notices

   These two are named by URL in the EULA that ships in every installer, so
   they are load-bearing documents, not marketing pages. They are styled as
   the back matter of the same datasheet: same rules, same mono/prose split,
   no new visual vocabulary. A license page that looked like a different site
   would read as less authoritative, not more.

   Nothing here introduces a radius, a shadow or a card. If you are adding one,
   you are on the wrong page.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------- the written offer ----------
   Set apart the way a datasheet sets apart a safety or certification clause:
   heavy rule top and bottom, not a tinted rounded box. This is the text the
   LGPL actually requires, so it must look like the operative part of the page
   rather than a callout. */

.clause {
  margin: 0 0 36px;
  padding: 26px 0;
  border-top: 2px solid var(--rule);
  border-bottom: 2px solid var(--rule);
}

.clause p {
  max-width: 74ch;
  margin: 0 0 14px;
  color: var(--text);
  font-size: 16px;
}

.clause p:last-child {
  margin-bottom: 0;
}

.clause strong {
  color: var(--text-bright);
  font-weight: 600;
}

.clause-ref {
  display: block;
  margin-bottom: 14px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* The downloads package table: size and link never wrap; on a phone each
   package stacks as name, description, then size and link on one line. */
.spec-pkg td:nth-child(3),
.spec-pkg td:nth-child(4) {
  width: 1%;
  white-space: nowrap;
}

@media (max-width: 600px) {
  .spec-pkg thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .spec-pkg tr {
    display: block;
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
  }

  .spec.spec-pkg tbody td {
    display: inline-block;
    width: auto;
    padding: 2px 16px 2px 0;
    border: 0;
  }

  .spec.spec-pkg tbody td:nth-child(1),
  .spec.spec-pkg tbody td:nth-child(2) {
    display: block;
    white-space: normal;
  }
}

/* Installer checksums on /downloads (tools/build-checksums.py). A 64-digit
   hash breaks anywhere rather than pushing the table off a phone. */
.checksums-h {
  margin: 36px 0 8px;
  color: var(--text-bright);
  font-size: 17px;
}

.spec-hashes td:first-child {
  width: auto;
  white-space: nowrap;
}

.spec-hashes .hash {
  font-size: 12px;
  word-break: break-all;
}

/* On a phone the filename column took most of the width and the hash wrapped
   two or three characters to a line. Stack each row instead, filename above
   hash, the way .spec-versus does — the hash then gets the full width and
   breaks into two readable lines. */
@media (max-width: 600px) {
  .spec-hashes thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .spec-hashes tr,
  .spec-hashes td {
    display: block;
  }

  .spec-hashes tr {
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
  }

  .spec-hashes tr:last-child {
    border-bottom: 0;
  }

  .spec.spec-hashes tbody td {
    width: auto;
    padding: 2px 0;
    white-space: normal;
    border: 0;
  }
}

/* ---------- product heroes: the animated art (Nexus, Studio, Spectrum,
   Lens, Platforms) ----------
   The product pages wear the home page's navy panel (.hero-feature) with one
   drawing each of what the product does. Decoration only: aria-hidden, no
   figures, generic tag names. CSS-only (no script, nothing the CSP blocks).

   REDUCED MOTION: the global rule at the end of this file sets animation:none,
   so every element's BASE style is its finished state — drawn strokes drawn,
   the HMI showing, traveling pulses hidden (opacity 0 until animated). */
.hero-product {
  background-color: var(--hero-bg);
}

.hero-feature .status-line {
  color: var(--hero-muted);
}























/* Traveling pulse: a short dash that runs the length of its wire. Every
   pulse path carries pathLength="100", so one keyframe fits any wire. */
















/* Activity bars (Nexus rows): widen and settle, staggered. */




/* Tank level: rises and falls from the bottom. */




/* Pump impeller. */




/* Scrolling trend pen (two periods long; slides one period). */




/* --- Studio: a screen being drawn, selected, published (9 s loop) --- */
































/* --- Spectrum: the alarm arrives, flashes, is acknowledged (8 s loop) --- */












/* --- Lens: boots straight into the HMI, then a tap (10 s loop) --- */



















/* --- Platforms: one package, arriving at both (4 s loop) --- */





/* ---------- /pricing: what it costs over time (pricing-time.js) ----------
   Two lines, one axis, a legend and end labels, hover crosshair with a
   tooltip, and a year-by-year table. Text is in text tokens; only the marks
   and the legend keys carry the series colors. */
.ot-grid-in {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 32px 56px;
  align-items: start;
}

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

.ot-controls .form-label { margin: 18px 0 8px; }
.ot-controls .form-label:first-child { margin-top: 0; }

.ot-money {
  display: flex;
  align-items: center;
  border: 1px solid var(--border-strong);
  background: var(--input);
}

.ot-money span {
  padding: 0 4px 0 14px;
  color: var(--muted);
  font-family: var(--f-mono);
}

.ot-money input {
  flex: 1;
  min-width: 0;
  height: 46px;
  padding: 0 14px 0 0;
  color: var(--text-bright);
  background: transparent;
  border: 0;
  outline: none;
  font-family: var(--f-mono);
  font-size: 17px;
}

.ot-money:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

.ot-licence {
  margin: 0 0 8px;
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: 15px;
}

.ot-check,
.ot-years label {
  display: flex;
  gap: 10px;
  align-items: baseline;
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.45;
}

.ot-years {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin: 22px 0 0;
  padding: 0;
  border: 0;
}

.ot-years legend { width: 100%; }

.ot-summary {
  margin: 0 0 14px;
  color: var(--text-bright);
  font-size: 17px;
  line-height: 1.5;
}

.ot-legend {
  display: flex;
  gap: 20px;
  margin: 0 0 6px;
  padding: 0;
  list-style: none;
  color: var(--secondary);
  font-size: 13px;
}

.ot-legend i,
.ot-tip i {
  display: inline-block;
  width: 14px;
  height: 3px;
  margin-right: 8px;
  vertical-align: middle;
  border-radius: 2px;
}

.ot-legend .is-current,
.ot-tip .is-current { background: var(--series-current); }
.ot-legend .is-raylux,
.ot-tip .is-raylux { background: var(--series-raylux); }

.ot-plot { position: relative; }

#ot-chart {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

#ot-chart .ot-grid line { stroke: var(--border); stroke-width: 1; }

#ot-chart .ot-tick {
  fill: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
}

#ot-chart .ot-end {
  fill: var(--secondary);
  font-size: 12px;
  font-weight: 600;
}

#ot-chart .ot-line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

#ot-chart .ot-line.is-current { stroke: var(--series-current); }
#ot-chart .ot-line.is-raylux { stroke: var(--series-raylux); }

#ot-chart .ot-hair {
  stroke: var(--border-strong);
  stroke-width: 1;
  opacity: 0;
}

#ot-chart .ot-dot {
  stroke: var(--band);
  stroke-width: 2;
  opacity: 0;
}

#ot-chart .ot-dot.is-current { fill: var(--series-current); }
#ot-chart .ot-dot.is-raylux { fill: var(--series-raylux); }

#ot-chart.is-hover .ot-hair,
#ot-chart.is-hover .ot-dot { opacity: 1; }

#ot-chart .ot-hit { fill: transparent; cursor: crosshair; }
#ot-chart .ot-hit:focus { outline: none; }
#ot-chart .ot-hit:focus-visible { outline: 2px solid var(--accent); }

.ot-tip {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  display: grid;
  gap: 2px;
  padding: 8px 10px;
  color: var(--text);
  background: var(--paper);
  border: 1px solid var(--border-strong);
  font-family: var(--f-mono);
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none;
}

.ot-tip b { color: var(--text-bright); font-size: 12px; }

/* display:grid above would otherwise override the hidden attribute. */
.ot-tip[hidden] { display: none; }

.ot-details { margin-top: 14px; }

.ot-details summary {
  color: var(--link);
  font-size: 14px;
  cursor: pointer;
}

/* ---------- /partners/integrators: the directory ----------
   One ruled row per company: identity and contact on the left, what they do
   on the right. The disclosure line is part of the listing, not small print. */
.integrator {
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  gap: 24px 56px;
  padding: 28px 0;
  border-top: 2px solid var(--rule);
}

.integrator h3 {
  margin: 0 0 4px;
  color: var(--text-bright);
  font-size: 22px;
  line-height: 1.2;
}

.integrator-where {
  margin: 0 0 18px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 13px;
}

.integrator-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0;
}

.integrator-body > p:first-child {
  margin: 0 0 16px;
  color: var(--secondary);
  font-size: 16px;
  line-height: 1.55;
}

.integrator-body .spec td:last-child {
  font-family: inherit;
  font-variant-numeric: normal;
}

.integrator-disclosure {
  margin-top: 16px;
}

/* The heading's rule already sits above the first listing. */
.section-head + .integrator {
  border-top: 0;
  padding-top: 8px;
}

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

/* ---------- /compatibility: the PLC checker (compatibility.js) ----------
   A search box and ruled result blocks. The verdict is carried by the rule
   color AND the word above the name — never color alone. */
.checker-form {
  display: grid;
  gap: 8px;
  max-width: 640px;
}

.checker-form input {
  height: 52px;
  padding: 0 16px;
  color: var(--text-bright);
  background: var(--input);
  border: 1px solid var(--input-border);
  font-size: 18px;
}

.checker-form input:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

.checker-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 28px;
}

.checker-chips button {
  padding: 6px 12px;
  color: var(--secondary);
  background: transparent;
  border: 1px solid var(--border-strong);
  font-family: var(--f-mono);
  font-size: 12px;
  cursor: pointer;
}

.checker-chips button:hover {
  color: var(--link);
  border-color: var(--accent);
}

.checker-out {
  display: grid;
  gap: 16px;
  max-width: 820px;
}

.checker-hint {
  margin: 0;
  color: var(--muted);
}

.checker-card {
  padding: 16px 0 16px 20px;
  border-left: 3px solid var(--border-strong);
}

.checker-card.is-native { border-left-color: var(--success); }
.checker-card.is-mixed { border-left-color: var(--accent); }
.checker-card.is-route,
.checker-card.is-older { border-left-color: var(--warning); }

.checker-verdict {
  margin: 0 0 4px;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.checker-card.is-native .checker-verdict { color: var(--success); }

.checker-card h3 {
  margin: 0 0 10px;
  color: var(--text-bright);
  font-size: 20px;
}

.checker-facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 4px 16px;
  margin: 0 0 10px;
}

.checker-facts dt {
  color: var(--muted);
  font-size: 13px;
}

.checker-facts dd {
  margin: 0;
  color: var(--text-bright);
  font-size: 14px;
}

.checker-note {
  margin: 0 0 8px;
  color: var(--secondary);
  font-size: 15px;
  line-height: 1.55;
}

.checker-link {
  font-size: 14px;
}

/* ---------- archive table ----------
   Five columns, hairline rows, every measured value monospace. Scrolls inside
   itself on narrow screens rather than forcing the page to scroll sideways. */

.table-scroll {
  overflow-x: auto;
  /* Scroll cue: a shadow on the right edge while there is more table to
     the right, gone once you reach the end. The paper-colored covers
     scroll with the content (local); the shadows stay put (scroll). */
  background:
    linear-gradient(to right, var(--bg), var(--bg)) left / 24px 100% no-repeat local,
    linear-gradient(to left, var(--bg), var(--bg)) right / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.16), transparent) right / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.16), transparent) left / 12px 100% no-repeat scroll;
}

.section-alt .table-scroll {
  background:
    linear-gradient(to right, var(--band), var(--band)) left / 24px 100% no-repeat local,
    linear-gradient(to left, var(--band), var(--band)) right / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.16), transparent) right / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.16), transparent) left / 12px 100% no-repeat scroll;
}

.dl {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
}

.dl th {
  padding: 0 16px 10px 0;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--rule);
}

.dl td {
  padding: 14px 16px 14px 0;
  vertical-align: baseline;
  border-bottom: 1px solid var(--border);
}

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

.dl .c-name {
  color: var(--text-bright);
  font-weight: 600;
}

/* Version, license and size are all measured values. */
.dl .c-ver,
.dl .c-lic,
.dl .c-size {
  color: var(--secondary);
  font-family: var(--f-mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.dl .c-size {
  text-align: right;
}

.dl .c-get {
  text-align: right;
  white-space: nowrap;
}

.dl .c-get a {
  font-family: var(--f-mono);
  font-size: 12px;
  text-decoration: none;
}

.dl .c-get a:hover {
  text-decoration: underline;
}

/* Copyleft rows carry the obligation, so they are marked in the table itself
   rather than only in prose. A rule, not a highlight fill. */
.dl .is-copyleft .c-lic {
  color: var(--text-bright);
  font-weight: 600;
}

/* ---------- checksums / command blocks ---------- */

.pre {
  margin: 0 0 8px;
  padding: 16px 18px;
  overflow-x: auto;
  color: var(--secondary);
  background: var(--sunken);
  border: 1px solid var(--border);
  font-family: var(--f-mono);
  font-size: 12.5px;
  line-height: 1.7;
  tab-size: 2;
}

.pre b {
  color: var(--text-bright);
  font-weight: 600;
}

.pre-note {
  margin: 0 0 28px;
  color: var(--muted);
  font-size: 13px;
}

/* ---------- notice inventory ----------
   ~80 components. A row each would be five screens of scrolling, so packages
   are grouped by license and set as a dense monospace column list — which is
   also how the closure is actually read. The license is the heading because
   the license is the thing with legal effect. */

.licence-block {
  padding: 22px 0;
  border-bottom: 1px solid var(--border);
}

.licence-block:last-child {
  border-bottom: 0;
}

.licence-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.licence-head h3 {
  margin: 0;
  color: var(--text-bright);
  font-family: var(--f-mono);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.licence-count {
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.licence-head p {
  margin: 0 0 0 auto;
  max-width: 48ch;
  color: var(--muted);
  font-size: 13px;
}

.pkg-list {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 4 170px;
  column-gap: 32px;
}

.pkg-list li {
  color: var(--secondary);
  font-family: var(--f-mono);
  font-size: 12.5px;
  line-height: 1.85;
  break-inside: avoid;
}

/* ---------- footer, extended ----------
   The compliance pages have to be reachable from every page, and a company
   selling to plant operators is expected to state who it is. Both live in a
   second footer row under a hairline. */

.footer-legal {
  display: flex;
  gap: 20px 32px;
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--border);
  justify-content: space-between;
  flex-wrap: wrap;
}

.footer-legal address {
  max-width: 46ch;
  color: var(--muted);
  font-size: 13px;
  font-style: normal;
  line-height: 1.6;
}

.footer-legal address b {
  color: var(--secondary);
  font-weight: 600;
}

.footer-legal nav {
  display: flex;
  gap: 0 20px;
  align-items: flex-start;
  flex-wrap: wrap;
}

.footer-legal nav a {
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
}

@media (max-width: 860px) {
  .pkg-list {
    columns: 2 150px;
  }

  .licence-head p {
    margin-left: 0;
  }
}

/* ---------- utilities ----------
   These exist because the site's CSP is `style-src 'self'` with no
   'unsafe-inline'. A style="" attribute is BLOCKED by that policy — silently,
   because nothing renders wrong enough to notice locally, and the console
   error only appears once the header is actually applied. Several pages had
   inline styles that had therefore never taken effect in production.

   So: no style attributes anywhere on this site. If you need a one-off, add a
   named class here. Do not "simplify" one of these back into an inline style.
   Verified with `wrangler pages dev`, which serves _headers, so the console
   shows the violations. */

/* A designation line sitting above a page's h1. */
.eyebrow {
  margin-bottom: 18px;
}

/* The eyebrow lives inside the h1 (as its first child) so the h1's text carries
   the page's searchable term. Render it as the standalone <p> did: a block-level
   flex row (the <p> was inline-flex in a block), body type instead of the h1's.
   Color, family, size, tracking and case come from .status-line / .eyebrow-product
   / .hero-feature .status-line, which still apply to the span; only what the h1
   would otherwise hand down is reset here. */
h1 > .status-line {
  display: flex;
  margin: 0 0 18px;
  font-weight: 400;
  line-height: 1.6;
  text-wrap: pretty;
}

.form-label {
  margin-bottom: 10px;
}

.stack-lg {
  margin-top: 28px;
}

.section-sub {
  margin: 40px 0 12px;
  color: var(--text-bright);
  font-size: 17px;
  font-weight: 600;
}

.block-gap {
  margin-top: 36px;
}

.block-gap-sm {
  margin-top: 26px;
}

/* Prose that has to stay readable when it is not in a .prose-pair column. */
.prose-wide {
  max-width: 74ch;
}

.prose-wide-gap {
  margin-top: 32px;
  max-width: 74ch;
}

/* Right-aligned column heads, paired with .c-size / .c-get on the cells. */
.dl th.num,
.dl th.act {
  text-align: right;
}

/* A two-column variant of .dl — component and license only, no measured
   values, so it needs far less room before it has to scroll. */
.dl-narrow {
  min-width: 420px;
}

/* ---------- partner application form ----------
   Two columns on wide screens, one on narrow. The two free-text boxes span the
   full width because they are the fields that decide the application, and a
   textarea squeezed into half a column reads as an afterthought.

   Inputs are styled here rather than reusing .notify-row's, which is a
   deliberately seamless input+button pair for a single field and has
   border-right:0 to prove it. */
.apply-form {
  max-width: 900px;
}

.apply-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px 28px;
}

.apply-wide {
  grid-column: 1 / -1;
}

.apply-field {
  min-width: 0;
}

.apply-field .form-label {
  display: block;
  margin-bottom: 8px;
}

/* "required" reads as a word, not a red asterisk nobody has a legend for. */
.apply-field .req {
  margin-left: 8px;
  color: var(--secondary);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

.apply-field input,
.apply-field select,
.apply-field textarea {
  width: 100%;
  padding: 12px 14px;
  color: var(--text-bright);
  background: var(--input);
  border: 1px solid var(--input-border);
  border-radius: 0;
  font: inherit;
  font-size: 15px;
  outline: none;
}

.apply-field input,
.apply-field select {
  height: 46px;
  padding: 0 14px;
}

/* The contact form's topic dropdown. Native appearance on purpose: the arrow
   follows the page's color-scheme in both themes, and a hand-drawn one would
   need a data: image to do the same. */
.apply-field select {
  cursor: pointer;
}

.apply-field textarea {
  min-height: 120px;
  line-height: 1.6;
  resize: vertical;
}

.apply-field input:focus-visible,
.apply-field select:focus-visible,
.apply-field textarea:focus-visible {
  /* Same gap as .notify-row input above: outline: none had no replacement.
     Restores the site's 2px solid var(--accent) ring, offset inward for the
     input's own border. */
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

/* Set by partners.js on a field the server or the browser rejected, so the
   message is attached to the field rather than only to the status line. */
.apply-field.is-invalid input,
.apply-field.is-invalid select,
.apply-field.is-invalid textarea {
  border-color: var(--error);
}

/* Per-field message, set by contact.js and tied to its field with
   aria-describedby. Empty until there is something to say. */
.apply-error {
  margin: 8px 0 0;
  color: var(--error-text);
  font-size: 13px;
  line-height: 1.55;
}

.apply-error:empty {
  display: none;
}

.apply-hint {
  margin: 8px 0 0;
  color: var(--secondary);
  font-size: 13px;
  line-height: 1.55;
}

.apply-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  margin-top: 28px;
}

.apply-note {
  margin: 0;
  color: var(--secondary);
  font-size: 14px;
}

.apply-status:empty {
  display: none;
}

.apply-status {
  margin-top: 18px;
  font-size: 15px;
}

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

/* ── the skip link under the download form ──────────────────────────────────
 *
 * Deliberately quiet (Cody, 2026-09-10): a major SCADA vendor puts one at
 * the bottom of their download form styled to be easy to miss, and that is
 * the behavior being matched. Small, muted, no button chrome, sitting below the
 * real call to action with enough space above it that the eye stops first.
 *
 * It stops there, and the stopping point is measured rather than assumed.
 * --muted was the first choice and it FAILS: #8a93a0 on the #f6f7f9 section
 * ground is 2.9:1, well under WCAG 1.4.3's 4.5:1 for normal text. --secondary
 * is 5.52:1 and still reads as clearly de-emphasized against body text at
 * 14.76:1 — quiet is a contrast budget, not the absence of one. It is also a
 * theme token, so it tracks dark mode instead of pinning a hex here.
 *
 * It keeps a visible focus ring and underlines on hover, so it reads as
 * something you can press. Anyone tempted to take the contrast further down
 * should read downloads.js first — quiet is the ask, invisible is a different
 * thing and is not.
 */
.lead-skip-row {
  margin: 28px 0 0;
  text-align: center;
}

.lead-skip {
  appearance: none;
  background: none;
  border: 0;
  padding: 4px 6px;
  color: var(--secondary);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  border-radius: 4px;
}

.lead-skip:hover {
  text-decoration: underline;
}

.lead-skip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---- Downloads: the lead dialog (added 2026-09-10) ---- */
/* ── the lead dialog on /downloads/ ──────────────────────────────────────
 *
 * Popped by downloads.js when a visitor clicks an installer link. Native
 * <dialog>, so showModal()/close() carry the focus trap and Escape handling
 * for free — no custom focus-trap JS to get wrong. Square-cornered to match
 * the rest of the site (.nav-menu, .spec, .button all avoid border-radius),
 * raised with the same shadow recipe as .nav-menu rather than inventing one.
 */
#lead-dialog {
  max-width: 640px;
  width: calc(100% - 48px);
  max-height: calc(100% - 48px);
  padding: 0;
  color: var(--text-bright);
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  box-shadow: 0 12px 28px rgba(13, 21, 33, 0.14);
}

:root[data-theme="dark"] #lead-dialog {
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
}

#lead-dialog::backdrop {
  background: rgba(13, 21, 33, 0.55);
}

:root[data-theme="dark"] #lead-dialog::backdrop {
  background: rgba(0, 0, 0, 0.7);
}

#lead-dialog[open] {
  display: flex;
  flex-direction: column;
}

.lead-dialog-in {
  padding: 32px;
  overflow-y: auto;
}

.lead-dialog-in h2 {
  margin: 0 0 8px;
}

.lead-dialog-in > p {
  margin: 0 0 24px;
  color: var(--secondary);
}

/* The form inside the dialog reuses .apply-form/.apply-grid/.apply-field —
   this just removes the max-width, which existed to keep the section-level
   form from stretching the full page width and is not needed in a 640px
   dialog. */
#lead-dialog .apply-form {
  max-width: none;
}

@media (max-width: 480px) {
  #lead-dialog {
    width: calc(100% - 24px);
    max-height: calc(100% - 24px);
  }

  .lead-dialog-in {
    padding: 22px;
  }
}

/* prefers-reduced-motion is already handled globally (styles.css disables
   all animation/transition under that query) — #lead-dialog has none of its
   own, so nothing further is needed here. Native <dialog> open/close has no
   default animation in this browser support target either. */

/* ---------- print ----------
   The legal pages (EULA, privacy, notices, /source, /licenses) are the ones
   that get printed or saved as PDF for a compliance file. Paper gets the page
   content and the footer's legal line, not the site chrome. main.js drops the
   dark theme for the duration of a print, so the light tokens apply here. */
@media print {
  .skip-link,
  .masthead-utility,
  .masthead-rule,
  .masthead-contents,
  .nav-burger,
  .cta-band,
  #lead-dialog {
    display: none !important;
  }

  .masthead {
    position: static;
  }

  body {
    background: #ffffff;
  }

  /* A scroll container clips on paper; let wide tables and <pre> run out. */
  .table-scroll,
  .pre {
    overflow: visible;
  }

  .pre {
    white-space: pre-wrap;
    word-break: break-all;
  }

  /* On paper a link is only useful if the address is printed with it. */
  .legal-doc a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    word-break: break-all;
  }

  h2,
  h3 {
    break-after: avoid;
  }

  tr,
  .pre {
    break-inside: avoid;
  }
}

/* --- /downloads/ version picker (downloads-versions.js) --- */
.version-pick {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 16px;
}
.version-pick[hidden] { display: none; }
.version-pick label { font-weight: 600; color: var(--text-bright); }
.version-pick select {
  font: inherit;
  color: var(--text);
  background: var(--input);
  border: 1px solid var(--input-border);
  border-radius: 4px;
  padding: 6px 10px;
  max-width: 100%;
}
.version-pick select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.version-pick-note { color: var(--muted); font-size: 0.9em; }
tr[data-kind][hidden] { display: none; }
/* --- /source: one disclosure per group of releases (tools/build-source-page.py) --- */
.src-release {
  border-top: 2px solid var(--rule);
  padding: 18px 0 8px;
}
.src-release + .src-release { margin-top: 8px; }
.src-release summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
  margin-bottom: 12px;
}
.src-release summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.src-rel { color: var(--text-bright); font-weight: 600; font-size: 17px; }
.src-meta {
  color: var(--secondary);
  font-family: var(--f-mono);
  font-size: 12px;
}
.src-release .pre-note { margin-top: 12px; }

/* ---------- Raylux Iris launch band (home page) ----------
   Copy beside the film on wide screens, film under the copy below 900px. The
   video starts with real controls in the markup; tour.js removes them when it
   can run the silent loop itself. The full film with sound is on /news/. */
.iris-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  gap: 48px;
}

.iris-status {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
  color: var(--link);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.iris-status::after {
  content: "";
  width: 40px;
  height: 2px;
  background: var(--accent);
}

.iris-copy h2 {
  margin: 0 0 16px;
  color: var(--text-bright);
  font-size: clamp(27px, 3.4vw, 42px);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
}

.iris-lede {
  margin: 0 0 20px;
  color: var(--secondary);
  font-size: 16px;
  line-height: 1.6;
}

.iris-points {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--border);
}

.iris-points li {
  padding: 9px 0;
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.5;
  border-bottom: 1px solid var(--border);
}

.iris-points b {
  color: var(--text-bright);
  font-weight: 600;
}

.iris-film {
  margin: 0;
}

.iris-film video {
  display: block;
  width: 100%;
  height: auto;
  background: var(--sunken);
  border: 1px solid var(--border-strong);
}

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

/* The full Iris film at the top of its /news/ entry (tools/news-media.json,
   rendered by tools/build-news.py). */
.news-film {
  margin: 0 0 28px;
}

.news-film video {
  display: block;
  width: 100%;
  height: auto;
  background: var(--sunken);
  border: 1px solid var(--border-strong);
}


/* ═══════════════════════════════════════════════════════════════════════════
   DAYLIGHT LAYER (ADR-170). Shape, depth, the beam, and the home page.
   Sits last so it wins over the older component rules above without
   rewriting each of them: radius 8/12/18/pill, one soft shadow, glass bars.
   ═══════════════════════════════════════════════════════════════════════════ */

body {
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .hero h1, .section-head h2, .cta-band h2 {
  text-wrap: balance;
}

.spec td, .proof-value, .d-price-list b, .d-terms, .mono, code {
  font-variant-numeric: tabular-nums;
}

/* ---------- masthead: translucent, one soft hairline ---------- */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--glass);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  backdrop-filter: blur(18px) saturate(1.6);
  border-bottom: 1px solid var(--border);
}

.masthead-rule { border-top: 0; }
.contents-label { display: none; }

.masthead nav a {
  border-bottom: 0;
  border-radius: var(--r-full);
  min-height: 36px;
  padding: 0 14px;
  transition: background 200ms ease-out, color 200ms ease-out;
}

.masthead nav a:hover { background: var(--hover); }

.masthead nav a[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
}

:root[data-theme="dark"] .masthead nav a[aria-current="page"] { color: var(--link); }

.masthead .nav-menu {
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  border-color: var(--border);
  background: var(--paper);
}

.theme-toggle, .signin {
  border-radius: var(--r-full);
}

/* ---------- buttons: pills; one primary per view ---------- */
.button {
  border-radius: var(--r-full);
  min-height: 46px;
  padding: 0 24px;
  transition: background 200ms ease-out, border-color 200ms ease-out, color 200ms ease-out;
}

.button-quiet {
  background: var(--paper);
  color: var(--text-bright);
  border-color: var(--border-strong);
}

.button-quiet:hover {
  color: var(--text-bright);
  background: var(--fill);
  border-color: var(--border-strong);
}

/* ---------- sections: air, not rules ---------- */
.section {
  padding: 88px 0;
  border-bottom: 0;
}

.section-alt { background: var(--band); }

.section-head {
  border-bottom: 0;
  padding-bottom: 0;
  margin-bottom: 32px;
}

.section-head h2 {
  font-weight: 700;
  letter-spacing: -0.035em;
}

.section-head p { color: var(--secondary); font-size: 15px; }

/* ---------- tables: a card with hairline rows ---------- */
.spec {
  background: var(--paper);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
}

.spec th {
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--paper);
}

.spec td { padding: 14px 20px; }
.spec td:first-child { padding-right: 20px; }
.spec tr:last-child td { border-bottom: 0; }

.table-scroll { padding: 4px 4px 14px; margin: -4px; }

/* ---------- figures ---------- */
.figure img {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  background: var(--paper);
}

.figure figcaption { border-top: 0; padding-top: 0; }

.module { border-radius: var(--r-md); }

/* ---------- product-page heroes: light, like everything else ----------
   The navy panel was the datasheet look. Daylight heroes sit on the page
   backdrop; the product drawings sit in their own panel so their
   navy-tuned strokes keep their contrast. */
.hero-feature {
  --hero-bg: var(--bg);
  --hero-text: var(--text-bright);
  --hero-secondary: var(--secondary);
  --hero-muted: var(--muted);
  --hero-line: var(--border);
  --hero-pen: var(--accent);
  background: var(--bg);
  color: var(--text-bright);
  border-bottom: 0;
}

.hero-feature h1 { color: var(--text-bright); letter-spacing: -0.04em; }
.hero-feature .proof-value { color: var(--text-bright); }
.hero-feature .hero-trust li::before { background: var(--accent); }
.hero-feature .button-quiet { color: var(--text-bright); border-color: var(--border-strong); background: var(--paper); }
.hero-feature .button-quiet:hover { color: var(--text-bright); border-color: var(--border-strong); background: var(--fill); }
.hero-feature :focus-visible { outline-color: var(--accent); }



/* ---------- closing band: a rounded card, not a full-bleed slab ---------- */
.cta-band {
  background: transparent;
  color: var(--text-bright);
  padding: 24px 0 96px;
}

.cta-band > .wrap {
  padding: clamp(32px, 5vw, 64px);
  border-radius: var(--r-lg);
  background: var(--accent-fill);
  color: #ffffff;
  box-shadow: var(--shadow-lg);
}

.cta-band .cta-eyebrow::after { background: #ffffff; }
.cta-band .button { color: var(--accent-fill); }
.cta-band .button:hover { color: #ffffff; }

/* ---------- footer ---------- */
.site-footer {
  border-top: 1px solid var(--border);
  background: var(--paper);
}

/* ---------- the beam: brand moments only ----------
   Seven stops of the logo beam. It flows at 2.4 s a cycle and is drawn
   still under prefers-reduced-motion. Never a status color. */
.beam-rule {
  display: block;
  height: 3px;
  width: 96px;
  border-radius: var(--r-full);
  background: var(--beam);
  background-size: 200% 100%;
  animation: beam-flow 2.4s linear infinite;
}

@keyframes beam-flow {
  to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .beam-rule { animation: none; background-size: 100% 100%; }
}

/* ---------- Daylight home ---------- */
.d-eyebrow {
  margin: 0;
  color: var(--muted);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Inside an h1 (the eyebrow is the h1's first child, so the h1 carries the
   searchable term): a block with the same type as the <p>, and the 18px the
   .d-hero-in / .d-pagehead grid gap used to put between eyebrow and headline. */
h1 > .d-eyebrow {
  display: block;
  margin: 0 0 18px;
  line-height: 1.6;
  text-wrap: pretty;
}

.d-eyebrow-beam {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.d-eyebrow-beam::before {
  content: "";
  width: 28px;
  height: 3px;
  border-radius: var(--r-full);
  background: var(--beam);
}

.d-lede {
  max-width: 40em;
  margin: 0;
  color: var(--secondary);
  font-size: clamp(17px, 1.6vw, 20px);
  line-height: 1.55;
}

.d-hero, .d-pagehead {
  padding: 72px 0 24px;
  text-align: center;
}

.d-pagehead { padding-bottom: 8px; }

.d-hero-in, .d-pagehead > .wrap {
  display: grid;
  gap: 18px;
  justify-items: center;
}

.d-hero h1, .d-pagehead h1 {
  max-width: 14ch;
  margin: 0;
  color: var(--text-bright);
  font-size: clamp(42px, 7vw, 84px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.045em;
}

.d-pagehead h1 { max-width: 20ch; font-size: clamp(36px, 5.4vw, 64px); }

.d-hero .hero-actions, .d-pagehead .hero-actions { justify-content: center; margin-top: 6px; }
.d-hero .hero-trust { justify-content: center; margin-top: 4px; }
.d-hero-in .hero-trust li { color: var(--secondary); }

.d-device {
  width: min(100%, 980px);
  margin: 36px 0 0;
}

.d-device-bezel {
  padding: 10px 10px 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: #1b1d22;
  box-shadow: var(--shadow-lg);
}

.d-device-bezel img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 8px 8px 0 0;
}

.d-device figcaption {
  margin-top: 14px;
  color: var(--muted);
  font-size: 13px;
}

.d-section { padding: 72px 0; }

.d-head {
  display: grid;
  gap: 10px;
  margin-bottom: 32px;
}

.d-head h2, .d-price h2, .d-ask h2 {
  margin: 0;
  color: var(--text-bright);
  font-size: clamp(30px, 4.2vw, 52px);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.04em;
}

.d-head p { margin: 0; color: var(--secondary); max-width: 46ch; }

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

.d-moment {
  display: grid;
  gap: 10px;
  align-content: start;
  padding: 28px;
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.d-moment h3 {
  margin: 0;
  color: var(--text-bright);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.d-moment p { margin: 0; color: var(--secondary); }
.d-moment a { margin-top: 6px; font-weight: 600; text-decoration: none; }
.d-moment a:hover { text-decoration: underline; }

.d-ask .iris-film video,
.d-ask .iris-film img {
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}

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

.d-product {
  display: grid;
  gap: 8px;
  align-content: start;
  padding: 24px;
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
  color: inherit;
  text-decoration: none;
  transition: transform 200ms ease-out, box-shadow 200ms ease-out;
}

.d-product:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }

.d-product img { width: 64px; height: 64px; margin-bottom: 8px; }

.d-product h3 {
  margin: 0;
  color: var(--text-bright);
  font-size: 20px;
  font-weight: 650;
  letter-spacing: -0.02em;
}

.d-product p { margin: 0; color: var(--secondary); font-size: 15px; }

.d-terms {
  margin-top: 6px;
  color: var(--link);
  font-family: var(--f-mono);
  font-size: 12.5px;
  font-weight: 500;
}

.d-price {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 40px 56px;
  align-items: center;
  padding: clamp(28px, 4vw, 56px);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.d-price > div { display: grid; gap: 16px; justify-items: start; }

.d-price-list {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.d-price-list li {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 14px 0;
  border-top: 1px solid var(--border);
}

.d-price-list li:first-child { border-top: 0; }

.d-price-list b {
  color: var(--text-bright);
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.04em;
}

.d-price-list small { margin-left: 3px; color: var(--muted); font-size: 14px; font-weight: 500; letter-spacing: 0; }
.d-price-list span { color: var(--secondary); font-size: 15px; }

.d-more { margin: 24px 0 0; color: var(--secondary); font-size: 14px; }

@media (max-width: 900px) {
  .d-moments { grid-template-columns: 1fr; }
  .d-family { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .d-price { grid-template-columns: 1fr; }
  .d-hero, .d-pagehead { padding-top: 48px; }
}

@media (max-width: 520px) {
  .d-family { grid-template-columns: 1fr; }
  .d-price-list li { grid-template-columns: 64px minmax(0, 1fr); }
  .d-price-list b { font-size: 32px; }
}

.cta-band .button-quiet {
  color: #ffffff;
  background: transparent;
  border-color: rgba(255, 255, 255, 0.7);
}

.cta-band .button-quiet:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.14);
  border-color: #ffffff;
}

.section-no { display: none; }

input, select, textarea {
  border-radius: var(--r-sm);
}

/* Daylight: the input and button are separate pills, so they need a gap and
   the input its full border (the joined, shared-edge look was the old idiom). */
.notify-row { gap: 8px; }
.notify-row input { border-radius: var(--r-full); padding-inline: 20px; border: 1px solid var(--input-border); }

.spec { margin-bottom: 28px; }
.table-scroll .spec, .spec-pair .spec:last-child { margin-bottom: 0; }
.table-scroll { position: relative; }
@media (max-width: 600px) { .spec th, .spec td { padding: 12px 10px; } .spec td:first-child { padding-right: 10px; } }

/* ═══════════════════════════════════════════════════════════════════════════
   DAYLIGHT, EVERY PAGE (2026-10-02). The inner pages catch up with the home
   page: hairlines in place of black rules, radius on every box, Geist for
   prose, mono for figures and identifiers only, and product drawings that sit
   on the page rather than a navy panel.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The masthead's two small boxes are drawn by ::before, so the radius goes there. */
.theme-toggle::before,
.signin::before { border-radius: var(--r-full); }

/* Light: the current-page pill is --accent on --accent-soft, 4.37:1. One step darker clears 4.5. */
:root:not([data-theme="dark"]) .masthead nav a[aria-current="page"],
:root:not([data-theme="dark"]) .masthead .nav-disclosure[data-current="true"] { color: var(--accent-hover); }

/* ---------- headings and rules ---------- */
.hero {
  padding: 80px 0 56px;
  border-bottom: 0;
}

.hero h1 {
  max-width: 20ch;
  font-size: clamp(36px, 5.4vw, 64px);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.04em;
}

.modules,
.clause,
.integrator,
.quote-total,
.src-release { border-top: 1px solid var(--border); }
.clause { border-bottom: 1px solid var(--border); }
.dl th { border-bottom: 1px solid var(--border); }

/* ---------- tables: prose in Geist, figures and identifiers in mono ---------- */
.spec td:last-child {
  font-family: var(--f-ui);
  font-size: 15px;
}

.spec td code,
.spec td .mono,
.spec .hash,
.spec-pkg td:nth-child(3),
.spec-pkg td:nth-child(4),
.config-quote .spec td:last-child,
.dl .c-size,
.dl .c-ver { font-family: var(--f-mono); }

/* ---------- forms: radius, Geist ---------- */
input, select, textarea,
.apply-field input,
.apply-field select,
.apply-field textarea {
  border-radius: var(--r-sm);
  font-family: var(--f-ui);
}

#lead-dialog { border-radius: var(--r-lg); box-shadow: var(--shadow-lg); }

.checker-chips button {
  border-radius: var(--r-full);
  font-family: var(--f-ui);
  font-size: 13px;
  padding: 6px 14px;
}

.pre { border-radius: var(--r-sm); font-size: 13px; }

/* ---------- product drawings: a card on the page ----------
   Daylight draws them in ink on paper. Night keeps the light-on-dark strokes
   the drawings were made with, on --paper instead of navy. No dot grid. */

























/* ---------- theme switch: the manual's (VitePress) appearance switch ----------
   main.js turns the masthead button into a 40x22 track with a sliding knob;
   the button keeps its 44px hit area. Colors match the manual exactly. */
.theme-toggle.ts-switch {
  --ts-track: #ECEEF2;
  --ts-edge: #D3D7DE;
  --ts-knob: #FFFFFF;
  --ts-ico: #4B5262;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  font-size: 0;
  letter-spacing: 0;
}
:root[data-theme="dark"] .theme-toggle.ts-switch {
  --ts-track: #1D2027;
  --ts-edge: #323743;
  --ts-knob: #000000;
  --ts-ico: #F1F2F5;
}
.theme-toggle.ts-switch::before { content: none; }
.ts-track {
  position: relative;
  display: block;
  width: 40px;
  height: 22px;
  border-radius: 11px;
  border: 1px solid var(--ts-edge);
  background: var(--ts-track);
  transition: border-color 0.25s;
}
.theme-toggle.ts-switch:hover .ts-track { border-color: var(--accent); }
.ts-knob {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ts-knob);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
  transition: transform 0.25s;
  display: grid;
  place-items: center;
}
:root[data-theme="dark"] .ts-knob { transform: translateX(18px); }
.ts-ico {
  grid-area: 1 / 1;
  width: 12px;
  height: 12px;
  fill: none;
  stroke: var(--ts-ico);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: opacity 0.25s;
}
.ts-moon { opacity: 0; }
:root[data-theme="dark"] .ts-sun { opacity: 0; }
:root[data-theme="dark"] .ts-moon { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .ts-knob, .ts-ico, .ts-track { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   /pricing, Daylight layout (2026-10-02). Cards on the page backdrop, one
   comparison table, a configurator with a sticky summary, and one card for the
   cost-over-time chart. Reuses .d-eyebrow / .d-head / .d-section / .d-lede and
   the tokens above. Everything here is namespaced pr-; the ot-* names are the
   ones pricing-time.js draws into the chart with.

   Beam: ONE, on the recommended edition card. Prices are Geist, tabular.
   Mono appears only in the shared eyebrow.
   ═══════════════════════════════════════════════════════════════════════════ */

#editions, #configurator, #addons, #licences, #over-time { scroll-margin-top: 116px; }

.pr-card {
  padding: clamp(24px, 3.5vw, 40px);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-hint {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
}

.pr-label {
  display: block;
  margin: 22px 0 8px;
  padding: 0;
  color: var(--text-bright);
  font-size: 14px;
  font-weight: 600;
}
.pr-label:first-child { margin-top: 0; }

/* A price is a figure: Geist, tabular, tight. */
.pr-price, .pr-hero-price, .pr-total b, .pr-opt-price, .pr-lines td:last-child,
.pr-compare .is-price td, .pr-licence, .pr-slider output, .pr-plain td {
  font-family: var(--f-ui);
  font-variant-numeric: tabular-nums;
}

/* ---------- hero ---------- */
.pr-hero { padding-bottom: 24px; }
.pr-hero .d-lede { margin-top: 4px; }

.pr-hero-price {
  margin: 20px 0 0;
  color: var(--text-bright);
  font-size: clamp(76px, 15vw, 152px);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.055em;
}

.pr-hero-line {
  margin: 0;
  color: var(--secondary);
  font-size: clamp(17px, 2vw, 22px);
  font-weight: 500;
}

/* ---------- editions ---------- */
.pr-editions {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  align-items: stretch;
}

.pr-ed {
  position: relative;
  display: grid;
  gap: 8px;
  align-content: start;
  justify-items: start;
  padding: 36px 32px 32px;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-ed.is-rec::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--beam);
}

.pr-badge {
  position: absolute;
  top: 20px;
  right: 20px;
  margin: 0;
  padding: 4px 12px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent-hover);
  font-size: 12px;
  font-weight: 600;
}

.pr-sym { display: block; margin-bottom: 8px; }
.pr-sym img { display: block; width: 56px; height: 56px; }

.pr-ed h3 {
  margin: 0;
  color: var(--text-bright);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.pr-ed-sub { margin: 0; color: var(--secondary); font-size: 16px; }

.pr-price {
  margin: 10px 0 8px;
  color: var(--text-bright);
  font-size: clamp(44px, 5vw, 60px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.05em;
}

.pr-ed .button { margin-bottom: 14px; }

.pr-ticks {
  display: grid;
  gap: 10px;
  width: 100%;
  margin: 0;
  padding: 20px 0 0;
  border-top: 1px solid var(--border);
  list-style: none;
}

.pr-ticks li {
  position: relative;
  padding-left: 26px;
  color: var(--text);
  font-size: 15px;
  line-height: 1.45;
}

.pr-ticks li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 3px;
  width: 6px;
  height: 11px;
  border: solid var(--accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ---------- configurator ---------- */
.pr-cfg {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 400px);
  gap: 32px;
  align-items: start;
}

.pr-set {
  min-width: 0;
  margin: 0 0 36px;
  padding: 0;
  border: 0;
}

.pr-set legend {
  margin: 0 0 14px;
  padding: 0;
  color: var(--text-bright);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.pr-set > .pr-hint { margin-bottom: 14px; }

.pr-opts { display: grid; gap: 12px; }
.pr-opts-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.pr-opts-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.pr-opt-row { position: relative; display: grid; }
.pr-opt-row[hidden] { display: none; }

/* The real input stays focusable and in the accessibility tree; the card
   beside it is what is drawn. pricing.js reads the inputs directly. */
.pr-opt-row > input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.pr-opt {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "name price" "sub sub";
  align-content: start;
  gap: 6px 12px;
  padding: 18px 20px;
  border: 1.5px solid var(--border);
  border-radius: var(--r-md);
  background: var(--paper);
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: border-color 160ms ease-out, background-color 160ms ease-out;
}

.pr-opt:hover { border-color: var(--border-strong); }

.pr-opt-name {
  grid-area: name;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-bright);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
}

/* Selection mark: a ring for "pick one", a ring with a tick for "pick any". */
.pr-opt-name::before {
  content: "";
  flex: none;
  width: 18px; height: 18px;
  border: 1.5px solid var(--input-border);
  border-radius: 50%;
  background: var(--input);
}

.pr-opt-sub { grid-area: sub; color: var(--muted); font-size: 13px; line-height: 1.45; }

.pr-opt-price {
  grid-area: price;
  color: var(--text-bright);
  font-size: 16px;
  font-weight: 600;
  white-space: nowrap;
}

.pr-opt-row input:checked + .pr-opt {
  border-color: var(--accent);
  background: var(--accent-wash);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow);
}

.pr-opt-row input:checked + .pr-opt .pr-opt-name::before {
  border-color: var(--accent-fill);
  background: var(--accent-fill);
}

.pr-opt-row input[type="radio"]:checked + .pr-opt .pr-opt-name::before {
  box-shadow: inset 0 0 0 3.5px var(--paper);
}

.pr-opt-row input[type="checkbox"]:checked + .pr-opt::after {
  content: "";
  position: absolute;
  left: 27.5px;
  top: 22px;
  width: 5px;
  height: 9px;
  border: solid var(--accent-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.pr-opt-row input:focus-visible + .pr-opt {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.pr-opt-row input:disabled + .pr-opt {
  cursor: not-allowed;
  background: var(--sunken);
  box-shadow: none;
}
.pr-opt-row input:disabled + .pr-opt .pr-opt-name,
.pr-opt-row input:disabled + .pr-opt .pr-opt-price { color: var(--muted); }

/* Alarm notification and its channels travel together; the rule at the left
   says the channels hang off it. */
/* The wrapper dissolves so the channels sit in the grid's second column,
   straight under the card they belong to, without stretching their neighbor. */
.pr-group { display: contents; }

.pr-channels {
  position: relative;
  display: grid;
  grid-column: 2;
  gap: 10px;
  margin-left: 18px;
  padding-left: 18px;
}

.pr-channels::before {
  content: "";
  position: absolute;
  left: 0; top: -12px; bottom: 26px;
  width: 2px;
  border-radius: 2px;
  background: var(--border-strong);
}

.pr-slider {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 6px 0 10px;
  padding: 16px 20px;
  border-radius: var(--r-md);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-slider input[type="range"] { flex: 1; min-width: 0; accent-color: var(--accent-fill); }

.pr-slider output {
  min-width: 4.5ch;
  color: var(--text-bright);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-align: right;
}

/* ---------- the sticky summary ---------- */
.pr-summary {
  position: sticky;
  top: 124px;
  padding: 28px;
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-lg);
}

.pr-sum-title {
  margin: 0 0 8px;
  color: var(--text-bright);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.pr-lines { width: 100%; border-collapse: collapse; }

.pr-lines td {
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.4;
  vertical-align: top;
}

.pr-lines td:last-child {
  padding-left: 16px;
  color: var(--text-bright);
  font-weight: 600;
  text-align: right;
  white-space: nowrap;
}

.pr-total {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 4px 12px;
  margin: 20px 0 6px;
  color: var(--muted);
  font-size: 14px;
  font-weight: 600;
}

.pr-total b {
  color: var(--text-bright);
  font-size: clamp(36px, 4vw, 46px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.045em;
}

.pr-summary .button { display: flex; width: 100%; margin-top: 12px; text-align: center; }
.pr-summary .pr-hint { margin-top: 4px; }
#copy-status:empty, #quote-note:empty { display: none; }

/* ---------- cost over time ---------- */
.pr-time-in {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 32px 56px;
  align-items: start;
}

.pr-money {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 56px;
  padding: 0 18px;
  border: 1.5px solid var(--input-border);
  border-radius: var(--r-md);
  background: var(--input);
}

.pr-money span { color: var(--muted); font-size: 20px; font-weight: 500; }

.pr-money input {
  flex: 1;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
  outline: none;
  background: transparent;
  color: var(--text-bright);
  font-family: var(--f-ui);
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.pr-money input::placeholder { color: var(--muted); font-size: 16px; font-weight: 400; letter-spacing: 0; }

.pr-money:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.pr-money + .pr-hint { margin-top: 10px; }

.pr-licence { margin: 0 0 10px; color: var(--text-bright); font-size: 17px; font-weight: 600; }

.pr-check {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.45;
  cursor: pointer;
}
.pr-check input { margin: 3px 0 0; accent-color: var(--accent-fill); }

.pr-years { min-width: 0; margin: 0; padding: 0; border: 0; }
.pr-years legend.pr-label { margin-top: 24px; }

.pr-seg {
  display: inline-flex;
  position: relative;
  padding: 4px;
  border-radius: var(--r-full);
  background: var(--fill);
}

.pr-seg input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

.pr-seg label {
  padding: 9px 20px;
  border-radius: var(--r-full);
  color: var(--secondary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 160ms ease-out, color 160ms ease-out;
}

.pr-seg input:checked + label {
  background: var(--paper);
  color: var(--text-bright);
  box-shadow: 0 1px 2px rgb(12 14 19 / 0.14), 0 2px 8px rgb(12 14 19 / 0.08);
}

.pr-seg input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

.pr-summary-line {
  margin: 0 0 14px;
  color: var(--text-bright);
  font-size: 18px;
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
}

.pr-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
}

.pr-legend li {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border-radius: var(--r-full);
  background: var(--fill);
  color: var(--secondary);
  font-size: 13px;
  font-weight: 500;
}

.pr-legend i {
  display: inline-block;
  width: 14px;
  height: 4px;
  margin-right: 8px;
  border-radius: 2px;
}
.pr-legend .is-current { background: var(--series-current); }
.pr-legend .is-raylux { background: var(--series-raylux); }

/* The chart's own text, in Geist at the muted color. Line colors stay as
   the series tokens set them. */
#ot-chart .ot-tick {
  fill: var(--muted);
  font-family: var(--f-ui);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
#ot-chart .ot-end { font-family: var(--f-ui); font-size: 13px; }
#ot-chart .ot-grid line { stroke: var(--border-subtle); }
#ot-chart .ot-dot { stroke: var(--paper); }

.ot-tip {
  padding: 10px 14px;
  border: 0;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-family: var(--f-ui);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.ot-tip b { font-size: 13px; }

.pr-year-details { margin-top: 24px; }

.pr-year-details summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 18px;
  border-radius: var(--r-full);
  background: var(--fill);
  color: var(--text-bright);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.pr-year-details summary::-webkit-details-marker { display: none; }
.pr-year-details summary::after { content: "+"; color: var(--secondary); font-size: 16px; line-height: 1; }
.pr-year-details[open] summary::after { content: "\2212"; }
.pr-year-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pr-year-details .pr-scroll { margin-top: 14px; }
.pr-time > .pr-hint { margin: 24px 0 0; }

.pr-scroll { overflow-x: auto; }
.pr-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pr-plain { width: 100%; border-collapse: collapse; }
.pr-plain th {
  padding: 10px 16px 10px 0;
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
}
.pr-plain td {
  padding: 10px 16px 10px 0;
  border-bottom: 1px solid var(--border-subtle);
  color: var(--text);
  font-size: 14px;
}

/* ---------- license comparison ---------- */
.pr-compare-wrap {
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-compare {
  width: 100%;
  min-width: 780px;
  border-collapse: separate;
  border-spacing: 0;
}

.pr-compare th, .pr-compare td {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  font-size: 15px;
  line-height: 1.45;
  text-align: left;
  vertical-align: top;
}

.pr-compare tbody tr:last-child > * { border-bottom: 0; }

.pr-compare thead th {
  padding-top: 22px;
  padding-bottom: 18px;
  color: var(--text-bright);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.pr-compare tbody th {
  width: 170px;
  color: var(--muted);
  font-weight: 600;
}

.pr-compare td { color: var(--text); }

/* The first column stays put while the table scrolls sideways on a phone. */
.pr-compare thead th:first-child,
.pr-compare tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--paper);
  border-right: 1px solid var(--border);
}

.pr-compare .is-price td {
  color: var(--text-bright);
  font-size: 32px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.045em;
}

/* ---------- add-ons ---------- */
.pr-addons {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.pr-add {
  display: grid;
  gap: 6px;
  align-content: start;
  padding: 32px;
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-addons .pr-add:first-child { grid-row: span 2; }
.pr-addons .pr-add:last-child { grid-column: 1 / -1; }

.pr-add h3 {
  margin: 0;
  color: var(--text-bright);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.03em;
}

.pr-add .pr-price { margin: 4px 0 2px; }
.pr-add-sub { margin: 0 0 14px; color: var(--secondary); font-size: 16px; }

.pr-facts { display: grid; margin: 0; padding: 0; list-style: none; }

.pr-facts li {
  display: grid;
  grid-template-columns: minmax(110px, 34%) minmax(0, 1fr);
  gap: 4px 16px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
  font-size: 15px;
  line-height: 1.45;
}

.pr-facts li > span { color: var(--muted); }
.pr-facts li > b { color: var(--text); font-weight: 500; }
.pr-facts li > b > span { color: var(--text-bright); font-weight: 650; font-variant-numeric: tabular-nums; }

.pr-bundle {
  margin: 14px 0 0;
  padding: 14px 18px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--text);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.pr-bundle b { color: var(--text-bright); }
.pr-bundle span { color: var(--text-bright); font-weight: 650; }

/* ---------- FAQ ---------- */
.pr-faq-sec { padding: 44px 0; }

.pr-faq-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 24px 56px;
  align-items: start;
}

.pr-faq-grid .d-head { margin-bottom: 0; }
.pr-faq { display: grid; gap: 10px; }

.pr-faq details {
  border-radius: var(--r-md);
  background: var(--paper);
  box-shadow: var(--shadow);
}

.pr-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
  color: var(--text-bright);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;
}
.pr-faq summary::-webkit-details-marker { display: none; }
.pr-faq summary::after {
  content: "+";
  flex: none;
  color: var(--secondary);
  font-size: 22px;
  font-weight: 400;
  line-height: 1;
}
.pr-faq details[open] summary::after { content: "\2212"; }
.pr-faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-md); }

.pr-faq details > p { margin: 0; padding: 0 24px 22px; color: var(--secondary); line-height: 1.6; }

.pr-rows { margin: 0; padding: 0 24px 8px; list-style: none; }
.pr-rows li { padding: 12px 0; border-top: 1px solid var(--border); }
.pr-rows b { display: block; color: var(--text-bright); font-weight: 600; }
.pr-rows span { color: var(--secondary); }

/* ---------- planned ---------- */
.pr-tag {
  display: inline-block;
  margin: 0 0 12px;
  padding: 6px 14px;
  border-radius: var(--r-full);
  background: var(--fill);
  color: var(--secondary);
  font-size: 13px;
  font-weight: 600;
}

.pr-plan-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 48px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pr-plan-list li { padding: 14px 0; border-top: 1px solid var(--border); }
.pr-plan-list b { display: block; color: var(--text-bright); font-weight: 600; }
.pr-plan-list span { color: var(--secondary); font-size: 15px; }

/* ---------- narrow ---------- */
@media (max-width: 1000px) {
  .pr-editions { grid-template-columns: minmax(0, 1fr); }
  .pr-cfg { grid-template-columns: minmax(0, 1fr); }
  .pr-summary { position: static; }
  .pr-time-in { grid-template-columns: minmax(0, 1fr); }
  .pr-addons { grid-template-columns: minmax(0, 1fr); }
  .pr-addons .pr-add:first-child { grid-row: auto; }
  .pr-faq-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 640px) {
  .pr-opts-3, .pr-opts-2 { grid-template-columns: minmax(0, 1fr); }
  .pr-channels { grid-column: auto; }
  .pr-plan-list { grid-template-columns: minmax(0, 1fr); }
  .pr-facts li { grid-template-columns: minmax(0, 1fr); }
  .pr-ed, .pr-add { padding: 28px 24px; }
  .pr-summary { padding: 24px 20px; }
  .pr-hero .hero-actions { width: 100%; }
  .pr-hero .hero-actions .button { flex: 1 1 auto; }
}

@media (prefers-reduced-motion: reduce) {
  .pr-opt, .pr-seg label { transition: none; }
}

/* Pricing comparison: the Edge columns' single fixed-scope cell. */
.cmp-fixed { vertical-align: middle; color: var(--secondary); }

/* ---------- impeccable pass (2026-10) ---------- */
/* Anchors land below the sticky masthead, not under it. */
html { scroll-padding-top: 112px; }

/* Browser surfaces carry the palette. */
::selection { background: var(--accent-wash); color: var(--text-bright); }
:root[data-theme="dark"] ::selection { background: rgb(92 141 255 / 0.35); }
input, textarea, select { caret-color: var(--accent); }

h1, h2, h3 { text-wrap: balance; }
p, li, figcaption { text-wrap: pretty; }

/* Touch: text links that sit alone on a line get a 44px target on phones. */
@media (max-width: 760px) {
  .d-moment a,
  .dl td a,
  .spec-pkg td a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .d-moment a { margin-top: 0; }
  .version-pick select { min-height: 44px; }
}

/* ---------- the Iris mark on the home page ----------
   The eye sits above the "New in 2026.20" eyebrow. Its motion (open, glance,
   blink) lives inside raylux-iris-*-alive.svg; tour.js swaps it in on scroll. */
.iris-mark {
  width: 112px;
  height: 112px;
  margin: -14px 0 6px -10px;
}
.iris-mark img {
  display: block;
  width: 100%;
  height: 100%;
}
@media (max-width: 760px) {
  .iris-mark { width: 84px; height: 84px; margin: -10px 0 4px -8px; }
}

/* The Iris eyebrow's beam flows, like the rainbow around the Iris eye. One tile
   is the beam with its first color repeated at the end, so the loop is seamless;
   it moves one tile (2 x the 28px line) per 2.4 s, the spec's beam speed. */
.d-ask .d-eyebrow-beam::before {
  background: linear-gradient(90deg, #cb1eff, #2a42ff 14%, #15e4ff 28%, #06aa00 43%, #fff805 57%, #ff5502 71%, #ff0000 86%, #cb1eff);
  background-size: 56px 100%;
  background-repeat: repeat-x;
  animation: beam-flow 2.4s linear infinite;
}
@keyframes beam-flow {
  from { background-position: 0 0; }
  to { background-position: -56px 0; }
}
@media (prefers-reduced-motion: reduce) {
  .d-ask .d-eyebrow-beam::before { animation: none; }
}

/* ---------- shop: buyer details, quantity ---------- */
.pr-details { gap: 22px 20px; }
/* Customer and site: the customer, new-customer, site and new-site blocks stack with the same gap as the fields in them. */
#owner-edit > * + * { margin-top: 22px; }

/* The marker sits on its own line under the label, so a long one ("required
   for an invoice") can never run past the column or into its neighbour. */
.pr-details .apply-field .req {
  display: block;
  margin: 4px 0 0;
}

.pr-qty { max-width: 140px; }
.pr-qty input { text-align: left; }

#shop-status:empty { display: none; }

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