/* The window is the tray: green felt bed, leather rim, brass fittings. */

:root {
    --felt: #1d4a38;
    --felt-shade: #10281d;
    --leather: #33231a;
    --leather-edge: #4a3526;
    --brass: #c9a45c;
    --brass-bright: #e6c98a;
    --parchment: #f2e8d5;
    --rim: 7px;
    /* the rim's corner radius; the physics walls cut the same corner off
       (CORNER_CUT in dice/box.js), so a die cannot lodge in a right angle
       the glass of a phone does not have */
    --corner: 28px;
    /* One family throughout, in four weights: hierarchy comes from weight,
       size, tracking and case rather than from a second face. Alegreya Sans
       ships with the app under the SIL Open Font License
       (static/assets/fonts/OFL-AlegreyaSans.txt); the fallbacks name free
       faces before the generic, so nothing waits on a licensed system font. */
    --font: 'Alegreya Sans', 'DejaVu Sans', 'Liberation Sans', sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
}

body {
    font-family: var(--font);
    color: var(--parchment);
    background:
        radial-gradient(ellipse at 50% 40%, var(--felt) 0%, var(--felt-shade) 100%);
    /* a long press on the felt is a gesture, not a selection; on iOS it raised
       the text loupe over the die under the finger */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

/* leather rim around the whole viewport — these are the walls the dice bounce
   off. The corners are rounded like the screens the tray plays on, and the
   outer spread paints leather into the square corners the arc leaves behind. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 6;
    border: var(--rim) solid var(--leather);
    border-radius: var(--corner);
    box-shadow:
        inset 0 0 0 1px var(--leather-edge),
        inset 0 0 0 2px rgba(201, 164, 92, 0.35),
        inset 0 0 60px 10px rgba(0, 0, 0, 0.55),
        0 0 0 var(--corner) var(--leather);
}

/* dice canvas covers the whole table; taps reach the controls below via tray.js forwarding */
#diceRoller {
    position: fixed;
    inset: 0;
    z-index: 5;
    overflow: hidden;
    touch-action: none;
    cursor: grab;
}

/* result plaque: stamps in when the dice settle, fades while they rest */
#plaque {
    position: fixed;
    top: calc(var(--rim) + 18px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
    pointer-events: none;
    text-align: center;
    padding: 10px 28px 12px;
    background: linear-gradient(180deg, rgba(20, 15, 10, 0.85), rgba(35, 26, 18, 0.85));
    border: 1px solid var(--brass);
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5), inset 0 0 0 3px rgba(201, 164, 92, 0.15);
    opacity: 1;
}

#plaque.stamp {
    animation: stamp 260ms cubic-bezier(0.2, 1.4, 0.4, 1);
}

/* the result stays legible; it only dims out of the way, and a new roll resets it */
#plaque.faded { opacity: 0.35; transition: opacity 2s ease; }

/* kept in the accessibility tree even when out of sight, so the live region works */
#plaque.away { opacity: 0; pointer-events: none; }

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* transform only: an animated opacity would pre-empt the fade transition below */
@keyframes stamp {
    from { transform: translateX(-50%) scale(1.25); }
    to   { transform: translateX(-50%) scale(1); }
}

#plaqueTotal {
    display: block;
    font-family: var(--font);
    font-weight: 800;
    font-size: clamp(2.2rem, 6vw, 3.4rem);
    line-height: 1.05;
    letter-spacing: -0.015em;
    font-variant-numeric: lining-nums tabular-nums;
    color: var(--brass-bright);
}

#plaqueDetail {
    display: block;
    font-size: 0.95rem;
    letter-spacing: 0.04em;
    color: var(--parchment);
    opacity: 0.85;
}

#hint {
    position: fixed;
    bottom: 200px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
    pointer-events: none;
    max-width: min(80vw, 34rem);
    text-align: center;
    font-size: 1rem;
    color: var(--parchment);
    opacity: 0.7;
    transition: opacity 0.8s ease;
}

#hint.gone { opacity: 0; }

