/**
 * GameOut - the application stylesheet
 *
 * HOW THIS FILE RELATES TO BOOTSTRAP
 *
 * Bootstrap 5.3 is loaded first and is kept for what it is good at: the grid, the
 * utilities and the javascript behaviours (collapse, dropdown, modal, offcanvas,
 * toast, tab). Everything a visitor can SEE is restyled here - the type, the
 * surfaces, the buttons, the form controls, the tables, the navigation - so the
 * app reads as its own design and not as a bootstrap page, while every class the
 * scripts depend on (form-control, is-invalid, invalid-feedback, was-validated,
 * spinner-border, toast, modal, btn-close, ...) keeps working exactly as before.
 *
 * The restyle goes through bootstrap's own css variables wherever a component
 * exposes them (--bs-btn-*, --bs-card-*, --bs-modal-*, ...) and through plain
 * rules where it does not. Nothing here needs !important except where bootstrap
 * itself uses it (the bg-* and text-* utilities), and those cases are commented.
 *
 * THE TOKENS
 *
 * Every neutral - the page canvas, the surfaces, the borders, the three text
 * steps - is an --app-* variable defined once per theme and then MAPPED onto the
 * bootstrap variables, so a bootstrap component that was never touched here still
 * lands on the right colour. The six semantic colours (primary, secondary, success,
 * info, warning, danger) are the app's re-tinted palette with its contrast table
 * further down. "success" is also the BRAND accent: links, the active navigation
 * entry, checked switches, the focus ring and the primary buttons all use it, so the
 * page has one accent and not two - and that accent is the PURPLE of the logo and the
 * favicon (#370166 at icon size), lifted to a mid purple (#8f55cc) that white text
 * reads on, so the page is purple without being dark. The pink beside it
 * (--app-accent-2-rgb) is only ever a tint, the hero's glow.
 *
 * The three text steps are TEXT, so each one is readable, not decoration: even the
 * faintest (--app-text-3: hints, placeholders, labels, the sign in divider) keeps
 * at least 4.5:1 (WCAG AA) against the canvas, the surface and surface-2 in both
 * themes - light #66707f is 4.56-5.01, dark #828a97 is 4.57-5.42 - and --app-text-2
 * holds it on the subtle alert backgrounds too. A step is for a NEUTRAL background
 * only: text that sits on a solid colour (inside a btn-success, a bg-* badge, an
 * active pill) takes that colour's own text colour - "color: inherit" - never a grey.
 *
 * Both themes define the SAME variable names: bootstrap's own [data-bs-theme=dark]
 * rule is earlier in the cascade but equal in specificity, so a variable set only
 * in :root here would beat it and leak a light-theme step into dark mode.
 */

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

:root,
[data-bs-theme=light] {
    --app-font-sans: "Inter Variable", Inter, ui-sans-serif, system-ui, -apple-system,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
    --app-font-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas,
        "Liberation Mono", monospace;

    --app-canvas: #f5f6f8;
    --app-surface: #ffffff;
    --app-surface-2: #f3f4f7;
    --app-surface-3: #e9ebf0;
    --app-border: #e3e6eb;
    --app-border-strong: #cfd4dc;
    --app-text: #171a21;
    --app-text-2: #5b6472;
    --app-text-3: #66707f;

    --app-accent: #8f55cc;
    --app-accent-strong: #7a48ad;
    --app-accent-soft: #e9ddf5;
    --app-accent-text: #392252;
    --app-ring: rgba(143, 85, 204, 0.28);
    /* the pink beside the purple - only ever a tint (the hero's glow) */
    --app-accent-2-rgb: 201, 71, 154;

    --app-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.04), 0 1px 3px rgba(16, 24, 40, 0.06);
    --app-shadow: 0 1px 3px rgba(16, 24, 40, 0.06), 0 6px 20px -6px rgba(16, 24, 40, 0.12);
    --app-shadow-lg: 0 4px 10px rgba(16, 24, 40, 0.06), 0 20px 48px -12px rgba(16, 24, 40, 0.22);
    /* what dims the page around a guided hint */
    --app-scrim: rgba(16, 24, 40, 0.42);

    --app-radius: 0.625rem;
    --app-radius-lg: 0.875rem;
    --app-radius-xl: 1rem;

    --app-topbar-height: 3.75rem;
}
[data-bs-theme=dark] {
    --app-canvas: #0f1116;
    --app-surface: #171a21;
    --app-surface-2: #1e222b;
    --app-surface-3: #262b36;
    --app-border: #262b35;
    --app-border-strong: #353c49;
    --app-text: #e7e9ee;
    --app-text-2: #a0a8b5;
    --app-text-3: #828a97;

    --app-accent: #8f55cc;
    --app-accent-strong: #a06fd4;
    --app-accent-soft: #2b1a3d;
    --app-accent-text: #bc99e0;
    --app-ring: rgba(160, 111, 212, 0.35);
    --app-accent-2-rgb: 217, 80, 156;

    --app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
    --app-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 8px 24px -8px rgba(0, 0, 0, 0.6);
    --app-shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.4), 0 24px 56px -12px rgba(0, 0, 0, 0.7);
    --app-scrim: rgba(0, 0, 0, 0.6);
}

/**
 * the neutrals, mapped onto bootstrap
 *
 * --bs-body-bg is the SURFACE, not the page: bootstrap paints inputs, cards,
 * dropdowns, modals, offcanvases and the floating label's cover with it, and
 * those sit on white. The page canvas is painted on <body> directly, below.
 */
:root,
[data-bs-theme=light] {
    --bs-font-sans-serif: var(--app-font-sans);
    --bs-font-monospace: var(--app-font-mono);
    --bs-body-font-family: var(--app-font-sans);
    --bs-body-font-size: 0.9375rem;
    --bs-body-line-height: 1.55;

    --bs-body-bg: var(--app-surface);
    --bs-body-bg-rgb: 255, 255, 255;
    --bs-body-color: var(--app-text);
    --bs-body-color-rgb: 23, 26, 33;
    --bs-emphasis-color: #0b0d12;
    --bs-emphasis-color-rgb: 11, 13, 18;
    --bs-secondary-color: var(--app-text-2);
    --bs-secondary-color-rgb: 91, 100, 114;
    --bs-secondary-bg: var(--app-surface-3);
    --bs-secondary-bg-rgb: 233, 235, 240;
    --bs-tertiary-color: var(--app-text-3);
    --bs-tertiary-color-rgb: 102, 112, 127;
    --bs-tertiary-bg: var(--app-surface-2);
    --bs-tertiary-bg-rgb: 243, 244, 247;
    --bs-heading-color: var(--app-text);
    --bs-border-color: var(--app-border);
    --bs-border-color-translucent: rgba(16, 24, 40, 0.08);
    --bs-code-color: #9c3f6b;
    --bs-highlight-bg: #fdefd0;

    --bs-link-color: var(--app-accent);
    --bs-link-color-rgb: 143, 85, 204;
    --bs-link-hover-color: var(--app-accent-strong);
    --bs-link-hover-color-rgb: 122, 72, 173;
    --bs-link-decoration: underline;

    --bs-border-radius: var(--app-radius);
    --bs-border-radius-sm: 0.5rem;
    --bs-border-radius-lg: var(--app-radius-lg);
    --bs-border-radius-xl: var(--app-radius-xl);
    --bs-border-radius-xxl: 1.25rem;

    --bs-box-shadow-sm: var(--app-shadow-sm);
    --bs-box-shadow: var(--app-shadow);
    --bs-box-shadow-lg: var(--app-shadow-lg);

    --bs-focus-ring-width: 0.2rem;
    --bs-focus-ring-color: var(--app-ring);
}
[data-bs-theme=dark] {
    --bs-body-bg: var(--app-surface);
    --bs-body-bg-rgb: 23, 26, 33;
    --bs-body-color: var(--app-text);
    --bs-body-color-rgb: 231, 233, 238;
    --bs-emphasis-color: #ffffff;
    --bs-emphasis-color-rgb: 255, 255, 255;
    --bs-secondary-color: var(--app-text-2);
    --bs-secondary-color-rgb: 160, 168, 181;
    --bs-secondary-bg: var(--app-surface-3);
    --bs-secondary-bg-rgb: 38, 43, 54;
    --bs-tertiary-color: var(--app-text-3);
    --bs-tertiary-color-rgb: 130, 138, 151;
    --bs-tertiary-bg: var(--app-surface-2);
    --bs-tertiary-bg-rgb: 30, 34, 43;
    --bs-heading-color: var(--app-text);
    --bs-border-color: var(--app-border);
    --bs-border-color-translucent: rgba(255, 255, 255, 0.09);
    --bs-code-color: #e38fbb;
    --bs-highlight-bg: #312304;

    --bs-link-color: var(--app-accent-text);
    --bs-link-color-rgb: 188, 153, 224;
    --bs-link-hover-color: #d2bbeb;
    --bs-link-hover-color-rgb: 210, 187, 235;

    --bs-box-shadow-sm: var(--app-shadow-sm);
    --bs-box-shadow: var(--app-shadow);
    --bs-box-shadow-lg: var(--app-shadow-lg);
    --bs-focus-ring-color: var(--app-ring);
}

/* ----------------------------------------------------------- the palette */

/**
 * Theme colours
 *
 * The bootstrap semantic palette, re-tinted. Five colours keep their MEANING and their
 * hue family — amber is still warning, cyan still info, red still danger — but each one
 * is moved a few degrees of hue and a step of saturation off the stock bootstrap value,
 * so the app stops reading as "default bootstrap" without any status changing colour.
 * SUCCESS is the exception: it is the brand accent (see the head of this file), so it
 * is the brand's purple and not a green — a saved form, an "on" badge and a valid field
 * show the accent, and the one colour that still shouts is danger.
 *
 * The rule the whole palette is built on: each seed sits at the SAME relative luminance
 * as the bootstrap colour it replaces (solved for, not eyeballed). Luminance is what
 * every contrast ratio is computed from, so holding it fixed means every pairing that
 * was readable before is readable now, in both themes, to within a few hundredths:
 *
 *              white on colour    black on colour    on dark body #212529
 *   primary      4.50 -> 4.52                          3.43 -> 3.42
 *   secondary    4.69 -> 4.68                          3.29 -> 3.30
 *   success      4.53 -> 4.84 (the brand purple)      3.40 -> 3.19
 *   info                            10.72 -> 10.81     7.88 -> 7.94
 *   warning                         12.88 -> 10.95     9.46 -> 8.04
 *   danger       4.53 -> 4.51                          3.41 -> 3.42
 *
 * Two seeds step off that rule on purpose. Success is the brand purple: chosen for the
 * brand first and checked after, it sits a little darker than the green it replaced
 * (white on it 4.84, above AA). Warning is a black-text colour with an enormous
 * contrast margin, so it is allowed to sit at its own lightness rather than chase the
 * luminance of stock #ffc107 — that colour is a near-yellow, and matching its luminance
 * at a warmer hue forces a washed-out pastel. It lands at 10.95 against black, still
 * more than double the AA threshold.
 *
 * Everything below the six seeds is DERIVED with bootstrap's own sass formulas rather
 * than picked by hand, which is why the steps stay in tune with each other:
 *
 *   light: text-emphasis = shade 60%, bg-subtle = tint 80%, border-subtle = tint 60%
 *   dark:  text-emphasis = tint 40%,  bg-subtle = shade 80%, border-subtle = shade 40%
 *   buttons, light text: hover = shade 15%, active = shade 20%, borders one step further
 *   buttons, dark text:  hover = tint 15%,  active = tint 20%  (bootstrap tints, not
 *                        shades, a button whose label is black — it keeps the label
 *                        contrast rising rather than falling on hover)
 *
 * Alert bodies (text-emphasis on bg-subtle) come out between 7.3 and 10.5 in both
 * themes, the same band as stock.
 *
 * WHY THIS IS SO LONG: bootstrap 5.3 splits its palette in two. The utilities, alerts,
 * form validation and list groups read css variables, so overriding the variables is
 * enough for them. The BUTTON variants do not — .btn-success and friends are compiled
 * to literal hex, so a variable-only override would leave every button on the old
 * palette while everything around it moved. Hence the .btn-* blocks in the buttons
 * section: they are not decoration, they are the half of bootstrap that cannot be
 * reached from :root.
 */
