/* Tool pages. Loads after /styles.5ac548e5.css and reuses its tokens, so the palette,
   type and spacing stay identical to the rest of the site.

   Every tool uses the same accent. One variable rather than a literal, so the
   whole set changes in one place; per-tool colours were tried and dropped. */
:root { --threat: var(--accent-2); }

.tool-head { padding: 40px 0 22px; }
.tool-kicker {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--threat); margin: 0 0 10px;
}
.tool-head h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); margin: 0 0 12px; line-height: 1.15; }
.tool-head .lede { max-width: 62ch; margin: 0; text-align: left; }

.panel {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 12px; padding: 20px;
}
.io { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; }
@media (max-width: 780px) { .io { grid-template-columns: 1fr; } }

.io label, .io .io-label, .findings h2, .reference h2 { font-size: .95rem; font-weight: 600; }
.io textarea, .io .out, .panel > div > textarea {
  width: 100%; min-height: 190px; margin-top: 8px; color: var(--fg);
  border-radius: 9px;
  padding: 12px 14px; font-family: var(--mono); font-size: .92rem; line-height: 1.6;
  resize: vertical; white-space: pre-wrap; word-break: break-word; overflow: auto;
}
.io textarea:focus-visible, .io .out:focus-visible { outline: 2px solid var(--threat); outline-offset: 2px; }
.io .row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.count { font-family: var(--mono); font-size: .78rem; color: var(--muted); }

.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; align-items: center; }
/* The example is the primary action while the field is empty — a visitor with no
   hostile text to paste has nothing else to do here. Once there is input, the
   cleaned output is what they came for and takes the emphasis instead. */
