/*
 * Semantic roles, then the components. One stylesheet for every page.
 *
 * TWO LAYERS, and the order matters:
 *
 *   --o-*   the palette (public/css/palette.css), byte-for-byte theme colours
 *   --bg, --card, --accent, ...   the roles the components actually use
 *
 * A COMPONENT NEVER NAMES A PALETTE COLOUR. It uses --bg or --accent, which this
 * file derives from the palette, so adding or changing a theme cannot break a
 * component and a component cannot end up hard-coded to one theme.
 *
 * The derivation uses color-mix, so a role is a proportion of the theme's own
 * foreground over its own background. That is what makes a card readable in all
 * 22 palettes without 22 rules: the value tracks the palette rather than being
 * chosen for one of them.
 */

/* ─── Roles ───────────────────────────────────────────────────────────────── */

:root {
    color-scheme: light dark;

    /* Surfaces */
    --bg: var(--o-bg);
    --surface: var(--o-bg);
    --card: color-mix(in srgb, var(--o-fg) 4%, var(--o-bg));
    --sunken: var(--o-bg-dark);
    --raised: color-mix(in srgb, var(--o-fg) 8%, var(--o-bg));

    /* Text.
       --muted is MIXED TOWARD THE FOREGROUND rather than used raw. The palette's
       dim foreground is designed for terminal chrome, and measured against each
       theme's own background it sits between 1.8 and 2.6:1, well under the 4.5
       that body text needs. This role carries .intro, .meta and .note, which are
       paragraphs rather than decoration.

       90% is the measured value, not a guess: at that mix the muted colour clears
       4.5:1 against its own background in ALL 22 palettes (the worst is rose-pine
       at 5.25, and the next lowest is catppuccin-latte). Lower mixes leave two
       themes short. The palette colour is the input, not the output. */
    --ink: var(--o-fg);
    --ink-bright: var(--o-fg-bright);
    --muted: color-mix(in srgb, var(--o-fg) 90%, var(--o-bg));

    /* THE PLACEHOLDER NEEDS ITS OWN ROLE, AND `--muted` WAS THE WRONG ONE.
       The owner asked for a placeholder that is "more opaque than the text once it is filled"
       and distinguishable from it. `--muted` is a 90% mix toward the foreground - deliberately
       almost the same as `--ink`, because it carries PARAGRAPHS (.intro, .meta, .note) that have
       to clear 4.5:1. Used for a placeholder it renders as effectively the same colour as
       entered text, which is the opposite of what is wanted.

       THE PALETTE'S OWN `--o-fg-dim` IS TOO FAINT ON ITS OWN - MEASURED at 2.51:1 on
       matte-black and 2.30:1 on catppuccin-latte, under the 3:1 that non-text content needs -
       so this is a mix toward it rather than the raw value, exactly as `--muted` is a mix away
       from it and for the same reason.

       60% IS THE MEASURED VALUE, not a guess. Across the two default palettes it gives 4.68:1
       on matte-black and 3.46:1 on catppuccin-latte - both legible, and still clearly not the
       entered text, which measures 10.08:1 and 7.06:1. At 70% the light palette falls to
       3.11:1 and at 80% to 2.80:1, below the floor; at 40% the placeholder is too close to the
       text to be told apart. The palette colour is the input, not the output. */
    --placeholder: color-mix(in srgb, var(--o-fg) 40%, var(--o-fg-dim));

    /* Lines and interaction */
    --line: color-mix(in srgb, var(--o-fg) 16%, var(--o-bg));
    --line-strong: color-mix(in srgb, var(--o-fg) 28%, var(--o-bg));
    --accent: var(--o-accent);
    --accent-ink: var(--o-bg-darker);
    --hover: color-mix(in srgb, var(--o-fg) 8%, transparent);
    --selected: var(--o-selection);

    /* Notice and warning, for the two states a page can be in */
    --notice-bg: color-mix(in srgb, var(--o-blue) 14%, var(--o-bg));
    --notice-line: color-mix(in srgb, var(--o-blue) 40%, var(--o-bg));
    --notice-ink: var(--o-fg-bright);
    --warn-bg: color-mix(in srgb, var(--o-yellow) 16%, var(--o-bg));
    --warn-line: color-mix(in srgb, var(--o-yellow) 45%, var(--o-bg));
    --warn-ink: var(--o-fg-bright);

    /* Status pills. Light and dark palettes need opposite treatments, which is
       why these read --o-mode rather than one fixed pair of colours. */
    --good-bg: color-mix(in srgb, var(--o-green) 18%, var(--o-bg));
    --good-ink: color-mix(in srgb, var(--o-green) 70%, var(--o-fg-bright));
    --idle-bg: color-mix(in srgb, var(--o-fg) 10%, var(--o-bg));
    --idle-ink: var(--o-fg-dim);

    --radius: 10px;
    --radius-sm: 8px;
    --shadow: 0 1px 2px color-mix(in srgb, var(--o-bg-darker) 40%, transparent);

    --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ─── Reset and page ──────────────────────────────────────────────────────── */

* { box-sizing: border-box; }

html { background: var(--bg); }

body {
    margin: 0;
    font-family: var(--font);
    background: var(--bg);
    color: var(--ink);
    line-height: 1.55;
    -webkit-text-size-adjust: 100%;
}

.wrap {
    max-width: 620px;
    margin: 0 auto;
    padding: 24px 16px 64px;
}

/* The narrow pages (sign-in, recovery, paste a code) want a single column. */
.wrap.narrow { max-width: 460px; margin-top: 8vh; }

/* ─── Type ────────────────────────────────────────────────────────────────── */

/* MEASURED: these labels were 11.2px against 14.4px body text - SMALLER than the paragraphs
   they head, and coloured `--accent` at a weight that reads as decoration. The owner's words
   were that it is "pretty difficult to see the difference between a normal text and a category
   header", and the measurement says why: there was no difference in size to see.

   SIZE AND LETTER-SPACING BOTH MOVE, because the complaint is about identifying a header rather
   than about reading one. 0.8rem with more tracking and full uppercase reads as a LABEL at a
   glance, which a size change alone does not achieve at this scale - and the tracking is what
   carries it, since these strings are short. */
.eyebrow {
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin: 0 0 6px;
}

h1 {
    font-size: 1.65rem;
    margin: 0 0 8px;
    letter-spacing: -0.01em;
    color: var(--ink-bright);
}

h2 {
    font-size: 1.05rem;
    margin: 0 0 8px;
    color: var(--ink-bright);
}

.card > h2.eyebrow { font-size: 0.8rem; }

.intro { color: var(--muted); font-size: 0.95rem; margin: 0 0 18px; }
.meta { color: var(--muted); font-size: 0.82rem; margin: 10px 0 0; }
.note { color: var(--muted); font-size: 0.8rem; margin: 12px 0 0; }

a { color: var(--accent); }

code, .code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background: var(--sunken);
    padding: 2px 5px;
    border-radius: 4px;
}

