/* The design system, shared by the marketing shell and the cabinet.

   It lives in one file rather than inline in two templates because the alternative is
   two copies that drift — and the cabinet drifting away from the landing page is exactly
   what made the first version look like two different products stapled together.

   THE PALETTE AND GEOMETRY COME FROM THE BRAND'S VISUAL GUIDELINES (brand/README.md
   links the living document). Every value a designer would want to change lives in the
   token block below and nowhere else: change a line here, the whole site follows. A
   colour written anywhere else in this file — or in a template — is a defect, and the
   tests enforce it.

   Light is the default because the brand is a white product with black masses and one
   loud accent; dark is a full second theme, not an afterthought, because the guidelines
   name a dark surface mode of its own. */

/* Self-hosted: no request leaves for a font, which in Germany is a legal position as
   much as a performance one. Latin plus latin-ext — video titles carry accents. */
@font-face {
  font-family: "InstrumentSans";
  src: url("/static/fonts/InstrumentSans.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "InstrumentSans";
  src: url("/static/fonts/InstrumentSans-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "PlexMono";
  src: url("/static/fonts/IBMPlexMono-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "PlexMono";
  src: url("/static/fonts/IBMPlexMono-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

/* ── The brand palette ────────────────────────────────────────────────────────
   Six colours and one accent, named exactly as the guidelines name them. Nothing
   else in the codebase may write a hex value; everything below derives from these.
   Change a line here and the site changes — that is the whole point of this block. */
:root{
  /* Native chrome — scrollbars, select popups, date pickers — follows this and nothing
     else. Without it a black page keeps a bright scrollbar down its right edge. */
  color-scheme:light;
  --brand-white:#ffffff;
  --brand-soft:#f6f7f5;
  --brand-black:#0d0f10;
  --brand-graphite:#242729;
  --brand-cool:#a3a8ac;
  --brand-border:#e4e7e5;
  --brand-lime:#c7ff3d;

  /* ── Light: the default. White page, black type, lime used once and loudly. ──── */
  --bg:var(--brand-soft);
  --bg-2:var(--brand-white);
  --surface:var(--brand-white);
  --surface-2:var(--brand-soft);
  /* Elevation is contrast, not shadow: with the page tinted, a card rises by being white. */
  --sunk:var(--brand-white);
  --line:var(--brand-border);
  --line-soft:color-mix(in srgb,var(--brand-border) 60%,var(--bg));
  /* A UI boundary — an input edge, a quote bar — needs 3:1 against its surround
     (WCAG 1.4.11), which the hairline border cannot carry. */
  --line-strong:var(--brand-cool);
  --fg:var(--brand-black);
  --fg-2:var(--brand-graphite);
  --fg-3:#6b7176;
  /* The accent as a FILL stays the loud lime; as INK it cannot — lime on white is
     1.2:1 and unreadable. Text and rules therefore take the black, and the lime does
     its shouting through fills, which is exactly how the guidelines use it. */
  --accent:var(--brand-lime);
  --accent-2:var(--brand-lime);
  --accent-ink:var(--brand-black);
  --accent-text:var(--brand-black);
  /* The mark's own colour, and the tile it sits on. Both are fixed across themes:
     a brand that changes colour with the interface is half the reason it stopped
     reading as a name. This pair also matches the favicon, so the tab and the
     navigation stop being two different logos. */
  --accent-mark:var(--brand-black);
  --brand-tile:transparent;
  --accent-rule:var(--brand-black);
  --accent-line:var(--brand-border);
  /* The inverted surface — a dark card on a light page. It has to be a token, not the
     brand black itself: on the dark theme the page IS brand black, so a card painted
     with the literal disappears into it. Light inverts to black; dark lifts to
     graphite, the guidelines' own secondary dark surface. */
  /* On a white page this is a grey card, one step below the ordinary tinted surface —
     enough to stand apart from the page, not enough to read as a black slab. Graphite
     here was the mistake: on white it looks black whatever it is called, and it made
     the issue header the heaviest thing on a page whose subject is the patterns below
     it. The ink stays dark, because dark type on light grey is the whole point.
     The dark theme lifts to graphite with white ink instead — see its own block. */
  --invert-face:#dde1db;
  --invert-ink:var(--brand-black);
  --invert-ink-2:var(--brand-graphite);
  --invert-ink-3:#5b6166;
  --invert-line:var(--brand-cool);
  --invert-line-soft:var(--brand-border);
  --invert-tile:var(--brand-white);
  /* Lime is ink on the dark face and invisible on this one, so the inverted surface
     names its own accent per theme. */
  --invert-accent:var(--brand-black);
  --warn:#b45309;
  --warn-text:#8a4708;

  /* ── Values a theme changes by more than a colour ───────────────────────────── */
  /* No glow, no lift: the guidelines separate surfaces by contrast and
     whitespace, never by shadow. */
  --shadow-card:none;
  --shadow-hero:none;
  --shadow-menu:0 8px 24px -12px rgba(13,15,16,.18);
  --hairline:transparent;
  --tint:transparent;

  /* ── The two tones ─────────────────────────────────────────────────────────────
     A theme is two surfaces and everything else derives. Nothing may name a surface
     directly: an element paints with --ground (what it sits on) or --panel (what sits
     on it), assigned by position — see "Ground and panel" below. That invariant is
     what stops a white card landing on a white page. */
  /* Swapped 30 Aug 2026: the page carries the tint and a card rises by being white.
     These two are what every card reads from — changing --bg and --surface without
     them leaves grey cards on a grey page. */
  --tone-1:var(--brand-soft);
  --tone-2:var(--brand-white);
  --mark-shadow:none;
  --stage-pool:none;
  --hero-wash-1:0%;
  --hero-wash-2:0%;
  --nav-bg:color-mix(in srgb,var(--brand-white) 92%,transparent);
  --nav-line:var(--line);
  --nav-sat:100%;

  /* ── Geometry ──────────────────────────────────────────────────────────────────
     Sharp and rectangular: 4-8px, never the 16-32px SaaS curve. --radius is the
     card-sized value; --radius-sm is for controls and small chips. */
  --radius:8px;
  --radius-sm:4px;
  --maxw:1120px;
}

/* ── The dark theme ───────────────────────────────────────────────────────────
   Not an inversion of the light one but the guidelines' own dark surface mode:
   #0D0F10 with white typography and a rare lime accent. Here the lime CAN act as ink,
   because on near-black it carries 14:1 — so links, rules and the mark take it back,
   and that is the one place the two themes genuinely differ in role rather than value. */
:root[data-theme="dark"]{
  color-scheme:dark;
  --bg:var(--brand-black);
  --bg-2:var(--brand-graphite);
  --surface:var(--brand-graphite);
  --surface-2:#2e3234;
  --sunk:var(--brand-black);
  --line:#33383a;
  --line-soft:#25292b;
  --line-strong:#7c8286;
  --fg:var(--brand-white);
  --fg-2:#c8ccce;
  --fg-3:var(--brand-cool);
  --accent:var(--brand-lime);
  --accent-2:var(--brand-lime);
  --accent-ink:var(--brand-black);
  --accent-text:var(--brand-lime);
  --accent-rule:var(--brand-lime);
  --accent-line:color-mix(in srgb,var(--brand-lime) 45%,transparent);
  /* On the dark theme the page is already brand black, so an inverted surface
     lifts instead of dropping — and there the ink does invert to white. */
  --invert-face:var(--brand-graphite);
  --invert-ink:var(--brand-white);
  --invert-ink-2:color-mix(in srgb,var(--brand-white) 76%,var(--brand-graphite));
  --invert-ink-3:color-mix(in srgb,var(--brand-white) 58%,var(--brand-graphite));
  --invert-accent:var(--brand-lime);
  --brand-tile:transparent;
  /* The mark's ink on the dark theme. Two approved variants, one line apart — white is
     the one being tried (owner, 30 Aug 2026), lime is the one it replaced. The tile is
     transparent in both themes now, so whichever is chosen sits directly on the page and
     on the graphite footer without a square around it. */
  --accent-mark:var(--brand-white);
  /* --accent-mark:var(--brand-lime);  <- the lime variant, swap these two lines */
  --invert-line:color-mix(in srgb,var(--brand-white) 22%,var(--brand-graphite));
  --invert-line-soft:color-mix(in srgb,var(--brand-white) 11%,var(--brand-graphite));
  --invert-tile:color-mix(in srgb,var(--brand-white) 9%,var(--brand-graphite));
  --warn:#f0b429;
  --warn-text:#f0b429;
  --shadow-menu:0 8px 24px -12px rgba(0,0,0,.6);
  --tone-1:var(--brand-black);
  --tone-2:var(--brand-graphite);
  --nav-bg:color-mix(in srgb,var(--bg) 90%,transparent);
  --nav-line:var(--line);
}

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

/* ── Double Bite ───────────────────────────────────────────────────────────────
   The brand mark's own geometry, read off the board: TWO adjacent notches bitten out
   of the right edge, sitting low rather than at the corners. Two teeth, one bite mark.

   MEASURED IN PIXELS, NEVER IN PERCENT. With percentages the teeth spread apart on a
   tall card and crowd together on a short one, so no two cards carried the same mark —
   which is the opposite of what a mark is for. The tooth height and the depth are fixed,
   and only their distance from the bottom edge is anchored.

   A clip-path, so the notch shows whatever is behind the object rather than a painted
   triangle. One object, one bite: the guidelines are explicit. */
:root{
  --bite:13px;        /* how deep the teeth cut in */
  --tooth:12px;       /* the height of one tooth */
  --bite-base:16px;   /* clearance between the lower tooth and the bottom edge */
}
@media (max-width:520px){ :root{ --bite:10px; --tooth:9px; --bite-base:12px; } }

/* The silhouette, once. Everything that needs the shape reads this one custom
   property rather than repeating a nine-point polygon that then drifts. */
*,*::before,*::after{
  --bite-path:polygon(
    0 0, 100% 0,
    100% calc(100% - var(--bite-base) - var(--tooth) * 4),
    calc(100% - var(--bite)) calc(100% - var(--bite-base) - var(--tooth) * 3),
    100% calc(100% - var(--bite-base) - var(--tooth) * 2),
    calc(100% - var(--bite)) calc(100% - var(--bite-base) - var(--tooth)),
    100% calc(100% - var(--bite-base)),
    100% 100%, 0 100%
  );
}
/* The mark is opt-in and it belongs to CARDS — a content card, not a stat tile, not a
   table, not a form panel. It is asked for by name: an element takes `.bite` (or is one
   of the few components that are cards by definition), and everything else stays a
   plain square. Applying it by surface turned a mark into a texture and put a notch pair
   meant for a card onto a 90px tile.

   The silhouette itself is still declared exactly once, above. */
.bite,.sheet,.pattern,.issue-head,.locked-offer{
  clip-path:var(--bite-path);
}
.bite-left,.who .initial,.acct-btn .initial{
  --bite-path-left:polygon(
    0 0, 100% 0, 100% 100%, 0 100%,
    0 calc(100% - var(--bite-base)),
    var(--bite) calc(100% - var(--bite-base) - var(--tooth)),
    0 calc(100% - var(--bite-base) - var(--tooth) * 2),
    var(--bite) calc(100% - var(--bite-base) - var(--tooth) * 3),
    0 calc(100% - var(--bite-base) - var(--tooth) * 4)
  );
  clip-path:var(--bite-path-left);
}
/* The avatar is 26px, so it needs its own scale or the teeth swallow the letter. */
.who .initial,.acct-btn .initial{--bite:5px; --tooth:5px; --bite-base:5px;}

/* ── Sheets ────────────────────────────────────────────────────────────────────
   The brand's way of setting text: not a monolithic column, but bitten sheets in one
   of four surface modes — white, soft grey, black, lime — each with a frame that can
   be quiet or loud. A sheet carries its own ground AND redefines the ink tokens inside
   it, so nested text can never inherit the page's colour and vanish.

   The frame is two stacked layers rather than a border: a real border is cut off by
   the clip-path along the teeth and leaves raw diagonal edges. The lower layer is the
   frame colour at full size, the upper is the fill inset by its width. */
.sheet{
  position:relative; padding:clamp(20px,2.6vw,30px);
  background:transparent; --edge:var(--line); --face:var(--bg); --edge-w:1.5px;
  clip-path:var(--bite-path);
}
.sheet::before,.sheet::after{
  content:""; position:absolute; z-index:0; pointer-events:none;
  clip-path:var(--bite-path);
}
.sheet::before{inset:0; background:var(--edge);}
.sheet::after{inset:var(--edge-w); background:var(--face);}
.sheet > *{position:relative; z-index:1;}

.sheet-soft{--face:var(--bg-2); --edge:var(--bg-2);}
/* Grey sheet with a drawn frame — the board's most common card. */
.sheet-soft.sheet-framed{--edge:var(--line-strong);}
.sheet-framed{--edge:var(--fg); --edge-w:2px;}
.sheet-accent{--edge:var(--accent); --edge-w:2px;}

/* Named for what it does on the light theme, where it began; the tokens make it the
   page's opposite whichever theme is running. */
.sheet-dark{
  --face:var(--invert-face); --edge:var(--invert-face);
  --fg:var(--invert-ink);
  --fg-2:var(--invert-ink-2);
  --fg-3:var(--invert-ink-3);
  --fg-3:var(--brand-cool);
  --line:var(--invert-line);
  --line-soft:var(--invert-line-soft);
  --accent-text:var(--invert-accent);
  --accent-rule:var(--invert-accent);
  --surface-2:var(--invert-tile);
  --accent-text:var(--accent);
  color:var(--fg);
}
.sheet-lime{
  --face:var(--accent); --edge:var(--accent);
  --fg:var(--accent-ink);
  /* Secondary text on lime has to stay genuinely dark: mixed halfway towards the lime it
     turns olive and stops being readable, which is what the owner saw. */
  --fg-2:color-mix(in srgb,var(--accent-ink) 92%,var(--accent));
  --fg-3:color-mix(in srgb,var(--accent-ink) 80%,var(--accent));
  --line:color-mix(in srgb,var(--accent-ink) 25%,var(--accent));
  --accent-text:var(--accent-ink);
  color:var(--fg);
}

/* ── Extraction Frame ──────────────────────────────────────────────────────────
   Four corner markers around a highlighted object, from the guidelines' list of
   permitted graphics. Two pseudo-elements, two corners each. */
.frame{position:relative;}
/* Four corner marks inside the object, as the board draws them on every card. Eight
   gradient layers on one pseudo-element — two strokes per corner — rather than
   clip-path L-shapes, which render a stub instead of a corner as soon as the box is
   not square. --mark-len and --mark-w keep the marks in step with each other. */
.frame::after{
  content:""; position:absolute; inset:12px; pointer-events:none;
  --mark-len:16px; --mark-w:2px; --ink:currentColor;
  background:
    linear-gradient(var(--ink),var(--ink)) left top/var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--ink),var(--ink)) left top/var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top/var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top/var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--ink),var(--ink)) left bottom/var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--ink),var(--ink)) left bottom/var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom/var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom/var(--mark-w) var(--mark-len) no-repeat;
  opacity:.5;
}

