/* =========================================================================
   123SpinWheel — stylesheet
   One dependency-free file. In order:
   1. Tokens (light, dark)   2. Base            3. Header and menu
   4. Wheel page shell       5. Buttons         6. Content and cards
   7. Share dialog           8. Footer          9. The wheel (ported from
   123Calculator's name picker)                 10. The wheel's new parts
   ========================================================================= */

/* 1. Tokens -------------------------------------------------------------- */
:root {
  --brand:        #1e1b4b;   /* deep indigo: header, hub ring, dark ink */
  --brand-600:    #2b2770;
  /* Brand used as INK on a card. --brand stays dark in both themes while the
     card under it flips, so a foreground written against --brand would vanish
     in the dark one; this follows the theme instead. */
  --brand-ink:    var(--brand);
  --accent:       #4f46e5;
  --accent-600:   #4338ca;
  --on-accent:    #ffffff;
  --accent-soft:  #eef0ff;
  --pop:          #f59e0b;   /* amber: the pointer, the logo, one highlight */
  --ink:          #1c1b33;
  --ink-soft:     #45465e;
  --muted:        #6a6b85;
  --line:         #e3e4f0;
  --line-soft:    #eff0f7;
  --bg:           #f5f6fb;
  --card:         #ffffff;
  --card-alt:     #f8f8fd;
  --ring:         rgba(79, 70, 229, .28);
  /* The chosen option of a segmented control, raised off its track. */
  --seg-on:       #ffffff;
  --danger:       #dc2626;
  --ok:           #15803d;
  --radius:       14px;
  --radius-sm:    9px;
  --shadow:       0 1px 2px rgba(30,27,75,.06), 0 10px 30px rgba(30,27,75,.08);
  --shadow-sm:    0 1px 2px rgba(30,27,75,.08);
  --maxw:         1200px;
  --header-h:     60px;
  --font:         system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono:         ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Read back by the wheel's canvas, which cannot use a class. */
  --wheel-rim:    #1e1b4b;
  --wheel-blank:  #e5e7eb;
  color-scheme: light;
}

/* Dark. theme.js resolves the reader's choice (light, dark or match the
   device) into one of two classes on <html> before the first paint; the media
   query is the same thing for a browser with scripts off. Overriding the
   tokens rather than restyling components is what makes the whole page —
   header, wheel panel, articles — follow at once.

   The slice colours are NOT touched: they are the reader's palette and the
   point of the wheel. */
@media (prefers-color-scheme: dark) {
  :root:not(.theme-light) {
    --brand:       #0f0d2e;
    --brand-600:   #1b1846;
    --brand-ink:   var(--ink);
    --accent:      #818cf8;
    --accent-600:  #a5b4fc;
    --on-accent:   #0f0d2e;
    --accent-soft: #23224a;
    --ink:         #e9e9f6;
    --ink-soft:    #c3c4dc;
    --muted:       #9a9bb8;
    --line:        #2f2e50;
    --line-soft:   #242342;
    --bg:          #0e0d20;
    --card:        #17162f;
    --card-alt:    #131228;
    --ring:        rgba(129, 140, 248, .38);
    --seg-on:      #34325e;
    --danger:      #f87171;
    --ok:          #4ade80;
    --shadow:      0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.4);
    --shadow-sm:   0 1px 2px rgba(0,0,0,.5);
    --wheel-rim:   #c7c9f5;
    --wheel-blank: #2a2946;
    color-scheme: dark;
  }
}
:root.theme-dark {
  --brand:       #0f0d2e;
  --brand-600:   #1b1846;
  --brand-ink:   var(--ink);
  --accent:      #818cf8;
  --accent-600:  #a5b4fc;
  --on-accent:   #0f0d2e;
  --accent-soft: #23224a;
  --ink:         #e9e9f6;
  --ink-soft:    #c3c4dc;
  --muted:       #9a9bb8;
  --line:        #2f2e50;
  --line-soft:   #242342;
  --bg:          #0e0d20;
  --card:        #17162f;
  --card-alt:    #131228;
  --ring:        rgba(129, 140, 248, .38);
  --seg-on:      #34325e;
  --danger:      #f87171;
  --ok:          #4ade80;
  --shadow:      0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.4);
  --shadow-sm:   0 1px 2px rgba(0,0,0,.5);
  --wheel-rim:   #c7c9f5;
  --wheel-blank: #2a2946;
  color-scheme: dark;
}

/* 2. Base ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 1rem; }
body {
  margin: 0; font-family: var(--font); font-size: 16px; line-height: 1.6;
  color: var(--ink); background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { line-height: 1.25; color: var(--ink); margin: 0 0 .5em; letter-spacing: -.01em; }
h1 { font-size: clamp(1.7rem, 3.6vw, 2.4rem); }
h2 { font-size: clamp(1.3rem, 2.6vw, 1.6rem); }
h3 { font-size: 1.12rem; }
p { margin: 0 0 1rem; }
a { color: var(--accent-600); text-decoration: none; }
a:hover { text-decoration: underline; }
img, svg { max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
input, select, textarea {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
kbd {
  font: 600 .78em/1 var(--mono); padding: .15em .4em; border-radius: 5px;
  border: 1px solid var(--line); border-bottom-width: 2px; background: var(--card-alt); color: var(--ink-soft);
}
code { font-family: var(--mono); font-size: .9em; background: var(--line-soft); padding: .1em .35em; border-radius: 5px; }
.container { max-width: var(--maxw); margin: 0 auto; padding-left: 1.25rem; padding-right: 1.25rem; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: .75rem; top: -3rem; z-index: 100;
  background: var(--card); color: var(--ink); padding: .5rem .9rem; border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
}
.skip-link:focus { top: .75rem; }
.tag {
  display: inline-block; background: var(--line-soft); color: var(--ink-soft);
  border-radius: 6px; padding: .1rem .5rem; font-size: .78rem;
}
.linkish {
  background: none; border: 0; padding: 0; font: inherit; font-size: inherit;
  color: var(--accent-600); cursor: pointer; text-decoration: underline;
}
.linkish:hover { color: var(--accent); }
.help { color: var(--muted); font-size: .8rem; }

/* 3. Header and menu ------------------------------------------------------
   One fixed height at every width — the wheel page subtracts it from the
   viewport, so it has to be a number rather than whatever the content wraps
   to. On a phone the two text links fold into the Wheels menu. */
.site-header { background: var(--brand); color: #fff; position: relative; z-index: 20; }
.header-inner {
  max-width: none; margin: 0 auto; height: var(--header-h);
  display: flex; align-items: center; gap: 1rem; padding: 0 1rem;
}
.logo { display: inline-flex; align-items: center; gap: .55rem; color: #fff; font-weight: 800; }
.logo:hover { text-decoration: none; }
.logo-mark { flex: 0 0 auto; transition: transform .6s cubic-bezier(.2, .8, .2, 1); }
.logo:hover .logo-mark { transform: rotate(200deg); }
.logo-text { font-size: 1.18rem; letter-spacing: -.02em; }
.logo-text b { color: var(--pop); font-weight: 800; }
.site-nav { display: flex; align-items: center; gap: .25rem; margin-left: auto; }
.nav-link, .nav-menu > summary {
  color: rgba(255,255,255,.88); font-weight: 600; font-size: .93rem;
  padding: .45rem .7rem; border-radius: 8px; cursor: pointer;
}
.nav-link:hover, .nav-menu > summary:hover, .nav-menu[open] > summary {
  color: #fff; background: rgba(255,255,255,.1); text-decoration: none;
}
.nav-menu { position: relative; }
.nav-menu > summary { list-style: none; display: inline-flex; align-items: center; gap: .35rem; }
.nav-menu > summary::-webkit-details-marker { display: none; }
.caret {
  width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg); transition: transform .15s;
}
.nav-menu[open] .caret { transform: translateY(1px) rotate(-135deg); }
.nav-pop {
  position: absolute; right: 0; top: calc(100% + .5rem); width: min(560px, calc(100vw - 2rem));
  background: var(--card); color: var(--ink); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: .6rem;
}
.nav-wheels { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: .15rem; }
.nav-wheels a {
  display: flex; align-items: center; gap: .6rem; padding: .45rem .55rem; border-radius: 8px;
  color: var(--ink); font-weight: 600; font-size: .92rem;
}
.nav-wheels a:hover { background: var(--accent-soft); text-decoration: none; }
.nav-wheels .mini-wheel { flex: 0 0 auto; }
.nav-all {
  display: block; margin-top: .4rem; padding: .55rem; border-top: 1px solid var(--line-soft);
  font-weight: 600; font-size: .9rem; text-align: center;
}
.theme-btn {
  flex: 0 0 auto; width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: 10px; border: 1px solid rgba(255,255,255,.18); background: transparent;
  color: #fff; cursor: pointer;
}
.theme-btn:hover { background: rgba(255,255,255,.1); }
.theme-btn .i-moon { display: none; }
.theme-dark .theme-btn .i-sun { display: none; }
.theme-dark .theme-btn .i-moon { display: block; }
@media (max-width: 720px) {
  .header-inner { gap: .5rem; padding: 0 .75rem; }
  .logo-text { font-size: 1.05rem; }
  .site-nav .nav-link { display: none; }
  .nav-pop { position: fixed; left: 1rem; right: 1rem; width: auto; top: calc(var(--header-h) - .25rem); max-height: calc(100dvh - var(--header-h) - 1rem); overflow-y: auto; }
}
@media (max-width: 480px) {
  .nav-wheels { grid-template-columns: 1fr; }
}
/* The two links hidden from the bar on a phone, offered inside the menu. */
.nav-extra { display: none; }
@media (max-width: 720px) {
  .nav-extra { display: flex; gap: .5rem; margin-top: .25rem; }
  .nav-extra a { flex: 1; text-align: center; padding: .5rem; border-radius: 8px; background: var(--card-alt); font-weight: 600; font-size: .9rem; }
}

/* 4. Wheel page shell -----------------------------------------------------
   The wheel gets the viewport: an app bar, then the wheel and its panel in
   whatever height is left under the header. The writing follows one scroll
   below. Below 721px the page simply scrolls. */
body.page-app .site-main { min-height: 0; }
.app-shell { display: flex; flex-direction: column; height: calc(100dvh - var(--header-h)); min-height: 520px; }
.app-bar {
  display: flex; align-items: center; gap: .75rem; flex: 0 0 auto;
  padding: .5rem 1rem; border-bottom: 1px solid var(--line); background: var(--card);
}
.app-head { display: flex; align-items: baseline; gap: .75rem; min-width: 0; }
.app-title { font-size: 1.12rem; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-sub {
  margin: 0; color: var(--muted); font-size: .87rem; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-bar-actions { margin-left: auto; display: flex; align-items: center; gap: .75rem; flex: 0 0 auto; }
.app-jump { font-size: .87rem; white-space: nowrap; font-weight: 600; }
.app-body { flex: 1 1 auto; min-height: 0; display: flex; }
.app-body > * { flex: 1 1 auto; min-width: 0; }
@media (max-width: 720px) {
  .app-shell { height: auto; min-height: 0; }
  .app-sub { display: none; }
  .app-bar { padding: .45rem .75rem; }
  .app-jump { display: none; }
  .app-body { padding: .75rem .75rem 0; }
}

/* 5. Buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  border: 1px solid transparent; border-radius: var(--radius-sm); padding: .65rem 1.25rem;
  font: 600 .98rem/1.2 var(--font); cursor: pointer; text-decoration: none; white-space: nowrap;
}
.btn:hover { text-decoration: none; }
.btn:disabled { opacity: .55; cursor: default; }
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover:not(:disabled) { background: var(--accent-600); }
.theme-dark .btn-primary:hover:not(:disabled) { background: #a5b4fc; }
.btn-ghost { background: var(--card); border-color: var(--line); color: var(--ink-soft); }
.btn-ghost:hover:not(:disabled) { border-color: var(--accent); color: var(--ink); }
.btn-share { background: var(--pop); color: #1e1b4b; padding: .45rem .95rem; font-size: .9rem; }
.btn-share:hover { background: #fbbf24; }

/* 6. Content and cards ---------------------------------------------------- */
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .75rem; }
.section-head h2 { margin: 0; font-size: 1.2rem; }
.section-head a { font-weight: 600; font-size: .92rem; }

.more-wheels { padding: 1.75rem 0 .5rem; border-top: 1px solid var(--line); background: var(--card-alt); }
.wheel-strip {
  list-style: none; margin: 0; padding: 0 0 .75rem; display: grid; gap: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.wheel-tile {
  display: flex; flex-direction: column; align-items: center; gap: .45rem; text-align: center;
  padding: .85rem .5rem .75rem; border-radius: var(--radius); background: var(--card);
  border: 1px solid var(--line); color: var(--ink); font-weight: 600; font-size: .88rem; height: 100%;
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.wheel-tile:hover { text-decoration: none; border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow); }
.wheel-tile:hover .mini-wheel, .wheel-card a:hover .mini-wheel, .related a:hover .mini-wheel { transform: rotate(60deg); }
.mini-wheel { transition: transform .5s cubic-bezier(.2, .8, .2, 1); display: block; }

.below { padding-top: 2.25rem; padding-bottom: 3rem; }
.below-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 2.5rem; align-items: start; }
.below-aside { display: grid; gap: 1rem; position: sticky; top: 1rem; }
@media (max-width: 960px) {
  .below-grid { grid-template-columns: minmax(0, 1fr); }
  .below-aside { position: static; }
}
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.1rem 1.2rem; box-shadow: var(--shadow-sm);
}
.card h2 { font-size: .82rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 0 0 .75rem; }
.facts dl { margin: 0; display: grid; gap: .55rem; }
.facts dl > div { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: .5rem; font-size: .9rem; }
.facts dt { color: var(--muted); }
.facts dd { margin: 0; color: var(--ink); }
.related ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .25rem; }
.related a { display: flex; align-items: center; gap: .65rem; padding: .4rem; border-radius: 8px; color: var(--ink); }
.related a:hover { background: var(--accent-soft); text-decoration: none; }
.related a > span { display: flex; flex-direction: column; min-width: 0; }
.related strong { font-size: .92rem; }
.related small { color: var(--muted); font-size: .8rem; line-height: 1.35; }
.share-card p { font-size: .92rem; color: var(--ink-soft); }
.share-card .btn { width: 100%; }