/* controls lie flat on the felt, under the dice canvas; no ledge */
#bar {
    position: fixed;
    bottom: calc(var(--rim) + 8px + env(safe-area-inset-bottom));
    left: calc(var(--rim) + 8px + env(safe-area-inset-left));
    right: calc(var(--rim) + 8px + env(safe-area-inset-right));
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* The set is laid out as a set: the row is as wide as the dice need and sits in
   the middle of the bar. Sharing the whole width between eight buttons left
   two dice worth of gap between neighbours on a desktop, and on a phone put
   the count badges into the die next door. */
#chips {
    /* eight dice and seven gaps have to fit the narrowest phone, and the active
       chip's 1.12 scale needs room to grow into on top of that */
    --art: clamp(22px, 6.9vw, 48px);
    --badge: clamp(16px, 2.2vw, 20px);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: clamp(9px, 3.2vw, 24px);
}

/* die silhouettes on one shared baseline, like a set laid out on the table */
.chip {
    flex: 0 0 auto;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: calc(var(--art) + 14px);
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    filter: saturate(0.35);
    opacity: 0.55;
    transition: filter 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}

.chip svg {
    height: var(--art);
    width: auto;
    overflow: visible;
}

.chip svg polygon, .chip svg circle, .chip svg rect {
    fill: var(--die, #0d4d8c);
    stroke: rgba(0, 0, 0, 0.25);
    stroke-width: 2;
    stroke-linejoin: round;
}

.chip svg .facet {
    fill: none;
    stroke: rgba(255, 249, 240, 0.35);
    stroke-width: 2;
}

/* the silhouettes wear whatever the settings paint on the dice themselves */
.chip svg text {
    fill: var(--numerals, var(--parchment));
    font-family: var(--font);
    font-weight: 700;
    font-size: 34px;
    text-anchor: middle;
    dominant-baseline: central;
    paint-order: stroke;
}

.chip.active {
    filter: saturate(1);
    opacity: 1;
    transform: scale(1.12);
    transform-origin: bottom center;
}

.chip.active svg {
    filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.4));
}

.chip.pressed, .chip:active { transform: scale(1.05) translateY(1px); }

/* locked while advantage owns the throw */
.chip.locked { opacity: 0.28; cursor: default; }

/* a refused press: the control is there, the answer is no */
.refused { animation: refuse 220ms ease; }

@keyframes refuse {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
}

.unread { color: var(--brass-bright); font-weight: 700; }

.chip.held svg { filter: drop-shadow(0 0 6px var(--brass-bright)); }

/* The count hangs off the silhouette, not off the button: the buttons share the
   bar evenly and are wider than the dice they draw, so a badge pinned to the
   button's own corner floated away from the die — furthest on the end ones. */
.chipArt {
    position: relative;
    display: inline-flex;
}

.chip .count {
    position: absolute;
    top: calc(var(--badge) * -0.3);
    right: calc(var(--badge) * -0.25);
    min-width: var(--badge);
    height: var(--badge);
    line-height: var(--badge);
    font-family: var(--font);
    font-weight: 700;
    font-size: calc(var(--badge) * 0.62);
    color: var(--leather);
    background: var(--brass);
    border-radius: 10px;
    text-align: center;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Roll in the middle, the two round satellites flanking it */
#controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
}

/* Panels that open over the tray. They sit above the dice canvas, so their own
   buttons take clicks directly instead of being forwarded through it. */
.sheet {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 7;
    display: flex;
    flex-direction: column;
    padding: 4px 6px 10px;
    background: linear-gradient(180deg, rgba(35, 26, 18, 0.97), rgba(24, 18, 12, 0.97));
    border: 1px solid var(--brass);
    border-radius: 10px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.55);
}

.sheet[hidden] { display: none; }

.sheet header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 6px 10px 12px;
    border-bottom: 1px solid rgba(201, 164, 92, 0.3);
}

.sheet h2 {
    font-family: var(--font);
    font-weight: 700;
    font-size: 0.86rem;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--brass-bright);
}

