/*
 * The public campaign page.
 *
 * Two rules shape all of it. Numbers are the content, so every figure is set in a tabular monospace and every
 * column of them is right-aligned -- a column of counts that does not line up is harder to read than no column
 * at all. And the palette is defined once as custom properties on :root, with a dark variant that redefines
 * only the properties, so nothing has a colour that exists in only one theme.
 */

:root {
    color-scheme: light dark;

    --ink: #1b1f24;
    --ink-soft: #57606a;
    --ink-faint: #8c959f;
    --page: #f6f7f9;
    --panel: #ffffff;
    --rule: #d8dee4;
    --rule-soft: #eaeef2;
    --accent: #1f6feb;
    --accent-soft: #ddeafc;
    --good: #1a7f37;
    --warn: #9a6700;
    --bad: #cf222e;
    --track: #e6eaef;

    --measured: #1f6feb;
    --prior: #6e40c9;
    --fitted: #9a6700;

    /* Progress is green everywhere. The weight SOURCE is carried by the chip in the class table instead of by
       the bar's colour: a bar that changed hue per class made the one thing people read -- how far along it is
       -- the hardest thing to compare across rows. */
    --done: #2da44e;
    --done-soft: #46954a;

    --radius: 10px;
    --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

@media (prefers-color-scheme: dark) {
    :root {
        --ink: #e6edf3;
        --ink-soft: #9198a1;
        --ink-faint: #6e7681;
        --page: #0d1117;
        --panel: #161b22;
        --rule: #30363d;
        --rule-soft: #21262d;
        --accent: #4493f8;
        --accent-soft: #16304d;
        --good: #3fb950;
        --warn: #d29922;
        --bad: #f85149;
        --track: #21262d;

        --measured: #4493f8;
        --prior: #a371f7;
        --fitted: #d29922;
        --done: #3fb950;
        --done-soft: #2ea043;
    }
}

* {
    box-sizing: border-box;
}

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

.shell {
    max-width: 1080px;
    margin: 0 auto;
    padding: 2.5rem 1.25rem 4rem;
}

/* ---------------------------------------------------------------------- masthead */

.masthead {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 1.75rem;
}

h1 {
    margin: 0;
    font-size: 1.75rem;
    letter-spacing: -0.02em;
    font-weight: 650;
}

.tagline {
    margin: 0.2rem 0 0;
    color: var(--ink-soft);
    font-size: 0.95rem;
}

.public-masthead h1 {
    font-size: clamp(2.5rem, 4.5vw, 3.5rem);
}

.public-masthead .tagline {
    font-size: clamp(1.15rem, 2vw, 1.35rem);
}

.masthead-side {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
}

.public-masthead .badge {
    padding: 0.4rem 0.85rem;
    font-size: 1rem;
}

.public-masthead .badge.is-complete {
    color: #fff;
    background: #238636;
    border-color: #238636;
}

.badge {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border: 1px solid var(--rule);
    border-radius: 999px;
    background: var(--panel);
    font-family: var(--mono);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.badge.is-active {
    color: var(--good);
    border-color: color-mix(in srgb, var(--good) 45%, var(--rule));
}

.badge.is-warn {
    color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 45%, var(--rule));
}

.badge.is-bad {
    color: var(--bad);
    border-color: color-mix(in srgb, var(--bad) 45%, var(--rule));
}

.run-picker {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--ink-soft);
}

.run-picker[hidden] {
    display: none;
}

.run-picker select {
    font-family: var(--mono);
    font-size: 0.8rem;
    padding: 0.2rem 0.4rem;
    color: var(--ink);
    background: var(--panel);
    border: 1px solid var(--rule);
    border-radius: 6px;
}

.public-masthead .run-picker {
    font-size: 0.95rem;
}

.public-masthead .run-picker select {
    font-size: 1rem;
    padding: 0.45rem 0.65rem;
}

.campaign-note {
    display: flex;
    gap: 0.7rem;
    align-items: baseline;
    margin: -0.45rem 0 1.1rem;
    padding: 0.8rem 1rem;
    border-left: 3px solid var(--warn);
    border-radius: 6px;
    background: color-mix(in srgb, var(--warn) 8%, var(--panel));
    color: var(--ink-soft);
}

