:root {
    color-scheme: light;

    --ink: #1c1a17;
    --ink-soft: #5b544b;
    --ink-faint: #8d857a;
    --paper: #f4efe4;
    --paper-raised: #fbf8f1;
    --rule: #ddd4c2;
    --accent: #7c2d12;
    --accent-soft: #f0e2d4;
}

* {
    box-sizing: border-box;
}

html {
    font-size: 16px;
}

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    line-height: 1.5;
}

h1, h2, h3 {
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.2;
    margin: 0 0 0.5rem;
}

h1 {
    font-size: 1.75rem;
}

h2 {
    font-size: 1.15rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-soft);
}

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

code {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.85em;
}

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

.shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    max-width: 68rem;
    margin: 0 auto;
    padding: 0 1.25rem;
}

.masthead {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 1.25rem 0 1rem;
    border-bottom: 2px solid var(--ink);
}

.wordmark {
    display: inline-flex;
    align-items: baseline;
    gap: 0.6rem;
    text-decoration: none;
    color: inherit;
}

.wordmark-name {
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.badge-unofficial {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    padding: 0.15rem 0.45rem;
    border: 1px solid var(--accent);
    border-radius: 2px;
    color: var(--accent);
}

.masthead-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 1.25rem;
}

.nav-link {
    color: var(--ink-soft);
    text-decoration: none;
    font-size: 0.95rem;
}

.nav-link:hover,
.nav-link.active {
    color: var(--ink);
    text-decoration: underline;
    text-underline-offset: 0.3em;
}

/* Reporting a problem is on every page and deliberately the quietest thing in the masthead: set off
   from the navigation by a rule, smaller and fainter than it, and never floating over the page. */
.nav-report {
    font: inherit;
    font-size: 0.85rem;
    color: var(--ink-faint);
    background: none;
    border: 0;
    border-left: 1px solid var(--rule);
    padding: 0 0 0 1.25rem;
    cursor: pointer;
}

.nav-report:hover {
    color: var(--ink);
    text-decoration: underline;
    text-underline-offset: 0.3em;
}

/* Narrow enough that it wraps onto a line of its own, where a divider would divide nothing. */
@media (max-width: 23rem) {
    .nav-report {
        border-left: 0;
        padding-left: 0;
    }
}

.report {
    width: min(34rem, calc(100vw - 2rem));
    border: 1px solid var(--ink);
    border-radius: 2px;
    background: var(--paper);
    color: var(--ink);
    padding: 1.25rem 1.5rem;
}

.report::backdrop {
    background: rgba(28, 26, 23, 0.45);
}

.report p {
    color: var(--ink-soft);
}

.report-title {
    text-transform: none;
    letter-spacing: normal;
    color: var(--ink);
}

.report-shot {
    border-top: 1px solid var(--rule);
    padding-top: 0.25rem;
}

.report-outcome {
    font-weight: 600;
}

.report-preview {
    display: block;
    max-width: 100%;
    max-height: 14rem;
    border: 1px solid var(--rule);
}

.report-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.25rem;
}

.report-actions a.button {
    text-decoration: none;
}

/* Hidden for the moment a problem report's screenshot is taken, because the issue it goes into is
   public. A dice table code is the whole of a table's key. */
.capturing .report-private {
    visibility: hidden;
}

.content {
    flex: 1 0 auto;
    padding: 1.5rem 0 2.5rem;
}

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

.notice {
    border: 1px solid var(--rule);
    border-left: 4px solid var(--accent);
    background: var(--paper-raised);
    padding: 0.9rem 1.1rem;
    margin: 0 0 1.5rem;
}

.notice h1,
.notice h2 {
    margin-top: 0;
    text-transform: none;
    letter-spacing: normal;
    color: var(--ink);
    font-size: 1.05rem;
}

.notice p {
    margin: 0;
    color: var(--ink-soft);
}

.notice details {
    margin-top: 0.6rem;
}

.notice summary {
    cursor: pointer;
    color: var(--ink);
}

.notice ul {
    margin: 0.5rem 0 0;
    padding-left: 1.25rem;
    color: var(--ink-soft);
}

.notice li + li {
    margin-top: 0.25rem;
}

.controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 1.25rem;
}

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

.kind-picker-label {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-faint);
}

select,
.button {
    font: inherit;
    font-size: 0.95rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--ink-faint);
    border-radius: 2px;
    padding: 0.35rem 0.7rem;
    cursor: pointer;
}

.button:hover,
select:hover {
    border-color: var(--ink);
}

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

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