.sheet header .spacer { flex: 1 1 auto; }

.sheetClose {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    font-size: 1.4rem;
    line-height: 1;
    color: var(--parchment);
    background: transparent;
    border: none;
    border-radius: 18px;
    cursor: pointer;
}

.sheetClose:active { background: rgba(242, 232, 213, 0.12); }

#rollMods {
    gap: 10px;
    width: min(348px, calc(100vw - 2 * var(--rim) - 24px));
    padding-bottom: 12px;
}

#rollMods #edge, #rollMods #modifier { margin: 0 6px; }

#edge {
    display: flex;
    align-items: center;
    border: 1px solid rgba(201, 164, 92, 0.35);
    border-radius: 10px;
    overflow: hidden;
}

#edge button {
    flex: 1 1 0;
    min-height: 44px;
    padding: 6px 14px;
    font-family: var(--font);
    font-size: 0.9rem;
    white-space: nowrap;
    color: var(--parchment);
    background: transparent;
    border: none;
    cursor: pointer;
    opacity: 0.65;
}

#edge button + button { border-left: 1px solid rgba(201, 164, 92, 0.25); }

#edge button.on {
    opacity: 1;
    color: var(--leather);
    background: var(--brass);
    font-weight: 700;
}

#edge button:not(.on):active { background: rgba(201, 164, 92, 0.2); }

#history {
    width: min(360px, calc(100vw - 2 * var(--rim) - 24px));
    max-height: min(70vh, 560px);
}


/* the pools worth keeping, each named by its own formula */
#saved {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 2px 6px 0;
    padding-top: 10px;
    border-top: 1px solid rgba(201, 164, 92, 0.25);
}

.savedHead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 0.85rem;
    color: var(--parchment);
    opacity: 0.75;
}

#saveRoll {
    padding: 5px 12px;
    font-family: var(--font);
    font-size: 0.82rem;
    color: var(--parchment);
    background: transparent;
    border: 1px solid rgba(242, 232, 213, 0.3);
    border-radius: 15px;
    cursor: pointer;
}

#saveRoll:disabled { opacity: 0.3; cursor: default; }

#saveRoll:not(:disabled):active { background: rgba(242, 232, 213, 0.12); }

/* Two even columns, and room kept for the first four: a ragged wrap made the
   panel jump under the finger every time a roll was saved or forgotten, and
   the panel is centred on the tray, so any change in height moves both edges. */
#savedList {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-content: start;
    /* fixed, not grown to fit: the panel is centred on the tray, so every row
       added or dropped would move both its edges under the finger. Four whole
       rows show eight of the ten; the last row scrolls. */
    height: 162px;
    overflow-y: auto;
    scrollbar-width: none;
}

/* nothing saved yet: the line below says so, and holding the space would be
   holding it for nothing */
#savedList:empty { height: 0; }

#savedList::-webkit-scrollbar { display: none; }

.savedItem {
    display: flex;
    align-items: stretch;
    min-width: 0;
    border: 1px solid rgba(201, 164, 92, 0.3);
    border-radius: 17px;
    overflow: hidden;
}

.savedLoad, .savedDrop {
    font-family: var(--font);
    color: var(--parchment);
    background: transparent;
    border: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.savedLoad {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 34px;
    padding: 5px 4px 5px 12px;
    font-size: 0.86rem;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.savedDrop {
    padding: 0 11px 0 5px;
    font-size: 1.1rem;
    line-height: 1;
    opacity: 0.5;
}

.savedLoad:active { background: rgba(201, 164, 92, 0.2); }

.savedDrop:active { background: rgba(242, 232, 213, 0.12); opacity: 1; }

#savedEmpty, #dieScope {
    font-size: 0.82rem;
    color: var(--parchment);
    opacity: 0.6;
}

#dieScope { margin-top: -2px; }

#historyWipe {
    padding: 6px 12px;
    font-family: var(--font);
    font-size: 0.85rem;
    color: var(--parchment);
    background: transparent;
    border: 1px solid rgba(242, 232, 213, 0.3);
    border-radius: 16px;
    cursor: pointer;
    opacity: 0.85;
}

