/* ============================================================
   system.css - the Cosmos1562 design system

   One file, linked by everything. The React pages pull it in
   through the bundler (frontend/src/styles/*.css import it); the
   Jinja pages link it directly:

       <link rel="stylesheet" href="/static/css/system.css">

   so both halves of the site are styled from the same tokens and
   cannot drift apart.

   CONTENTS
     1. Typefaces
     2. Design tokens - colour, type scale, space, motion
     3. Reset, document defaults and scrollbars
     4. Headings, prose, numerals
     5. Links, focus, selection
     6. Layout and text primitives
     7. Top bar, wordmark, nav, language switch
     8. Buttons
     9. Form controls
    10. Long-form prose and page footer
    11. Document pages
    12. Auth panel
    13. Cookie banner
    14. Modal
    15. Language footer
   ============================================================ */


@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-400-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-400-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
    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: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
    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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-500-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-500-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
    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: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
    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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-400-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-400-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-400-latin-ext.woff2') format('woff2');
    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: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-400-latin.woff2') format('woff2');
    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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-500-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-500-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-500-latin-ext.woff2') format('woff2');
    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: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-500-latin.woff2') format('woff2');
    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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-600-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-600-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-600-latin-ext.woff2') format('woff2');
    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: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/ibm-plex-sans-600-latin.woff2') format('woff2');
    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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Wordmark only. Latin-only face; the wordmark is Latin in both
   locales, so its lack of Cyrillic never shows. */
@font-face {
    font-family: 'Orbitron';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('/static/fonts/orbitron.woff2') format('woff2');
}

/* --- 1. Typefaces ---------------------------------------------
   IBM Plex Sans for prose and IBM Plex Mono for every number.
   Plex was drawn for technical interfaces and carries a full
   Cyrillic, which matters here - the old Orbitron/Audiowide pair
   has none, so every Russian heading was silently falling back to
   whatever the OS offered.

   Orbitron survives in exactly one place, the wordmark, where a
   display face is doing a job a text face cannot.
   -------------------------------------------------------------- */



:root {

    /* --- 2. Colour --------------------------------------------
       A single cool near-black ramp, and one accent.

       The accent is a signal, not decoration: it marks things
       that are live, current or interactive, and nothing else.
       When every heading is cyan, cyan stops meaning anything.
       ---------------------------------------------------------- */

    --bg:          #070b10;
    --surface:     #0b1119;
    --surface-2:   #101822;
    --surface-3:   #16202b;

    /* Hairlines. Light-on-dark at low alpha reads as a drawn rule
       rather than the glowing box the old panels used. */
    --line:        rgba(146, 178, 204, 0.11);
    --line-strong: rgba(146, 178, 204, 0.22);

    /* Text. The old --text-muted (#2e5f7a) sat at about 2.5:1 on
       the page background, under half the AA floor. These clear it:
       ink 14.6:1, ink-2 7.4:1, ink-3 4.8:1. */
    --ink:         #dde6ef;
    --ink-2:       #97a8b9;
    --ink-3:       #6d7e8f;

    --signal:      #29c5e8;
    --signal-dim:  rgba(41, 197, 232, 0.13);
    --signal-line: rgba(41, 197, 232, 0.34);
    /* Full-strength cyan, held back for genuinely live readouts. */
    --signal-live: #00c8ff;

    --ok:          #3ddc97;
    --ok-dim:      rgba(61, 220, 151, 0.13);
    --warn:        #e8a33d;
    --warn-dim:    rgba(232, 163, 61, 0.13);
    --bad:         #ef6461;
    --bad-dim:     rgba(239, 100, 97, 0.13);


    /* --- 3. Type scale ----------------------------------------
       1.2 from a 15px base, rounded to whole pixels. Nine steps,
       and nothing between them.
       ---------------------------------------------------------- */

    --t-11: 0.6875rem;   /* 11px - table headers, micro labels */
    --t-12: 0.75rem;     /* 12px - labels, captions, footnotes */
    --t-13: 0.8125rem;   /* 13px - table cells, dense readouts */
    --t-15: 0.9375rem;   /* 15px - body */
    --t-17: 1.0625rem;   /* 17px - lead paragraph */
    --t-21: 1.3125rem;   /* 21px - section heading */
    --t-27: 1.6875rem;   /* 27px - page section heading */
    --t-34: 2.125rem;    /* 34px - statistic */
    --t-44: 2.75rem;     /* 44px - page title */

    --lh-tight: 1.15;
    --lh-snug:  1.35;
    --lh-body:  1.6;

    --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
    --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
    --font-mark: 'Orbitron', var(--font-sans);


    /* --- 4. Space, rule, radius, motion -----------------------
       A 4px grid. Every margin and padding in the build resolves
       to one of these, which is the whole defence against the
       spacing drifting a few pixels per component.
       ---------------------------------------------------------- */

    --s-1:  0.25rem;   /*  4 */
    --s-2:  0.5rem;    /*  8 */
    --s-3:  0.75rem;   /* 12 */
    --s-4:  1rem;      /* 16 */
    --s-5:  1.5rem;    /* 24 */
    --s-6:  2rem;      /* 32 */
    --s-7:  3rem;      /* 48 */
    --s-8:  4rem;      /* 64 */
    --s-9:  6rem;      /* 96 */

    /* One radius. Instruments have tight corners or none. */
    --r: 2px;

    /* Motion is for acknowledging input, never for arrival.
       Nothing on this site fades in as you scroll to it. */
    --dur: 110ms;
    --ease: cubic-bezier(0.2, 0, 0.15, 1);

    --measure: 68ch;
    --page-max: 1180px;
}