/* ─── Cards and sections ──────────────────────────────────────────────────── */

.card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 18px;
    margin-bottom: 16px;
}

.card p { color: var(--muted); font-size: 0.9rem; margin: 0 0 12px; }
.card p:last-child { margin-bottom: 0; }

/* ─── Forms ───────────────────────────────────────────────────────────────── */

label {
    display: block;
    font-size: 0.82rem;
    font-weight: 600;
    margin-bottom: 6px;
    color: var(--ink);
}

input[type="text"], input[type="email"], input[type="password"], select {
    width: 100%;
    padding: 11px 13px;
    font-size: 1rem;
    font-family: inherit;
    color: var(--ink);
    background: var(--raised);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

/* The token fields are long unbroken strings, so a monospace face helps. */
input.mono, .mono-input {
    font-family: var(--font-mono);
    letter-spacing: 0.02em;
}

input:focus, select:focus, button:focus-visible, a.button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* THE PLACEHOLDER IS DISTINGUISHABLE, LEGIBLE, AND GONE THE MOMENT THEY TYPE.
   `:focus::placeholder` is explicit rather than relied upon: Chrome and Firefox hide the
   placeholder on typing, but keeping it while the field is focused is allowed and some
   browsers do it, so the owner's "disappear once clicked" is stated here instead of assumed.
   `opacity: 1` is required alongside it - Firefox applies a default reduction to placeholder
   colour, which would undo the measured contrast. */
input::placeholder, textarea::placeholder { color: var(--placeholder); opacity: 1; }
input:focus::placeholder, textarea:focus::placeholder { color: transparent; }

/* A row of controls that wraps on a phone rather than overflowing. */
/* MEASURED, NOT CHOSEN BY EYE: the gap was 8px, which put the Save button 8px from the
   input it saves - close enough that the two read as one control rather than a field and
   its action. 12px separates them without the row wrapping any sooner, and `flex-wrap`
   still lets a narrow screen stack them.

   THE TWO FORMS ON THE CONSOLE SHARE THIS, which is the point of it being here: the
   organisation name row and the invite row are the same shape, so they must not drift
   apart in spacing. */
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.row > div { flex: 1 1 160px; min-width: 0; }
.row input { flex: 1 1 200px; min-width: 0; }

/* MEASURED: 11px of vertical padding gave a 38px button, which the owner read as cramped -
   "padding around the button is way too small on top and bottom". The HORIZONTAL padding was
   never the complaint and is unchanged at 18px, so the button is taller without becoming wider:
   15px top and bottom gives 46px, which is a comfortable touch target on a phone (44px is the
   usual floor) and reads as a button rather than as a label with a background.

   ONE RULE FOR EVERY BUTTON on every surface - the console's Save and Send invitation, the
   questionnaire's submit, and Sign out - because they share this selector. The questionnaire
   was checked separately: its submit measured 38px too and is now 46px with the rest. */
button {
    padding: 15px 18px;
    font-size: 0.9rem;
    font-weight: 700;
    font-family: inherit;
    border: none;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--accent-ink);
    cursor: pointer;
}

