/* ============================================================
   EDITORIAL — shared layout system (`.ed-*`)
   Brand-constrained: #6B46C1 / #8B5CF6 / #EDE9FE accents on
   pure #FFFFFF (light) and #000000 (dark). Support greys only.
   Exactly one gradient PER PAGE — the approved hero gradient
   on the accent headline. No shadows, no decorative elements,
   square corners, 1px hairline rules.

   Linked unconditionally from base.html, so every page can reach
   it. That costs one ~16KB request on pages that use none of it;
   the alternative — duplicating the token block and the frame
   arithmetic into each template's `head_extra` — is how the home
   page and the product pages drifted apart in the first place.

   Lives in a plain stylesheet rather than a `type="text/tailwindcss"`
   block on purpose: Tailwind is a CDN *script* here, so anything
   compiled through it disappears if the CDN is blocked. The
   measured contrast ratios below are load-bearing, not polish.
============================================================ */

:root {
  --ed-bg:          #FFFFFF;
  --ed-alt:         #F7F7F7;
  --ed-fg:          #0A0A0A;
  /* #767676 measured 4.24:1 against --ed-alt (#F7F7F7), below the 4.5:1
     WCAG AA floor for normal text — and most muted copy on this page sits
     on an alt band. #6E6E6E measures 4.73:1 on --ed-alt and 5.09:1 on
     --ed-bg, so it clears AA on both surfaces. The dark scope needs the
     opposite correction; see .dark below. */
  --ed-muted:       #6E6E6E;
  --ed-rule:        #E5E5E5;
  --ed-brand:       #6B46C1;
  --ed-brand-light: #8B5CF6;
  --ed-tint:        #EDE9FE;
  --ed-ease:        cubic-bezier(0.23, 1, 0.32, 1);
}

.dark {
  --ed-bg:   #000000;
  --ed-alt:  #0A0A0A;
  --ed-fg:   #FFFFFF;
  /* Muted grey has to move the other way on dark: against --ed-alt
     (#0A0A0A) the light-mode #6E6E6E would drop to ~3.8:1. #7A7A7A
     measures 4.61:1 on --ed-alt and 4.89:1 on --ed-bg (#000000). */
  --ed-muted: #7A7A7A;
  --ed-rule: rgba(255, 255, 255, 0.14);
  --ed-tint: rgba(107, 70, 193, 0.14);
}

/* ─── Frame ─── */
/* Matches the nav container exactly: partials/nav.html uses
   `max-w-6xl mx-auto px-6`, i.e. a 1152px box with 24px of padding, so its
   content measures 1104px and insets 24px once the viewport is narrower.
   Keep these two numbers in sync or the page edge drifts off the nav edge. */
.ed-frame { width: min(1104px, calc(100% - 48px)); margin: 0 auto; }
.ed-band  { background: var(--ed-bg); color: var(--ed-fg); padding: 130px 0; transition: background 0.3s ease, color 0.3s ease; }
.ed-band--alt { background: var(--ed-alt); }
.ed-sub { margin-top: 118px; padding-top: 70px; border-top: 1px solid var(--ed-rule); }

/* ─── Display type ─── */
.ed-h1 {
  font-family: 'Raleway', sans-serif; font-weight: 300;
  font-size: clamp(2.9rem, 6.4vw, 4.5rem); line-height: 1.0;
  letter-spacing: -0.045em; color: var(--ed-fg);
}
.ed-h2 {
  font-family: 'Raleway', sans-serif; font-weight: 300;
  font-size: clamp(2.2rem, 4.6vw, 3.25rem); line-height: 1.04;
  letter-spacing: -0.04em; color: var(--ed-fg);
}
.ed-h3 {
  font-family: 'Raleway', sans-serif; font-weight: 500;
  font-size: 1.32rem; line-height: 1.18;
  letter-spacing: -0.025em; color: var(--ed-fg);
}
.ed-h1 span, .ed-h2 span { display: block; }

/* The one approved gradient: hero accent only. */
.ed-accent {
  background: linear-gradient(135deg, #6B46C1 0%, #8B5CF6 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.ed-em { color: var(--ed-brand); font-style: normal; }
.dark .ed-em { color: var(--ed-brand-light); }

/* ─── Body type ─── */
.ed-lead  { font-size: 1.3rem; font-weight: 500; line-height: 1.38; letter-spacing: -0.02em; color: var(--ed-fg); }
.ed-body  { font-size: 0.95rem; line-height: 1.72; color: var(--ed-muted); }
.ed-body--lg { font-size: 1.05rem; line-height: 1.66; color: var(--ed-muted); }
.ed-note  { font-size: 0.8rem; line-height: 1.6; color: var(--ed-muted); }

/* Visually hidden but read aloud. Tailwind ships an `sr-only` utility, but
   this editorial system is otherwise self-contained and must not break if the
   CDN is blocked or fails to scan — the "opens in a new tab" warnings on the
   outbound app links are the only thing standing between a screen-reader user
   and an unannounced context switch, so they get a local rule. */
.ed-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ─── Buttons ─── */
.ed-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  min-height: 52px; padding: 0 26px;
  background: var(--ed-brand); color: #FFFFFF;
  font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  transition: background 180ms var(--ed-ease), transform 180ms var(--ed-ease);
}
.ed-btn:hover  { background: var(--ed-brand-light); transform: translateY(-2px); }
.ed-btn:active { transform: scale(0.98); }

.ed-btn-ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  min-height: 52px; padding: 0 26px;
  background: transparent; color: var(--ed-fg);
  border: 1px solid var(--ed-rule);
  font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  transition: border-color 180ms var(--ed-ease), transform 180ms var(--ed-ease);
}
.ed-btn-ghost:hover { border-color: var(--ed-brand); transform: translateY(-2px); }

