:root {
  color-scheme: light dark;
  --bg: #fff;
  --fg: #18181b;
  --mut: #71717a;
  --bd: #e4e4e7;
  --card: #fafafa;
  --ac: #4f46e5;
  --ac-fg: #fff;
  --ok: #059669;
  --warn: #b45309;
  --err: #dc2626;
  --rad: 6px;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a0a0b; --fg: #f4f4f5; --mut: #a1a1aa; --bd: #27272a; --card: #141416;
    --ac: #818cf8; --ac-fg: #0a0a0b; --ok: #34d399; --warn: #fbbf24; --err: #f87171;
  }
}
:root[data-theme="light"] {
  --bg: #fff; --fg: #18181b; --mut: #71717a; --bd: #e4e4e7; --card: #fafafa;
  --ac: #4f46e5; --ac-fg: #fff; --ok: #059669; --warn: #b45309; --err: #dc2626;
}
:root[data-theme="dark"] {
  --bg: #0a0a0b; --fg: #f4f4f5; --mut: #a1a1aa; --bd: #27272a; --card: #141416;
  --ac: #818cf8; --ac-fg: #0a0a0b; --ok: #34d399; --warn: #fbbf24; --err: #f87171;
}

* { box-sizing: border-box; }
body {
  margin: 0; padding: 20px; background: var(--bg); color: var(--fg);
  font: 14px/1.45 var(--sans); max-width: 1000px; margin-inline: auto;
}
.bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
h1 { font-size: 17px; font-weight: 700; margin: 0; letter-spacing: -.01em; }

.tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 16px; }
.tab {
  padding: 7px 13px; border: 1px solid var(--bd); border-radius: var(--rad);
  background: var(--bg); color: var(--mut); font: inherit; cursor: pointer;
}
.tab[aria-selected="true"] { background: var(--ac); color: var(--ac-fg); border-color: var(--ac); font-weight: 600; }

.layout { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }

.card { border: 1px solid var(--bd); border-radius: var(--rad); background: var(--card); padding: 14px; }
.field + .field, .rows { margin-top: 12px; }
.label-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.label { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; color: var(--mut); font-weight: 600; }
.hint { font-size: 11px; color: var(--mut); }

.slider-row { display: flex; align-items: center; gap: 12px; }
input[type="range"] { flex: 1; min-width: 0; accent-color: var(--ac); height: 26px; }
.num { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--bd);
       border-radius: var(--rad); padding: 4px 9px; background: var(--bg); }
/* 16px keeps iOS Safari from zooming the page on focus. Do not reduce. */
.num input, .mini { font: 600 16px var(--mono); border: 0; background: none; color: inherit;
                    width: 3.4em; text-align: right; padding: 0; }
.mini { border: 1px solid var(--bd); border-radius: var(--rad); padding: 5px 8px; background: var(--bg); }
.num input:focus, .mini:focus, select:focus { outline: 2px solid var(--ac); outline-offset: 1px; }
.unit { font-size: 11px; color: var(--mut); }

.chips { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 10px; }
.chip { padding: 6px 11px; border: 1px solid var(--bd); border-radius: var(--rad);
        background: var(--bg); color: var(--mut); font: 12px var(--mono); cursor: pointer; }
.chip[aria-pressed="true"] { background: var(--ac); color: var(--ac-fg); border-color: var(--ac); font-weight: 600; }

.rows.bordered { border-top: 1px solid var(--bd); margin-top: 12px; padding-top: 6px; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 44px; }
.row-main { display: inline-flex; align-items: center; gap: 9px; }
input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--ac); margin: 0; }
select { font: inherit; color: inherit; background: var(--bg); border: 1px solid var(--bd);
         border-radius: var(--rad); padding: 8px 9px; width: 100%; }

.entropy { margin-top: 14px; }
.entropy-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.entropy-label { font-weight: 700; font-size: 13px; color: var(--ok); }
.entropy-bits { font: 12px var(--mono); color: var(--mut); }
.segments { display: flex; gap: 3px; }
.segments i { flex: 1; height: 6px; border-radius: 2px; background: var(--bd); }
.segments i.on { background: var(--ok); }
.warning { font-size: 12px; color: var(--warn); margin: 8px 0 0; }
.error { font-size: 13px; color: var(--err); margin: 0 0 10px; }

.primary { display: block; width: 100%; margin-top: 14px; padding: 11px; background: var(--ac);
           color: var(--ac-fg); border: 0; border-radius: var(--rad); font: 600 14px var(--sans); cursor: pointer; }