button:hover { filter: brightness(1.1); }
button:disabled { opacity: 0.55; cursor: default; filter: none; }

button.secondary, a.button {
    background: var(--raised);
    color: var(--ink);
    border: 1px solid var(--line);
}

a.button {
    display: inline-block;
    padding: 10px 16px;
    font-size: 0.9rem;
    font-weight: 700;
    text-decoration: none;
    border-radius: var(--radius-sm);
}

a.button.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

form.inline { display: inline; }
.actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* ─── Notices ─────────────────────────────────────────────────────────────── */

.notice, .problem {
    padding: 11px 13px;
    border-radius: var(--radius-sm);
    font-size: 0.88rem;
    margin-bottom: 14px;
}

.notice { background: var(--notice-bg); border: 1px solid var(--notice-line); color: var(--notice-ink); }
.problem { background: var(--warn-bg); border: 1px solid var(--warn-line); color: var(--warn-ink); }

/* ─── Tables (the console) ────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; font-size: 0.87rem; }
/* ROW RHYTHM. 8px of vertical padding made a table of addresses and timestamps read as a
   dense block rather than as rows - the owner's "cramped vertical rhythm". 10px top and
   bottom gives each row room without turning a five-row table into a column of islands,
   and the 10px of horizontal padding stops a long address touching the column beside it.
   Measured: a row was 70px tall before, and the tokens inside cells carry their own
   spacing, so this is the one gap that was doing all the work. */
th, td { text-align: left; padding: 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
th {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    font-weight: 700;
}

.who { font-weight: 600; color: var(--ink-bright); margin: 0 0 4px; }
.empty { color: var(--muted); font-size: 0.87rem; margin: 0; }

.pill {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: 99px;
    white-space: nowrap;
}

.pill.yes { background: var(--good-bg); color: var(--good-ink); }
.pill.no { background: var(--idle-bg); color: var(--idle-ink); }

/* ─── Header and the theme/language switcher ──────────────────────────────── */

/* MEASURED: the theme picker sat 0px from the paragraph below it on the questionnaire, and the
   owner read that as "too close to the text below it". The header carried no bottom margin, so
   its last row - the picker and the language links - touched whatever came next. The console
   views looked fine only because their nav has its own `margin-bottom: 18px`, which is why this
   was visible on one surface and not the others.

   THE SPACE BELONGS TO THE HEADER RATHER THAN TO THE PICKER, because what needs separating is
   the whole header block from the page's content. Putting it on `.theme-picker` would fix the
   questionnaire alone and leave the console's nav sizing to drift against a second rule. */
header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}

.switchers { display: flex; gap: 10px; align-items: center; font-size: 0.8rem; }
.lang-switch a { color: var(--muted); text-decoration: none; margin-left: 8px; }
.lang-switch a.active { color: var(--accent); font-weight: 700; }

/* The theme picker. A <select> rather than a custom control: it is keyboard and
   screen-reader accessible by default, and it works without JavaScript for the
   stored case, because the cookie already chose the theme server-side. */
.theme-picker { display: flex; align-items: center; gap: 6px; }
.theme-picker label { margin: 0; font-size: 0.78rem; font-weight: 600; color: var(--muted); }
.theme-picker select {
    width: auto;
    padding: 6px 8px;
    font-size: 0.8rem;
    background: var(--raised);
}

/* ─── The cookie notice ──────────────────────────────────────────────────────
   MOVED HERE FROM questionnaire.css: see views/partials/cookie-notice.ejs for why it is a
   site component rather than a questionnaire one.

   `.hidden` COMES WITH IT. It is what the banner uses to hide itself, and it lived only in
   questionnaire.css - so on the other pages the banner would render visible on load and
   clicking would do nothing, which is the defect this move fixes.

   `--cookie-notice-height` IS WRITTEN BY THE PARTIAL'S SCRIPT from the banner's own measured
   height, and the body padding reserves exactly that much. Zero until then, so a page without
   JavaScript keeps today's behaviour rather than reserving a gap for a banner that may not
   appear. The padding is what stops the fixed banner covering the questionnaire's answer
   input, which it otherwise does at 390px. */
.hidden { display: none !important; }

.cookie-banner {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    background: var(--card);
    border-top: 1px solid var(--line);
    padding: 14px 16px;
    font-size: 0.85rem;
    box-shadow: 0 -2px 12px color-mix(in srgb, var(--o-bg-darker) 30%, transparent);
}

.cookie-inner { max-width: 620px; margin: 0 auto; }
.cookie-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.cookie-actions button { width: auto; margin: 0; padding: 9px 16px; font-size: 0.85rem; }

body { padding-bottom: var(--cookie-notice-height, 0); }
