/* dashboards.rhys.wtf, in rhys.wtf's design language. The fonts, palette,
   tokens, dark scheme, header, toggle, apps mark and menu, section
   headings, footer and form controls are copied from links'
   static/main.css (from dailies', from profiles', itself from
   rhyswtf-rs/style/main.css): keep them in step. Dashboards' own parts
   are at the end. Recoleta is licensed for rhys.wtf sites only. */

/* The fonts, their faces and the --font-* tokens (Recoleta, RecoletaBold,
   Figtree, Lora-Italic, Monaspace Neon) are rhyswtf-apps' type.css,
   linked after this file: the same on every platform. */

:root {
    /* The rhys.wtf type system: Recoleta Bold for headings, Recoleta for
       reading, Figtree for the interface, Lora Italic for the logo only,
       Monaspace Neon for anything monospaced (code, tokens, addresses).
       The --font-* tokens are type.css's. */

    /* Gruvbox light palette — https://github.com/morhetz/gruvbox */
    --gruvbox-bg0-hard: #f9f5d7;
    --gruvbox-bg0: #fbf1c7;
    --gruvbox-bg0-soft: #f2e5bc;
    --gruvbox-bg1: #ebdbb2;
    --gruvbox-bg2: #d5c4a1;
    --gruvbox-bg3: #bdae93;
    --gruvbox-bg4: #a89984;

    --gruvbox-fg0: #282828;
    --gruvbox-fg1: #3c3836;
    --gruvbox-fg2: #504945;
    --gruvbox-fg3: #665c54;
    --gruvbox-fg4: #7c6f64;

    --gruvbox-gray: #928374;

    --gruvbox-red: #cc241d;
    --gruvbox-green: #98971a;
    --gruvbox-yellow: #d79921;
    --gruvbox-blue: #458588;
    --gruvbox-purple: #b16286;
    --gruvbox-aqua: #689d6a;
    --gruvbox-orange: #d65d0e;

    --gruvbox-red-faded: #9d0006;
    --gruvbox-green-faded: #79740e;
    --gruvbox-yellow-faded: #b57614;
    --gruvbox-blue-faded: #076678;
    --gruvbox-purple-faded: #8f3f71;
    --gruvbox-aqua-faded: #427b58;
    --gruvbox-orange-faded: #af3a03;

    /* Semantic tokens — change these to retheme without touching components.
       They reference only the --gruvbox-* slots, which palettes.css (loaded
       after this file) redefines for each palette but Gruvbox: keep it so. */
    --color-bg: var(--gruvbox-bg0);
    --color-surface: var(--gruvbox-bg0-hard);
    --color-border: var(--gruvbox-fg0);
    --color-text: var(--gruvbox-fg1);
    --color-text-strong: var(--gruvbox-fg3);
    --color-muted: var(--gruvbox-fg3);
    --color-link: var(--gruvbox-fg1);
    --color-link-hover: var(--gruvbox-blue-faded);
    --color-error: var(--gruvbox-red-faded);
    /* Empty part of progress bars. */
    --color-track: var(--gruvbox-bg1);
    /* Glyphs on an accent fill (app icons), the same in both schemes.
       palettes.css sets it for each palette. */
    --color-on-accent: #fbf1c7;
    /* A calendar's selected day: a darker shade of the faded yellow, with
       the surface's ink on it (Gruvbox 5.0:1, Solarized 8.1:1, Boring
       7.5:1); in dark, the plain yellow with the dark surface's ink (6.6,
       5.2, 11.3:1). Only slots, so every palette follows. */
    --color-day-selected: color-mix(in srgb, var(--gruvbox-yellow-faded), black 20%);
    --color-on-day-selected: var(--gruvbox-bg0-hard);

    --spacing: 1rem;
    --radius: 1rem;
    /* Text boxes and selects: much squarer than cards and buttons. */
    --radius-field: 0.25rem;

    /* Form controls and scrollbars follow the palette in each scheme. */
    color-scheme: light;

    /* Gruvbox dark palette, held here and applied by the two blocks below
       (once for the OS preference, once for the explicit choice) so the
       values exist in exactly one place. The plain accents are the same in
       both schemes, as in Gruvbox itself; the "faded" slots, which this
       stylesheet uses for emphasis, take the dark scheme's bright variants. */
    --gruvbox-dark-bg0-hard: #1d2021;
    --gruvbox-dark-bg0: #282828;
    --gruvbox-dark-bg0-soft: #32302f;
    --gruvbox-dark-bg1: #3c3836;
    --gruvbox-dark-bg2: #504945;
    --gruvbox-dark-bg3: #665c54;
    --gruvbox-dark-bg4: #7c6f64;
    --gruvbox-dark-fg0: #fbf1c7;
    --gruvbox-dark-fg1: #ebdbb2;
    --gruvbox-dark-fg2: #d5c4a1;
    --gruvbox-dark-fg3: #bdae93;
    --gruvbox-dark-fg4: #a89984;
    --gruvbox-dark-red-bright: #fb4934;
    --gruvbox-dark-green-bright: #b8bb26;
    --gruvbox-dark-yellow-bright: #fabd2f;
    --gruvbox-dark-blue-bright: #83a598;
    --gruvbox-dark-purple-bright: #d3869b;
    --gruvbox-dark-aqua-bright: #8ec07c;
    --gruvbox-dark-orange-bright: #fe8019;
}

/* Dark scheme. The visitor's choice (`data-theme` on <html>, set from the
   `theme` cookie by the server and by the header toggle in the browser)
   beats the OS preference in both directions: the first block applies
   the OS's dark preference unless light was chosen, the second applies a
   dark choice regardless of the OS. The two must stay identical. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;
        --gruvbox-bg0-hard: var(--gruvbox-dark-bg0-hard);
        --gruvbox-bg0: var(--gruvbox-dark-bg0);
        --gruvbox-bg0-soft: var(--gruvbox-dark-bg0-soft);
        --gruvbox-bg1: var(--gruvbox-dark-bg1);
        --gruvbox-bg2: var(--gruvbox-dark-bg2);
        --gruvbox-bg3: var(--gruvbox-dark-bg3);
        --gruvbox-bg4: var(--gruvbox-dark-bg4);
        --gruvbox-fg0: var(--gruvbox-dark-fg0);
        --gruvbox-fg1: var(--gruvbox-dark-fg1);
        --gruvbox-fg2: var(--gruvbox-dark-fg2);
        --gruvbox-fg3: var(--gruvbox-dark-fg3);
        --gruvbox-fg4: var(--gruvbox-dark-fg4);
        --gruvbox-red-faded: var(--gruvbox-dark-red-bright);
        --gruvbox-green-faded: var(--gruvbox-dark-green-bright);
        --gruvbox-yellow-faded: var(--gruvbox-dark-yellow-bright);
        --gruvbox-blue-faded: var(--gruvbox-dark-blue-bright);
        --gruvbox-purple-faded: var(--gruvbox-dark-purple-bright);
        --gruvbox-aqua-faded: var(--gruvbox-dark-aqua-bright);
        --gruvbox-orange-faded: var(--gruvbox-dark-orange-bright);
        /* The page sits on bg1 rather than bg0, so controls on the darker
           surface stand out, and all text and borders take the brightest
           foreground. */
        --color-bg: var(--gruvbox-bg1);
        --color-text: var(--gruvbox-fg0);
        --color-text-strong: var(--gruvbox-fg0);
        --color-muted: var(--gruvbox-fg0);
        --color-link: var(--gruvbox-fg0);
        --color-border: var(--gruvbox-fg0);
        --color-track: var(--gruvbox-bg2);
        --color-day-selected: var(--gruvbox-yellow);
    }
}