/* No `overflow: hidden` here: it would establish this box as the scrolling
   container `.list-head`'s `position: sticky` resolves against. Since `.list`
   itself never scrolls internally (the page does), that silently breaks the
   sticky header instead of clipping anything — verified by scrolling a
   100-item list and watching `.list-head` scroll off with the rest instead of
   freezing at `top: 0`. The rounded corners are recreated on the children
   that actually sit in them instead. */
.list { border: 1px solid var(--bd); border-radius: var(--rad); background: var(--card); }
.list-head {
  position: sticky; top: 0; z-index: 1; display: flex; justify-content: space-between; align-items: center;
  gap: 10px; padding: 8px 10px; background: var(--card); border-bottom: 1px solid var(--bd);
  border-radius: var(--rad) var(--rad) 0 0;
  font-size: 10px; text-transform: uppercase; letter-spacing: .07em; color: var(--mut); font-weight: 600;
  user-select: none; -webkit-user-select: none;
}
.list-actions { display: flex; gap: 6px; }
.text-btn { font: 600 11px var(--sans); text-transform: uppercase; letter-spacing: .07em;
            color: var(--mut); background: var(--bg); border: 1px solid var(--bd);
            border-radius: var(--rad); padding: 6px 9px; min-height: 32px; cursor: pointer;
            user-select: none; -webkit-user-select: none; }
.rows-out { list-style: none; margin: 0; padding: 0; }
.out-row { display: flex; align-items: center; justify-content: space-between; gap: 9px;
           padding: 7px 10px; border-bottom: 1px solid var(--bd); min-height: 44px; }
.out-row:last-child { border-bottom: 0; border-radius: 0 0 var(--rad) var(--rad); }
/* Long values must wrap: base64 of 32 bytes is 44 chars, hex runs to 64, UUID is 36. */
.out-val { font: 13px var(--mono); overflow-wrap: anywhere; min-width: 0; }
.copy-btn { flex: none; width: 40px; height: 40px; display: inline-flex; align-items: center;
            justify-content: center; border: 1px solid var(--bd); border-radius: var(--rad);
            background: var(--bg); color: var(--mut); cursor: pointer;
            user-select: none; -webkit-user-select: none; }
.copy-btn.done { color: var(--ok); border-color: var(--ok); }
.copy-btn.failed, .text-btn.failed { color: var(--err); border-color: var(--err); }
.copy-status { margin: 0; padding: 8px 10px; font-size: 12px; color: var(--err);
               border-top: 1px solid var(--bd); }
.copy-btn svg { width: 15px; height: 15px; }
::selection { background: color-mix(in srgb, var(--ac) 25%, transparent); }

.icon-btn { background: var(--bg); border: 1px solid var(--bd); border-radius: var(--rad);
            width: 36px; height: 36px; cursor: pointer; color: var(--mut); font-size: 15px; }
.sheet-toggle { display: none; }