:root {
    --bs-primary: #4b6ee4;
    --bs-secondary: #6a7584;
    --bs-success: #8f55cc;
    --bs-info: #26cce1;
    --bs-warning: #f5ae14;
    --bs-danger: #d63d53;

    --bs-primary-rgb: 75, 110, 228;
    --bs-secondary-rgb: 106, 117, 132;
    --bs-success-rgb: 143, 85, 204;
    --bs-info-rgb: 38, 204, 225;
    --bs-warning-rgb: 245, 174, 20;
    --bs-danger-rgb: 214, 61, 83;

    --bs-primary-text-emphasis: #1e2c5b;
    --bs-secondary-text-emphasis: #2a2f35;
    --bs-success-text-emphasis: #392252;
    --bs-info-text-emphasis: #0f525a;
    --bs-warning-text-emphasis: #624608;
    --bs-danger-text-emphasis: #561821;

    --bs-primary-bg-subtle: #dbe2fa;
    --bs-secondary-bg-subtle: #e1e3e6;
    --bs-success-bg-subtle: #e9ddf5;
    --bs-info-bg-subtle: #d4f5f9;
    --bs-warning-bg-subtle: #fdefd0;
    --bs-danger-bg-subtle: #f7d8dd;

    --bs-primary-border-subtle: #b7c5f4;
    --bs-secondary-border-subtle: #c3c8ce;
    --bs-success-border-subtle: #d2bbeb;
    --bs-info-border-subtle: #a8ebf3;
    --bs-warning-border-subtle: #fbdfa1;
    --bs-danger-border-subtle: #efb1ba;

    --bs-form-valid-color: #8f55cc;
    --bs-form-valid-border-color: #8f55cc;
    --bs-form-invalid-color: #d63d53;
    --bs-form-invalid-border-color: #d63d53;
}
[data-bs-theme=dark] {
    --bs-primary-text-emphasis: #93a8ef;
    --bs-secondary-text-emphasis: #a6acb5;
    --bs-success-text-emphasis: #bc99e0;
    --bs-info-text-emphasis: #7de0ed;
    --bs-warning-text-emphasis: #f9ce72;
    --bs-danger-text-emphasis: #e68b98;

    --bs-primary-bg-subtle: #0f162e;
    --bs-secondary-bg-subtle: #15171a;
    --bs-success-bg-subtle: #1d1129;
    --bs-info-bg-subtle: #08292d;
    --bs-warning-bg-subtle: #312304;
    --bs-danger-bg-subtle: #2b0c11;

    --bs-primary-border-subtle: #2d4289;
    --bs-secondary-border-subtle: #40464f;
    --bs-success-border-subtle: #56337a;
    --bs-info-border-subtle: #177a87;
    --bs-warning-border-subtle: #93680c;
    --bs-danger-border-subtle: #802532;

    --bs-form-valid-color: #bc99e0;
    --bs-form-valid-border-color: #bc99e0;
    --bs-form-invalid-color: #e68b98;
    --bs-form-invalid-border-color: #e68b98;
}

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

html {
    scroll-padding-top: calc(var(--app-topbar-height) + 1rem);
}
body {
    background-color: var(--app-canvas);
    color: var(--app-text);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: "cv11", "ss01", "ss03";
}
main {
    flex: 1;
}
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-weight: 600;
    letter-spacing: -0.015em;
    line-height: 1.25;
}
h1, .h1 { font-size: 1.875rem; letter-spacing: -0.025em; }
h2, .h2 { font-size: 1.5rem; letter-spacing: -0.02em; }
h3, .h3 { font-size: 1.25rem; }
h4, .h4 { font-size: 1.125rem; }
h5, .h5 { font-size: 1rem; }
h6, .h6 { font-size: 0.9rem; }
.display-5 {
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.05;
}
.display-6 {
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.lead {
    font-size: 1.125rem;
    font-weight: 400;
    color: var(--app-text-2);
}
a {
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
    text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
a:hover {
    text-decoration-color: currentColor;
}
hr {
    border-top-color: var(--app-border);
    opacity: 1;
}
small, .small {
    font-size: 0.85em;
}
strong, b {
    font-weight: 600;
}
code {
    font-size: 0.85em;
    padding: 0.1em 0.35em;
    border-radius: 0.35rem;
    background-color: var(--app-surface-2);
    border: 1px solid var(--app-border);
    color: var(--app-text);
    overflow-wrap: anywhere;
}
pre {
    font-size: 0.8125rem;
    line-height: 1.55;
    padding: 1rem 1.1rem;
    border-radius: var(--app-radius-lg);
    background-color: var(--app-surface-2);
    border: 1px solid var(--app-border);
    color: var(--app-text);
    overflow: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
pre code {
    padding: 0;
    border: 0;
    background: transparent;
    font-size: inherit;
    color: inherit;
}
/* the ad-hoc "p-2 border border-1 border-solid" blocks are plain pre blocks now */
pre.border {
    border-color: var(--app-border) !important;
    padding: 1rem 1.1rem !important;
}
kbd {
    background-color: var(--app-text);
    color: var(--app-surface);
    border-radius: 0.35rem;
    font-size: 0.8em;
}
.font-monospace {
    font-family: var(--app-font-mono) !important;
}
::selection {
    background-color: var(--app-accent-soft);
    color: var(--app-text);
}
:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}
fieldset:disabled {
    color: var(--app-text-2);
}
.text-muted {
    color: var(--app-text-2) !important;
}
/* the utilities keep their meaning and move to the palette steps that read as text */
.text-success { color: var(--bs-success-text-emphasis) !important; }
.text-danger { color: var(--bs-danger-text-emphasis) !important; }
.text-warning { color: var(--bs-warning-text-emphasis) !important; }
.text-info { color: var(--bs-info-text-emphasis) !important; }
.text-primary { color: var(--bs-primary-text-emphasis) !important; }
[data-bs-theme=dark] .text-success { color: var(--bs-success-text-emphasis) !important; }
.link-danger {
    color: var(--bs-danger-text-emphasis) !important;
    text-decoration-color: var(--bs-danger-border-subtle) !important;
}
.link-danger:hover,
.link-danger:focus {
    color: var(--bs-danger) !important;
    text-decoration-color: var(--bs-danger) !important;
}
.link-secondary,
.link-body-emphasis {
    color: var(--app-text-2) !important;
}
.link-secondary:hover,
.link-body-emphasis:hover {
    color: var(--app-text) !important;
}

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

/**
 * three weights of button, told apart by fill and not by colour alone:
 *   solid   (.btn-success, .btn-danger, ...)  - the one action a block is for
 *   neutral (.btn-outline-secondary)          - everything else, on a surface
 *   soft    (.btn-outline-success, ...)       - a tinted neutral, for an action
 *                                               that carries a meaning (export,
 *                                               disconnect, ...) but is not primary
 * the outline classes are kept, because the templates and the scripts know them;
 * what changed is what they look like
 */
.btn {
    --bs-btn-padding-x: 0.95rem;
    --bs-btn-padding-y: 0.5rem;
    --bs-btn-font-size: 0.875rem;
    --bs-btn-font-weight: 500;
    --bs-btn-line-height: 1.35;
    --bs-btn-border-radius: var(--app-radius);
    --bs-btn-focus-box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease,
        box-shadow 0.15s ease, transform 0.1s ease;
}
.btn:active:not(:disabled) {
    transform: translateY(1px);
}
.btn > i,
.btn > .fa-solid,
.btn > .fa-regular,
.btn > .fa-brands {
    font-size: 0.95em;
    line-height: 1;
}
.btn-sm {
    --bs-btn-padding-x: 0.7rem;
    --bs-btn-padding-y: 0.375rem;
    --bs-btn-font-size: 0.8125rem;
    --bs-btn-border-radius: 0.5rem;
}
.btn-lg {
    --bs-btn-padding-x: 1.25rem;
    --bs-btn-padding-y: 0.7rem;
    --bs-btn-font-size: 0.95rem;
    --bs-btn-border-radius: var(--app-radius);
}
.btn-link {
    --bs-btn-font-weight: 500;
    --bs-btn-color: var(--bs-link-color);
    --bs-btn-hover-color: var(--bs-link-hover-color);
    text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
}
/* an icon-only button is square */
.app-btn-icon {
    --bs-btn-padding-x: 0.5rem;
    width: 2.1rem;
    height: 2.1rem;
    padding: 0;
}
.app-btn-icon.btn-sm {
    width: 1.9rem;
    height: 1.9rem;
}

.btn-primary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #4b6ee4;
    --bs-btn-border-color: #4b6ee4;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #405ec2;
    --bs-btn-hover-border-color: #3c58b6;
    --bs-btn-focus-shadow-rgb: 102, 132, 232;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #3c58b6;
    --bs-btn-active-border-color: #3852ab;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #4b6ee4;
    --bs-btn-disabled-border-color: #4b6ee4;
}
.btn-secondary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #6a7584;
    --bs-btn-border-color: #6a7584;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #5a6370;
    --bs-btn-hover-border-color: #555e6a;
    --bs-btn-focus-shadow-rgb: 128, 138, 150;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #555e6a;
    --bs-btn-active-border-color: #505863;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #6a7584;
    --bs-btn-disabled-border-color: #6a7584;
}
.btn-success {
    --bs-btn-color: #fff;
    --bs-btn-bg: #8f55cc;
    --bs-btn-border-color: #8f55cc;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #7a48ad;
    --bs-btn-hover-border-color: #7244a3;
    --bs-btn-focus-shadow-rgb: 160, 111, 212;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #7244a3;
    --bs-btn-active-border-color: #6b4099;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #8f55cc;
    --bs-btn-disabled-border-color: #8f55cc;
}
.btn-info {
    --bs-btn-color: #000;
    --bs-btn-bg: #26cce1;
    --bs-btn-border-color: #26cce1;
    --bs-btn-hover-color: #000;
    --bs-btn-hover-bg: #47d4e6;
    --bs-btn-hover-border-color: #3cd1e4;
    --bs-btn-focus-shadow-rgb: 32, 173, 191;
    --bs-btn-active-color: #000;
    --bs-btn-active-bg: #51d6e7;
    --bs-btn-active-border-color: #3cd1e4;
    --bs-btn-disabled-color: #000;
    --bs-btn-disabled-bg: #26cce1;
    --bs-btn-disabled-border-color: #26cce1;
}
.btn-warning {
    --bs-btn-color: #000;
    --bs-btn-bg: #f5ae14;
    --bs-btn-border-color: #f5ae14;
    --bs-btn-hover-color: #000;
    --bs-btn-hover-bg: #f6ba37;
    --bs-btn-hover-border-color: #f6b62b;
    --bs-btn-focus-shadow-rgb: 208, 148, 17;
    --bs-btn-active-color: #000;
    --bs-btn-active-bg: #f7be43;
    --bs-btn-active-border-color: #f6b62b;
    --bs-btn-disabled-color: #000;
    --bs-btn-disabled-bg: #f5ae14;
    --bs-btn-disabled-border-color: #f5ae14;
}
.btn-danger {
    --bs-btn-color: #fff;
    --bs-btn-bg: #d63d53;
    --bs-btn-border-color: #d63d53;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #b63447;
    --bs-btn-hover-border-color: #ab3142;
    --bs-btn-focus-shadow-rgb: 220, 90, 109;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #ab3142;
    --bs-btn-active-border-color: #a02e3e;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #d63d53;
    --bs-btn-disabled-border-color: #d63d53;
}
.btn-primary,
.btn-secondary,
.btn-success,
.btn-danger {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 1px 2px rgba(16, 24, 40, 0.12);
}
.btn-info,
.btn-warning {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(16, 24, 40, 0.1);
}

