/* Colors follow josephscott.org: green tinted neutrals with its green
   as the accent, so the two sites read as one.

   The grays are a readable scale, not a decorative one.  --faint,
   --muted2, and --muted each clear WCAG AA (4.5:1) on the darkest
   background they are used against, and stay a visible step apart from
   each other, so hierarchy survives without anything dropping out of
   legibility.  --btnborder clears 3:1 so the edge of a control is
   findable.  --line and --line2 are decorative separators and are
   deliberately left faint.  Check with the numbers before lightening
   any of these. */
:root{
  color-scheme:light dark;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --bf:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --hf:Georgia,"Iowan Old Style","Times New Roman",serif;
  --bg:#EBEFEC; --panel:#FBFBFB; --panel2:#F3F5F3; --pillbg:#E0E6E1;
  --ink:#080C09; --ink2:#2A2F2B; --muted:#454946; --muted2:#575D59; --faint:#666C67;
  --line:#D3DAD5; --line2:#E2E7E3; --btnborder:#7C8A80;
  --ac:#007629; --ac2:#005D20; --acsoft:rgb(0 118 41 / .10);
  --focus:rgb(0 118 41 / .4); --onac:#FAFCFA;
  /* diff: a tint for a whole changed line, a stronger one for the words
     inside it that actually moved */
  --addbg:#E4F6E9; --addbg2:#B7E9C6; --delbg:#FCE9E7; --delbg2:#F6C6C2; --skipbg:#EEF1EE;
  --errbg:oklch(52% .16 25 / .08); --errline:oklch(52% .16 25 / .24); --err:oklch(42% .16 25);
  --scrim:rgb(8 12 9 / .38); --sh:0 1px 3px rgb(8 12 9 / .07);
  --shbig:0 20px 54px rgb(8 12 9 / .22);
  --r1:16px; --r2:11px; --r3:9px; --bw:1px; --ltt:none; --lls:.02em;
}
@media (prefers-color-scheme:dark){:root{
  --bg:#0B0F0C; --panel:#131814; --panel2:#191F1A; --pillbg:#242C26;
  --ink:#DADFDB; --ink2:#C9CFCA; --muted:#B4B9B5; --muted2:#A1A6A2; --faint:#8D948E;
  --line:#28302A; --line2:#20271F; --btnborder:#5F6D63;
  --ac:#31AA5B; --ac2:#43C06F; --acsoft:rgb(49 170 91 / .14);
  --focus:rgb(49 170 91 / .45); --onac:#0B140D;
  --addbg:#0E2617; --addbg2:#1B5030; --delbg:#2B1315; --delbg2:#5C2024; --skipbg:#141A15;
  --errbg:oklch(65% .17 25 / .12); --errline:oklch(65% .17 25 / .3); --err:oklch(80% .13 25);
  --scrim:rgb(2 5 2 / .66); --sh:none; --shbig:0 20px 54px rgb(0 0 0 / .55);
}}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;min-height:100vh;display:flex;flex-direction:column;background:var(--bg);color:var(--ink);
  font-family:var(--bf);font-size:16px;-webkit-font-smoothing:antialiased}
a{color:var(--ac);text-decoration:none}
a:hover{color:var(--ac2)}
:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
::placeholder{color:var(--faint)}
.skip{position:absolute;left:-9999px}
.skip:focus{left:12px;top:12px;padding:8px 12px;background:var(--panel);border:var(--bw) solid var(--line);border-radius:var(--r2);z-index:99}

/* header */
.site-header{display:flex;flex-wrap:wrap;gap:10px 24px;align-items:baseline;justify-content:space-between;
  padding:16px clamp(16px,4vw,40px);border-bottom:var(--bw) solid var(--line);background:var(--panel2)}
.wordmark{font-family:var(--hf);font-size:18.5px;font-weight:650;letter-spacing:-.01em;color:var(--ink)}
.wordmark span{color:var(--faint);font-weight:500}
/* Both halves react together.  The generic a:hover only reaches the part
   outside the span, which lit up "josephscott.org" alone and read as a
   link to the parent site rather than to this one. */