#historyWipe:disabled { opacity: 0.3; cursor: default; }

#historyWipe:not(:disabled):active { background: rgba(242, 232, 213, 0.12); }

#historyList {
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: 0;
    scrollbar-width: none;
}

#historyList::-webkit-scrollbar { display: none; }

#historyList li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 12px;
    border-bottom: 1px solid rgba(201, 164, 92, 0.12);
}

#historyList li:last-child { border-bottom: none; }

.entryRoll {
    font-family: var(--font);
    font-weight: 500;
    font-size: 0.95rem;
    color: var(--parchment);
}

.entryDice {
    display: block;
    font-size: 0.8rem;
    color: var(--parchment);
    opacity: 0.6;
}

.entryTotal {
    flex: 0 0 auto;
    font-family: var(--font);
    font-weight: 800;
    font-size: 1.25rem;
    font-variant-numeric: lining-nums tabular-nums;
    color: var(--brass-bright);
}

.dropped { opacity: 0.45; text-decoration: line-through; }

.sep { opacity: 0.45; }

#historyEmpty {
    padding: 16px 12px 8px;
    font-size: 0.9rem;
    color: var(--parchment);
    opacity: 0.65;
}

#settings {
    width: min(400px, calc(100vw - 2 * var(--rim) - 24px));
    max-height: min(95vh, 680px);
}

/* On a short screen the list is longer than the panel however tight the rows
   are, and the scrollbar is hidden — so the panel says there is more below
   rather than looking as though it was cut off. */
#settingsBody.more {
    mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
}

#settingsBody {
    display: flex;
    flex-direction: column;
    gap: 5px;
    /* the bottom padding keeps the last switch off the panel's own edge, where
       it read as cut off rather than as the end of the list */
    padding: 8px 12px;
    overflow-y: auto;
    scrollbar-width: none;
}

#settingsBody::-webkit-scrollbar { display: none; }

#settingsBody h3 {
    margin-top: 3px;
    font-family: var(--font);
    font-weight: 700;
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brass);
    opacity: 0.8;
}

#settingsBody h3:first-child { margin-top: 0; }

#settingsReset {
    padding: 6px 12px;
    font-family: var(--font);
    font-size: 0.85rem;
    color: var(--parchment);
    background: transparent;
    border: 1px solid rgba(242, 232, 213, 0.3);
    border-radius: 16px;
    cursor: pointer;
    opacity: 0.85;
}

#settingsReset:active { background: rgba(242, 232, 213, 0.12); }

.pickRow {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.pickRow button {
    display: flex;
    align-items: center;
    gap: 7px;
    min-height: 34px;
    padding: 5px 12px;
    font-family: var(--font);
    font-size: 0.88rem;
    color: var(--parchment);
    background: transparent;
    border: 1px solid rgba(201, 164, 92, 0.3);
    border-radius: 17px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.pickRow button.on {
    border-color: var(--brass-bright);
    box-shadow: inset 0 0 0 1px var(--brass-bright);
}

/* A die wears its own colour, so a brass outline on a bone or brass set is
   invisible. The mark has to read against any body colour, hence a dot with a
   dark collar rather than a ring around the button. */
#diePick button { position: relative; }

#diePick button.on {
    box-shadow: none;
    border-color: rgba(0, 0, 0, 0.45);
}

#diePick button.on::after {
    content: '';
    position: absolute;
    top: 4px;
    right: 5px;
    width: 9px;
    height: 9px;
    background: var(--brass-bright);
    border: 1.5px solid rgba(24, 18, 12, 0.9);
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.pickRow .dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, 0.35);
}

/* Even columns rather than a ragged wrap: the names are different lengths, so
   flowing them left to right left the rows jumping about. */
#presets {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
}

#presets button {
    justify-content: flex-start;
    padding: 5px 9px;
    font-size: 0.84rem;
}