/* the neutral button - a surface with a hairline, the workhorse of the app */
.btn-outline-secondary {
    --bs-btn-color: var(--app-text);
    --bs-btn-bg: var(--app-surface);
    --bs-btn-border-color: var(--app-border-strong);
    --bs-btn-hover-color: var(--app-text);
    --bs-btn-hover-bg: var(--app-surface-2);
    --bs-btn-hover-border-color: var(--app-border-strong);
    --bs-btn-focus-shadow-rgb: 106, 117, 132;
    --bs-btn-active-color: var(--app-text);
    --bs-btn-active-bg: var(--app-surface-3);
    --bs-btn-active-border-color: var(--app-border-strong);
    --bs-btn-disabled-color: var(--app-text-3);
    --bs-btn-disabled-bg: var(--app-surface);
    --bs-btn-disabled-border-color: var(--app-border);
    box-shadow: var(--app-shadow-sm);
}
.btn-outline-secondary.active {
    background-color: var(--app-surface-3);
}
/* the soft buttons - tinted, the colour's text step on its subtle step */
.btn-outline-primary {
    --bs-btn-color: var(--bs-primary-text-emphasis);
    --bs-btn-bg: var(--bs-primary-bg-subtle);
    --bs-btn-border-color: var(--bs-primary-border-subtle);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #4b6ee4;
    --bs-btn-hover-border-color: #4b6ee4;
    --bs-btn-focus-shadow-rgb: 75, 110, 228;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #3c58b6;
    --bs-btn-active-border-color: #3c58b6;
    --bs-btn-disabled-color: var(--bs-primary-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-primary-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-primary-border-subtle);
}
.btn-outline-success {
    --bs-btn-color: var(--bs-success-text-emphasis);
    --bs-btn-bg: var(--bs-success-bg-subtle);
    --bs-btn-border-color: var(--bs-success-border-subtle);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #8f55cc;
    --bs-btn-hover-border-color: #8f55cc;
    --bs-btn-focus-shadow-rgb: 143, 85, 204;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #7244a3;
    --bs-btn-active-border-color: #7244a3;
    --bs-btn-disabled-color: var(--bs-success-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-success-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-success-border-subtle);
}
.btn-outline-info {
    --bs-btn-color: var(--bs-info-text-emphasis);
    --bs-btn-bg: var(--bs-info-bg-subtle);
    --bs-btn-border-color: var(--bs-info-border-subtle);
    --bs-btn-hover-color: #000;
    --bs-btn-hover-bg: #26cce1;
    --bs-btn-hover-border-color: #26cce1;
    --bs-btn-focus-shadow-rgb: 38, 204, 225;
    --bs-btn-active-color: #000;
    --bs-btn-active-bg: #26cce1;
    --bs-btn-active-border-color: #26cce1;
    --bs-btn-disabled-color: var(--bs-info-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-info-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-info-border-subtle);
}
.btn-outline-warning {
    --bs-btn-color: var(--bs-warning-text-emphasis);
    --bs-btn-bg: var(--bs-warning-bg-subtle);
    --bs-btn-border-color: var(--bs-warning-border-subtle);
    --bs-btn-hover-color: #000;
    --bs-btn-hover-bg: #f5ae14;
    --bs-btn-hover-border-color: #f5ae14;
    --bs-btn-focus-shadow-rgb: 245, 174, 20;
    --bs-btn-active-color: #000;
    --bs-btn-active-bg: #f5ae14;
    --bs-btn-active-border-color: #f5ae14;
    --bs-btn-disabled-color: var(--bs-warning-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-warning-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-warning-border-subtle);
}
.btn-outline-danger {
    --bs-btn-color: var(--bs-danger-text-emphasis);
    --bs-btn-bg: var(--bs-danger-bg-subtle);
    --bs-btn-border-color: var(--bs-danger-border-subtle);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #d63d53;
    --bs-btn-hover-border-color: #d63d53;
    --bs-btn-focus-shadow-rgb: 214, 61, 83;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #ab3142;
    --bs-btn-active-border-color: #ab3142;
    --bs-btn-disabled-color: var(--bs-danger-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-danger-bg-subtle);
    --bs-btn-disabled-border-color: var(--bs-danger-border-subtle);
}
/* a bare .btn (the eye and regenerate buttons in input groups) is a ghost */
.btn.text-secondary {
    --bs-btn-hover-bg: var(--app-surface-2);
    --bs-btn-active-bg: var(--app-surface-3);
    --bs-btn-hover-border-color: var(--app-border-strong);
    color: var(--app-text-2) !important;
}
.btn.text-secondary:hover {
    color: var(--app-text) !important;
}
.btn-close {
    --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'%3e%3cpath d='M3.5 3.5l9 9M12.5 3.5l-9 9'/%3e%3c/svg%3e");
    --bs-btn-close-opacity: 0.55;
    --bs-btn-close-hover-opacity: 1;
    --bs-btn-close-focus-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
    border-radius: 0.5rem;
    transition: opacity 0.15s ease, background-color 0.15s ease;
}
.btn-close:hover {
    background-color: var(--app-surface-2);
}
.dropdown-toggle::after {
    border: 0;
    width: 0.8em;
    height: 0.8em;
    margin-left: 0.15em;
    vertical-align: -0.1em;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e") center / contain no-repeat;
    opacity: 0.7;
}
.btn-group > .btn-check:checked + .btn,
.btn-group > .btn-check:active + .btn {
    box-shadow: inset 0 1px 2px rgba(16, 24, 40, 0.15);
}

/* ------------------------------------------------------------------ forms */

.form-control,
.form-select {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius);
    color: var(--app-text);
    padding: 0.55rem 0.8rem;
    font-size: 0.9375rem;
    line-height: 1.45;
    box-shadow: inset 0 1px 2px rgba(16, 24, 40, 0.03);
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.form-control::placeholder {
    color: var(--app-text-3);
}
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    background-color: var(--app-surface);
    border-color: var(--app-accent);
    box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
    color: var(--app-text);
}
.form-control:disabled,
.form-select:disabled {
    background-color: var(--app-surface-2);
    color: var(--app-text-2);
    border-color: var(--app-border);
}
/* the ATTRIBUTE, not :read-only - a <select> matches :read-only in every browser */
.form-control[readonly]:not(:disabled) {
    background-color: var(--app-surface-2);
    opacity: 1;
}
.form-control-lg {
    padding: 0.7rem 1rem;
    font-size: 1rem;
    border-radius: var(--app-radius);
}
.form-control-sm {
    padding: 0.35rem 0.6rem;
    font-size: 0.85rem;
    border-radius: 0.5rem;
}
textarea.form-control {
    line-height: 1.5;
}
.form-control[type=file] {
    padding: 0.45rem 0.8rem;
}
.form-control::file-selector-button {
    background-color: var(--app-surface-2);
    color: var(--app-text);
    border-radius: 0.5rem;
    border: 1px solid var(--app-border);
    padding: 0.3rem 0.75rem;
    margin: 0 0.75rem 0 0;
    font-weight: 500;
    font-size: 0.85rem;
}
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
    background-color: var(--app-surface-3);
}
/* a select, whether .form-select or .form-control, carries the same chevron */
.form-select,
select.form-control {
    --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236a7584' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
    background-image: var(--bs-form-select-bg-img);
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 1rem 1rem;
    padding-right: 2.4rem;
    appearance: none;
    -webkit-appearance: none;
}
select.form-control[multiple],
.form-select[multiple] {
    background-image: none;
    padding-right: 0.8rem;
}
[data-bs-theme=dark] .form-select,
[data-bs-theme=dark] select.form-control {
    --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23a0a8b5' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
}
.form-label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--app-text);
    margin-bottom: 0.35rem;
}
.form-text,
.app-form-hint {
    font-size: 0.8125rem;
    color: var(--app-text-2);
    margin-top: 0.35rem;
    line-height: 1.45;
}
.input-group {
    border-radius: var(--app-radius);
}
.input-group-text {
    background-color: var(--app-surface-2);
    border: 1px solid var(--app-border-strong);
    color: var(--app-text-2);
    font-size: 0.875rem;
    border-radius: var(--app-radius);
}
.input-group > .btn {
    box-shadow: none;
}
.input-group > .form-control:focus,
.input-group > .form-select:focus {
    z-index: 5;
}

/**
 * floating labels - the mechanics are bootstrap's (the label scales up to the top
 * edge when the field has a value); the sizes, the colours and the cover behind
 * the label are the app's
 */
.form-floating > .form-control,
.form-floating > .form-control-plaintext,
.form-floating > .form-select {
    height: calc(3.375rem + 2px);
    min-height: calc(3.375rem + 2px);
    line-height: 1.3;
    padding: 1.25rem 0.8rem 0.5rem;
}
.form-floating > textarea.form-control {
    height: auto;
}
.form-floating > label {
    padding: 0.95rem 0.8rem;
    color: var(--app-text-2);
    font-size: 0.9375rem;
    line-height: 1.3;
    height: 100%;
    transform-origin: 0 0;
}
.form-floating > .form-control:focus ~ label,
.form-floating > .form-control:not(:placeholder-shown) ~ label,
.form-floating > .form-control-plaintext ~ label,
.form-floating > .form-select ~ label {
    color: var(--app-text-2);
    transform: scale(0.8) translateY(-0.6rem) translateX(0.2rem);
}
.form-floating > .form-control:focus ~ label::after,
.form-floating > .form-control:not(:placeholder-shown) ~ label::after,
.form-floating > .form-control-plaintext ~ label::after,
.form-floating > .form-select ~ label::after {
    background-color: transparent;
}
.form-floating > .form-control:focus ~ label {
    color: var(--app-accent-text);
}
.form-floating > .form-control:disabled ~ label,
.form-floating > .form-control[readonly] ~ label,
.form-floating > :disabled ~ label {
    color: var(--app-text-3);
}
.form-floating > .form-control:disabled ~ label::after,
.form-floating > .form-control[readonly] ~ label::after,
.form-floating > :disabled ~ label::after,
.form-floating > :read-only ~ label::after {
    background-color: transparent;
}
.form-floating > .form-control:focus,
.form-floating > .form-control:not(:placeholder-shown) {
    padding-top: 1.45rem;
    padding-bottom: 0.45rem;
}

