/* Monochrome by design. The accent is ink rather than a brand hue, and the only
   actual colour on the page is the strength readout — so colour means something
   when it appears. The previous palette was Tailwind's zinc scale paired with
   indigo-600, which is the most recognisable default in modern web UI and made a
   purpose-built tool look like scaffolding. */
:root {
  color-scheme: light dark;
  --bg: #fbfbfa;
  --fg: #1c1f24;
  --mut: #71767e;
  --bd: #dcdcd8;
  --card: #f4f4f1;
  --ac: #1c1f24;
  --ac-fg: #fbfbfa;
  --ok: #3f7d4e;
  --warn: #8a6a1f;
  --err: #a3352b;
  --rad: 2px;
  --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: #111316; --fg: #e6e6e3; --mut: #8b9098; --bd: #282b30; --card: #191c20;
    --ac: #e6e6e3; --ac-fg: #111316; --ok: #6bbb7f; --warn: #d0a54a; --err: #e0776c;
  }
}
:root[data-theme="light"] {
  --bg: #fbfbfa; --fg: #1c1f24; --mut: #71767e; --bd: #dcdcd8; --card: #f4f4f1;
  --ac: #1c1f24; --ac-fg: #fbfbfa; --ok: #3f7d4e; --warn: #8a6a1f; --err: #a3352b;
}
:root[data-theme="dark"] {
  --bg: #111316; --fg: #e6e6e3; --mut: #8b9098; --bd: #282b30; --card: #191c20;
  --ac: #e6e6e3; --ac-fg: #111316; --ok: #6bbb7f; --warn: #d0a54a; --err: #e0776c;
}

* { box-sizing: border-box; }

/* iOS Safari reads two quick taps in the same spot as double-tap-to-zoom, so
   hitting `new` a few times in a row zoomed the whole page instead of generating
   twice. `manipulation` removes that one gesture and nothing else: **pinch zoom
   still works**, as does browser zoom. That distinction is the whole point —
   `user-scalable=no` and `maximum-scale=1` would also stop the zoom, and both are
   ruled out because they take zooming away from people who need it.
   Applied to controls only. On ordinary text, double-tap zoom is a legitimate
   reading aid and there is no repeated-tap problem to solve. `.out-val` counts as
   a control here, since tapping it copies. */
button, input, select, label, .out-val { touch-action: manipulation; }
/* Monospace for the chrome, not only the output. Everything this tool emits is a
   monospace string, so the interface speaking the same language is the one piece
   of identity available for free. `--sans` stays defined for anything that
   deliberately wants proportional text later. */
body {
  margin: 0; padding: 20px; background: var(--bg); color: var(--fg);
  font: 14px/1.45 var(--mono); max-width: 1000px; margin-inline: auto;
}
.bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
/* The full name is 30 monospace characters — about 270px at 15px — sharing this
   row with the theme toggle. One size at every width: it fits on one line from
   roughly 350px of viewport upward, and below that it wraps rather than
   overflowing, because the flex item's min-content width is its longest word
   ("Generator"), not the whole string. */
h1 { font-size: 15px; font-weight: 600; margin: 0; letter-spacing: -.02em; }
h1::after { content: "_"; color: var(--mut); animation: caret 1.6s steps(1) infinite; }
@keyframes caret { 0%, 60% { opacity: 1 } 61%, 100% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) { h1::after { animation: none } }

.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; }

/* Hairlines instead of boxes. Wrapping every group in a bordered, tinted,
   equally-rounded rectangle gave the page a uniform dashboard texture with no
   hierarchy beyond vertical order. Rules separate the same groups without
   drawing four sides around each of them. */
.card { border: 0; border-radius: 0; background: none; padding: 0; }
.field + .field, .rows { margin-top: 13px; border-top: 1px solid var(--bd); padding-top: 13px; }
.label-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
/* Lowercase, no letterspacing. Uppercase micro-labels with tracking are the
   other half of the dashboard tell. */
.label { font-size: 11px; text-transform: lowercase; letter-spacing: 0; 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%; }