.ed-link {
  display: inline-flex; align-items: center; gap: 9px;
  color: var(--ed-brand);
  font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  transition: color 180ms var(--ed-ease);
}
.dark .ed-link { color: var(--ed-brand-light); }
.ed-link:hover { color: var(--ed-brand-light); }

/* ─── Hero ─── */
/* isolation + overflow make this the stacking context the orbital canvas
   sits inside: the hero's own background paints first, the z-index:-1 canvas
   above it, then in-flow content above that. overflow clips the outer rings
   so they can never widen the document. */
.ed-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ed-bg); color: var(--ed-fg);
  padding: 170px 0 96px;
  transition: background 0.3s ease, color 0.3s ease;
}

/* ─── Orbital wave ─── */
/* Canvas is injected by /static/js/orbital-wave.js, so with JS off the hero
   is simply flat — nothing to clean up and no layout reserved. The left-hand
   mask fades the geometry out behind the headline, which keeps the text on
   plain --ed-bg and leaves the measured contrast ratios untouched. */
.ed-orbital {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 44%, #000 66%, #000 100%);
          mask-image: linear-gradient(to right, transparent 0%, transparent 44%, #000 66%, #000 100%);
}
.ed-orbital canvas { display: block; }
/* The breakpoint is arithmetic, not taste. The hero paragraph is a fixed
   560px wide inside .ed-frame (width min(1104px, 100% - 48px), centred), but
   the mask stops are percentages of the viewport — so as the viewport
   narrows, the opaque edge at 0.66vw marches leftward INTO the text while
   the text itself stops shrinking. Ink only clears the copy while

       0.66 * vw  >  (vw - frameWidth) / 2 + 560

   which holds down to roughly 1010px and fails below it. At 768px the result
   was rings crossing every line of the paragraph. So: hidden under 1024px
   rather than scaled down, because a shrunken system just reads as stray
   scratches behind the copy. display: none also collapses the host to a zero
   box, so the IntersectionObserver parks the rAF loop and narrow devices do
   no canvas work at all. */
@media (max-width: 1023px) {
  .ed-orbital { display: none; }
}
/* Product-page hero label. The home hero has no eyebrow — it opens straight
   on the headline — but a product page has to name the product before the
   claim, which is what the old `uppercase tracking-widest text-xs` span did.
   Sized to match .ed-lockup-label so the two read as the same voice. */
.ed-eyebrow { display: block; color: var(--ed-brand); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.dark .ed-eyebrow { color: var(--ed-brand-light); }

/* Product mark + eyebrow on one line, for the products that have a logo.
   34px matches the eyebrow's line box closely enough to sit on the baseline
   without a nudge. */
.ed-hero-mark { display: flex; align-items: center; gap: 13px; }
.ed-hero-mark img { display: block; width: 34px; height: 34px; }
.ed-hero-mark .ed-eyebrow { display: inline; }

.ed-hero-copy { max-width: 820px; margin-top: 34px; }
.ed-hero-copy > p.ed-body--lg { max-width: 560px; margin-top: 28px; }
/* Two app CTAs now, not a CTA plus a jump link. 18px reads as a pair of
   controls; 28px read as a control and an afterthought. */
.ed-hero-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 38px; }
.ed-lockup { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 90px; padding-top: 26px; border-top: 1px solid var(--ed-rule); color: var(--ed-muted); font-size: 0.68rem; letter-spacing: 0.04em; }
.ed-lockup strong { color: var(--ed-fg); font-family: 'Raleway', sans-serif; font-size: 1.3rem; font-weight: 500; letter-spacing: -0.06em; }
.ed-lockup-label { color: var(--ed-brand); font-size: 0.6rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.dark .ed-lockup-label { color: var(--ed-brand-light); }
.ed-lockup-rule { width: 32px; height: 1px; background: var(--ed-rule); }

/* ─── About ─── */
.ed-about-layout { display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(340px, 0.85fr); gap: 8vw; margin-top: 58px; }
.ed-about-body { max-width: 520px; }
.ed-about-body > p + p { margin-top: 22px; }
.ed-about-body .ed-link { margin-top: 30px; }

.ed-belief { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 110px; border-top: 1px solid var(--ed-rule); }
.ed-belief > div { min-height: 130px; padding: 20px 28px 0 0; border-right: 1px solid var(--ed-rule); }
.ed-belief > div + div { padding-left: 28px; }
.ed-belief > div:last-child { border-right: 0; }
.ed-belief span { color: var(--ed-brand); font-size: 0.6rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.dark .ed-belief span { color: var(--ed-brand-light); }
.ed-belief p { max-width: 210px; margin-top: 20px; font-size: 1rem; font-weight: 500; line-height: 1.38; letter-spacing: -0.02em; color: var(--ed-fg); }

/* ─── Hairline card grids ─── */
.ed-grid-4, .ed-grid-3, .ed-grid-2 { display: grid; gap: 1px; background: var(--ed-rule); border: 1px solid var(--ed-rule); }
.ed-grid-4 { grid-template-columns: repeat(4, 1fr); }
.ed-grid-3 { grid-template-columns: repeat(3, 1fr); }
.ed-grid-2 { grid-template-columns: repeat(2, 1fr); }

.ed-cell {
  background: var(--ed-bg); padding: 24px 26px 30px;
  transition: background 200ms var(--ed-ease), transform 200ms var(--ed-ease);
}
.ed-band--alt .ed-cell { background: var(--ed-alt); }
.ed-cell:hover { background: var(--ed-tint); transform: translateY(-4px); }
.ed-cell-meta { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; color: var(--ed-brand); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; }
.dark .ed-cell-meta { color: var(--ed-brand-light); }
.ed-cell p { margin-top: 16px; font-size: 0.85rem; line-height: 1.68; color: var(--ed-muted); }
.ed-cell-source { display: block; margin-top: 20px; padding-top: 12px; border-top: 1px solid var(--ed-rule); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ed-muted); }
/* Both selectors are load-bearing. `.ed-cell p` above is (0,1,1) and this was
   a bare `.ed-cell-stat` at (0,1,0), so inside a cell — which is the only
   place it is ever used — the muted 0.85rem body rule won on font-size and
   colour and the stat rendered as ordinary paragraph text. Measured at 13.6px
   / #6E6E6E instead of 38.4px / #6B46C1 on the home page's three breach
   figures. `.ed-cell p.ed-cell-stat` is (0,2,1) and settles it; the unadorned
   class stays for any future use outside a cell. */
.ed-cell-stat,
.ed-cell p.ed-cell-stat { margin-top: 0; font-family: 'Raleway', sans-serif; font-size: 2.4rem; font-weight: 300; line-height: 1; letter-spacing: -0.04em; color: var(--ed-brand); }
.dark .ed-cell-stat,
.dark .ed-cell p.ed-cell-stat { color: var(--ed-brand-light); }
.ed-cell-title { margin-top: 54px; }
.ed-cell-title--tight { margin-top: 20px; }

/* ─── Section intro block ─── */
.ed-intro { max-width: 860px; margin-top: 56px; }
.ed-intro .ed-h2 { margin-top: 20px; }
.ed-intro > p.ed-body { max-width: 580px; margin-top: 24px; }
.ed-intro-title { margin-top: 20px; }

/* Sub-block headings inside a numbered section */
.ed-sub-title   { margin-top: 20px; max-width: 820px; }
.ed-sub-body    { max-width: 680px; margin-top: 22px; }
.ed-sub-note    { margin-top: 24px; max-width: 660px; }
.ed-sub-actions { margin-top: 26px; }
.ed-cards       { margin-top: 56px; }
.ed-col-key     { width: 34%; }
.ed-checks--flush { margin-bottom: 0; }
.ed-tenure-action { flex-shrink: 0; }

/* ─── Urgency callout ─── */
.ed-callout {
  display: flex; gap: 18px; align-items: flex-start;
  margin-top: 56px; padding: 26px 28px;
  border-left: 2px solid var(--ed-brand);
  background: var(--ed-tint);
}
.ed-callout p { font-size: 0.92rem; line-height: 1.66; color: var(--ed-fg); }
.ed-callout strong { font-weight: 600; }

/* ─── Prose ─── */
/* .ed-intro handles a heading plus ONE paragraph. A product page's "here is
   the thing in three paragraphs" band needs the run to space itself, and at a
   shorter measure than .ed-intro's 580px so a three-paragraph stretch does
   not turn into a wall. */
.ed-prose { max-width: 660px; margin-top: 28px; }
.ed-prose > p { font-size: 0.95rem; line-height: 1.72; color: var(--ed-muted); }
.ed-prose > p + p { margin-top: 20px; }
.ed-prose > .ed-link { margin-top: 30px; }

/* ─── Figures ─── */
/* Square corners, one hairline, no shadow — the old product pages used
   `rounded-xl … shadow-lg`, which the system forbids outright. The border is
   the same --ed-rule the grids use, so a screenshot reads as another cell on
   the page rather than a floating card. */
.ed-figure { margin: 0; }
.ed-figure img { display: block; width: 100%; height: auto; border: 1px solid var(--ed-rule); }
.ed-figcaption { margin-top: 14px; font-size: 0.8rem; line-height: 1.6; color: var(--ed-muted); }

/* A hero figure spans the frame rather than the 820px copy column: the
   screenshot is the widest thing on the page, and indenting it to the measure
   makes it read as an inline illustration instead of the product.
   Declared HERE, after .ed-figure, and not up with the rest of the hero: a
   hero figure carries both classes, and `.ed-figure { margin: 0 }` is a
   shorthand at equal specificity, so whichever comes last wins outright. Up
   in the hero block this silently resolved to a 0 gap. */
.ed-hero-figure { margin-top: 72px; }

/* ─── Numbered steps ─── */
/* A left number rail, not the alternating left/right zig-zag the old product
   pages used. Every other sequence in this system — .ed-belief, .ed-path,
   .ed-layer-split — runs label-left / content-right down a steady axis, and a
   zig-zag inside a page built on hairline rules reads as a different design.
   Rules on the rows only: no outer box, no card per step. */
.ed-steps { margin-top: 62px; border-top: 1px solid var(--ed-rule); }
/* The 62px is clearance from whatever introduced the list. When the list IS
   the band — a bare "here is the sequence" section with no heading above it —
   it lands on top of the band's own 130px and reads as a dropped section. */
.ed-frame > .ed-steps:first-child { margin-top: 0; }
.ed-step {
  display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 32px;
  align-items: start; padding: 44px 0; border-bottom: 1px solid var(--ed-rule);
}
/* Three columns when a step carries its own screenshot. The number rail keeps
   its width so the numerals stay on one axis whether or not a row has art. */
.ed-step--figure { grid-template-columns: 92px minmax(0, 0.9fr) minmax(0, 1.05fr); gap: 40px; }
.ed-step-num {
  font-family: 'Raleway', sans-serif; font-weight: 300;
  font-size: 2.4rem; line-height: 0.92; letter-spacing: -0.04em;
  color: var(--ed-brand);
}
.dark .ed-step-num { color: var(--ed-brand-light); }
/* Nudges the heading's cap height onto the numeral's, which the 0.92 line
   height on a 2.4rem Raleway 300 leaves about 5px high. */
.ed-step-copy > .ed-h3 { margin-top: 5px; }
.ed-step-copy > p { max-width: 560px; margin-top: 14px; font-size: 0.95rem; line-height: 1.72; color: var(--ed-muted); }

/* ─── Solution / product cards ─── */
.ed-solution-head { display: grid; grid-template-columns: 1.05fr 0.55fr; align-items: end; gap: 90px; margin-top: 56px; }
.ed-solution-head > p { max-width: 380px; }

/* Four products, four different access models, so not four equal cards. One
   grid reads top to bottom as the architecture: the protocol underneath, the
   two apps you can actually sign up for in the middle, the registry below.
   gap:1px over a --ed-rule background puts a hairline on every tier boundary
   for free — the same trick the old .ed-products grid used, which this
   replaces. DOM order IS architecture order, so it survives CSS being off. */
.ed-stack {
  display: grid;
  /* minmax(0, 1fr), never bare 1fr: a long product name or a 25-character
     hostname in a child would otherwise push its column past its share and
     carry the whole grid out past the frame. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-areas:
    "proto proto"
    "appa  appb"
    "reg   reg";
  gap: 1px;
  margin-top: 62px;
  background: var(--ed-rule);
  border: 1px solid var(--ed-rule);
}
.ed-stack-card { display: flex; flex-direction: column; padding: 28px 26px 26px; text-decoration: none; }
.ed-stack-meta { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }

/* Layers sit flush with the ground; apps lift off it. Section 03 is an
   .ed-band--alt, so painting a layer --ed-alt makes it the same value as the
   band (substrate) while painting an app --ed-bg makes it stand clear of it
   — #F7F7F7 vs #FFFFFF in light, #0A0A0A vs #000000 in dark, so the contrast
   inverts correctly with no second rule. This pairing assumes the alt band;
   on a plain .ed-band it reads backwards. */
.ed-layer      { grid-area: proto; background: var(--ed-alt); }
.ed-layer--reg { grid-area: reg; }
/* The floor stops the two app cards going squat and keeps the footer rule
   pinned low, which is the intended editorial device. 400px was sized for
   longer body copy; at the current length it trapped ~88px of dead space
   above the rule. Grid stretch equalises the pair regardless of this value. */
.ed-app        { background: var(--ed-bg); min-height: 330px; transition: background 200ms var(--ed-ease), transform 200ms var(--ed-ease); }
.ed-app--a     { grid-area: appa; }
.ed-app--b     { grid-area: appb; }
.ed-app:hover  { background: var(--ed-tint); transform: translateY(-4px); }

/* Tier label, left of the meta row — the architecture stated in words as well
   as in position, so it survives a screen reader and a zoomed viewport. */
.ed-tier { color: var(--ed-muted); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }

/* Access model, stated once per card and never twice. The load-bearing signal
   is the arrow on the card's control: a self-serve card uses an outbound
   arrow and prints its destination host, a gated card does neither. This
   label and its colour are the quiet confirmation, not a badge system. */
.ed-access        { font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap; }
.ed-access--open  { color: var(--ed-brand); }
.dark .ed-access--open { color: var(--ed-brand-light); }
.ed-access--gated { color: var(--ed-muted); }

/* Layers run horizontally: mark on the left, copy and actions on the right.
   A wide shallow band reads as a stratum where a tall card reads as a thing. */
.ed-layer-split { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: 48px; margin-top: 30px; }
.ed-layer-copy > p { margin-top: 14px; font-size: 0.9rem; line-height: 1.7; color: var(--ed-muted); }
.ed-layer-actions { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; margin-top: 26px; }

/* The foundation and registry marks run larger than the app marks: the layers
   are what the apps stand on, and scale says so without adding a graphic.
   text-transform is pinned to none because the pap:// wordmark lives in this
   rule and pap:// is a URI scheme — "PAP" alone is unrelated Microsoft auth
   terminology. .ed-keepcase is deliberately NOT used here: it also forces
   letter-spacing: 0.06em, which would undo the -0.06em display tracking.
   overflow-wrap is the 390px backstop. */
.ed-layer-mark {
  font-family: 'Raleway', sans-serif; font-weight: 300;
  font-size: clamp(2.6rem, 4.6vw, 3.8rem); line-height: 1;
  letter-spacing: -0.06em; text-transform: none;
  color: var(--ed-brand); overflow-wrap: anywhere;
}
.dark .ed-layer-mark { color: var(--ed-brand-light); }

.ed-app-mark { margin-top: 40px; font-family: 'Raleway', sans-serif; font-size: 2.4rem; font-weight: 300; line-height: 1; letter-spacing: -0.06em; color: var(--ed-brand); }
.dark .ed-app-mark { color: var(--ed-brand-light); }
.ed-app h3 { margin-top: 14px; }
.ed-app-body { margin-top: 14px; margin-bottom: auto; font-size: 0.9rem; line-height: 1.7; color: var(--ed-muted); }
.ed-app-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--ed-rule); }

/* The destination host, printed literally. For a card whose whole promise is
   "you can just go there", the most useful thing on it is where the click
   lands — and a hostname is a stronger self-serve cue than any badge. */
.ed-app-host { min-width: 0; font-size: 0.72rem; line-height: 1.4; color: var(--ed-muted); overflow-wrap: anywhere; }

/* Looks and sizes like .ed-btn but is a <span>: the entire self-serve card is
   already one <a>, and an anchor inside an anchor is invalid markup and
   unreachable by keyboard. Slightly shorter than .ed-btn so a control inside
   a card does not read as the page's primary action. */
.ed-stack-go {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 44px; padding: 0 20px;
  background: var(--ed-brand); color: #FFFFFF;
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  transition: background 180ms var(--ed-ease);
}
.ed-app:hover .ed-stack-go { background: var(--ed-brand-light); }

/* ─── Access paths (Chrysalis) ─── */
/* Two paths as an annotated definition list, not two bordered columns. Boxes
   side by side with headings read as a pricing table and invite a price
   comparison; these are not plans, they are two places the registry can run.
   Row rules only — no outer box, no vertical divider, no emphasised column. */
.ed-paths { margin-top: 26px; border-top: 1px solid var(--ed-rule); }
.ed-path {
  display: grid; grid-template-columns: 152px minmax(0, 1fr); gap: 24px;
  padding: 16px 0; border-bottom: 1px solid var(--ed-rule);
}
.ed-path dt { padding-top: 3px; color: var(--ed-brand); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.dark .ed-path dt { color: var(--ed-brand-light); }
.ed-path dd { margin: 0; font-size: 0.9rem; line-height: 1.66; color: var(--ed-fg); }

/* Compact variant for the stack card, where each path is a label and a
   one-liner rather than the full explanation the 03.1 block carries. */
.ed-paths--compact .ed-path { grid-template-columns: 124px minmax(0, 1fr); gap: 16px; padding: 12px 0; }
.ed-paths--compact .ed-path dd { font-size: 0.82rem; line-height: 1.6; color: var(--ed-muted); }

/* .ed-feature-actions had no top margin of its own — it inherited the gap
   from .ed-checks's bottom margin. The paths list now sits between the two,
   so the spacing has to be handed along explicitly. */
.ed-checks + .ed-paths { margin-top: 0; }
.ed-paths + .ed-feature-actions { margin-top: 32px; }

/* Portability line, directly under the stack. Measure is capped well short of
   the frame so three short imperatives break where the sentences do. */
.ed-portability { max-width: 680px; margin-top: 34px; }

.ed-statement { display: flex; align-items: center; gap: 22px; margin-top: 72px; font-family: 'Raleway', sans-serif; font-size: clamp(1.35rem, 2.1vw, 1.9rem); font-weight: 300; line-height: 1.14; letter-spacing: -0.035em; color: var(--ed-muted); }
.ed-statement strong { font-weight: 500; color: var(--ed-brand); }
.dark .ed-statement strong { color: var(--ed-brand-light); }
.ed-statement-rule { flex: 0 0 48px; height: 1px; background: var(--ed-brand); }

/* ─── Feature block ─── */
.ed-feature { margin-top: 70px; padding: 44px; border: 1px solid var(--ed-rule); border-top: 2px solid var(--ed-brand); }
.ed-feature .ed-h2 { margin-top: 18px; max-width: 620px; }
.ed-feature > p.ed-body { max-width: 600px; margin-top: 20px; }
.ed-checks { list-style: none; margin: 32px 0; padding: 0; display: grid; gap: 12px; }
.ed-checks li { display: flex; gap: 12px; align-items: flex-start; font-size: 0.92rem; line-height: 1.5; color: var(--ed-fg); }
.ed-checks li span { color: var(--ed-brand); font-weight: 700; flex-shrink: 0; }
.dark .ed-checks li span { color: var(--ed-brand-light); }
.ed-feature-actions { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }

/* ─── Tables ─── */
.ed-table-wrap { margin-top: 48px; overflow-x: auto; border: 1px solid var(--ed-rule); }
.ed-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.ed-table thead th {
  padding: 16px 20px; text-align: left; vertical-align: bottom;
  font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ed-muted); border-bottom: 1px solid var(--ed-rule);
}
.ed-table thead th.ed-th-brand { color: var(--ed-brand); border-bottom-color: var(--ed-brand); }
.dark .ed-table thead th.ed-th-brand { color: var(--ed-brand-light); }

/* `pap://` is always lowercase — it is a URI scheme, not an acronym, and
   "PAP" on its own is unrelated Microsoft auth terminology. Any element
   whose copy contains it must opt out of the uppercase transform. */
.ed-keepcase { text-transform: none !important; letter-spacing: 0.06em; }
.ed-table thead th span { display: block; margin-top: 4px; font-size: 0.68rem; font-weight: 400; letter-spacing: 0; text-transform: none; }
.ed-table tbody tr { border-bottom: 1px solid var(--ed-rule); transition: background 180ms var(--ed-ease); }
.ed-table tbody tr:last-child { border-bottom: 0; }
.ed-table tbody tr:hover { background: var(--ed-tint); }
.ed-table td { padding: 18px 20px; vertical-align: top; line-height: 1.62; color: var(--ed-muted); }
.ed-table td.ed-td-key { font-weight: 600; color: var(--ed-fg); }
.ed-table td.ed-td-win { color: var(--ed-fg); }
.ed-mark-no  { color: var(--ed-muted); font-weight: 600; }
.ed-mark-yes { color: var(--ed-brand); font-weight: 600; }
.dark .ed-mark-yes { color: var(--ed-brand-light); }

/* ─── Certification ─── */
.ed-cert-id { color: var(--ed-brand); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.dark .ed-cert-id { color: var(--ed-brand-light); }
.ed-cert-badge { display: block; max-width: 190px; margin-top: 24px; background: #FFFFFF; padding: 8px; }
.ed-cert-link { color: inherit; text-decoration: none; }
.ed-cert-link:hover { color: var(--ed-brand); }
.dark .ed-cert-link:hover { color: var(--ed-brand-light); }
.ed-highlights { margin-top: -1px; padding: 34px 36px; border: 1px solid var(--ed-rule); }
.ed-highlights h4 { font-family: 'Raleway', sans-serif; font-size: 1rem; font-weight: 500; letter-spacing: -0.02em; color: var(--ed-fg); }

/* ─── Tenure ─── */
.ed-tenure { display: flex; align-items: center; justify-content: space-between; gap: 48px; flex-wrap: wrap; margin-top: 56px; padding: 44px; border: 1px solid var(--ed-rule); }
.ed-tenure-text { flex: 1; min-width: 280px; }
.ed-tenure-text .ed-h2 { margin-top: 18px; max-width: 480px; }
.ed-tenure-text > p.ed-body { max-width: 460px; margin-top: 18px; }

/* ─── Closing ─── */
.ed-close { background: var(--ed-bg); color: var(--ed-fg); padding: 130px 0 140px; transition: background 0.3s ease, color 0.3s ease; }
.ed-close-main { max-width: 760px; margin-top: 80px; }
/* The home close opens on an .ed-h1 and earns the 80px run-up. A product page
   closes on an .ed-h2, where the same gap on top of the band's 130px just
   reads as a dropped section. */
.ed-close-main--tight { margin-top: 0; }
.ed-close-main > p.ed-body--lg { max-width: 520px; margin-top: 22px; }
.ed-close-actions { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 40px; }
.ed-close-meta { margin-top: 26px; }

/* ─── Pull quote ─── */
/* The one line in a prose run that is the argument. Sized between .ed-h3 and
   .ed-h2 so it lifts out of the paragraphs around it without competing with
   the section heading above them. */
.ed-pull {
  max-width: 760px; margin-top: 34px;
  font-family: 'Raleway', sans-serif; font-weight: 300;
  font-size: clamp(1.4rem, 2.4vw, 1.85rem); line-height: 1.2;
  letter-spacing: -0.035em; color: var(--ed-brand);
}
.dark .ed-pull { color: var(--ed-brand-light); }

/* ─── Anchor targets ─── */
/* The nav is fixed at 80px (base.html wraps `content` in `pt-20`), so a
   same-page jump to a section would otherwise land with its heading tucked
   underneath it. Tailwind's `scroll-mt-24` did this before; it has to be local
   for the same reason the rest of this file is — the CDN is a script. */
.ed-anchor { scroll-margin-top: 96px; }

/* ─── Option cards with their own control ─── */
/* A cell that ends in a button, for "here are three ways to get this". The
   control is the clickable thing, not the card, so the lift-and-tint hover
   .ed-cell carries is switched off — it reads as "click anywhere" and then
   only one 180px-wide strip actually does anything. */
.ed-cell--action { display: flex; flex-direction: column; }
.ed-cell--action:hover { background: var(--ed-bg); transform: none; }
.ed-band--alt .ed-cell--action:hover { background: var(--ed-alt); }
.ed-cell--action > h3 { margin-top: 14px; }
/* Qualified with the type selector on purpose: `.ed-cell p` is (0,1,1), so a
   bare `.ed-cell-tagline` at (0,1,0) loses its colour to the muted default. */
p.ed-cell-tagline { color: var(--ed-fg); font-weight: 500; }
/* margin-top:auto pins the control to the floor of the tallest cell, so the
   three buttons land on one line regardless of how much copy each option has.
   The 1fr-equal grid rows already stretch every cell to the same height. */
.ed-cell-action { margin-top: auto; padding-top: 26px; }

/* The emphasised option. Both selectors are needed: `.ed-band--alt .ed-cell`
   repaints the cell at specificity (0,2,0), which a bare `.ed-cell--feature`
   at (0,1,0) would lose to on exactly the alt band this grid sits in. */
.ed-cell--feature,
.ed-band--alt .ed-cell--feature { background: var(--ed-tint); }
.ed-cell--feature:hover,
.ed-band--alt .ed-cell--feature:hover { background: var(--ed-tint); }

/* ─── Forms ─── */
/* Square, hairline, no shadow — the same border token the grids and figures
   use, so a field reads as another cell rather than a floating input. */
.ed-form { display: flex; flex-direction: column; gap: 22px; max-width: 620px; margin-top: 44px; }
.ed-form-intro { max-width: 620px; margin-top: 18px; }
.ed-field { display: flex; flex-direction: column; gap: 8px; }
.ed-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
.ed-label { color: var(--ed-muted); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; }
.ed-field-note { font-size: 0.72rem; line-height: 1.5; color: var(--ed-muted); }

.ed-input, .ed-select, .ed-textarea {
  width: 100%; padding: 13px 15px;
  background: var(--ed-bg); color: var(--ed-fg);
  border: 1px solid var(--ed-rule); border-radius: 0;
  /* Controls inherit a UA font, not the page's, unless told to. */
  font-family: inherit; font-size: 0.92rem; line-height: 1.5;
  transition: border-color 180ms var(--ed-ease);
}
/* An alt band paints its own surface, and a field left on --ed-bg there reads
   as a lit box instead of a recess. */
.ed-band--alt .ed-input, .ed-band--alt .ed-select, .ed-band--alt .ed-textarea { background: var(--ed-alt); }
.ed-input::placeholder, .ed-textarea::placeholder { color: var(--ed-muted); opacity: 1; }
/* Native focus rings are rounded and blue, which is the one thing on the page
   that would be. A 1px outline on top of the 1px border reads as a 2px brand
   edge with the geometry the system already uses, and outline (not box-shadow)
   keeps it visible under forced-colors. */
.ed-input:focus, .ed-select:focus, .ed-textarea:focus {
  border-color: var(--ed-brand); outline: 1px solid var(--ed-brand); outline-offset: 0;
}
.dark .ed-input:focus, .dark .ed-select:focus, .dark .ed-textarea:focus {
  border-color: var(--ed-brand-light); outline-color: var(--ed-brand-light);
}
/* Scoped rather than put on .dark itself: color-scheme is what makes the
   native select popup, the caret and Chrome's autofill wash render dark. On
   the root it would also repaint every un-converted page's controls. */
.dark .ed-input, .dark .ed-select, .dark .ed-textarea { color-scheme: dark; }
.ed-textarea { min-height: 140px; resize: vertical; }

/* A <button> needs the reset an <a class="ed-btn"> does not. */
button.ed-btn { border: 0; cursor: pointer; font-family: inherit; }
/* .ed-form is a flex column, which stretches its children — so the submit
   control would run the full 620px measure and read as a banner rather than a
   button. The old markup pinned this with Tailwind's `self-start`. */
.ed-form > .ed-btn, .ed-form > .ed-btn-ghost { align-self: flex-start; }
.ed-btn:disabled { opacity: 0.6; cursor: default; transform: none; }
.ed-btn:disabled:hover { background: var(--ed-brand); transform: none; }

/* Honeypot. Deliberately NOT display:none and NOT Tailwind's `.hidden`: the
   old markup used the latter, so with the CDN blocked the field appeared in
   the form as an unlabelled "Website" box that a real person would then fill
   in — turning the spam trap into a submission blocker. Off-screen also
   catches more bots than display:none, which the careful ones skip.
   aria-hidden + tabindex="-1" in the markup keep it off the a11y tree and out
   of the tab order. */
.ed-honey { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ─── Alerts ─── */
.ed-alert { max-width: 620px; margin-top: 32px; padding: 22px 26px; border-left: 2px solid var(--ed-brand); background: var(--ed-tint); }
.ed-alert p { font-size: 0.9rem; line-height: 1.66; color: var(--ed-fg); }
.ed-alert p + p { margin-top: 6px; }
.ed-alert strong { font-weight: 600; }
.ed-alert .ed-link { margin-top: 18px; }

/* Success is brand, not green: purple is already this system's affirmative —
   .ed-mark-yes, .ed-access--open, .ed-checks all use it — so a green panel
   would introduce a second "yes" colour for one state. */
.ed-alert--ok { border-left-color: var(--ed-brand); }
.dark .ed-alert--ok { border-left-color: var(--ed-brand-light); }

/* Failure is the one exception to the brand-only palette, because "your
   application did not send" has to not look like "your application sent".
   #B3261E measures 6.54:1 on --ed-bg, 6.10:1 on --ed-alt and 5.72:1 on its
   own tint; #F2B8B5 measures 12.30:1 on #000000 and 11.60:1 on #0A0A0A. */
.ed-alert--err { border-left-color: #B3261E; background: #FDECEA; }
.ed-alert--err p, .ed-alert--err { color: #B3261E; font-size: 0.9rem; line-height: 1.66; }
.dark .ed-alert--err { border-left-color: #F2B8B5; background: rgba(242, 184, 181, 0.12); }
.dark .ed-alert--err p, .dark .ed-alert--err { color: #F2B8B5; }

/* ─── Scripted visibility ─── */
/* The forms on these pages toggle visibility from JS. They used to toggle
   Tailwind's `.hidden`, which is compiled by the CDN script — so with the CDN
   blocked the success and error panels were both visible on page load, under
   a form that had not been submitted. This is the local equivalent, and it is
   `.ed-hidden` rather than a second `.hidden` rule so it cannot collide with
   the utility the un-converted pages still rely on. */
.ed-hidden { display: none !important; }

/* ─── Badges ─── */
/* Third-party badge art, left at its own intrinsic size. */
.ed-badge { display: inline-block; margin-top: 44px; }
.ed-badge img { display: block; }

/* ─── Responsive ─── */
@media (max-width: 1100px) {
  .ed-grid-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 900px) {
  /* Layer cards go single-column while the mark column is still wide enough
     to set "Chrysalis" on one line. */
  .ed-layer-split { grid-template-columns: minmax(0, 1fr); gap: 24px; }

  /* The 92px number rail costs more than it returns once the copy column
     drops under roughly 45 characters. Collapsing to one column keeps DOM
     order — number, copy, figure — so the sequence still reads in order. */
  .ed-step, .ed-step--figure { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .ed-step-copy > .ed-h3 { margin-top: 0; }
  .ed-hero-figure { margin-top: 52px; }
}

@media (max-width: 760px) {
  /* One column. appa and appb stay adjacent in the area list, so Papillon and
     Imago remain neighbours — stacked rather than side by side, never split
     across a layer. */
  .ed-stack {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "proto" "appa" "appb" "reg";
  }
  .ed-app { min-height: 0; }
}

@media (max-width: 700px) {
  /* The 152px/124px label column stops earning its keep once the body column
     drops under about 40 characters. */
  .ed-path, .ed-paths--compact .ed-path { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}

@media (max-width: 960px) {
  .ed-about-layout, .ed-solution-head { display: block; }
  .ed-about-body, .ed-solution-head > p { margin-top: 44px; }
  .ed-belief { margin-top: 80px; }
}

@media (max-width: 640px) {
  .ed-band { padding: 86px 0; }
  .ed-hero { padding: 128px 0 72px; }
  .ed-close { padding: 86px 0 96px; }
  .ed-sub { margin-top: 76px; padding-top: 52px; }
  .ed-grid-4, .ed-grid-3, .ed-grid-2 { grid-template-columns: 1fr; }
  .ed-belief { grid-template-columns: 1fr; }
  .ed-belief > div, .ed-belief > div + div { min-height: 0; padding: 20px 0; border-right: 0; border-bottom: 1px solid var(--ed-rule); }
  .ed-belief > div:last-child { border-bottom: 0; }
  .ed-lockup { margin-top: 56px; }
  .ed-feature, .ed-tenure { padding: 28px 22px; }
  .ed-cell-title { margin-top: 32px; }
  .ed-statement { margin-top: 52px; }
  .ed-close-main { margin-top: 48px; }
  .ed-close-main--tight { margin-top: 0; }
  .ed-step { padding: 32px 0; }
  .ed-step-num { font-size: 2rem; }
  /* Two 160px-wide inputs side by side is worse than two full-width ones. */
  .ed-field-row { grid-template-columns: minmax(0, 1fr); }
  .ed-pull { margin-top: 26px; }
  .ed-btn, .ed-btn-ghost { width: 100%; }
  .ed-stack-card { padding: 24px 20px; }
  .ed-stack-go { width: 100%; }
  .ed-layer-actions { gap: 16px; }

  /* Tables collapse to stacked rows */
  .ed-table-wrap { overflow-x: visible; border: 0; }
  .ed-table, .ed-table tbody, .ed-table tr, .ed-table td { display: block; width: 100%; }
  .ed-table thead { display: none; }
  .ed-table tbody tr { padding: 20px 0; border-top: 1px solid var(--ed-rule); border-bottom: 0; }
  .ed-table td { padding: 0 0 8px; }
  .ed-table td.ed-td-key { font-size: 1rem; margin-bottom: 10px; }
}

/* ─── Motion ─── */
@media (prefers-reduced-motion: reduce) {
  .ed-cell, .ed-app, .ed-stack-go, .ed-btn, .ed-btn-ghost, .ed-table tbody tr { transition: none; }
  .ed-cell:hover, .ed-app:hover, .ed-btn:hover, .ed-btn-ghost:hover { transform: none; }
}

/* ─── Print ─── */
@media print {
  .ed-band, .ed-hero, .ed-close { padding: 1.5rem 0 !important; }
  .ed-accent { -webkit-text-fill-color: #6B46C1 !important; color: #6B46C1 !important; background: none !important; }
  .ed-table-wrap { overflow: visible; }
  /* base.html's print rules match [class*="gradient"]/[class*="blur"], which
     a canvas never matches — hide it explicitly so it can't ink a page. */
  .ed-orbital { display: none !important; }
}