/* checks and switches */
.form-check {
    min-height: 1.5rem;
    padding-left: 1.75em;
    margin-bottom: 0.25rem;
}
.form-check-input {
    width: 1.15em;
    height: 1.15em;
    margin-top: 0.2em;
    margin-left: -1.75em;
    background-color: var(--app-surface);
    border: 1.5px solid var(--app-border-strong);
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-check-input[type=checkbox] {
    border-radius: 0.35em;
}
.form-check-input:checked {
    background-color: var(--app-accent);
    border-color: var(--app-accent);
}
.form-check-input:focus {
    border-color: var(--app-accent);
}
.form-check-label {
    cursor: pointer;
}
.form-switch {
    padding-left: 2.9em;
}
.form-switch .form-check-input {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
    width: 2.4em;
    height: 1.35em;
    margin-top: 0.08em;
    margin-left: -2.9em;
    border: 0;
    background-color: var(--app-border-strong);
    background-image: var(--bs-form-switch-bg);
    background-position: left center;
    border-radius: 2em;
    box-shadow: inset 0 1px 2px rgba(16, 24, 40, 0.15);
    transition: background-position 0.15s ease-in-out, background-color 0.15s ease;
}
.form-switch .form-check-input:focus {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
    box-shadow: inset 0 1px 2px rgba(16, 24, 40, 0.15), 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
}
.form-switch .form-check-input:checked {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
    background-color: var(--app-accent);
    background-position: right center;
}
[data-bs-theme=dark] .form-switch .form-check-input {
    background-color: var(--app-surface-3);
}
[data-bs-theme=dark] .form-switch .form-check-input:checked {
    background-color: var(--app-accent);
}
.form-switch .form-check-input:disabled {
    opacity: 0.5;
}
.form-check-inline {
    margin-right: 1.25rem;
}

/* validation - the scripts write .invalid-feedback and .was-validated, bootstrap shows them */
.invalid-feedback {
    font-size: 0.8125rem;
    margin-top: 0.35rem;
    color: var(--bs-danger-text-emphasis);
}
.was-validated .form-control:invalid,
.form-control.is-invalid,
.was-validated .form-select:invalid,
.form-select.is-invalid {
    border-color: var(--bs-danger);
    background-image: none;
    padding-right: 0.8rem;
}
.was-validated .form-control:invalid:focus,
.form-control.is-invalid:focus,
.was-validated .form-select:invalid:focus,
.form-select.is-invalid:focus {
    border-color: var(--bs-danger);
    box-shadow: 0 0 0 var(--bs-focus-ring-width) rgba(214, 61, 83, 0.25);
}
.was-validated .form-control:valid,
.form-control.is-valid,
.was-validated .form-select:valid,
.form-select.is-valid {
    border-color: var(--app-border-strong);
    background-image: none;
    padding-right: 0.8rem;
}
.was-validated .form-control:valid:focus,
.form-control.is-valid:focus {
    border-color: var(--app-accent);
    box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
}
.was-validated .form-check-input:valid,
.form-check-input.is-valid {
    border-color: var(--app-border-strong);
}
.was-validated .form-check-input:valid:checked,
.form-check-input.is-valid:checked {
    background-color: var(--app-accent);
    border-color: var(--app-accent);
}
.was-validated .form-check-input:valid ~ .form-check-label,
.form-check-input.is-valid ~ .form-check-label {
    color: inherit;
}
.was-validated .form-check-input:invalid ~ .form-check-label,
.form-check-input.is-invalid ~ .form-check-label {
    color: var(--bs-danger-text-emphasis);
}
.was-validated .form-floating > .form-control:invalid ~ label,
.form-floating > .form-control.is-invalid ~ label {
    color: var(--bs-danger-text-emphasis);
}
.was-validated .form-floating > .form-control:valid ~ label,
.form-floating > .form-control.is-valid ~ label {
    color: var(--app-text-2);
}

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

.card {
    --bs-card-bg: var(--app-surface);
    --bs-card-border-color: var(--app-border);
    --bs-card-border-radius: var(--app-radius-lg);
    --bs-card-inner-border-radius: calc(var(--app-radius-lg) - 1px);
    --bs-card-spacer-y: 1.25rem;
    --bs-card-spacer-x: 1.25rem;
    --bs-card-cap-bg: transparent;
    --bs-card-cap-padding-y: 1rem;
    --bs-card-cap-padding-x: 1.25rem;
    --bs-card-title-spacer-y: 0.5rem;
    box-shadow: var(--app-shadow-sm);
}
.card-title {
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}
.card-subtitle {
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.card-header {
    font-weight: 600;
}
.card-header h5,
.card-header .h5 {
    font-size: 1rem;
}
.card .card-text {
    color: var(--app-text-2);
}
/* the two coloured cards of the pricing pages */
.card.border-success {
    border-color: var(--bs-success-border-subtle) !important;
}
.card.border-secondary {
    border-color: var(--app-border) !important;
}
.card.border-warning {
    border-color: var(--bs-warning-border-subtle) !important;
}
.card.border-danger,
.modal-content.border-danger {
    border-color: var(--bs-danger-border-subtle) !important;
}
.card.bg-body-tertiary {
    background-color: var(--app-surface-2) !important;
    box-shadow: none;
}

/* ----------------------------------------------------------------- badges */

/**
 * badges are soft: the colour's text step on its subtle step, never white on a
 * solid fill - a solid chip shouts, and nothing a badge says here is urgent.
 * bg-* sets its colour with !important, so the overrides have to as well; the
 * selectors are more specific, so they win
 */
.badge {
    --bs-badge-padding-x: 0.6em;
    --bs-badge-padding-y: 0.3em;
    --bs-badge-font-size: 0.74em;
    --bs-badge-font-weight: 600;
    --bs-badge-border-radius: 50rem;
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    vertical-align: middle;
    letter-spacing: 0.01em;
    line-height: 1.2;
}
.badge.bg-success, .badge.text-bg-success {
    background-color: var(--bs-success-bg-subtle) !important;
    color: var(--bs-success-text-emphasis) !important;
    box-shadow: inset 0 0 0 1px var(--bs-success-border-subtle);
}
.badge.bg-secondary, .badge.text-bg-secondary {
    background-color: var(--app-surface-3) !important;
    color: var(--app-text-2) !important;
    box-shadow: inset 0 0 0 1px var(--app-border-strong);
}
.badge.bg-danger, .badge.text-bg-danger {
    background-color: var(--bs-danger-bg-subtle) !important;
    color: var(--bs-danger-text-emphasis) !important;
    box-shadow: inset 0 0 0 1px var(--bs-danger-border-subtle);
}
.badge.bg-warning, .badge.text-bg-warning, .badge.bg-warning.text-dark {
    background-color: var(--bs-warning-bg-subtle) !important;
    color: var(--bs-warning-text-emphasis) !important;
    box-shadow: inset 0 0 0 1px var(--bs-warning-border-subtle);
}
.badge.bg-info, .badge.text-bg-info, .badge.bg-info.text-dark {
    background-color: var(--bs-info-bg-subtle) !important;
    color: var(--bs-info-text-emphasis) !important;
    box-shadow: inset 0 0 0 1px var(--bs-info-border-subtle);
}
.badge.bg-primary, .badge.text-bg-primary {
    background-color: var(--bs-primary-bg-subtle) !important;
    color: var(--bs-primary-text-emphasis) !important;
    box-shadow: inset 0 0 0 1px var(--bs-primary-border-subtle);
}
.badge.border {
    border-color: var(--app-border-strong) !important;
    background-color: var(--app-surface) !important;
}

/* ----------------------------------------------------------------- alerts */

.alert {
    --bs-alert-padding-x: 1rem;
    --bs-alert-padding-y: 0.8rem;
    --bs-alert-border-radius: var(--app-radius-lg);
    --bs-alert-margin-bottom: 1rem;
    font-size: 0.9rem;
    line-height: 1.5;
}
.alert > i:first-child,
.alert > .fa-solid:first-child,
.alert > .fa-regular:first-child {
    margin-right: 0.35rem;
}
.alert-heading {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 0.35rem;
}
.alert p:last-child {
    margin-bottom: 0;
}
.alert-success { box-shadow: inset 3px 0 0 var(--bs-success); }
.alert-info { box-shadow: inset 3px 0 0 var(--bs-info); }
.alert-warning { box-shadow: inset 3px 0 0 var(--bs-warning); }
.alert-danger { box-shadow: inset 3px 0 0 var(--bs-danger); }
.alert-primary { box-shadow: inset 3px 0 0 var(--bs-primary); }
.alert-secondary {
    --bs-alert-bg: var(--app-surface-2);
    --bs-alert-border-color: var(--app-border);
    --bs-alert-color: var(--app-text);
    box-shadow: inset 3px 0 0 var(--app-border-strong);
}
.alert.rounded-0 {
    box-shadow: none;
}

/* -------------------------------------------------------------- dropdowns */

.dropdown-menu {
    --bs-dropdown-bg: var(--app-surface);
    --bs-dropdown-color: var(--app-text);
    --bs-dropdown-border-color: var(--app-border);
    --bs-dropdown-border-radius: var(--app-radius-lg);
    --bs-dropdown-padding-x: 0.4rem;
    --bs-dropdown-padding-y: 0.4rem;
    --bs-dropdown-item-padding-x: 0.75rem;
    --bs-dropdown-item-padding-y: 0.5rem;
    --bs-dropdown-item-border-radius: 0.55rem;
    --bs-dropdown-link-color: var(--app-text);
    --bs-dropdown-link-hover-color: var(--app-text);
    --bs-dropdown-link-hover-bg: var(--app-surface-2);
    --bs-dropdown-link-active-color: var(--app-accent-text);
    --bs-dropdown-link-active-bg: var(--app-accent-soft);
    --bs-dropdown-font-size: 0.9rem;
    --bs-dropdown-divider-bg: var(--app-border);
    --bs-dropdown-divider-margin-y: 0.35rem;
    --bs-dropdown-header-color: var(--app-text-2);
    --bs-dropdown-header-padding-x: 0.75rem;
    --bs-dropdown-header-padding-y: 0.5rem;
    --bs-dropdown-min-width: 12rem;
    box-shadow: var(--app-shadow-lg);
}
.dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-weight: 500;
}
.dropdown-item > i {
    width: 1.1em;
    text-align: center;
    color: var(--app-text-2);
    font-size: 0.9em;
}
.dropdown-item-text {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}
.dropdown-header {
    font-size: 0.78rem;
    font-weight: 500;
}
/* the datatable's sort menu wraps its links in a dropdown-item span */
.dropdown-item a {
    text-decoration: none;
}

/* ----------------------------------------------------------------- modals */

.modal {
    --bs-modal-bg: var(--app-surface);
    --bs-modal-border-color: var(--app-border);
    --bs-modal-border-radius: var(--app-radius-xl);
    --bs-modal-inner-border-radius: calc(var(--app-radius-xl) - 1px);
    --bs-modal-header-border-color: var(--app-border);
    --bs-modal-footer-border-color: var(--app-border);
    --bs-modal-padding: 1.25rem;
    --bs-modal-header-padding: 1rem 1.25rem;
    --bs-modal-footer-gap: 0.5rem;
    --bs-modal-box-shadow: var(--app-shadow-lg);
    --bs-modal-box-shadow-sm-up: var(--app-shadow-lg);
}
.modal-title {
    font-size: 1.05rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.modal-header.bg-danger-subtle {
    background-color: var(--bs-danger-bg-subtle) !important;
}
.modal-backdrop {
    --bs-backdrop-bg: #0b0d12;
    --bs-backdrop-opacity: 0.55;
}

/* -------------------------------------------------------------- offcanvas */

.offcanvas,
.offcanvas-lg {
    --bs-offcanvas-bg: var(--app-surface);
    --bs-offcanvas-color: var(--app-text);
    --bs-offcanvas-border-color: var(--app-border);
    --bs-offcanvas-width: 320px;
    --bs-offcanvas-padding-x: 1.25rem;
    --bs-offcanvas-padding-y: 1rem;
    --bs-offcanvas-box-shadow: var(--app-shadow-lg);
}
.offcanvas-title {
    font-size: 1rem;
    font-weight: 600;
}

/* -------------------------------------------------------------- accordion */

.accordion {
    --bs-accordion-bg: var(--app-surface);
    --bs-accordion-border-color: var(--app-border);
    --bs-accordion-border-radius: var(--app-radius-lg);
    --bs-accordion-inner-border-radius: calc(var(--app-radius-lg) - 1px);
    --bs-accordion-btn-padding-x: 1.25rem;
    --bs-accordion-btn-padding-y: 1rem;
    --bs-accordion-btn-color: var(--app-text);
    --bs-accordion-btn-bg: var(--app-surface);
    --bs-accordion-active-bg: var(--app-surface-2);
    --bs-accordion-active-color: var(--app-text);
    --bs-accordion-btn-focus-box-shadow: inset 0 0 0 2px var(--app-ring);
    --bs-accordion-body-padding-x: 1.25rem;
    --bs-accordion-body-padding-y: 1.25rem;
    --bs-accordion-btn-icon-width: 1rem;
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235b6472' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230c3628' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
    box-shadow: var(--app-shadow-sm);
    border-radius: var(--app-radius-lg);
}
[data-bs-theme=dark] .accordion-button::after {
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23a0a8b5' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2379b6a1' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 6l4 4 4-4'/%3e%3c/svg%3e");
}
.accordion-button {
    font-weight: 500;
    font-size: 0.95rem;
}
.accordion-button:not(.collapsed) {
    box-shadow: inset 0 -1px 0 var(--app-border);
}
.accordion-button > i {
    color: var(--app-text-2);
}
.accordion-button:not(.collapsed) > i {
    color: var(--app-accent-text);
}
.accordion-header {
    margin: 0;
}

/* ----------------------------------------------------------------- tables */

.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--app-text);
    --bs-table-border-color: var(--app-border);
    --bs-table-hover-bg: var(--app-surface-2);
    --bs-table-hover-color: var(--app-text);
    --bs-table-striped-bg: var(--app-surface-2);
    --bs-table-striped-color: var(--app-text);
    --bs-table-active-bg: var(--app-accent-soft);
    --bs-table-active-color: var(--app-text);
    font-size: 0.9rem;
    vertical-align: middle;
}
.table > :not(caption) > * > * {
    padding: 0.65rem 0.75rem;
    border-bottom-width: 1px;
}
.table-sm > :not(caption) > * > * {
    padding: 0.45rem 0.6rem;
}
.table > thead th,
.table > tfoot th {
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-2);
    white-space: nowrap;
}
.table > thead {
    border-bottom: 1px solid var(--app-border);
}
.table > tfoot {
    border-top: 1px solid var(--app-border);
}
.table > tbody > tr:last-child > * {
    border-bottom-width: 0;
}
.table-bordered > :not(caption) > * {
    border-width: 0;
}
.table-bordered > :not(caption) > * > * {
    border-width: 0 0 1px 0;
}
.table-warning {
    --bs-table-bg: var(--bs-warning-bg-subtle);
    --bs-table-color: var(--app-text);
}
.table-active {
    --bs-table-bg: var(--app-accent-soft);
}