/* Sits above the results list, in both columns' terms at the top of the output
   column — so no top margin, or it would hang below the settings column. */
.entropy { margin: 0 0 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(--mono); 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. */
/* Rules top and bottom, no side borders and no fill — the list reads as a block
   of values rather than a widget. */
.list { border: 1px solid var(--bd); border-width: 1px 0; border-radius: 0; background: none; }
.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(--bg); border-bottom: 1px solid var(--bd);
  font-size: 11px; text-transform: lowercase; letter-spacing: 0; 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(--mono); text-transform: lowercase; letter-spacing: 0;
            color: var(--mut); background: none; border: 0;
            border-radius: 3px; padding: 6px 9px; min-height: 32px; cursor: pointer;
            transition: background-color .12s ease, color .12s ease;
            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.
   Bigger than the chrome on purpose: these are what the page is for, and the
   size contrast is what makes the layout read as deliberate. The negative margin
   cancels the padding so giving the value a hover surface does not change row
   height. `cursor: copy` names the action, and the text stays selectable under it. */
.out-val { font: 16px var(--mono); overflow-wrap: anywhere; min-width: 0;
           cursor: copy; border-radius: 3px; padding: 2px 5px; margin: -2px -5px;
           transition: background-color .12s ease, color .12s ease; }
.out-val:hover { background: color-mix(in srgb, var(--fg) 7%, transparent); }
.out-val.done { background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok); }
.out-val.failed { background: color-mix(in srgb, var(--err) 18%, transparent); }

/* No frame on the copy affordances: the icon is the affordance and the surface
   appears only under the pointer. The 40px target and `user-select: none` both
   stay — the target because a finger needs the area whether or not anything is
   drawn around it, and user-select because a selectable button would put its own
   text into a multi-row copy, which is the defect the copy interception exists
   to prevent. */
.copy-btn { flex: none; width: 40px; height: 40px; display: inline-flex; align-items: center;
            justify-content: center; border: 0; border-radius: 3px;
            background: none; color: var(--mut); cursor: pointer;
            transition: background-color .12s ease, color .12s ease;
            user-select: none; -webkit-user-select: none; }
.copy-btn:hover, .text-btn:hover {
  background: color-mix(in srgb, var(--fg) 9%, transparent); color: var(--fg);
}
.copy-btn:active, .text-btn:active { background: color-mix(in srgb, var(--fg) 16%, transparent); }
/* Removing the border removed the only cue a keyboard user had, so the ring is
   not optional. Inset, because there is no border to sit outside of. */
.copy-btn:focus-visible, .text-btn:focus-visible {
  outline: 2px solid var(--ac); outline-offset: -2px;
}
/* Success and failure now carry as colour plus a tint, there being no border left
   to recolour. */
.copy-btn.done, .text-btn.done { color: var(--ok); background: color-mix(in srgb, var(--ok) 14%, transparent); }
.copy-btn.failed, .text-btn.failed { color: var(--err); background: color-mix(in srgb, var(--err) 14%, transparent); }
.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;
    /* Capped rather than anchored to a computed boundary. Nothing inside the
       sheet is positioned out of it any more, so there is no element above whose
       space the sheet has to respect — it only has to leave some results in view
       and keep its own content scrollable. `dvh` first for browsers that have it,
       since `vh` overshoots while mobile browser chrome is showing. */
    max-height: 80vh;
    max-height: 80dvh;
    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; }

  /* The strength readout used to be pinned out of this sheet, which forced a
     computed boundary the sheet had to respect, a space reservation above the
     results, a shifted sticky header, and a 300px height floor below which the
     whole arrangement collapsed. Moving `.entropy` into the results section in
     the markup removed the reason for all of it: an element in normal flow
     cannot be overlapped by a sheet that slides in front of it. Roughly 95
     lines of CSS went with the workaround.

     Regenerate lives in the sticky list header on narrow screens. The button
     inside the sheet is redundant there — every settings change regenerates. */

  .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; }
}
