/* ==========================================================================
   Voiceland AI public site
   One stylesheet, no build step, no webfonts. Mobile first.
   Breakpoints: 48em (tablet) and 64em (desktop).

   Icons: Lucide (ISC licence), 24x24 viewBox, paths copied unmodified.
   One family throughout. Two stroke widths only, and the split is a legibility
   decision, not a style one: 1.8 everywhere, 3 on the 14-15px ticks and
   crosses in the plan lists and the feature matrix, where 1.8 disappears.
   ========================================================================== */

/* --------------------------------------------------------------- reset -- */

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body,
h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol, table, fieldset {
  margin: 0;
  padding: 0;
}

/* Lists are chrome by default here; the few that want markers opt in
   (see .panel ul). Covers unclassed lists in the footer and plan cards too. */
ul, ol { list-style: none; }

img, svg { display: block; max-width: 100%; }
img { height: auto; }

button, input, select, textarea { font: inherit; color: inherit; }

table { border-collapse: collapse; }

fieldset { border: 0; }

/* -------------------------------------------------------------- tokens -- */

:root {
  /* colour: dark ink on light paper, one accent.
     Every colour on the site is a token so the dark theme below is a single
     swap. Two roles are easy to conflate and must not be:
       --hairline    decorative rules and card edges only
       --line-strong the visible boundary of a control (WCAG 1.4.11, 3:1) */
  --ink:         #14121a;
  --ink-soft:    #3a3646;
  --muted:       #56525f;
  --faint:       #6e6879;
  --hairline:    #e5e2ec;
  --line-strong: #8b8496;
  --paper:       #ffffff;
  --surface:     #f8f7fb;
  --surface-2:   #f1eef8;
  --accent:      #7c3aed;
  --accent-ink:  #5b21b6;
  --accent-wash: #f3ecff;
  --ok-ink:      #0a7c42;   /* saving line + yearly chip text */
  --on-accent:   #ffffff;
  --focus:       #5b21b6;
  --header-bg:   rgba(255, 255, 255, 0.92);

  /* the one sanctioned off-hue: semantic error */
  --danger:      #b3261e;
  --danger-wash: #fbe9e7;

  /* type: system stack only. Greek coverage comes from SF, Segoe UI, Roboto,
     Noto Sans, Liberation Sans and Arial; no webfont is needed or wanted. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          "Noto Sans", "Liberation Sans", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  /* scale: 1.25-ish, fluid where it matters.
     --t--2 is for uppercase micro-labels ONLY. Running text bottoms out at
     --t--1 (15px): the footer, the form labels and the whole feature matrix
     sit there and are read by people, not skimmed. */
  --t--2: 0.8125rem;
  --t--1: 0.9375rem;
  --t-0:  1.0625rem;
  --t-1:  1.1875rem;
  --t-2:  clamp(1.3125rem, 0.9rem + 1.4vw, 1.5rem);
  --t-3:  clamp(1.5rem, 1rem + 2.2vw, 1.9375rem);
  --t-4:  clamp(1.875rem, 1.1rem + 3.4vw, 2.625rem);
  --t-5:  clamp(2.25rem, 1.15rem + 4.8vw, 3.5rem);

  /* Three weights, because the stack is mostly STATIC families. Anything above
     500 resolves up to 700 on Arial / Liberation Sans, so a 520/550/620/640/660
     ladder renders as one flat bold for most Windows and Android visitors.
     --fw-soft is the deliberate exception: it resolves DOWN to 400 there, which
     is the quiet reading it wants anyway. */
  --fw-normal: 400;
  --fw-soft:   500;
  --fw-medium: 600;
  --fw-bold:   700;

  /* Greek carries tonos above the x-height on a high proportion of words, so
     display line-height cannot be a Latin 1.15. Negative tracking is a display
     device: it closes Greek alpha/omicron/sigma counters at subhead sizes. */
  --lh-display: 1.2;
  --lh-head:    1.28;
  --lh-body:    1.6;
  --track-display: -0.02em;
  --track-caps:     0.06em;

  /* rhythm */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;

  --radius-sm: 4px;
  --radius:    10px;
  --radius-lg: 16px;
  --wrap:      72rem;
  --measure:   34rem;

  --shadow-1: 0 1px 2px rgba(20, 18, 26, 0.05);
  --shadow-2: 0 4px 18px -6px rgba(20, 18, 26, 0.12);

  --ease:       cubic-bezier(0.23, 1, 0.32, 1);
  --dur-press:  120ms;
  --dur:        180ms;
  --dur-reveal: 240ms;
}

/* Form controls and scrollbars follow the theme too. */
html { color-scheme: light dark; }

/* Dark is designed, not inverted. The hierarchy is the same the whole way down
   (ink > ink-soft > muted > faint), the accent stays the same 258-262deg violet
   lifted for a dark ground, and the 'ink' role of the accent inverts to the
   LIGHTER variant so hover still reads as a step up.

   Measured against this palette: ink/paper 16.71, ink-soft/paper 11.54,
   muted/paper 7.19, accent/paper 6.82, accent-ink/paper 10.06, on-accent on
   accent 6.82 (primary button) and on accent-ink 10.06 (its hover),
   danger/paper 10.93, focus/paper 10.06, line-strong/paper 4.10 and against
   surface-2 3.41 (both clear the 3:1 floor for a control edge). */
@media (prefers-color-scheme: dark) {
  :root {
    --ink:         #f4f2f8;
    --ink-soft:    #cfc9dd;
    --muted:       #a49eb4;
    --faint:       #8a8399;
    --hairline:    #3a3448;
    --line-strong: #7a7290;
    --paper:       #14121a;   /* off-black, never #000 */
    --surface:     #1c1926;
    --surface-2:   #262233;
    --accent:      #a78bfa;
    --accent-ink:  #c4b5fd;
    --accent-wash: #251b3d;
    --ok-ink:      #5fd39a;
    --on-accent:   #14121a;
    --focus:       #c4b5fd;
    --header-bg:   rgba(20, 18, 26, 0.92);

    --danger:      #ffb4ab;
    --danger-wash: #3a1712;

    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.45);
    --shadow-2: 0 4px 18px -6px rgba(0, 0, 0, 0.65);
  }
  html { color-scheme: dark; }
}

/* An explicit choice beats the OS signal, in both directions. The theme
   toggle on the documentation pages (assets/js/docs.js) stamps
   data-theme="dark" or "light" on <html> and persists it; an inline head
   snippet re-applies it before first paint. The attribute selectors carry
   more specificity than the bare :root blocks above, so whichever value is
   present wins over both the light default and the media query. Pages
   without the attribute (all of them, until the visitor chooses) follow
   prefers-color-scheme exactly as before. The two token lists are verbatim
   copies of the light and dark sets above: a new token belongs in three
   places or it silently stops toggling. */
html[data-theme="dark"] {
  --ink:         #f4f2f8;
  --ink-soft:    #cfc9dd;
  --muted:       #a49eb4;
  --faint:       #8a8399;
  --hairline:    #3a3448;
  --line-strong: #7a7290;
  --paper:       #14121a;
  --surface:     #1c1926;
  --surface-2:   #262233;
  --accent:      #a78bfa;
  --accent-ink:  #c4b5fd;
  --accent-wash: #251b3d;
  --ok-ink:      #5fd39a;
  --on-accent:   #14121a;
  --focus:       #c4b5fd;
  --header-bg:   rgba(20, 18, 26, 0.92);

  --danger:      #ffb4ab;
  --danger-wash: #3a1712;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.45);
  --shadow-2: 0 4px 18px -6px rgba(0, 0, 0, 0.65);

  color-scheme: dark;
}