/* ----------------------------------------------------------------- toasts */

.toast {
    --bs-toast-bg: var(--app-surface);
    --bs-toast-color: var(--app-text);
    --bs-toast-border-color: var(--app-border);
    --bs-toast-border-radius: var(--app-radius-lg);
    --bs-toast-box-shadow: var(--app-shadow-lg);
    --bs-toast-header-bg: transparent;
    --bs-toast-header-color: var(--app-text-2);
    --bs-toast-header-border-color: var(--app-border);
    --bs-toast-padding-x: 1rem;
    --bs-toast-padding-y: 0.75rem;
    --bs-toast-font-size: 0.9rem;
    --bs-toast-max-width: 380px;
    overflow: hidden;
}
.toast-header {
    font-size: 0.8rem;
}
.toast-header .blockquote {
    font-size: 1rem;
    margin: 0;
}
.toast-header .btn-close {
    font-size: 0.7rem;
}
.toast-body strong {
    font-weight: 600;
}
/* the script marks a toast with its type; the header's bottom line carries it */
.toast .toast-header.border-success { border-bottom-color: var(--bs-success) !important; border-bottom-width: 2px; }
.toast .toast-header.border-danger { border-bottom-color: var(--bs-danger) !important; border-bottom-width: 2px; }
.toast .toast-header.border-warning { border-bottom-color: var(--bs-warning) !important; border-bottom-width: 2px; }
.toast .toast-header.border-info { border-bottom-color: var(--bs-info) !important; border-bottom-width: 2px; }

/* ----------------------------------------------------- progress, spinner */

.progress,
.progress-stacked {
    --bs-progress-height: 0.5rem;
    --bs-progress-bg: var(--app-surface-3);
    --bs-progress-border-radius: 50rem;
    --bs-progress-bar-bg: var(--app-accent);
    --bs-progress-bar-transition: width 0.4s ease;
}
.spinner-border {
    --bs-spinner-border-width: 0.18em;
}
.pace .pace-progress {
    background: var(--app-accent);
    height: 3px;
}
.pace .pace-progress-inner {
    box-shadow: 0 0 10px var(--app-accent), 0 0 5px var(--app-accent);
}

/* ----------------------------------------------------------------- navs */

.nav {
    --bs-nav-link-color: var(--app-text-2);
    --bs-nav-link-hover-color: var(--app-text);
    --bs-nav-link-font-weight: 500;
}
.nav-pills {
    --bs-nav-pills-border-radius: var(--app-radius);
    --bs-nav-pills-link-active-bg: var(--app-accent-soft);
    --bs-nav-pills-link-active-color: var(--app-accent-text);
}
/* the segmented control - a nav-tabs with the tabs drawn as one pill */
.app-segmented {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    padding: 0.25rem;
    background-color: var(--app-surface-2);
    border: 1px solid var(--app-border) !important;
    border-radius: var(--app-radius);
    margin-bottom: 1rem;
}
.app-segmented .nav-item {
    margin: 0;
}
.app-segmented .nav-link {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    padding: 0.45rem 0.9rem;
    border: 0;
    border-radius: calc(var(--app-radius) - 0.2rem);
    color: var(--app-text-2);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    background: transparent;
    transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.app-segmented .nav-link:hover {
    color: var(--app-text);
}
.app-segmented .nav-link.active {
    background-color: var(--app-surface);
    color: var(--app-text);
    box-shadow: var(--app-shadow-sm);
}
.app-segmented .nav-link .form-check-input {
    margin: 0;
    float: none;
    width: 1em;
    height: 1em;
}
.tab-content > .tab-pane {
    padding-top: 0.25rem;
}

/* ----------------------------------------------------------------- shell */

/**
 * the top bar - a translucent surface pinned to the top, one hairline below it
 */
.app-topbar {
    position: sticky;
    top: 0;
    z-index: 1030;
    background-color: color-mix(in srgb, var(--app-surface) 86%, transparent);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
    backdrop-filter: saturate(160%) blur(14px);
    border-bottom: 1px solid var(--app-border);
}
.app-topbar .navbar {
    --bs-navbar-padding-y: 0.5rem;
    --bs-navbar-padding-x: 0;
    --bs-navbar-brand-padding-y: 0;
    --bs-navbar-brand-margin-end: 1.25rem;
    --bs-navbar-nav-link-padding-x: 0.75rem;
    --bs-navbar-toggler-padding-y: 0.4rem;
    --bs-navbar-toggler-padding-x: 0.6rem;
    --bs-navbar-toggler-font-size: 1rem;
    --bs-navbar-toggler-border-radius: 0.6rem;
    --bs-navbar-toggler-focus-width: var(--bs-focus-ring-width);
    --bs-navbar-color: var(--app-text-2);
    --bs-navbar-hover-color: var(--app-text);
    --bs-navbar-active-color: var(--app-text);
    --bs-navbar-brand-color: var(--app-text);
    --bs-navbar-brand-hover-color: var(--app-text);
    min-height: var(--app-topbar-height);
}
.app-topbar .navbar-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -0.02em;
}
.app-topbar .navbar-brand img {
    width: 28px;
    height: auto;
    border-radius: 7px;
}
.app-topbar .navbar-toggler {
    border: 0;
    color: var(--app-text-2);
    box-shadow: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.app-topbar .navbar-toggler:hover {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.app-topbar .navbar-toggler:focus-visible {
    box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
}
.app-topbar .navbar-nav {
    gap: 0.15rem;
}
.app-topbar .navbar-nav .nav-link {
    padding: 0.45rem 0.75rem;
    border-radius: 0.6rem;
    font-size: 0.9rem;
    font-weight: 500;
    line-height: 1.3;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.app-topbar .navbar-nav .nav-link:hover {
    background-color: var(--app-surface-2);
}
.app-topbar .navbar-nav .nav-link.active {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.app-topbar .btn {
    --bs-btn-padding-y: 0.42rem;
    --bs-btn-padding-x: 0.85rem;
    --bs-btn-font-size: 0.85rem;
}
/* the admin entry keeps its amber mark, quietly: an icon, no glow */
.gameout-nav-admin > i {
    color: var(--bs-warning-text-emphasis);
}
/* the account chip - the dropdown toggle of the signed in menu */
.app-user-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.75rem 0.25rem 0.3rem;
    border-radius: 50rem;
    border: 1px solid var(--app-border);
    background-color: var(--app-surface);
    color: var(--app-text);
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.3;
    text-decoration: none;
    max-width: 16rem;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.app-user-chip:hover,
.app-user-chip.active,
.app-user-chip.show {
    background-color: var(--app-surface-2);
    border-color: var(--app-border-strong);
    color: var(--app-text);
}
.app-user-chip .app-user-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.app-user-chip .app-user-id {
    color: var(--app-text-3);
    font-size: 0.75rem;
    font-weight: 500;
}
.app-user-chip::after {
    opacity: 0.5;
}
.app-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
    font-weight: 700;
    font-size: 0.78rem;
    flex: 0 0 auto;
    text-transform: uppercase;
}
.app-account-menu {
    min-width: 15rem;
}
.app-account-menu .dropdown-header {
    white-space: normal;
    overflow-wrap: anywhere;
}
/* the top bar's icon buttons: the theme toggle and the "show the hints" button */
.app-topbar-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 0;
    border-radius: 0.6rem;
    background: transparent;
    color: var(--app-text-2);
    font-size: 1rem;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.app-topbar-icon:hover {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.app-topbar-icon:focus-visible {
    outline: 0;
    box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--app-ring);
}
/* the theme toggle - one icon, the one for the OTHER theme */
[data-bs-theme=light] .app-theme-toggle .fa-sun,
:root:not([data-bs-theme=dark]) .app-theme-toggle .fa-sun {
    display: none;
}
[data-bs-theme=dark] .app-theme-toggle .fa-moon {
    display: none;
}
.app-nav-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
@media (max-width: 991.98px) {
    .app-topbar .navbar-collapse {
        padding: 0.75rem 0 0.5rem;
        border-top: 1px solid var(--app-border);
        margin-top: 0.5rem;
    }
    .app-topbar .navbar-nav .nav-link {
        padding: 0.6rem 0.75rem;
    }
    .app-nav-actions {
        padding-top: 0.5rem;
    }
    .app-user-chip {
        max-width: 100%;
    }
}

/* the impersonation banner - a bar, not a card */
.app-banner {
    border-radius: 0;
    margin: 0;
    padding: 0.6rem 1rem;
    text-align: center;
    font-size: 0.875rem;
    box-shadow: none;
    border-left: 0;
    border-right: 0;
}

/* the page canvas */
.app-main {
    flex: 1 1 auto;
    padding-bottom: 3rem;
}
/**
 * the one page width: the top bar, the footer and every page's frame are a
 * container-fluid capped here - the app (container-app) and everything else
 * (container-general) alike. inside it nothing caps the content again: no narrower
 * bootstrap .container, no col-xl-10 around a page body. only what is a card on its own
 * (the sign in box, the OAuth approval) and the reading measure of a paragraph stay narrow
 */
.container-general,
.container-app {
    max-width: 1680px;
    margin-left: auto;
    margin-right: auto;
}

/**
 * the two column layout of the app, the profile and the admin area: a sidebar that
 * stays put, and the content. below lg the sidebar becomes an offcanvas drawer,
 * opened by the button in the top bar
 */
.bd-layout {
    padding-top: 1.75rem;
}
.bd-sidebar {
    grid-area: sidebar;
}
@media (min-width: 992px) {
    .bd-layout {
        display: grid;
        grid-template-areas: "sidebar main";
        grid-template-columns: 15rem minmax(0, 1fr);
        gap: 2.5rem;
    }
    .bd-sidebar {
        position: sticky;
        top: calc(var(--app-topbar-height) + 1.5rem);
        display: block !important;
        height: calc(100vh - var(--app-topbar-height) - 3rem);
        overflow-y: auto;
        align-self: start;
        scrollbar-width: thin;
        scrollbar-color: var(--app-border-strong) transparent;
    }
    .bd-sidebar .offcanvas-body {
        padding: 0;
    }
}
@media (min-width: 1400px) {
    .bd-layout {
        gap: 3rem;
    }
}
.app-sidenav {
    --bs-nav-pills-border-radius: var(--app-radius);
    gap: 0.15rem;
}
.app-sidenav .nav-link {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.55rem 0.8rem;
    border-radius: var(--app-radius);
    color: var(--app-text-2);
    font-size: 0.9rem;
    font-weight: 500;
    line-height: 1.3;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.app-sidenav .nav-link > i {
    width: 1.25em;
    text-align: center;
    font-size: 0.95em;
    color: var(--app-text-3);
    transition: color 0.15s ease;
}
.app-sidenav .nav-link:hover {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.app-sidenav .nav-link:hover > i {
    color: var(--app-text-2);
}
.app-sidenav .nav-link.active {
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
}
.app-sidenav .nav-link.active > i {
    color: currentColor;
}
.app-sidenav .nav-item > hr {
    margin: 0.6rem 0.5rem;
}
.app-sidenav-label {
    padding: 0.75rem 0.8rem 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-text-3);
}
/* the one entry that cannot be undone: red, and red when current */
.app-sidenav .nav-link.app-sidenav-danger {
    color: var(--bs-danger-text-emphasis);
}
.app-sidenav .nav-link.app-sidenav-danger > i {
    color: var(--bs-danger);
}
.app-sidenav .nav-link.app-sidenav-danger:hover {
    background-color: var(--bs-danger-bg-subtle);
}
.app-sidenav .nav-link.app-sidenav-danger.active {
    background-color: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
/* the documentation's contents list */
.app-docnav {
    font-size: 0.875rem;
}
.app-docnav > ul > li {
    margin-bottom: 0.9rem;
}
.app-docnav a {
    display: block;
    padding: 0.25rem 0.6rem;
    border-radius: 0.5rem;
    color: var(--app-text-2);
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.app-docnav > ul > li > a {
    font-weight: 600;
    color: var(--app-text);
}
.app-docnav a:hover {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.app-docnav a.active {
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
}
.app-docnav ul ul {
    margin-top: 0.15rem;
    padding-left: 0.5rem;
    border-left: 1px solid var(--app-border);
    margin-left: 0.6rem;
}
.app-docnav ul ul a {
    padding: 0.2rem 0.6rem;
    font-size: 0.84rem;
}

/* the footer */
.app-footer {
    border-top: 1px solid var(--app-border);
    padding: 1.5rem 0;
    font-size: 0.85rem;
    color: var(--app-text-2);
    background-color: var(--app-canvas);
}
.app-footer a {
    color: var(--app-text-2);
    text-decoration: none;
}
.app-footer a:hover {
    color: var(--app-text);
}
.app-footer .app-footer-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
}
.app-footer .app-footer-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1.25rem;
}
.app-footer .app-footer-brand {
    font-weight: 600;
    color: var(--app-text);
}

/* --------------------------------------------------------- page chrome */

/**
 * every page of the app opens the same way: a title with its icon, a line under
 * it when there is something to say, and the page's actions on the right
 */
.app-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem 1.5rem;
    margin-bottom: 1.5rem;
}
.app-page-heading {
    min-width: 0;
    flex: 1 1 20rem;
}
.app-page-title {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1.2;
    overflow-wrap: anywhere;
}
.app-page-title > i {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.7rem;
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
    font-size: 0.95rem;
}
.app-page-title.text-danger > i {
    background-color: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
.app-page-title .app-page-title-meta {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--app-text-3);
    letter-spacing: 0;
    align-self: flex-end;
    padding-bottom: 0.2rem;
}
.app-page-subtitle {
    margin: 0.4rem 0 0;
    color: var(--app-text-2);
    font-size: 0.9rem;
}
.app-page-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}
.app-section-title {
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 2rem 0 0.75rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.app-section-title > i {
    color: var(--app-text-3);
    font-size: 0.9em;
}
.app-section-lead {
    color: var(--app-text-2);
    font-size: 0.9rem;
    margin-bottom: 1rem;
}
.app-eyebrow {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-text-2);
}
.app-empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--app-text-2);
    border: 1px dashed var(--app-border-strong);
    border-radius: var(--app-radius-lg);
    font-size: 0.9rem;
}
.app-empty > i {
    display: block;
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
    color: var(--app-text-3);
}
.app-btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    align-items: center;
}