.sample-note { margin: 12px 0 0; color: var(--muted); font-size: .88rem; max-width: 68ch; }
.sample-note.hidden { display: none; }
.actions .btn { cursor: pointer; font-family: inherit; }
button.btn.primary { background: var(--accent); color: #07101f; border-color: var(--accent); }
button.btn.primary:hover { background: #8bbcff; }
button.btn:disabled { opacity: .5; cursor: not-allowed; }

/* Verdict — the whole point of the tool. Stripping invisibles leaves output
   that looks identical to the input, so the count has to be stated loudly. */
.verdict {
  margin-top: 18px; padding: 16px 18px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg);
  border-left: 4px solid var(--threat);
}
.verdict.clean { border-left-color: var(--muted); }
/* `bad` and `warn` were set by three tools and never defined, so a banner saying
   the text was damaged rendered in the same green as a clean one. They match the
   truncation cards: red for damage, amber for intact but wrong. */
.verdict.bad { border-left-color: #e06c58; background: color-mix(in srgb, #e06c58 7%, var(--bg)); }
.verdict.warn { border-left-color: #e0a458; background: color-mix(in srgb, #e0a458 6%, var(--bg)); }
.verdict .headline { font-size: 1.12rem; font-weight: 600; margin: 0; }
.verdict .sub { margin: 6px 0 0; color: var(--muted); font-size: .92rem; }

/* The reveal. A table says what was removed; this shows where it sat, which is
   the part that makes the attack legible — a brand name split down the middle. */
.reveal-wrap { margin-top: 22px; }
.reveal-wrap h2 { font-size: .95rem; font-weight: 600; margin: 0 0 6px; }
.reveal-note { color: var(--muted); font-size: .88rem; margin: 0 0 10px; max-width: 68ch; }
.reveal {
  background: var(--bg); border: 1px solid var(--border); border-radius: 9px;
  padding: 14px 16px; font-family: var(--mono); font-size: .95rem; line-height: 2.1;
  white-space: pre-wrap; word-break: break-word; max-height: 260px; overflow: auto;
}
.reveal:focus-visible { outline: 2px solid var(--threat); outline-offset: 2px; }
.reveal-gap { color: var(--muted); opacity: .7; padding: 0 .35em; user-select: none; }
.reveal-trim { margin: 8px 0 0; color: var(--muted); font-size: .82rem; font-family: var(--mono); }
.cp-badge {
  display: inline-block; font-family: var(--mono); font-size: .72rem;
  padding: 1px 5px; margin: 0 1px; border-radius: 4px; vertical-align: baseline;
  background: var(--threat); color: #07101f; font-weight: 600; cursor: help;
}

table.findings-table { width: 100%; border-collapse: collapse; margin-top: 14px; font-size: .9rem; }
table.findings-table th, table.findings-table td {
  text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top;
}
table.findings-table th {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); font-weight: 500;
}
table.findings-table td.cp { font-family: var(--mono); color: var(--accent-2); white-space: nowrap; }
table.findings-table td.n { font-family: var(--mono); text-align: right; font-variant-numeric: tabular-nums; }
.tablescroll { overflow-x: auto; }

.chip {
  display: inline-block; font-family: var(--mono); font-size: .74rem;
  padding: 1px 7px; border-radius: 5px; border: 1px solid var(--threat);
  color: var(--threat); white-space: nowrap;
}
/* .chip is used on both spans and buttons. The transparent background comes
   from the base rule in styles.css; these are the button affordances. */
button.chip { font-family: var(--mono); font-size: .74rem; line-height: inherit; }
button.chip:hover { background: color-mix(in srgb, var(--threat) 16%, transparent); }
button.chip:active { background: color-mix(in srgb, var(--threat) 26%, transparent); }

.equiv { margin-top: 26px; }
/* The examples run the other way round from the page: dark text on light. They
   are a quotation from a file rather than part of the interface, and the reversed
   polarity says which is which before a word is read. Colours are generated into
   the block at the foot of this file. */
.equiv pre {
  background: #f2f4f8; border: 1px solid #cbd3e1; border-radius: 9px;
  padding: 14px 16px; overflow-x: auto; font-family: var(--mono); font-size: .86rem;
  line-height: 1.55; margin: 8px 0 0; color: #000;
}
.equiv pre code { background: none; }
.equiv pre::selection, .equiv pre ::selection { background: #b9d2ff; }
.equiv .tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.equiv .tab {
  font-family: var(--mono); font-size: .78rem; padding: 5px 11px; cursor: pointer;
  color: var(--muted); border: 1px solid var(--border); border-radius: 7px;
}
.equiv .tab[aria-selected="true"] { color: var(--threat); border-color: var(--threat); }

.reference { margin-top: 44px; }
.reference p { color: var(--muted); max-width: 68ch; }
.engine-note { margin-top: 30px; font-size: .85rem; color: var(--muted); }
.engine-note code { font-family: var(--mono); color: var(--accent-2); }

/* Tools index */
.tool-list { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); padding: 8px 0 20px; }
.tool-card {
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--threat);
  border-radius: 12px; padding: 20px; display: flex; flex-direction: column; gap: 8px;
}
.tool-card h3 { margin: 0; font-size: 1.05rem; }
.tool-card h3 a { color: var(--fg); }
.tool-card p { margin: 0; color: var(--muted); font-size: .93rem; }
.tool-card .meta { margin-top: auto; padding-top: 12px; font-family: var(--mono); font-size: .72rem; color: var(--muted); }
.tool-card.soon { opacity: .62; }

/* Trojan Source: the two readings side by side. The left pane is deliberately
   rendered with the browser's own bidi algorithm applied — that is the point, it
   shows what a reviewer sees. The right pane replaces each control with a badge,
   so nothing remains to reorder and the bytes appear in logical order. */
.two-views { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 20px; }
@media (max-width: 860px) { .two-views { grid-template-columns: 1fr; } }
.two-views h2 { font-size: .95rem; font-weight: 600; margin: 0 0 4px; }
.view-note { color: var(--muted); font-size: .82rem; margin: 0 0 8px; }
.view {
  background: var(--field); border: 1px solid var(--readout-border); border-radius: 9px;
  padding: 12px 14px; font-family: var(--mono); font-size: .86rem; line-height: 1.9;
  min-height: 190px; max-height: 340px; overflow: auto;
  white-space: pre-wrap; word-break: break-word; margin: 0;
}
.view.rendered { color: var(--muted); }
.code-line { display: flex; gap: 12px; align-items: baseline; }
.line-no {
  color: var(--muted); opacity: .6; user-select: none; text-align: right;
  min-width: 2.4ch; flex: none; font-variant-numeric: tabular-nums;
}
.line-body { white-space: pre-wrap; word-break: break-word; }

/* The source pane is code, so it wants to be taller than a text field. */
#input { min-height: 210px; }

/* Confusables: rows where the two digit policies disagree are the point of the
   table, so they carry the accent rather than being left to the reader to spot. */
table.findings-table tr.differs td { background: color-mix(in srgb, var(--threat) 8%, transparent); }
table.findings-table tr.differs td:first-child { box-shadow: inset 3px 0 0 var(--threat); }

/* A confusable character is visible, unlike an invisible one, so the chip shows
   the glyph as well as its codepoint — the glyph is the thing that fooled you,
   and the codepoint is the proof. */
.conf-chip {
  display: inline-flex; align-items: baseline; gap: .3em;
  padding: 1px 6px; margin: 0 2px; border-radius: 5px;
  background: color-mix(in srgb, var(--threat) 16%, transparent);
  border: 1px solid var(--threat); cursor: help; line-height: 1.4;
}
.conf-chip b { font-weight: 600; color: var(--fg); font-style: normal; }
.conf-chip i { font-size: .68em; font-style: normal; color: var(--threat); letter-spacing: .02em; }

/* Zalgo: depth is the signal, so the chip carries it and the shade rises with it.
   Depths 1 and 2 stay quiet — that is where ordinary orthography lives. */
.zal-chip {
  display: inline-flex; align-items: baseline; gap: .25em;
  padding: 1px 4px; margin: 1px; border-radius: 4px; line-height: 1.9;
  border: 1px solid transparent;
}
.zal-chip b { font-weight: 400; font-style: normal; color: var(--fg); }
.zal-chip i {
  font-size: .62em; font-style: normal; color: var(--bg);
  background: var(--threat); border-radius: 3px; padding: 0 3px; font-weight: 700;
}
.zal-chip.d0 i { display: none; }
.zal-chip.d1 { background: color-mix(in srgb, var(--muted) 12%, transparent); }
.zal-chip.d1 i { background: var(--muted); }
.zal-chip.d2 { background: color-mix(in srgb, var(--muted) 18%, transparent); }
.zal-chip.d2 i { background: var(--muted); }
.zal-chip.d3 { background: color-mix(in srgb, #e0a458 22%, transparent); border-color: #e0a458; }
.zal-chip.d3 i { background: #e0a458; }
.zal-chip.d4 { background: color-mix(in srgb, #e06c58 26%, transparent); border-color: #e06c58; }
.zal-chip.d4 i { background: #e06c58; }

/* Hostname labels, shown apart so the script of each is visible — the TLD's
   script is half the question and it is easy to overlook when the name is one
   string. */
.labels { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-top: 14px; }
.label-box {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 12px;
}
.label-box.flagged { border-color: var(--threat); background: color-mix(in srgb, var(--threat) 10%, transparent); }
.label-box b { font-family: var(--mono); font-size: 1rem; font-weight: 600; color: var(--fg); }
.label-box span { font-size: .72rem; color: var(--muted); }
.label-box.flagged span { color: var(--threat); }
.label-dot { font-family: var(--mono); color: var(--muted); font-size: 1.1rem; }

.host-chip {
  font-family: var(--mono); font-size: .82rem; cursor: pointer;
  background: var(--panel); color: var(--fg);
  border: 1px solid var(--border); border-radius: 7px; padding: 5px 10px;
}
.host-chip:hover { border-color: var(--threat); }
#example-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
#input.host { min-height: 0; height: auto; }

/* Whole-script confusables: the string is drawn exactly like Latin, so every
   impostor character carries its codepoint and what it folds to. */
.char-chip {
  display: inline-flex; align-items: baseline; gap: .3em;
  padding: 1px 6px; margin: 1px 2px; border-radius: 5px;
  background: color-mix(in srgb, var(--threat) 14%, transparent);
  border: 1px solid var(--threat); cursor: help;
}
.char-chip b { font-weight: 600; font-style: normal; color: var(--fg); font-size: 1.02em; }
.char-chip i { font-size: .64em; font-style: normal; color: var(--threat); white-space: nowrap; }
#chars {
  background: var(--bg); border: 1px solid var(--border); border-radius: 9px;
  padding: 14px 16px; font-family: var(--mono); font-size: .95rem; line-height: 2.2;
  word-break: break-word;
}
.char-summary { margin: 10px 0 0; font-size: .9rem; color: var(--muted); }
.char-summary strong { color: var(--fg); }

/* Whitespace: the classification is the signal, so the chip carries it. The
   blind-spot class — renders blank, but the caller's own whitespace test says
   no — is the one the page exists to show, so only it takes the threat colour.
   Ordinary spaces are never chipped at all; they are rendered as spaces. */
.ws-chip {
  display: inline-flex; align-items: baseline; gap: .3em;
  padding: 1px 5px; margin: 0 2px; border-radius: 5px; line-height: 1.7;
  border: 1px solid var(--border); background: color-mix(in srgb, var(--muted) 12%, transparent);
  cursor: help; white-space: nowrap;
}
.ws-chip b { font-weight: 600; font-style: normal; color: var(--fg); font-size: .82em; }
.ws-chip i { font-size: .62em; font-style: normal; color: var(--muted); letter-spacing: .02em; }
.ws-chip .flag {
  font-size: .6em; font-weight: 700; letter-spacing: .03em;
  color: var(--bg); background: var(--threat); border-radius: 3px; padding: 0 3px;
}
.ws-chip.ws-blind { background: color-mix(in srgb, var(--threat) 16%, transparent); border-color: var(--threat); }
.ws-chip.ws-blind i { color: var(--threat); }
.ws-chip.ws-line { border-style: dashed; }

.left-note {
  margin: 14px 0 0; padding: 12px 14px; font-size: .92rem; color: var(--muted);
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0;
}
.left-note strong { color: var(--fg); }

/* Filenames: the reveal is a comparison of two rows, not an annotation of one.
   The chips are deliberately identical in style between the rows — the whole
   point is that the two forms look the same — so the difference has to come
   from the codepoints and the byte counts, not from colour. */
.form-row { margin: 0 0 10px; }
.form-label {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: .82rem; font-weight: 600; color: var(--fg); margin: 0 0 5px;
}
.form-label em { font-style: normal; font-weight: 400; color: var(--muted); font-size: .92em; }
.form-label .tag {
  font-family: var(--mono); font-size: .72em; font-weight: 700; letter-spacing: .04em;
  color: var(--bg); background: var(--muted); border-radius: 3px; padding: 1px 5px;
}
.form-meta { margin-left: auto; font-weight: 400; color: var(--muted); font-family: var(--mono); font-size: .92em; }
.fn-strip {
  display: flex; flex-wrap: wrap; gap: 3px; padding: 9px 10px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  overflow-x: auto;
}
.fn-chip {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 3px 5px; border-radius: 5px; border: 1px solid transparent; min-width: 2.1em;
}
.fn-chip b { font-family: var(--mono); font-weight: 500; font-size: .95em; color: var(--fg); }
.fn-chip i { font-family: var(--mono); font-style: normal; font-size: .58em; color: var(--muted); }
.fn-chip.non-ascii {
  background: color-mix(in srgb, var(--threat) 15%, transparent);
  border-color: var(--threat);
}
.fn-chip.non-ascii i { color: var(--threat); }

/* The parameter selects sit in the action bar beside the buttons. */
.sel {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .85rem; color: var(--muted);
}
.sel select {
  font: inherit; font-size: .95em; color: var(--fg); background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px;
}
.sel select:hover { border-color: var(--accent); }

/* Graphemes: for once the damage is visible, so the three cuts sit side by side
   and the styling only has to say which one is correct. */
.limit-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 14px 0 4px;
  font-size: .88rem; color: var(--muted);
}
.limit-row label { font-weight: 600; color: var(--fg); }
.limit-row input[type="range"] { flex: 1 1 200px; min-width: 160px; accent-color: var(--threat); }
.limit-row output {
  font-family: var(--mono); font-weight: 700; color: var(--threat);
  min-width: 2.2em; text-align: right;
}
.limit-unit { font-size: .92em; }
.sec-h { font-size: .95rem; font-weight: 600; margin: 20px 0 8px; }

.cuts { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; }
.cut {
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--border); border-radius: 0 9px 9px 0; padding: 12px 14px;
}
/* The disarm card is the product, not one of three equal options. It gets the
   full border, a tinted ground and a badge; the other two are what the caller's
   own standard library does, and are labelled as such. Without that the three
   cards read as an authorless comparison and the page stops demonstrating
   anything. */
.cut.good {
  border-color: var(--threat); border-left-color: var(--threat);
  background: color-mix(in srgb, var(--threat) 8%, var(--panel));
  order: -1;
}
/* A mangled cut is the finding, so it carries the same visual weight as the
   disarm card and the opposite colour. Amber sits between: nothing is broken,
   but the limit bought fewer characters than it promised. */
.cut.broken {
  border-color: #e06c58; border-left-color: #e06c58;
  background: color-mix(in srgb, #e06c58 12%, var(--panel));
}
.cut.broken .cut-body { color: #ffb9ac; }
.cut.broken .cut-badge { color: #2a0d08; background: #e06c58; border-color: #e06c58; }
.cut.short {
  border-color: #e0a458; border-left-color: #e0a458;
  background: color-mix(in srgb, #e0a458 10%, var(--panel));
}
.cut.short .cut-badge { color: #2a1c08; background: #e0a458; border-color: #e0a458; }
.cut.short .cut-note strong { color: #e0a458; }
.cut-head { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.cut-who { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.cut-head strong { font-size: .9rem; color: var(--fg); }
.cut-head > span { font-family: var(--mono); font-size: .7rem; color: var(--muted); }
.cut-badge {
  font-family: var(--mono); font-size: .62rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: 2px 6px; border-radius: 4px;
  color: var(--muted); border: 1px solid var(--border);
}
.cut-badge.disarm {
  color: var(--bg); background: var(--threat); border-color: var(--threat);
}
.cut-badge.disarm::before { content: "\203A"; margin-right: .35em; font-weight: 700; }
.cut.good .cut-note strong { color: var(--fg); }
.cut-body {
  font-family: var(--mono); font-size: 1.35rem; line-height: 1.5; min-height: 1.9em;
  padding: 8px 10px; background: var(--bg); border-radius: 7px;
  word-break: break-word; white-space: pre-wrap;
}
.cut-note { margin: 8px 0 0; font-size: .8rem; color: var(--muted); line-height: 1.5; }
.cut.broken .cut-note strong { color: #e06c58; }

/* One box per user-perceived character. The multi-codepoint ones are the only
   ones a naive cut can land inside, so only they are marked. */
#clusters { display: flex; flex-wrap: wrap; gap: 5px; padding: 10px; background: var(--bg);
  border: 1px solid var(--border); border-radius: 8px; overflow-x: auto; }
.gr-cluster {
  display: inline-flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 5px 8px; border-radius: 6px; border: 1px solid var(--border); min-width: 2.4em;
}
.gr-cluster b { font-size: 1.25rem; font-weight: 400; line-height: 1.25; }
.gr-cluster i { font-family: var(--mono); font-style: normal; font-size: .56rem; color: var(--muted); }
.gr-cluster.multi { border-color: var(--threat); background: color-mix(in srgb, var(--threat) 13%, transparent); }
.gr-cluster.multi i { color: var(--threat); }

/* A C0 control has no glyph, so a table cell holding one renders as nothing at
   all — an empty box where the reader expects to see the character. Show its
   abbreviation in a frame instead, the way a hex editor would. */
.ctl-box {
  display: inline-block; font-family: var(--mono); font-size: .72em; font-weight: 600;
  padding: 0 4px; border: 1px dashed var(--muted); border-radius: 3px; color: var(--muted);
}

/* The index is organised by mechanism, which only helps a reader who already
   knows what they are looking at. The router is the other door in: a symptom
   is what someone actually arrives with. */
.router { margin: 8px 0 34px; }
.router h2 { font-size: 1.15rem; margin: 0 0 6px; }
.router-note { color: var(--muted); font-size: .93rem; margin: 0 0 14px; max-width: 62ch; }
.symptoms { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.symptoms li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5em;
  padding: 9px 12px; background: var(--panel);
  border: 1px solid var(--border); border-left: 3px solid var(--threat); border-radius: 0 8px 8px 0;
}
.symptoms a { font-weight: 600; font-size: .95rem; }
.symptoms span { color: var(--muted); font-size: .88rem; }

/* The corpus behind the confusables supplement came from mined misses. A link
   costs nothing and points at the one thing a visitor has that the project
   does not: a string that broke for them. */
.report-miss {
  margin: 30px 0 0; padding: 12px 14px; font-size: .9rem; color: var(--muted);
  background: var(--panel); border: 1px dashed var(--border); border-radius: 9px;
}

/* Said next to the feature, because "we do not upload your text" and "here is a
   link containing your text" look contradictory until you know where the text
   actually lives. */
.share-note { margin: 8px 0 0; font-size: .82rem; color: var(--muted); max-width: 66ch; }

/* The ellipsis costs one cluster, one codepoint, one UTF-16 unit — and three
   bytes. Which is the page's argument, applied to the marker itself. */
.ell-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .85rem; color: var(--muted); cursor: pointer;
}
.ell-toggle input { accent-color: var(--threat); cursor: pointer; }

/* The copy button tells a visitor to paste the sample elsewhere; this is where
   they bring it back, which is the half of the demonstration that was missing. */
.round-trip { margin-top: 22px; padding: 14px 16px; background: var(--panel);
  border: 1px dashed var(--border); border-radius: 10px; }
.round-trip textarea {
  width: 100%; min-height: 0; margin-top: 6px; color: var(--fg);
  border-radius: 8px; padding: 9px 11px;
  font-family: var(--mono); font-size: .9rem; resize: vertical;
}

/* Two strings overlaid. Shared text is printed plainly as context; a badge
   marks a codepoint present in only one of them. A and B get different colours
   because "which side is this on" is the question being asked — but neither is
   the bad one, so neither takes the threat colour. */
#diff {
  padding: 11px 13px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 9px; font-family: var(--mono); font-size: .95rem; line-height: 2.1;
  white-space: pre-wrap; word-break: break-word; overflow-x: auto;
}
.diff-chip {
  display: inline-flex; align-items: baseline; gap: .3em;
  padding: 1px 5px; margin: 0 2px; border-radius: 5px; cursor: help;
  border: 1px solid; white-space: nowrap;
}
.diff-chip s { text-decoration: none; font-size: .58em; font-weight: 700; letter-spacing: .04em; }
.diff-chip b { font-weight: 500; font-style: normal; color: var(--fg); }
.diff-chip i { font-size: .6em; font-style: normal; }
.diff-chip.only-a {
  border-color: #6ea8fe; background: color-mix(in srgb, #6ea8fe 15%, transparent);
}
.diff-chip.only-a s, .diff-chip.only-a i { color: #9ec5ff; }
.diff-chip.only-b {
  border-color: #e0a458; background: color-mix(in srgb, #e0a458 15%, transparent);
}
.diff-chip.only-b s, .diff-chip.only-b i { color: #e8bd7f; }

/* The narrowest fix is the answer; the rest are context. */
.findings-table tr.narrowest td { background: color-mix(in srgb, var(--threat) 9%, transparent); }
.findings-table tr.narrowest td:first-child { font-weight: 700; }

/* Presets: the finding is a row that produced a character the input did not
   have, so that row is the one that gets the colour. */
.findings-table tr.manufactured td {
  background: color-mix(in srgb, #e06c58 12%, transparent);
}
.chip.danger { color: #e06c58; border-color: #e06c58; }
.chip.quiet { color: var(--muted); border-color: var(--border); }

/* The two orderings, side by side, because the whole lesson is which one you
   do first. */
.order-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.order-step {
  padding: 13px 15px; border-radius: 0 9px 9px 0; background: var(--panel);
  border: 1px solid var(--border); border-left: 3px solid var(--border);
}
.order-step h3 { margin: 0 0 8px; font-size: .95rem; }
.order-step.bad-order { border-left-color: #e06c58; background: color-mix(in srgb, #e06c58 8%, var(--panel)); }
.order-step.good-order { border-left-color: var(--threat); background: color-mix(in srgb, var(--threat) 8%, var(--panel)); }
.order-in { margin: 8px 0 2px; font-size: .9rem; }
.order-note { margin: 0 0 8px; font-size: .84rem; color: var(--muted); line-height: 1.5; }

/* Slugify: the argument is one string romanised two ways, so the two ways sit
   side by side. Neither is "hostile" — one is simply the language and the other
   is not — so this uses the muted/accent pair rather than red. */
.slug-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-top: 6px; }
.slug-side {
  padding: 13px 15px; border-radius: 0 9px 9px 0;
  background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--border);
}
.slug-side h3 { margin: 0 0 6px; font-size: .88rem; font-weight: 600; }
.slug-side.wrong { border-left-color: var(--muted); }
.slug-side.right { border-left-color: var(--threat); background: color-mix(in srgb, var(--threat) 8%, var(--panel)); }
.slug-out { margin: 0; font-family: var(--mono); font-size: 1.05rem; color: var(--fg); word-break: break-all; }
.slug-note { margin: 6px 0 0; font-size: .82rem; color: var(--muted); }

/* The character that gave the language away. */
.disc-row { display: flex; flex-wrap: wrap; gap: 8px; }
.disc-chip {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 7px 12px; border-radius: 8px;
  border: 1px solid var(--threat); background: color-mix(in srgb, var(--threat) 14%, transparent);
}
.disc-chip b { font-size: 1.4rem; font-weight: 400; line-height: 1.2; color: var(--fg); }
.disc-chip i { font-family: var(--mono); font-style: normal; font-size: .62rem; color: var(--threat); }
.disc-chip s { text-decoration: none; font-size: .58rem; color: var(--muted); letter-spacing: .02em; }
.sel input[type="text"], .sel input[type="number"] {
  font: inherit; font-size: .95em; color: var(--fg); background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; width: 5em;
}

/* The count sits beside the language selector, because "83" is the answer to a
   question the dropdown otherwise invites and does not answer. */
.lang-count { font-size: .78rem; color: var(--muted); align-self: center; }


/* Every field, one rule. The textarea, the selects and the small text and
   number inputs were each styled separately and each ended up invisible against
   the panel; this is the single place that decides what a field looks like. */
.panel textarea,
.panel input[type="text"],
.panel input[type="number"],
.panel select,
.panel .out,
.round-trip textarea {
  background: var(--field);
  border: 1px solid var(--field-border);
}
/* A readout is not a field. Same ground, quieter edge, so "where can I type" is
   answerable without reading a single label. */
.panel .out { border-color: var(--readout-border); }
.panel textarea:hover,
.panel input[type="text"]:hover,
.panel input[type="number"]:hover,
.panel select:hover { border-color: var(--accent); }
.panel textarea:focus-visible,
.panel input:focus-visible,
.panel select:focus-visible {
  outline: 2px solid var(--threat); outline-offset: 1px; border-color: var(--threat);
}
/* A checkbox is a control too, and the browser draws it with its own chrome. */
.panel input[type="checkbox"] { accent-color: var(--threat); width: 1em; height: 1em; }


/* code-colours:start */
/* Generated by scripts/inject-examples.py from the Pygments 'xcode' style.
   Do not edit: the next run overwrites it, and CI checks it round-trips. */
.equiv pre code { background: #f2f4f8; color: #000; }
.equiv pre code .t-k { color: #A90D91 } /* Keyword */
.equiv pre code .t-n { color: #000 } /* Name */
.equiv pre code .t-o { color: #000 } /* Operator */
.equiv pre code .t-cm { color: #177500 } /* Comment.Multiline */
.equiv pre code .t-cp { color: #633820 } /* Comment.Preproc */
.equiv pre code .t-cpf { color: #177500 } /* Comment.PreprocFile */
.equiv pre code .t-c1 { color: #177500 } /* Comment.Single */
.equiv pre code .t-kc { color: #A90D91 } /* Keyword.Constant */
.equiv pre code .t-kd { color: #A90D91 } /* Keyword.Declaration */
.equiv pre code .t-kn { color: #A90D91 } /* Keyword.Namespace */
.equiv pre code .t-kp { color: #A90D91 } /* Keyword.Pseudo */
.equiv pre code .t-kt { color: #A90D91 } /* Keyword.Type */
.equiv pre code .t-m { color: #1C01CE } /* Literal.Number */
.equiv pre code .t-s { color: #C41A16 } /* Literal.String */
.equiv pre code .t-na { color: #836C28 } /* Name.Attribute */
.equiv pre code .t-nb { color: #A90D91 } /* Name.Builtin */
.equiv pre code .t-nc { color: #3F6E75 } /* Name.Class */
.equiv pre code .t-no { color: #000 } /* Name.Constant */
.equiv pre code .t-ne { color: #000 } /* Name.Exception */
.equiv pre code .t-nf { color: #000 } /* Name.Function */
.equiv pre code .t-nn { color: #000 } /* Name.Namespace */
.equiv pre code .t-nx { color: #000 } /* Name.Other */
.equiv pre code .t-nv { color: #000 } /* Name.Variable */
.equiv pre code .t-ow { color: #000 } /* Operator.Word */
.equiv pre code .t-mf { color: #1C01CE } /* Literal.Number.Float */
.equiv pre code .t-mh { color: #1C01CE } /* Literal.Number.Hex */
.equiv pre code .t-mi { color: #1C01CE } /* Literal.Number.Integer */
.equiv pre code .t-sa { color: #C41A16 } /* Literal.String.Affix */
.equiv pre code .t-sb { color: #C41A16 } /* Literal.String.Backtick */
.equiv pre code .t-sc { color: #2300CE } /* Literal.String.Char */
.equiv pre code .t-s2 { color: #C41A16 } /* Literal.String.Double */
.equiv pre code .t-se { color: #C41A16 } /* Literal.String.Escape */
.equiv pre code .t-si { color: #C41A16 } /* Literal.String.Interpol */
.equiv pre code .t-sx { color: #C41A16 } /* Literal.String.Other */
.equiv pre code .t-sr { color: #C41A16 } /* Literal.String.Regex */
.equiv pre code .t-s1 { color: #C41A16 } /* Literal.String.Single */
.equiv pre code .t-ss { color: #C41A16 } /* Literal.String.Symbol */
.equiv pre code .t-fm { color: #000 } /* Name.Function.Magic */
/* code-colours:end */


/* Small headings and spacing that used to be inline style attributes.
   They were the only reason style-src needed 'unsafe-inline', which the whole
   site paid for. Named by the job they do, so the next page reuses them rather
   than inlining another one. */
.view-h { font-size: .95rem; font-weight: 600; margin: 0 0 6px; }
.view-h.spaced { margin-top: 18px; }
.view-h.sm { font-size: .9rem; }
.view-block { margin-top: 20px; }
.view.short { min-height: 0; }
.table-note { margin-top: 18px; }

/* The profile grid: seven pipelines down, twelve probes across. Wide by nature,
   so it scrolls inside .tablescroll rather than squeezing the columns. */
.profile-grid { font-size: .82rem; }
.profile-grid th, .profile-grid td { vertical-align: top; white-space: nowrap; }
.profile-grid .eff-h { font-weight: 600; font-size: .74rem; color: var(--muted); }
.profile-grid .eff-h span { border-bottom: 1px dotted var(--readout-border); cursor: help; }
/* The profile names are identifiers a reader will type into get_pipeline, so
   they keep their own casing rather than taking the table's uppercase heading
   style. */
.profile-grid .prof-name { text-align: left; font-weight: 600; color: var(--fg);
  text-transform: none; letter-spacing: 0; font-size: .8rem; white-space: nowrap; }
.profile-grid .prof-name s { display: block; text-decoration: none; font-weight: 400;
  font-size: .68rem; color: var(--muted); letter-spacing: .04em; }
/* The output is the only free-text column and it must wrap inside its own width,
   or a long result runs under the probe columns beside it. */
.profile-grid .prof-out { width: 24ch; min-width: 24ch; white-space: normal;
  overflow-wrap: anywhere; word-break: break-word; }
/* An effect that fires shows what came out, because "folded onto paypal" and
   "romanised to raurai" are both yes and are not the same answer. */
.profile-grid .eff.yes code { color: var(--threat); font-size: .78rem; }
.profile-grid .eff.no { color: var(--muted); text-align: center; }


/* Collisions. The values in a group are the finding, so they read as data
   rather than as prose. */
.coll-v { font-family: var(--mono); font-size: .84rem; color: var(--threat);
  background: var(--field); border: 1px solid var(--readout-border);
  border-radius: 5px; padding: 1px 6px; display: inline-block; margin: 1px 0; }
/* How far a key reaches, drawn rather than counted: "seven names against three"
   reads at a glance where the two numbers alone do not. */
.findings-table td.reach { white-space: nowrap; font-variant-numeric: tabular-nums; }
.reach-bar { display: inline-block; width: 72px; height: 7px; margin-right: 8px;
  background: var(--field); border: 1px solid var(--readout-border);
  border-radius: 4px; overflow: hidden; vertical-align: middle; }
.reach-bar i { display: block; height: 100%; background: var(--threat); }
/* Eleven buckets rather than a computed width: the prerenderer bakes this
   widget's output into the page, and a width set as a style property would
   ship as the style attribute the CSP forbids. */
.reach-bar.b0 i { width: 0%; }
.reach-bar.b1 i { width: 10%; }
.reach-bar.b2 i { width: 20%; }
.reach-bar.b3 i { width: 30%; }
.reach-bar.b4 i { width: 40%; }
.reach-bar.b5 i { width: 50%; }
.reach-bar.b6 i { width: 60%; }
.reach-bar.b7 i { width: 70%; }
.reach-bar.b8 i { width: 80%; }
.reach-bar.b9 i { width: 90%; }
.reach-bar.b10 i { width: 100%; }
.findings-table td.cp s { text-decoration: none; color: var(--muted); font-size: .72rem; }