:root[data-theme="dark"] {
    color-scheme: dark;
    --gruvbox-bg0-hard: var(--gruvbox-dark-bg0-hard);
    --gruvbox-bg0: var(--gruvbox-dark-bg0);
    --gruvbox-bg0-soft: var(--gruvbox-dark-bg0-soft);
    --gruvbox-bg1: var(--gruvbox-dark-bg1);
    --gruvbox-bg2: var(--gruvbox-dark-bg2);
    --gruvbox-bg3: var(--gruvbox-dark-bg3);
    --gruvbox-bg4: var(--gruvbox-dark-bg4);
    --gruvbox-fg0: var(--gruvbox-dark-fg0);
    --gruvbox-fg1: var(--gruvbox-dark-fg1);
    --gruvbox-fg2: var(--gruvbox-dark-fg2);
    --gruvbox-fg3: var(--gruvbox-dark-fg3);
    --gruvbox-fg4: var(--gruvbox-dark-fg4);
    --gruvbox-red-faded: var(--gruvbox-dark-red-bright);
    --gruvbox-green-faded: var(--gruvbox-dark-green-bright);
    --gruvbox-yellow-faded: var(--gruvbox-dark-yellow-bright);
    --gruvbox-blue-faded: var(--gruvbox-dark-blue-bright);
    --gruvbox-purple-faded: var(--gruvbox-dark-purple-bright);
    --gruvbox-aqua-faded: var(--gruvbox-dark-aqua-bright);
    --gruvbox-orange-faded: var(--gruvbox-dark-orange-bright);
    /* The page sits on bg1 rather than bg0, so controls on the darker
       surface stand out, and all text and borders take the brightest
       foreground. */
    --color-bg: var(--gruvbox-bg1);
    --color-text: var(--gruvbox-fg0);
    --color-text-strong: var(--gruvbox-fg0);
    --color-muted: var(--gruvbox-fg0);
    --color-link: var(--gruvbox-fg0);
    --color-border: var(--gruvbox-fg0);
    --color-track: var(--gruvbox-bg2);
    --color-day-selected: var(--gruvbox-yellow);
}

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

/* Interface text is Figtree; prose (paragraphs, leads, descriptions) stays
   in the body's Recoleta. */
label,
input,
select,
textarea,
button,
.button,
.help,
.notice,
.error,
.badge,
.hint,
.facts,
.list,
.skip-link,
.card__meta,
.card__empty,
.day__date,
.archive__date,
.edition__meta,
.edition__nav,
.token,
.tabs,
.widget,
.facts,
.crumbs,
.filename,
.picker,
.check {
    font-family: var(--font-ui);
}

body {
    margin: 0;
    min-height: 100vh;
    /* The visible height, with a phone's toolbars shown or not. */
    min-height: 100dvh;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-body);
    line-height: 1.5;
    display: flex;
    flex-direction: column;
}

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

a:hover {
    color: var(--color-link-hover);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible {
    outline: 2px solid var(--color-link-hover);
    outline-offset: 2px;
}

strong {
    font-family: var(--font-display);
    font-weight: normal;
}

.page {
    flex: 1;
    width: 100%;
    max-width: 900px;
    margin: 0 auto;
    padding: var(--spacing);
    /* Clear of a phone's rounded corners and notch when it's on its side
       (layout.css's gutters; the viewport covers them: viewport-fit=cover). */
    padding-left: var(--safe-left);
    padding-right: var(--safe-right);
}

/* Site header, as on rhys.wtf, with this app's name beside the title. */
.site-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-left: var(--safe-left);
    padding-right: var(--safe-right);
}

.site-header__brand {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0 0.6rem;
    min-width: 0;
}

.site-header__title {
    font-family: var(--font-italic);
    font-size: 3rem;
    color: var(--color-text-strong);
    text-decoration: none;
}

.site-header__title:hover,
.site-header__app:hover {
    color: var(--color-text-strong);
    text-decoration: underline;
}

.site-header__app {
    font-family: var(--font-ui);
    font-size: 1.6rem;
    color: var(--color-muted);
    text-decoration: none;
}

/* Top right, the same in every rhys.wtf app: the apps mark, opening the
   apps menu with the light/dark switch at its foot. */
.site-header__actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* The apps mark: a rounded square per app in its Gruvbox colour, faded in
   light and bright in dark (the tokens flip). Profiles is red, feeds
   purple, dailies orange; later apps add squares. */
.apps-mark { width: 2rem; height: 2rem; display: block; }
.apps-mark__red { fill: var(--gruvbox-red-faded); }
.apps-mark__orange { fill: var(--gruvbox-orange-faded); }
.apps-mark__yellow { fill: var(--gruvbox-yellow-faded); }
.apps-mark__green { fill: var(--gruvbox-green-faded); }
.apps-mark__aqua { fill: var(--gruvbox-aqua-faded); }
.apps-mark__gray { fill: var(--gruvbox-gray); }
.apps-mark__blue { fill: var(--gruvbox-blue-faded); }
.apps-mark__purple { fill: var(--gruvbox-purple-faded); }

/* An app's icon: its favicon glyph on a square in its colour. */
.app-icon { width: 2rem; height: 2rem; flex: none; }
.app-icon__back { fill: var(--gruvbox-blue-faded); }
.app-icon--red .app-icon__back { fill: var(--gruvbox-red-faded); }
.app-icon--purple .app-icon__back { fill: var(--gruvbox-purple-faded); }
.app-icon--blue .app-icon__back { fill: var(--gruvbox-blue-faded); }
.app-icon--green .app-icon__back { fill: var(--gruvbox-green-faded); }
.app-icon--orange .app-icon__back { fill: var(--gruvbox-orange-faded); }
.app-icon--yellow .app-icon__back { fill: var(--gruvbox-yellow-faded); }
.app-icon--aqua .app-icon__back { fill: var(--gruvbox-aqua-faded); }
.app-icon--gray .app-icon__back { fill: var(--gruvbox-gray); }
/* The glyphs carry fill="#fbf1c7" for copies without this stylesheet;
   here they follow the palette. */
.app-icon g,
.app-icon text { fill: var(--color-on-accent); }
.app-icon [stroke]:not([stroke="none"]) { stroke: var(--color-on-accent); }

/* The app switcher: click the mark for a list of every app. A <details>,
   so it works without JavaScript; app.js closes it on an outside click. */
.apps-menu { position: relative; }

.apps-menu__button {
    list-style: none;
    cursor: pointer;
    padding: 0.3rem;
    border-radius: 0.6rem;
    line-height: 0;
}

.apps-menu__button::-webkit-details-marker { display: none; }
.apps-menu__button:hover,
.apps-menu[open] .apps-menu__button { background: var(--color-surface); }
.apps-menu__button:focus-visible { outline: 2px solid var(--color-link-hover); }

.apps-menu__panel {
    position: absolute;
    right: 0;
    top: calc(100% + 0.4rem);
    z-index: 20;
    min-width: 17rem;
    padding: 0.5rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.15);
}

.apps-menu__list { list-style: none; margin: 0; padding: 0; }

.apps-menu__item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.6rem;
    border-radius: 0.6rem;
    text-decoration: none;
    color: var(--color-text);
}

.apps-menu__item:hover,
.apps-menu__item:focus-visible { background: var(--color-bg); }

.apps-menu__text { display: flex; flex-direction: column; min-width: 0; }
.apps-menu__name { font-family: var(--font-ui); font-size: 1.1rem; }
.apps-menu__desc { font-family: var(--font-ui); font-size: 0.9rem; color: var(--color-muted); }

/* The menu's last row, under a rule: the light/dark switch, laid out like
   an app (glyph in a 2rem box, then its name). The moon and "Dark mode"
   show in the light scheme, the sun and "Light mode" in the dark. */
.apps-menu__rule {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: 0.5rem 0.25rem;
}

.theme-toggle {
    margin: 0;
}