.wordmark:hover{color:var(--ac2)}
.wordmark:hover span{color:var(--ac2);opacity:.6}
.up{font-size:15px;color:var(--muted)}

/* layout */
.layout{flex:1;display:flex;flex-wrap:wrap;align-items:flex-start;gap:clamp(18px,2.6vw,34px);
  padding:clamp(18px,2.6vw,28px) clamp(16px,4vw,40px) 40px;max-width:1680px;width:100%;margin:0 auto}
.sidebar{flex:0 1 232px;min-width:220px;background:var(--panel2);border:var(--bw) solid var(--line);border-radius:var(--r1);box-shadow:var(--sh)}
@media (min-width:820px){.sidebar{position:sticky;top:16px}}
.side-top{display:flex;align-items:center;gap:6px;padding:8px}
.side-top input{flex:1;min-width:0;padding:8px 10px;font:inherit;font-size:14.5px;background:var(--panel);
  border:var(--bw) solid var(--btnborder);border-radius:var(--r3);color:var(--ink)}
.iconbtn{flex:0 0 auto;width:34px;height:34px;font:inherit;font-size:15px;background:transparent;
  border:var(--bw) solid var(--btnborder);border-radius:var(--r3);color:var(--muted);cursor:pointer}
.iconbtn:hover{background:var(--pillbg);color:var(--ink)}
.sidebar nav{padding:2px 8px 10px}
.navempty{margin:4px 8px;font-size:14px;color:var(--muted)}
.navempty[hidden]{display:none}

/* collapsed rail */
.side-rail{display:none;flex-direction:column;align-items:center;gap:6px;padding:8px 6px}
.side-rail hr{width:26px;height:1px;margin:3px 0;border:0;background:var(--line)}
.side-rail .iconbtn{width:40px;height:36px}
.side-rail>a{display:flex;align-items:center;justify-content:center;width:40px;height:32px;
  border-radius:var(--r3);font-family:var(--mono);font-size:11.5px;font-weight:700;color:var(--muted2)}
.side-rail>a:hover{background:var(--pillbg);color:var(--ink)}
.side-rail>a[aria-current]{background:var(--acsoft);color:var(--ac)}
@media (min-width:820px){
  .sidebar[data-railed]{flex:0 0 58px;min-width:0;max-height:calc(100vh - 32px);overflow:auto}
  .sidebar[data-railed] .side-top,.sidebar[data-railed] nav{display:none}
  .sidebar[data-railed] .side-rail{display:flex}
}
@media (max-width:819px){
  .sidebar{flex:1 1 100%}
  .sidebar:not([data-open]) nav{display:none}
}
@media (min-width:820px){.sidebar nav{max-height:calc(100vh - 210px);overflow:auto}}
.navgroup{margin-bottom:14px}
.navgroup>h2{margin:0 0 5px;padding:0 8px;font-size:10.5px;font-weight:700;letter-spacing:var(--lls);text-transform:var(--ltt);color:var(--faint)}
.navgroup a{display:block;padding:8px 10px;border-radius:var(--r2);font-size:15px;line-height:1.25;color:var(--ink2);font-weight:470}
.navgroup a:hover{background:var(--pillbg);color:var(--ink)}
.navgroup a[hidden],.navgroup[hidden]{display:none}
.navgroup a[aria-current]{background:var(--acsoft);color:var(--ac);font-weight:640}

/* tool main */
.tool{flex:1 1 540px;min-width:0;display:flex;flex-direction:column;gap:16px}
.tool>header h1{margin:0 0 5px;font-size:clamp(23px,3.2vw,31px);font-weight:650;letter-spacing:-.022em;line-height:1.15}
.tool>header p{margin:0;max-width:68ch;font-size:16.5px;line-height:1.5;color:var(--muted);text-wrap:pretty}
.note{margin:0;font-size:14.5px;line-height:1.55;color:var(--muted2);max-width:74ch;text-wrap:pretty}