html[data-theme="light"] {
  --ink:         #14121a;
  --ink-soft:    #3a3646;
  --muted:       #56525f;
  --faint:       #6e6879;
  --hairline:    #e5e2ec;
  --line-strong: #8b8496;
  --paper:       #ffffff;
  --surface:     #f8f7fb;
  --surface-2:   #f1eef8;
  --accent:      #7c3aed;
  --accent-ink:  #5b21b6;
  --accent-wash: #f3ecff;
  --ok-ink:      #0a7c42;
  --on-accent:   #ffffff;
  --focus:       #5b21b6;
  --header-bg:   rgba(255, 255, 255, 0.92);

  --danger:      #b3261e;
  --danger-wash: #fbe9e7;

  --shadow-1: 0 1px 2px rgba(20, 18, 26, 0.05);
  --shadow-2: 0 4px 18px -6px rgba(20, 18, 26, 0.12);

  color-scheme: light;
}

/* ---------------------------------------------------------------- base -- */

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-0);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures;
  overflow-wrap: break-word;
}

h1, h2, h3, h4 {
  font-weight: var(--fw-bold);
  text-wrap: balance;
}

/* Display sizes take the tight leading and the negative tracking. Subheads sit
   near body size, where both would crowd Greek accents and counters. */
h1, h2 { line-height: var(--lh-display); letter-spacing: var(--track-display); }
h3, h4 { line-height: var(--lh-head);    letter-spacing: 0; }

h1 { font-size: var(--t-5); }
h2 { font-size: var(--t-4); }
h3 { font-size: var(--t-2); }
h4 { font-size: var(--t-1); }

p, li { text-wrap: pretty; }

a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--accent); }

strong { font-weight: var(--fw-bold); }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}
/* Only bare inline links need the ring's corners softened; every component
   with a focusable box already declares its own radius. */
a:focus-visible { border-radius: var(--radius-sm); }

/* The skip link is the one place a focus ring lands on a filled dark box, and
   main takes focus programmatically, so it must not draw one. */
main:focus { outline: none; }

::selection { background: var(--accent-wash); color: var(--ink); }

/* ----------------------------------------------------------- utilities -- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Parked off-screen with a transform, not with `top`: this animation fires on
   the first Tab of a keyboard session, when the browser is often still busy,
   and a layout property is the one thing that visibly stutters there. */
.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-4);
  z-index: 100;
  background: var(--ink);
  color: var(--paper);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  text-decoration: none;
  transform: translateY(calc(-100% - var(--sp-6)));
  transition: transform var(--dur) var(--ease);
}
.skip-link:focus { transform: none; color: var(--paper); }

.lede {
  font-size: var(--t-1);
  color: var(--ink-soft);
  max-width: var(--measure);
}

.muted { color: var(--muted); }

/* Uppercase micro-label. The tonos MUST be dropped when Greek is set in
   capitals, and that mapping is the browser's, keyed off the document
   language: every Greek page carries lang="el" on <html> and
   scripts/check_site.py asserts it. Do not move these labels into a
   lang-less fragment. */
.eyebrow {
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-ink);
  margin-bottom: var(--sp-3);
}

.note {
  font-size: var(--t--1);
  color: var(--muted);
  max-width: var(--measure);
}

/* Inline status marker next to a feature name (plan bullet or matrix row
   label). Quieter than .plan__tag, which owns a whole card. */
.badge {
  display: inline-block;
  margin-inline-start: 0.4rem;
  vertical-align: 0.05em;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  background: var(--accent-wash);
  color: var(--accent-ink);
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  line-height: 1.5;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  white-space: nowrap;
}

.stack > * + * { margin-top: var(--sp-4); }

.section {
  padding-block: var(--sp-8);
  border-top: 1px solid var(--hairline);
}
.section--plain { border-top: 0; }
.section--tint { background: var(--surface); }
/* Opens directly under the hero, which already carries a full bottom pad. */
.section--lead { padding-top: var(--sp-5); }

/* Two measures, because a heading is not running text. The head block takes a
   display measure; the paragraph inside it takes the reading one, so a long
   Greek page title does not have to set in three lines to keep its intro at
   65ch. */
.section__head { max-width: 46rem; margin-bottom: var(--sp-6); }
/* The company page opens with a head and nothing else in the section. Its
   trailing margin would then be dead space above the hairline, so it goes. */
.section__head:only-child { margin-bottom: 0; }
.section__head p { margin-top: var(--sp-3); color: var(--muted); max-width: var(--measure); }
/* A CTA row that belongs to the intro paragraph, not to the section below it. */
.section__head .actions { margin-top: var(--sp-5); }

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.7em 1.25em;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: var(--t-0);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform var(--dur-press) var(--ease);
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn--primary:hover {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: var(--on-accent);
}
.btn--primary:active { transform: translateY(1px); }

.btn--ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}
.btn--ghost:hover { border-color: var(--ink); color: var(--ink); background: var(--surface); }
.btn--ghost:active { transform: translateY(1px); }

.btn--small { padding: 0.5em 0.9em; font-size: var(--t--2); }
.btn--block { width: 100%; justify-content: center; }

.btn .icon { flex: none; }

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

/* A row of plain destinations, not a row of buttons: used where a page has
   more than two places to send someone (the 404). */
.linklist {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  justify-content: center;
  margin-top: var(--sp-5);
  font-size: var(--t--1);
}

/* -------------------------------------------------------------- header -- */

/* Static on phones. The nav wraps to a second row there and a sticky two-row
   bar eats too much of a short viewport. Sticky from 48em up, where the nav is
   pinned to a single line. */
.site-header {
  border-bottom: 1px solid var(--hairline);
  background: var(--header-bg);
  z-index: 50;
}

/* A page may carry a sticky bar of its own: the documentation tree puts a
   second <header class="docs-topbar"> immediately after this one, at the same
   z-index. Equal z-index means the LATER sibling wins, so that bar paints over
   this header and swallows anything hanging out of it - the Applications list
   and the language list both opened behind it. A descendant cannot escape its
   ancestor's stacking context, which is why the z-index on .lang__list below
   never helped.
   So the header out-ranks the page's bar only WHILE one of its menus is open.
   Not permanently: when the reader scrolls, both bars stick to the top and the
   docs bar is MEANT to own that strip (its tabs and its search box are what a
   reader needs there), so leaving the resting order alone is the point.
   Each selector is one way a menu opens: pointer, keyboard, click (site.js
   sets data-open), the language list (site.js clears its hidden attribute) and
   the phone menu (site.js sets is-nav-open, and there the whole panel hangs
   below the bar). */
.site-header:has(.nav__group:hover),
.site-header:has(.nav__group:focus-within),
.site-header:has(.nav__group[data-open="true"]),
.site-header:has(.lang__list:not([hidden])),
.site-header.is-nav-open { z-index: 60; }

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) var(--sp-5);
  padding-block: var(--sp-3);
  min-height: 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: var(--fw-bold);
  font-size: var(--t-1);
  letter-spacing: var(--track-display);
  margin-inline-end: auto;
  /* A wordmark never breaks across lines. The header is a nowrap flex row from
     64em up and the nav is nowrap too, which left the brand as the only item
     that could absorb a squeeze -- so on the Greek tree, whose nav labels are
     the longest, "Voiceland AI" folded into two lines. nowrap also pins the
     item's min-content width, so it stops shrinking instead of wrapping. */
  white-space: nowrap;
}
.brand:hover { color: var(--ink); }
.brand .mark { color: var(--accent); flex: none; }
.brand .brand__sub { color: var(--muted); font-weight: var(--fw-soft); }

.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
  order: 3;
  width: 100%;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--hairline);
}

.nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  padding-block: var(--sp-1);
  border-bottom: 2px solid transparent;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              opacity var(--dur-press) var(--ease);
}
.nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }
.nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

/* A transform on a text link inside a wrapping flex row causes subpixel
   reflow, so press feedback on links is an opacity step. */
.nav a:active,
.brand:active,
.site-footer a:active { opacity: 0.7; }

/* ------------------------------------------------- mobile navigation -- */