/* ---------------------------------------------------------- stat tiles */

.app-stat {
    height: 100%;
}
.app-stat .card-body {
    padding: 1.1rem 1.25rem 1.15rem;
}
.app-stat-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-2);
    margin: 0;
}
.app-stat-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.6rem;
    background-color: var(--app-surface-2);
    color: var(--app-text-2);
    font-size: 0.85rem;
    flex: 0 0 auto;
}
.app-stat-value {
    margin: 0.6rem 0 0.2rem;
    font-size: 2rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.app-stat-value > i {
    font-size: 0.8em;
}
.app-stat-note {
    margin: 0;
    font-size: 0.82rem;
    color: var(--app-text-2);
    line-height: 1.45;
}

/* ------------------------------------------------------ dashboard charts */

/**
 * Dashboard — exports per day chart
 *
 * One stacked column per day, drawn with plain elements: the app ships no chart
 * library and thirty columns are not worth adding one for.
 *
 * The two series colours are NOT the bootstrap theme colours. Green already means
 * "active" everywhere else in this app, and a status colour standing in for a
 * series is a colour that says two things at once. These are a categorical pair,
 * checked in both themes against the card surface for contrast and for separation
 * under protanopia / deuteranopia — the dark steps are chosen for the dark
 * surface, not flipped from the light ones.
 *
 * Segments are separated by a 2px gap in the SURFACE colour rather than by a
 * border: a stroke would add ink that is not data.
 *
 * The two colours are declared on the ROOT rather than on the plot, because the
 * legend keys stand outside it — a variable scoped to the plot leaves them blank.
 */
:root {
    --app-chart-ui: #2a78d6;
    --app-chart-api: #eb6834;
}
[data-bs-theme=dark] {
    --app-chart-ui: #3987e5;
    --app-chart-api: #d95926;
}
.app-chart {
    height: 140px;
    border-bottom: 1px solid var(--app-border);
    /**
     * a SMALL base gap, not a bootstrap spacing class: thirty fixed 4px gaps eat
     * a phone's plot width and leave hairlines. the columns are capped instead
     * (.app-chart-day), so on a wide screen the leftover spreads between them
     * and the air comes back on its own
     */
    gap: 2px;
}
.app-chart-day {
    min-width: 0;
    /* never let a column fill its slot — the leftover is the air between bars */
    max-width: 24px;
}
.app-chart-seg {
    min-height: 2px;
}
.app-chart-seg-ui {
    background-color: var(--app-chart-ui);
}
.app-chart-seg-api {
    background-color: var(--app-chart-api);
}
/* the data-end is rounded, the baseline end stays square */
.app-chart-seg-top {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}
/* the 2px surface gap, only where two segments actually touch */
.app-chart-seg-gap {
    margin-bottom: 2px;
}
/* a day with nothing on it is a hairline, not a missing column */
.app-chart-zero {
    height: 1px;
    background-color: var(--app-border-strong);
}
.app-chart-key {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 3px;
    vertical-align: -1px;
}
.app-chart-key-ui {
    background-color: var(--app-chart-ui);
}
.app-chart-key-api {
    background-color: var(--app-chart-api);
}

/**
 * Dashboard — "most exported" bars
 *
 * One bar per template, all in the SAME hue: the templates are nominal, so
 * colouring them apart would spend the identity channel on something bar length
 * already says. The track is transparent — a band behind every bar reads as a
 * total the bar is a share of, and it is not one.
 */
.app-bar {
    height: 0.4rem;
    background-color: transparent;
}
.app-bar .progress-bar {
    background-color: var(--app-chart-ui);
    /* the data-end is rounded, the baseline end stays square */
    border-radius: 0 4px 4px 0;
}

/**
 * Dashboard — allowance meters
 *
 * The fill carries severity, so the track takes the matching subtle step of the
 * same colour: the state then reads across the whole bar, not only across the
 * filled part.
 */
.app-meter {
    height: 0.45rem;
}
.app-meter-success {
    background-color: var(--bs-success-bg-subtle);
}
.app-meter-warning {
    background-color: var(--bs-warning-bg-subtle);
}
.app-meter-danger {
    background-color: var(--bs-danger-bg-subtle);
}
.app-meter .progress-bar.bg-success { background-color: var(--bs-success) !important; }
.app-meter .progress-bar.bg-warning { background-color: var(--bs-warning) !important; }
.app-meter .progress-bar.bg-danger { background-color: var(--bs-danger) !important; }
.app-meter-list .app-meter-item + .app-meter-item {
    margin-top: 0.9rem;
}
.app-card-divider {
    border: 0;
    border-top: 1px solid var(--app-border);
    margin: 1.1rem 0;
}
.app-mini-title {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-2);
    margin-bottom: 0.35rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* ------------------------------------------------------------- forms, app */

/**
 * a form is a stack of sections, each a surface with a title, a line about it,
 * and its fields; the actions close it in one bar
 */
.app-form-section {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    box-shadow: var(--app-shadow-sm);
    padding: 1.25rem 1.25rem 1.4rem;
    margin-bottom: 1rem;
    min-width: 0;
}
.app-form-section > legend {
    float: left;
    width: 100%;
    margin: 0 0 0.2rem;
    padding: 0;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.app-form-section > legend > i {
    color: var(--app-text-3);
    font-size: 0.9em;
}
.app-form-section > legend > sup,
.app-form-section > legend .app-legend-note {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--app-text-3);
    top: 0;
}
.app-form-help {
    clear: both;
    color: var(--app-text-2);
    font-size: 0.875rem;
    margin: 0 0 1rem;
    line-height: 1.5;
}
.app-form-section > legend + .row,
.app-form-section > legend + div {
    clear: both;
}
.app-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1rem;
    padding: 0.5rem 0 0;
}
.app-form-actions-end {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-left: auto;
}
.app-form-meta {
    font-size: 0.8125rem;
    color: var(--app-text-2);
    line-height: 1.5;
}
.app-switch-block {
    padding: 0.9rem 1rem;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    background-color: var(--app-surface-2);
    height: 100%;
}
.app-switch-block .form-check {
    margin-bottom: 0;
}
.app-switch-block .form-check-label {
    font-weight: 500;
}
.app-switch-block .app-form-hint {
    margin-left: 0;
}
.app-current-file {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    background-color: var(--app-surface-2);
    color: var(--app-text);
    text-decoration: none;
    font-weight: 500;
    font-size: 0.9rem;
    max-width: 100%;
    overflow-wrap: anywhere;
}
.app-current-file:hover {
    border-color: var(--app-border-strong);
    color: var(--app-text);
}
.app-current-file > i {
    color: var(--app-accent-text);
}

/* a list row: one api key, one share, one e-mail address */
.app-list-row {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    box-shadow: var(--app-shadow-sm);
    padding: 1.1rem 1.25rem;
    margin-bottom: 0.75rem;
}
.app-list-row-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
.app-list-row-head > .app-list-row-main {
    flex: 1 1 18rem;
    min-width: 0;
}
.app-list-row-tools {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex: 0 0 auto;
}
.app-list-row-tools .input-group {
    width: auto;
    flex-wrap: nowrap;
}
.app-list-row-tools .input-group > .btn {
    --bs-btn-padding-x: 0.65rem;
}
.app-inline-fields {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
}
.app-inline-fields .form-floating {
    display: inline-block;
}
.app-validity {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}
.app-validity > .form-floating .form-control {
    width: 11rem;
}
.app-fold-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 500;
    font-size: 0.9rem;
    text-decoration: none;
    color: var(--app-text);
}
.app-fold-toggle > i.fa-chevron-down {
    font-size: 0.7em;
    color: var(--app-text-3);
    transition: transform 0.15s ease;
}
.app-fold-toggle[aria-expanded=true] > i.fa-chevron-down {
    transform: rotate(180deg);
}
.app-fold-toggle:hover {
    color: var(--app-accent-text);
}

