@import url('https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@700;900&family=Grandstander:wght@700;800&family=Varela+Round&family=Cinzel:wght@700&family=MedievalSharp&family=Pirata+One&family=Almendra:wght@700&family=Caveat:wght@700&display=swap');
/* GROWLMASTER DICE (Maayan): one implementation, the dice page's and the table's (dice.js). Every rule under .gmdice.
   THIS FILE IS THE SOURCE (2026-10-20), as dice.js is: edited here, never built from anywhere else. */
.gmdice { --paper:#efe7d3; --sheet:#faf5e8; --edge:#cbb98f; --ink:#3a2c17; --ink2:#6f5c3c; --gold:#865309; }
  .gmdice, .gmdice * { box-sizing: border-box; }
  .gmdice { background: var(--paper); color: var(--ink); font-family: 'Grandstander', system-ui, sans-serif; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; }
  /* A watermark (Maayan): GrowlMaster's print logotype, large in the middle of the table,
     multiplied so its white disappears and only a soft trace of the outline shows. */
  .gmdice .watermark { position: fixed; left: calc((100% - var(--pw)) / 2); top: 45%; translate: -50% -50%; width: min(78vw, 640px); mix-blend-mode: multiply; opacity: .07; pointer-events: none; z-index: 0;  transition: left .25s ease, width .25s ease; }
  /* The table: dice lying on it; only the dice take taps. */
  .gmdice { --pw: 0px; }
  /* PRESENTED DICE FLOAT (Maayan): a die showing its number hovers up and down, very
     subtly, so we know it is off the table. Each die has its own pace and height, set
     once when the page loads; its mirror image moves the other way, its shadow stays. */
  .gmdice .tdie.floating .face { animation: bob var(--bob-d, 2.8s) ease-in-out var(--bob-delay, 0s) infinite alternate; }
  .gmdice .tdie.floating .reflwrap { animation: bobr var(--bob-d, 2.8s) ease-in-out var(--bob-delay, 0s) infinite alternate; }
  @keyframes bob { from { translate: 0 0; } to { translate: 0 calc(var(--bob-a, 5px) * -1); } }
  @keyframes bobr { from { translate: 0 0; } to { translate: 0 var(--bob-a, 5px); } }
  .gmdice .table { position: fixed; inset: 0 var(--pw) 0 0; pointer-events: none; z-index: 2; transition: right .25s ease; }
  .gmdice .tdie { position: absolute; left: 50%; top: 50%; width: 220px; height: 220px; margin: -110px 0 0 -110px; }
  .gmdice .tk { position: absolute; inset: 0; transition: scale .25s ease; }
  .gmdice .tdie .face { position: absolute; inset: 0; pointer-events: auto; cursor: grab; touch-action: none; }
  .gmdice .tdie .shade { position: absolute; inset: 0; pointer-events: none; }
  .gmdice .tdie svg, .gmdice .sdie svg { width: 100%; height: 100%; display: block; overflow: visible; }
  .gmdice .num { font-family: 'Varela Round', 'Nunito', system-ui, sans-serif; fill: #2b2b2b; }
  .gmdice .num-lit { fill: #ffffff42; opacity: .85; }   /* Maayan's value, tuned in the browser */
  /* The score: a box at the top, centred over the table (Maayan: "score is at the top of the screen in a box"). */
  .gmdice .score { position: fixed; left: calc((100% - var(--pw)) / 2); top: 14px; translate: -50% 0; z-index: 4; pointer-events: none;
    height: 46px; box-sizing: border-box; padding: 0 18px; display: flex; align-items: center; gap: 10px;
    border: 2px solid var(--edge); background: var(--sheet); color: var(--ink); box-shadow: 0 6px 18px rgba(0,0,0,.12);
    opacity: 0; scale: .8; transition: opacity .25s, scale .35s cubic-bezier(.2, 1.6, .4, 1), left .25s ease; }
  .gmdice .score.show { opacity: 1; scale: 1; }
  .gmdice .score span { font-family: 'Grandstander', system-ui, sans-serif; font-weight: 800; font-size: 16px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink2); }
  .gmdice .score b { font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 30px; line-height: 1; color: var(--ink); }
  /* You do the math: the sum on a slip of paper where the score shows, written by hand, line after line. */
  .gmdice .mathbox { position: fixed; left: calc((100% - var(--pw)) / 2); top: 14px; translate: -50% 0; z-index: 5; pointer-events: auto; transition: left .25s ease;
    display: flex; flex-direction: column; align-items: center; gap: 8px; }   /* the sum in it is GrowlMaster's math (/growlmaster/math) */
  .gmdice .mathbox[hidden] { display: none; }
  /* A phone: the top row is full, so the box sits just below it. */
  @media (max-width: 720px) { .gmdice .score { top: 70px; } }
  .gmdice .throw-text { position: fixed; top: 72px; left: 16px; right: calc(16px + var(--pw)); transition: right .25s ease, opacity .25s; text-align: center; opacity: 0; pointer-events: none; z-index: 3; }
  .gmdice .throw-text.show { opacity: 1; }
  .gmdice.is-onboarding .throw-text { display: none; }
  .gmdice .throw-text b { display: block; font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 28px; color: var(--ink); }
  .gmdice .throw-text span { display: block; font-size: 16px; color: var(--ink2); margin-top: 2px; }
  .gmdice .dbtn { border-radius: 0; box-shadow: none; text-transform: none; letter-spacing: normal; line-height: normal; min-width: 0; margin: 0; position: fixed; top: 14px; height: 46px; border: 2px solid var(--edge); background: var(--sheet); color: var(--ink); font: inherit; font-weight: 800; cursor: pointer; z-index: 3; }
  /* Roll and Clear, top left, only while there are dice on the table (Maayan). */
  /* Top right, left to right: Settings, then Save in GrowlMaster (Maayan). */
  .gmdice .rightbtns { position: fixed; top: 14px; right: calc(14px + var(--pw)); display: flex; gap: 8px; z-index: 3; transition: right .25s ease; }
  .gmdice .rightbtns .dbtn { position: static; }
  .gmdice .gmbtn { padding: 0 16px; font-size: 16px; color: #fff; background: var(--gold); border-color: var(--gold); text-decoration: none; display: inline-flex; align-items: center; }
  /* A phone: the top row fits on one line (Roll, Clear | GrowlMaster, 🔊). */
  @media (max-width: 520px) {
    .gmdice .gmbtn { padding: 0 10px; font-size: 13px; line-height: 1.05; max-width: 112px; text-align: center; }
  }
  /* THE SOUND LAB: a panel from the right, a grid of dice sounds (Maayan: "lets go crazy with this"). */
  .gmdice .sfxbtn { width: 46px; font-size: 20px; display: inline-flex; align-items: center; justify-content: center; }
  .gmdice .sfxpanel { position: fixed; top: 0; right: 0; bottom: 0; width: min(88vw, 360px); z-index: 6; background: var(--sheet); border-left: 2px solid var(--edge); box-shadow: -10px 0 30px rgba(0,0,0,.18); overflow: hidden; translate: 100% 0; transition: translate .25s ease; }
  /* TWO PAGES (Maayan): the settings, and a second page that slides in from the right for
     the colours, the sounds or the table's background; ‹ slides it back. */
  /* The panel and its track are laid out left to right in every language, so the track starts at the panel's left and
     slides the same way; each page reads in the page's own direction (:dir() follows the dir attribute, not the CSS
     direction). In Hebrew the track sat from the right and slid into nothing: a blank page (Maayan, 2026-10-20). */
  .gmdice .sfxpanel { direction: ltr; }
  .gmdice .pages { display: flex; width: 300%; height: 100%; transition: translate .3s cubic-bezier(.3, .7, .2, 1); }
  .gmdice .page:dir(rtl) { direction: rtl; }
  .gmdice .pages.deep { translate: -33.3333% 0; }
  .gmdice .pages.deeper { translate: -66.6667% 0; }
  .gmdice #gd-subTitle { font-size: 21px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .gmdice .page { width: 33.3333%; height: 100%; overflow-y: auto; padding: 0 16px 24px; box-sizing: border-box; }
  .gmdice .sub-pane[hidden] { display: none; }
  .gmdice .pback { width: 42px; height: 42px; border: 0; background: none; font-size: 34px; line-height: 1; cursor: pointer; color: var(--ink); padding: 0 0 4px; }
  /* A setting that opens its second page: what it is now, and › */
  .gmdice .go { display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px 12px; border: 2px solid var(--edge); background: #fff; font: inherit; font-weight: 800; font-size: 16px; color: var(--ink); cursor: pointer; text-align: left; }
  .gmdice .go:hover { border-color: var(--ink2); }
  .gmdice .go::after { content: '›'; margin-left: auto; font-size: 26px; line-height: 1; color: var(--ink2); }
  .gmdice .go:disabled { opacity: .45; cursor: default; }
  .gmdice .pvbar[hidden] { display: none; }
  .gmdice .giftask { pointer-events: auto; }
  .gmdice .giftask__btns { display: flex; gap: 10px; margin-top: 6px; }
  .gmdice .giftask__btns .dbtn { position: static; flex: 1; height: 48px; font-size: 17px; }
  .gmdice .giftask__go { background: #865309; color: #fff; border-color: #865309; }
  .gmdice .colllist { display: flex; flex-direction: column; gap: 8px; }
  .gmdice .collrow { display: flex; align-items: center; gap: 12px; }
  .gmdice .collpv { position: relative; width: 64px; height: 64px; flex: none; }
  .gmdice .collpv::before { display: none; }
  .gmdice .colln { flex: 1; display: flex; flex-direction: column; }
  .gmdice .colln b { font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 18px; color: #b5560c; }
  .gmdice .colln small { font-weight: 700; color: var(--ink2); }
  .gmdice .go i { font-style: normal; font-size: 24px; width: 32px; text-align: center; }
  .gmdice .go .thumb { width: 44px; height: 32px; border-radius: 4px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.15); flex: none; }
  .gmdice .phead { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: 10px; margin: 0 -16px 16px; padding: 12px 16px; background: var(--sheet); border-bottom: 2px solid var(--edge); }
  .gmdice .phead b { flex: 1; font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 24px; }
  /* the panel's tabs, Dice and Bag (GrowlMaster's one tabs control, app.css .gm-seg), under its title */
  .gmdice .ptabs { margin: -4px 0 16px; }
  .gmdice .ptab[hidden], .gmdice .ptabs[hidden] { display: none; }
  /* THE BAG TAB SCROLLS WITH THE PANEL (Maayan, 2026-10-24: "the bag grid can't be scrolled"): the bags' grid is the
     picker window's (studio.js gridHtml), where it is its own scroller with overscroll-behavior: contain. Here it never
     overflows, so it took every swipe and wheel and handed none on: the panel's page is the one scroller here. */
  .gmdice .ptab .gm-pick__grid { overflow: visible; overscroll-behavior: auto; }
  .gmdice .pclose { flex: none; width: 48px; height: 48px; border: 2px solid var(--edge); background: #fff; font-size: 30px; font-weight: 800; line-height: 1; cursor: pointer; color: var(--ink); }   /* a thumb's target (Maayan, 2026-10-24: the ✕ too tiny on phones) */
  .gmdice.panel-open .sfxbtn { visibility: hidden; }
  .gmdice .sfxpanel.open { translate: 0 0; }
  .gmdice .sfxhead b { display: block; font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 19px; }
  .gmdice .sfxhead span { display: block; font-size: 14px; color: var(--ink2); margin: 2px 0 12px; }
  /* The sounds: one clean list, the chosen one marked (a tap chooses it and plays it). */
  .gmdice .sndlist { display: flex; flex-direction: column; }
  .gmdice .snd { display: flex; align-items: center; gap: 14px; padding: 9px 6px; border: 0; border-bottom: 1px solid rgba(203,185,143,.55); background: none; font: inherit; font-weight: 800; font-size: 16px; color: var(--ink); cursor: pointer; text-align: left; }
  .gmdice .snd i { font-style: normal; font-size: 22px; width: 40px; height: 40px; display: grid; place-items: center; border-radius: 50%; background: #fff; box-shadow: 0 0 0 1px rgba(203,185,143,.7); flex: none; }
  .gmdice .snd:hover { background: rgba(255,255,255,.6); }
  .gmdice .snd.on { color: var(--gold); }
  /* Choosing player's ways: the one in use ticked */
  #gd-whoseg .snd i { background: none; box-shadow: 0 0 0 2px var(--edge); color: transparent; font-size: 20px; font-weight: 900; }
  #gd-whoseg .snd.on i { background: #865309; box-shadow: none; color: #fff; }
  #gd-whoseg .snd::after { content: none; display: none; }
  .gmdice .go:disabled { opacity: .45; cursor: default; }
  .gmdice .snd.on i { box-shadow: 0 0 0 2px var(--gold); }
  .gmdice .snd.on::after { content: '✓'; margin-left: auto; font-size: 18px; }
  /* The tables' backgrounds: their swatches, two across, the chosen one ringed. */
  .gmdice .tgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px 12px; }
  .gmdice .tpick { padding: 0; border: 0; background: none; font: inherit; font-size: 13.5px; font-weight: 800; color: var(--ink); cursor: pointer; text-align: center; }
  .gmdice .tpick .tswatch { display: block; height: 74px; border-radius: 8px; margin-bottom: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); transition: box-shadow .15s; }
  .gmdice .tpick.on .tswatch { box-shadow: 0 0 0 3px var(--sheet), 0 0 0 5px var(--gold); }
  .gmdice .tpick.on { color: var(--gold); }
  /* DICE SETTINGS, per die (Maayan): the die picked last, its colour on large sliders,
     its numbers' colour and carving, then its sound. */
  /* The die picked last, large and centred on a soft spotlight, still; its two colours below it (tap: the colours page). */
  .gmdice .pick { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 4px 0 8px; }
  .gmdice .pv { position: relative; flex: none; }
  .gmdice .pv svg { position: relative; width: 100%; height: 100%; display: block; overflow: visible; filter: drop-shadow(0 8px 8px rgba(0,0,0,.28)); }
  .gmdice .pv::before { content: ''; position: absolute; inset: -8% -8% -14%; border-radius: 50%; background: radial-gradient(circle at 50% 45%, rgba(255,255,255,.95), rgba(255,255,255,0) 68%); }
  .gmdice .pick .pv { width: 108px; height: 108px; }
  .gmdice .swatches { display: flex; justify-content: center; gap: 18px; }
  .gmdice .cswatch { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 0; border: 0; background: none; font: inherit; font-size: 11.5px; font-weight: 800; color: var(--ink2); cursor: pointer; }
  .gmdice .cswatch span { width: 38px; height: 38px; border-radius: 50%; border: 3px solid #fff; box-shadow: 0 0 0 2px var(--edge), 0 2px 5px rgba(0,0,0,.15); transition: transform .15s; }
  .gmdice .cswatch:hover span { transform: scale(1.08); }
  .gmdice .swatches.big { gap: 34px; margin: 6px 0 4px; }
  .gmdice .swatches.big .cswatch { font-size: 17px; gap: 8px; }
  .gmdice .swatches.big .cswatch span { width: 72px; height: 72px; border-width: 5px; }
  .gmdice .swatches.big .cswatch.on span { box-shadow: 0 0 0 5px #111, 0 2px 6px rgba(0,0,0,.2); }
  .gmdice .bigpv { width: 160px; height: 160px; margin: 0 auto; }
  .gmdice .ptop { position: sticky; top: 0; z-index: 2; margin: 0 -16px 16px; background: var(--sheet); }
  .gmdice .ptop .phead { position: static; margin: 0; }
  .gmdice .pvbar.bg .pv::before { display: none; }
  .gmdice .sec .sndico { vertical-align: -3px; margin-left: 6px; }
  .gmdice .pvbar { padding: 12px 0; border-bottom: 2px solid var(--edge); background: var(--sheet); }
  .gmdice .tcustom .tswatch { position: relative; }
  .gmdice .tcustom .tswatch i { position: absolute; right: 8px; bottom: 8px; width: 26px; height: 26px; border-radius: 50%; border: 2px solid #fff; background: conic-gradient(hsl(0 62% 50%), hsl(60 62% 50%), hsl(120 62% 50%), hsl(180 62% 50%), hsl(240 62% 50%), hsl(300 62% 50%), hsl(360 62% 50%)); }
  .gmdice .sec { font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 19px; margin: 6px 0 10px; }
  .gmdice .swrow { display: flex; flex-wrap: wrap; gap: 10px; margin: 6px 0 16px; }
  .gmdice .sw { width: 44px; height: 44px; flex: none; padding: 0; border-radius: 50%; border: 3px solid #fff; box-shadow: 0 0 0 2px var(--edge); cursor: pointer; transition: box-shadow .15s, scale .15s; }
  .gmdice .sw.on { box-shadow: 0 0 0 3px #2a2216; }
  .gmdice .sw:hover { scale: 1.06; }
  .gmdice .swadd { display: grid; place-items: center; background: #efe4cc; color: #b9a67c; }
  /* a new swatch grows in where + was, and + moves on */
  .gmdice .sw.new { animation: gmd-swin .45s cubic-bezier(.2, 1.5, .4, 1); }
  @keyframes gmd-swin { 0% { width: 0; margin-right: -10px; scale: 0; } 60% { width: 44px; margin-right: 0; } 100% { scale: 1; } }
  @keyframes gmd-pop { from { scale: .85; opacity: 0; } to { scale: 1; opacity: 1; } }
  /* the secret settings */
  .gmdice .gosecret i { font-style: normal; width: 34px; text-align: center; color: #b5560c; }
  .gmdice .fxrow { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 6px; }
  .gmdice .fxc { min-width: 44px; height: 44px; padding: 0 12px; border: 2px solid var(--edge); background: var(--sheet); color: var(--ink); font: inherit; font-weight: 800; font-size: 15px; cursor: pointer; }
  .gmdice .fxc[style*="background:#"] { width: 44px; padding: 0; border-radius: 50%; border: 3px solid #fff; box-shadow: 0 0 0 2px var(--edge); }
  .gmdice .fxc[style*="linear-gradient"] { color: #1d1408; text-shadow: 0 1px 0 #fff8; }
  .gmdice .fxc.on { border-color: #2a2216; box-shadow: 0 0 0 1px #2a2216; }
  .gmdice .fxc--go { color: #865309; }
  .gmdice .third-pane[hidden] { display: none; }
  .gmdice .signs .fxc { font-size: 22px; }
  .gmdice #gd-sign .fxc[data-v]:not([data-v=""]) { font-size: 22px; }   /* the chosen sign, as big as on the signs' page */
  .gmdice .fxc[style*="background:#"].on { box-shadow: 0 0 0 3px #2a2216; }
  /* a gift: its die, its name, Add to my dice */
  .gmdice .giftbox { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; background: rgba(20,14,8,.55); pointer-events: auto; }
  .gmdice .giftcard { display: flex; flex-direction: column; align-items: center; gap: 8px; width: min(88vw, 340px); padding: 22px 22px 20px; background: var(--sheet); border: 3px solid #c98f1c; box-shadow: 0 16px 40px rgba(0,0,0,.35); animation: gmd-pop .5s cubic-bezier(.2, 1.7, .4, 1); text-align: center; }
  .gmdice .giftcard__face { display: block; width: 72px; height: 72px; border-radius: 50%; overflow: hidden; }
  .gmdice .giftcard__face > * { width: 100%; height: 100%; display: block; }
  .gmdice .giftbox.is-watching { pointer-events: none; }
  .gmdice .giftcard__t { font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 24px; color: var(--ink); }
  .gmdice .giftcard__die { width: 190px; height: 190px; }
  /* a bag won (showGift): the bag large, where a die's gift shows its die */
  .gmdice .giftcard__bag { display: grid; place-items: center; width: 190px; height: 190px; }
  .gmdice .giftcard__bagimg { width: 200px; filter: drop-shadow(0 8px 14px rgba(0,0,0,.35)); }
  .gmdice .giftcard__name { font-weight: 800; font-size: 22px; color: #b5560c; }
  .gmdice .giftcard small, .gmdice .pvname small { font-weight: 700; color: var(--ink2); }
  .gmdice .giftcard__go { width: 100%; height: 52px; margin-top: 8px; border: 0; background: #865309; color: #fff; font: inherit; font-weight: 800; font-size: 19px; cursor: pointer; }
  .gmdice .pvname { display: flex; flex-direction: column; align-items: center; margin-top: 6px; }
  .gmdice .pvname b { font-family: 'Frank Ruhl Libre', serif; font-weight: 900; font-size: 28px; color: #b5560c; line-height: 1.1; }
  .gmdice .pvname small { font-weight: 800; font-size: 14px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink2); }
  .gmdice .toggle[hidden], .gmdice .go[hidden] { display: none; }
  .gmdice .go:disabled > * , .gmdice .go:disabled { cursor: default; }
  .gmdice .secsub { margin: -4px 0 12px; font-weight: 700; font-size: 14px; line-height: 1.4; color: var(--ink2); }
  .gmdice .sub { font-weight: 800; font-size: 14px; color: var(--ink2); margin: 14px 0 8px; }
  .gmdice .secrow { display: flex; align-items: center; justify-content: space-between; }
  .gmdice .secrow .toggle { margin: 0; }
  .gmdice .sep { border: 0; border-top: 2px solid var(--edge); margin: 18px 0 12px; }
  .gmdice .slider, .gm-sliders .slider { position: relative; height: 34px; border-radius: 17px; margin-bottom: 10px; border: 2px solid var(--edge); }
  /* A slider is its colour scale, drawn left to right (its gradient): it runs left to right in every language. */
  .gmdice .slider input, .gm-sliders .slider input { direction: ltr; }
  .gmdice .slider.size, .gm-sliders .slider.size { background: linear-gradient(90deg, #efe4cc, #cbb98f); }
  .gmdice .slider.hue, .gm-sliders .slider.hue { background: linear-gradient(90deg, hsl(0 62% 45%), hsl(60 62% 45%), hsl(120 62% 45%), hsl(180 62% 45%), hsl(240 62% 45%), hsl(300 62% 45%), hsl(360 62% 45%)); }
  .gmdice .slider input, .gm-sliders .slider input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; }
  .gmdice .slider input::-webkit-slider-thumb, .gm-sliders .slider input::-webkit-slider-thumb { -webkit-appearance: none; width: 30px; height: 30px; border-radius: 50%; background: #fff; border: 3px solid var(--ink); box-shadow: 0 2px 6px rgba(0,0,0,.3); }
  .gmdice .slider input::-moz-range-thumb, .gm-sliders .slider input::-moz-range-thumb { width: 26px; height: 26px; border-radius: 50%; background: #fff; border: 3px solid var(--ink); }
  .gmdice .toggle { display: flex; align-items: center; gap: 10px; margin-top: 14px; font-weight: 800; font-size: 15px; cursor: pointer; }
  .gmdice .toggle em { font-style: normal; } .gmdice .toggle small { display: block; font-weight: 700; color: var(--ink2); font-size: 12.5px; }
  .gmdice .toggle input { display: none; }
  .gmdice .toggle span { width: 46px; height: 26px; border-radius: 13px; background: #cbbf9f; position: relative; flex: none; transition: background .2s; }
  .gmdice .toggle span::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; transition: left .2s; }
  .gmdice .toggle input:checked + span { background: var(--gold); }
  .gmdice .toggle input:checked + span::after { left: 23px; }
  /* numbers: their colour, and whether they're carved */
  .gmdice .num { fill: var(--ink, #2b2b2b) !important; }
  .gmdice .no-carve .num-lit { display: none; }
  .gmdice .no-carve .num { filter: none !important; }
  /* THE TABLES (Maayan: "the table we have is 'default'… another table made out of
     something reflective, then the dice will have a nice reflective effect"). */
  .gmdice { transition: background .3s; }
  .gmdice.t-felt { background-image: radial-gradient(ellipse at 50% 35%, rgba(255,255,255,.10), rgba(0,0,0,.28) 85%), repeating-linear-gradient(45deg, rgba(255,255,255,.025) 0 2px, transparent 2px 4px); }
  .gmdice.t-dark .throw-text b { color: #f4ecdc; } .gmdice.t-dark .throw-text span { color: #d8cbb3; }
  .gmdice.t-dark .watermark { mix-blend-mode: screen; opacity: .04; filter: invert(1); }
  /* A reflective table: each die has its mirror image under it, fading into the table.
     It mirrors at the point where the die touches the table (--rb, % down the die):
     the bottom of a die standing to face us, but the middle of a d20 lying on the table,
     whose contact is under its centre (Maayan: "the touch point is in its center").
     The wrapper grows with the die (the same scale, from the same middle), so the
     reflection grows with it when it rises to show its number, at the same strength. */
  .gmdice .reflwrap { position: absolute; inset: 0; pointer-events: none; display: none; }
  .gmdice .refl { position: absolute; inset: 0; transform: scaleY(-1); transform-origin: 50% calc(var(--rb, 92) * 1%); opacity: .38; filter: blur(2.5px);
    -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--rb, 92) * 1% - var(--rf, 48) * 1%), rgba(0,0,0,.5) calc(var(--rb, 92) * 1% - var(--rf, 48) * .5%), #000 calc(var(--rb, 92) * 1% - 6%), #000 calc(var(--rb, 92) * 1%), transparent calc(var(--rb, 92) * 1% + .5%));
            mask-image: linear-gradient(to bottom, transparent calc(var(--rb, 92) * 1% - var(--rf, 48) * 1%), rgba(0,0,0,.5) calc(var(--rb, 92) * 1% - var(--rf, 48) * .5%), #000 calc(var(--rb, 92) * 1% - 6%), #000 calc(var(--rb, 92) * 1%), transparent calc(var(--rb, 92) * 1% + .5%)); }
  .gmdice.t-reflect .reflwrap { display: block; }
  .gmdice.t-reflect .tdie .shade { opacity: .45; }
  /* THE SHELF (Maayan; his sketch, 2026-11-07: dice-shelf-sketch-552.jpg): a wooden board, its rounded tray of felt the
     dice sit on in one row, and under it ⊕, Customise and ✕. Edge to edge on a phone; on a wider screen a board of its
     own. As tall as what is on it. */
  .gmdice .shelf { position: fixed; left: calc((100% - var(--pw)) / 2); translate: -50% 0; bottom: 0; width: min(100% - var(--pw), 720px); transition: left .25s ease, width .25s ease; height: auto; z-index: 1;
    background:
      repeating-linear-gradient(92deg, rgba(0,0,0,.05) 0 2px, transparent 2px 9px, rgba(255,255,255,.04) 9px 10px, transparent 10px 23px),
      linear-gradient(180deg, #8a5a2b 0, #6e4520 12px, #7b4e25 40%, #5c3a1a 100%);
    box-shadow: 0 -6px 18px rgba(0,0,0,.25), inset 0 2px 0 rgba(255,255,255,.18); }
  @media (min-width: 721px) { .gmdice .shelf { bottom: 24px; border-radius: 10px; } }
  /* Laid out left to right in every language, like the settings' track: ⊕ at the left, the hole at the right, and the
     felt scrolls from its start (in Hebrew a sideways scroller starts at its right end: Maayan, 2026-10-20). */
  .gmdice .shelf { direction: ltr; }
  /* THE TRAY: the felt, rounded, sunk into the wood, the dice's row in it and the hole at its right end (dice.js HOLE_W,
     --hw: a die's place), which stays there as the row scrolls. The felt is the player's bag's (dice.js felt, from
     sprites.js diceBag.felt): its two tones, changing smoothly with the player (registered colours, so they ease). */
  @property --felt-a { syntax: '<color>'; inherits: true; initial-value: #c4a97e; }
  @property --felt-b { syntax: '<color>'; inherits: true; initial-value: #8a704a; }
  .gmdice .tray { position: relative; height: calc(var(--sd, 78px) + 24px); margin: 12px 14px 0; border-radius: 18px;
    background: radial-gradient(ellipse at 50% 30%, var(--felt-a), var(--felt-b) 72%), var(--felt-b); transition: --felt-a .6s ease, --felt-b .6s ease;
    box-shadow: inset 0 3px 8px rgba(0,0,0,.55), inset 0 -1px 0 rgba(255,255,255,.08), 0 1px 0 rgba(255,236,200,.12); }
  /* the felt, the row's scroller: the tray but the hole's place, never under the hole (Maayan, 2026-11-07: "can't scroll
     behind hole. hole is fixed and prominent"); a swipe anywhere on it scrolls the row. Scrolling, its end fades out
     (--fade), and past its last die it scrolls on that far, so the last one ends clear of it, never cut by an edge. */
  .gmdice .felt { --fade: 28px; position: absolute; inset: 0 var(--hw, 82px) 0 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain; touch-action: pan-x; border-radius: 18px 0 0 18px; }
  /* UNDER THE TRAY (his sketch): ⊕ at the start, Customise in the middle, ✕ at the end; each a parchment tile with a
     carved edge, pressed down when tapped; ⊕ and ✕ round, Customise a pill with its palette and its word */
  .gmdice .shelfbar { --bs: 38px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px 12px; }
  .gmdice .shelfbtn { position: relative; width: var(--bs); height: var(--bs); display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 0 0 3px; border: 0; border-radius: 50%; cursor: pointer; color: #4a2f14;
    font: inherit; font-weight: 800; font-size: 16px; white-space: nowrap;
    background: radial-gradient(circle at 35% 25%, #fbf3df, #ead9b4 70%, #dcc597); box-shadow: inset 0 -3px 0 #a87b45, inset 0 1px 0 rgba(255,255,255,.8), 0 2px 3px rgba(0,0,0,.45);
    transition: box-shadow .08s, padding .08s, filter .15s; -webkit-tap-highlight-color: transparent; }
  .gmdice .shelfbtn.is-pill { width: auto; padding: 0 18px 3px; border-radius: 999px; }
  /* the app's tap floor (AppStyles: body.jim-app :is(button, …) { min-height: var(--app-tap) }, specificity 0,2,2) never
     here, out-specified for these alone: the shelf's buttons are the same size on the dice page and at a table */
  body.jim-app .gmdice button.shelfbtn { min-width: 0; min-height: 0; }
  .gmdice .shelfbtn:hover { filter: brightness(1.05); }
  .gmdice .shelfbtn:focus-visible { outline: 2px solid #f5c66b; outline-offset: 2px; }
  /* pressed: its carved edge gone, the face sunk in (the button keeps its size) */
  .gmdice .shelfbtn:active { padding-top: 3px; padding-bottom: 0; box-shadow: inset 0 3px 5px rgba(90,55,20,.4), 0 0 1px rgba(0,0,0,.5); }
  .gmdice .shelfbtn.is-full { opacity: .45; cursor: not-allowed; padding-top: 3px; padding-bottom: 0; box-shadow: inset 0 3px 5px rgba(90,55,20,.3); filter: saturate(.6); }
  .gmdice .shelfbtn svg { flex: none; width: 22px; height: 22px; filter: drop-shadow(0 1px 0 rgba(255,255,255,.7)); }
  /* Clear and X in the one button (dice.js syncX), one turning away as the other turns in */
  .gmdice #gd-xbtn { display: inline-grid; place-items: center; }
  .gmdice #gd-xbtn svg { grid-area: 1 / 1; transition: opacity .2s ease, rotate .3s cubic-bezier(.3, 1.5, .5, 1), scale .3s cubic-bezier(.3, 1.5, .5, 1); }
  .gmdice #gd-xbtn .ic-x, .gmdice #gd-xbtn.is-x .ic-clear { opacity: 0; rotate: -90deg; scale: .4; }
  .gmdice #gd-xbtn.is-x .ic-x { opacity: 1; rotate: 0deg; scale: 1; }
  .gmdice .sdie { position: absolute; top: 50%; width: var(--sd, 78px); height: var(--sd, 78px); margin: calc(var(--sd, 78px) / -2) 0 0 calc(var(--sd, 78px) / -2); cursor: grab; touch-action: none; filter: drop-shadow(2px 4px 3px rgba(0,0,0,.45)); }
  /* The row scrolls sideways when the dice don't all fit (a phone); its track holds them. */
  .gmdice .felt::-webkit-scrollbar { display: none; }
  .gmdice .felttrack { position: relative; height: 100%; min-width: 100%; }
  .gmdice .shelf.scrolls .felttrack { min-width: 0; }
  .gmdice .shelf.scrolls .felt { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--fade)), transparent); mask-image: linear-gradient(to right, #000 calc(100% - var(--fade)), transparent); }
  .gmdice .shelf.scrolls .felttrack::after { content: ''; position: absolute; left: 100%; top: 0; width: var(--fade); height: 1px; }
  .gmdice .felttrack .sdie { z-index: 3; }
  /* The hole, in its own place at the tray's right end (dice.js HOLE_W), smaller than a die's place (Maayan, 2026-11-07:
     "make it small like it was before"); a groove parts it from the row. */
  .gmdice .tray::after { content: ''; position: absolute; top: 10px; bottom: 10px; right: var(--hw, 82px); width: 2px; border-radius: 1px; background: rgba(0,0,0,.28); box-shadow: 1px 0 0 rgba(255,255,255,.12); pointer-events: none; }
  .gmdice .shelfhole { --c: calc(var(--sd, 78px) * 0.72); z-index: 2; position: absolute; top: 50%; right: calc((var(--hw, 82px) - var(--c)) / 2); width: var(--c); height: var(--c); margin-top: calc(var(--c) / -2); border-radius: 50%; box-sizing: border-box; }
  /* THE FIRST TIME (onboard.js): a slim band, its step's words, never taking a tap, never over what its step needs:
     at the top of the table while dice move (data-at="top"); just above the shelf for the math and Customise
     ("shelf", --onb-above: the shelf's height there), its arrow down at Customise, which pulses; in the center of the
     table still seen beside the open settings ("center", --onb-x and --onb-w: onboard.js). A new step bounces in
     (Maayan, 2026-11-07: "onboarding tips show with a bounce effect"); the last done, it fades. Over a table only
     while the dice are out. Fits a phone's width with room at its sides. Each is the math's note and handwriting
     (math.css .gmmath .msum), its words written in as the math's sum is (.mrow), a sentence's length, after the bounce
     (onboard.js WRITE_AT, WRITE). */
  .gmdice .onboard { position: absolute; z-index: 65; left: 50%; top: 12px; translate: -50% 0; width: min(100% - 24px, 420px); box-sizing: border-box; text-align: center; pointer-events: none; transition: opacity .35s ease; }
  .gmdice .onboard .msum { width: 100%; min-width: 0; box-sizing: border-box; }
  .gmdice .onboard .mrow { padding: 0; animation-delay: .25s; animation-duration: .8s; }
  .gmdice .onboard[data-at="shelf"] { top: auto; bottom: calc(var(--onb-above, 0px) + 14px); }
  .gmdice .onboard[data-at="center"] { top: 50%; left: var(--onb-x, 50%); translate: -50% -50%; width: min(var(--onb-w, 100%) - 24px, 420px); }
  .gmdice .onboard.is-pointing::after { content: ''; position: absolute; left: 50%; bottom: -9px; width: 16px; height: 16px; translate: -50% 0; rotate: 45deg;
    background: var(--sheet); box-shadow: 2px 2px 0 0 var(--edge); }
  .gmdice .onboard.is-new { animation: gmd-bounce .6s ease-out; }
  @keyframes gmd-bounce { 0% { scale: .5; opacity: 0; } 45% { scale: 1.12; opacity: 1; } 65% { scale: .94; } 82% { scale: 1.03; } 100% { scale: 1; } }
  .gmdice .onboard.is-gone { opacity: 0; }
  .gmdice .onboard__say { margin: 0; font-size: 30px; }
  .gmdice--layer:not(.is-on) .onboard { display: none; }
  .gmdice .shelfbtn.is-guided { animation: gmd-guide 1.2s ease-in-out infinite; }
  @keyframes gmd-guide { 0%, 100% { box-shadow: inset 0 -3px 0 #a87b45, 0 0 0 0 rgba(245,198,107,.9); } 50% { box-shadow: inset 0 -3px 0 #a87b45, 0 0 0 8px rgba(245,198,107,0); } }
  /* A DIE UP, AND OVER THE TRASH (dice.js lift, trashHint): no reflection while it is held; held over the hole a moment,
     a little see-through, its word in a box above it, the box itself never see-through */
  .gmdice .tdie.is-held .reflwrap { display: none; }
  .gmdice.trash-hint .tdie.is-held { opacity: .55; transition: opacity .2s ease; }
  .gmdice .trashtip { position: absolute; z-index: 40; translate: -50% -100%; padding: 6px 14px; border-radius: 10px; background: #fbf3df; color: #3a2c17;
    font-weight: 800; font-size: 15px; white-space: nowrap; pointer-events: none; box-shadow: 0 0 0 2px #3a2c17, 0 6px 16px rgba(0,0,0,.25); }
  .gmdice .trashtip[hidden] { display: none; }
  .gmdice .shelfhole { background: radial-gradient(circle at 50% 42%, #000 0 48%, #120a1c 62%, #2a1840 78%, rgba(0,0,0,0) 80%);
    box-shadow: inset 0 6px 10px rgba(0,0,0,.9), 0 1px 0 rgba(255,255,255,.12); }
  .gmdice .undohole { position: absolute; right: 14px; top: -44px; padding: 8px 16px; border: 2px solid var(--edge); background: var(--sheet); color: var(--ink); font: inherit; font-weight: 800; font-size: 15px; cursor: pointer;
    box-shadow: 0 6px 16px rgba(0,0,0,.18); opacity: 0; translate: 0 6px; pointer-events: none; transition: opacity .2s, translate .2s; }
  .gmdice .undohole.show { opacity: 1; translate: 0 0; pointer-events: auto; }
  .gmdice .addmodal { position: fixed; inset: 0; z-index: 8; display: grid; place-items: center; padding: 16px; background: rgba(20,12,6,.5); }
  .gmdice .addmodal[hidden] { display: none; }
  .gmdice .addbox { width: min(460px, 100%); background: var(--sheet); border: 2px solid var(--edge); box-shadow: 0 20px 50px rgba(0,0,0,.35); padding: 0 14px 18px; }
  .gmdice .addgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .gmdice .addpick { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 4px 0; border: 0; background: none; font: inherit; color: var(--ink); cursor: pointer; }
  .gmdice .addpick .pv { width: 96px; height: 96px; filter: drop-shadow(2px 5px 4px rgba(0,0,0,.35)); transition: transform .15s; }
  .gmdice .addpick .pv svg { width: 100%; height: 100%; overflow: visible; }
  .gmdice .addpick:hover .pv { transform: scale(1.08); }
  /* The paywall (Maayan): what it unlocks, the subscription, Subscribe. */
  .gmdice .linkbtn { display: block; margin: 22px auto 0; padding: 4px; border: 0; background: none; font: inherit; font-weight: 800; font-size: 14px; color: var(--ink2); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
  .gmdice .linkbtn:hover { color: var(--ink); }
  .gmdice .payline { margin: 0 0 16px; font-size: 16px; font-weight: 700; color: var(--ink2); line-height: 1.4; }
  .gmdice .paybtn { display: block; padding: 13px; text-align: center; background: var(--gold); color: #fff; font-weight: 800; font-size: 18px; text-decoration: none; }
  .gmdice .paybtn:hover { filter: brightness(1.08); }
  .gmdice .addpick b { font-size: 16px; color: #3a2c17; }   /* the page's ink: the button carries the die's yellow --ink */
/* OVER A GROWLMASTER TABLE (mode "layer", app.js): only the dice and the shelf, over the board and under the app's
   windows and panels; the page's top row, score, words, watermark and settings are not here. The shelf rises from
   the bottom when the dice are on, and goes down again with them. */
.gmdice.gmdice--layer { position: absolute; inset: 0; z-index: 30; pointer-events: none; background: none; overflow: hidden; }
/* pinned to the part of its board on the screen (dice.js opts.frame): a phone's page scrolls, the shelf never with it */
.gmdice.gmdice--layer.gmdice--pinned { position: fixed; inset: auto; left: var(--gd-l, 0px); top: var(--gd-t, 0px); width: var(--gd-w, 100%); height: var(--gd-h, 100%); }
.gmdice--layer .table, .gmdice--layer .shelf, .gmdice--layer .mathbox { position: absolute; }
.gmdice--layer .watermark, .gmdice--layer .score, .gmdice--layer .throw-text, .gmdice--layer .rightbtns { display: none; }
/* The dice settings over GrowlMaster (the app's cog): from the left, over the board, under the app's windows. */
.gmdice--layer .sfxpanel { position: absolute; left: 0; right: auto; translate: -100% 0; border-left: 0; border-right: 2px solid var(--edge); box-shadow: 10px 0 30px rgba(0,0,0,.18); pointer-events: auto; z-index: 40; }
.gmdice--layer .sfxpanel.open { translate: 0 0; }
.gmdice--free .sfxpanel { display: none; }
.gmdice--layer .shelf { pointer-events: auto; bottom: 0; transition: translate .35s cubic-bezier(.3, .7, .2, 1), left .25s ease, width .25s ease; }
@media (min-width: 721px) { .gmdice--layer .shelf { bottom: 16px; } }
.gmdice--layer:not(.is-on) .shelf { translate: -50% calc(100% + 30px); }
.gmdice--layer .table { transition: opacity .25s; }
.gmdice--layer:not(.is-on) .table { opacity: 0; visibility: hidden; }
.gmdice--layer .addmodal { pointer-events: auto; }
.gmdice--layer.is-holding { pointer-events: auto; cursor: grabbing; }
/* AT THE TOP OF A PAGE (mode "section", /growlmaster/dice): the page's table in its own box, the words below it. */
.gmdice.gmdice--section { position: relative; height: calc(100svh - var(--gmdice-top, 0px)); min-height: 520px; overflow: hidden; }
.gmdice--section .watermark, .gmdice--section .table, .gmdice--section .score, .gmdice--section .mathbox, .gmdice--section .throw-text,
.gmdice--section .rightbtns, .gmdice--section .shelf { position: absolute; }
/* A subscriber's: no watermark, and nothing to offer about it. */
.gmdice.no-watermark .watermark, .gmdice.no-watermark [data-paywall] { display: none; }
.gmdice .sfxbtn svg { display: block; width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor; }
.gmdice .sfxpanel:not(.open) { box-shadow: none; visibility: hidden; transition: translate .25s ease, visibility 0s .25s; }
/* On a phone (dice.js panel: no room to push, at 700px and under) the settings are the whole screen, on the page and
   over a table alike (Maayan, 2026-10-20: it covered only part of it). */
@media (max-width: 700px) { .gmdice .sfxpanel { width: 100%; border-left: 0; border-right: 0; } }
/* Over a table on a phone the board is taller than the screen: the settings are the screen's, from under the app's
   header (or the top, once it has scrolled away; dice.js keeps --gd-top), never the board's height. */
@media (max-width: 700px) { .gmdice--layer .sfxpanel { position: fixed; top: var(--gd-top, 0px); bottom: 0; } }
/* FREE DICE over a page's words (mode "free"): only the dice; the shelf and its windows are not there. */
.gmdice--free .shelf, .gmdice--free .addmodal, .gmdice--free .undohole { display: none; }
/* THE d20 IN THE APP'S PLACE FOR IT (dock): drawn as itself, the size of the place, a small bounce as it sits down. */
.gmdice-dock .dockdie { width: 100%; height: 100%; scale: var(--dock-scale, 1); cursor: grab; touch-action: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.3)); }
.gmdice-dock .dockdie svg { width: 100%; height: 100%; display: block; overflow: visible; }
.gmdice-dock .dockdie .num { fill: var(--ink, #2b2b2b) !important; }
.gmdice-dock .dockdie.no-carve .num-lit { display: none; }
.gmdice-dock.dock-in .dockdie { animation: dockbounce .45s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes dockbounce { 0% { scale: calc(var(--dock-scale, 1) * .82); } 60% { scale: calc(var(--dock-scale, 1) * 1.08); } 100% { scale: var(--dock-scale, 1); } }

/* A phone: the top row is full, so the sum sits just below it. */
@media (max-width: 720px) { .gmdice .mathbox { top: 70px; } .gmdice--layer .mathbox { top: 10px; } }
.gmdice--layer:not(.is-on) .mathbox { display: none; }
/* TWELVE AT MOST (dice.js MAX_SHELF): the + stays, dimmed; a tap says why, in a note above it. */
.gmdice .shelfnote { position: absolute; left: 12px; bottom: calc(100% + 8px); max-width: calc(100% - 24px); padding: 8px 12px; border-radius: 10px; background: #2a1b3a; color: #f3e7ff; font: 700 14px/1.3 'Grandstander', system-ui, sans-serif; box-shadow: 0 6px 16px rgba(0,0,0,.3); opacity: 0; translate: 0 6px; transition: opacity .2s, translate .2s; pointer-events: none; z-index: 3; }
.gmdice .shelfnote.show { opacity: 1; translate: 0 0; }
/* THE ONE COLOUR PICKER (dice.js #gd-picker): moved into the page that shows it; its sliders and swatches as before. */
.gmdice .pickslot:empty { display: none; }
/* A colour in the secret settings: one swatch, pressed, the picker (no fixed swatches beside it). */
.gmdice .fxc.fxsw { width: 44px; padding: 0; border-radius: 50%; font-size: 20px; color: var(--ink2); }
/* No gradient: an empty swatch, crossed. */
.gmdice .nograd { background: #fff !important; background-image: linear-gradient(135deg, transparent 46%, #c0392b 46%, #c0392b 54%, transparent 54%) !important; }
/* THE TABLE FULL (dice.js TABLE_MAX): the shelf's dice stay, dimmed; a touch says why. */
.gmdice .felt.table-full .sdie { opacity: .4; filter: grayscale(.6) drop-shadow(2px 4px 3px rgba(0,0,0,.25)); cursor: not-allowed; }
/* The die's colour, its two tabs at the top: Solid colour | Gradient (dice.js cTab). */
.gmdice .ctabs { display: flex; margin: 0 0 14px; border: 2px solid var(--edge); background: var(--sheet); }
.gmdice .ctabs[hidden] { display: none; }
.gmdice .ctabs button { flex: 1; min-height: 44px; padding: 8px 10px; border: 0; background: none; font: inherit; font-weight: 800; font-size: 15px; color: var(--ink2); cursor: pointer; }
.gmdice .ctabs button + button { border-inline-start: 2px solid var(--edge); }
.gmdice .ctabs button.on { background: var(--ink); color: var(--sheet); }
.gmdice .gradpane[hidden], .gmdice .pickslot[hidden] { display: none; }
/* THE GRADIENT'S COLOURS ROW (Maayan: "colors row has a black 5% opacity background with padding and rounded corners"):
   its colours where they sit along it, the one chosen ringed; dragged along, or out to take one away. */
.gmdice .gstops { position: relative; height: 56px; margin: 4px 0 8px; padding: 0 26px; border-radius: 14px; background: rgba(0,0,0,.05); touch-action: none; outline: none; box-sizing: border-box; }
.gmdice .gstops .gline { position: absolute; left: 26px; right: 26px; top: 50%; height: 10px; margin-top: -5px; border-radius: 5px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.gmdice .gstops .gstop { position: absolute; top: 50%; width: 38px; height: 38px; margin: -19px 0 0 -19px; padding: 0; border: 3px solid #fff; border-radius: 50%; box-shadow: 0 0 0 2px var(--edge), 0 2px 5px rgba(0,0,0,.25); cursor: grab; touch-action: none; transition: transform .12s, opacity .12s; }
.gmdice .gstops .gstop.on { box-shadow: 0 0 0 3px var(--ink), 0 2px 6px rgba(0,0,0,.3); transform: scale(1.12); z-index: 1; }
.gmdice .gstops .gstop.out { opacity: .35; transform: scale(.8); }
.gmdice .ghint { margin: 0 0 10px; }
.gmdice .gtools { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.gmdice .gtool { min-height: 40px; padding: 0 12px; border: 2px solid var(--edge); background: #fff; font: inherit; font-weight: 800; font-size: 14px; color: var(--ink); cursor: pointer; }
.gmdice .gtool:disabled { opacity: .45; cursor: default; }
.gmdice .gsave { margin-top: 10px; justify-content: center; }
.gmdice .gsave::after { content: none; }
/* THE PLAYER AND THEIR SACK OF DICE (public/growlmaster/diceBadge.js, Maayan's sketch): one badge over a table and over the
   dice page, at every width, carried by a finger or the mouse (by the portrait or the sack). The portrait in a thick
   double ring; the tall sack, about two thirds of its height, its top over the ring's lower right, hanging below it.
   Over the dice (they lie under it), under windows. Its colours are GrowlMaster's own, here as they are in the app. */
/* all of them in one layer over the window (or in the dice page's hero, .is-in), only the badges themselves taking a touch */
.gm-dicebadges { position: fixed; inset: 0; z-index: 35; pointer-events: none; }
.gm-dicebadges.is-in { position: absolute; }
/* a bit larger and the bag a bit more to the right (Maayan, 2026-10-21: "so not too over the image, so we can see it,
   but a bit so we know it's his"): it only just overlaps the ring */
.gm-dicebadge { --ink-b: var(--ink, #3a2c17); --sheet-b: var(--sheet, #faf5e8); position: absolute; left: 0; top: 0; width: 98px; height: 98px; pointer-events: auto; touch-action: none; -webkit-user-select: none; user-select: none; cursor: grab; will-change: translate; }
.gm-dicebadge.is-held { cursor: grabbing; }
.gm-dicebadge button { -webkit-tap-highlight-color: transparent; }
.gm-dicebadge__who { position: absolute; left: 8px; top: 8px; width: 82px; height: 82px; padding: 0; border: 3px solid var(--ink-b); border-radius: 50%; background: var(--ink-b); overflow: hidden; cursor: inherit;
  box-shadow: 0 0 0 4px var(--sheet-b), 0 0 0 6.5px var(--ink-b), 0 6px 14px rgba(0,0,0,.3); }
.gm-dicebadge__who .gm-avatar { width: 100%; height: 100%; display: grid; place-items: center; border-radius: 50%; overflow: hidden; background: #e7dcc0; border: 0; font-size: 34px; color: var(--ink-b); }
.gm-dicebadge__who .gm-avatar b { font-size: 34px; color: var(--ink-b); }
.gm-dicebadge__who .gm-avatar img { width: 100%; height: 100%; object-fit: cover; }
.gm-dicebadge__sack { position: absolute; left: 90px; top: 22px; width: 70px; height: 70px; padding: 0; border: 0; background: none; cursor: inherit; filter: drop-shadow(0 3px 4px rgba(0,0,0,.3)); transform-origin: 50% 20%; transition: transform .18s ease; }
.gm-dicebadge__bag { width: 100%; pointer-events: none; }
/* THEIR NAME (Maayan: "a small parchment with name, handwritten font… very close to profile and a bit overlapping it";
   "the dice-bag shouldn't be over the name"): a little tag under the portrait, over its ring's foot, over the bag too; the whole name, on two lines when long. */
.gm-dicebadge__name { position: absolute; left: 49px; top: 84px; translate: -50% 0; rotate: -2deg; width: max-content; max-width: 112px; box-sizing: border-box; z-index: 1;
  padding: 2px 9px 3px; white-space: normal; text-wrap: balance; overflow-wrap: break-word; pointer-events: none;   /* a long name on two lines, whole (Maayan: "no 3 dots. you may break it in two lines"), broken between its words (a nickname's three) */
  font: 700 14px/1.2 'Grandstander', system-ui, sans-serif; color: #3a2a14; text-align: center;
  background: linear-gradient(#fbf3dc, #eedfb9); border: 1.5px solid #b48f55; border-radius: 4px 6px 4px 7px;
  box-shadow: 0 2px 4px rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.7); }   /* the bag sprite (diceBadge.js), its frame's own shape */

.gm-dicebadge.is-held .gm-dicebadge__who { box-shadow: 0 0 0 4px var(--sheet-b), 0 0 0 6.5px var(--ink-b), 0 12px 24px rgba(0,0,0,.36); }
/* No picture in it starts the browser's own drag (Maayan: "in browser image drag is preventing the div to be dragged"). */
.gm-dicebadge img { -webkit-user-drag: none; user-select: none; }
/* THE DICE PAGE'S DICE AWAY (its badge's sack): the shelf gone below, the table empty; out again by dice-rain. */
.gmdice--section .shelf { transition: translate .35s cubic-bezier(.3, .7, .2, 1), left .25s ease, width .25s ease; }
.gmdice--section:not(.is-on) .shelf { translate: -50% calc(100% + 40px); }
.gmdice--section:not(.is-on) .table { opacity: 0; visibility: hidden; }
/* Dice-rain: the shelf's dice wait unseen until the shelf is its length, then drop on (dice.js rainOnto). */
.gmdice .sdie.rain-wait { opacity: 0; }
/* Closing with dice on the table (dice.js rainClose): the table stays seen while its dice slide off with the shelf. */
.gmdice--layer.closing:not(.is-on) .table, .gmdice--section.closing:not(.is-on) .table { opacity: 1; visibility: visible; }
/* A SPRITE'S FRAME (lib/growlmaster/sprites.js): the sheet, its frame at (--col, --row) of --cols × --rows, the box at the
   frame's own shape. The one drawing of every sprite, set by spriteVars (the server) or the same properties (diceBadge.js). */
.gm-sprite { display: block; aspect-ratio: var(--ratio, 1); background-image: var(--src); background-repeat: no-repeat;
  background-size: calc(var(--cols) * 100%) calc(var(--rows) * 100%);
  background-position: calc(var(--col) / max(1, var(--cols) - 1) * 100%) calc(var(--row) / max(1, var(--rows) - 1) * 100%); }
/* A PICTURE FROM THE PRESETS (sprites.js avatarFace): its frame on its own colour, round. Here, beside the sprites', so
   every page that shows one draws it the same (a table, the dice page's badges). */
/* FILLED TO THE CIRCLE'S BOTTOM EDGE (Maayan, 2026-10-24: "the character picture is cut flat at the bottom… the coloured
   circle shows below the cut"): each preset is a bust cut out of its frame, ending a little above the frame's bottom
   (up to 27px of 256). The picture is drawn 12% larger from its top, so the cut always falls below the circle, and the
   circle (this, round, overflow hidden) clips it: every avatar, every size, every place. */
.gm-avatar--preset { background: none; border-radius: 50%; overflow: hidden; }   /* the clip only: each place keeps its own size */
.gm-av { display: block; width: 100%; height: 100%; border-radius: 50%; background-color: var(--av); transform: scale(1.12); transform-origin: 50% 0; }
@keyframes gm-sprite-squash { 0% { scale: 1 1; } 35% { scale: 1.16 .84; } 65% { scale: .94 1.07; } 100% { scale: 1 1; } }
.gm-sprite.is-squash { animation: gm-sprite-squash .32s ease-out; transform-origin: 50% 90%; }
/* WHOSE TURN IT IS (Maayan: "the one which is now its turn has the circle around it selected somehow"): their ring gold
   and glowing. ITS SIZE (diceBadge.js sizeOf): L always, XL answering the math (Maayan, 2026-11-02: no badge grows small
   after a while), grown around the portrait's middle with a little bounce. */
.gm-dicebadge { transform-origin: 49px 49px; transition: scale .32s cubic-bezier(.3, 1.6, .5, 1), opacity .3s; scale: 1; z-index: 1; }
.gm-dicebadge[data-size="XL"] { scale: 1.36; z-index: 2; }
.gm-dicebadge .gm-dicebadge__sack, .gm-dicebadge .gm-dicebadge__name, .gm-dicebadge .gm-dicebadge__flame { transition: opacity .2s ease, visibility .2s, left .25s ease; }
/* its bag on the side with room (diceBadge.js flip): toward the middle of the page */
.gm-dicebadge.is-flip .gm-dicebadge__sack { left: -62px; }
/* another device's player at the table (diceBadge.js seats): their circle only, no bag of theirs here */
.gm-dicebadge.is-seat .gm-dicebadge__sack, .gm-dicebadge.is-seat .gm-dicebadge__flame { display: none; }
.gm-dicebadge.is-flip .gm-dicebadge__flame { left: auto; right: -6px; }
/* THEIR STREAK (Maayan: "a tiny flame icon next to his profile with the recent points he has"): the math's flame (math.js
   FLAME_ICON), the number on it */
.gm-dicebadge__flame { position: absolute; left: -6px; top: -4px; z-index: 2; display: flex; align-items: center; gap: 1px; padding: 1px 6px 1px 2px; border-radius: 12px;
  background: #fff6dc; border: 1.5px solid #e2a72e; box-shadow: 0 2px 4px rgba(0,0,0,.25); font: 800 13px/1 'Grandstander', system-ui, sans-serif; color: #8a4b08; pointer-events: none; }
.gm-dicebadge__flame svg { width: 14px; height: 18px; }
.gm-dicebadge__flame[hidden] { display: none; }
/* leaving: shrunk away */
/* one in the place of another (diceBadge.js show): the one before shrinks away quickly, then the next pops in */
.gm-dicebadge.is-leaving { scale: 0; opacity: 0; transition: scale .16s ease-in, opacity .16s ease-in; }
@keyframes gm-dicebadge-in { from { scale: 0; opacity: 0; } to { scale: 1; opacity: 1; } }
.gm-dicebadge.is-coming { animation: gm-dicebadge-in .22s cubic-bezier(.3, 1.6, .5, 1); }
/* a die held over a full bag: it stays shut, with a shake */
@keyframes gm-bag-no { 0%, 100% { translate: 0 0; } 25% { translate: -4px 0; } 75% { translate: 4px 0; } }
.gm-dicebadge.is-full .gm-dicebadge__sack { animation: gm-bag-no .3s ease 2; filter: grayscale(.4) drop-shadow(0 3px 4px rgba(0,0,0,.3)); }
/* A DIE IN THE HAND passes over the badges (Maayan: "drag a dice, should hover over players"): its layer over theirs */
.gmdice.gmdice--layer.is-holding { z-index: 36; }
.gmdice.is-holding .table { z-index: 36; }
.gm-dicebadge__who { transition: box-shadow .3s ease, border-color .3s ease; }
.gm-dicebadge.is-turn .gm-dicebadge__who { border-color: #e2a72e; box-shadow: 0 0 0 4px #fff3cf, 0 0 0 7.5px #e2a72e, 0 0 18px 6px rgba(242,183,64,.55), 0 6px 14px rgba(0,0,0,.3); }