/* Nine links plus the language switcher wrap to three rows on a phone: the
   header measured 191 px on a 375 px viewport, over a quarter of the screen
   before a word of content, and that height is why the bar could not be
   sticky there either. Below 48em the links collapse behind a disclosure
   button and the header comes down to one row.

   That 191 px was measured with the switcher as a single anchor. The menu's
   trigger is the same pill at the same size, so the number should hold, but
   it is worth re-measuring on the first tree that ships the menu before
   leaning on this reasoning again.

   The button is injected by site.js, so this whole block only ever applies
   to a page that has it (.has-nav-toggle); without JavaScript nothing here
   engages and the nav keeps its wrapping-row layout. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  /* 44px is the Apple HIG minimum and this control is the entry point to
     the whole site on a phone. */
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-2, 8px);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
.nav-toggle:active { opacity: 0.7; }

/* Up to 64em, not 48em: the 48em block above turns the nav into a single
   nowrap row, and with nine links that row no longer fits beside the brand -
   at 768px the header still measured 152px because the nav wrapped to a line
   of its own. 64em is the width the header design already treats as "wraps
   below here, so do not stick", which is exactly the condition the menu
   solves. */
@media (max-width: 63.99em) {
  .site-header.has-nav-toggle .nav-toggle { display: inline-flex; }

  /* Collapsed by default. `display: none` rather than height animation:
     a wrapping flex row has no stable height to animate to, and a menu
     that measures itself on every open is a jank source on low-end
     phones. */
  .site-header.has-nav-toggle .nav { display: none; }
  .site-header.has-nav-toggle.is-nav-open .nav { display: flex; }

  .site-header.has-nav-toggle .nav {
    /* order/width/white-space undo the 48em single-row treatment, which
       still matches between 48em and 64em. */
    order: 3;
    width: 100%;
    white-space: normal;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-top: var(--sp-2);
  }
  /* Full-width rows at 44px: the whole row is the target, not just the
     glyphs, which is what the audit flagged (45x31 for "Αρχική"). */
  .site-header.has-nav-toggle .nav a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding-block: 0;
    padding-inline: var(--sp-1);
    font-size: var(--t--1);
    border-bottom: 1px solid var(--hairline);
  }
  .site-header.has-nav-toggle .nav a:last-child { border-bottom: 0; }
  /* The current page keeps its accent without the underline fighting the
     row divider. */
  .site-header.has-nav-toggle .nav a[aria-current="page"] {
    color: var(--ink);
    border-bottom-color: var(--hairline);
    font-weight: var(--fw-bold);
  }
  /* A vertical divider between stacked rows is meaningless. */
  .site-header.has-nav-toggle .nav__spacer { display: none; }

  /* One row means it can stick, which is the point: the menu and the
     language toggle stay reachable while reading a long page. The inner
     row keeps `flex-wrap: wrap` from the base rule on purpose - the open
     menu is width:100% and needs a line of its own under the brand. */
  .site-header.has-nav-toggle { position: sticky; top: 0; }
  .site-header.has-nav-toggle .site-header__inner { gap: var(--sp-2) var(--sp-3); }
}

/* Every menu entry, Documentation and Sign-in included, is visible at every
   width. Below 64em the header wraps (it is not sticky there, see the 64em
   block), so width never forces an entry off the bar; the earlier design hid
   these two below 64em to keep a sticky bar on one row, which read as the
   Documentation section vanishing on tablets. Only the divider is a
   wide-screen ornament. */
.nav__ext,
.nav__more { display: inline-flex; align-items: center; gap: 0.3rem; }
.nav__spacer { display: none; }

/* ------------------------------------------- the applications group -- */

/* One disclosure in the main navigation: a trigger that reads like the
   other links and a list of the four application pages under it. Desktop
   shows the list as a small panel on hover, focus or click; below 64em the
   nav is a stacked column and the list is simply indented rows, always
   visible, because a menu inside a menu on a phone is a trap. Every
   selector is qualified by .nav__group so the links around it render as
   before. */
.nav__group { position: relative; display: inline-flex; align-items: center; }
.nav__group-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: var(--sp-1) 0;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.nav__group-btn .icon { transition: transform var(--dur) var(--ease); }
.nav__group-btn:hover,
.nav__group:focus-within .nav__group-btn,
.nav__group[data-open="true"] .nav__group-btn,
.nav__group[data-current="true"] .nav__group-btn { color: var(--ink); border-bottom-color: var(--accent); }
.nav__group[data-open="true"] .nav__group-btn .icon { transform: rotate(180deg); }
.nav__sub {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 30;
  min-width: 15rem;
  margin: 0;
  padding: var(--sp-2);
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-2, 8px);
  box-shadow: 0 12px 32px rgba(20, 18, 26, 0.12);
}
.nav__group:hover .nav__sub,
.nav__group:focus-within .nav__sub,
.nav__group[data-open="true"] .nav__sub { display: block; }
.nav__sub li { margin: 0; }
.nav .nav__sub a {
  display: flex;
  align-items: center;
  min-height: 40px;
  padding: 0 var(--sp-2);
  border-bottom: 0;
  border-radius: var(--radius-2, 8px);
}
.nav .nav__sub a:hover,
.nav .nav__sub a[aria-current="page"] { background: var(--accent-wash); color: var(--accent-ink); }

@media (max-width: 63.99em) {
  .site-header.has-nav-toggle .nav__group { display: block; }
  .site-header.has-nav-toggle .nav__group-btn {
    width: 100%;
    min-height: 44px;
    justify-content: space-between;
    padding: 0 var(--sp-1);
    font-size: var(--t--1);
    border-bottom: 1px solid var(--hairline);
    cursor: default;
  }
  .site-header.has-nav-toggle .nav__group-btn .icon { display: none; }
  .site-header.has-nav-toggle .nav__sub,
  .site-header.has-nav-toggle .nav__group:hover .nav__sub {
    display: block;
    position: static;
    min-width: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: none;
  }
  .site-header.has-nav-toggle .nav .nav__sub a {
    min-height: 44px;
    padding-inline: var(--sp-4);
    border-radius: 0;
    border-bottom: 1px solid var(--hairline);
  }
}

/* language switcher */
.lang {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  position: relative;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  letter-spacing: 0.03em;
  color: var(--ink);
  text-decoration: none;
  background: var(--paper);
  transition: border-color var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              transform var(--dur-press) var(--ease);
}
.lang:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-wash); }
.lang:active { transform: scale(0.97); }
.lang .lang__from { color: var(--muted); font-weight: var(--fw-soft); }

/* Set by site.js when a stored preference points at another language. The
   accent border, wash and colour are the whole signal; site.js swaps the
   flagged link's accessible name at the same time so it is not colour-only. */
.lang[data-suggested="true"] {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent-ink);
}

/* The switcher as a menu.

   The header emits one of two shapes, because the trees do not all ship on
   the same day. With one sibling language it stays the single anchor styled
   above. With more it becomes

     <div class="lang" data-lang-menu>
       <button class="lang__btn" aria-expanded="false" aria-controls="lang-list">
       <ul class="lang__list" id="lang-list" hidden>

   and class="lang" stays on the wrapper in both, because assets/js/site.js
   inserts the mobile menu button before .lang and because the block above is
   what sizes the control on a phone. That is why the pill has to come OFF
   the wrapper here and go onto the trigger: the wrapper is now a positioning
   context with a button inside it, and two nested pills would read as a
   control inside a control.

   Every selector below is qualified by [data-lang-menu] or by one of the two
   element classes, so a page still carrying the single anchor renders exactly
   as it did before this block existed. */
.lang[data-lang-menu] {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  transition: none;
}
/* The wrapper is not the control any more, so it must not answer to the
   pointer as if it were; the trigger below does. */
.lang[data-lang-menu]:hover {
  border-color: transparent;
  background: none;
  color: inherit;
}
.lang[data-lang-menu]:active { transform: none; }

