/* 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.2ee17598a9.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.c4fcfea41f.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.08949f728d.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "PlexMono";
  src: url("/static/fonts/IBMPlexMono-500.01d2854474.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:#f2f3f1;
  --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);
  /* The direction's rgba(13,15,16,.12) — a divider a shade darker — written as what it
     actually composites to on this ground, so it stays one token instead of a literal
     repeated per section. It is not a taste change alone: when --brand-soft dropped to
     #F2F3F1 the old Border Gray fell to 1.12:1 against the page and
     tests/test_theme_toggle stopped seeing a border at all. */
  --line:color-mix(in srgb,var(--brand-black) 12%,var(--brand-soft));
  --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);
  /* Cool Gray as the guidelines name it measures 2.23:1 on white — tests/test_theme_toggle
     requires 3.0 for text and refuses it. This is the palette's own colour taken towards
     Graphite until it is readable, and nothing else about it changes. */
  --fg-3:color-mix(in srgb,var(--brand-cool) 55%,var(--brand-graphite));
  /* 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:var(--brand-border);
  --invert-ink:var(--brand-black);
  --invert-ink-2:var(--brand-graphite);
  --invert-ink-3:color-mix(in srgb,var(--brand-cool) 40%,var(--brand-graphite));
  --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);

  /* ── 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-solid:var(--brand-white);
  --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:var(--brand-graphite);
  --sunk:var(--brand-black);
  /* Graphite is the palette's own secondary dark, and against this theme's surface it
     measures 1.00:1 — an invisible border. Lifted towards Cool Gray by the smallest
     amount the contrast test accepts. */
  --line:color-mix(in srgb,var(--brand-cool) 32%,var(--brand-black));
  --line-soft:var(--brand-graphite);
  --line-strong:var(--brand-cool);
  --fg:var(--brand-white);
  --fg-2:var(--brand-cool);
  /* Cool Gray as the guidelines name it measures 2.23:1 on white — tests/test_theme_toggle
     requires 3.0 for text and refuses it. This is the palette's own colour taken towards
     Graphite until it is readable, and nothing else about it changes. */
  /* Cool Gray exactly as the guidelines name it: on this dark ground it reads. */
  --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(--accent);
  /* The mark follows `color`, not the token — the dark footer needed the same line. */
  /* --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));
  --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-solid:var(--bg);
  --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. */
/* One bite per composition, not one per card. The guidelines say both "one object, one
   bite" AND "do not repeat the motif many times inside one composition", and a page of
   sheets and patterns was four bitten objects. The motif stays on `.bite`, the element
   named for it, and is asked for by name where a page wants it. */
.bite{
  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:1px;
}
.sheet::before,.sheet::after{
  content:""; position:absolute; z-index:0; pointer-events:none;
}
.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);}
/* 1px, like every other rule on the page. The guidelines ask for thin borders and
   say plainly that structure comes from spacing, scale and contrast rather than from
   the weight of a line — so a frame is loud by its COLOUR here, not by its width. */
.sheet-framed{--edge:var(--fg); --edge-w:1px;}
.sheet-accent{--edge:var(--accent); --edge-w:1px;}

/* 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);
  /* Cool Gray as the guidelines name it measures 2.23:1 on white — tests/test_theme_toggle
     requires 3.0 for text and refuses it. This is the palette's own colour taken towards
     Graphite until it is readable, and nothing else about it changes. */
  --fg-3:color-mix(in srgb,var(--brand-cool) 55%,var(--brand-graphite));
  --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:1px;
  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:1px;}
/* 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,
/* Hover changes the frame's COLOUR, not its weight. A border that thickens on hover
   moves the content by a pixel, and the guidelines want one width everywhere. */
.script:hover,.script:focus-within{--edge:var(--accent);}
.bite-lime{--edge:var(--accent); --face:var(--accent);
  --fg:var(--accent-ink);
  --fg-2:color-mix(in srgb,var(--accent-ink) 82%,var(--accent));
  /* The same correction `.sheet-lime` carries, and for the same reason the owner gave:
     a tertiary grey inherited from the theme goes olive on lime and stops being read.
     Without it the pricing card's label and its per-month line were the two dimmest
     things on a card that is meant to be the loudest. */
  --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);}