.seed {
    margin-left: auto;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-faint);
}

.site {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
    gap: 1.75rem;
    align-items: start;
}

.site-map svg {
    width: 100%;
    height: auto;
    border: 1px solid var(--rule);
    background: var(--paper-raised);
}

.field {
    border-bottom: 1px solid var(--rule);
    padding: 0.5rem 0;
}

.field.is-pinned {
    background: var(--accent-soft);
    box-shadow: -0.6rem 0 0 var(--accent-soft), 0.6rem 0 0 var(--accent-soft);
}

.field-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.field-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--ink-faint);
}

.field-actions {
    display: inline-flex;
    gap: 0.25rem;
    opacity: 0.35;
    transition: opacity 0.12s ease-in-out;
}

.field:hover .field-actions,
.field:focus-within .field-actions,
.field.is-pinned .field-actions {
    opacity: 1;
}

.chip {
    font: inherit;
    font-size: 0.8rem;
    line-height: 1;
    background: none;
    border: 1px solid transparent;
    border-radius: 2px;
    padding: 0.2rem 0.35rem;
    cursor: pointer;
}

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

.chip-on {
    border-color: var(--accent);
}

.field-value {
    margin: 0.1rem 0 0;
    font-size: 1.05rem;
}

.field-value.is-empty {
    color: var(--ink-faint);
}

.areas {
    margin-top: 2rem;
}

.area-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0 1.5rem;
}

/* The landing page. Neither tool is the front door any more, so the two cards are equal weight and
   the page says what each one is for before asking anybody to choose. */

.tool-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.25rem;
    margin-top: 1.75rem;
}

.tool-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 1.1rem 1.25rem 1.25rem;
    border: 1px solid var(--rule);
    border-top: 3px solid var(--ink);
    background: var(--paper-raised);
}

.tool-card h2 {
    margin: 0;
    color: var(--ink);
    font-size: 1.05rem;
    text-transform: none;
    letter-spacing: normal;
}

.tool-card p {
    flex: 1 0 auto;
    margin: 0;
    color: var(--ink-soft);
}

/* The call to action is an anchor rather than a button, because it goes somewhere. */
.tool-card .button {
    display: inline-block;
    margin-top: 0.3rem;
    text-decoration: none;
}

.landing-about {
    max-width: 44rem;
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
    font-size: 0.9rem;
    color: var(--ink-soft);
}

.prose {
    max-width: 44rem;
    margin-bottom: 2rem;
}

.lede {
    max-width: 44rem;
    font-size: 1.1rem;
    color: var(--ink-soft);
}

.provenance {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.provenance th,
.provenance td {
    text-align: left;
    padding: 0.4rem 0.6rem 0.4rem 0;
    border-bottom: 1px solid var(--rule);
    vertical-align: top;
}

.colophon {
    flex: 0 0 auto;
    border-top: 1px solid var(--rule);
    padding: 1rem 0 1.5rem;
}

.notice-line {
    margin: 0 0 0.35rem;
    font-size: 0.8rem;
    color: var(--ink-soft);
}

/* The Games Omnivorous compatibility badge, which section 1 of the licence requires and forbids
   altering. Only the height is set, so the browser derives the width from the file's own 564:511 —
   a second dimension here is all it would take to squash a mark we are not allowed to change, and
   it would look like a typo rather than a licence breach. The file itself is pinned by checksum in
   ArtworkTests; this rule is the other half of leaving it alone.

   The logo is a lead-in, not a stamp: it ends in the words "SWINGS HARD WITH", set low inside its
   frame, and the product name finishes the sentence. So the two are aligned on that bottom line
   rather than centred on the block, and the padding is what drops the name onto the same optical
   baseline as the logo's own last row of type.

   Both numbers below are measured off the file rather than chosen, because the phrase is a small
   part of a picture that is mostly flail: its capitals occupy rows 402-445 of 511, so they render
   at 8.61% of whatever height this rule sets, and its baseline sits 12.72% of the height above the
   bottom edge. That first figure decides the size. The badge first shipped at 3rem, which put the
   logo's own words at 4.1px against an 11px product name beside them — more than two and a half
   times the size, so the sentence the badge is built around could not be read at all. The height is
   set instead at the point where the two halves match: 8rem x 8.61% is 11.02px of capital, against
   the name's 11px.

   Both are expressed against one custom property so that stays true. Resizing the badge by editing
   --compat-logo-height alone keeps the name on the baseline; a hand-tuned padding would silently
   come adrift the next time somebody nudged the height. The trailing 1.79px is the part of the
   name's own line box that already sits below its baseline, which the padding must not pay for
   twice. */

.compat-badge {
    --compat-logo-height: 8rem;
    display: flex;
    align-items: flex-end;
    gap: 0.75rem;
    margin: 0 0 0.9rem;
}

.compat-logo {
    height: var(--compat-logo-height);
    width: auto;
    max-width: 100%;
}

.compat-name {
    padding-bottom: calc(var(--compat-logo-height) * 0.1272 - 1.79px);
    font-size: 0.95rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink);
}