.lang__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  letter-spacing: 0.03em;
  color: var(--ink);
  background: var(--paper);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              transform var(--dur-press) var(--ease);
}
.lang__btn:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-wash); }
.lang__btn:active { transform: scale(0.97); }

/* A caret drawn in CSS rather than shipped as markup: the button's contents
   are an accessible name and a two-letter badge, both of which the chrome
   writes, and a disclosure hint is presentation. It turns over when the list
   opens, which is the one state change a mouse user sees at a glance. */
.lang__btn::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 0.25rem solid transparent;
  border-right: 0.25rem solid transparent;
  border-top: 0.3rem solid currentColor;
  opacity: 0.75;
  transition: transform var(--dur) var(--ease);
}
.lang__btn[aria-expanded="true"]::after { transform: rotate(180deg); }

.lang__list {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  /* Above the rest of the bar's own content, so the open list is not slid
     under a sibling. Clearing a sticky bar that belongs to the PAGE is a
     different problem and is solved on .site-header above: this value cannot
     reach past the header's stacking context, whatever it is set to. */
  z-index: 60;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  margin: 0;
  padding: var(--sp-1);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-2);
}
/* site.js closes the menu by setting the hidden attribute, and the display
   rule above beats that attribute's user-agent default. Without this line the
   list is permanently open and the whole disclosure is inert - including with
   JavaScript off, where the attribute is the only thing keeping it shut. */
.lang__list[hidden] { display: none; }

.lang__list a {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  font-size: var(--t--1);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.lang__list a:hover { background: var(--accent-wash); color: var(--accent-ink); }

/* The stored-preference cue, in the menu shape. It goes on both ends on
   purpose: the flagged option is not on screen while the list is collapsed,
   and a cue nobody can see is not a cue. */
.lang[data-suggested="true"] .lang__btn {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent-ink);
}
.lang__list a[data-suggested="true"] {
  background: var(--accent-wash);
  color: var(--accent-ink);
  font-weight: var(--fw-medium);
}

/* Same rule as the touch block far below, for the same reason: the control a
   thumb aims at is the trigger and the rows, not the wrapper around them. */
@media (max-width: 63.99em), (pointer: coarse) {
  .lang__btn {
    min-height: 44px;
    min-width: 44px;
    justify-content: center;
  }
  .lang__list a { min-height: 44px; }
}

/* ------------------------------------------------------- breadcrumbs -- */

/* The visible trail. Every page that carries a BreadcrumbList renders one,
   because structured data may only describe what the page actually shows.
   The list IS the .wrap, so the trail lines up with the content below it
   instead of adding a second container. */
.crumbs { padding-top: var(--sp-4); font-size: var(--t--2); }
.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.5rem;
  margin: 0;
  list-style: none;
}
.crumbs li { display: inline-flex; align-items: center; gap: 0.3rem 0.5rem; }
/* The separator is generated, so it is never read out and never copied. */
.crumbs li + li::before { content: "/"; color: var(--muted); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--ink); text-decoration: underline; }
.crumbs a[aria-current="page"] { color: var(--ink-soft); }

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

.hero {
  position: relative;
  overflow: hidden;
  padding-block: var(--sp-8) var(--sp-8);
  background:
    radial-gradient(60rem 30rem at 82% -18%, var(--accent-wash), transparent 62%),
    linear-gradient(180deg, var(--surface), var(--paper) 72%);
  border-bottom: 1px solid var(--hairline);
}

.hero__grid {
  display: grid;
  gap: var(--sp-7);
  align-items: center;
}

/* Ceiling lowered from the global --t-5 so the 50-character Greek headline
   sets in two lines, not three. It needs 616px of column at this size; the
   hero does not split into two columns until there is more than that (72em).
   Still above the --t-4 ceiling, so h1 never reads smaller than an h2. */
.hero h1 {
  font-size: clamp(2.25rem, 1.15rem + 4.8vw, 2.75rem);
  margin-bottom: var(--sp-4);
}
.hero .lede { font-size: var(--t-1); line-height: 1.5; color: var(--ink-soft); }
.hero .actions { margin-top: var(--sp-6); }

/* The three plain facts that used to sit under the hero CTAs. They open the
   next section instead: the hero stack stops at the CTA row. */
.facts-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  font-size: var(--t--1);
  color: var(--muted);
  padding-block: var(--sp-4);
  border-bottom: 1px solid var(--hairline);
  margin-bottom: var(--sp-7);
}
.facts-strip li { display: flex; align-items: center; gap: 0.4rem; }
.facts-strip .icon { color: var(--accent); flex: none; }

.hero__art {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--paper);
  box-shadow: var(--shadow-2);
  padding: var(--sp-5);
}
.hero__art svg { width: 100%; height: auto; }

/* The hero artwork is inline SVG precisely so it can read the theme tokens.
   Nothing in it is a hex literal; it follows light and dark for free. */
.hero__art .art-bubble           { fill: var(--surface-2); }
.hero__art .art-bubble--out      { fill: var(--paper); stroke: var(--accent); stroke-opacity: 0.35; }
.hero__art .art-bar              { fill: var(--line-strong); }
.hero__art .art-bar--soft        { fill: var(--line-strong); opacity: 0.55; }
.hero__art .art-bar--accent      { fill: var(--accent); }
.hero__art .art-bar--accent-soft { fill: var(--accent); opacity: 0.45; }
.hero__art .art-base             { stroke: var(--hairline); }
.hero__art .art-stop-a { stop-color: var(--accent); stop-opacity: 0.5; }
.hero__art .art-stop-b { stop-color: var(--accent); }
.hero__art .art-stop-c { stop-color: var(--accent-ink); }

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

.grid {
  display: grid;
  gap: var(--sp-5);
}
.grid--2 { grid-template-columns: 1fr; }
.grid--3 { grid-template-columns: 1fr; }

/* No elevation. A card earns a shadow only where elevation carries meaning
   (the featured plan, the hero art, the partner lockup); twenty shadowed boxes
   on one page means the shadow says nothing. The hairline and the section tint
   do the separating. */
.card {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--paper);
  padding: var(--sp-5);
}
.card h3 { margin-bottom: var(--sp-2); }
.card p { color: var(--muted); }

.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem; height: 2.5rem;
  border-radius: var(--radius);
  background: var(--accent-wash);
  color: var(--accent-ink);
  margin-bottom: var(--sp-4);
}

/* Hairline definition rows: a label column and a body column, no box, no
   chrome. Used where the three items have wildly different body lengths and a
   card row would leave two half-empty boxes. */
.rows { display: grid; }
.row {
  display: grid;
  gap: var(--sp-3);
  padding-block: var(--sp-5);
  border-top: 1px solid var(--hairline);
}
.row:last-child { border-bottom: 1px solid var(--hairline); }
.row h3 { font-size: var(--t-1); }
/* On the FAQ and company pages a row heading is an h2, because there the rows
   ARE the page rather than a detail inside a section. The rank changes, the
   type does not: same size as the h3 it stands in for. */
.row h2 { font-size: var(--t-1); }
.row p { color: var(--muted); max-width: var(--measure); }

/* The direct answer that follows a question heading. It is the sentence a
   search engine or an assistant lifts, so it reads a step louder than the
   supporting copy under it rather than as a caption: same measure, stronger
   ink. `.answer` is also the selector the speakable markup on the home pages
   points at, so the class is load-bearing and not decoration. */
.answer { color: var(--ink-soft); }
.section__head .answer { font-size: var(--t-1); }

/* ------------------------------------------------------------ partners -- */
/* Partner marks are the official asset, used unaltered: never recoloured,
   never redrawn, never stretched. width/height on the <img> carry the real
   aspect ratio so nothing shifts while it loads, and the boxes around them
   exist to give the mark its clear space. The dark-theme swap is a <picture>
   choosing between two official files, not a filter. See assets/img/README.md. */