/* options */
.opts{display:flex;flex-wrap:wrap;gap:12px 26px;align-items:center;margin:0;padding:12px 15px;
  background:var(--panel2);border:var(--bw) solid var(--line);border-radius:var(--r2);box-shadow:var(--sh)}
.opt{display:flex;align-items:center;gap:9px;border:0;margin:0;padding:0}
.opt>legend,.opt>.lbl{float:none;width:auto;padding:0;font-size:11.5px;font-weight:700;letter-spacing:var(--lls);
  text-transform:var(--ltt);color:var(--muted)}
.opt>legend{display:contents}
.seg{display:flex;flex-wrap:wrap;gap:3px;background:var(--pillbg);padding:3px;border-radius:var(--r2)}
.seg label{position:relative;font-size:13.5px;font-weight:600;padding:5px 11px;border-radius:var(--r3);cursor:pointer;
  color:var(--muted);border:1px solid transparent}
.seg input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer}
.seg label:has(input:checked){background:var(--ac);border-color:var(--ac);color:var(--onac)}
.seg label:has(input:focus-visible){outline:2px solid var(--focus);outline-offset:1px}
.flag{position:relative;font-size:13.5px;font-weight:600;padding:6px 13px;border-radius:var(--r2);cursor:pointer;
  border:var(--bw) solid var(--btnborder);background:var(--panel);color:var(--muted2)}
.flag input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer}
.flag:has(input:checked){border-color:var(--ac);background:var(--acsoft);color:var(--ac)}
.flag:has(input:focus-visible){outline:2px solid var(--focus);outline-offset:1px}
.num{width:80px;padding:6px 8px;font:inherit;font-size:15px;font-variant-numeric:tabular-nums;
  background:var(--panel);border:var(--bw) solid var(--btnborder);border-radius:var(--r3);color:var(--ink)}
.swatch{width:46px;height:30px;padding:2px;background:var(--panel);cursor:pointer;
  border:var(--bw) solid var(--btnborder);border-radius:var(--r3)}
.presets{display:flex;flex-wrap:wrap;gap:4px}
.preset{width:22px;height:22px;padding:0;cursor:pointer;background:var(--panel);
  border:var(--bw) solid var(--btnborder);border-radius:var(--r3)}
.preset:hover{border-color:var(--ac)}
.preset:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.alpha{width:96px;accent-color:var(--ac);cursor:pointer}
.txt{padding:6px 8px;font:inherit;font-size:15px;
  background:var(--panel);border:var(--bw) solid var(--btnborder);border-radius:var(--r3);color:var(--ink)}

/* the table panel: a reference that has to line up, with a header that
   stays put through a long scroll.  border-collapse would drop the
   borders on a sticky header, so the cells carry their own edges and the
   table is separate with no spacing.

   The numeric cells are .n, not .num: .num is the number *input* above,
   and an unscoped match put a rounded 80px box around every figure.

   A div grows with its content where a textarea does not, so the height
   is capped as well as floored: without that the panel is thousands of
   pixels tall, the page scrolls instead of the box, and the sticky
   header has nothing to stay put inside. */
.tbl{flex:1;overflow:auto;resize:vertical;background:var(--panel);
  min-height:clamp(240px,46vh,620px);max-height:clamp(240px,46vh,620px)}
.tbl:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.tbl table{border-collapse:separate;border-spacing:0;width:100%;font-size:13px}
.tbl th,.tbl td{padding:5px 16px;text-align:left;white-space:nowrap;
  border-bottom:1px solid var(--line2);border-right:1px solid var(--line2)}
.tbl th:last-child,.tbl td:last-child{border-right:0;width:100%}
.tbl tbody tr:last-child td{border-bottom:0}
.tbl thead th{position:sticky;top:0;z-index:1;background:var(--panel2);
  font-size:11.5px;font-weight:700;letter-spacing:var(--lls);text-transform:var(--ltt);
  color:var(--muted);border-bottom:var(--bw) solid var(--line)}