.theme-toggle__button {
    width: 100%;
    background: none;
    border: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.theme-toggle__glyph {
    width: 2rem;
    height: 2rem;
    flex: none;
    display: grid;
    place-items: center;
    color: var(--color-text-strong);
}

.theme-toggle__icon,
.menu-icon {
    width: 1.5rem;
    height: 1.5rem;
    display: none;
}

.menu-icon,
.theme-toggle__icon--moon,
.theme-toggle__label--to-dark {
    display: block;
}

.theme-toggle__label--to-light {
    display: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-toggle__icon--moon,
    :root:not([data-theme="light"]) .theme-toggle__label--to-dark {
        display: none;
    }

    :root:not([data-theme="light"]) .theme-toggle__icon--sun,
    :root:not([data-theme="light"]) .theme-toggle__label--to-light {
        display: block;
    }
}

:root[data-theme="dark"] .theme-toggle__icon--moon,
:root[data-theme="dark"] .theme-toggle__label--to-dark {
    display: none;
}

:root[data-theme="dark"] .theme-toggle__icon--sun,
:root[data-theme="dark"] .theme-toggle__label--to-light {
    display: block;
}

.skip-link {
    position: absolute;
    left: -999px;
    top: 0;
    padding: 0.5rem 1rem;
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    z-index: 10;
}

.skip-link:focus {
    left: 1rem;
    top: 1rem;
}

/* Home hero, as rhys.wtf's greeting. */
.greeting {
    text-align: center;
    margin: 2rem 0 3rem;
}

.greeting__hello {
    font-family: var(--font-display);
    /* rem plus some width (vw alone wouldn't follow text zoom). */
    font-size: clamp(3rem, 1.5rem + 7.5vw, 6rem);
    font-weight: normal;
    color: var(--color-text-strong);
    margin: 0;
    line-height: 1;
}

.greeting__welcome {
    font-family: var(--font-body);
    font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
    color: var(--color-text-strong);
    margin: 0.5rem 0 0;
}

.lead {
    font-size: 1.2rem;
}

/* Section heading: title and rule. */
.section-header {
    margin-bottom: 1rem;
}

.section-header__title {
    font-family: var(--font-display);
    font-size: 2rem;
    font-weight: normal;
    color: var(--color-text-strong);
    margin: 0 0 -0.25rem 0;
}

.section-header__rule {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: 0;
}

/* Sign-in-sized pages: rhys.wtf's login column. */
.narrow {
    max-width: 24rem;
    margin: 2rem auto;
}

.narrow__title,
.page-title {
    font-family: var(--font-display);
    font-weight: normal;
    margin: 0 0 1rem 0;
}

.page-title {
    font-size: 2.5rem;
    color: var(--color-text-strong);
}

.narrow-form {
    max-width: 24rem;
}

.form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin: 0 0 1rem;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

/* Every text-like input, including those with no type attribute (which
   browsers treat as text but `[type="text"]` doesn't match). Sized to sit
   at about the height of a line of text, with only slight rounding. */
input:not([type]),
input[type="text"],
input[type="url"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="time"],
input[type="date"],
select,
textarea {
    width: 100%;
    min-width: 0;
    padding: 0.2rem 0.45rem;
    font-size: 1rem;
    line-height: 1.4;
    font-family: var(--font-ui);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-field);
    background: var(--color-surface);
    color: var(--color-text);
}

.prefixed {
    display: flex;
    align-items: stretch;
}

/* The prefix keeps its width and stays on one line ("/d/" would
   otherwise break at each slash); the input takes the rest of the row. */
.prefixed__prefix {
    flex: none;
    white-space: nowrap;
    display: flex;
    align-items: center;
    padding: 0 0.6rem;
    border: 1px solid var(--color-border);
    border-right: 0;
    border-radius: var(--radius-field) 0 0 var(--radius-field);
    background: var(--gruvbox-bg0-soft);
    color: var(--color-muted);
}

.prefixed input {
    flex: 1;
    width: auto;
    border-radius: 0 var(--radius-field) var(--radius-field) 0;
}

.help {
    font-size: 0.95rem;
    color: var(--color-muted);
    margin: 0;
}

.help:empty {
    display: none;
}

/* Buttons: rhys.wtf's outlined pills, as wide as their label. In a
   column (a form, a field) they keep to their own width at the start
   rather than stretching across it. */
.button {
    display: inline-block;
    width: auto;
    align-self: flex-start;
    justify-self: start;
    text-align: center;
    padding: 0.5rem 1rem;
    font-family: var(--font-ui);
    font-size: 1rem;
    line-height: 1.5;
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    text-decoration: none;
    cursor: pointer;
}

.button:hover {
    background: var(--gruvbox-bg0-soft);
    color: var(--color-text);
}

.button:disabled {
    opacity: 0.55;
    cursor: progress;
}

.button--small {
    padding: 0.35rem 0.75rem;
}

.button--danger {
    border-color: var(--color-error);
    color: var(--color-error);
}

.button--danger:hover {
    background: var(--color-error);
    color: var(--color-bg);
}

/* A row of buttons (Save, Cancel, Remove…): side by side, from the
   start, wrapping on a narrow screen. */
.actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    gap: 0.75rem;
}

.rule {
    margin: 1.5rem 0;
    border: none;
    border-top: 1px solid var(--color-border);
}

.hint {
    margin-top: 1.25rem;
}

details summary {
    cursor: pointer;
    text-decoration: underline;
    margin: 0.5rem 0;
}

details[open] summary {
    margin-bottom: 0.75rem;
}

/* Messages: rhys.wtf's .error box, and a neutral twin. */
.error,
/* An admin reading someone else's dashboard: whose, read-only, recorded. */
.admin-banner {
    margin: 0 0 1rem;
    padding: 0.6rem 1rem;
    border: 2px solid var(--gruvbox-orange-faded);
    border-radius: 1rem;
    background: var(--color-surface);
    color: var(--color-text);
    font-family: var(--font-ui);
    font-weight: bold;
}

.notice {
    padding: 0.5rem var(--spacing);
    border: 1px solid var(--color-error);
    border-radius: var(--radius);
    margin: 0 0 1rem;
}

/* On the surface, not the page: in the dark schemes the bright reds
   only pass AA on the darker surface (Gruvbox's #fb4934 is 3.4:1 on its
   dark page, 4.8:1 on the surface). */
.error {
    color: var(--color-error);
    background: var(--color-surface);
}

.notice {
    border-color: var(--color-border);
    background: var(--color-surface);
}

.error p,
.notice p {
    margin: 0.25rem 0;
}

.list {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
}

.list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--color-border);
}

.list__item form {
    margin: 0;
}

/* Site footer, as on rhys.wtf: the one place outside the type system's
   rules, in Recoleta like the rest of rhys.wtf's footer, links and
   sign-out button included. */
.site-footer {
    font-family: var(--font-body);
    text-align: center;
    font-size: 1.2rem;
    padding: 0 var(--safe-right) max(1rem, var(--safe-bottom)) var(--safe-left);
    width: 100%;
}

.site-footer__rule {
    border: 0;
    border-top: 1px solid var(--color-border);
    width: 30%;
}

.site-footer__links {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0 0.5rem;
}

.site-footer__links li + li::before {
    content: "⁕";
    margin-right: 0.5rem;
}

.site-footer__link {
    font-size: 1.3rem;
    text-decoration: underline;
}

.site-footer__form {
    display: inline;
    margin: 0;
}

.site-footer__button {
    font-family: var(--font-body);
    color: var(--color-link);
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.site-footer__button:hover {
    color: var(--color-link-hover);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

@media (max-width: 30rem) {
    .site-header__title {
        font-size: 2.4rem;
    }

    .site-header__app {
        font-size: 1.3rem;
    }

    .facts {
        grid-template-columns: 1fr;
    }

    .facts dd {
        margin-bottom: 0.4rem;
    }
}

/* ------------------------------------------------------------------ */
/* Dashboards' own parts. Dashboards is aqua: --gruvbox-aqua-faded.     */

.page--wide {
    max-width: 1400px;
}

.narrow--wide {
    max-width: 44rem;
}

/* Each dashboard a tab; the current one underlined in the app's colour. */
/* One row, scrolled sideways on a narrow screen if they must. */
.tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    white-space: nowrap;
    gap: 0.25rem 1rem;
    margin: 0 0 1rem;
    border-bottom: 1px solid var(--color-border);
}

.tabs::-webkit-scrollbar {
    display: none;
}

.tab {
    padding: 0.4rem 0;
    text-decoration: none;
    color: var(--color-muted);
    border-bottom: 3px solid transparent;
    margin-bottom: -1px;
}

.tab[aria-current="page"] {
    color: var(--color-text-strong);
    border-bottom-color: var(--gruvbox-aqua-faded);
}

.tab--new {
    margin-left: auto;
    padding-left: 1rem;
}

.dash-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}