.partner-logo { width: 9rem; height: auto; }

/* home page: the whole partners story in one strip. Mark, one line, one link. */
.partners-teaser {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-6);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: var(--sp-5) var(--sp-6);
}
.partners-teaser .eyebrow { margin-bottom: var(--sp-2); }
.partners-teaser p { color: var(--muted); max-width: var(--measure); }

/* partners page: one .panel per partner, stacked and centred in the wrap. A
   second partner is one more block: same markup, no layout change. */
.partners { display: grid; gap: var(--sp-6); max-width: 46rem; margin-inline: auto; }

.partner > * + * { margin-top: var(--sp-3); }
.partner > h3 { margin-top: var(--sp-5); }
.partner__logo { line-height: 0; }
.panel h2.partner__name { font-size: var(--t-3); }
.partner__lead { font-size: var(--t-1); color: var(--ink-soft); }

/* the lockup: our artwork underneath (assets/img/partner-hero.svg), the
   official mark on top as its own image. The two never touch. */
.partner-hero {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  margin: 0;
  min-height: 17rem;
  padding: var(--sp-6) var(--sp-5);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
}
.partner-hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Stacked on phones: wrapping a two-part row leaves the marks off centre from
   each other. Side by side from 48em up. */
.partner-hero__row {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
}
.partner-hero__tile {
  min-width: 10.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 5.5rem;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--paper);
  font-size: var(--t-1);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-display);
  color: var(--ink);
}
.partner-hero__tile .mark { color: var(--accent); flex: none; }
.partner-hero__tile .brand__sub { color: var(--muted); font-weight: var(--fw-soft); }
.partner-hero .partner-logo { width: 11rem; }

/* numbered steps */
.steps { counter-reset: step; display: grid; gap: var(--sp-5); }
.step {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: var(--sp-4);
  align-items: start;
}
.step__n {
  counter-increment: step;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem; height: 2.25rem;
  border-radius: 50%;
  border: 1px solid var(--accent);
  color: var(--accent-ink);
  font-size: var(--t--2);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}
.step__n::before { content: counter(step); }
.step h3 { font-size: var(--t-1); margin-bottom: var(--sp-1); }
.step p { color: var(--muted); }

/* checklist */
.checks { display: grid; gap: var(--sp-3); max-width: var(--measure); }
.checks li { display: grid; grid-template-columns: 1.25rem 1fr; gap: var(--sp-3); align-items: start; }
.checks .icon { color: var(--accent); margin-top: 0.28em; flex: none; }

/* ------------------------------------------------------------- pricing -- */

.plans {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: 1fr;
}

.plan {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--paper);
  padding: var(--sp-5);
}
/* The one card on the page that is supposed to lift off it. */
.plan--featured {
  border-color: var(--accent);
  box-shadow: var(--shadow-2);
}
.plan__tag {
  align-self: flex-start;
  font-size: var(--t--2);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent-wash);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  margin-bottom: var(--sp-3);
}
.plan__name { font-size: var(--t-2); margin-bottom: var(--sp-2); }
/* One cadence at a time. The paragraph is a column so the yearly view can
   stack what changed: the undiscounted year struck through above, the price
   charged below, the saving under it. Monthly shows only the middle row, so
   the card height moves by two small lines between cadences and by nothing
   else. */
.plan__price {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  font-variant-numeric: tabular-nums;
}
.plan__price-now {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.plan__price-amount {
  font-size: var(--t-4);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-display);
}
.plan__price small {
  font-size: var(--t--1);
  font-weight: var(--fw-normal);
  color: var(--muted);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
/* Yearly pricing: the old year struck through above, the saving beside and
   below. All three belong to the yearly view only: while the page shows
   monthly prices there is nothing to strike and nothing saved. */
.plan__price-was,
.plan__price-off,
.plan__price-save { display: none; }
.plans--yearly .plan__price-was {
  display: block;
  font-size: var(--t-0);
  font-weight: var(--fw-normal);
  color: var(--muted);
  letter-spacing: 0;
  text-decoration-thickness: 1px;
}
.plans--yearly .plan__price-save {
  display: block;
  font-size: var(--t--1);
  font-weight: var(--fw-medium);
  color: var(--ok-ink, #0a7c42);
  letter-spacing: 0;
}
.plans--yearly .plan__price-off {
  display: inline;
  font-size: var(--t--1);
  font-weight: var(--fw-bold);
  letter-spacing: 0;
  color: var(--ok-ink);
  background: color-mix(in srgb, var(--ok-ink) 14%, transparent);
  border-radius: 999px;
  padding: 0.1rem 0.45rem;
}
/* Monthly | Yearly. Two buttons, one pressed: no select, no JS framework. */
.cycle-toggle {
  display: flex;
  justify-content: center;
  gap: 0;
  margin: 0 auto var(--sp-5);
  width: fit-content;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.cycle-toggle button {
  font: inherit;
  font-size: var(--t--1);
  padding: 0.45rem 1.1rem;
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
/* The pressed side is the accent, the same pair the primary buttons use, so
   it reads as chosen in BOTH themes. --ink on #fff went white-on-near-white
   the moment the dark palette flipped --ink to off-white. */
.cycle-toggle button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: var(--fw-bold);
}
.cycle-toggle button:hover:not([aria-pressed="true"]) { color: var(--ink); }
/* The saving rides on the Yearly button so the reason to flip is visible
   before flipping. Written by scripts/gen_pricing.py from the catalogue. */
.cycle-toggle__save {
  margin-left: 0.4rem;
  font-size: var(--t--1);
  font-weight: var(--fw-bold);
  letter-spacing: 0;
  color: var(--ok-ink);
  background: color-mix(in srgb, var(--ok-ink) 14%, transparent);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
}
.cycle-toggle button[aria-pressed="true"] .cycle-toggle__save {
  color: var(--on-accent);
  background: color-mix(in srgb, var(--on-accent) 22%, transparent);
}

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

/* Enterprise Plus is quoted, not listed. Same slot and weight as a price, but
   sized and spaced to read as a phrase. An empty-looking number is worse. */
.plan__price--quote {
  display: block;
  font-size: var(--t-1);
  line-height: 1.3;
  letter-spacing: 0;
  font-variant-numeric: normal;
  text-wrap: balance;
}
.plan__note { font-size: var(--t--1); color: var(--muted); margin-top: var(--sp-1); }

.plan__specs {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--hairline);
  display: grid;
  gap: var(--sp-2);
  font-size: var(--t--1);
}
.plan__specs > div { display: flex; justify-content: space-between; gap: var(--sp-4); }
.plan__specs dt { color: var(--muted); }
.plan__specs dd { font-weight: var(--fw-medium); font-variant-numeric: tabular-nums; text-align: end; }

.plan__adds {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--hairline);
  font-size: var(--t--1);
}
.plan__adds > p { font-weight: var(--fw-medium); margin-bottom: var(--sp-3); }
.plan__adds ul { display: grid; gap: var(--sp-2); }
.plan__adds li { display: grid; grid-template-columns: 1rem 1fr; gap: var(--sp-2); align-items: start; color: var(--ink-soft); }
.plan__adds .icon { color: var(--accent); margin-top: 0.25em; }

.plan__cta { margin-top: var(--sp-5); }
.plan > .plan__cta { margin-top: auto; padding-top: var(--sp-5); }

/* ------------------------------------------------------- feature table -- */

/* The one element on the site that has to scroll horizontally, so it is also
   the one element that needs a tab stop: the markup carries tabindex="0" and
   role="region" pointing at the caption. */
.table-scroll {
  overflow-x: auto;
  /* `overflow-x: auto` alone still let the 46rem table push the ROOT scroll
     width past the viewport on a phone, so the whole page could be swiped
     sideways into 330px of nothing. Paint containment ends that: the scroller
     is the only thing that scrolls. */
  contain: paint;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--paper);
  -webkit-overflow-scrolling: touch;
}
.table-scroll:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.matrix {
  width: 100%;
  min-width: 46rem;
  font-size: var(--t--1);
}
.matrix caption {
  text-align: start;
  padding: var(--sp-4) var(--sp-5);
  color: var(--muted);
  border-bottom: 1px solid var(--hairline);
  max-width: var(--measure);
}
.matrix th,
.matrix td {
  padding: var(--sp-3) var(--sp-3);
  border-bottom: 1px solid var(--hairline);
  vertical-align: middle;
}
.matrix thead th {
  background: var(--surface);
  font-size: var(--t--2);
  font-weight: var(--fw-bold);
  text-align: center;
  white-space: nowrap;
  border-bottom: 1px solid var(--hairline);
}
.matrix thead th:first-child { text-align: start; }
.matrix tbody th[scope="row"] {
  text-align: start;
  font-weight: var(--fw-soft);
  color: var(--ink-soft);
  min-width: 18rem;
}
.matrix td { text-align: center; }
.matrix .matrix__group th {
  background: var(--surface-2);
  text-align: start;
  font-size: var(--t--2);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-ink);
  padding-block: var(--sp-2);
}
.matrix tbody tr:last-child th,
.matrix tbody tr:last-child td { border-bottom: 0; }