/* eight dice never fit one row, so they get even ones instead of a ragged wrap */
#diePick {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
}

/* each die wears its own colours, so the row doubles as a preview of the set */
#diePick button {
    justify-content: center;
    padding: 5px 10px;
    font-weight: 600;
    border-color: rgba(0, 0, 0, 0.4);
}

/* Each field is a heading with its row underneath, spread across the panel:
   set beside its swatches, the label ate a column and the row wrapped into a
   ragged block against it. */
.field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.fieldName {
    font-family: var(--font);
    font-weight: 700;
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--parchment);
    opacity: 0.6;
}

/* One row per field, whatever the width: the swatches share the panel between
   them and shrink to fit rather than spilling onto a second, ragged line. */
/* eight swatches and the colour picker, both rows alike */
.swatches {
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    justify-items: center;
    gap: 5px;
}

.swatches button {
    width: min(28px, 100%);
    aspect-ratio: 1;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.4);
    border-radius: 50%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.swatches button.on {
    box-shadow: 0 0 0 2px var(--brass-bright);
}

/* the finishes are words, not colours: a 28px disc shows nothing of a texture */
/* three even columns at every width: auto-fit would swing between four and
   five as the panel changed, and five names never fit a phone */
#finishPick {
    grid-template-columns: repeat(3, 1fr);
}

#finishPick button {
    width: 100%;
    height: 30px;
    padding: 0 6px;
    font-family: var(--font);
    font-size: 0.82rem;
    color: var(--parchment);
    background: transparent;
    border: 1px solid rgba(201, 164, 92, 0.3);
    border-radius: 15px;
}

#finishPick button.on {
    color: var(--leather);
    background: var(--brass);
    font-weight: 700;
    box-shadow: none;
}

input.custom {
    width: min(28px, 100%);
    aspect-ratio: 1;
    padding: 0;
    background: transparent;
    border: 1px dashed rgba(242, 232, 213, 0.45);
    border-radius: 50%;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

input.custom::-webkit-color-swatch-wrapper { padding: 3px; }
input.custom::-webkit-color-swatch { border: none; border-radius: 50%; }
input.custom::-moz-color-swatch { border: none; border-radius: 50%; }

.switchRow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 30px;
    font-size: 0.95rem;
    color: var(--parchment);
}

/* the two switches are one group, closer to each other than to the heading
   above them */
.switchRow + .switchRow { margin-top: -3px; }

.switchRow button[role="switch"] {
    flex: 0 0 auto;
    width: 48px;
    height: 27px;
    padding: 2px;
    background: rgba(242, 232, 213, 0.12);
    border: 1px solid rgba(201, 164, 92, 0.35);
    border-radius: 14px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s ease;
}

.switchRow button[aria-checked="true"] { background: var(--brass); }

.knob {
    display: block;
    width: 21px;
    height: 21px;
    background: var(--parchment);
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
    transition: transform 0.15s ease;
}

.switchRow button[aria-checked="true"] .knob { transform: translateX(21px); }

#shakeNote {
    margin-top: -4px;
    font-size: 0.8rem;
    color: var(--brass-bright);
    opacity: 0.8;
}

#shakeNote:empty { display: none; }

#modifier {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border: 1px solid rgba(201, 164, 92, 0.35);
    border-radius: 10px;
}

#modifier button {
    width: 56px;
    min-height: 44px;
    font-size: 1.2rem;
    color: var(--parchment);
    background: transparent;
    border: none;
    border-radius: 10px;
    cursor: pointer;
}

#modifier button:active { background: rgba(201, 164, 92, 0.25); }

#modValue {
    min-width: 48px;
    text-align: center;
    font-family: var(--font);
    font-weight: 800;
    font-size: 1.1rem;
    font-variant-numeric: lining-nums tabular-nums;
    color: var(--brass-bright);
}

#gestures {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 8px 16px;
    margin: 0;
    padding: 12px;
}

#gestures dt {
    font-weight: 600;
    color: var(--brass-bright);
    white-space: nowrap;
}

