:root{--bg:#0b0b0b;--bg-soft:#141414;--fg:#e6e6e6;--fg-dim:#9a9a9a;--rule:#2a2a2a;--inv-bg:#e6e6e6;--inv-fg:#0b0b0b;--ok:#6ee787;--err:#e5534b;--font:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"DejaVu Sans Mono","Liberation Mono",monospace}
@media(prefers-color-scheme:light){:root{--bg:#f4f4f4;--bg-soft:#e9e9e9;--fg:#141414;--fg-dim:#4a4a4a;--rule:#d4d4d4;--inv-bg:#141414;--inv-fg:#f4f4f4;--ok:#1a7f37}}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font:1rem/1.6 var(--font)}
.wrap{max-width:78ch;margin:0 auto;padding:0 1.5rem}
/* Prompt on the left, who is signed in on the right. Wraps rather than shrinks,
   because a truncated name is worse than a second line. */
header{border-bottom:1px solid var(--rule);padding:1rem 0;display:flex;gap:1rem;flex-wrap:wrap;align-items:baseline;justify-content:space-between}
.prompt{font-size:.875rem;color:var(--fg-dim)}.prompt .cmd{color:var(--fg)}
.session{font-size:.8rem;color:var(--fg-dim)}
.session a{color:var(--fg-dim);text-decoration-style:dotted}
.session a:hover,.session a:focus-visible{color:var(--fg);outline:none}
h2{font-size:.95rem;font-weight:400;margin:0 0 .5rem}
h2 a{text-decoration:none}
.k{display:inline-block;border:1px solid var(--rule);border-radius:3px;padding:0 .3em;font-size:.75rem;color:var(--fg-dim)}
a{color:var(--fg)}
input,select,button,textarea{background:var(--bg-soft);color:var(--fg);border:1px solid var(--rule);border-radius:4px;padding:.45rem .6rem;font:inherit;font-size:.9rem}
input:focus,select:focus,textarea:focus{outline:1px solid var(--fg);border-color:var(--fg)}
textarea{width:100%;resize:vertical}
button{cursor:pointer}
button:hover{background:var(--inv-bg);color:var(--inv-fg)}
.note{color:var(--fg-dim);font-size:.78rem;margin:.5rem 0 0;overflow-wrap:anywhere}
.note .sub{display:block;margin-top:.35rem}

/* The hero: ascii art over a centred search box, and nothing else competing with
   it. The art is centred as a block but keeps its own left alignment, because
   text-align on a <pre> would centre every line on its own and turn a mountain
   into a scribble. */
.hero{text-align:center;padding:3.5rem 0 3rem}
.art{display:inline-block;text-align:left;margin:0 0 1.75rem;font-size:.8rem;line-height:1.25;color:var(--fg-dim)}
.art .topic{display:block;margin-top:.75rem;font-size:.7rem;opacity:.65}
.searchrow{display:flex;gap:.5rem;max-width:44ch;margin:0 auto}
.searchrow #q{flex:1;min-width:0;font-size:1rem;padding:.6rem .75rem}
.searchrow select{max-width:9rem}
.hero .note{max-width:52ch;margin:.75rem auto 0}
@media(max-width:34rem){.hero{padding:2rem 0}.searchrow{flex-wrap:wrap}.searchrow #q{flex-basis:100%}}

/* widget grid: one column on a phone, two from tablet up. A new widget is a
   new <section class="widget">, nothing else. */
.grid{display:grid;gap:1rem;grid-template-columns:1fr}
@media(min-width:640px){.grid{grid-template-columns:1fr 1fr}.wide{grid-column:1/-1}}
.widget{border:1px solid var(--rule);border-radius:8px;padding:1rem;background:var(--bg-soft)}
.widget form{display:flex;gap:.5rem;flex-wrap:wrap}
.widget form input{flex:1;min-width:10rem}
/* A file input's own button is styled by the OS otherwise, and a white pill in
   the middle of a terminal is the one thing on this page that looks borrowed. */
.widget form input[type=file]{color:var(--fg-dim);font-size:.8rem;padding:.35rem .5rem}
.widget form input[type=file]::file-selector-button{margin-right:.6rem;background:var(--bg);color:var(--fg);border:1px solid var(--rule);border-radius:4px;padding:.15rem .5rem;font:inherit;font-size:.8rem;cursor:pointer}
.widget form input[type=file]::file-selector-button:hover{background:var(--inv-bg);color:var(--inv-fg)}
/* A textarea takes the row to itself, so its button lands underneath it. */
.widget form textarea{flex-basis:100%}
.widget h2 a:hover,.widget h2 a:focus-visible{background:var(--inv-bg);color:var(--inv-fg);outline:none}

/* admin console (admin.html). Wider than the dashboard because five tables of
   codes, dates and targets do not fit 78ch. */
.wrap.console{max-width:110ch}
.lead{max-width:70ch;margin:1.5rem 0 0}
.panel{border-top:1px solid var(--rule);margin-top:1.75rem;padding-top:1rem}
.panel h2{font-size:1rem}
.panel h2 span{color:var(--fg-dim);font-size:.7rem}
/* Each table scrolls inside its own box, so a long target url cannot make the
   whole page scroll sideways. */
.scroll{overflow-x:auto;margin-top:.75rem}
.panel table{width:100%;border-collapse:collapse;font-size:.8rem}
.panel th,.panel td{text-align:left;padding:.35rem .6rem .35rem 0;border-bottom:1px solid var(--rule);white-space:nowrap;vertical-align:top}
.panel th{color:var(--fg-dim);font-weight:400}
/* The target url is the one cell allowed to be long, and it truncates rather than
   widening the table past the viewport. */
.panel td:nth-child(2){max-width:32ch;overflow:hidden;text-overflow:ellipsis}
.panel td.actions{white-space:nowrap;text-align:right}
.panel td.actions button{margin:0 0 0 .35rem;padding:.1rem .45rem;font-size:.75rem}
.panel td.actions button:disabled{opacity:.5;cursor:default;background:var(--bg-soft);color:var(--fg-dim)}
/* Armed: one more click does it. Loud on purpose, and it disarms itself after a
   few seconds, so it never looks like the resting state of a button. */
.panel td.actions button.armed{border-color:var(--err);color:var(--err)}
.panel td.actions button.armed:hover{background:var(--err);color:var(--bg)}
.note.err{color:var(--err)}

.hint{color:var(--fg-dim);font-size:.8rem;margin-top:1.5rem}
footer{border-top:1px solid var(--rule);margin-top:1.5rem;padding:1rem 0;color:var(--fg-dim);font-size:.8rem}
footer a{color:var(--fg-dim)}