/* Stick the axis that actually scrolls. thead does NOT stick: .table-scroll
   has no vertical scrollport, so `top: 0` there is dead weight. The feature
   name is what a phone reader must not lose. */
.matrix thead th:first-child,
.matrix tbody th[scope="row"],
.matrix .matrix__group th {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: var(--paper);
}
.matrix .matrix__group th { background: var(--surface-2); }
.matrix thead th:first-child { background: var(--surface); z-index: 2; }

/* The tick and the cross are the only visual difference between an included
   and an excluded feature: the sr-only text beside them is not on screen. Both
   have to be legible, and the cross has to stay the quieter of the two. */
.matrix .yes { color: var(--accent); display: inline-block; }
.matrix .no  { color: var(--faint);  display: inline-block; }

/* --------------------------------------------------------- info panels -- */

.panel {
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--sp-5);
}
.panel h3,
.contact-side .panel h2 { font-size: var(--t-1); margin-bottom: var(--sp-3); }
.panel ul { display: grid; gap: var(--sp-2); padding-inline-start: 1.1rem; list-style: disc; }
.panel li::marker { color: var(--accent); }
.panel p, .panel li { max-width: var(--measure); }

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}
.chips li {
  border: 1px solid var(--line-strong);
  background: var(--paper);
  border-radius: 999px;
  padding: 0.3rem 0.85rem;
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
}

.price-list {
  display: grid;
  gap: var(--sp-2);
  font-variant-numeric: tabular-nums;
}
.price-list > div {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-2);
  border-bottom: 1px dashed var(--hairline);
}
.price-list > div:last-child { border-bottom: 0; }
.price-list dt { color: var(--ink-soft); }
.price-list dd { font-weight: var(--fw-medium); white-space: nowrap; }

/* Name, address, phone: the block a visitor scans for a number and the one a
   search engine reads next to the Organization node, so the two say the same
   thing. Stacked rather than two column: a role label as long as
   "Εξυπηρέτηση πελατών και πωλήσεις" and a number should never have to share
   a line on a 390px screen. The numbers are links, not text, because on that
   screen the only useful thing to do with one is dial it. */
.nap { display: grid; gap: var(--sp-4); margin-top: var(--sp-4); }
.nap > div { display: grid; gap: 0.15rem; }
.nap dt { color: var(--muted); font-size: var(--t--1); }
.nap dd { font-weight: var(--fw-medium); font-variant-numeric: tabular-nums; }
.nap dd a { font-weight: var(--fw-medium); }
/* The list of official profiles, and the line that says what the list means.
   Nothing else in a panel puts a note straight after a list. */
.panel .profiles + .note { margin-top: var(--sp-3); }
.nap__hint {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--t--1);
  font-weight: var(--fw-normal);
  color: var(--muted);
  max-width: var(--measure);
}
/* <address> is italic by default in every browser, which is wrong for a
   postal address printed as itself. */
.nap__addr { font-style: normal; }
.panel .nap__addr { max-width: var(--measure); }

/* ----------------------------------------------------------- cta strip -- */

.cta {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(40rem 20rem at 90% 120%, var(--accent-wash), transparent 65%),
    var(--surface);
  padding: var(--sp-7) var(--sp-5);
  text-align: center;
}
.cta h2 { font-size: var(--t-3); margin-bottom: var(--sp-3); }
.cta p { color: var(--muted); max-width: var(--measure); margin-inline: auto; }
.cta .actions { justify-content: center; margin-top: var(--sp-5); }

/* ---------------------------------------------------------------- form -- */

.form { max-width: 40rem; }
.form__row { display: grid; gap: var(--sp-5); }
.field { margin-bottom: var(--sp-5); }

.field label,
.legend {
  display: block;
  font-size: var(--t--1);
  font-weight: var(--fw-medium);
  margin-bottom: var(--sp-2);
}
.field .req { color: var(--accent-ink); }
/* A hint describes the control; it is a sibling of the label, not part of it,
   so it never inflates the control's accessible name. */
.field .hint {
  display: block;
  font-size: var(--t--1);
  font-weight: var(--fw-normal);
  color: var(--muted);
  margin-top: calc(var(--sp-2) * -1);
  margin-bottom: var(--sp-2);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.field textarea { min-height: 8rem; resize: vertical; }
.field select { appearance: none; padding-inline-end: 2.25rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 1.15rem, calc(100% - 0.8rem) 1.15rem;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--ink-soft); }

/* :focus is the "active field" look and fires for the mouse too. The keyboard
   ring is a separate, later rule so it wins: the wash halo alone measures
   about 1.09:1 against a white field, which is no indicator at all. */
.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
  outline: 2px solid transparent;
}
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.field [aria-invalid="true"] { border-color: var(--danger); }
.field [aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--danger-wash); }

.field--check { display: grid; grid-template-columns: 1.1rem 1fr; gap: var(--sp-3); align-items: start; }
.field--check input { width: 1.1rem; height: 1.1rem; margin-top: 0.28em; accent-color: var(--accent); }
.field--check label { font-weight: var(--fw-normal); font-size: var(--t--1); margin-bottom: 0; color: var(--ink-soft); }
.field--check .field__error { grid-column: 2; }

.field__error {
  display: none;
  margin-top: var(--sp-2);
  font-size: var(--t--1);
  color: var(--danger);
  font-weight: var(--fw-medium);
}
.field__error:not(:empty) { display: block; }

.form__status {
  display: none;
  margin-top: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  background: var(--surface);
  font-size: var(--t-0);
}
.form__status[data-state="visible"] { display: block; }
.form__status[data-tone="error"] { border-left-color: var(--danger); }

/* Every multi-column primitive declares its one-column base explicitly, so a
   future display change can never silently break the phone layout. */
.contact-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-7); }
.contact-side { display: grid; gap: var(--sp-5); align-content: start; }

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

.site-footer {
  border-top: 1px solid var(--hairline);
  background: var(--surface);
  padding-block: var(--sp-7);
  font-size: var(--t--1);
  color: var(--muted);
}
.site-footer__grid { display: grid; gap: var(--sp-6); }
.site-footer h2 {
  font-size: var(--t--2);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: var(--sp-3);
}
.site-footer__blurb { margin-top: var(--sp-3); max-width: 26rem; }
.site-footer ul { display: grid; gap: var(--sp-2); }
.site-footer a { color: var(--ink-soft); text-decoration: none; transition: color var(--dur) var(--ease), opacity var(--dur-press) var(--ease); }
.site-footer a:hover { color: var(--accent-ink); text-decoration: underline; }
.site-footer__bottom {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  justify-content: space-between;
}
.site-footer__bottom--bare { margin-top: 0; border-top: 0; padding-top: 0; }

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