.tbl tbody tr:hover td{background:var(--pillbg)}
/* Colors are set on these from script, through the CSSOM: a style
   attribute in the markup would be refused by style-src 'self'. */
.tbl .sw{display:block;width:34px;height:18px;border-radius:3px;border:1px solid var(--btnborder)}
.tbl td.swcell{width:1%;padding:4px 10px}
.tbl tbody tr.bad td{color:var(--err)}
/* the diff: two gutters of line numbers and the text beside them, and a
   stronger tint on the words that moved within a changed line */
.tbl table.diff{font-family:var(--mono);font-size:12.5px;line-height:1.6}
.tbl table.diff td{padding:1px 10px;white-space:pre-wrap;word-break:break-word;
  vertical-align:top;border-right:0}
.tbl table.diff th{padding:5px 10px}
.tbl table.diff .d-num{width:1%;white-space:nowrap;text-align:right;
  color:var(--muted2);background:var(--panel2);
  border-right:var(--bw) solid var(--line2);
  -webkit-user-select:none;user-select:none}
.tbl table.diff .d-line{width:50%}
.tbl table.diff tr.d-del td{background:var(--delbg)}
.tbl table.diff tr.d-ins td{background:var(--addbg)}
.tbl table.diff tr.d-del .d-mark{background:var(--delbg2)}
.tbl table.diff tr.d-ins .d-mark{background:var(--addbg2)}
.tbl table.diff tr.d-skip td{background:var(--skipbg);color:var(--muted2);
  text-align:center;font-size:11.5px;padding:4px 10px}
.tbl table.diff tbody tr:hover td{background:inherit}

/* the wifi card: the code and the details, and the only thing that goes
   on the paper when this page is printed */
.wifi-card{padding:20px 16px 24px;text-align:center}
.wifi-qr{display:block;width:min(280px,72vw);height:auto;margin:0 auto 18px;
  background:#fff;border-radius:var(--r3)}
.wifi-label{font-size:11.5px;font-weight:700;letter-spacing:var(--lls);
  text-transform:var(--ltt);color:var(--muted);margin-top:12px}
.wifi-value{font-family:var(--mono);font-size:20px;color:var(--ink);
  word-break:break-all;margin-top:2px}
.wifi-hint{margin-top:18px;font-size:13px;color:var(--muted2)}

/* the clock: two readings, one above the other, each with what it would
   copy printed under it */
.clock{padding:16px 16px 18px}
.clock + .clock{border-top:var(--bw) solid var(--line)}
.clock-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:4px}
.clock-zone{font-size:11.5px;font-weight:700;letter-spacing:var(--lls);text-transform:var(--ltt);color:var(--muted)}
/* The whole reading is one line, so it is sized to fit one rather than
   to be as large as possible. */
.clock-time{font-family:var(--mono);font-size:clamp(16px,3.4vw,28px);line-height:1.3;
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;color:var(--ink);white-space:nowrap}

/* The stopwatch reading stays put while the laps scroll under it. */
.watch{position:sticky;top:0;z-index:2;padding:18px 16px;background:var(--panel);
  border-bottom:var(--bw) solid var(--line);font-family:var(--mono);
  font-size:38px;font-variant-numeric:tabular-nums;letter-spacing:-.01em;color:var(--ink)}
.watch.done{color:var(--ac)}
/* The countdown's own progress, emptying as it goes.  The width is set
   from script, through the CSSOM, as style-src 'self' requires. */
.bar{height:6px;margin:14px 16px;background:var(--pillbg);border-radius:999px;overflow:hidden}
.bar-fill{height:100%;background:var(--ac);border-radius:999px}
/* A results table is only as wide as its own columns; a reference fills
   the panel so the last column takes up the slack. */