.campaign-note[hidden] {
    display: none;
}

.campaign-note strong {
    flex: 0 0 auto;
    color: var(--warn);
    font-size: 0.76rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.campaign-note span {
    overflow-wrap: anywhere;
}

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

.card {
    background: var(--panel);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 1.25rem 1.35rem;
    margin-bottom: 1.1rem;
}

.card h2 {
    margin: 0 0 0.9rem;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.hero-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.6rem;
}

.hero-head h2 {
    margin: 0;
}

.hero-figure {
    font-family: var(--mono);
    font-size: 2.4rem;
    font-weight: 600;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------- bars */

.bar {
    position: relative;
    height: 8px;
    border-radius: 999px;
    background: var(--track);
    overflow: hidden;
}

.bar-large {
    height: 14px;
}

.bar-fill {
    height: 100%;
    width: 0;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--done-soft), var(--done));
    transition: width 0.6s ease;
}

/* A bar whose weights are only task counts is drawn hatched -- still green, so it reads as progress, but
   visibly not the same claim as a work-weighted bar. */
.bar-fill.is-naive {
    background: repeating-linear-gradient(135deg, var(--done) 0 6px, var(--done-soft) 6px 12px);
}

.hero-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 0.75rem 1.5rem;
    margin: 1.1rem 0 0;
}

.hero-facts div,
.facts div {
    margin: 0;
}

dt {
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

dd {
    margin: 0.1rem 0 0;
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: 1rem;
}

.facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 0.8rem 1.5rem;
    margin: 0;
}

/* ---------------------------------------------------------------------- the count */

/* Its own row, because at N=28 this is a couple of dozen digits and breaking it across two lines makes it
   unreadable. It scrolls sideways inside its own box rather than wrapping or shrinking the page. */
.solutions {
    padding-bottom: 1.1rem;
    text-align: center;
}

.solutions-value {
    display: flex;
    justify-content: safe center;
    margin: 0;
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: clamp(1.5rem, 4.5vw, 3rem);
    font-weight: 600;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 0.15rem;
}

.solutions-scale {
    margin: 0.3rem 0 0;
    font-family: var(--mono);
    font-size: 0.8rem;
    color: var(--ink-faint);
}

/* The fundamental count, under the total and visibly subordinate to it: same typeface and the same sideways
   scroll, a step smaller, separated by the card's own rule so that the two counts are never read as one
   number split over two lines. It appears only when the campaign is finished, so the space it takes is space
   nothing else wanted. */
.solutions-unique {
    margin-top: 1.05rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--rule-soft);
}

.solutions-unique h3 {
    margin: 0 0 0.5rem;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.solutions-value.is-secondary {
    font-size: clamp(0.95rem, 2vw, 1.35rem);
    color: var(--ink-soft);
}

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

.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 0.6rem;
    margin-bottom: 1.1rem;
}

/* Label on top, figure beneath it. Two lines rather than the three it used to be: the explanation that was a
   third line is now the tile's info card. */
.tile {
    background: var(--panel);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 0.7rem 0.9rem;
}

.tile-label {
    display: block;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

.tile-value {
    display: block;
    margin-top: 0.2rem;
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: 1.15rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------- current class */

.current-class {
    display: grid;
    grid-template-columns: 4.5rem 1fr auto;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--rule-soft);
}

.current-class:last-child {
    border-bottom: none;
}

.current-class .q {
    font-family: var(--mono);
    font-size: 0.85rem;
    color: var(--ink-soft);
}

.current-class .pct {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    min-width: 4.5rem;
    text-align: right;
}

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

.scroll {
    overflow-x: auto;
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.87rem;
}

.table th {
    text-align: left;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-faint);
    padding: 0 0.55rem 0.45rem;
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}

.table td {
    padding: 0.5rem 0.55rem;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: middle;
}

.table tbody tr:last-child td {
    border-bottom: none;
}

.table .right {
    text-align: right;
}