.notfound {
  padding-block: var(--sp-9);
  text-align: center;
}
.notfound .code {
  font-family: var(--mono);
  font-size: var(--t--2);
  letter-spacing: 0.2em;
  color: var(--accent-ink);
  margin-bottom: var(--sp-4);
}
.notfound p { color: var(--muted); max-width: var(--measure); margin-inline: auto; margin-top: var(--sp-4); }
.notfound .actions { justify-content: center; margin-top: var(--sp-6); }
.notfound__alt {
  margin-top: var(--sp-7);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--hairline);
}
.notfound__alt h2 { font-size: var(--t-2); }

/* ----------------------------------------------------------------docs -- */
/* The generated documentation pages (scripts/gen_docs.py). Everything else on
   this site is composed copy, where a paragraph is placed; the docs are the one
   place where a Markdown body renders itself, so running prose needs styling
   that the rest of the site never asked for: list markers, code, a quote.
   Scoped under .docs-prose so none of it leaks into a hand written page. */

.docs-search { margin-top: var(--sp-6); margin-bottom: var(--sp-8); max-width: 34rem; }
.docs-search .field { margin-bottom: 0; }

.docs-area + .docs-area { margin-top: var(--sp-7); }
.docs-area h2 { font-size: var(--t-2); margin-bottom: var(--sp-5); }
.docs-area .card h3 { font-size: var(--t-1); }
.docs-area .card h3 a { text-decoration: none; }
.docs-area .card h3 a:hover { text-decoration: underline; }

.docs-empty { color: var(--muted); max-width: var(--measure); }
/* The filter hides cards and whole area blocks with the hidden attribute, and
   a grid or flex display would otherwise win over it. */
[hidden] { display: none !important; }

.docs-prose { max-width: 44rem; }
.docs-prose p,
.docs-prose li { max-width: var(--measure); }
.docs-prose h2 { font-size: var(--t-2); margin-top: var(--sp-7); }
.docs-prose h3,
.docs-prose h4 { font-size: var(--t-1); margin-top: var(--sp-6); }
.docs-prose > :first-child { margin-top: 0; }

.docs-prose ul,
.docs-prose ol {
  display: grid;
  gap: var(--sp-3);
  padding-inline-start: 1.3rem;
  list-style: disc;
}
.docs-prose ol { list-style: decimal; }
.docs-prose li::marker { color: var(--accent); }

.docs-prose blockquote {
  border-left: 3px solid var(--accent);
  background: var(--surface);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5);
}
.docs-prose blockquote p { color: var(--ink-soft); }

.docs-prose code,
.docs-code code {
  font-family: var(--mono);
  font-size: 0.9em;
}
.docs-prose > p code,
.docs-prose li code {
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.35em;
}

.docs-code {
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5);
}

.docs-pager {
  display: grid;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hairline);
  font-size: var(--t--1);
}
.docs-pager__label {
  display: block;
  font-size: var(--t--2);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--sp-1);
}
.docs-pager__all { color: var(--muted); }

/* The console callout closes every documentation page. Its heading is a section
   heading, not a card title, so it is an h2; the panel's own scale is the one
   that suits it. */
.docs-console h2 { font-size: var(--t-2); margin-bottom: var(--sp-3); }
.docs-console .actions { margin-top: var(--sp-5); }

/* Wide enough for three columns: previous, all, next, in reading order. */
@media (min-width: 48em) {
  .docs-pager {
    grid-template-columns: 1fr auto 1fr;
    align-items: start;
    gap: var(--sp-5);
  }
  .docs-pager__next { text-align: right; }
  .docs-pager__all { grid-column: 2; }
}

/* -------------------------------------------------------------- reveal -- */
/* The class that hides is added by the same function that reveals (site.js
   initReveal), so no code path can hide content without also un-hiding it. */

.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(8px) scale(0.985);
  transition: opacity var(--dur-reveal) var(--ease), transform var(--dur-reveal) var(--ease);
}
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; }

/* A three-step stagger, scoped to the children of a row so it reads as reading
   order rather than as decoration. Capped: the last card must not feel late. */
.js-reveal .grid > [data-reveal]:nth-child(3n + 2),
.js-reveal .plans > [data-reveal]:nth-child(3n + 2) { transition-delay: 60ms; }
.js-reveal .grid > [data-reveal]:nth-child(3n + 3),
.js-reveal .plans > [data-reveal]:nth-child(3n + 3) { transition-delay: 120ms; }

/* --------------------------------------------------------- breakpoints -- */

@media (min-width: 48em) {
  .wrap { padding-inline: var(--sp-6); }
  .section { padding-block: var(--sp-9); }

  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(2, 1fr); }
  .plans { grid-template-columns: repeat(2, 1fr); }
  .steps { grid-template-columns: repeat(2, 1fr); gap: var(--sp-6) var(--sp-6); }
  .site-footer__grid { grid-template-columns: 1.5fr 1fr 1fr; gap: var(--sp-7); }
  .form__row { grid-template-columns: 1fr 1fr; }
  .cta { padding: var(--sp-8) var(--sp-7); }

  .partner-hero { min-height: 20rem; padding: var(--sp-7) var(--sp-6); }
  .partner-hero__row { flex-direction: row; gap: var(--sp-6); }
}

@media (min-width: 64em) {
  /* Asymmetric: the hero sits under a sticky bar, so the top pad reads better
     shorter than the bottom one, and the CTA row stays above the fold on a
     1366x768 laptop. */
  .hero { padding-block: var(--sp-8) var(--sp-9); }

  /* The bar is sticky ONLY from here up, because this is the first width
     where every Greek label incl. Documentation and Sign-in fits one row: a
     sticky bar must never fold, a folded bar must never stick. Below 64em the
     header simply wraps like any other block and the FULL menu stays visible;
     hiding entries to keep a one-row sticky bar is what made Documentation
     "disappear" on tablets. */
  .site-header { position: sticky; top: 0; }
  /* Only blur where the bar actually sticks, and only where the engine says
     it is cheap: a full-width backdrop blur is re-rasterised every scroll
     frame. The opaque --header-bg is the base everywhere else. */
  @supports (backdrop-filter: blur(8px)) {
    .site-header { backdrop-filter: saturate(150%) blur(8px); }
  }
  .site-header__inner { flex-wrap: nowrap; padding-block: var(--sp-4); }
  .nav {
    order: 0;
    width: auto;
    padding-top: 0;
    border-top: 0;
    flex-wrap: nowrap;
    white-space: nowrap;
    gap: var(--sp-5);
  }
  .brand { margin-inline-end: auto; }
}

/* The narrow half of the one-row bar. 64em is where the header stops wrapping,
   but "it fits" was only ever true because the brand was allowed to fold into
   two lines and absorb the difference -- which it did, on the Greek tree, at
   every width from 64em up, long before this file grew a language menu. With
   the wordmark pinned to one line the row has to earn its space honestly, so
   the gaps tighten until 72em, where there is room for the roomier rhythm.
   Greek is the constraint: its labels are the longest of the six trees. */