.button--quiet {
    border-color: transparent;
    background: none;
}

.crumbs {
    margin: 0;
}

/* A dashboard's columns are rhyswtf-apps' layout panes (layout.css):
   side by side when they fit, swiped one or two at a time when they
   don't, opening on the middle one, with a bar of them along the bottom;
   how many are in sight follows from its --pane-min (18rem), which is
   what a side column's widgets need before they wrap. The bar's dot in
   Dashboards' colour. */
.panes {
    --pane-accent: var(--gruvbox-aqua-faded);
}

/* The edit page's columns are for arranging: as many side by side as fit
   at that same 18rem, wrapping below. */
.columns--edit {
    display: grid;
    gap: 1rem;
    align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}

/* ?debug=1: how wide the window is, small, under the title. */
.viewport-debug {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--color-muted);
    margin: -0.5rem 0 0.75rem;
}

.column {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    min-width: 0;
}

.widget {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    min-width: 0;
    overflow-wrap: anywhere;
}

.widget__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.widget__title {
    font-family: var(--font-display);
    font-weight: normal;
    font-size: 1.35rem;
    color: var(--color-text-strong);
    margin: 0;
}

.widget__kind {
    font-size: 0.8rem;
    color: var(--color-muted);
    white-space: nowrap;
}

.widget__state {
    margin: 0.25rem 0;
}

.widget__state--off {
    color: var(--color-muted);
}

.widget__state--error {
    color: var(--color-error);
}

.widget__age {
    font-size: 0.8rem;
    color: var(--color-muted);
    margin: 0.5rem 0 0;
}

.widget__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

.widget--editing {
    border-style: dashed;
}

/* The edit view's columns are drop zones (app.js): each keeps some
   height when empty, and the one a dragged widget would land in is
   outlined. */
.columns--edit .column {
    min-height: 6rem;
    padding: 0.5rem;
    border: 2px dashed transparent;
    border-radius: var(--radius);
}

.columns--edit .column--target {
    border-color: var(--color-link-hover);
    background: var(--color-track);
}

.columns--edit .widget__head {
    align-items: center;
    justify-content: flex-start;
}

.columns--edit .widget__head .widget__kind {
    margin-left: auto;
}

.columns--edit .widget__head .drag-handle {
    margin: 0 0 0 -0.4rem;
}

.widget--dragging {
    opacity: 0.6;
}

/* The edit view's "Add widget" frame at the foot of each column: faded
   (muted text, a faint dashed rule, in the palette's slots so every
   palette and scheme has it), a little brighter when pointed at or
   focused. */
.add-frame {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.85rem 1rem;
    border: 2px dashed var(--gruvbox-bg2);
    border-radius: var(--radius);
    color: var(--color-muted);
    font-family: var(--font-ui);
    text-decoration: none;
}

.add-frame:hover,
.add-frame:focus-visible {
    border-color: var(--gruvbox-bg4);
    background: var(--gruvbox-bg0-soft);
    color: var(--color-text);
}

.add-frame__plus {
    font-size: 1.25rem;
    line-height: 1;
}

/* The columns select needs room for one digit only. */
.select--digit {
    width: 5ch;
}

/* The add dialog: a card in the middle of the window, a sheet along the
   bottom of a phone's. */
.add-dialog {
    width: min(44rem, calc(100vw - 2rem));
    max-height: calc(100vh - 2rem);
    max-height: calc(100dvh - 2rem);
    padding: 1rem 1.25rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg);
    color: var(--color-text);
}

.add-dialog::backdrop {
    background: rgb(0 0 0 / 0.45);
}

.add-dialog__title {
    font-family: var(--font-display);
    font-weight: normal;
    font-size: 1.8rem;
    color: var(--color-text-strong);
    margin: 0;
}

.add-dialog__head {
    margin: 0 0 0.75rem;
}

@media (max-width: 40rem) {
    .add-dialog {
        width: 100%;
        max-width: none;
        margin: auto 0 0;
        border-radius: var(--radius) var(--radius) 0 0;
        max-height: 90vh;
        max-height: 90dvh;
        /* Clear of the home indicator and gesture bar. */
        padding-bottom: max(1rem, var(--safe-bottom));
    }
}

/* The kinds to choose from: a grid of cards, each its icon, name and a
   line about it. */
.kind-group {
    font-family: var(--font-ui);
    font-weight: normal;
    font-size: 0.95rem;
    color: var(--color-muted);
    margin: 0.75rem 0 0.4rem;
}

.kind-grid {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
    gap: 0.5rem;
}

.kind-option {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr);
    gap: 0.1rem 0.65rem;
    align-items: start;
    align-content: start;
    height: 100%;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--gruvbox-bg2);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
    text-decoration: none;
}

.kind-option:hover,
.kind-option:focus-visible {
    border-color: var(--color-border);
    background: var(--gruvbox-bg0-soft);
}

.kind-option__icon {
    grid-row: span 2;
    width: 2rem;
    height: 2rem;
}

.kind-option__name {
    font-family: var(--font-ui);
    color: var(--color-text-strong);
}

.kind-option__blurb {
    font-size: 0.85rem;
    color: var(--color-muted);
}

.widget--dragging .drag-handle {
    cursor: grabbing;
}

.inline-form {
    display: inline;
    margin: 0;
}

.icon-button {
    font: inherit;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
}

.icon-button:disabled {
    opacity: 0.35;
    cursor: default;
}

.empty {
    color: var(--color-muted);
}

/* Lists from the apps. */
.items,
.monitors,
.streams,
.metrics,
.tiles {
    list-style: none;
    margin: 0;
    padding: 0;
}

.item {
    display: flex;
    flex-direction: column;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--color-track);
}

.item:last-child,
.monitor:last-child,
.stream:last-child,
.metric:last-child {
    border-bottom: 0;
}

.item__title {
    color: var(--color-text);
}

.item--unread .item__title {
    font-family: var(--font-display);
}

.item__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 0.5rem;
    font-size: 0.85rem;
    color: var(--color-muted);
}

.item__dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--gruvbox-aqua-faded);
}

.item__summary {
    font-family: var(--font-body);
    font-size: 0.95rem;
    color: var(--color-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* State badges: up, down, warning, unknown and live. */
.badge {
    display: inline-block;
    font-size: 0.8rem;
    padding: 0 0.5rem;
    border-radius: 1rem;
    border: 1px solid currentColor;
    white-space: nowrap;
}

/* The faded accents drawn a little toward the scheme's strongest ink, so
   a badge's small text is at AA on the widget in every palette and both
   schemes (Gruvbox light's green and yellow alone are 4.4 and 3.4:1). */
.badge--up { color: color-mix(in srgb, var(--gruvbox-green-faded) 80%, var(--gruvbox-fg0)); }
.badge--down { color: color-mix(in srgb, var(--gruvbox-red-faded) 85%, var(--gruvbox-fg0)); }
.badge--warn { color: color-mix(in srgb, var(--gruvbox-yellow-faded) 70%, var(--gruvbox-fg0)); }
.badge--unknown { color: var(--color-muted); }
.badge--live {
    color: var(--color-on-accent);
    background: var(--gruvbox-red-faded);
    border-color: var(--gruvbox-red-faded);
}

/* Monitors. */
.monitor,
.stream {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--color-track);
}

.monitor__head,
.stream__head,
.host__head,
.pool__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
}