#gestures dd { margin: 0; color: var(--parchment); opacity: 0.85; font-size: 0.92rem; }

#help { width: min(380px, calc(100vw - 2 * var(--rim) - 24px)); }

/* the two controls that are not part of the throw, kept out of the bar so Roll
   stays in the middle of it */
#helpOpen, #settingsOpen {
    position: fixed;
    top: calc(var(--rim) + 10px + env(safe-area-inset-top));
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    color: var(--parchment);
    background: rgba(16, 40, 29, 0.35);
    border: 1px solid rgba(242, 232, 213, 0.25);
    border-radius: 50%;
    cursor: pointer;
    opacity: 0.75;
}

#helpOpen { right: calc(var(--rim) + 10px + env(safe-area-inset-right)); }

#settingsOpen { left: calc(var(--rim) + 10px + env(safe-area-inset-left)); }

#helpOpen.pressed, #settingsOpen.pressed { background: rgba(242, 232, 213, 0.14); }

#clear, #historyOpen {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    color: var(--parchment);
    background: rgba(16, 40, 29, 0.4);
    border: 1px solid rgba(242, 232, 213, 0.3);
    border-radius: 50%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

#clear svg, #historyOpen svg, #helpOpen svg, #settingsOpen svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
}

#clear.pressed, #historyOpen.pressed { background: rgba(242, 232, 213, 0.14); }

/* the Roll button is a d20 face struck in brass, matching the chips */
#roll {
    width: 96px;
    height: 96px;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.5));
    transition: transform 0.12s ease, filter 0.12s ease;
}

#roll svg { width: 100%; height: 100%; display: block; }

#roll .rim {
    fill: url(#brassFace);
    stroke: #f0dcae;
    stroke-width: 1.5;
    stroke-linejoin: round;
}

/* Three layers over the base gradient, which on its own read as flat fill:
   brushed lines, a little grain, then a highlight that also darkens the far
   rim. */
#roll .brush {
    fill: url(#brassBrush);
    opacity: 0.42;
}

/* any opaque fill will do: the filter only reads this layer's alpha, to trim
   the noise to the face */
#roll .grain {
    fill: #000;
    opacity: 0.6;
    mix-blend-mode: overlay;
}

#roll .glow { fill: url(#brassGlow); }

/* the facets are engraved rather than drawn: a dark groove with a lit edge
   picked out just below it, where the light from the gradient would catch */
#roll .facet {
    fill: none;
    stroke: rgba(63, 44, 30, 0.42);
    stroke-width: 1.2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

#roll .facetLight {
    fill: none;
    stroke: rgba(255, 246, 222, 0.55);
    stroke-width: 1.1;
    stroke-linejoin: round;
    stroke-linecap: round;
}

/* No fill here: the colour belongs to each struck copy in the markup, and a
   fill on the original would win over the one on the <use> that clones it. */
#rollLabel {
    font-family: var(--font);
    font-weight: 800;
    font-size: 19px;
    letter-spacing: 0.16em;
    text-anchor: middle;
    dominant-baseline: central;
}

/* what the next throw carries, engraved under the word rather than stuck on top */
#rollBadge {
    font-family: var(--font);
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 0.04em;
    text-anchor: middle;
    dominant-baseline: central;
}

#roll .label { fill: #3a2617; }

#roll .labelLight { fill: rgba(255, 246, 222, 0.5); }

#roll.pressed, #roll:active {
    transform: translateY(2px) scale(0.97);
    filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.5));
}

button:focus-visible {
    outline: 2px solid var(--brass-bright);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    #plaque.stamp { animation: none; }
    #plaque.faded { transition: none; }
    .refused { animation: none; outline: 2px solid var(--brass-bright); }
    .chip, #roll, .knob, .switchRow button[role="switch"] { transition: none; }
}

@media (max-width: 640px) {
    #hint { bottom: 225px; }
    #controls { flex-wrap: wrap; gap: 8px; }
    #edge button { padding: 6px 10px; font-size: 0.85rem; }
}