@media (min-width: 64em) and (max-width: 71.99em) {
  /* 0.85rem, not a token: the tokens step 0.75 -> 1rem and 1rem still put the
     Russian language pill under the scrollbar gutter at exactly 1024px, which
     is the width this breakpoint exists to serve. Measured, not guessed. */
  .nav { gap: 0.85rem; }
  .site-header__inner { gap: var(--sp-3) var(--sp-4); }
  .nav__ext,
  .nav__more { display: inline-flex; }
  .nav__spacer {
    display: block;
    width: 1px;
    height: 1.1rem;
    background: var(--hairline);
  }

  /* Unequal tracks: three identical columns of identical boxes is the one
     grid this site does not want. */
  .grid--3 { grid-template-columns: 1.15fr 1fr 1fr; }
  .plans { grid-template-columns: repeat(3, 1fr); }

  .row { grid-template-columns: 16rem minmax(0, var(--measure)); gap: var(--sp-7); align-items: start; }
  .rows--tight .row { grid-template-columns: 14rem minmax(0, var(--measure)); }

  /* Numbered rail: the counters sit ON a hairline and the copy hangs beneath,
     so this is not another row of equal thirds. */
  .steps--rail {
    grid-template-columns: 1fr 1fr 1.15fr;
    position: relative;
  }
  .steps--rail::before {
    content: "";
    position: absolute;
    inset-inline-start: 1.125rem;
    top: 1.125rem;
    width: calc(100% - 2.25rem);
    height: 1px;
    background: var(--hairline);
  }
  .steps--rail .step { grid-template-columns: 1fr; gap: var(--sp-4); }
  .steps--rail .step__n { position: relative; background: var(--paper); }
  .section--tint .steps--rail .step__n { background: var(--surface); }

  .checks { grid-template-columns: repeat(2, minmax(0, 26rem)); gap: var(--sp-5) var(--sp-7); max-width: 59rem; }

  .contact-layout { grid-template-columns: 1.35fr 0.65fr; gap: var(--sp-8); align-items: start; }
}

/* The hero splits into text + artwork only once the text column can still hold
   the headline in two lines. Below this it is one column and the artwork sits
   underneath, which is better than a squeezed three-line headline. */
@media (min-width: 72em) {
  .hero__grid { grid-template-columns: 1.3fr 0.7fr; gap: var(--sp-8); }
}

/* ----------------------------------------------------- reduced motion -- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .js-reveal [data-reveal] { opacity: 1; transform: none; }
}

/* Legal footer links + cookie consent (prd-legal-consent-and-cookies) */
.site-footer__legal{margin-top:.6rem;font-size:.82rem;line-height:1.9;opacity:.85}
.site-footer__legal a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
.site-footer__legal a:hover,.site-footer__legal a:focus-visible{border-bottom-color:currentColor}
.site-footer__legal .sep{margin:0 .5rem;opacity:.5}

.vl-cc{position:fixed;inset:auto 0 0 0;z-index:2147483000;display:flex;justify-content:center;
  padding:16px;pointer-events:none}
.vl-cc__panel{pointer-events:auto;max-width:760px;width:100%;background:#0f1720;color:#e8edf2;
  border:1px solid rgba(255,255,255,.12);border-radius:12px;box-shadow:0 18px 50px rgba(0,0,0,.45);
  padding:20px 22px;font-size:.92rem;line-height:1.55}
.vl-cc__body p{margin:0 0 .6rem}
.vl-cc__body p:last-child{margin-bottom:0}
.vl-cc__cats{margin-top:12px;display:grid;gap:8px}
.vl-cc__cat{display:flex;align-items:center;gap:9px;font-size:.9rem}
.vl-cc__cat input{width:16px;height:16px;accent-color:#3f9c72}
.vl-cc__actions{margin-top:16px;display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end}
.vl-cc__btn{font:inherit;font-size:.88rem;font-weight:600;padding:9px 16px;border-radius:8px;cursor:pointer;
  border:1px solid rgba(255,255,255,.22);background:transparent;color:inherit}
.vl-cc__btn--ghost:hover{background:rgba(255,255,255,.08)}
.vl-cc__btn--solid{background:#2f9e6a;border-color:#2f9e6a;color:#04140c}
.vl-cc__btn--solid:hover{background:#37b47a}
.vl-cc__btn:focus-visible{outline:2px solid #7fd8b6;outline-offset:2px}
@media (max-width:520px){.vl-cc__actions{justify-content:stretch}.vl-cc__btn{flex:1}}

/* The consent panel is unbounded, so on a phone the full policy text made it
   taller than the viewport: it covered the page from edge to edge, its first
   line was cut off above the top of the screen, and because it is fixed at
   z-index 2147483000 nothing behind it could be tapped - the header included.
   Cap it at 70% of the viewport and let the prose scroll inside; the actions
   stay pinned below it so "Αποδοχή όλων" is always on screen and always
   reachable. */
@media (max-width: 63.99em) {
  .vl-cc { padding: 12px; }
  .vl-cc__panel {
    display: flex;
    flex-direction: column;
    max-height: 70dvh;
    padding: 16px;
    font-size: .86rem;
    line-height: 1.5;
  }
  .vl-cc__body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Room for the scrollbar so the last line never sits under it. */
    padding-inline-end: 4px;
  }
  .vl-cc__actions { margin-top: 12px; flex: none; }
  /* Consent is a decision, and a mis-tap on it is a privacy decision the
     visitor did not make: full 44px rows, one per button. */
  .vl-cc__btn { min-height: 44px; flex: 1 1 100%; }
  .vl-cc__cat { min-height: 32px; }
  .vl-cc__cat input { width: 20px; height: 20px; }
}
/* Older engines without dvh: vh is close enough and still bounded. */
@supports not (max-height: 70dvh) {
  @media (max-width: 63.99em) { .vl-cc__panel { max-height: 70vh; } }
}

/* ------------------------------------------------------- touch targets -- */

/* Measured in headless chromium at 375px: the footer link lists rendered at
   20px tall, the brand at 30px and the language toggle at 32px. A finger pad
   is about 9mm, which is why both platform guidelines land near 44px, and
   these are the site's standing navigation - the links a visitor reaches for
   most often on the smallest screen.

   Deliberately NOT applied to links inside running prose: a link in a
   sentence cannot grow to 44px without breaking the line box it sits in, and
   the surrounding text already gives the finger somewhere safe to land.
   Standalone controls are what this targets. */
@media (max-width: 63.99em), (pointer: coarse) {
  .site-footer ul a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
  /* The grid gap doubled the visual spacing once the rows grew; the taller
     rows are the separation now. */
  .site-footer ul { gap: 0; }

  .brand,
  .lang {
    min-height: 44px;
  }
  .lang {
    min-width: 44px;
    justify-content: center;
  }

  /* Form controls: a mis-tap here costs the visitor their message. */
  .form input,
  .form select,
  .form textarea,
  .field input,
  .field select { min-height: 44px; }

  /* Standalone buttons rather than links in text: the pricing period
     switch, and anything sharing the button styles. */
  .btn,
  button.btn,
  .cycle-toggle button { min-height: 44px; }
}

/* Legal document pages */
.legal-page{max-width:760px;margin:0 auto;padding:48px 22px 96px}
.legal-page h1{font-size:2rem;line-height:1.15;margin:0 0 .4rem}
.legal-page .legal-meta{font-size:.85rem;opacity:.7;margin:0 0 2rem}
.legal-body h2{font-size:1.35rem;margin:2rem 0 .6rem}
.legal-body h3{font-size:1.1rem;margin:1.4rem 0 .4rem}
.legal-body h4{font-size:1rem;margin:1.1rem 0 .3rem}
.legal-body p,.legal-body li{max-width:70ch;line-height:1.65}
.legal-body ul,.legal-body ol{padding-left:1.4rem}
.legal-body hr{border:0;border-top:1px solid rgba(128,128,128,.3);margin:2rem 0}
.legal-table{overflow-x:auto;margin:1rem 0}
.legal-table table{border-collapse:collapse;width:100%;font-size:.9rem}
.legal-table th,.legal-table td{border:1px solid rgba(128,128,128,.3);padding:7px 10px;text-align:left;vertical-align:top}

/* The legal body is published by the legal service, which does not carry every
   document in every language yet. When a tree's own translation is missing the
   page shows the English text with this note above it, rather than silently
   serving another language's words as if they were this one's. */
.legal-untranslated {
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-wash);
  color: var(--muted);
  font-size: 0.925rem;
}