.monitor__name,
.stream__name,
.host__name,
.pool__name {
    flex: 1;
    min-width: 0;
    color: var(--color-text);
}

.icon {
    width: 1.2rem;
    height: 1.2rem;
    display: inline-block;
    vertical-align: -0.2rem;
    color: var(--color-muted);
}

.monitor__figures {
    display: flex;
    flex-wrap: wrap;
    gap: 0 0.75rem;
    font-size: 0.85rem;
    color: var(--color-muted);
}

.sparkline {
    display: block;
    width: 100%;
    height: 1.5rem;
    margin-top: 0.25rem;
}

/* A monitor's state: a tick, a cross, a ! or a dash, each its own shape
   (and labelled), coloured as the badges. */
.status {
    width: 1rem;
    height: 1rem;
    flex: none;
    display: inline-block;
    vertical-align: -0.15rem;
}

.status--up { color: var(--gruvbox-green-faded); }
.status--down { color: var(--gruvbox-red-faded); }
.status--warn { color: var(--gruvbox-yellow-faded); }
.status--unknown { color: var(--color-muted); }

/* Compact monitors: one line each, the sparkline in it. */
.monitors--compact .monitor {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0;
    white-space: nowrap;
}

.monitors--compact .monitor__icon {
    flex: none;
    display: inline-flex;
    width: 1.2rem;
}

.monitors--compact .monitor__name {
    overflow: hidden;
    text-overflow: ellipsis;
}

.monitor__ms,
.monitor__uptime {
    flex: none;
    font-size: 0.85rem;
    color: var(--color-muted);
    font-variant-numeric: tabular-nums;
}

.monitor__ms {
    min-width: 3.25rem;
    text-align: right;
}

.sparkline.sparkline--inline {
    flex: none;
    width: 72px;
    height: 1rem;
    margin-top: 0;
}

/* Large monitors, as Glance's: the icon the full height of the row (a
   service's own, bare and in its colours; a drawn one on a coloured tile,
   the plain accents every palette defines for both schemes), and beside
   it one column of the name, the figures and the sparkline. The tile is
   sized to that column: a line of name (1.5rem), one of figures (0.85rem
   type, about 1.3rem) and the sparkline (1.5rem and its 0.25rem gap). */
.monitors--large {
    --monitor-tile: 2.8rem;
}

.monitors--large.monitors--sparks {
    --monitor-tile: 4.5rem;
}

.monitor__row {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
}

.monitor__tile {
    flex: none;
    width: var(--monitor-tile);
    height: var(--monitor-tile);
}

.monitor__main {
    flex: 1;
    min-width: 0;
}

.icon-tile {
    display: block;
    width: 100%;
    height: 100%;
    color: var(--color-on-accent);
}

.icon-tile__back { fill: var(--gruvbox-gray); }
.icon-tile--red .icon-tile__back { fill: var(--gruvbox-red); }
.icon-tile--orange .icon-tile__back { fill: var(--gruvbox-orange); }
.icon-tile--yellow .icon-tile__back { fill: var(--gruvbox-yellow); }
.icon-tile--green .icon-tile__back { fill: var(--gruvbox-green); }
.icon-tile--aqua .icon-tile__back { fill: var(--gruvbox-aqua); }
.icon-tile--blue .icon-tile__back { fill: var(--gruvbox-blue); }
.icon-tile--purple .icon-tile__back { fill: var(--gruvbox-purple); }
.icon-tile--gray .icon-tile__back { fill: var(--gruvbox-gray); }

/* A service's own icon, small or large: the picture fills its box. One
   with a variant for a scheme has both, and shows the scheme's (the
   visitor's choice beating the OS's, as everywhere). */
.icon--real img,
.icon-tile--real img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.icon-tile--real {
    padding: 0.15rem;
    box-sizing: border-box;
}

.icon--real .svc--dark,
.icon-tile--real .svc--dark { display: none; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .svc--light { display: none; }
    :root:not([data-theme="light"]) .svc--dark { display: block; }
}

:root[data-theme="dark"] .svc--light { display: none; }
:root[data-theme="dark"] .svc--dark { display: block; }

.sparkline__line {
    stroke: var(--gruvbox-aqua-faded);
    stroke-width: 1.5;
    vector-effect: non-scaling-stroke;
}

.sparkline__down {
    fill: var(--gruvbox-red-faded);
}

/* Streams, laid out as Glance's: avatar, then name and the live mark,
   title, category, and the description in two lines at most. */
.stream {
    display: flex;
    gap: 0.6rem;
    align-items: flex-start;
}

.stream__avatar {
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    object-fit: cover;
    background: var(--color-track);
}

.stream__avatar--none {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-display);
    color: var(--color-muted);
}

.stream__body {
    flex: 1;
    min-width: 0;
}

.stream__head .stream__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.stream__live {
    color: var(--gruvbox-red-faded);
    font-size: 0.85rem;
    white-space: nowrap;
}

.stream__when,
.stream__meta,
.stream__error {
    font-size: 0.85rem;
    color: var(--color-muted);
}

.stream__when {
    white-space: nowrap;
}