/* ── Highlight Strip ───────────────────────────────────────────────────────────
   Lime tape behind a short line of text — the brand board's own device. Black ink,
   never white, and only ever on a phrase short enough to stay on one line. */
.strip{
  display:inline-block; background:var(--accent); color:var(--accent-ink);
  padding:.12em .5em; font-weight:520;
}

/* ── Lime discipline ───────────────────────────────────────────────────────────
   The guidelines want one large lime object per composition, not a scattering of
   green details. In light that happens by itself (--accent-text resolves to black);
   in dark it does not, and the eyebrow, the section labels, the stat numbers and the
   table multiples all inherited the lime — twenty-three lime things on one page.
   These carry information, not emphasis, so they take ink. */
:root[data-theme="dark"] .eyebrow,
:root[data-theme="dark"] .sec-label,
:root[data-theme="dark"] .stat .n,
:root[data-theme="dark"] .tbl .mult,
:root[data-theme="dark"] .metric .n.up{color:var(--fg);}



/* ── Revealing a password ─────────────────────────────────────────────────────
   A typed password nobody can read is how a person ends up locked out of an account
   whose password they typed correctly twice. The control sits inside the field and
   changes what it announces, so it is usable without seeing it. */
.pw{position:relative;}
.pw input{padding-right:46px;}
.pw-eye{
  position:absolute; right:1px; top:1px; bottom:1px; width:42px;
  display:grid; place-items:center; cursor:pointer;
  background:none; border:0; color:var(--fg-3); border-radius:var(--radius-sm);
}
.pw-eye:hover{color:var(--fg);}
.pw-eye svg{width:19px; height:19px;}
.pw-eye .eye-shut{opacity:0; transition:opacity .15s;}
.pw-eye[aria-pressed="true"] .eye-shut{opacity:1;}
.pw-eye[aria-pressed="true"]{color:var(--fg);}