/* Long-form writing. Answer-first: the opening block is set apart, because
   it is the paragraph a reader — or an answer engine — should be able to take
   on its own. */
.prose { font-size: 1.02rem; color: var(--ink); }
.prose h2 { margin-top: 2.2rem; }
.prose h3 { margin-top: 1.6rem; }
.prose ul, .prose ol { padding-left: 1.3rem; margin: 0 0 1rem; }
.prose li { margin: .3rem 0; }
.prose li::marker { color: var(--accent); }
.prose blockquote { margin: 1rem 0; padding: .5rem 1rem; border-left: 3px solid var(--accent); color: var(--ink-soft); }
.answer {
  background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: var(--radius); padding: 1.1rem 1.3rem; margin-bottom: 1.5rem; box-shadow: var(--shadow-sm);
}
.answer p:last-child { margin-bottom: 0; }
.answer strong { color: var(--brand-ink); }
.steps { counter-reset: step; list-style: none; padding: 0 !important; display: grid; gap: .6rem; }
.steps li {
  counter-increment: step; position: relative; padding: .7rem .9rem .7rem 3.1rem; margin: 0 !important;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.steps li::before {
  content: counter(step); position: absolute; left: .8rem; top: .65rem;
  width: 1.65rem; height: 1.65rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--accent); color: var(--on-accent); font-weight: 700; font-size: .85rem;
}
.faq { display: grid; gap: .5rem; }
.faq details { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.faq summary {
  cursor: pointer; padding: .8rem 2.4rem .8rem 1rem; font-weight: 600; position: relative; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: 1.05rem; top: 50%; width: 8px; height: 8px; margin-top: -6px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted); transform: rotate(45deg);
  transition: transform .15s;
}
.faq details[open] summary::after { transform: rotate(-135deg); margin-top: -2px; }
.faq details p { padding: 0 1rem .9rem; margin: 0; color: var(--ink-soft); }
.doc-note { margin-top: 2rem; color: var(--muted); font-size: .88rem; }
table.data { width: 100%; border-collapse: collapse; margin: 0 0 1.25rem; font-size: .95rem; background: var(--card); border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--line); display: block; overflow-x: auto; }
table.data thead, table.data tbody { display: table; width: 100%; border-collapse: collapse; }
table.data th, table.data td { text-align: left; padding: .6rem .8rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
table.data th { background: var(--card-alt); font-size: .85rem; color: var(--ink-soft); }
table.data tr:last-child td { border-bottom: 0; }
.chips { list-style: none; padding: 0 !important; display: flex; flex-wrap: wrap; gap: .45rem; }
.chips li { margin: 0 !important; padding: .3rem .75rem; border-radius: 999px; background: var(--accent-soft); color: var(--accent-600); font-size: .9rem; font-weight: 600; }
.chips a { color: inherit; }

/* Content pages. */
.doc { padding-top: 1.25rem; padding-bottom: 3.5rem; max-width: 880px; }
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .35rem; padding: 0; margin: 0 0 1.25rem; font-size: .86rem; color: var(--muted); }
.crumbs li + li::before { content: "/"; margin-right: .35rem; color: var(--line); }
.crumbs a { color: var(--muted); }
.doc-head { margin-bottom: 1.75rem; }
.doc-head h1 { margin-bottom: .4rem; }
.eyebrow { text-transform: uppercase; letter-spacing: .08em; font-weight: 700; color: var(--accent-600); font-size: .82rem; margin: 0 0 .4rem; }
.lede { font-size: 1.15rem; color: var(--ink-soft); max-width: 62ch; }
.wheel-cards { list-style: none; padding: 0 !important; margin: 0 0 2rem; display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); }
.wheel-card { margin: 0 !important; }
.wheel-card a {
  display: flex; gap: 1rem; align-items: center; height: 100%; padding: 1rem; border-radius: var(--radius);
  background: var(--card); border: 1px solid var(--line); color: var(--ink);
  transition: border-color .15s, box-shadow .15s, transform .15s;
}
.wheel-card a:hover { text-decoration: none; border-color: var(--accent); box-shadow: var(--shadow); transform: translateY(-2px); }
.wheel-card .mini-wheel { flex: 0 0 auto; }
.wheel-card-body { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.wheel-card-body strong { font-size: 1.05rem; }
.wheel-card-body > span { color: var(--ink-soft); font-size: .92rem; line-height: 1.45; }
.wheel-card-body small { color: var(--muted); font-size: .8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notfound .wheel-cards { margin-top: 1rem; }

/* 7. Share dialog ---------------------------------------------------------- */
.modal {
  /* Above the confetti (200) and the colour picker (210): sharing the
     result is read while the flakes are still falling. */
  position: fixed; inset: 0; z-index: 220; display: grid; place-items: center; padding: 1rem;
  background: rgba(15, 13, 46, .55); backdrop-filter: blur(3px);
}
.modal[hidden] { display: none; }
.modal-box {
  position: relative; width: min(520px, 100%); max-height: calc(100dvh - 2rem); overflow-y: auto;
  background: var(--card); color: var(--ink); border-radius: 18px; padding: 1.4rem 1.4rem 1.25rem;
  box-shadow: 0 24px 70px rgba(15, 13, 46, .4); border: 1px solid var(--line);
}
.modal-box h2 { font-size: 1.25rem; margin: 0 2rem .9rem 0; }
.modal-x {
  position: absolute; top: .7rem; right: .7rem; width: 36px; height: 36px; border-radius: 50%;
  border: 0; background: var(--line-soft); color: var(--ink-soft); font-size: 1.4rem; line-height: 1; cursor: pointer;
}
.modal-x:hover { color: var(--ink); background: var(--line); }
.share-result {
  margin: 0 0 1rem; padding: .8rem 1rem; border-radius: var(--radius-sm); background: var(--accent-soft);
  color: var(--ink); font-size: 1.02rem;
}
.share-result strong { color: var(--accent-600); }
.share-wheel { display: grid; gap: .65rem; margin-bottom: 1rem; }
.field { display: grid; gap: .3rem; font-size: .88rem; color: var(--ink-soft); font-weight: 600; }
.field input { padding: .6rem .75rem; font-weight: 400; color: var(--ink); width: 100%; }
.check { display: flex; gap: .55rem; align-items: flex-start; font-size: .9rem; color: var(--ink-soft); cursor: pointer; }
.check input { margin-top: .25rem; accent-color: var(--accent); width: 1rem; height: 1rem; flex: 0 0 auto; }
.share-link { display: flex; gap: .5rem; }
.share-link input {
  flex: 1 1 auto; min-width: 0; padding: .6rem .75rem; font: .88rem/1.3 var(--mono);
  color: var(--ink-soft); background: var(--card-alt);
}
.share-link .btn { padding: .6rem 1rem; }
.share-note { min-height: 1.2em; margin: .45rem 0 .9rem; font-size: .82rem; color: var(--muted); }
.share-note.is-warn { color: var(--danger); }
.share-nets { display: grid; grid-template-columns: repeat(4, 1fr); gap: .5rem; }
.net {
  display: flex; flex-direction: column; align-items: center; gap: .3rem; padding: .65rem .25rem;
  border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--card);
  color: var(--ink-soft); font: 600 .78rem/1.2 var(--font); cursor: pointer;
}
.net:hover { text-decoration: none; border-color: currentColor; }
.net[hidden] { display: none; }
.net svg { width: 22px; height: 22px; }
.net-x:hover { color: var(--ink); }
.net-fb:hover { color: #1877f2; }
.net-wa:hover { color: #1fa855; }
.net-tg:hover { color: #229ed9; }
.net-rd:hover { color: #ff4500; }
.net-li:hover { color: #0a66c2; }
.net-mail:hover, .net-native:hover { color: var(--accent-600); }
@media (max-width: 420px) {
  .share-nets { grid-template-columns: repeat(4, 1fr); }
  .share-link { flex-direction: column; }
}

/* 8. Footer ---------------------------------------------------------------- */
.site-footer { background: var(--brand); color: rgba(255,255,255,.75); padding: 2.5rem 0 1.25rem; font-size: .92rem; }
.footer-grid { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr); gap: 2rem; }
.footer-brand p { margin: .75rem 0 0; max-width: 40ch; line-height: 1.55; }
.site-footer h2 { color: #fff; font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; margin: 0 0 .7rem; }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.site-footer a { color: rgba(255,255,255,.9); }
.site-footer a:hover { color: #fff; }
.footer-more { margin-top: 1.4rem !important; }
.footer-base {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 1.5rem;
  margin-top: 2rem; padding-top: 1.1rem; border-top: 1px solid rgba(255,255,255,.12); font-size: .84rem;
}
.footer-base p { margin: 0; }
@media (max-width: 760px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 440px) {
  .footer-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* 9. The wheel -------------------------------------------------------------
   Ported from 123Calculator's random name picker with its own commentary,
   which explains why each rule is the way it is. Only the brand colours were
   changed. */
.nw-body { --nw-seam: 8px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.75rem; align-items: start; }
.nw-main { display: flex; align-items: center; justify-content: center; min-width: 0; }
.nw-stage { position: relative; width: 100%; max-width: 520px; margin: 0 auto; aspect-ratio: 1; }
.nw-canvas { display: block; width: 100%; height: 100%; }

/* --- Full-screen layout -------------------------------------------------
   The page asks for the app shell, so above the phone breakpoint the wheel
   gets the viewport: it sits centred in the space left over once the entry
   panel has taken its column, and grows to whatever square fits.

   Keeping it square while filling a box of unknown proportions is the whole
   trick. height:100% + aspect-ratio derives the width from the height, and
   max-width:100% then clamps the width back when the column is the tighter
   of the two — with aspect-ratio the height follows it down, so the circle
   never distorts and never overflows in either direction. */
@media (min-width: 721px) {
  body.page-app .namewheel { display: flex; flex-direction: column; min-height: 0; }
  body.page-app .nw-body {
    /* How far the panel card is held off the shell's edges, and — derived
       from it, never guessed twice — where the toggle sits. The button is
       34px, so half of it plus the inset lands its centre exactly on the
       card's left border however the inset is retuned. */
    --nw-inset: .75rem;
    --nw-seam: calc(-17px - var(--nw-inset));
    flex: 1 1 auto; min-height: 0;
    grid-template-columns: minmax(0, 1fr) clamp(340px, 34vw, 520px);
    gap: 0; align-items: stretch;
  }
  body.page-app .nw-main { padding: 1rem 1.25rem; min-height: 0; }
  /* The square must be the SMALLER of the space's width and height, and that
     is not something max-width can say. height:100% + aspect-ratio + max-width
     looks right and is not: with the height definite, clamping the width does
     not pull the height down with it, so on a tall narrow screen the wheel
     came out 677 wide by 1216 tall and pushed the page sideways.

     Container query units state the intent directly — min(100cqw, 100cqh) is
     literally "the smaller dimension of my container". @supports keeps the
     plain 520px wheel from the base rules for anything without them, which is
     a smaller wheel rather than a broken one. */
  @supports (width: 1cqw) {
    body.page-app .nw-main { container-type: size; }
    body.page-app .nw-stage {
      width: min(100cqw, 100cqh); height: auto;
      max-width: none; max-height: none;
      aspect-ratio: 1 / 1; margin: 0;
    }
  }
  /* The panel scrolls on its own so a long entry list never pushes the wheel
     off the screen it was just given.

     Still the card from the base rules, inset from the shell's edges rather
     than ruled off from the wheel by a single left border: it is the same
     object here as on a phone, and the inset is what makes it read as sitting
     ON the page instead of being the wall the page stops at. Only the things
     the shell actually changes are restated. */
  body.page-app .nw-side {
    min-height: 0; overflow-y: auto; gap: .85rem;
    margin: var(--nw-inset);
  }
  /* Clear of the card's rounded corner. At the shared top of 12px the button
     straddles the curve rather than the straight edge, and half of it hangs
     off the card. */
  body.page-app .nw-panelbtn { top: calc(12px + var(--nw-inset)); }
  body.page-app .nw-panel[data-nw-panel="results"] { flex: 1 1 auto; min-height: 0; }
  body.page-app .nw-history {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  }
  body.page-app .nw-history ol {
    flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto;
  }
}

/* Portrait tablets. Side by side, the wheel is limited by a column barely
   half the screen wide while a thousand pixels of height go unused — 480px of
   wheel in a 1064px-tall area on an 820x1180 iPad. Stacking gives it the full
   width instead and lets the page scroll to the controls.

   :has() scopes the un-pinning to this page: .app-shell is shared with the
   graphers, which genuinely do want the fixed viewport height. Anything
   without :has() keeps the two-column layout above, which is a smaller wheel
   rather than a broken one — and the cq rule is overridden here explicitly,
   because 100cqh means nothing once the shell's height is auto. */
@supports selector(:has(*)) {
  @media (min-width: 721px) and (max-width: 1100px) and (orientation: portrait) {
    body.page-app:has(.namewheel) .app-shell { height: auto; }
    body.page-app .nw-body { --nw-seam: 8px; grid-template-columns: minmax(0, 1fr); }
    body.page-app .nw-main { container-type: normal; padding: 1rem; }
    body.page-app .nw-stage {
      width: min(100%, 78vh); height: auto; aspect-ratio: 1 / 1; margin: 0 auto;
    }
    /* Stacked, so the card keeps all four of its own edges — there is no
       column beside it for a border to divide it from. */
    body.page-app .nw-side { overflow: visible; min-height: auto; }
    /* Nothing here has a definite height to fill, so the scrollpane goes back
       to a cap rather than collapsing to nothing. */
    body.page-app .nw-history ol { flex: 0 1 auto; max-height: 46vh; }
  }
}

/* The pointer sits at the RIGHT of the wheel and overlaps the rim, so it
   reads as touching the slice it names. A triangle pointing left is drawn by
   filling the RIGHT border and leaving the other two clear — which is also
   why the script tints border-right-color to match the slice underneath it. */
/* A white edge round the marker, so it reads as clearly against a dark slice
   as against a light one.

   A CSS-BORDER TRIANGLE CANNOT CARRY A BORDER OF ITS OWN — the shape IS a
   border — so this is a clipped box instead: the element is the white
   outline and the pseudo-element is the coloured triangle inside it. That
   way round because of painting order. A pseudo-element draws on top of its
   parent's background, which is what is wanted here; a negative-z-index
   child would draw on top of the parent's background AND border, so putting
   the white behind as a child would have covered the colour instead.

   The numbers offset the triangle by 3px along every edge, which is a scale
   about the INCENTRE and not about the tip: 30x30 becomes 40x40, and the
   inner triangle sits 6.9px in from the tip and 3.1px in from the base.
   Scaling about the tip would have left the edge at the base twice as thick
   as the edge at the point. The whole thing is shifted 3.1px further right
   so the coloured triangle stays exactly where it has always been. */
.nw-pointer {
  /* The shape's own size, named so the placement below can do arithmetic
     with it rather than every style repeating the sums. */
  --pw: 40px; --ph: 40px;
  /* How far the mounting stands off the rim. The tip then reaches the
     shape's own height inward from there, so raising this moves the whole
     marker out and leaves more of the slice showing. */
  --pgap: 10px;
  --nw-shade-x: 0px;
  position: absolute; z-index: 2;
  width: var(--pw); height: var(--ph);
  /* It swings from its mounting, which is the middle of its top edge in the
     orientation it is drawn in — the outside of the wheel in both places.
     Pivoting anywhere else and the tip would stay put while the base waved
     about. */
  transform-origin: 50% 0;
  /* THE SHAPE IS IN THE PSEUDO-ELEMENTS, and it has to be. clip-path clips
     what the filter produced, so an element carrying both its outline and
     its drop-shadow clips the shadow away with the same polygon that casts
     it — the computed style says the shadow is there and not one pixel of
     it is drawn. Leaving this box unclipped gives the shadow somewhere to
     fall.

     Offset, blur and darkness all scale with one number, so nought really
     is no shadow rather than a hard edge with the blur still on it. */
  filter: drop-shadow(
    calc(var(--nw-shade-x, 0px) * var(--nw-shade, 1))
    calc(5px * var(--nw-shade, 1)) calc(8px * var(--nw-shade, 1))
    rgba(30,27,75, calc(.75 * var(--nw-shade, 1))));
}

/* --- Where it sits --------------------------------------------------------
   The base stands --pgap outside the wheel, so the mounting reads as fixed
   to the rim, and the tip reaches the shape's own height inward from there.
   The angle in POSITIONS has to say the same thing, or the wheel points at
   one slice and announces another. */
.nw-at-top {
  left: 50%; top: calc(-1 * var(--pgap)); right: auto; bottom: auto;
  transform: translateX(-50%) rotate(var(--nw-flick, 0deg));
}
.nw-at-right {
  top: 50%; left: auto; bottom: auto;
  right: calc(-1 * var(--pgap) - var(--pw) / 2);
  /* A quarter-turn takes "down" to "left", which is inward from the right
     of the wheel. The flick is added to it rather than replacing it. */
  transform: rotate(calc(90deg + var(--nw-flick, 0deg)));
}

/* --- The shapes -----------------------------------------------------------
   Each is two layers: ::before is the white edge and ::after the colour
   inside it. The uneven insets offset the triangle by about 3px along every
   edge, which is a scale about its incentre; an even inset would leave the
   edge thicker at the base than at the point. */
.nw-pointer::before, .nw-pointer::after {
  content: ''; position: absolute;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.nw-pointer::before {
  inset: 0;
  background: var(--nw-pointer-edge, #fff);
}
.nw-pointer::after {
  inset: 3.1px 5px 6.9px 5px;
  background: var(--nw-pointer-ink, var(--brand));
  transition: background-color .12s linear;
}

.nw-p-slim { --pw: 30px; --ph: 60px; }
.nw-p-slim::after { inset: 3.1px 4px 12.9px 4px; }

/* The other way round from the rest: a white shape with a thin dark edge,
   which reads as a fixed part of the page rather than as part of the wheel.
   So it does not take the slice's colour, and the engine leaves it alone. */
.nw-p-arrow { --nw-pointer-edge: #1e1b4b; }
.nw-p-arrow::after { background: #fff; }

/* A pin. Not a triangle, so the inner shape is the same polygon in a box
   inset by 4px — which scales it about the middle rather than offsetting
   it, near enough on a shape this size. */
.nw-p-pin { --pw: 40px; --ph: 48px; }
.nw-p-pin::before, .nw-p-pin::after {
  clip-path: polygon(50% 100%, 8% 48%, 22% 10%, 78% 10%, 92% 48%);
}
.nw-p-pin::after { inset: 4px; }

/* A dart: its base dips in the middle rather than running straight across,
   and that notch is what turns a plain triangle into an arrowhead — it is
   the whole shape. A thicker white edge than the others, because the
   outline is most of what it is. */
.nw-p-dart { --pw: 46px; --ph: 52px; }
.nw-p-dart::before, .nw-p-dart::after {
  clip-path: polygon(0 0, 50% 26%, 100% 0, 50% 100%);
}
.nw-p-dart::after { inset: 7px 6px 6px 6px; }

/* The size comes from the engine, which also draws the labels from just
   outside it: one number, handed over as a property, rather than a
   percentage here and a fraction there drifting apart. */
.nw-hub {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: var(--nw-hub, 27%); height: var(--nw-hub, 27%);
  border-radius: 50%; z-index: 2;
  background: var(--card); color: var(--brand-ink); cursor: pointer;
  border: 4px solid var(--brand);
  font: 700 clamp(.9rem, 2.6vw, 1.15rem)/1 var(--font);
  box-shadow: 0 2px 10px rgba(30,27,75,.22);
}
/* background-COLOR, never the `background` shorthand. The shorthand resets
   every background longhand it does not mention, and this selector outranks
   the .has-img rule below — so a hover was clearing background-size and
   background-repeat, tiling the hub picture at its natural size and painting
   navy through any transparency. A hover must not be able to redraw the
   image at all. */
.nw-hub:hover:not(:disabled) { background-color: var(--brand); color: #fff; }
.nw-hub:disabled { cursor: default; opacity: .75; }
.nw-hub:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

/* --- The wheel's own styles ----------------------------------------------
   The rim, the dividers and which way the labels face are drawn on the
   canvas and live in WHEELS in the engine. What is left for the stylesheet
   is the centre's trim, which is a DOM element: the flat and segmented
   looks drop the ring and the shadow, because a wheel with no rim and a
   hub with a heavy one looks like two drawings. */
.nw-w-flat .nw-hub, .nw-w-segments .nw-hub {
  border: 0; box-shadow: none; background-color: var(--card-alt);
}
.nw-w-flat .nw-hub:hover:not(:disabled),
.nw-w-segments .nw-hub:hover:not(:disabled) {
  background-color: var(--line-soft); color: var(--brand-ink);
}

/* The right-hand column is a CARD. It holds every control the page has —
   entries, results, settings, the file buttons — and with no edges of its own
   those read as loose furniture standing next to the wheel rather than as one
   panel belonging to it. Same chrome as .panel, which is what the rest of the
   site puts its controls in. */
.nw-side {
  display: flex; flex-direction: column; gap: 1rem;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
  padding: 1rem 1.25rem;
}


/* The magnifier: a live copy of the region under the pointer, for when the
   wheel has more slices than its labels can be read at. */
/* An overlay in the top right of the wheel area, not a panel in the side
   column. It shows what is under the pointer, so it belongs next to the
   pointer; and floating it frees the side column for the lists.
   pointer-events:none so it can never intercept a click meant for the wheel
   underneath it. */
.nw-lens {
  position: absolute; top: 12px; right: 50px; z-index: 3;
  width: clamp(180px, 26%, 300px);
  display: flex; flex-direction: column;
  padding: 0; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 6px 18px rgba(30,27,75,.16);
  pointer-events: none;
}
/* An author `display` beats the user agent's [hidden] rule, so a panel
   given display:flex stays on screen however many times the script sets
   .hidden = true. Both of these ship hidden, so both need saying twice. */
.nw-lens[hidden], .nw-custom[hidden] { display: none; }
.nw-lens-canvas {
  display: block; width: 100%; aspect-ratio: 56 / 15;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card-alt);
}

/* Colour scheme controls. */
/* Images. The background sits on the area around the wheel, under a scrim so
   a busy photo cannot swallow the slice colours; the hub image fills the
   centre button and replaces its label. */
.nw-main.nw-has-bg { background-size: cover; background-position: center; background-repeat: no-repeat; }
.nw-main.nw-has-bg::before {
  content: ""; position: absolute; inset: 0; background: rgba(255,255,255,.45);
  pointer-events: none;
}
.nw-main { position: relative; }
/* The wheel sits above the background image's scrim. This used to be a
   blanket `.nw-main > *`, which also hit the magnifier overlay — same
   specificity as its own rule, declared later, so it silently forced the
   overlay back into the document flow and it never reached the corner.
   Naming the stage instead leaves absolutely-positioned children alone. */
.nw-main > .nw-stage { position: relative; z-index: 1; }
.nw-hub.nw-has-img {
  background-size: cover; background-position: center; background-repeat: no-repeat;
  color: transparent;
}
/* With a picture in the hub, hovering changes NOTHING — no tint, no ring, no
   shadow. Re-stating the resting background-color is what neutralises the
   hover rule above, which would otherwise paint through any transparency.
   The cursor still says the hub is clickable, and :focus-visible below still
   draws a real outline for the keyboard, so nothing is lost by leaving the
   picture alone under the mouse. */
.nw-hub.nw-has-img:hover:not(:disabled) {
  background-color: var(--card);
  box-shadow: 0 2px 10px rgba(30,27,75,.22);
}
.nw-hub.nw-has-img span { visibility: hidden; }

/* --- View modes -----------------------------------------------------------
   The two small controls opposite the magnifier, and what they do. Both live
   inside the element that goes full screen, or using one would hide the
   other. */
.nw-viewbtns { position: absolute; top: 12px; left: 12px; z-index: 3; display: flex; gap: .35rem; }
.nw-viewbtn {
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
  padding: .3rem .75rem; font: 600 .8rem/1 var(--font);
  background: var(--card); color: var(--ink-soft);
  box-shadow: 0 2px 8px rgba(30,27,75,.12);
}
.nw-viewbtn:hover { color: var(--ink); border-color: var(--accent); }
.nw-viewbtn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The panel toggle, on the seam. Round, and small enough that it reads as
   part of the edge rather than as another control competing with the wheel.
   It is a child of .nw-main so that going full screen takes it along; the
   negative offset is what carries it out over the panel's border. */
.nw-panelbtn {
  position: absolute; top: 12px; right: var(--nw-seam, 8px); z-index: 4;
  width: 34px; height: 34px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
  background: var(--card); color: var(--ink-soft);
  box-shadow: 0 2px 10px rgba(30,27,75,.16);
}
.nw-panelbtn:hover { color: var(--ink); border-color: var(--accent); }
.nw-panelbtn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* The chevron is a square with two of its borders, turned. Drawing it rather
   than setting a glyph means no font can render it as a box, and the script
   never has to touch the button's contents — the direction follows the state
   class, and it points the way the panel is about to travel. */
.nw-panelbtn span {
  display: block; width: 8px; height: 8px;
  border-top: 2px solid currentColor; border-right: 2px solid currentColor;
  transform: translateX(-2px) rotate(45deg);
}
.nw-body.nw-nopanel .nw-panelbtn span { transform: translateX(2px) rotate(-135deg); }

/* Panel hidden: the wheel takes the whole width.

   Qualified with body.page-app because the full-screen shell sets the two
   columns at that specificity — a bare .nw-body.nw-nopanel loses to it and
   the panel's column stayed reserved, so hiding the panel changed nothing
   but left a gap. Third time this stylesheet has caught the same shape of
   mistake: a broad rule quietly outranking a specific one. */
.nw-body.nw-nopanel,
body.page-app .nw-body.nw-nopanel { --nw-seam: 8px; grid-template-columns: minmax(0, 1fr); }
.nw-body.nw-nopanel .nw-side { display: none; }

/* Full screen. The element is lifted out of the page, so it has to supply the
   background and the height the app shell was giving it — without this the
   browser paints the default black behind a transparent wheel. */
.namewheel:fullscreen { background: var(--bg); width: 100vw; height: 100vh; }
.namewheel:fullscreen .nw-body { height: 100vh; min-height: 0; }
.namewheel:fullscreen .nw-side { max-height: 100vh; }

@media (max-width: 720px) {
  /* Stacked, hiding the panel is the difference between a wheel and a wheel
     with a page of controls under it, so the control matters most here. */
  .nw-viewbtns { top: 6px; left: 6px; }
  .nw-panelbtn { top: 6px; width: 30px; height: 30px; }
  /* Exactly the pointer's overhang, so it still touches the rim and the page
     no longer scrolls sideways by those two pixels. */
  .nw-main { padding: 0 2px; }
  .nw-viewbtn { padding: .25rem .6rem; font-size: .75rem; }
}

/* --- Right-hand panel -----------------------------------------------------
   A settings column reads as a wall of controls unless the groups are visibly
   separated, so each one gets a heading of the same weight and a rule above
   it. The whole column scrolls as one, which is what keeps a long entry list
   from pushing the buttons below the fold. */
.nw-group-title {
  font-weight: 600; font-size: .82rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.nw-panel > * + * { padding-top: .9rem; border-top: 1px solid var(--line-soft); }
.nw-panel > .nw-mode + * { padding-top: 0; border-top: 0; }

/* Screen-reader-only: the group heading names the control visually, but the
   select still needs a real label of its own. */
.nw-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* File pickers that look like the site's other buttons, and that say what was
   chosen. The native control renders as an unstyled "Choose File / No file
   chosen" which matches nothing else here and never reports the filename
   afterwards. */
.nw-file { display: flex; flex-direction: column; gap: .3rem; }
.nw-file-label { font-size: .85rem; color: var(--ink-soft); }
.nw-file-btn {
  position: relative; overflow: hidden; display: inline-flex; align-items: center;
  align-self: flex-start; gap: .4rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .4rem .8rem; font-size: .85rem; font-weight: 600;
  background: var(--card); color: var(--ink);
}
.nw-file-btn:hover { border-color: var(--accent); color: var(--accent-600); }
.nw-file-btn input {
  position: absolute; inset: 0; opacity: 0; cursor: pointer;
  width: 100%; height: 100%;
}
.nw-file-name {
  font-size: .78rem; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
/* Quiet, and only there when there is a picture to take off. */
.nw-file-x {
  align-self: flex-start; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--ink-soft);
  padding: .15rem .55rem; font: 600 .72rem/1.4 var(--font);
}
.nw-file-x[hidden] { display: none; }
.nw-file-x:hover { color: var(--ink); border-color: var(--accent); }
.nw-file-x:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The result rows carry the slice colour, so a glance down the list matches
   the wheel rather than being eight identical lines of text. */
.nw-history li { display: flex; align-items: center; gap: .5rem; }
.nw-history li .nw-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid rgba(0,0,0,.12);
}

/* Bigger hit targets on the advanced rows. The icon buttons were about 18px
   square, which is below any reasonable touch target and fiddly with a mouse
   too. */
.nw-row > button[data-dup], .nw-row > button[data-del],
.nw-row > button[data-rmimg] { min-width: 28px; min-height: 28px; }
.nw-row-move button { padding: 2px 3px; font-size: .65rem; }
.nw-row-img { min-width: 28px; min-height: 28px; display: inline-flex;
              align-items: center; justify-content: center; border-radius: 6px; }
.nw-row-img:hover { background: var(--card-alt); }

/* Entries / Results tabs. */
/* The tabs and the file buttons share one row, so the underline that marks
   the open tab belongs to the ROW rather than to the tab strip — otherwise it
   would stop halfway across, under the tabs only. align-items: flex-end keeps
   the buttons sitting on that line rather than floating above it. */
.nw-tabrow {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: .5rem .4rem; flex-wrap: wrap; border-bottom: 1px solid var(--line);
}
.nw-tabs { display: flex; gap: .25rem; }
.nw-tab {
  border: 0; background: transparent; cursor: pointer; padding: .5rem .65rem;
  font: 600 .92rem/1 var(--font); color: var(--muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: .4rem;
}
.nw-tab:hover { color: var(--ink); }
.nw-tab.on { color: var(--ink); border-bottom-color: var(--accent); }
.nw-tab-count {
  background: var(--line-soft); color: var(--ink-soft); border-radius: 999px;
  padding: .05rem .4rem; font-size: .75rem; font-weight: 600;
}
.nw-tab-count[hidden] { display: none; }
/* margin-left: auto rather than justify-content alone, so the buttons stay
   hard right even on the wrap, when they are the only thing on their line. */
.nw-filebar {
  display: flex; align-items: center; gap: .3rem;
  margin-left: auto; padding-bottom: .3rem;
}
.nw-filebar .btn { padding: .3rem .55rem; font-size: .8rem; }
/* Speaks only when it has something to say, and says it under the row rather
   than in a dialog nobody asked for. No flex sizing: it is a child of the
   side column now, not of the button strip, and a basis meant for a row
   would be read down the column. */
.nw-filestatus { font-size: .78rem; color: var(--muted); margin-top: -.6rem; }
.nw-filestatus:empty { display: none; }
.nw-panel { display: flex; flex-direction: column; gap: 1rem; }
.nw-panel[hidden] { display: none; }
.nw-history-title { font-size: .85rem; color: var(--muted); }
.nw-empty { color: var(--muted); font-size: .9rem; margin: .25rem 0 0; }
.nw-empty[hidden] { display: none; }

/* Simple / Advanced switch. */
.nw-mode { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; padding: 2px; gap: 2px; }
.nw-mode-btn {
  border: 0; background: transparent; cursor: pointer; border-radius: 999px;
  padding: .3rem .85rem; font: 600 .85rem/1 var(--font); color: var(--ink-soft);
}
.nw-mode-btn.on { background: var(--brand); color: #fff; }

/* The row editor. A grid so every row's controls line up in columns however
   long the names are. */
.nw-adv { display: flex; flex-direction: column; gap: .5rem; }
.nw-adv[hidden] { display: none; }
.nw-adv-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.nw-adv-head span { font-weight: 600; font-size: .92rem; }
.nw-adv-head .btn { padding: .35rem .8rem; font-size: .85rem; }
.nw-rows {
  display: flex; flex-direction: column; gap: .3rem;
  max-height: 420px; overflow-y: auto; padding-left: 0;
}
.nw-row {
  display: grid; align-items: center; gap: .35rem;
  grid-template-columns: auto auto auto minmax(0, 1fr) auto auto auto auto auto;
  padding: .25rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card); list-style: none;
}
.nw-row-grip { cursor: grab; color: var(--muted); font-size: .9rem; padding: 0 .1rem; }
.nw-row-move { display: flex; flex-direction: column; }
.nw-row-move button {
  border: 0; background: transparent; cursor: pointer; color: var(--muted);
  font-size: .6rem; line-height: 1; padding: 1px 2px;
}
.nw-row-move button:hover { color: var(--ink); }
.nw-row-swatch {
  width: 22px; height: 22px; border-radius: 6px; border: 1px solid rgba(0,0,0,.15);
  position: relative; overflow: hidden; cursor: pointer; flex: 0 0 auto;
}
.nw-row-swatch input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.nw-row-name {
  border: 1px solid transparent; border-radius: 6px; padding: .3rem .4rem;
  font: inherit; font-size: .92rem; background: transparent; color: var(--ink); min-width: 0;
}
.nw-row-name:hover { border-color: var(--line); }
.nw-row-name:focus { outline: none; border-color: var(--accent); background: var(--card); box-shadow: 0 0 0 3px var(--ring); }
.nw-row-chance { display: inline-flex; align-items: center; gap: 1px; }
.nw-row-chance input {
  width: 62px; border: 1px solid var(--line); border-radius: 6px;
  padding: .28rem .3rem; font: inherit; font-size: .85rem; text-align: right;
}
.nw-row-pct { color: var(--muted); font-size: .8rem; }
/* The picture control marks itself when the entry has one, so a glance down
   the list says which rows carry a photo. */
.nw-row-img { position: relative; overflow: hidden; cursor: pointer; font-size: .95rem;
              padding: 0 .15rem; opacity: .55; }
.nw-row-img.has { opacity: 1; }
.nw-row > button[data-rmimg] {
  border: 0; background: transparent; cursor: pointer; color: var(--muted);
  font-size: .9rem; padding: .2rem .25rem; border-radius: 6px;
}
.nw-row > button[data-rmimg]:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
.nw-row-img input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }
.nw-row > button[data-dup], .nw-row > button[data-del] {
  border: 0; background: transparent; cursor: pointer; color: var(--muted);
  font-size: .95rem; padding: .2rem .3rem; border-radius: 6px;
}
.nw-row > button[data-dup]:hover { color: var(--ink); background: var(--card-alt); }
.nw-row > button[data-del]:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }

/* Confetti and the winner dialog both sit over the wheel. */
/* Fixed, not absolute: the flakes are meant to fall across the whole screen
   rather than being clipped to the wheel's own box. pointer-events:none is
   what keeps a full-viewport overlay from swallowing every click on the page
   underneath — without it the confetti canvas would make the site inert. */
.nw-confetti {
  position: fixed; inset: 0; width: 100vw; height: 100vh;
  pointer-events: none; z-index: 200;
}
.nw-dialog {
  position: absolute; inset: 0; z-index: 4; display: flex;
  align-items: center; justify-content: center; padding: 1rem;
  background: rgba(15,13,46,.45);
}
.nw-dialog[hidden] { display: none; }
/* Sized for the room it is announcing in rather than for its text: a width
   rather than a max-width, so a three-letter answer and a long name produce
   the same dialog, and the result reads as the thing everyone was waiting
   for. It can still run out of stage on a short phone, hence the cap and the
   scroll — the buttons must stay reachable. */
.nw-dialog-box {
  background: var(--card); border-radius: var(--radius); padding: 2rem 2.25rem;
  box-shadow: 0 24px 60px rgba(20,16,60,.32); text-align: center;
  width: min(560px, 100%); max-width: min(560px, 100%);
  max-height: 100%; overflow-y: auto;
}
.nw-dialog-label {
  margin: 0; font-size: .85rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}
/* Round, because that is how the wheel and the magnifier already show the
   same picture, and cropped from the middle by object-fit so a portrait is
   not squashed into the square. */
.nw-dialog-photo {
  display: block; margin: .9rem auto .3rem;
  width: clamp(84px, 22vw, 132px); height: clamp(84px, 22vw, 132px);
  border-radius: 50%; object-fit: cover;
  box-shadow: 0 6px 18px rgba(20,16,60,.28);
}
.nw-dialog-photo[hidden] { display: none; }
.nw-dialog-name {
  margin: .3rem 0 1.25rem; font-size: clamp(1.9rem, 6vw, 3.2rem); font-weight: 700;
  line-height: 1.15; overflow-wrap: anywhere;
}
.nw-dialog-actions { display: flex; gap: .5rem; justify-content: center; flex-wrap: wrap; }

/* --- Colours from a picture ----------------------------------------------
   Fixed rather than absolute, unlike the winner's dialog: that one belongs
   over the wheel it is announcing, while this is opened from the settings
   column and wants the whole window whatever the layout is doing. */
.nw-picker {
  position: fixed; inset: 0; z-index: 210; display: flex;
  align-items: center; justify-content: center; padding: 1rem;
  background: rgba(15,13,46,.45);
}
.nw-picker[hidden] { display: none; }
.nw-picker-box {
  display: flex; flex-direction: column; gap: .75rem;
  width: min(440px, 100%); max-height: min(88vh, 720px); overflow-y: auto;
  background: var(--card); border-radius: var(--radius); padding: 1.25rem 1.4rem;
  box-shadow: 0 24px 60px rgba(20,16,60,.32);
}
.nw-picker-title { margin: 0; font-size: 1.05rem; }
.nw-picker-empty { margin: 0; }
.nw-picker-empty[hidden], .nw-picker-stage[hidden] { display: none; }
.nw-picker-stage { display: flex; flex-direction: column; gap: .6rem; }
.nw-picker-preview {
  display: block; width: 100%; aspect-ratio: 50 / 21;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--card-alt);
}
/* One row of the colours themselves, each labelled with its own hex in
   whichever ink is legible on it — a swatch you cannot copy the value of is
   half a result. */
.nw-picker-shot { position: relative; line-height: 0; }
/* The markers layer sits exactly over the picture and takes the pointer, so
   a drag anywhere on the photograph is a drag of the marker being held. */
.nw-picker-pins { position: absolute; inset: 0; }
.nw-pin {
  position: absolute; width: 24px; height: 24px; margin: -12px 0 0 -12px;
  padding: 0; cursor: grab; touch-action: none;
  display: grid; place-items: center;
  font: 700 11px/1 var(--font); font-variant-numeric: tabular-nums;
  border: 2px solid #fff; border-radius: 999px;
  box-shadow: 0 0 0 1px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.35);
}
.nw-pin:active { cursor: grabbing; }
.nw-pin:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.nw-picker-swatches { display: flex; flex-wrap: wrap; gap: .3rem; }
.nw-picker-chip {
  /* Wider than before: it now carries a number as well as a value, and
     hsl(210, 45%, 62%) is a good deal longer than #6a9bc3. */
  flex: 1 1 108px; min-width: 108px; height: 46px;
  display: flex; align-items: center; justify-content: center; gap: .3rem;
  padding: 0 .35rem;
  border-radius: var(--radius-sm); border: 1px solid rgba(0,0,0,.12);
  font: 600 .72rem/1 var(--mono, ui-monospace, monospace);
}
/* The number reads as an index rather than as part of the value: a ring in
   the chip's own contrast-chosen ink, so it works on a colour of any
   lightness without a second colour having to be computed for it. */
.nw-chip-no {
  flex: 0 0 auto; width: 16px; height: 16px;
  display: grid; place-items: center;
  border: 1px solid currentColor; border-radius: 999px; opacity: .75;
  font: 700 10px/1 var(--font); font-variant-numeric: tabular-nums;
}
.nw-chip-val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nw-frompic { align-self: flex-start; }

@media (max-width: 420px) {
  .nw-picker-box { padding: 1rem; gap: .6rem; }
  .nw-picker-chip { flex-basis: 92px; min-width: 92px; height: 40px; }
}


.nw-images { display: flex; flex-direction: column; gap: .4rem; }
.nw-images-title { font-weight: 600; font-size: .88rem; }
.nw-file { display: flex; flex-direction: column; gap: .2rem; font-size: .85rem; color: var(--ink-soft); }
.nw-file input[type="file"] { font-size: .82rem; }
.nw-images .btn { align-self: flex-start; padding: .4rem .75rem; font-size: .85rem; }
.nw-images .help { color: var(--muted); font-size: .78rem; }


.nw-entries-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .35rem; }
.nw-entries-head label { font-weight: 600; font-size: .92rem; }
.nw-entries textarea {
  width: 100%; border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .6rem .75rem; font-size: 1rem; font-family: inherit; line-height: 1.5;
  background: var(--card); color: var(--ink); resize: vertical;
}
.nw-entries textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
.nw-entries .help { display: block; margin-top: .3rem; color: var(--muted); font-size: .8rem; }

.nw-tools { display: flex; flex-wrap: wrap; gap: .5rem; }
.nw-tools .btn { padding: .45rem .8rem; font-size: .88rem; }
.nw-menu { position: relative; display: inline-flex; }
/* The caret says the button opens something rather than doing something.
   Drawn from two borders rather than set as a glyph, like the panel toggle's
   chevron: no font can render it as a box, and it turns to point at the menu
   it has opened — which it can do off aria-expanded, already kept in step for
   screen readers, so there is no second piece of state to go stale. */
.nw-addimg { display: inline-flex; align-items: center; gap: .45rem; }
.nw-caret {
  display: block; width: 6px; height: 6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .12s linear;
}
.nw-addimg[aria-expanded="true"] .nw-caret { transform: translateY(1px) rotate(225deg); }
/* Below the button. The row sits near the TOP of a panel with overflow-y:auto
   now, so a menu opening upwards would be clipped by the panel's own top edge
   — the same trap as before, the other way up. */
.nw-menu-pop {
  position: absolute; top: calc(100% + .35rem); left: 0; z-index: 5;
  display: flex; flex-direction: column; min-width: max(100%, 210px);
  padding: .25rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card); box-shadow: 0 10px 30px rgba(30,27,75,.22);
}
.nw-menu-pop[hidden] { display: none; }
.nw-menu-pop button {
  text-align: left; cursor: pointer; white-space: nowrap;
  border: 0; border-radius: var(--radius-sm); background: transparent;
  padding: .45rem .6rem; font: 500 .88rem/1.3 var(--font); color: var(--ink);
}
.nw-menu-pop button:hover { background: var(--card-alt); }
.nw-menu-pop button:focus-visible { outline: 3px solid var(--accent); outline-offset: -2px; }

.nw-opts { display: flex; flex-direction: column; gap: .4rem; }
.nw-opt { display: flex; align-items: center; gap: .5rem; font-size: .92rem; color: var(--ink-soft); cursor: pointer; }
.nw-opt input { width: 1.05rem; height: 1.05rem; accent-color: var(--accent); }
/* A three-way choice cannot be a checkbox, so this row is a label and a select
   pushed to opposite ends rather than a control followed by its text. */
.nw-opt-sel { justify-content: space-between; cursor: default; }
.nw-opt-sel select {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .25rem .45rem; font-size: .88rem; font-family: inherit;
  background: var(--card); color: var(--ink); cursor: pointer;
}
.nw-opt-sel select:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}

/* --- Settings, in three tabs ---------------------------------------------
   Lighter than the panel's own tabs above them: two rows of tabs at the same
   weight read as one confused row, so these are a segmented control rather
   than a second set of underlines. */
.nw-subtabs {
  display: flex; gap: .25rem; padding: .25rem;
  background: var(--card-alt); border: 1px solid var(--line);
  border-radius: 999px;
}
.nw-subtab {
  flex: 1 1 0; min-width: 0; cursor: pointer;
  border: 0; border-radius: 999px; padding: .4rem .5rem;
  font: 600 .82rem/1.2 var(--font); color: var(--ink-soft); background: transparent;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nw-subtab:hover { color: var(--ink); }
.nw-subtab.on { background: var(--seg-on); color: var(--ink); box-shadow: 0 1px 3px rgba(30,27,75,.14); }
.nw-subtab:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.nw-subpanel { display: flex; flex-direction: column; gap: 1rem; }
.nw-subpanel[hidden] { display: none; }
.nw-set { display: flex; flex-direction: column; gap: .45rem; }
/* Author display beats the user agent's [hidden] rule, so the custom
   colours box stayed on screen for every palette however many times the
   script set .hidden = true. Third time this stylesheet has needed the
   same line: any box given a display of its own must say it twice. */
.nw-set[hidden] { display: none; }

/* --- Palettes -------------------------------------------------------------
   The swatches are the control. A name on its own asks people to remember
   what "Berry" looks like, which is exactly what a preview is for. */
/* Light / Dark / Match device — the same segmented control as the settings
   tabs, because it is the same kind of choice. */
.nw-modes {
  display: flex; gap: .25rem; padding: .25rem;
  background: var(--card-alt); border: 1px solid var(--line); border-radius: 999px;
}
.nw-mode-pick {
  flex: 1 1 0; min-width: 0; cursor: pointer;
  border: 0; border-radius: 999px; padding: .35rem .5rem;
  font: 600 .8rem/1.2 var(--font); color: var(--ink-soft); background: transparent;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nw-mode-pick:hover { color: var(--ink); }
.nw-mode-pick.on { background: var(--seg-on); color: var(--ink); box-shadow: 0 1px 3px rgba(0,0,0,.18); }
.nw-mode-pick:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.nw-palettes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem; }
.nw-pal {
  display: flex; flex-direction: column; gap: .3rem; cursor: pointer;
  padding: .45rem .5rem; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card); color: var(--ink-soft); font: inherit;
}
.nw-pal:hover { border-color: var(--accent); color: var(--ink); }
.nw-pal.on {
  border-color: var(--accent); color: var(--ink);
  box-shadow: 0 0 0 2px var(--ring);
}
.nw-pal:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* ONE PAINTED BOX, NOT SIX SIZED ONES. The strip used to be six empty spans
   sharing the width with `flex: 1 1 0`, which asks a great deal of a box with
   no content: a flex container had to lay them out, that container had to be
   a flex item of a <button>, and each chip had to take its whole height from
   `align-items: stretch` alone. iOS Safari lost that bet and the strip came
   out with nothing in it, so every palette showed as a bare name — the one
   thing a preview must never do, since the name is exactly what the swatches
   are there to save you from having to remember. The script paints the same
   six bands as a gradient instead: no boxes to size, nothing to stretch, and
   no clipping needed to keep the paint inside the corners. */
.nw-pal-chips { display: block; width: 100%; height: 20px; border-radius: 4px; }
.nw-pal-name { font-size: .8rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Only on the palettes taken from pictures — the built-in ones cannot be
   forgotten, so they carry no cross to suggest otherwise. */
.nw-pal { position: relative; }
.nw-pal-x {
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--muted);
  font: 700 12px/1 var(--font); opacity: 0; transition: opacity .12s linear;
}
.nw-pal:hover .nw-pal-x, .nw-pal-x:focus { opacity: 1; }
.nw-pal-x:hover { color: var(--ink); border-color: var(--accent); }

/* --- Your own colours ---------------------------------------------------- */
.nw-swatches { display: flex; flex-wrap: wrap; gap: .4rem; }
.nw-swatch { position: relative; display: block; width: 44px; height: 34px; }
.nw-swatch-btn {
  width: 100%; height: 100%; padding: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.nw-swatch-btn.on { box-shadow: 0 0 0 2px var(--ring); border-color: var(--accent); }
.nw-swatch-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The grid. Ten to a row so the hues line up in columns and the lightnesses
   in rows — scanning it is then reading a table rather than hunting a list. */
.nw-gridpop {
  display: flex; flex-direction: column; gap: .5rem; margin-top: .1rem;
  padding: .5rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card-alt);
}
.nw-gridpop[hidden] { display: none; }
.nw-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 3px; }
.nw-grid-cell {
  aspect-ratio: 1; padding: 0; cursor: pointer;
  border: 1px solid rgba(0,0,0,.10); border-radius: 3px;
}
.nw-grid-cell:hover { transform: none; box-shadow: 0 0 0 2px var(--accent); }
.nw-grid-cell.on { box-shadow: 0 0 0 2px var(--ink); }
.nw-grid-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.nw-grid-foot { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.nw-grid-custom { display: flex; align-items: center; gap: .4rem; font-size: .82rem; color: var(--ink-soft); }
.nw-grid-custom input[type="color"] {
  width: 34px; height: 26px; padding: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: none;
}
.nw-grid-custom input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.nw-grid-custom input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 3px; }
/* Small, and only under the pointer or the keyboard: a row of permanent
   delete crosses reads as a warning rather than as a palette. */
.nw-swatch-x {
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--muted);
  font: 700 12px/1 var(--font); padding: 0; opacity: 0;
  transition: opacity .12s linear;
}
.nw-swatch:hover .nw-swatch-x,
.nw-swatch-x:focus-visible { opacity: 1; }
.nw-swatch-x:hover { color: var(--ink); border-color: var(--accent); }
.nw-swatch-add { align-self: flex-start; }

/* --- Volume and the other paired rows ------------------------------------ */
.nw-vol { display: flex; align-items: center; gap: .5rem; }
.nw-vol input[type="range"] { width: clamp(90px, 26vw, 150px); accent-color: var(--accent); }
.nw-vol output { min-width: 3.2em; text-align: right; font-variant-numeric: tabular-nums; font-size: .85rem; color: var(--muted); }
.nw-try { align-self: flex-start; }
.nw-secs { display: flex; align-items: center; gap: .4rem; }
.nw-secs input[type="number"] {
  width: 4.2rem; text-align: right;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .3rem .4rem; font: inherit; font-size: .88rem;
  background: var(--card); color: var(--ink);
}
.nw-secs input[type="number"]:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}
.nw-secs > span { font-size: .85rem; color: var(--muted); }
.nw-field { display: flex; flex-direction: column; gap: .25rem; font-size: .88rem; font-weight: 600; }
.nw-field input[type="text"] {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .45rem .6rem; font: inherit; font-weight: 400;
  background: var(--card); color: var(--ink);
}
.nw-field input[type="text"]:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}

@media (max-width: 420px) {
  .nw-palettes { grid-template-columns: minmax(0, 1fr); }
}


.nw-history-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.nw-history h3 { margin: 0 0 .3rem; font-size: 1rem; }
.nw-history ol {
  counter-reset: nw; max-height: 190px; overflow-y: auto;
  overscroll-behavior: contain; padding-right: .35rem;
}
.nw-history li {
  counter-increment: nw; list-style: none; padding: .3rem 0;
  border-bottom: 1px solid var(--line-soft); font-size: .92rem; overflow-wrap: anywhere;
}
.nw-history li::before { content: counter(nw) "."; color: var(--muted); margin-right: .5rem; font-variant-numeric: tabular-nums; }
.nw-history li:last-child { border-bottom: 0; }

@media (max-width: 900px) {
  .nw-body { grid-template-columns: 1fr; gap: 1.25rem; }
}


/* 10. The wheel's new parts ------------------------------------------------
   What 123SpinWheel adds to the ported picker: quick lists, the number range,
   the team splitter, copy and clear on the results, the share button in the
   winner pop-up and the keyboard hint. */
.namewheel { min-width: 0; }
.nw-noscript {
  margin: 0 0 1rem; padding: .8rem 1rem; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--ink);
}
.nw-mode-btn.on { background: var(--accent); color: var(--on-accent); }

/* The hint sits in the corner of the wheel's area rather than under it, so
   the wheel keeps every pixel of height the shell gave it. A touch screen
   has no keyboard to hint at. */
.nw-hint {
  position: absolute; left: 14px; bottom: 10px; z-index: 3; margin: 0;
  font-size: .74rem; color: var(--muted); pointer-events: none;
}
.nw-hint kbd { font-size: .7rem; }
@media (hover: none), (max-width: 720px) { .nw-hint { display: none; } }

/* Quick lists: one row of pills that scrolls sideways rather than wrapping
   into a block of buttons above the list. */
.nw-presets { display: grid; gap: .4rem; }
.nw-presets-label { font-weight: 600; font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.nw-chiprow { display: flex; flex-wrap: wrap; gap: .4rem; }
.nw-chip {
  border: 1px solid var(--line); background: var(--card); color: var(--ink-soft);
  border-radius: 999px; padding: .32rem .8rem; font: 600 .84rem/1.2 var(--font); cursor: pointer;
}
.nw-chip:hover { border-color: var(--accent); color: var(--ink); }
.nw-chip[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-600); }

/* From / To / Step, then the button, on one line where there is room. */
.nw-gen { display: grid; gap: .4rem; }
.nw-gen-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem; }
.nw-gen-row label { display: grid; gap: .2rem; font-size: .8rem; color: var(--muted); font-weight: 600; }
.nw-gen-row input { width: 6.5rem; padding: .45rem .55rem; font-size: .95rem; color: var(--ink); }
.nw-gen-row label:nth-child(3) input { width: 4.5rem; }
.nw-gen-row .btn { padding: .5rem 1rem; font-size: .9rem; }