@media (prefers-reduced-motion: reduce) {
    :root { --dur: 0ms; }
}

/* --- 1. Reset ----------------------------------------------- */

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

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

ul, ol { list-style: none; }

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

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


/* --- 2. Document -------------------------------------------- */

html {
    /* 15px base. The old build set html{font-size:112%} on top of
       browser defaults, which put every value in the stylesheet a
       multiplier away from what it looked like. */
    font-size: 93.75%;
    -webkit-text-size-adjust: 100%;

    /* Tells the browser to draw its own furniture dark: scrollbars,
       select menus, the time picker in the pass controls, the focus
       ring. Without it those render in the light theme and are the
       brightest thing on the page. */
    color-scheme: dark;
}


/* Scrollbars.

   Anything that scrolls - the page, the satellite list, the wide
   tables - was getting the platform's default bar, which on this
   background reads as a bright stripe down the side of a panel. A
   scrollbar is a position indicator, not a control to look at, so
   it sits at the same weight as a hairline until you reach for it.

   The two syntaxes cannot both be set. Once scrollbar-width is
   declared, Chrome switches that element to the standard scrollbar
   and ignores the ::-webkit pseudo-elements entirely - which on
   Windows means its own stepper arrows come back and there is no
   way to remove them. So each engine is given exactly one:
   Firefox the standard properties, WebKit the pseudo-elements. */

@supports not selector(::-webkit-scrollbar) {
    * {
        scrollbar-width: thin;
        scrollbar-color: var(--surface-3) transparent;
    }
}

@supports selector(::-webkit-scrollbar) {
    ::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }

    ::-webkit-scrollbar-track { background: transparent; }

    /* Styling the bar at all makes Chrome draw stepper arrows,
       which it hides on an unstyled one. Nothing uses them. */
    ::-webkit-scrollbar-button {
        display: none;
        width: 0;
        height: 0;
    }

    ::-webkit-scrollbar-thumb {
        /* The transparent border plus padding-box clipping insets
           the thumb, so it reads as a slim bar with air around it
           rather than a block filling the gutter. */
        background: var(--surface-3);
        background-clip: padding-box;
        border: 3px solid transparent;
        border-radius: 999px;
    }

    ::-webkit-scrollbar-thumb:hover {
        background: var(--ink-3);
        background-clip: padding-box;
    }

    ::-webkit-scrollbar-corner { background: transparent; }
}

body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: var(--t-15);
    line-height: var(--lh-body);
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}


/* --- 3. Headings and prose ----------------------------------
   Hierarchy comes from size, weight and space. Not from colour:
   a heading is not a different colour from the text under it.
   ------------------------------------------------------------ */