/* The dice table. Rolls are read at a glance, across the room, by somebody who is also holding a
   character sheet — so the faces are large, the dropped ones are dimmed rather than hidden, and the
   reading gets a line of its own rather than being tucked beside the total. */

input[type="text"],
input[type="number"] {
    font: inherit;
    font-size: 0.95rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--ink-faint);
    border-radius: 2px;
    padding: 0.35rem 0.55rem;
}

input[type="text"]:hover,
input[type="number"]:hover {
    border-color: var(--ink);
}

.dice-heading {
    margin-bottom: 0.75rem;
}

.dice-field {
    display: inline-flex;
    flex-direction: column;
    gap: 0.2rem;
}

.dice-field-number input {
    width: 5rem;
}

.dice-identity {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.6rem;
}

.dice-code {
    display: inline-flex;
    flex-direction: column;
    gap: 0.2rem;
    margin: 0;
}

.dice-code code {
    font-size: 1rem;
    letter-spacing: 0.06em;
}

.dice-status {
    margin: 0.75rem 0 0;
    font-size: 0.85rem;
    color: var(--ink-soft);
}

.dice-status-open::before,
.dice-status-searching::before,
.dice-status-joining::before,
.dice-status-away::before,
.dice-status-failed::before {
    content: "●";
    margin-right: 0.4rem;
    color: var(--ink-faint);
}

.dice-status-open::before {
    color: #3f6212;
}

.dice-status-failed::before {
    color: var(--accent);
}

/* Names rather than a headcount: a table that has gone quiet still shows who is sitting at it. */
.dice-present {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin-top: 0.5rem;
}

.dice-roster {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.dice-seat {
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--rule);
    border-radius: 0.9rem;
    font-size: 0.85rem;
}

.dice-seat-mine {
    border-color: var(--ink);
}

/* Connected, not yet introduced: dashed rather than missing, so the count and the list agree. */
.dice-seat-waiting {
    border-style: dashed;
    color: var(--ink-faint);
}

.dice-you {
    margin-left: 0.3rem;
    font-size: 0.75rem;
    color: var(--ink-faint);
}

.dice-hint,
.dice-notes p {
    margin: 0.5rem 0 0;
    max-width: 44rem;
    font-size: 0.85rem;
    color: var(--ink-faint);
}

/* The relay check: which half of the connection is failing, in one line per relay. Shut by default,
   because almost every table works and a page that leads with its own diagnostics reads like a page
   expecting to fail. The people who need it are already looking for something to press. */
.dice-check {
    margin-top: 0.75rem;
}

.dice-check > summary {
    width: fit-content;
    font-size: 0.85rem;
    color: var(--ink-faint);
    cursor: pointer;
}

.dice-check > summary:hover {
    color: var(--ink-soft);
}

.dice-check[open] > summary {
    margin-bottom: 0.5rem;
}

/* Once the check has run, its answer stops being a hint and becomes the thing to go and do. */
.dice-verdict {
    color: var(--ink-soft);
}

.dice-relays {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 0.5rem 0 0;
    padding: 0;
    list-style: none;
}

.dice-relay {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.85rem;
}

.dice-relay-host {
    min-width: 15rem;
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.8rem;
    color: var(--ink-soft);
}

/* The faint default, and the green and rust the status dot uses so the two agree at a glance. Both
   the row and the key below it carry these same two classes, because a key printed in a different
   colour from the thing it explains is worse than no key: the reader takes the difference for
   meaning. Single class each, so the state wins on source order rather than on specificity. */
.dice-relay-state {
    color: var(--ink-faint);
}

.dice-state-open {
    color: #3f6212;
}

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

/* The key. Three words are scannable but not actionable, and the row that reads worst — a relay
   answering nothing — is the one whose meaning flips entirely depending on how many others did. */
.dice-legend {
    margin: 0.65rem 0 0;
    max-width: 44rem;
    font-size: 0.85rem;
}