/* ── How lines break ──────────────────────────────────────────────────────────
   The right edge stays ragged — justified text on a 412px column opens rivers of white
   between words, and English without hyphenation opens them wide. What is fixed is the
   kind of ragged: headings break into even lines rather than leaving one word below a
   full one, and a paragraph never ends on a single short word stranded on its own line.
   Browsers without these keep today's behaviour, which is the current page. */
h1,h2,h3,h4,.hook,.brand,.plan-name,.locked-offer b{text-wrap:balance;}
p,li,.formula,.issue-meta,.support,.sub,dd{text-wrap:pretty;}

/* ── Mono, for the technical 5-10% ─────────────────────────────────────────────
   Ids, code, timestamps and money columns — where character alignment is the point.
   Not support lines: those are sentences, and setting them in a second face is what
   made the hooks page read as five typefaces at once. */
.mono,.pay-when,.pay-sum,time,code{
  font-family:"PlexMono",ui-monospace,SFMono-Regular,Menlo,monospace;
  font-variant-numeric:tabular-nums;
}

/* The cabinet has no <section> elements, so it never took part in the alternation at all —
   which is precisely how a white card ended up on a white page there while the marketing
   page was fine. It opens on tone 1, and every panel in it takes tone 2. */
body{--ground-base:var(--tone-1); --ground:var(--tone-1); --panel:var(--tone-2);
     background:var(--ground);}