.tbl table.fit{width:auto}
.tbl table.fit th:last-child,.tbl table.fit td:last-child{width:auto;padding-right:22px}
.tbl tbody tr.sep td{border-top:var(--bw) solid var(--line)}
.tbl .n,.tbl .g{font-family:var(--mono);font-variant-numeric:tabular-nums}
.tbl .n{text-align:right}
.tbl .mn{color:var(--muted2)}

/* The options bar filters the table with no script behind it: the form
   sits before .io, so what is checked in it selects what stays shown. */
#opts:has(input[name="range"][value="ascii"]:checked) ~ .io tr.r-ext,
#opts:has(input[name="range"][value="printable"]:checked) ~ .io tr.r-ctl,
#opts:has(input[name="range"][value="printable"]:checked) ~ .io tr.r-ext,
#opts:has(input[name="range"][value="control"]:checked) ~ .io tr.r-prn,
#opts:has(input[name="range"][value="control"]:checked) ~ .io tr.r-ext,
#opts:has(input[name="range"][value="extended"]:checked) ~ .io tr.r-ctl,
#opts:has(input[name="range"][value="extended"]:checked) ~ .io tr.r-prn,
#opts:has(input[name="oct"]:not(:checked)) ~ .io .c-oct,
#opts:has(input[name="bin"]:not(:checked)) ~ .io .c-bin,
#opts:has(input[name="html"]:not(:checked)) ~ .io .c-html{display:none}

/* io panels */
.io{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,350px),1fr));gap:14px;align-items:stretch}
.panel{display:flex;flex-direction:column;background:var(--panel);border:var(--bw) solid var(--line);border-radius:var(--r1);box-shadow:var(--sh);overflow:hidden}
.panel-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 11px 9px 14px;
  border-bottom:var(--bw) solid var(--line2);background:var(--panel2)}
.panel-head h2{margin:0;font-size:11.5px;font-weight:700;letter-spacing:var(--lls);text-transform:var(--ltt);color:var(--muted)}
.acts{display:flex;gap:6px}
.btn{font:inherit;font-size:13px;font-weight:550;padding:5px 10px;background:var(--panel);
  border:var(--bw) solid var(--btnborder);border-radius:var(--r3);color:var(--ink2);cursor:pointer}
.btn:hover{background:var(--pillbg)}
.btn-ac{font-weight:600;padding:5px 12px;border-color:var(--ac);background:var(--acsoft);color:var(--ac)}
.btn-ac[data-copied]{background:var(--ac);color:var(--onac)}
.panel textarea{flex:1;width:100%;resize:vertical;min-height:clamp(210px,38vh,520px);padding:14px;border:0;
  background:transparent;color:var(--ink);font-family:var(--mono);font-size:15px;line-height:1.6;tab-size:2}
.panel textarea:focus{outline:none;box-shadow:inset 0 0 0 2px var(--focus)}
.panel-foot{padding:7px 14px;border-top:var(--bw) solid var(--line2);font-size:12.5px;color:var(--faint);font-variant-numeric:tabular-nums}
.err{padding:11px 14px;background:var(--errbg);border-bottom:var(--bw) solid var(--errline);color:var(--err);
  font-size:14.5px;line-height:1.45;font-family:var(--mono)}
.err:empty{display:none}

/* index */
.index{flex:1;width:100%;max-width:780px;margin:0 auto;padding:clamp(32px,7vh,72px) clamp(20px,5vw,32px) 64px}
.index h1{margin:0 0 10px;font-size:clamp(28px,5vw,38px);font-weight:650;letter-spacing:-.025em;line-height:1.1}
.lede{margin:0 0 26px;font-size:17px;line-height:1.5;color:var(--muted);max-width:56ch;text-wrap:pretty}
.filter{width:100%;padding:12px 15px;margin-bottom:30px;font:inherit;font-size:16.5px;background:var(--panel);
  border:var(--bw) solid var(--btnborder);border-radius:var(--r2);color:var(--ink)}