/* Teams. Cards in a grid, each headed with its colour and its size. */
.nw-team-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .25rem; }
.nw-team-actions .btn { padding: .5rem 1rem; font-size: .9rem; }
.nw-team-actions .btn[hidden] { display: none; }
.nw-teams { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.nw-teams:empty { display: none; }
.nw-teams .nw-empty { grid-column: 1 / -1; }
.nw-team { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .6rem .75rem; background: var(--card-alt); }
.nw-team-name { display: flex; align-items: center; gap: .45rem; font-size: .95rem; margin: 0 0 .35rem; }
.nw-team-name .nw-dot { width: 12px; height: 12px; border-radius: 50%; border: 1px solid rgba(0,0,0,.12); flex: 0 0 auto; }
.nw-team-size { margin-left: auto; font-size: .75rem; color: var(--muted); background: var(--line-soft); border-radius: 999px; padding: .05rem .45rem; }
.nw-team ol { margin: 0; padding-left: 1.2rem; font-size: .9rem; }
.nw-team li { margin: .1rem 0; }

.nw-history-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.nw-history-actions { display: flex; gap: .8rem; font-size: .85rem; }

.nw-dialog-actions { flex-wrap: wrap; }
.nw-dialog-actions .btn svg { flex: 0 0 auto; }
.nw-filestatus .linkish { font-size: inherit; margin-left: .15rem; }
/* Four tabs and three file buttons share one row; a little less air in each
   keeps them on it at the panel's usual width. */
.nw-tab { padding: .5rem .5rem; }
.nw-tabs { gap: .1rem; }

/* 11. On a phone: an app ---------------------------------------------------
   At phone width the wheel page behaves like an app rather than a web page.
   The header stays put, the wheel has the screen to itself, a tab bar sits
   under it with Spin in the middle, and the panel that is a column on a
   computer becomes a sheet that slides up over the wheel (wheel.js opens and
   closes it). The result and the share dialog arrive the same way, from the
   bottom, where a thumb already is.

   The sheet and tab bar need the wheel script, so they are built under .js
   (set by theme.js before the first paint). With scripts off the page keeps
   the plain stacked layout, where every control is still on the page.

   env(safe-area-inset-*) is the notch and the home bar: zero in a browser
   tab, and real once the site is installed and runs full screen. */
:root { --tabbar-h: 0px; }
.nw-tabbar, .nw-scrim, .nw-sheet-head, .nw-tapnote, .nw-again { display: none; }
.site-header { padding-top: env(safe-area-inset-top); }

/* A finger has no hover: no grey flash on tap, no wait for a double-tap
   zoom, and a press that visibly gives. */
a, button, summary, label, .nw-canvas { -webkit-tap-highlight-color: transparent; }
a, button, summary, .nw-canvas { touch-action: manipulation; }
.btn, .nw-chip, .net { transition: transform .08s ease-out; }
.btn:active:not(:disabled), .nw-chip:active, .net:active { transform: scale(.97); }

.nav-install {
  display: flex; align-items: center; justify-content: center; gap: .45rem; width: 100%;
  margin-top: .4rem; padding: .6rem; border: 0; border-radius: 8px; cursor: pointer;
  background: var(--accent); color: var(--on-accent); font: 600 .92rem/1.2 var(--font);
}
.nav-install[hidden] { display: none; }

@keyframes nw-up { from { transform: translateY(100%); } to { transform: none; } }

/* Bigger targets wherever the pointer is a finger — a phone held either way
   up, or a tablet. A fingertip is about 40px across. */
@media (max-width: 720px), (pointer: coarse) {
  .nw-chip { padding: .5rem .95rem; font-size: .9rem; }
  .nw-tools .btn { padding: .6rem .9rem; font-size: .92rem; }
  .nw-filebar .btn { padding: .45rem .8rem; font-size: .85rem; }
  .nw-mode-btn { padding: .65rem 1.1rem; font-size: .92rem; }
  .nw-viewbtn { padding: .5rem .8rem; font-size: .8rem; }
  .nw-subtab { padding: .6rem .5rem; font-size: .88rem; }
  .nw-mode-pick { padding: .55rem .5rem; font-size: .88rem; }
  .nw-opt { min-height: 40px; font-size: .95rem; }
  .nw-opt input { width: 1.3rem; height: 1.3rem; }
  .nw-opt-sel select { padding: .45rem .6rem; }
  .nw-history-actions .linkish { padding: .35rem .2rem; }
  .nw-history li { padding: .5rem 0; font-size: 1rem; }
  .nw-row > button[data-dup], .nw-row > button[data-del],
  .nw-row > button[data-rmimg] { min-width: 36px; min-height: 36px; }
  .app-jump { padding: .5rem 0; }
}

@media (max-width: 720px) {
  :root { --header-h: 56px; }
  .site-header { position: sticky; top: 0; z-index: 35; }
  .header-inner {
    padding-left: max(.75rem, env(safe-area-inset-left));
    padding-right: max(.75rem, env(safe-area-inset-right));
  }
  .nav-menu > summary { padding: .55rem .8rem; }
  .theme-btn { width: 40px; height: 40px; }
  .nav-pop {
    top: calc(var(--header-h) + env(safe-area-inset-top) - .25rem);
    max-height: calc(100dvh - var(--header-h) - env(safe-area-inset-top) - var(--tabbar-h) - env(safe-area-inset-bottom) - 1rem);
  }

  /* iOS zooms the whole page into any text box under 16px the moment it is
     tapped, and does not zoom back out. Several controls here are smaller by
     design on a computer; on a phone none may be. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]),
  select, textarea { font-size: 16px !important; }

  .nw-gen-row input { width: 5.5rem; }

  /* Ready-made wheels: one row that scrolls sideways, like an app's shelf,
     rather than a block of ten tiles to scroll past. */
  .wheel-strip {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 7.25rem;
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
    scrollbar-width: none; margin: 0 -1.25rem; padding: .15rem 1.25rem .9rem;
    scroll-padding: 0 1.25rem;
  }
  .wheel-strip::-webkit-scrollbar { display: none; }
  .wheel-strip > li { scroll-snap-align: start; }

  /* The share dialog, as a sheet. */
  .modal { place-items: end center; padding: 0; }
  .modal-box {
    width: 100%; max-height: 92dvh; border-radius: 22px 22px 0 0; border-bottom: 0;
    padding: 1.9rem max(1.1rem, env(safe-area-inset-right)) calc(1.25rem + env(safe-area-inset-bottom)) max(1.1rem, env(safe-area-inset-left));
    animation: nw-up .3s cubic-bezier(.2, .8, .2, 1);
  }
  .modal-box::before {
    content: ""; position: absolute; top: 8px; left: 50%; width: 40px; height: 5px; margin-left: -20px;
    border-radius: 999px; background: var(--line);
  }
  .modal-x { top: .9rem; }
  /* The phone's own share sheet reaches every app on it, so it leads. */
  .share-nets .net-native {
    grid-column: 1 / -1; flex-direction: row; justify-content: center; gap: .5rem;
    padding: .85rem; border-color: var(--accent); background: var(--accent); color: var(--on-accent);
    font-size: .98rem;
  }
  .share-nets .net-native:hover { color: var(--on-accent); }
  .net { padding: .75rem .25rem; }
  .share-link .btn { padding: .75rem 1rem; }

  /* --- The wheel page ----------------------------------------------------- */
  .js body.page-app { --tabbar-h: 64px; padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
  .js body.page-app .app-shell {
    height: calc(100dvh - var(--header-h) - env(safe-area-inset-top) - var(--tabbar-h) - env(safe-area-inset-bottom));
    min-height: 340px;
  }
  .js .app-bar { height: 46px; padding-top: 0; padding-bottom: 0; }
  .js .app-bar .btn-share { display: none; }
  .js .app-body { display: flex; padding: 0; }
  .js .namewheel {
    display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
    /* The tallest the wheel can be: the screen, less the header, the title
       bar, the tab bar and the line under the wheel. Worked out rather than
       measured, so the wheel is the right size on the first paint. */
    --nw-wheel-max: calc(100dvh - var(--header-h) - env(safe-area-inset-top) - var(--tabbar-h)
                    - env(safe-area-inset-bottom) - 46px - 3.25rem);
  }
  .js .nw-body { display: flex; align-items: stretch; flex: 1 1 auto; min-height: 0; gap: 0; }
  .js .nw-main {
    flex: 1 1 auto; flex-direction: column; gap: .6rem; min-height: 0;
    padding: .5rem .75rem .6rem;
  }
  .js .nw-stage { width: min(100%, max(200px, var(--nw-wheel-max))); max-width: none; flex: 0 0 auto; margin: 0 auto; }
  .js .nw-panelbtn { display: none; }
  .js .nw-tapnote {
    display: block; margin: 0; max-width: 100%; text-align: center;
    font-size: .9rem; line-height: 1.35rem; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .js .nw-tapnote strong { color: var(--ink); }
  .namewheel:fullscreen { --nw-wheel-max: calc(100dvh - 3.5rem); }
  .namewheel:fullscreen .nw-tabbar { display: none; }

  /* The tab bar. Frosted, so the page scrolling under it still shows. */
  .js .nw-tabbar {
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    background: color-mix(in srgb, var(--card) 88%, transparent);
    -webkit-backdrop-filter: saturate(1.8) blur(16px); backdrop-filter: saturate(1.8) blur(16px);
    border-top: 1px solid var(--line);
  }
  .nw-tb {
    position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; min-width: 0; height: var(--tabbar-h); padding: 0;
    border: 0; background: none; color: var(--muted); font: 600 .7rem/1.1 var(--font); cursor: pointer;
  }
  .nw-tb > svg { flex: 0 0 auto; transition: transform .08s ease-out; }
  .nw-tb:active > svg { transform: scale(.88); }
  .nw-tb > span:not([class]) { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nw-tb.on { color: var(--accent-600); }
  .nw-tb:focus-visible { outline-offset: -4px; border-radius: 12px; }
  .nw-tb-count {
    position: absolute; top: 7px; left: calc(50% + 5px); min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px; background: var(--pop); color: #1e1b4b; font: 700 .68rem/18px var(--font); text-align: center;
  }
  .nw-tb-count[hidden] { display: none; }
  .nw-tb-spin { color: var(--ink); justify-content: flex-end; padding-bottom: 7px; }
  .nw-tb-disc {
    display: grid; place-items: center; width: 58px; height: 58px; margin-top: -30px;
    border-radius: 50%; background: var(--accent); color: var(--on-accent);
    box-shadow: 0 0 0 4px var(--bg), 0 8px 20px rgba(79, 70, 229, .45);
    transition: transform .08s ease-out;
  }
  .nw-tb-spin:active .nw-tb-disc { transform: scale(.93); }
  .nw-tb-disc svg { width: 28px; height: 28px; }

  /* The sheet. Below the screen until opened; a fixed height whichever tab
     is showing, so switching tabs does not make it jump. --nw-kb and
     --nw-vvh come from wheel.js and keep it above the on-screen keyboard. */
  html.nw-locked { overflow: hidden; }
  .js .nw-scrim {
    display: block; position: fixed; inset: 0; z-index: 40;
    background: rgba(15, 13, 46, .45); opacity: 0; pointer-events: none; transition: opacity .3s;
  }
  .js .nw-sheet-on .nw-scrim { opacity: 1; pointer-events: auto; }
  .js .nw-side {
    position: fixed; left: 0; right: 0; bottom: var(--nw-kb, 0px); z-index: 41;
    height: min(86dvh, calc(var(--nw-vvh, 100dvh) - 12px));
    margin: 0; gap: .9rem; border: 0; border-radius: 22px 22px 0 0;
    padding: 0 max(1rem, env(safe-area-inset-right)) calc(1.5rem + env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));
    box-shadow: 0 -12px 40px rgba(15, 13, 46, .28);
    overflow-y: auto; overscroll-behavior: contain;
    transform: translateY(calc(100% + 24px)); visibility: hidden;
    transition: transform .3s cubic-bezier(.2, .8, .2, 1), visibility 0s linear .3s;
  }
  .js .nw-sheet-on .nw-side {
    transform: none; visibility: visible;
    transition: transform .3s cubic-bezier(.2, .8, .2, 1), visibility 0s;
  }
  .js .nw-side.nw-dragging { transition: none; }
  /* The handle and the tabs stay at the top while the panel scrolls. */
  .js .nw-sheet-top {
    position: sticky; top: 0; z-index: 3; background: var(--card);
    margin: 0 -1rem; padding: 0 1rem .2rem;
  }
  .js .nw-sheet-head {
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
    height: 46px; touch-action: none; cursor: grab;
  }
  .nw-grip { grid-column: 2; width: 40px; height: 5px; border-radius: 999px; background: var(--line); }
  .nw-sheet-done {
    grid-column: 3; justify-self: end; border: 0; background: none; cursor: pointer;
    color: var(--accent-600); font: 700 1rem/1 var(--font); padding: .7rem .1rem .7rem .9rem;
  }
  .js .nw-tabrow { flex-direction: column; align-items: stretch; gap: .55rem; border-bottom: 0; }
  .js .nw-tabs {
    gap: .2rem; padding: .25rem; border: 1px solid var(--line); border-radius: 12px; background: var(--card-alt);
  }
  .js .nw-tab {
    flex: 1 1 0; justify-content: center; min-width: 0; margin: 0; padding: .6rem .3rem;
    border: 0; border-radius: 9px; font-size: .9rem; color: var(--ink-soft);
  }
  .js .nw-tab.on { background: var(--seg-on); color: var(--ink); box-shadow: 0 1px 3px rgba(30, 27, 75, .16); }
  .js .nw-filebar { margin-left: 0; padding-bottom: 0; justify-content: flex-end; }
  .js .nw-filestatus { margin-top: -.4rem; }

  /* The result, as a sheet over the bottom of the screen, with the wheel
     still in sight above it and the pointer on the winning slice. */
  .js .nw-dialog {
    position: fixed; inset: 0; z-index: 190; align-items: flex-end; padding: 0;
    background: rgba(15, 13, 46, .3);
  }
  .js .nw-dialog-box {
    width: 100%; max-width: none; max-height: 82dvh; border-radius: 24px 24px 0 0;
    padding: 1.4rem max(1.25rem, env(safe-area-inset-right)) calc(1.1rem + env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left));
    animation: nw-up .32s cubic-bezier(.2, .8, .2, 1);
  }
  .js .nw-dialog-name { font-size: clamp(1.8rem, 9vw, 2.6rem); margin-bottom: 1.1rem; }
  .js .nw-dialog-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
  .js .nw-dialog-actions .btn { width: 100%; padding: .85rem .5rem; white-space: normal; }
  .js .nw-dialog-actions .nw-again { display: inline-flex; grid-column: 1 / -1; font-size: 1.05rem; }
  .js .nw-dialog-actions [data-nw-dialog-close] {
    grid-column: 1 / -1; order: 9; background: none; color: var(--ink-soft); padding: .6rem;
  }
}

/* A phone on its side is wider than the phone layout, and has very little
   height: the two-column layout, without the minimum height that would push
   the bottom of the wheel off the screen. */
@media (min-width: 721px) and (max-height: 520px) {
  .app-shell { min-height: 0; }
  .app-sub { display: none; }
  .app-bar { padding-top: .3rem; padding-bottom: .3rem; }
}