/* a definition list laid out as a grid - the view page's summary */
.app-dl {
    display: grid;
    grid-template-columns: minmax(8rem, max-content) 1fr;
    gap: 0.6rem 1.5rem;
    margin: 0;
    font-size: 0.9rem;
}
.app-dl > dt {
    color: var(--app-text-2);
    font-weight: 500;
}
.app-dl > dd {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}
@media (max-width: 575.98px) {
    .app-dl {
        grid-template-columns: 1fr;
        gap: 0.15rem;
    }
    .app-dl > dd {
        margin-bottom: 0.6rem;
    }
}
.app-key-value {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* the sticky "unsaved changes" note of the list forms */
.app-unsaved {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

/* ---------------------------------------------------------------- tables */

/**
 * the data table chrome (@see /_tables/datatable.template.php)
 *
 * a toolbar above the table - sort, filter, the column switch, the advanced
 * switch, and the search on the right -, the table in a bordered surface, and a
 * bar below it with the page controls. the panels that unfold (columns, group by,
 * footer, widths, sticky, config) are surfaces of their own
 */
.dttbl-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1rem;
    margin-bottom: 0.75rem;
}
.dttbl-toolbar-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}
.dttbl-search {
    flex: 1 1 18rem;
    max-width: 26rem;
}
.dttbl-search .form-control {
    font-size: 0.9rem;
}
.dttbl-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.375rem 0.7rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--app-text-2);
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.dttbl-toggle:hover {
    background-color: var(--app-surface-2);
    color: var(--app-text);
}
.dttbl-toggle > i {
    font-size: 0.75em;
    transition: transform 0.15s ease;
}
.dttbl-toggle[aria-expanded=true] > i {
    transform: rotate(180deg);
}
.dttbl-advanced-switch {
    display: inline-flex;
    align-items: center;
    padding-left: 2.9em;
    margin: 0 0 0 0.25rem;
}
.dttbl-advanced-switch .form-check-label {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--app-text-2);
}
.dttbl-panel {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    padding: 1rem 1.1rem;
    margin-bottom: 0.75rem;
    box-shadow: var(--app-shadow-sm);
}
.dttbl-panel .form-check-label {
    font-size: 0.875rem;
}
.dttbl-panel label {
    font-size: 0.8rem;
    color: var(--app-text-2);
    font-weight: 500;
    margin-bottom: 0.25rem;
}
.dttbl-panel .form-control,
.dttbl-panel .input-group-text {
    font-size: 0.875rem;
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
}
.dttbl-active,
.dttbl-active.btn {
    border-color: var(--app-accent) !important;
    box-shadow: 0 0 0 2px var(--app-ring) !important;
}
.dttbl-hints {
    font-size: 0.78rem;
    color: var(--app-text-3);
    margin-bottom: 0.35rem;
    line-height: 1.4;
}
.dttbl-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
}
.dttbl-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    max-width: 100%;
    padding: 0.25rem 0.65rem;
    border-radius: 50rem;
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.dttbl-chip-remove {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.65rem;
    border-radius: 50rem;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--app-text-2);
    text-decoration: none;
    border: 1px dashed var(--app-border-strong);
}
.dttbl-chip-remove:hover {
    color: var(--bs-danger-text-emphasis);
    border-color: var(--bs-danger-border-subtle);
    background-color: var(--bs-danger-bg-subtle);
}
.dttbl-table-wrap {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    box-shadow: var(--app-shadow-sm);
    overflow: hidden;
}
.dttbl-table-wrap .table-responsive {
    border-radius: inherit;
    scrollbar-width: thin;
    scrollbar-color: var(--app-border-strong) transparent;
    /* a vertical scrollbar only when the table was given a height (table config) */
    overflow-y: hidden;
}
.dttbl-table-wrap .table-responsive[style*="max-height"] {
    overflow-y: auto;
}
.dttbl table {
    margin-bottom: 0;
    font-size: 0.875rem;
}
.dttbl table > thead th {
    background-color: var(--app-surface-2);
    vertical-align: top;
    white-space: normal;
    overflow-wrap: anywhere;
}
.dttbl table > tfoot th {
    background-color: var(--app-surface-2);
}
.dttbl table > tbody td {
    overflow-wrap: anywhere;
    vertical-align: top;
}
/* a sticky cell has to be opaque, or the columns scrolling under it show through */
.dttbl table > tbody td.position-sticky {
    background-color: var(--app-surface);
    box-shadow: 1px 0 0 var(--app-border);
}
.dttbl table > thead th.position-sticky,
.dttbl table > tfoot th.position-sticky {
    background-color: var(--app-surface-2);
}
.dttbl table > thead th .fa-thumbtack {
    font-size: 0.75em;
    color: var(--app-text-3);
}
.dttbl table > thead th input.form-control {
    font-size: 0.8rem;
    padding: 0.35rem 0.55rem;
    border-radius: 0.5rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}
.dttbl table > thead th input.form-control.dttbl-active {
    box-shadow: 0 0 0 2px var(--app-ring) !important;
}
.dttbl table pre {
    padding: 0.5rem 0.65rem;
    font-size: 0.78rem;
    margin: 0;
    background-color: var(--app-surface-2);
}
.dttbl .show-more,
.dttbl .show-less {
    text-decoration: none;
    font-weight: 500;
}
.dttbl-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1rem;
    margin-top: 0.75rem;
}
.dttbl-footer .input-group {
    width: auto;
    flex-wrap: nowrap;
}
.dttbl-footer .input-group .form-control {
    width: 4.5rem;
    flex: 0 0 auto;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.dttbl-footer .input-group .form-select {
    width: auto;
    flex: 0 0 auto;
}
.dttbl-footer .input-group-text {
    font-variant-numeric: tabular-nums;
}
.dttbl-advanced-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.25rem 0.5rem;
    margin-top: 0.75rem;
}
.dttbl-advanced-links .dttbl-toggle.text-danger:hover {
    background-color: var(--bs-danger-bg-subtle);
}
/* the offcanvas filter */
.datatable-offcanvas-filter .form-check {
    margin-bottom: 0.4rem;
}
.datatable-offcanvas-filter h6 {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-2);
    margin: 0 0 0.5rem;
}
.datatable-offcanvas-filter .dttbl-filter-group {
    padding-bottom: 1.1rem;
    margin-bottom: 1.1rem;
    border-bottom: 1px solid var(--app-border);
}
.datatable-offcanvas-filter .dttbl-filter-group:last-of-type {
    border-bottom: 0;
}
.datatable-offcanvas-filter .btn-group .btn {
    --bs-btn-padding-y: 0.3rem;
    --bs-btn-padding-x: 0.6rem;
    --bs-btn-font-size: 0.78rem;
}
.datatable-offcanvas-filter select[multiple].form-control {
    min-height: 10rem;
}
.datatable-offcanvas-filter .offcanvas-footer {
    position: sticky;
    bottom: 0;
    padding: 1rem 0 0.25rem;
    background-color: var(--app-surface);
    border-top: 1px solid var(--app-border);
    margin-top: 1rem;
}

/**
 * the datatable's "Advanced" switch: the grouping, footer, widths, sticky columns,
 * table style, export and reset controls carry .dttbl-advanced and are hidden while
 * the table section is in its simple state (@see /_tables/datatable.template.php)
 */
.dttbl-simple .dttbl-advanced {
    display: none !important;
}

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

.app-plan {
    height: 100%;
    position: relative;
}
.app-plan.app-plan-featured {
    border-color: var(--app-accent);
    box-shadow: 0 0 0 3px var(--app-ring), var(--app-shadow);
}
.app-plan .card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    border-bottom: 1px solid var(--app-border);
}
.app-plan-name {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
}
.app-plan-price {
    font-size: 2.25rem;
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.1;
    margin: 0;
    font-variant-numeric: tabular-nums;
}
.app-plan-per {
    font-size: 0.85rem;
    color: var(--app-text-2);
    margin: 0.2rem 0 1rem;
}
.app-plan-lines {
    font-size: 0.875rem;
    margin: 0 0 1.25rem;
    color: var(--app-text-2);
}
.app-plan-lines > div + div {
    margin-top: 0.3rem;
}
.app-plan .form-check-label {
    line-height: 1.45;
}
.app-limits-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: 0.4rem;
    font-size: 0.9rem;
}
.app-limits-list > li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.4rem;
}
.app-limits-list > li > i {
    width: 1.4em;
    text-align: center;
    flex: 0 0 auto;
}
.app-access-state {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
    margin: 0.5rem 0 0.35rem;
}

/* ----------------------------------------------------------- sign in */

.app-auth {
    max-width: 27rem;
    margin: 3rem auto 2rem;
}
.app-auth-wide {
    max-width: 34rem;
}
.app-auth .card {
    box-shadow: var(--app-shadow);
}
.app-auth .card-body {
    padding: 2rem;
}
.app-auth-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 1rem;
    background-color: var(--app-accent-soft);
    margin: 0 auto 1rem;
}
.app-auth-logo img {
    width: 1.9rem;
    height: auto;
}
.app-auth h1 {
    font-size: 1.5rem;
    text-align: center;
    margin-bottom: 0.35rem;
}
.app-auth .app-auth-lead {
    text-align: center;
    color: var(--app-text-2);
    font-size: 0.9rem;
    margin-bottom: 1.5rem;
}
.app-auth-providers {
    display: grid;
    gap: 0.6rem;
}
.app-auth-provider {
    --bs-btn-padding-y: 0.7rem;
    --bs-btn-font-size: 0.95rem;
    justify-content: flex-start;
    gap: 0.75rem;
    text-align: left;
}
.app-auth-provider > i {
    width: 1.25em;
    text-align: center;
    font-size: 1.1em;
}
.app-auth-provider > span {
    flex: 0 0 auto;
    white-space: nowrap;
}
/* the hint sits ON the button, whatever colour that is (the magic link one is a solid
   btn-success) - so it is the button's own text colour, never a neutral grey that
   reads on a surface and vanishes on green. smaller and lighter in weight is the
   whole difference; an opacity would take white on green below 4.5:1 */
.app-auth-provider > small {
    flex: 1 1 auto;
    text-align: right;
    font-weight: 400;
    color: inherit;
    font-size: 0.75rem;
    line-height: 1.25;
}
.app-auth-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1.25rem 0;
    color: var(--app-text-3);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.app-auth-divider::before,
.app-auth-divider::after {
    content: "";
    flex: 1 1 auto;
    border-top: 1px solid var(--app-border);
}
.app-auth-note {
    text-align: center;
    color: var(--app-text-2);
    font-size: 0.8125rem;
    margin: 1.25rem 0 0;
}
.app-auth .alert {
    font-size: 0.85rem;
}
#section-loginerror .text-danger {
    font-size: 0.875rem;
    margin-bottom: 0.75rem;
}

/* ------------------------------------------------------------ marketing */

.app-hero {
    position: relative;
    padding: 4.5rem 0 3.5rem;
    text-align: center;
    overflow: hidden;
}
.app-hero::before {
    content: "";
    position: absolute;
    inset: -40% -10% auto;
    height: 120%;
    background:
        radial-gradient(45% 55% at 30% 30%, rgba(143, 85, 204, 0.18), transparent 70%),
        radial-gradient(40% 50% at 75% 20%, rgba(var(--app-accent-2-rgb), 0.14), transparent 70%);
    pointer-events: none;
    z-index: 0;
}
[data-bs-theme=dark] .app-hero::before {
    background:
        radial-gradient(45% 55% at 30% 30%, rgba(143, 85, 204, 0.22), transparent 70%),
        radial-gradient(40% 50% at 75% 20%, rgba(var(--app-accent-2-rgb), 0.16), transparent 70%);
}
.app-hero > .container {
    position: relative;
    z-index: 1;
}
.app-hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.85rem;
    border-radius: 50rem;
    border: 1px solid var(--bs-success-border-subtle);
    background-color: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    font-size: 0.8rem;
    font-weight: 600;
    margin-bottom: 1.25rem;
}
.app-hero h1 {
    font-size: clamp(2.1rem, 5vw, 3.4rem);
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.05;
    margin: 0 auto 1.25rem;
    max-width: 16ch;
}
.app-hero .lead {
    font-size: 1.15rem;
    color: var(--app-text);
    max-width: 44rem;
    margin: 0 auto 0.75rem;
}
.app-hero p {
    max-width: 44rem;
    margin-left: auto;
    margin-right: auto;
}
.app-hero-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.6rem;
    margin: 1.75rem 0 1rem;
}
.app-hero-actions .btn-lg {
    --bs-btn-padding-x: 1.5rem;
    --bs-btn-padding-y: 0.8rem;
}
.app-section {
    padding: 3rem 0;
}
.app-section-heading {
    text-align: center;
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-bottom: 0.5rem;
}
.app-section-sub {
    text-align: center;
    color: var(--app-text-2);
    max-width: 40rem;
    margin: 0 auto 2rem;
}
.app-usecases {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.6rem;
}
.app-usecase {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 1rem;
    border-radius: 50rem;
    border: 1px solid var(--app-border);
    background-color: var(--app-surface);
    font-size: 0.9rem;
    font-weight: 500;
    box-shadow: var(--app-shadow-sm);
}
.app-usecase > i {
    color: var(--app-accent-text);
}
.app-feature {
    height: 100%;
    text-align: left;
}
.app-feature .card-body {
    padding: 1.5rem;
}
.app-feature-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 0.85rem;
    background-color: var(--app-accent-soft);
    color: var(--app-accent-text);
    font-size: 1.15rem;
    margin-bottom: 1rem;
}
.app-feature h4 {
    font-size: 1.05rem;
    margin-bottom: 0.5rem;
}
.app-feature p {
    color: var(--app-text-2);
    font-size: 0.9rem;
    margin: 0;
}
.app-sample-frame {
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-xl);
    box-shadow: var(--app-shadow);
    padding: 1rem;
    display: block;
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
}
#app-sample-img {
    width: 100%;
    height: auto;
    /* never wider than the phone it is on - a fixed 400px used to force a horizontal scroll below that width */
    min-width: min(400px, 100%);
}
#app-howitworks svg {
    filter: drop-shadow(0 1px 2px rgba(16, 24, 40, 0.06));
}
.hero-section {
    padding: 5rem 1rem;
    text-align: center;
}
.feature-icon {
    font-size: 3rem;
    color: var(--app-accent-text);
}
.app-formats {
    display: inline-flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    margin: 1rem 0 0.75rem;
}
.app-formats > span {
    padding: 0.2rem 0.6rem;
    border-radius: 0.4rem;
    background-color: var(--app-surface-2);
    border: 1px solid var(--app-border);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--app-text-2);
}