/* 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;
  /* No backdrop blur. "glassmorphism" is on the guidelines' do-not-use list, and the bar
     is opaque instead — separation by contrast, which is what the same document asks for
     everywhere else. */
  background:var(--nav-solid);
  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.7cbf120473.png") center/contain no-repeat;
  mask:url("/static/mark.7cbf120473.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:var(--accent); 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);}
/* No rules between the bands. The sections already alternate white and soft white, and
   the guidelines are explicit that blocks are separated by spacing, scale and background
   contrast rather than by a quantity of lines. Two fading hairlines per section were the
   quantity. */
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;}
/* The hero carries no glow. It had one — a blurred radial wash behind the headline — and
   the guidelines rule out neon glow and gradients as a brand device in the same breath.
   What separates the hero now is scale and whitespace, which is what they ask for. */
.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. */
/* One continuous segmented strip, not four cells. The art direction's note: four equal
   boxes with a number in each is the most recognisable SaaS pattern on the page, and it
   was the least Shortirex thing we had. Segmented Forms are on the approved list; four
   cards are not. So: one band, hairline segments, much larger figures, technical labels
   in the mono face — which the guidelines want visible at 5-10% and we were using at 3%.

   The lime falls on the last figure alone, and by meaning rather than decoration: it is
   the count the page's honesty rests on. While it is zero the page says zero. */
.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);}
/* The block centres, the type inside it does not. A grid whose single column is sized
   to its content and then justified centres the NUMBER AND LABEL AS ONE OBJECT, which is
   what a stat tile is; centring the text instead would ragged both edges of a
   two-line label. Design direction, 5 Sep 2026. */
.stat{padding:30px 26px; text-align:left; border-left:1px solid var(--line);
      display:grid; justify-content:center; align-content:start;}
.stat:first-child{border-left:0;}
.stat:first-child{border-left:0;}
.stat .n{display:block; font-size:clamp(30px,4vw,46px); font-weight:600;
  letter-spacing:-.03em; line-height:1;
  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-3); font-family:"PlexMono",ui-monospace,monospace;
  font-size:11px; text-transform:uppercase; letter-spacing:.07em;
  margin-top:12px; line-height:1.5;}
/* The art direction asked for one lime marker in this strip. It cannot be the figure
   itself: lime type on white measures about 2:1 and cannot be read. So it is a small
   block beside the figure that the page's honesty rests on — the count of topics
   qualified right now, which says zero while it is zero. */
.stat:last-child .n{display:flex; align-items:center; gap:12px;}
.stat:last-child .n::before{
  content:""; width:14px; height:26px; flex:0 0 auto; background:var(--accent);
}
@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;
  /* The Double Bite on the headline's own highlight — the art direction's smallest and
     best-placed note: the first screen was recognisably modern tech and not yet
     recognisably ours, and one bite on the strip signs it without adding any graphic.
     Sized in em so the teeth scale with the headline instead of being three fixed pixels
     on a clamp()ed type scale. */
  --bite:.09em; --tooth:.13em; --bite-base:.10em;
  clip-path:var(--bite-path);
  padding-right:.26em;
}
: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 ─────────────────────────────────────────────────────── */
/* Flat, and separated by a hairline rather than by a shadow: the guidelines rule out
   gradients as a brand device and say plainly that a shadow is not how a card is set
   apart — background contrast, whitespace, a thin border or the Double Bite are. The
   dropdown menu keeps its shadow, because a floating layer is not a card. */
.panel{background:var(--surface); border:1px solid var(--line);
       border:1px solid var(--line); border-radius:var(--radius);}
.divider{height:1px; background:var(--line);}

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);}
/* A brand strip, not a neutral tail. The page had a character and then ended in the
   standard four-column SaaS footer — the art direction's words, and they are right. On a
   light page the footer is now the black mass closing the composition, with one lime
   rule above it; the dark theme already sits on black, so it takes the lime rule alone. */
/* Graphite rather than black, and the lockup carries the accent instead of the rule
   above it doing all the work. Design direction, 5 Sep 2026: #3B3F41 ground, lime mark,
   Soft White links, a quieter grey for the secondary line. */