/* Anything that sits ON a surface takes --panel, and then becomes the ground for whatever
   sits on IT — so a tile inside a card flips back to the section's own tone instead of
   disappearing into the card.

   --ground-base is what makes the swap possible without a cycle. CSS drops a custom
   property that refers to itself, so `--ground: var(--panel); --panel: var(--ground)`
   silently produces nothing at all; the section's tone is therefore parked in a third
   variable that panels never rewrite, and the swap reads from that.

   One selector list, so a component added later is covered rather than remembered. */
.card,.note,.script,.wrap-box,.panel,.plan-panel,.pay-list,.acct-menu,.stats,.plan{
  background:var(--panel);
}
/* A bitten card cannot carry a border — the clip cuts it off along the teeth and leaves
   raw diagonals — so it draws its frame as two layers: the edge at full size with the
   fill laid over it, both cut to the same silhouette and both behind the content.
   One rule, taken by whatever wears `.bite`. */
/* Padding, not just a frame: the first version ran text into the stroke, and the
   bitten corner needs more clearance than a square one because the notch eats into
   the right edge exactly where a line of text ends. */
.bite{
  position:relative; isolation:isolate; border:0 !important; box-shadow:none;
  padding:clamp(20px,2.2vw,26px) clamp(22px,2.4vw,30px);
  /* Cool Gray, not the hairline: a card whose fill matches the page needs an edge
     that can actually be seen. Border Gray on white measures 1.25:1, which is a
     line nobody notices; this one holds in both themes. */
  --edge:var(--line-strong); --face:var(--panel); --edge-w:1.5px;
  background:transparent;
}
.bite::before,.bite::after{
  content:""; position:absolute; pointer-events:none; clip-path:var(--bite-path);
}
.bite::after{inset:0; z-index:-2; background:var(--edge);}
/* The fill is inset by the frame width, so its own 100% is a smaller box: reusing the
   outer polygon put its teeth two pixels off and the frame's lower diagonals vanished
   under the fill. The inner path therefore takes the inset out of every anchor, which
   lands its teeth on exactly the same pixels as the outer ones and leaves an even
   stroke all the way round. */
.bite::before{
  inset:var(--edge-w); z-index:-1; background:var(--face);
  clip-path:polygon(
    0 0, 100% 0,
    100% calc(100% - (var(--bite-base) - var(--edge-w)) - var(--tooth) * 4),
    calc(100% - (var(--bite) - var(--edge-w)))
      calc(100% - (var(--bite-base) - var(--edge-w)) - var(--tooth) * 3),
    100% calc(100% - (var(--bite-base) - var(--edge-w)) - var(--tooth) * 2),
    calc(100% - (var(--bite) - var(--edge-w)))
      calc(100% - (var(--bite-base) - var(--edge-w)) - var(--tooth)),
    100% calc(100% - (var(--bite-base) - var(--edge-w))),
    100% 100%, 0 100%
  );
}
.bite-accent{--edge:var(--accent-rule); --edge-w:2px;}
/* Where the reader is. Lime is the brand's "you are here", and it is the one
   place a card's frame changes colour. */
.pattern.here,.script.here,
.pattern:hover,.pattern:focus-within,
.script:hover,.script:focus-within{--edge:var(--accent); --edge-w:2px;}
.bite-lime{--edge:var(--accent); --face:var(--accent);
  --fg:var(--accent-ink);
  --fg-2:color-mix(in srgb,var(--accent-ink) 82%,var(--accent));
  --accent-text:var(--accent-ink); color:var(--fg);}
/* Withheld work keeps the shape and the frame; what marks it is the missing body, not
   a texture. The hatch that stood here broke up along the notches and read as a damaged
   line rather than as a deliberate state. */
.bite.locked{--face:var(--bg); --edge:var(--line-strong);}