.index section{margin-bottom:34px}
.index section h2{margin:0 0 10px;font-size:12px;font-weight:700;letter-spacing:var(--lls);text-transform:var(--ltt);color:var(--faint)}
.list{border-top:var(--bw) solid var(--line2)}
.list a{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 16px;padding:13px 6px;
  border-bottom:var(--bw) solid var(--line2);color:var(--ink)}
.list a:hover{background:var(--acsoft)}
.list b{font-size:17px;font-weight:600;letter-spacing:-.01em;min-width:190px}
.list span{font-size:15px;line-height:1.4;color:var(--muted);flex:1 1 240px;text-wrap:pretty}
.list a[hidden],.index section[hidden]{display:none}
.empty{margin:0;font-size:16px;color:var(--muted)}
.empty[hidden]{display:none}

/* prose, for pages that are only words */
.index>h2{margin:34px 0 8px;font-size:19px;font-weight:650;letter-spacing:-.015em}
.index>p{margin:0 0 14px;font-size:16.5px;line-height:1.6;color:var(--ink2);max-width:66ch;text-wrap:pretty}
.index>p.lede{color:var(--muted)}
.index code{font-family:var(--mono);font-size:.92em;padding:1px 5px;background:var(--pillbg);border-radius:5px}
.index kbd{font-family:var(--mono);font-size:.85em;padding:2px 7px;background:var(--panel);
  border:var(--bw) solid var(--btnborder);border-bottom-width:2px;border-radius:6px}

/* palette */
.palette{border:0;padding:0;max-width:520px;width:calc(100% - 32px);max-height:70vh;margin-top:clamp(40px,12vh,120px);
  background:var(--panel);border:var(--bw) solid var(--line);border-radius:var(--r1);box-shadow:var(--shbig);
  color:var(--ink);overflow:hidden}
.palette::backdrop{background:var(--scrim)}
.palette form{display:flex;flex-direction:column;max-height:70vh}
.palette input{width:100%;padding:15px 18px;font:inherit;font-size:17px;background:transparent;border:0;
  border-bottom:var(--bw) solid var(--line2);color:var(--ink)}
.palette input:focus{outline:none}
.palette ul{list-style:none;margin:0;padding:6px;overflow:auto}
.palette a{display:flex;align-items:baseline;justify-content:space-between;gap:14px;padding:11px 12px;
  border-radius:var(--r2);color:var(--ink)}
.palette a em{font-style:normal;font-size:16px;font-weight:600}
.palette a small{font-size:13.5px;color:var(--muted)}
.palette li[hidden]{display:none}
.palette li[data-sel] a{background:var(--acsoft)}

footer.site{padding:15px clamp(16px,4vw,40px) 24px;border-top:var(--bw) solid var(--line);font-size:14px;color:var(--muted2)}

/* ### print ###
   Only the wifi card reaches paper: no header, no sidebar, no options,
   no panel around it.  Sized in millimetres and points, because that is
   what the sheet is measured in, and large enough to read across a room
   or from a frame on a shelf. */
@media print {
  .skip, .site-header, .site, #sidebar, .opts, .panel-head, .panel-foot,
  main.tool > header, .note, .err{display:none !important}

  html, body{background:#fff;color:#000}
  body{display:block;min-height:0}
  .layout, main.tool, .io, .panel{display:block;margin:0;padding:0;border:0;
    box-shadow:none;background:#fff;max-width:none}
  .tbl{min-height:0;max-height:none;overflow:visible;resize:none;background:#fff}

  .wifi-card{padding:0;page-break-inside:avoid}
  .wifi-qr{width:95mm;margin:6mm auto 10mm}
  .wifi-label{font-size:11pt;letter-spacing:.08em;text-transform:uppercase;
    color:#555;margin-top:7mm}
  .wifi-value{font-size:24pt;color:#000;margin-top:1mm}
  .wifi-hint{font-size:11pt;color:#555;margin-top:10mm}

  @page{margin:16mm}
}