.dice-legend dt {
    margin-top: 0.4rem;
}

.dice-legend dd {
    margin: 0.1rem 0 0;
    color: var(--ink-faint);
}

@media (min-width: 40rem) {
    .dice-legend dt {
        float: left;
        clear: left;
        width: 15rem;
        margin-top: 0;
    }

    .dice-legend dd {
        margin: 0 0 0.5rem 15.5rem;
        min-height: 1.4em;
    }
}

.dice-table {
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--rule);
}

.dice-controls {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
    padding: 1.25rem 0;
    border-bottom: 1px solid var(--rule);
}

.dice-rolls {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0.75rem;
}

/* Each way to roll is framed, so the number field and the edge steps sit plainly with the button
   they belong to rather than running into the next roll along. */
.dice-roll {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-raised);
}

/* One row of steps rather than a dropdown, so the whole range is visible while choosing. */
.dice-edge {
    display: inline-flex;
    align-self: flex-end;
    border: 1px solid var(--rule);
    border-radius: 0.2rem;
    overflow: hidden;
}

.dice-edge-step {
    min-width: 2rem;
    padding: 0.35rem 0.4rem;
    border: 0;
    border-right: 1px solid var(--rule);
    background: none;
    font: inherit;
    font-size: 0.85rem;
    color: var(--ink-soft);
    cursor: pointer;
}

.dice-edge-step:last-child {
    border-right: 0;
}

.dice-edge-step:hover {
    color: var(--ink);
}

.dice-edge-step.is-on {
    background: var(--accent);
    color: var(--paper);
}

.dice-secret {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
    color: var(--ink-soft);
}

.dice-log {
    margin-top: 1.5rem;
}

.dice-log-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

.button-quiet {
    background: none;
    border-color: transparent;
    color: var(--ink-faint);
    font-size: 0.85rem;
}

.button-quiet:hover {
    color: var(--ink);
}

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

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

.dice-entry {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 0.75rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--rule);
}

.dice-entry-mine {
    background: var(--accent-soft);
    box-shadow: -0.6rem 0 0 var(--accent-soft), 0.6rem 0 0 var(--accent-soft);
}

/* A private roll still shows its own roller the dice, so it needs a mark that says the rest of the
   table is not seeing this. Dashed, because the entry is deliberately incomplete elsewhere. */
.dice-entry-secret {
    border-bottom-style: dashed;
}

.dice-who {
    font-weight: 600;
    min-width: 6rem;
}

.dice-notation,
.dice-seed {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.8rem;
    color: var(--ink-faint);
}

.dice-seed {
    margin-left: auto;
}

/* The attribute a save was rolled against, kept beside the notation because it is part of what was
   rolled rather than part of what came up. Monospaced with the notation so "1d20 ≤14" reads as one
   statement. */
.dice-target {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    color: var(--ink-soft);
    white-space: nowrap;
}

.dice-faces {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.die {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.6rem;
    height: 1.6rem;
    padding: 0 0.25rem;
    border: 1px solid var(--ink);
    border-radius: 3px;
    background: var(--paper-raised);
    font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
}

/* Dropped dice stay on the table. Half the pleasure of keeping the best three is seeing what you
   did not keep. */
.die-dropped {
    border-color: var(--rule);
    color: var(--ink-faint);
    opacity: 0.6;
}

.dice-total {
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.dice-reading {
    color: var(--accent);
}

/* Poker results sit apart from the reading: they are what the dice also showed, not the answer. */
.dice-note {
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--rule);
    border-radius: 0.75rem;
    font-size: 0.8rem;
    letter-spacing: 0.02em;
    color: var(--ink-soft);
    white-space: nowrap;
}

/* Shown only when a result happened more than once, which a pool large enough can manage. */
.dice-note-count {
    margin-left: 0.15rem;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.dice-private {
    color: var(--ink-faint);
    font-style: italic;
}

.dice-notes {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
}

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

    .dice-rolls {
        flex-direction: column;
        align-self: stretch;
    }

    .seed {
        margin-left: 0;
    }

    .dice-seed {
        margin-left: 0;
    }
}

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    background: #f7d9a0;
    padding: 0.6rem 1.25rem;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

.loading-progress {
    display: block;
    width: 6rem;
    height: 6rem;
    margin: 20vh auto 1rem;
}

.loading-progress circle {
    fill: none;
    stroke: var(--rule);
    stroke-width: 0.5rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8) 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    text-align: center;
    color: var(--ink-faint);
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}