*{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;}
body{
  margin:0; background:var(--bg); color:var(--fg);
  /* Sticky footer. Short pages — the trial account's cabinet is one — otherwise leave
     the footer sitting directly under the last line with empty background below it,
     which reads as a page that failed to finish loading. */
  min-height:100dvh; display:flex; flex-direction:column;
  font-family:"InstrumentSans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",
              Roboto,sans-serif;
  font-size:16px; line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
a{color:inherit;}
img{max-width:100%;display:block;}

/* ── Nav: brand left, one action right. Nothing else competes with the headline. ── */
.nav{
  position:sticky; top:0; z-index:50;
  backdrop-filter:saturate(var(--nav-sat)) blur(12px);
  background:var(--nav-bg);
  border-bottom:1px solid transparent;
  transition:border-color .2s;
}
.nav.scrolled{border-bottom-color:var(--nav-line);}
.nav-in{
  max-width:var(--maxw); margin:0 auto; padding:14px 22px;
  display:flex; align-items:center; gap:18px;
}
/* The brand as a badge: the mark inside a near-black tile with the glyph knocked out in
   lime, and the word a clear step above the navigation in size, weight and tracking.
   Before this the word was 1.14x a nav link in the same ink — so it read as the first
   item in the menu rather than as the name of the product.

   Identical in both shells, both themes and both placements. A lockup with variants is
   how a brand starts to rot. */
/* Proportions measured off brand/source/shortirex-horizontal.png rather than judged by
   eye: against the mark's height, the gap is 0.19 and the word 0.434. With the mark box
   at 38px — the size of the controls it sits beside — that is a 5.8px gap. Most of it is
   already spent before the CSS gap: the mark asset carries 8% empty canvas on its right
   and the tile another 2px, so the declared gap is 1px and the measured one is right.
   The source sets the word slightly below the mark's centre; here it is centred, which
   is the owner's call and what a 38px bar wants. */
.brand{
  display:flex; align-items:center; gap:1px; text-decoration:none;
  font-size:23px; font-weight:500; letter-spacing:-.03em;
  /* Load-bearing: with the inherited 1.6 the word becomes the tallest flex item and
     drives the box height instead of the tile. */
  line-height:1;
  color:var(--fg);
}
.brand .tile{
  display:grid; place-items:center; width:38px; height:38px; flex:0 0 auto;
  background:var(--brand-tile); border-radius:var(--radius-sm);
}
.brand .mark{
  width:34px; height:34px; display:block;
  background:var(--accent-mark);
  -webkit-mask:url("/static/mark.png") center/contain no-repeat;
  mask:url("/static/mark.png") center/contain no-repeat;
}
/* --fg-2 against the brand's --fg is 1.3:1, which nobody can see. --fg-3 keeps AA
   on both grounds and lets the name lead. */
/* "rex" is the half the brand leans on — the Rex in the name and in the mark. The rest
   of the word carries it at normal weight so the emphasis lands. */
.brand b{font-weight:700;}
.nav-link{color:var(--fg-3); text-decoration:none; font-size:14.5px; padding:7px 4px;}
/* The controls on the right sit on one line, pushed to the edge. Without this they are
   block-level children of a plain div and stack — the theme button above the account
   chip, each in its own box, which is exactly how it looked. */
.nav-right{display:flex; align-items:center; gap:10px; margin-left:auto;}
.nav-link:hover{color:var(--fg);}
.icon-btn{
  display:inline-grid; place-items:center; width:38px; height:38px; border-radius:var(--radius-sm);
  border:1px solid var(--line); color:var(--fg-2); text-decoration:none;
  background:var(--surface); transition:.18s;
}
.icon-btn:hover{color:var(--fg); border-color:var(--fg-3); transform:translateY(-1px);}
/* The toggle shows the theme you would switch TO, which is what every phone does and
   therefore what a reader already expects: a sun while it is dark, a moon while it is
   light. Showing the CURRENT theme is the other defensible choice and it is the one that
   makes people click twice to find out which it meant.

   Both icons are in the markup and CSS chooses; swapping them in JavaScript would mean
   the wrong one is on screen for the moment before the script runs. */
.theme-btn svg{width:18px; height:18px;}
.theme-btn .i-moon{display:none;}
:root[data-theme="light"] .theme-btn .i-sun{display:none;}
:root[data-theme="light"] .theme-btn .i-moon{display:block;}
.icon-btn svg{width:18px;height:18px;}

/* ── The account menu, under the person ──────────────────────────────────────
   Sign out used to sit in the main navigation beside the product sections, which put
   an irreversible action one mistap away from Pricing. It belongs behind the person,
   with the name visible so you can tell which account you are in. */
.acct{position:relative;}
/* The name sits on the same line as the brand and is set to match it, so the bar reads
   as one row of one size rather than a large name and a small one. The tile inside
   stays 28px: it is a letter in a chip, not a second logo. */
.acct-btn{display:inline-flex; align-items:center; gap:9px; cursor:pointer;
  padding:4px 12px 4px 4px; border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--surface); color:var(--fg); font:inherit; font-size:19px;
  font-weight:500; letter-spacing:-.02em; line-height:1; max-width:260px;}
.acct-btn:hover{color:var(--fg); border-color:var(--fg-3);}
.acct-btn .initial{display:grid; place-items:center; width:28px; height:28px;
  border-radius:var(--radius-sm); flex:0 0 auto; font-weight:700; font-size:12.5px;
  background:linear-gradient(135deg,var(--accent),var(--accent-2)); color:var(--accent-ink);}