:root[data-theme="light"] footer{
  /* Mixed from the palette's own Graphite and Cool Gray rather than added as a seventh
     brand hex: the token block is the only place a literal may live, and the guidelines
     name six colours. This lands on #3B3E41 — one unit of green from the direction's
     #3B3F41, and indistinguishable from it. */
  --foot-ground:color-mix(in srgb,var(--brand-cool) 18%,var(--brand-graphite));
  background:var(--foot-ground);
  --fg:var(--brand-soft); --fg-2:var(--brand-soft);
  /* The direction's #B7BBBD, reached the same way: Cool Gray a quarter of the way to
     Soft White, which lands on it exactly. */
  --fg-3:color-mix(in srgb,var(--brand-cool) 75%,var(--brand-soft));
  --line-soft:color-mix(in srgb,var(--brand-white) 14%,var(--foot-ground));
  /* The mark takes its colour from the theme, and the theme here is light while this
     surface is not. Without this the lockup is black on black and simply gone — which is
     what the first version of this footer shipped. White was the first repair; the
     direction of 5 Sep 2026 asks for the lime lockup in BOTH footers, so the glyph now
     matches the wordmark beside it instead of being the one white thing in the block. */
  --accent-mark:var(--accent);
  /* Declared here and not only on `footer`, because the light override outranks it by
     specificity and the rule below it lost silently. */
  border-top:3px solid var(--accent);
}
footer{border-top:3px solid var(--accent);}
/* The lockup carries the accent in BOTH themes now. Design direction, 5 Sep 2026. */
:root[data-theme="light"] footer .foot-brand .brand{color:var(--accent);}

/* The dark theme's footer stays graphite, as the art direction allowed — but only on the
   condition it gained a large lime element, and without one the two footers read as the
   same dark slab even though one is #0d0f10 and the other #242729. So the brand block
   itself becomes the lime surface: one large accent, black ink on it, which is the
   formula the guidelines state and the closing note the page was missing.

   No Double Bite on it. The page already spends the motif twice — the hero highlight and
   the product card — and a third would be the repetition the guidelines rule out. */
/* The lime plate is gone. It made the two footers different components — one a slab,
   one a panel — and it forced the brand block into a padding box the light footer does
   not have, so the two differed in height as well as colour. Now the accent lives in the
   MARK AND THE WORDMARK only, and everything else takes the footer's own ink.
   Design direction, 5 Sep 2026. */
:root[data-theme="dark"] .foot-brand{
  background:transparent; padding:0;
  --accent-mark:var(--accent);
  color:var(--fg-3);
}
:root[data-theme="dark"] .foot-brand p{color:var(--fg-3);}
/* The wordmark beside the mark, in the accent. Only the lockup — the tagline under it
   stays the footer's secondary ink. */
:root[data-theme="dark"] .foot-brand .brand{color:var(--accent);}
.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;} }
/* The footer lockup STACKS, in the proportions of the brand's own stacked lockup rather
   than in ones chosen by eye. Measured off brand/source/shortirex-stacked.png:

     mark width  / wordmark width      0.874
     mark height / wordmark ink height 4.889
     gap         / wordmark ink height 0.361
     horizontal centres                aligned (0.7% apart)

   The numbers below were then measured back off a real 1440px render, not derived and
   trusted — the first attempt was 19% short because `mask: contain` fits the whole image
   including mark.png's transparent margin, which mark-tight.png removes. What the browser
   now draws:

     mark width  / wordmark width      0.907  (reference 0.874)
     mark height / wordmark ink height 4.882  (reference 4.889)
     gap         / wordmark ink height 0.353  (reference 0.361)
     horizontal centres                exactly aligned

   The width is 3.8% wide because the two source files crop the glyph slightly
   differently — the stacked reference's mark is 640x704, ours is 481x512. Height is the
   ratio that carries the lockup, and it is exact.

   inline-flex so the lockup hugs its content: the two children are centred on each other,
   and the block itself still sits at the left of the column with the sentence beneath it.
   The header keeps them side by side, where the scarce dimension is vertical. */
.foot-brand .brand{
  display:inline-flex; flex-direction:column; align-items:center;
  gap:0.225em; font-size:22px;
}
.foot-brand .brand .tile{width:3.78em; height:3.78em;}
.foot-brand .brand .mark{
  width:3.78em; height:3.78em;
  /* the margin-free glyph, so `contain` scales the mark and not its padding */
  -webkit-mask-image:url("/static/mark-tight.b15ac59d96.png");
  mask-image:url("/static/mark-tight.b15ac59d96.png");
}
.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;}

/* The tax-and-currency explainer under the plans. A question asked once, answered in
   place: <details> so it needs no JavaScript and opens on a phone, where a title
   attribute does nothing at all. */
details.why{margin:18px 0 0; font-size:14px;}
details.why > summary{
  cursor:pointer; color:var(--fg-2); width:max-content;
  text-decoration:underline; text-underline-offset:3px;
  text-decoration-color:var(--line); text-decoration-thickness:1px;
}
details.why > summary:hover{color:var(--fg); text-decoration-color:currentColor;}
details.why > summary::marker{color:var(--fg-3);}
details.why p{margin:12px 0 0; max-width:62ch; color:var(--fg-2);}
details.why b{color:var(--fg);}