.stream__title {
    margin: 0.15rem 0 0;
    font-family: var(--font-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.stream__meta,
.stream__error {
    margin: 0;
}

.stream__bio {
    margin: 0.15rem 0 0;
    font-family: var(--font-body);
    font-size: 0.85rem;
    color: var(--color-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.stream--offline .stream__name,
.stream--scheduled .stream__name,
.stream--pending .stream__name,
.stream--missing .stream__name,
.stream--error .stream__name {
    color: var(--color-muted);
}

.streams--trouble .stream__avatar {
    width: 1.75rem;
    height: 1.75rem;
}

.streams__offline,
.peers__offline {
    margin-top: 0.5rem;
}

.streams__offline > summary,
.peers__offline > summary {
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--color-muted);
}

.streams__offline > summary:hover,
.peers__offline > summary:hover {
    color: var(--color-text);
}

/* Servers and pools. */
.hosts,
.pools {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.host + .host,
.pool + .pool {
    border-top: 1px solid var(--color-track);
    padding-top: 0.75rem;
}

.host--offline .gauges,
.pools--offline {
    opacity: 0.6;
}

.host__platform {
    font-size: 0.8rem;
    color: var(--color-muted);
}

.gauges {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.3rem 0.75rem;
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
}

.gauges dt {
    color: var(--color-muted);
}

.gauges dd {
    margin: 0;
}

.gauge__text {
    display: block;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.bar {
    display: block;
    width: 100%;
    height: 0.4rem;
    margin: 0.3rem 0 0.1rem;
}

.bar__track { fill: var(--color-track); }
.bar--ok .bar__fill { fill: var(--gruvbox-aqua-faded); }
.bar--warn .bar__fill { fill: var(--gruvbox-yellow-faded); }
.bar--high .bar__fill { fill: var(--gruvbox-red-faded); }

.pool__space,
.pool__scan,
.pool__status {
    margin: 0.2rem 0 0;
    font-size: 0.85rem;
    color: var(--color-muted);
}

.pool__status {
    color: var(--color-error);
}

.pool__devices {
    margin: 0.2rem 0 0;
    padding-left: 1.2rem;
    font-size: 0.85rem;
    color: var(--color-error);
}

/* Containers, DNS, the tailnet and time: a machine's heading, its
   figures, and lists of rows that each start with a state's dot. */
h3.host__name {
    font: inherit;
    margin: 0;
}

.host__summary {
    font-size: 0.85rem;
    color: var(--color-muted);
    font-variant-numeric: tabular-nums;
}

.host__summary--big {
    flex: 1;
    font-size: 1rem;
    color: var(--color-text);
}

.host__from {
    font-weight: normal;
    color: var(--color-text);
}

.host--offline .host__body {
    opacity: 0.6;
}

.host__body > .help {
    margin: 0.3rem 0 0;
    font-size: 0.8rem;
}

.host__why {
    color: var(--color-error);
}

.dot {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    flex: none;
    align-self: center;
    background: var(--color-muted);
}

.dot--up { background: var(--gruvbox-green-faded); }
.dot--down { background: var(--gruvbox-red-faded); }
.dot--warn { background: var(--gruvbox-yellow-faded); }
/* Stopped, offline: a hollow ring, so it isn't told by colour alone. */
.dot--off {
    background: transparent;
    box-shadow: inset 0 0 0 1.5px var(--color-muted);
}

.ctrs,
.peers,
.probes,
.addrs,
.warnings {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ctrs,
.peers,
.probes {
    margin-top: 0.4rem;
    font-size: 0.85rem;
}

/* A row: the dot, the name (wrapping when it must), what's beside it on
   the right; its addresses below the name. */
.ctr,
.peer,
.probe {
    display: grid;
    grid-template-columns: 0.55rem minmax(0, 1fr) auto;
    column-gap: 0.5rem;
    align-items: baseline;
    padding: 0.15rem 0;
}

.ctr__name,
.peer__name,
.probe__name {
    color: var(--color-text);
    min-width: 0;
}

.ctr--stopped .ctr__name,
.peer--offline .peer__name {
    color: var(--color-muted);
}

.ctr__meta,
.peer__meta,
.probe__ms {
    font-size: 0.8rem;
    color: var(--color-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* A container's figures (disk, memory, uptime) can outgrow a narrow
   column: the name keeps up to 60% of the row, and the figures wrap
   between parts, never inside one. */
.ctr {
    grid-template-columns: 0.55rem fit-content(60%) minmax(0, 1fr);
}

.ctr__name {
    overflow-wrap: anywhere;
}

.ctr__part {
    white-space: nowrap;
}

/* With the figures on two lines, the dot stays by the name's line. */
.ctr > .dot {
    align-self: baseline;
}

.ctr__stale {
    font-size: 0.8rem;
    margin-top: 0.3rem;
}

/* Software versions: a row a piece of software, its name (linked to where
   it's served) and its container, muted, on the left; the installed
   version in Monaspace on the right. One older than its newest release is
   in the house error colour, with that release beside it; one up to date
   has a quiet tick; one that can't be compared is muted, its title saying
   why. A long name or version wraps rather than widening the column. */
.sw__head {
    margin: 0;
}

.sws {
    list-style: none;
    margin: 0.4rem 0 0;
    padding: 0;
    font-size: 0.85rem;
}

.sw {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    column-gap: 0.75rem;
    padding: 0.15rem 0;
}

.sw__name {
    min-width: 0;
    color: var(--color-text);
    overflow-wrap: anywhere;
}

.sw__name a {
    color: inherit;
    text-decoration-color: var(--color-track);
    text-underline-offset: 0.15em;
}

.sw__name a:hover {
    color: var(--color-link-hover);
    text-decoration-color: currentColor;
}

.sw__ctr {
    font-size: 0.8rem;
    color: var(--color-muted);
}

.sw__vers {
    margin-left: auto;
    max-width: 100%;
    text-align: right;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.sw__installed {
    color: var(--color-text);
}

.sw--old .sw__installed {
    color: var(--color-error);
}

.sw--unknown .sw__installed {
    color: var(--color-muted);
}

.sw__arrow,
.sw__ok {
    color: var(--color-muted);
}

.sw__ok {
    font-family: var(--font-ui);
    font-size: 0.75rem;
}

.sw__latest {
    color: var(--color-text);
    text-decoration-color: var(--color-track);
    text-underline-offset: 0.15em;
}

.sw__latest:hover {
    color: var(--color-link-hover);
    text-decoration-color: currentColor;
}

.sw-all > .help {
    margin: 0.3rem 0 0;
    font-size: 0.8rem;
}

.peer__os {
    font-size: 0.8rem;
    color: var(--color-muted);
}

/* The node the report came from, in the list with its peers. */
.peer__me {
    font-size: 0.75rem;
    color: var(--color-muted);
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
    padding: 0 0.35rem;
    white-space: nowrap;
}

.probe__why,
.probe__state {
    grid-column: 2 / -1;
    font-size: 0.8rem;
    color: var(--color-error);
}

.probe__state {
    color: var(--color-muted);
}

.addrs {
    grid-column: 2 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: 0 0.9rem;
}

.addr {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--color-text);
    overflow-wrap: normal;
}

.addr--tailnet {
    color: var(--color-muted);
}

.addr__tag {
    font-size: 0.7rem;
    color: var(--color-muted);
    letter-spacing: 0.02em;
}

.figures {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.15rem 0.75rem;
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
}

.figures dt {
    color: var(--color-muted);
}

.figures dd {
    margin: 0;
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

.figures__aside {
    color: var(--color-muted);
}

.warnings {
    margin-top: 0.4rem;
    font-size: 0.85rem;
    color: var(--color-error);
}

.peers__title {
    margin: 0.6rem 0 0;
    font-family: var(--font-ui);
    font-weight: normal;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.sources {
    margin-top: 0.4rem;
}

.sources > summary {
    font-size: 0.8rem;
    color: var(--color-muted);
}

/* A monitor widget's incidents: a line each, its mark first. */
.incidents {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.9rem;
}

.incident {
    padding: 0.2rem 0;
    border-bottom: 1px solid var(--color-border);
}

.incident__when {
    color: var(--color-muted);
    font-variant-numeric: tabular-nums;
}

/* Watch: a group's name above its checks, in the fold. */
.watch__group {
    margin: 0.5rem 0 0;
    font-size: 0.75rem;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Metrics: a list, or big numbers. */
.metrics__title {
    margin: 0 0 0.25rem;
    color: var(--color-muted);
    font-size: 0.85rem;
}

/* A list's rows are set apart by space alone, no rules between them. */
.metric {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.3rem 0;
}

.metric__label {
    color: var(--color-text);
}

/* Items set in under the one above (a list's `indent`). */
.metric--indent-1 { padding-left: 1rem; }
.metric--indent-2 { padding-left: 2rem; }
.metric--indent-3 { padding-left: 3rem; }

.metric__value {
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.metric__delta {
    font-size: 0.8rem;
}

.metric__delta--up { color: var(--gruvbox-green-faded); }
.metric__delta--down { color: var(--gruvbox-red-faded); }
.metric__delta--flat { color: var(--color-muted); }

.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    gap: 0.5rem;
}

.tile {
    display: flex;
    flex-direction: column;
    padding: 0.5rem;
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
}

.tile__value {
    font-family: var(--font-display);
    font-size: 1.8rem;
    line-height: 1.1;
    color: var(--color-text-strong);
    font-variant-numeric: tabular-nums;
}

.tile__unit {
    font-family: var(--font-ui);
    font-size: 0.9rem;
}

/* Settings pages. */
.panel {
    margin: 2rem 0;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
}

.panel__title {
    font-family: var(--font-display);
    font-weight: normal;
    font-size: 1.8rem;
    color: var(--color-text-strong);
    margin: 0 0 0.5rem;
}

.panel__subtitle {
    font-family: var(--font-display);
    font-weight: normal;
    font-size: 1.2rem;
    color: var(--color-text-strong);
    margin: 1.25rem 0 0.4rem;
}

.field-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: 0.75rem;
}

.field-row > .field {
    min-width: 0;
}

fieldset.field {
    border: 0;
    padding: 0;
    margin: 0;
}

fieldset.field legend {
    padding: 0;
    margin-bottom: 0.25rem;
}

.picker__list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 16rem;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: 0.6rem;
    padding: 0.4rem 0.6rem;
    background: var(--color-surface);
}

.check {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.target {
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
    padding: 0 0.75rem;
    margin: 0 0 0.5rem;
}

.target > summary {
    text-decoration: none;
}

.danger-zone {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 1.5rem;
}

pre {
    background: var(--color-surface);
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
    padding: 0.6rem 0.75rem;
    overflow-x: auto;
    font-size: 0.85rem;
    line-height: 1.4;
}

code {
    font-family: var(--font-mono);
    font-size: 0.9em;
}

.filename {
    margin: 0.75rem 0 -0.5rem;
}

.token {
    width: 100%;
    font-family: var(--font-mono);
}

.notice--token {
    border-color: var(--gruvbox-aqua-faded);
}

.facts {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.25rem 1rem;
}

.facts dd {
    margin: 0;
}

.help-steps ol {
    padding-left: 1.4rem;
}

/* The icon picker: each icon as it shows, with its name, to choose. */
.icon-picker__group {
    margin: 0.5rem 0 0.25rem;
    font-family: var(--font-ui);
    font-size: 0.85rem;
    color: var(--color-muted);
}

.icon-picker__choices {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: 0.25rem;
}

.icon-choice {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.4rem;
    border: 1px solid transparent;
    border-radius: var(--radius-field);
    font-size: 0.9rem;
    cursor: pointer;
    min-width: 0;
}

.icon-choice:hover {
    background: var(--gruvbox-bg0-soft);
}

.icon-choice:has(input:checked) {
    border-color: var(--color-border);
    background: var(--color-surface);
}

.icon-choice input {
    margin: 0;
    flex: none;
}

.icon-choice .icon {
    flex: none;
}

.icon-choice__none {
    width: 1.2rem;
    height: 1.2rem;
    flex: none;
}

.icon-choice__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A monitor widget's targets (and a machine widget's machines), in
   order: a handle to drag (shown by the WASM), the target, and buttons a
   place up or down. */
.targets__tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    margin: 0 0 0.75rem;
}

.targets {
    list-style: none;
    margin: 0;
    padding: 0;
}

.targets__item {
    margin: 0 0 0.5rem;
}

.targets__bar {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
}

.targets__bar > .target {
    flex: 1;
    min-width: 0;
    margin: 0;
}

/* A machine, in a machine widget's order: its name in a box like a
   target's, and whether it's hidden. */
.targets__bar > .targets__name {
    flex: 1;
    min-width: 0;
    margin: 0;
    padding: 0.55rem 0.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
    overflow-wrap: anywhere;
}

.targets__moves {
    flex: none;
    display: flex;
    gap: 0.25rem;
    padding-top: 0.35rem;
}

.drag-handle {
    flex: none;
    width: 1.75rem;
    height: 2.2rem;
    margin-top: 0.2rem;
    padding: 0.2rem;
    border: 0;
    border-radius: var(--radius-field);
    background: none;
    color: var(--color-muted);
    cursor: grab;
    touch-action: none;
}

.drag-handle[hidden] {
    display: none;
}

.drag-handle svg {
    display: block;
    width: 100%;
    height: 100%;
}

.drag-handle:hover {
    background: var(--gruvbox-bg0-soft);
}

.targets__item--dragging {
    opacity: 0.6;
}

.targets__item--dragging .drag-handle {
    cursor: grabbing;
}

.actions > .button {
    align-self: center;
}

/* Calendar: a month's grid, narrow enough for an outer column. Today is
   a disc of the palette's plain blue (a fill every palette defines for
   both schemes) in the on-accent ink; the days of the months beside it
   are faded, and weekends a little. */
.cal {
    font-family: var(--font-ui);
    max-width: 22rem;
    margin: 0 auto;
}

.cal__head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.4rem;
}

.cal__month {
    margin: 0;
    margin-right: auto;
    font-weight: 600;
    color: var(--color-text-strong);
}

.cal__nav,
.cal__today {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.75rem;
    border: 1px solid var(--color-border);
    border-radius: 1rem;
    background: var(--color-surface);
    color: var(--color-text);
    text-decoration: none;
    line-height: 1;
}

.cal__nav {
    width: 1.75rem;
    font-size: 1.1rem;
}

.cal__today {
    padding: 0 0.6rem;
    font-size: 0.8rem;
}

a.cal__nav:hover,
.cal__today:hover {
    background: var(--gruvbox-bg0-soft);
    color: var(--color-link-hover);
}

.cal__nav--off {
    opacity: 0.35;
}

.cal__grid {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.cal__grid th {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--color-muted);
    padding: 0 0 0.15rem;
}

.cal__grid abbr {
    text-decoration: none;
}

.cal__grid th.cal__wk {
    width: 2rem;
    font-weight: normal;
    font-size: 0.7rem;
    opacity: 0.75;
}

.cal__grid tbody th.cal__wk {
    border-right: 1px solid var(--color-track);
}

.cal__day {
    /* Room under the number for the day's marks. */
    padding: 0 0 0.4rem;
    position: relative;
    border-radius: 0.5rem;
}

.cal__date {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.55rem;
    height: 1.55rem;
    border-radius: 50%;
    font-size: 0.85rem;
}

.cal__day--weekend .cal__date {
    opacity: 0.7;
}

.cal__day--out .cal__date {
    opacity: 0.35;
}

.cal__day--today .cal__date {
    opacity: 1;
    background: var(--gruvbox-blue);
    color: var(--color-on-accent);
    font-weight: 700;
}

/* A day with something on: a mark an event, three at most, then "+".
   Each is an SVG circle filled with its calendar's colour (a
   presentation attribute: the CSP allows no inline styles), ringed in
   the page's ink so a pale colour on a pale page (or a dark one on a
   dark page, or yellow on the selected day) still shows; one whose
   calendar has no colour takes the palette's orange. */
.cal__dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0.05rem;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.15rem;
    line-height: 1;
}

.cal__dot {
    width: 0.5rem;
    height: 0.5rem;
    display: block;
}

.cal__dot circle {
    stroke: var(--color-text);
    stroke-width: 1.5;
}

.cal__day[aria-selected="true"] .cal__dot circle {
    stroke: var(--color-on-day-selected);
}

.cal__dot--plain circle,
.swatch--plain circle {
    fill: var(--gruvbox-orange);
}

.cal__more {
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--color-muted);
}

/* Adding an event: the owner's days take a right-click (or a long-press,
   or the Menu key once focused; one day is in the tab order and the
   arrow keys move between them), and a small link under the grid goes to
   the same form. */
.cal__day[data-date] {
    cursor: pointer;
    /* A long-press is the menu, not a selection of the date. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

.cal__day[data-date]:hover {
    background: var(--gruvbox-bg0-soft);
}

/* A day with events: its number links to them. */
.cal__link {
    color: inherit;
    text-decoration: none;
}

/* The selected day (one a widget, chosen in the browser): the darker
   yellow, in its own ink, at full strength even beside the month or at
   the weekend. Today keeps its blue disc, ringed in that ink. */
.cal__day[aria-selected="true"],
.cal__day[aria-selected="true"]:hover {
    background: var(--color-day-selected);
    color: var(--color-on-day-selected);
}

.cal__day[aria-selected="true"] .cal__date {
    opacity: 1;
    font-weight: 700;
}

.cal__day[aria-selected="true"] .cal__more {
    color: var(--color-on-day-selected);
}

.cal__day--today[aria-selected="true"] .cal__date {
    box-shadow: 0 0 0 2px var(--color-on-day-selected);
}

.cal__day[data-date]:focus-visible {
    outline: 2px solid var(--color-link-hover);
    outline-offset: -2px;
    border-radius: 0.4rem;
}

.cal__add {
    margin: 0.3rem 0 0;
    font-size: 0.8rem;
    text-align: right;
}

.cal__notice {
    margin: 0.4rem 0 0;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.cal__notice--alert {
    color: var(--color-error);
}

/* The form. Its times are always there without script; the script hides
   them (their `hidden` attribute) while it's all day. */
.event-form [hidden] {
    display: none;
}

.event-form__when .check {
    margin-right: 1rem;
}

.event-form__note {
    margin: 0 0 0.75rem;
}

.event-form__notes {
    white-space: pre-line;
}

.event-form__delete {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--color-track);
}

/* A day's events: each a row to open, its calendar's colour, its time,
   its title and its calendar. */
.add-dialog:has(.day-list) {
    width: min(28rem, calc(100vw - 2rem));
}

.day-list__events {
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
    display: grid;
    gap: 0.4rem;
}

.day-list__event {
    display: grid;
    grid-template-columns: 0.8rem 6.5rem minmax(0, 1fr);
    grid-template-areas:
        "swatch time title"
        ". . calendar";
    gap: 0.1rem 0.5rem;
    align-items: baseline;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
    font-family: var(--font-ui);
    color: var(--color-text);
    text-decoration: none;
}

.day-list__event:hover,
.day-list__event:focus-visible {
    background: var(--gruvbox-bg0-soft);
    border-color: var(--color-border);
}

.day-list__event .swatch {
    grid-area: swatch;
    width: 0.8rem;
    height: 0.8rem;
}

.day-list__time {
    grid-area: time;
    color: var(--color-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.day-list__title {
    grid-area: title;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.day-list__cal {
    grid-area: calendar;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.day-list__add {
    margin: 0 0 0.5rem;
    font-family: var(--font-ui);
}

/* A calendar that couldn't be fetched: a line under the grid, never the
   dashboard's problem. */
.cal__problems {
    list-style: none;
    margin: 0.4rem 0 0;
    padding: 0;
    font-size: 0.8rem;
    color: var(--color-error);
}

/* The Upcoming list: day, time, then the title with its calendar's
   swatch. Interface text, in the palette's own colours. */
.cal__upcoming {
    margin-top: 0.6rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--color-track);
}

.cal__upcoming-title {
    margin: 0 0 0.3rem;
    font-family: var(--font-ui);
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-muted);
}

.cal__events {
    list-style: none;
    margin: 0;
    padding: 0;
}

.cal__event {
    display: grid;
    grid-template-columns: 5.5rem 4.5rem minmax(0, 1fr);
    gap: 0 0.4rem;
    align-items: baseline;
    padding: 0.15rem 0;
    font-size: 0.85rem;
}

.cal__event-day,
.cal__event-time {
    color: var(--color-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.cal__event-title {
    color: var(--color-text);
    overflow-wrap: anywhere;
}

.cal__none {
    margin: 0;
    font-size: 0.85rem;
    color: var(--color-muted);
}

/* A calendar's colour, small. */
.swatch {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    vertical-align: baseline;
}

.cal-source {
    border: 1px solid var(--color-track);
    border-radius: 0.6rem;
    padding: 0.6rem 0.75rem;
    margin: 0 0 0.75rem;
}

.cal-source__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Weather (kinds::weather): the current conditions big, then the hours
   as a strip that wraps to the widget's width, then the days as rows.
   Its icons are drawn here (inline SVG, coloured by these classes from
   the palette's slots, so every palette and both schemes have them): the
   sun yellow, clouds in the surface outlined in the ink (overcast's back
   one grey), rain blue, snow and hail in the ink, thunder orange, fog
   muted. */
.wx {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    container-type: inline-size;
}

.wx-icon {
    display: block;
    flex: none;
    overflow: visible;
}

.wx-icon--large {
    width: 4rem;
    height: 4rem;
}

.wx-icon--small {
    width: 2rem;
    height: 2rem;
}

.wx-icon path,
.wx-icon circle {
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.wx-sun { fill: var(--gruvbox-yellow); stroke: var(--gruvbox-yellow-faded); }
.wx-icon .wx-ray { fill: none; stroke: var(--gruvbox-yellow-faded); stroke-width: 1.8; }
.wx-moon { fill: var(--gruvbox-bg2); stroke: var(--color-text); }
.wx-cloud { fill: var(--color-surface); stroke: var(--color-text); }
.wx-cloud--back { fill: var(--gruvbox-gray); }
.wx-cloud--unknown { fill: none; stroke: var(--color-muted); stroke-dasharray: 2 2.5; }
.wx-icon .wx-rain { fill: none; stroke: var(--gruvbox-blue-faded); stroke-width: 2; }
.wx-drop { fill: var(--gruvbox-blue-faded); stroke: none; }
.wx-icon .wx-snow { fill: none; stroke: var(--color-text); stroke-width: 1.4; }
.wx-icon .wx-hail { fill: var(--color-surface); stroke: var(--color-text); stroke-width: 1.3; }
.wx-icon .wx-bolt { fill: var(--gruvbox-orange); stroke: var(--gruvbox-orange-faded); stroke-width: 1.2; }
.wx-icon .wx-fog { fill: none; stroke: var(--color-muted); stroke-width: 2; }

.wx-now {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 0.5rem 0.75rem;
}

.wx-now__main {
    min-width: 0;
}

.wx-now__temp {
    margin: 0;
    font-family: var(--font-display);
    font-size: 2.6rem;
    line-height: 1;
    color: var(--color-text-strong);
    font-variant-numeric: tabular-nums;
}

.wx-now__unit {
    margin-left: 0.1rem;
    font-family: var(--font-ui);
    font-size: 1rem;
    vertical-align: top;
}

.wx-now__desc {
    margin: 0.3rem 0 0;
}

.wx-now__facts {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 1rem;
    margin: 0;
    font-size: 0.9rem;
}

.wx-now__facts > div {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.wx-now__facts dt {
    color: var(--color-muted);
}

.wx-now__facts dd {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0;
    font-variant-numeric: tabular-nums;
}

/* Wide enough: the facts beside the temperature, one a line. */
@container (min-width: 26rem) {
    .wx-now {
        grid-template-columns: auto auto minmax(0, 1fr);
    }

    .wx-now__facts {
        grid-column: auto;
        flex-direction: column;
        align-items: flex-end;
    }
}

.wx-arrow {
    width: 0.9rem;
    height: 0.9rem;
    fill: currentColor;
}

/* The hours and the days: one row each, as many equal columns as there
   are items (six hours, five days). */
.wx-hours {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.25rem;
    list-style: none;
    margin: 0;
    padding: 0.4rem 0 0;
    border-top: 1px solid var(--color-track);
}

.wx-hour {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    font-variant-numeric: tabular-nums;
}

.wx-hour__time,
.wx-hour__rain {
    font-size: 0.75rem;
    color: var(--color-muted);
}

.wx-days {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.25rem;
    list-style: none;
    margin: 0;
    padding: 0.4rem 0 0;
    border-top: 1px solid var(--color-track);
}

.wx-day {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    font-variant-numeric: tabular-nums;
    min-width: 0;
}

.wx-day__name {
    font-size: 0.75rem;
    color: var(--color-muted);
}

.wx-day__temps {
    white-space: nowrap;
}

.wx-day__low,
.wx-day__sep,
.wx-day__rain {
    color: var(--color-muted);
}

.wx-day__low,
.wx-day__sep {
    font-size: 0.85em;
}

.wx-day__sep {
    margin: 0 0.1em;
}

.wx-day__rain {
    font-size: 0.75rem;
}

.wx__note {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: var(--color-muted);
}

/* The settings form's latitude and longitude, side by side. */
.weather-place {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 0.75rem;
}

.weather-key--refused {
    color: var(--color-error);
}

/* UPSes: each a name and its state's pill, gauges for the charge and the
   load, the figures, and a day's sparklines side by side (stacked on a
   phone). */
.upses {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.ups + .ups {
    border-top: 1px dashed var(--color-track);
    padding-top: 0.6rem;
}

.ups__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
    margin-top: 0.3rem;
}

h4.ups__name {
    flex: 1;
    min-width: 0;
    margin: 0;
    font: inherit;
    font-size: 0.95rem;
    color: var(--color-text);
    overflow-wrap: anywhere;
}

.ups__trends {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.25rem 0.75rem;
    margin-top: 0.5rem;
}

.ups__trend-label {
    display: block;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.ups__spark {
    margin-top: 0.15rem;
}

/* The day's frame, so a flat line (a full battery) reads as a level, not
   a rule. */
.ups__spark-back {
    fill: color-mix(in srgb, var(--color-track) 45%, transparent);
}

@media (max-width: 30rem) {
    .ups__trends {
        grid-template-columns: minmax(0, 1fr);
    }
}