h1, h2, h3 {
    font-weight: 600;
    line-height: var(--lh-tight);
    letter-spacing: -0.015em;
    color: var(--ink);
    text-wrap: balance;
}

h1 { font-size: var(--t-44); }
h2 { font-size: var(--t-27); }
h3 { font-size: var(--t-21); letter-spacing: -0.01em; }

p { max-width: var(--measure); text-wrap: pretty; }

strong, b { font-weight: 600; color: var(--ink); }


/* --- 4. Numerals --------------------------------------------
   This is a page of measurements. Figures are monospaced and
   tabular so that a column of altitudes lines up on the decimal
   and a live counter does not jitter as it ticks.
   ------------------------------------------------------------ */

.num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'zero' 1;
    letter-spacing: -0.01em;
}

/* Unit suffix after a figure: present but subordinate. */
.unit {
    color: var(--ink-3);
    font-weight: 400;
}


/* --- 5. Links, focus, selection ------------------------------ */

a {
    color: var(--signal);
    text-decoration: none;
    border-bottom: 1px solid var(--signal-line);
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

a:hover {
    color: var(--signal-live);
    border-bottom-color: currentColor;
}

/* Visible for keyboard users, absent for mouse users. */
:focus-visible {
    outline: 2px solid var(--signal);
    outline-offset: 2px;
    border-radius: var(--r);
}

::selection {
    background: var(--signal);
    color: var(--bg);
}


/* --- 6. Shared primitives ------------------------------------ */

.shell {
    max-width: var(--page-max);
    margin-inline: auto;
    padding-inline: var(--s-5);
}

@media (max-width: 640px) {
    .shell { padding-inline: var(--s-4); }
}

/* A drawn hairline. The old build used glowing 1px borders with a
   box-shadow bloom; a rule that is just a rule reads sharper. */
.rule {
    height: 1px;
    background: var(--line);
    border: 0;
}

/* Small uppercase label. Used for field labels and column heads -
   never for a sentence, which is where tracking-out becomes
   unreadable. */
.eyebrow {
    font-family: var(--font-mono);
    font-size: var(--t-11);
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
    line-height: 1.4;
}

/* Status pill. Carries a dot so the state survives being read in
   greyscale or by anyone who cannot separate the hues. */
.tag {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.04em;
    padding: 2px var(--s-2);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    color: var(--ink-2);
    white-space: nowrap;
}

.tag::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.tag-ok   { color: var(--ok);     border-color: color-mix(in srgb, var(--ok) 34%, transparent); }
.tag-warn { color: var(--warn);   border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
.tag-bad  { color: var(--bad);    border-color: color-mix(in srgb, var(--bad) 34%, transparent); }
.tag-info { color: var(--signal); border-color: var(--signal-line); }

/* Screen-reader-only, for the labels sighted users get from layout. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --- 1. Top bar ---------------------------------------------- */

.topbar {
    border-bottom: 1px solid var(--line);
    background: var(--bg);
}

.topbar-inner {
    display: flex;
    align-items: center;
    gap: var(--s-5);
    height: 56px;
}

.wordmark {
    display: flex;
    align-items: baseline;
    gap: var(--s-3);
    border: 0;
    color: var(--ink);
    flex: none;
}

.wordmark-name {
    font-family: var(--font-mark);
    font-size: var(--t-13);
    font-weight: 700;
    letter-spacing: 0.14em;
}

.wordmark-sub {
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.topnav {
    display: flex;
    gap: var(--s-5);
    margin-left: auto;
}

.topnav a {
    font-size: var(--t-13);
    color: var(--ink-2);
    border-bottom: 1px solid transparent;
    padding-block: 2px;
}

.topnav a:hover { color: var(--ink); border-bottom-color: var(--line-strong); }

/* A nav item that performs an action rather than navigating - log
   out posts a form, log in opens a dialog. They sit in the same
   row as the links and should read the same, so they are styled to
   match rather than looking like buttons that wandered in. */
.topbar-form { display: contents; }

.topnav button {
    background: none;
    border: 0;
    border-bottom: 1px solid transparent;
    padding-block: 2px;
    font-size: var(--t-13);
    color: var(--ink-2);
    cursor: pointer;
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.topnav button:hover { color: var(--ink); border-bottom-color: var(--line-strong); }

/* Language: two hard-edged options, current one filled. No select
   that navigates on arrow-key. */
.lang-switch {
    display: flex;
    border: 1px solid var(--line);
    border-radius: var(--r);
    overflow: hidden;
    flex: none;
}

.lang-opt {
    background: none;
    border: 0;
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.08em;
    color: var(--ink-3);
    padding: var(--s-1) var(--s-2);
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.lang-opt + .lang-opt { border-left: 1px solid var(--line); }
.lang-opt:hover { color: var(--ink); background: var(--surface-2); }
.lang-opt[aria-pressed='true'] { color: var(--bg); background: var(--signal); }

/* Narrow: the bar wraps to two rows rather than dropping the nav.
   Hiding it would take the tracker's only log in / log out control
   off the page on a phone. */
@media (max-width: 720px) {
    .topbar-inner {
        height: auto;
        flex-wrap: wrap;
        gap: var(--s-3);
        padding-block: var(--s-3);
    }

    .topnav {
        order: 3;
        flex-basis: 100%;
        margin-left: 0;
        gap: var(--s-4);
    }

    .lang-switch { margin-left: auto; }
}


/* --- 2. Buttons ----------------------------------------------
   Two weights and nothing else. Hover changes the surface, not
   the opacity - a button that fades under the cursor reads as
   disabled for the moment it is fading.
   -------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    height: 38px;
    padding-inline: var(--s-4);
    border-radius: var(--r);
    border: 1px solid transparent;
    font-size: var(--t-13);
    font-weight: 500;
    letter-spacing: 0.01em;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}

.btn-sm { height: 30px; padding-inline: var(--s-3); font-size: var(--t-12); }

.btn-primary {
    background: var(--signal);
    color: #041018;
    border-color: var(--signal);
}

.btn-primary:hover { background: var(--signal-live); border-color: var(--signal-live); }
.btn-primary:disabled { background: var(--surface-3); border-color: var(--line); color: var(--ink-3); cursor: default; }

.btn-ghost {
    background: transparent;
    color: var(--ink);
    border-color: var(--line-strong);
}

.btn-ghost:hover { background: var(--surface-2); border-color: var(--ink-3); }

a.btn { border-bottom-width: 1px; }


/* --- 9. Form controls ----------------------------------------- */

.field { margin-bottom: var(--s-4); }

.field .eyebrow { display: block; margin-bottom: var(--s-2); }

.field input[type='email'],
.field input[type='password'],
.field input[type='text'] {
    width: 100%;
    height: 38px;
    padding-inline: var(--s-3);
    background: var(--bg);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    color: var(--ink);
    font-size: var(--t-13);
    transition: border-color var(--dur) var(--ease);
}

.field input::placeholder { color: var(--ink-3); }
.field input:focus { border-color: var(--signal); outline: none; }
.field input:focus-visible { outline: none; }

.field-affix { position: relative; display: flex; }
.field-affix input { padding-right: 4.5rem; }

/* A word, not an eye glyph. The old build used a bare emoji, which
   renders differently on every platform and says nothing to a
   screen reader. */
.reveal {
    position: absolute;
    right: 1px;
    top: 1px;
    bottom: 1px;
    padding-inline: var(--s-3);
    background: none;
    border: 0;
    border-left: 1px solid var(--line);
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-3);
    cursor: pointer;
    transition: color var(--dur) var(--ease);
}

.reveal:hover { color: var(--signal); }

.check {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--t-13);
    color: var(--ink-2);
    cursor: pointer;
    user-select: none;
}

.check input {
    appearance: none;
    width: 15px;
    height: 15px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    background: var(--bg);
    cursor: pointer;
    display: grid;
    place-content: center;
    flex: none;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.check input::before {
    content: '';
    width: 7px;
    height: 7px;
    transform: scale(0);
    background: var(--bg);
    clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
    transition: transform var(--dur) var(--ease);
}

.check input:checked { background: var(--signal); border-color: var(--signal); }
.check input:checked::before { transform: scale(1); }

.form-error {
    margin-bottom: var(--s-4);
    padding: var(--s-3);
    border: 1px solid color-mix(in srgb, var(--bad) 40%, transparent);
    border-radius: var(--r);
    background: var(--bad-dim);
    color: var(--bad);
    font-size: var(--t-13);
}


/* --- 10. Long-form prose and page footer ----------------------
   These rules style the static sections in welcome.html. They are
   not rendered by React: search engines get that copy in the HTML
   without running anything, which is the point of leaving it in
   the template.
   -------------------------------------------------------------- */

.prose {
    border-top: 1px solid var(--line);
    padding-block: var(--s-8);
}

.prose-grid {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: var(--s-7);
    align-items: start;
}

.prose-grid + .prose-grid {
    margin-top: var(--s-7);
    padding-top: var(--s-7);
    border-top: 1px solid var(--line);
}

.prose h2 {
    font-size: var(--t-15);
    font-weight: 600;
    letter-spacing: 0;
    color: var(--ink-2);
    position: sticky;
    top: var(--s-5);
}

.prose p {
    font-size: var(--t-15);
    color: var(--ink-2);
}

.prose p + p { margin-top: var(--s-4); }

/* A definition list, because that is what these are: a name and
   what it covers. The old build used a bulleted list with a hyphen
   typed in front of each item. */
.prose-list { display: grid; gap: var(--s-3); }

.prose-list > div {
    display: grid;
    grid-template-columns: 180px minmax(0, 1fr);
    gap: var(--s-4);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--line);
    font-size: var(--t-13);
}

.prose-list dt {
    font-family: var(--font-mono);
    font-size: var(--t-12);
    letter-spacing: 0.03em;
    color: var(--ink);
}

.prose-list dd { margin: 0; color: var(--ink-2); line-height: var(--lh-snug); }

@media (max-width: 820px) {
    .prose-grid { grid-template-columns: 1fr; gap: var(--s-4); }
    .prose h2 { position: static; }
    .prose-list > div { grid-template-columns: 1fr; gap: var(--s-1); }
}

.site-foot {
    border-top: 1px solid var(--line);
    padding-block: var(--s-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    flex-wrap: wrap;
    font-size: var(--t-12);
    color: var(--ink-3);
}

.site-foot nav { display: flex; gap: var(--s-4); }
.site-foot a { color: var(--ink-3); border-bottom-color: transparent; }
.site-foot a:hover { color: var(--ink); }


/* --- 11. Document pages ---------------------------------------
   Long-form text with no interface around it: the acceptable use
   policy, the legal-restriction page. One column, measured line
   length, hierarchy from size and space alone.
   -------------------------------------------------------------- */

.doc {
    max-width: 760px;
    margin-inline: auto;
    padding: var(--s-8) var(--s-5) var(--s-9);
}

.doc-head {
    padding-bottom: var(--s-5);
    border-bottom: 1px solid var(--line);
    margin-bottom: var(--s-6);
}

.doc h1 {
    font-size: var(--t-34);
    margin-bottom: var(--s-3);
}

.doc-date {
    font-family: var(--font-mono);
    font-size: var(--t-12);
    color: var(--ink-3);
}

/* A part heading. Numbered sections sit under these, so it gets a
   rule above it rather than more size - four heading levels of
   escalating size would be shouting by the bottom of the page. */
.doc h2 {
    font-size: var(--t-21);
    margin-top: var(--s-8);
    margin-bottom: var(--s-5);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
}

.doc h2:first-of-type { margin-top: 0; }

.doc h3 {
    font-size: var(--t-15);
    font-weight: 600;
    margin-top: var(--s-6);
    margin-bottom: var(--s-3);
}

/* The clause number is part of the heading text (3.0, 11.0 and so
   on), so it is set in mono to read as a reference. */
.doc h3 .clause {
    font-family: var(--font-mono);
    font-weight: 500;
    color: var(--ink-3);
    margin-right: var(--s-2);
}

.doc p {
    color: var(--ink-2);
    margin-bottom: var(--s-3);
}

.doc ul {
    display: grid;
    gap: var(--s-2);
    margin-bottom: var(--s-4);
}

.doc li {
    position: relative;
    padding-left: var(--s-4);
    color: var(--ink-2);
    max-width: var(--measure);
}

.doc li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.65em;
    width: 5px;
    height: 1px;
    background: var(--ink-3);
}

.doc li strong { color: var(--ink); }


/* A short centred message with nothing else on the page: the 451,
   and anything else that is one sentence and a way out. */
.notice {
    min-height: 100dvh;
    display: grid;
    place-content: center;
    justify-items: start;
    gap: var(--s-4);
    max-width: 560px;
    margin-inline: auto;
    padding: var(--s-6) var(--s-5);
}

.notice-code {
    font-family: var(--font-mono);
    font-size: var(--t-12);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--bad);
}

.notice h1 { font-size: var(--t-34); }

.notice p { color: var(--ink-2); }


/* --- 12. Auth panel -------------------------------------------
   The sign-in and sign-up card. One object, used on / by the React
   landing island and on /register by the Jinja template, so the two
   forms cannot end up looking like different products.
   -------------------------------------------------------------- */

*/

.signin {
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
    padding: var(--s-6);
}

.signin-title {
    font-size: var(--t-21);
    margin-bottom: var(--s-5);
}

.signin-actions {
    display: grid;
    gap: var(--s-2);
    margin-top: var(--s-5);
}

.signin-actions .btn { width: 100%; }

.or {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin-block: var(--s-5);
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.or::before, .or::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--line);
}

.oauth { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); }