/* ------------------------------------------------------- documentation */

.app-docs {
    max-width: 92rem;
}
.app-docs h1,
.app-docs h1.app-doc-head {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-top: 3rem;
    padding-top: 2rem;
    border-top: 1px solid var(--app-border);
}
.app-docs > div:first-child > h1 {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}
.app-docs h2,
.app-docs h2.app-doc-head {
    font-size: 1.2rem;
    margin-top: 2rem;
    margin-bottom: 0.6rem;
}
.app-docs h3,
.app-docs h3.app-doc-head {
    font-size: 1rem;
    margin-top: 1.5rem;
    margin-bottom: 0.5rem;
}
.app-docs p,
.app-docs li {
    line-height: 1.6;
}
.app-docs ul,
.app-docs ol {
    padding-left: 1.4rem;
}
.app-docs li + li {
    margin-top: 0.25rem;
}
.app-docs pre {
    margin: 0.75rem 0 1.25rem;
}
.app-docs table {
    width: 100%;
    margin: 0.75rem 0 1.5rem;
    font-size: 0.875rem;
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    overflow: hidden;
}
.app-docs table th,
.app-docs table td {
    padding: 0.55rem 0.75rem;
    border-bottom: 1px solid var(--app-border);
    vertical-align: top;
}
.app-docs table th {
    background-color: var(--app-surface-2);
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-2);
    text-align: left;
}
.app-docs table tr:last-child td {
    border-bottom: 0;
}
.app-docs .table-responsive table {
    margin: 0;
}
.app-doc-headlink {
    text-decoration: none;
    font-size: 0.5em;
    margin-right: 0.5em;
    opacity: 0.4;
    vertical-align: middle;
}
.app-doc-head:hover .app-doc-headlink {
    opacity: 1;
}
.app-docs .alert {
    margin: 2rem 0 1rem;
}
.app-docs code {
    font-size: 0.82em;
}

/* --------------------------------------------------------------- legal */

.app-legal {
    max-width: 92rem;
}
.app-legal h2 {
    font-size: 1.25rem;
    margin-top: 2.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--app-border);
}
.app-legal h3 {
    font-size: 1rem;
}
.app-legal p,
.app-legal li {
    line-height: 1.6;
}
.app-legal .card.bg-body-tertiary {
    border: 1px solid var(--app-border) !important;
}

/* -------------------------------------------------------------- hints */

/**
 * the guided hints (Tour.js): a host over the whole viewport that lets clicks through,
 * four panes that dim everything but a hole around the anchor and swallow clicks, a
 * ring on the hole, and the card. above the sticky top bar (1030) so a hint can point
 * at it, below bootstrap's modals (1055) so the sign in dialog of an expired session
 * stays on top of everything
 */
.app-tour {
    position: fixed;
    inset: 0;
    z-index: 1040;
    pointer-events: none;
}
.app-tour-pane {
    position: fixed;
    background-color: var(--app-scrim);
    pointer-events: auto;
}
.app-tour-spot {
    position: fixed;
    border-radius: var(--app-radius);
    box-shadow: 0 0 0 2px var(--app-accent), 0 0 0 6px var(--app-ring);
    animation: app-tour-pulse 1.6s ease-in-out infinite;
    pointer-events: none;
}
@keyframes app-tour-pulse {
    0%,
    100% {
        box-shadow: 0 0 0 2px var(--app-accent), 0 0 0 6px var(--app-ring);
    }
    50% {
        box-shadow: 0 0 0 2px var(--app-accent), 0 0 0 9px var(--app-ring);
    }
}
.app-tour-card {
    position: fixed;
    width: min(24rem, calc(100vw - 1.5rem));
    padding: 1rem 1.15rem 0.9rem;
    background-color: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    box-shadow: var(--app-shadow-lg);
    color: var(--app-text);
    pointer-events: auto;
    animation: app-tour-in 0.2s ease;
}
@keyframes app-tour-in {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}
.app-tour-eyebrow {
    margin: 0 0 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-accent-text);
}
.app-tour-title {
    margin: 0 0 0.4rem;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
}
.app-tour-body {
    margin: 0 0 0.9rem;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--app-text-2);
    white-space: pre-line;
}
.app-tour-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}
.app-tour-nav {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.app-tour-dismiss {
    --bs-btn-color: var(--app-text-3);
    --bs-btn-hover-color: var(--app-text);
    --bs-btn-padding-x: 0.25rem;
    margin-left: -0.25rem;
    text-decoration: none;
}
.app-tour-dismiss:hover {
    text-decoration: underline;
}

/* -------------------------------------------------------------- misc */

.app-toc-card ol {
    columns: 2;
    column-gap: 2rem;
}
@media (max-width: 575.98px) {
    .app-toc-card ol {
        columns: 1;
    }
}
details > summary {
    cursor: pointer;
    font-weight: 500;
    color: var(--app-text-2);
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
details > summary::-webkit-details-marker {
    display: none;
}
details > summary::before {
    content: "";
    width: 0.7em;
    height: 0.7em;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M6 4l4 4-4 4'/%3e%3c/svg%3e") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M6 4l4 4-4 4'/%3e%3c/svg%3e") center / contain no-repeat;
    transition: transform 0.15s ease;
}
details[open] > summary::before {
    transform: rotate(90deg);
}
details > summary:hover {
    color: var(--app-text);
}

/* a folded help note under a page header: closed it is one line, open it is a short
   read in a quiet surface (the share page's "what each permission allows") */
.app-help-fold > summary {
    font-size: 0.9rem;
}
.app-help-fold-body {
    margin-top: 0.6rem;
    padding: 0.85rem 1rem;
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    font-size: 0.9rem;
    color: var(--app-text-2);
    max-width: 72ch;
}
.app-help-fold-body .app-dl {
    margin-bottom: 0.75rem;
}
.app-help-fold-body p:last-child {
    margin-bottom: 0;
}
.text-decoration-line-through {
    opacity: 0.7;
}
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}
@media print {
    .app-topbar,
    .app-footer,
    .bd-sidebar,
    .app-page-actions,
    .app-tour {
        display: none !important;
    }
    body {
        background: #fff;
    }
}

/* the map picker of the game edit form (@see /games/games/edit.php) */
.app-map-pick {
    height: 60vh;
    min-height: 320px;
    border-radius: var(--app-radius, 0.5rem);
    background: var(--app-surface-2);
}
.app-map-pick-label {
    background: transparent;
    border: 0;
    box-shadow: none;
    color: #fff;
    font-weight: 700;
    font-size: 0.8rem;
}
.app-map-pick-label::before {
    display: none;
}

/* the picture picker of the game edit form: a thumbnail beside the select, and the
   grid of tiles in the modal it opens (@see /games/games/edit.php) */
.app-picture-pick {
    display: flex;
    align-items: stretch;
    gap: .5rem;
}
.app-picture-pick > .form-select {
    min-width: 0;
}
.app-picture-pick-thumb {
    flex: 0 0 auto;
    width: 3.5rem;
    height: 2.625rem;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm, .375rem);
    background: var(--app-surface-2);
    color: var(--app-text-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.app-picture-pick-thumb:hover,
.app-picture-pick-thumb:focus-visible {
    border-color: var(--bs-success);
    outline: none;
    box-shadow: 0 0 0 .2rem rgba(var(--bs-success-rgb), .25);
}
.app-picture-pick-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.app-picture-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
    gap: .75rem;
}
.app-picture-tile {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .35rem;
    border: 2px solid var(--app-border);
    border-radius: var(--app-radius, .5rem);
    background: var(--app-surface);
    color: var(--app-text-2);
    text-align: left;
    cursor: pointer;
}
.app-picture-tile:hover,
.app-picture-tile:focus-visible {
    border-color: var(--app-border-strong);
    outline: none;
}
.app-picture-tile.active {
    border-color: var(--bs-success);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-success-rgb), .25);
}
.app-picture-tile-img {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: calc(var(--app-radius, .5rem) - .25rem);
    background: var(--app-surface-2);
    color: var(--app-text-3);
    font-size: 1.5rem;
}
.app-picture-tile-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.app-picture-tile-label {
    font-size: .8rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* the villain's face forms: the "surprise me" row and the add-ons of an upload */
.app-avatar-surprise {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    margin-bottom: 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px dashed var(--app-border);
}
.app-avatar-more > summary {
    cursor: pointer;
}
.app-avatar-addons output {
    font-variant-numeric: tabular-nums;
    color: var(--app-text-3);
}

/* the treasure photo of the edit form with the ring the players will see
   (@see /games/games/edit.php, the final section; the play page's .chest-ring) */
.app-chest-preview {
    position: relative;
    max-width: 28rem;
    overflow: hidden;
    border-radius: var(--app-radius, .5rem);
    border: 1px solid var(--app-border);
    background: var(--app-surface-2);
}
.app-chest-preview img {
    display: block;
    width: 100%;
    height: auto;
    cursor: crosshair;
}
.app-chest-ring {
    position: absolute;
    border: 4px solid #c0392b;
    border-radius: 50%;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, .7), 0 0 14px #c0392b;
    pointer-events: none;
}

/* the uploaded face's preview on the add-ons form: the picture placed in its square,
   the add-ons' frame over it in dashes (@see /games/games/edit.php) */
.app-face-preview {
    position: relative;
    width: 16rem;
    max-width: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--app-radius, .5rem);
    border: 1px solid var(--app-border);
    /* a checkerboard: what is not picture is transparent in the face */
    background-color: var(--app-surface-2);
    background-image:
        linear-gradient(45deg, var(--app-border) 25%, transparent 25%, transparent 75%, var(--app-border) 75%),
        linear-gradient(45deg, var(--app-border) 25%, transparent 25%, transparent 75%, var(--app-border) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
}
.app-face-preview img {
    position: absolute;
    left: 50%;
    top: 50%;
    max-width: none;
    transform: translate(-50%, -50%);
}
.app-face-guide-head,
.app-face-guide-line {
    position: absolute;
    pointer-events: none;
    border: 0 dashed rgba(192, 57, 43, .9);
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .5);
}
.app-face-guide-head {
    border-width: 2px;
    border-radius: 50%;
}
.app-face-guide-line {
    height: 0;
    border-top-width: 2px;
}