.num {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.dim {
    color: var(--ink-faint);
}

/* The order-of-magnitude line under a long count inside a table cell. */
.cell-note {
    display: block;
    margin-top: 0.1rem;
    font-family: var(--mono);
    font-size: 0.7rem;
    color: var(--ink-faint);
}

.state-complete {
    color: var(--good);
}

.state-running {
    color: var(--accent);
}

.state-waiting {
    color: var(--ink-faint);
}

.bar-cell {
    min-width: 130px;
}

.machines {
    font-size: 0.8rem;
    color: var(--ink-soft);
}

.machines span {
    display: block;
    white-space: nowrap;
}

.rank {
    font-family: var(--mono);
    color: var(--ink-faint);
    width: 1.5rem;
}

.who {
    font-weight: 600;
    white-space: nowrap;
}

.share-cell {
    min-width: 120px;
}

.share-cell .num {
    display: block;
    font-size: 0.8rem;
    margin-bottom: 0.25rem;
}

.chip {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.03em;
    border: 1px solid currentColor;
    white-space: nowrap;
}

.chip-measured {
    color: var(--measured);
}

.chip-prior {
    color: var(--prior);
}

.chip-fitted {
    color: var(--fitted);
}

.chip-task_count {
    color: var(--ink-faint);
}

.chip-tail,
.chip-audit {
    color: var(--warn);
}

/* ---------------------------------------------------------------------- notes */

.footnote {
    margin: 0.9rem 0 0;
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--ink-soft);
}

.footnote b {
    color: var(--ink);
    font-weight: 600;
}

.foot {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
    justify-content: space-between;
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--ink-faint);
}

.empty {
    margin: 0;
    color: var(--ink-faint);
    font-size: 0.87rem;
}

@media (max-width: 640px) {
    .shell {
        padding: 1.5rem 0.9rem 3rem;
    }

    .hero-figure {
        font-size: 1.9rem;
    }
}

/* ---------------------------------------------------------------------- operator forms
 *
 * Only the operator page uses these. Kept in the one stylesheet rather than a second file because it is a
 * handful of rules and the two pages should not be able to drift apart visually.
 */

form p {
    margin: 0 0 0.7rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
}

form label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--ink-soft);
}

input[type="number"],
input[type="text"],
select,
textarea {
    font-family: var(--mono);
    font-size: 0.82rem;
    color: var(--ink);
    background: var(--page);
    border: 1px solid var(--rule);
    border-radius: 6px;
    padding: 0.3rem 0.5rem;
}

input[type="text"],
textarea {
    width: 100%;
    flex: 1 1 20rem;
}

textarea {
    resize: vertical;
    line-height: 1.4;
}

input[type="file"] {
    font-size: 0.82rem;
    color: var(--ink-soft);
}

button {
    font-family: var(--sans);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--panel);
    background: var(--accent);
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 0.32rem 0.9rem;
    cursor: pointer;
    margin-right: 0.4rem;
}

button:disabled {
    opacity: 0.5;
    cursor: default;
}

button:hover:not(:disabled) {
    filter: brightness(1.08);
}

td button {
    font-size: 0.75rem;
    padding: 0.15rem 0.55rem;
}