@media (max-width: 720px) {
  body { padding: 14px 14px 76px; }

  /* Results first, settings second — the sheet is pinned, so source order
     only matters for the non-sheet fallback and for the tab sequence. */
  .layout { grid-template-columns: 1fr; }
  .output { order: 1; }
  .settings { order: 2; }

  .settings {
    position: fixed; left: 0; right: 0; bottom: -100vh; z-index: 20;
    background: var(--bg); border-top: 2px solid var(--ac);
    border-radius: 12px 12px 0 0; box-shadow: 0 -6px 20px rgb(0 0 0 / .18);
    padding: 14px;
    overflow-y: auto;
    /* Slides via `bottom`, not `transform` — a transformed ancestor becomes
       the containing block for fixed descendants, which would drag `.entropy`
       (see below) off-screen along with the sheet, above the height floor
       where `.entropy` escapes it. `bottom` avoids that. */
    transition: bottom .18s ease;
    visibility: hidden;
  }
  .settings.open { bottom: 0; visibility: visible; }

  /* Regenerate lives in the sticky list header on narrow screens. The button
     inside the sheet is redundant there — every settings change regenerates. */
  #regenerate { display: none; }

  /* --- Height floor: a pinned strength readout above the sheet, and a sheet
     with room to be useful, cannot both fit once there isn't much vertical
     space to begin with — `.settings`'s `height: calc(100% - var(--sheet-top))`
     (below) cannot go negative, `box-sizing: border-box` floors it at its own
     border+padding total (~30px) instead, and once clamped there the box's
     *top* edge keeps rising as the viewport shrinks further, back into
     `.entropy`'s reserved space — the same overlap this task exists to
     prevent, just from the opposite direction. Measured crossover (375px
     width, sheet open): ~205-206px tall by default, ~243-248px with the
     warning line showing. Rather than chase a universal pin that this
     arithmetic cannot deliver, 300px is a deliberate floor: comfortably above
     the worst measured crossover (248px) with margin, and comfortably below
     667×375 (iPhone SE/8 landscape, no keyboard) and every other viewport
     this feature is meant to support, so those stay pinned. Below 300px —
     realistically an Android phone in landscape with the on-screen keyboard
     open, the very fields the keyboard targets — `.entropy` returns to
     normal flow inside `#settings` (see the `max-height: 299.98px` block):
     an in-flow element cannot overlap its own container, so the overlap
     becomes structurally impossible rather than margin-dependent. That's a
     deliberate trade, not a silent failure: the readout stays present and
     reachable whenever the sheet is open, on par with the fields it
     describes — what's given up below the floor is only staying pinned
     above the sheet, and visibility without opening the sheet. */
  @media (min-height: 300px) {
    /* Single source of truth for "how far down the entropy readout
       reaches." Both the results reservation (.output/.list-head) and the
       sheet's own extent (.settings) are expressed off this one boundary
       instead of independently — a fixed-pixel reservation next to a
       `vh`-based sheet cap is exactly what let them drift apart at short
       viewport heights in an earlier pass. Tying both to `--sheet-top`
       means there is one number to keep correct, not two that can disagree.
       --header-h: measured header+tabs height — depends on viewport *width*
         (tab wrapping), not height, so it's stable across the heights this
         fix is for.
       --entropy-reserve: `.entropy`'s tallest rendering (fields + the
         contradictory-requirements warning line, measured 95.6px at the
         narrowest supported width, where wrapping is worst) plus a small
         gap. */
    :root {
      --header-h: 124px;
      --entropy-reserve: 110px;
      --sheet-top: calc(var(--header-h) + var(--entropy-reserve));
    }

    .settings {
      /* Anchored to fill from just below the entropy readout down to the
         toggle, rather than capped at a fraction of viewport height
         (`62vh`). `height: calc(100% - var(--sheet-top))` and `--sheet-top`
         scale together by construction, so the sheet can never grow into
         the entropy readout's space at any height above the floor above —
         a vh-based cap and a px-based reservation could (and did) fall out
         of sync instead. `100%` resolves against the viewport because this
         box's containing block is the initial containing block (no
         transformed ancestor). */
      height: calc(100% - var(--sheet-top));
    }

    /* `.entropy` is markup nested inside `#settings` (the sheet), but the
       plan requires the live strength readout to stay visible above the
       sheet at all times — on load, and while the sheet is open — which is
       only possible if it escapes the sheet's own box. `position: fixed`
       here anchors to the viewport (not `#settings`) because `#settings`
       never uses `transform`; `visibility: visible` overrides the `hidden`
       value `#settings.settings` (closed) would otherwise inherit down to
       it. */
    .entropy {
      position: fixed; visibility: visible; z-index: 15;
      top: var(--header-h); left: 14px; right: 14px; margin: 0;
      background: var(--bg); border: 1px solid var(--bd); border-radius: var(--rad);
      padding: 10px 12px; box-shadow: 0 2px 6px rgb(0 0 0 / .1);
    }

    /* Reserve space under the fixed entropy bar so it doesn't cover the top
       of the results on load, and keep the sticky list header settling just
       below it (rather than under it) once the page scrolls — same
       `--sheet-top` boundary the sheet itself respects. */
    .output { margin-top: var(--entropy-reserve); }
    .list-head { top: var(--sheet-top); }
  }

  /* Below the floor, `.entropy` is not overridden above, so it keeps its
     un-mediaed `position: static` and renders in its original document
     position inside `#settings`, between the fields and the (hidden)
     Regenerate button. `.output`/`.list-head` likewise keep their
     un-mediaed defaults (no extra top reservation, sticky `top: 0`) since
     nothing above them needs the space. `.settings` still needs a height
     cap so its content stays reachable by scrolling rather than the box
     silently growing taller than the viewport with nothing to scroll it. */
  @media (max-height: 299.98px) {
    .settings { max-height: 100%; }
  }

  .sheet-toggle {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 21;
    align-items: center; justify-content: space-between; gap: 10px;
    padding: 14px; min-height: 56px;
    background: var(--bg); border: 0; border-top: 1px solid var(--bd);
    color: var(--fg); font: inherit; text-align: left; cursor: pointer;
  }
  #sheet-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .chev { color: var(--mut); flex: none; }

  .tabs { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 4px; }
  .tab { min-height: 40px; white-space: nowrap; }
  .text-btn { min-height: 40px; padding: 8px 11px; }
}