.acct-btn .nm{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.acct-btn .chev{width:14px;height:14px;flex:0 0 auto;transition:transform .18s;}
.acct.open .acct-btn .chev{transform:rotate(180deg);}
@media (max-width:520px){ .acct-btn .nm, .acct-btn .chev{display:none;}
  .acct-btn{padding:4px;} }

.acct-menu{position:absolute; right:0; top:calc(100% + 8px); min-width:216px;
  border:1px solid var(--line-strong); border-radius:0;
  box-shadow:var(--shadow-menu); padding:6px; z-index:60;
  display:none;}
.acct.open .acct-menu{display:block;}
.acct-who{padding:10px 12px 11px; border-bottom:1px solid var(--line-soft); margin-bottom:6px;}
.acct-who b{display:block; font-size:14.5px; font-weight:620; color:var(--fg);}
.acct-who span{display:block; font-size:12.5px; color:var(--fg-3); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis;}
.acct-menu a{display:block; padding:9px 12px; border-radius:0; text-decoration:none;
  color:var(--fg-2); font-size:14.5px;}
.acct-menu a:hover{background:var(--panel); color:var(--fg);}
/* Sign out is an ordinary menu item, not a warning. Amber made it the only coloured
   thing in the menu and put an off-palette colour on every signed-in page. */
.acct-menu a.leave{color:var(--fg-2);}
/* Without scripting the menu cannot open, so the name links straight to the account
   page and Sign out stays reachable in the footer. */
html:not(.js) .acct-menu{display:none !important;}

/* Groups inside the account menu. On a desktop the product links live in the top bar and
   this menu holds only the account, so the group headings would be labelling a single
   list — they appear only where the menu carries both. */
.acct-group + .acct-group{border-top:1px solid var(--line-soft); margin-top:6px; padding-top:6px;}
.acct-menu h5{margin:8px 12px 4px; font-size:11px; font-weight:650; letter-spacing:.07em;
  text-transform:uppercase; color:var(--fg-3);}
.only-narrow{display:none;}
/* The cabinet's inline product links stop fitting well before the 780px breakpoint:
   with six of them the row measured 839px wide, so between 781px and ~838px the account
   control — the only route to Sign out — was clipped off the right edge, and
   body{overflow-x:hidden} meant it could not even be scrolled to. The account menu holds
   the same links, so hiding them earlier costs nothing. */
@media (max-width:900px){ .app .nav-mid{display:none;} }

@media (max-width:780px){
  /* The cabinet's one menu. .nav-mid has no burger to reopen it here, so it stays hidden
     and its links move into the account menu instead. */
  .app .nav-mid{display:none !important;}
  .only-narrow{display:block;}
}

main{display:block; flex:1 0 auto; width:100%;}
.wrap{max-width:var(--maxw); margin:0 auto; padding:0 22px;}
section{padding:96px 0; position:relative;}
/* Alternating section backgrounds, taken from satety.de where white and off-white give
   the page its rhythm. On a dark palette the same trick has to be much quieter — two
   values a few percent apart — or it reads as banding rather than as structure. */
/* Automatic, on satety.de's pattern (main > *:nth-child(odd|even)): the tone follows the
   section's position, so adding or reordering a section cannot leave two of the same tone
   touching. The explicit .alt class stays as an override for a section that must break
   the rhythm.

   Cards take the OPPOSITE tone to the section holding them, which is the half that makes
   it read as depth rather than as stripes — a card the same colour as its section is
   invisible. Keyed off one variable rather than a list of component classes, so a card
   type added later is covered instead of forgotten. */
/* ── Ground and panel ────────────────────────────────────────────────────────
   Two variables carry the whole rule. --ground is the tone an element SITS ON; --panel is
   the tone anything sitting on it must take, and it is always the other one. Assigned by
   position, never by hand, so adding a section shifts the rhythm for everything below it
   automatically and nobody has to remember which way round this one is.

   The pair is redefined on a panel as well: inside a card, the ground IS the card, so a
   tile nested in it flips back to the section's tone rather than disappearing into the
   card. That is the second half of the rule and the half that is forgotten by hand. */
/* Scoped to the marketing shell. The cabinet has no bands, and its subscription panel is
   itself a <section> — so the unscoped rule counted that panel as a page band, handed it
   the ground tone, and the panel then painted itself the colour of the page it sits on.
   The check found it; the markup was innocent. */
body:not(.app) section{--ground-base:var(--tone-1); --ground:var(--tone-1); --panel:var(--tone-2);}
body:not(.app) main > section:not(.hero):nth-of-type(odd){
  --ground-base:var(--tone-2); --ground:var(--tone-2); --panel:var(--tone-1);}
body:not(.app) main > section:not(.hero):nth-of-type(even){
  --ground-base:var(--tone-1); --ground:var(--tone-1); --panel:var(--tone-2);}
/* The hero opens the page on the base tone and stays out of the rhythm. */
body:not(.app) main > section.hero{
  --ground-base:var(--tone-1); --ground:var(--tone-1); --panel:var(--tone-2);}
body:not(.app) main > section{background:var(--ground);}
/* Kept as an alias so a component written before this rule still lands on the right tone
   rather than on a guess. */
body:not(.app) section{--card-bg:var(--panel);}
main > section:not(.hero):nth-of-type(odd)::before,
main > section:not(.hero):nth-of-type(odd)::after{content:""; position:absolute; left:0; right:0;
  height:1px; background:linear-gradient(90deg,transparent,var(--line-soft) 20%,var(--line-soft) 80%,transparent);}
main > section:not(.hero):nth-of-type(odd)::before{top:0;}
main > section:not(.hero):nth-of-type(odd)::after{bottom:0;}
@media (max-width:640px){ section{padding:64px 0;} }

/* ── Hero ─────────────────────────────────────────────────────────────────── */
.hero{padding:clamp(56px,10vw,120px) 0 clamp(48px,7vw,88px); position:relative; overflow:hidden;}
.hero::before{
  /* the glow. one light source, behind the product, like every page worth copying */
  content:""; position:absolute; inset:-30% -10% auto; height:620px; pointer-events:none;
  background:
    radial-gradient(52% 60% at 50% 0%, color-mix(in srgb,var(--accent) var(--hero-wash-1),transparent) 0%, transparent 70%),
    radial-gradient(38% 45% at 78% 12%, color-mix(in srgb,var(--accent-2) var(--hero-wash-2),transparent) 0%, transparent 72%);
  filter:blur(10px);
}
.eyebrow{margin:0 0 20px; color:var(--accent-text); font-size:13.5px; font-weight:650;
  letter-spacing:.14em; text-transform:uppercase;}

/* Two-column hero: headline left, the explanation and the action right. Straight from
   satety.de — a single column leaves half a desktop viewport doing nothing, and the
   split gives the eye somewhere to land after the headline. */
.hero-grid{display:grid; grid-template-columns:1.05fr .95fr; gap:clamp(28px,5vw,64px);
  align-items:start;}
@media (max-width:900px){ .hero-grid{grid-template-columns:1fr; gap:26px;} }
.hero-right{padding-top:clamp(0px,3vw,42px);}
.hero-kicker{font-size:clamp(17px,2vw,21px); font-weight:620; letter-spacing:-.02em;
  margin:0 0 14px; color:var(--fg);}

/* The stats strip under the hero, with dividers — hers, and it is exactly the shape our
   evidence numbers want. */
.stats{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px solid var(--line-soft); border-bottom:1px solid var(--line-soft);}
.stat{padding:26px 22px; text-align:center; border-left:1px solid var(--line-soft);}
.stat:first-child{border-left:0;}
.stat .n{display:block; font-size:clamp(21px,2.6vw,28px); font-weight:680;
  letter-spacing:-.03em; color:var(--accent-text); line-height:1.15;
  /* A value longer than its column wraps rather than running past the edge. The strip
     is the first thing under the hero, so a clipped word there reads as a broken page. */
  overflow-wrap:break-word; hyphens:none;}
.stat .l{display:block; color:var(--fg-2); font-size:13.5px; margin-top:7px; line-height:1.4;}
@media (max-width:640px){ .stat{padding:20px 14px;}
  .stat:nth-child(odd){border-left:0;} .stat:nth-child(n+3){border-top:1px solid var(--line-soft);} }
/* The accent phrase, on the pattern satety.de uses for its own — a gradient running from
   deeper to lighter across the words. The two colours are the button gradient's rather
   than a new pair, so the headline and the thing it asks you to click are the same green.

   Deliberately NOT as deep as the orange one. Measured against the navy: #28c98a is
   8.2:1 and #5ae7a6 is 11.2:1, both AAA. Taking the dark end down to satety's depth puts
   it at 3.5:1 — and rendered, the first letter goes muddy and reads as a mistake.

   The solid colour is set first and the effect only inside @supports: background-clip
   with a transparent fill makes the text INVISIBLE wherever the clip does not apply, and
   this is the sentence the page exists to say. Forced-colors mode gets the plain text
   back for the same reason. */
/* Oversized, plain, black. The guidelines ask for large typography without weight
   games and forbid gradients as a brand device, so the headline is simply type — and
   the accent phrase is marked with the lime as a HIGHLIGHT STRIP behind it (a permitted
   graphic) rather than as coloured ink, because lime on white is 1.2:1 and unreadable
   as text. Black ink on lime is the guidelines' own rule. */
h1 .hl{
  /* One strip, never two. Left to wrap, the highlight breaks wherever the line happens
     to end and paints two ragged blocks of different widths — the owner's "part on one
     line, part on another". The phrase is therefore its own unbreakable line: the
     headline is two statements anyway (the promise, then the proof), and giving the
     second one its own row is what the composition wanted. */
  display:block; width:fit-content; white-space:nowrap;
  color:var(--fg); background:var(--accent);
  padding:.02em .16em; margin-top:.1em;
}
:root[data-theme="dark"] h1 .hl{color:var(--accent-ink);}
h1{
  font-size:clamp(34px,6vw,68px);
  line-height:1.02; letter-spacing:-.04em; font-weight:600;
  margin:0 0 22px; max-width:15.5em;
  color:var(--fg);
  /* Same reason as h2: on a phone this split "Stop guessing what to" / "post.", leaving
     one word alone under four. Balanced it becomes an even pair of lines. */
  text-wrap:balance;
}
.lede{font-size:clamp(16.5px,1.9vw,20px); line-height:1.55; color:var(--fg-2);
      max-width:33em; margin:0 0 32px;}
.lede strong{color:var(--fg); font-weight:560;}

.cta-row{display:flex; gap:12px; flex-wrap:wrap; align-items:center;}
/* In the hero the two buttons share the text column, so the row ends on the same line as
   the paragraph above it. Content-width buttons left a ragged right edge — measured at
   119px short at 1440 and 184px at 820 — which reads as a mistake rather than as spacing.
   Only in the hero: elsewhere a pair of buttons sitting at their natural width is right. */
.hero .cta-row{display:grid; grid-template-columns:1fr 1fr;
  /* The same cap as .lede above it — and computed from .lede's font size, not from this
     element's. Both say 33em, but em resolves against the element's own size: the lede
     scales with clamp(16.5px,1.9vw,20px) while this row inherits 16px, so a plain 33em
     left the buttons 17px short at 820. Multiplying out the same clamp makes the two
     edges land together at every width rather than at most of them. */
  max-width:calc(33 * clamp(16.5px, 1.9vw, 20px));}
@media (max-width:520px){ .hero .cta-row{grid-template-columns:1fr;} }
.btn{
  /* Works on a <button> as well as an <a>: the cabinet shell resets `button`, the
     marketing shell does not, and a control that changes shape depending on which page
     it lands on is how the two sign-out buttons ended up different. */
  font-family:inherit; border:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  height:48px; padding:0 22px; border-radius:var(--radius-sm); text-decoration:none;
  font-size:15.5px; font-weight:580; letter-spacing:-.01em; white-space:nowrap;
  /* No colour transition: the page's background flips instantly on a theme
     switch, so a fading label spends ~150ms as the wrong colour on the new
     ground. Geometry may animate; colour may not. */
}
/* equal height and consistent shape — the first version had two buttons of different
   widths stacked on a phone, which reads as broken rather than as a choice */
/* The one loud surface on a page: solid lime, black ink, no glow and no lift. The
   guidelines are explicit — black text on lime, never white — and equally explicit that
   lime should appear rarely and largely rather than as scattered green details. */
.btn.primary{
  background:var(--accent);
  color:var(--accent-ink);
  border:1px solid var(--accent);
}
.btn.primary:hover{background:color-mix(in srgb,var(--accent) 86%,var(--fg));}
.btn.ghost{border:1px solid var(--line-strong); color:var(--fg); background:transparent;}
.btn.ghost:hover{border-color:var(--fg); background:var(--bg-2);}
/* A disabled action has to look disabled. The gradient alone reads as available, and the
   generate button is disabled whenever the quota is spent — the one moment it matters. */
.btn[disabled]{opacity:.5; cursor:not-allowed; box-shadow:none;}
.btn[disabled]:hover{transform:none;}
@media (max-width:520px){ .cta-row{display:grid; grid-template-columns:1fr;}
  /* Scoped: unscoped, this turned every 'Watch here' chip on /hooks into a
     full-width block with the weight of a primary call to action. */
  .cta-row .btn{width:100%;} }

.hero-note{color:var(--fg-3); font-size:14px; margin:26px 0 0; max-width:38em;}

/* ── Section headings ─────────────────────────────────────────────────────── */
.sec-label{color:var(--accent-text); font-size:13px; font-weight:600; letter-spacing:.08em;
           text-transform:uppercase; margin:0 0 12px;}
h2{font-size:clamp(25px,3.6vw,40px); line-height:1.14; letter-spacing:-.03em;
   font-weight:660; margin:0 0 14px;
   /* 19em, not 18. The pricing heading measures 18.25em and was landing 10px over the old
      cap at every width, which put one word — "page." — alone on a second line. The ratio
      is font-size-independent, so it wrapped identically at 1440, 1000 and 820. */
   max-width:19em;
   /* And where a heading genuinely cannot fit, the browser splits it evenly instead of
      leaving a single trailing word. On a phone the long ones still wrap; this is what
      stops them wrapping badly. */
   text-wrap:balance;}
.sec-sub{color:var(--fg-2); font-size:clamp(15.5px,1.7vw,18px); max-width:34em; margin:0 0 40px;}

/* ── Cards / surfaces ─────────────────────────────────────────────────────── */
.panel{background:linear-gradient(180deg,var(--surface),var(--bg-2)); box-shadow:var(--shadow-card);
       border:1px solid var(--line); border-radius:var(--radius);}
.divider{height:1px; background:linear-gradient(90deg,transparent,var(--line),transparent);}

footer{flex-shrink:0; border-top:1px solid var(--line-soft); padding:40px 0 56px; color:var(--fg-3);
       font-size:14px;}
/* One of the two tones, like everything else. It used to name --bg-2 directly, which in
   the light theme is a different mix from the alternating band and made the footer read as
   a third colour nobody chose. The page ends on the tone opposite whatever the last
   section used, which is the same rule the sections follow. */
footer{background:var(--panel);}
.foot-grid{display:grid; grid-template-columns:1.5fr repeat(3,1fr); gap:30px; align-items:start;}
@media (max-width:760px){ .foot-grid{grid-template-columns:1fr 1fr; gap:26px;} }
@media (max-width:420px){ .foot-grid{grid-template-columns:1fr;} }
.foot-brand p{margin:12px 0 0; color:var(--fg-3); font-size:14px; max-width:26em;}
.foot-nav{display:flex; flex-direction:column; gap:9px;}
.foot-nav h4{margin:0 0 4px; font-size:11.5px; text-transform:uppercase; letter-spacing:.08em;
  color:var(--fg-3); font-weight:650;}
.foot-nav a{font-size:14.5px;}
footer a{color:var(--fg-2); text-decoration:none;}
footer a:hover{color:var(--fg);}
.foot-right{margin-left:auto;}

/* Reveal on scroll, as progressive enhancement.
   The hidden state is applied only when JS is running (html.js), never by CSS alone.
   The first version hid every section below the fold with opacity:0 and relied on an
   IntersectionObserver to bring them back — so if the script failed, was blocked, or
   simply had not run yet, the entire page below the hero was invisible. Content must not
   depend on JavaScript to exist. */
.js .rise{opacity:0; transform:translateY(14px); transition:opacity .6s ease, transform .6s ease;}
.js .rise.in{opacity:1; transform:none;}
@media (prefers-reduced-motion:reduce){ .js .rise{opacity:1; transform:none; transition:none;} }

/* ── Responsive navigation, structured like satety.de ──────────────────────────
   Desktop: brand, the section links inline, the account icon.
   Phone:   brand, the account icon, and a burger that opens the same links.

   The first version simply hid the links below 780px, which is not responsive — it is
   just missing. The links are the same markup in both cases; only the container changes,
   so there is one menu to keep correct rather than two. */
.burger{display:none; width:38px; height:38px; border-radius:var(--radius); border:1px solid var(--line);
  background:var(--surface); color:var(--fg-2); cursor:pointer; place-items:center; padding:0;}
.burger:hover{color:var(--fg); border-color:var(--fg-3);}
.burger svg{width:18px;height:18px;}
.burger .x{display:none;}
.nav.open .burger .x{display:block;} .nav.open .burger .m{display:none;}

@media (max-width:780px){
  .burger{display:grid;}
  .nav-mid{
    display:grid; position:absolute; left:0; right:0; top:100%;
    background:var(--bg-2); border-bottom:1px solid var(--line);
    gap:0; margin:0;
    /* collapsed by height rather than display:none, so it can animate and so the links
       stay in the accessibility tree in a predictable place.
       The padding and the border move to the OPEN state: box-sizing cannot shrink a box
       below its own padding, so a closed menu with 20px of padding painted a permanent
       21px grey bar under the navigation on every marketing page. */
    padding:0 22px; border-bottom-width:0;
    max-height:0; overflow:hidden; transition:max-height .24s ease;
  }
  .nav.open .nav-mid{max-height:60vh; padding:6px 22px 14px; border-bottom-width:1px;}
  .nav-mid .nav-link{padding:14px 0; border-bottom:1px solid var(--line-soft); font-size:16px;}
  .nav-mid .nav-link:last-child{border-bottom:0;}
}
/* Without JS the burger cannot open anything, so it should not be offered. */
html:not(.js) .burger{display:none;}
html:not(.js) .nav-mid{max-height:none; overflow:visible;}