pre {
    margin: 0;
    font-family: var(--mono);
    font-size: 0.8rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

code {
    font-family: var(--mono);
    font-size: 0.92em;
    padding: 0.05rem 0.3rem;
    border-radius: 4px;
    background: var(--accent-soft);
}

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

/* ---------------------------------------------------------------------- info hover cards
 *
 * Every caveat on this page lives in one of these instead of in a paragraph under the numbers. Three things
 * make it usable rather than merely tidy: it opens on `:hover` AND on `:focus-within`, so it is reachable by
 * keyboard (the marker is `tabindex="0"`); it is a real element in the flow rather than a `title` attribute,
 * so it can hold markup and does not wait a second to appear; and on a touch screen a tap focuses the marker,
 * which is why focus and not only hover is the trigger.
 */

.info {
    position: relative;
    display: inline-block;
    width: 1.05em;
    height: 1.05em;
    margin-left: 0.4em;
    border: 1px solid var(--ink-faint);
    border-radius: 50%;
    color: var(--ink-faint);
    font-family: var(--sans);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.05em;
    text-align: center;
    text-transform: none;
    letter-spacing: 0;
    cursor: help;
    vertical-align: middle;
    flex: none;
}

.info::before {
    content: "i";
}

.info:hover,
.info:focus {
    color: var(--accent);
    border-color: var(--accent);
    outline: none;
}

/* `display` here also has to defeat the browser's own `[hidden] { display: none }`, which every tip carries as
   a fallback for a missing stylesheet -- an author rule wins over the UA one, so the hover rule below opens it
   normally. */
.info .tip {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: -0.6rem;
    z-index: 30;
    display: none;
    width: max-content;
    max-width: min(30rem, 78vw);
    padding: 0.7rem 0.85rem;
    background: var(--panel);
    color: var(--ink-soft);
    border: 1px solid var(--rule);
    border-radius: 8px;
    box-shadow: 0 6px 24px rgb(0 0 0 / 18%);
    font-family: var(--sans);
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.5;
    letter-spacing: 0;
    text-align: left;
    text-transform: none;
    white-space: normal;
    cursor: auto;
}

.info .tip b {
    color: var(--ink);
    font-weight: 600;
}

.info:hover .tip,
.info:focus .tip,
.info:focus-within .tip {
    display: block;
}

/* Near the right edge the card would leave the viewport, so the last column's markers open leftwards. */
.tile .info .tip,
.hero-facts div:last-child .info .tip {
    left: auto;
    right: -0.6rem;
}

/* A tile's label is a flex child, so its marker needs to sit on the text baseline rather than stretch. */
.tile-label .info {
    margin-left: 0.35em;
}

dt .info {
    vertical-align: baseline;
}

/* ---------------------------------------------------------------------- operator page, wider surface
 *
 * The operator page grew from three forms to nine sections, which is enough that it needs a way in at the top
 * and enough that a destructive button should not look like a confirming one. Everything below is the operator
 * page only; the public page uses none of it.
 */

.admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.1rem;
    margin: 0 0 1.1rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--rule);
    font-size: 0.78rem;
    font-family: var(--mono);
}

.admin-nav a {
    text-decoration: none;
}

.admin-nav a:hover {
    text-decoration: underline;
}

/* A second heading inside one card -- devices under contributors. */
.subhead {
    margin: 1.4rem 0 0.7rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

/* Retire, release, rename: reversible and unremarkable, so they should not read as the primary action. */
button.is-secondary {
    color: var(--ink-soft);
    background: var(--panel);
    border-color: var(--rule);
}

/* Quarantine and revoke: they take something away from a contributor. */
button.is-danger {
    color: var(--bad);
    background: var(--panel);
    border-color: color-mix(in srgb, var(--bad) 45%, var(--rule));
}

/* The state colour a build in an unusual status gets; `state-complete`/`state-waiting` cover the rest. */
.state-warn {
    color: var(--warn);
}

/* The generated `faffer distributed manifest` command line, meant to be selected and copied. */
.command {
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--rule);
    border-radius: 6px;
    background: var(--page);
    color: var(--ink);
    user-select: all;
}

/* What `inspect` renders: a manifest read back before anything is committed. */
.preview:not(:empty) {
    margin: 0.9rem 0;
    padding: 0.9rem 1rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--page);
}

.preview .table {
    margin-bottom: 0.8rem;
}

/* The activation code, shown beside the form that asked for it: it exists in exactly one response and
   nowhere else, so it must not be something the operator has to go looking for. */
.issued:not(:empty) {
    margin: 0.9rem 0;
    padding: 0.9rem 1rem;
    border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--rule));
    border-radius: var(--radius);
    background: var(--accent-soft);
}

.issued-head {
    margin: 0 0 0.6rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--ink);
}

.issued .command {
    margin-bottom: 0.6rem;
}

/* The activity log is a glance. Anything longer belongs in the database, not on the page. */
#activity .scroll {
    max-height: 22rem;
    overflow-y: auto;
}

/* Ranking controls remain reachable when wide result tables scroll on small screens. */
.ranking-controls, .admin-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
}
.ranking-controls { margin: 1rem 0; }
.ranking-controls label, .admin-filters label { display: flex; gap: .5rem; align-items: center; }
.ranking-detail { display: block; font-size: .8rem; font-weight: normal; opacity: .75; }
.join-card p { max-width: 75ch; }

.device-ranking summary { cursor: pointer; font-size: 1.1rem; font-weight: 600; }