.oauth-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    height: 38px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    font-size: var(--t-13);
    color: var(--ink);
    background: transparent;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.oauth-btn:hover { background: var(--surface-2); border-color: var(--ink-3); color: var(--ink); }
.oauth-btn img { border-radius: 50%; }


/* Centres the panel on a page that is nothing but the panel. */
.auth-page {
    min-height: 100dvh;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: var(--s-5);
    padding: var(--s-6) var(--s-5);
}

.auth-alt {
    font-size: var(--t-13);
    color: var(--ink-3);
    text-align: center;
}


/* --- 13. Cookie banner ----------------------------------------
   Shared with the React landing island, which renders the same
   markup from CookieBanner.jsx.
   -------------------------------------------------------------- */

.cookie {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-4);
    flex-wrap: wrap;
    padding: var(--s-3) var(--s-5);
    background: var(--surface-2);
    border-top: 1px solid var(--line-strong);
}

.cookie-text {
    font-size: var(--t-12);
    color: var(--ink-2);
    max-width: none;
    margin: 0;
}


/* --- 14. Modal -------------------------------------------------
   One dialog treatment, used by the tracker's log-in prompt and
   the globe page's panels.
   -------------------------------------------------------------- */

.modal {
    position: fixed;
    inset: 0;
    z-index: 9998;
    /* tracker.js and ui.js open this by setting an inline
       display:flex, so the closed state is the default here. */
    display: none;
    align-items: center;
    justify-content: center;
    padding: var(--s-4);
    background: rgba(4, 7, 11, 0.78);
}

.modal-card {
    width: 100%;
    max-width: 360px;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    padding: var(--s-5);
}

.modal-title {
    font-size: var(--t-17);
    font-weight: 600;
    margin-bottom: var(--s-4);
}

.modal-close {
    display: block;
    width: 100%;
    margin-top: var(--s-4);
    padding: var(--s-2);
    background: none;
    border: 0;
    font-family: var(--font-mono);
    font-size: var(--t-11);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
    cursor: pointer;
    transition: color var(--dur) var(--ease);
}

.modal-close:hover { color: var(--ink); }


/* --- 15. Language footer ---------------------------------------
   A plain pair of links. The old build used a <select> that
   navigated on change, which fires on every arrow-key press for
   anyone choosing with the keyboard.
   -------------------------------------------------------------- */

.lang-footer {
    display: flex;
    justify-content: center;
    gap: var(--s-4);
    padding: var(--s-5);
    border-top: 1px solid var(--line);
    font-size: var(--t-12);
}

.lang-footer a {
    color: var(--ink-3);
    border-bottom-color: transparent;
}

.lang-footer a:hover { color: var(--ink); }
.lang-footer a[aria-current='true'] { color: var(--ink); border-bottom-color: var(--line-strong); }
