/* The side-by-side / unified diff view.
 *
 * Its own sheet because two pages now render the same thing: tools-json-diff.html, where these
 * rules lived inline and light-only, and tools-diff-checker.html. A second inline copy would have
 * been two stylesheets that start identical and drift the first time one is tuned.
 *
 * A TABLE, not two scrolling columns. Two columns cannot keep a removed line opposite the
 * insertion that replaced it - they drift apart the moment one side is taller - and a diff whose
 * halves do not line up is not a diff. The explicit <colgroup> matters for the same reason:
 * table-layout is fixed, so widths come from the colgroup rather than from whatever the first row
 * happens to contain.
 */

.dv-wrap {
  border: 1px solid var(--ts-line); border-radius: .75rem; overflow: auto;
  background: var(--ts-card); max-height: 70vh;
}
.dv {
  width: 100%; border-collapse: collapse; table-layout: fixed;
  font-family: ui-monospace, Consolas, Menlo, monospace; font-size: .78rem; line-height: 1.6;
}
.dv td { vertical-align: top; padding: .05rem .5rem; }

/* Line-number gutters. user-select:none so dragging across the diff copies the code and not the
   numbers - the single most annoying thing a diff view can get wrong. */
.dv td.ln {
  width: 3.2rem; text-align: right; user-select: none; -webkit-user-select: none;
  color: var(--ts-faint); background-color: var(--ts-soft);
  border-right: 1px solid var(--ts-line); position: sticky; left: 0;
}
.dv td.side { white-space: pre-wrap; word-break: break-word; color: var(--ts-ink); }
/* "Disable line wrap": the row keeps its own line and the container scrolls sideways. */
.dv.nowrap td.side { white-space: pre; word-break: normal; }

.dv tr.eq td.side { color: var(--ts-muted); }
.dv tr.del td.side, .dv tr.chg td.side.a { background-color: var(--dv-del-bg); }
.dv tr.ins td.side, .dv tr.chg td.side.b { background-color: var(--dv-ins-bg); }
.dv td.empty { background-color: var(--ts-soft); }

/* Word-level marks inside a changed line. */
mark.w-del, mark.w-ins { border-radius: .2rem; padding: 0 .05rem; }
mark.w-del { background: var(--dv-del-mark); color: var(--dv-del-ink); }
mark.w-ins { background: var(--dv-ins-mark); color: var(--dv-ins-ink); }

/* The collapsed-context band. colspan is set by the renderer to match the live column count -
   split and unified do not have the same number of columns. */
.dv tr.gap td {
  text-align: center; font-size: .72rem; color: var(--ts-muted);
  background-color: var(--ts-soft); cursor: pointer; padding: .3rem;
}
.dv tr.gap td:hover { color: var(--ts-brand-t); }

/* Where "next change" has just landed.

   A MARCHING outline rather than a solid ring. The solid 2px indigo box read as a heavy border
   drawn around three unrelated rows - it competed with the red and green that carry the actual
   meaning, and on a multi-line change it boxed in the whole block so hard that the change inside
   was harder to read, not easier. Dashes that travel say "this is selected" while staying out of
   the way, which is what a selection marker is for.

   Drawn as two gradient strips on the cell background, so it layers over the del/ins colour
   instead of replacing it - which is why the rules above set background-COLOR and not the
   background shorthand. */
@keyframes dc-march { to { background-position: 14px 0, -14px 100%; } }
.dv tr.focus td {
  background-image:
    repeating-linear-gradient(90deg, var(--ts-brand-t) 0 7px, transparent 7px 14px),
    repeating-linear-gradient(90deg, var(--ts-brand-t) 0 7px, transparent 7px 14px);
  background-size: 100% 2px, 100% 2px;
  background-position: 0 0, 0 100%;
  background-repeat: repeat-x, repeat-x;
  animation: dc-march .6s linear infinite;
}

.dv-empty { padding: 2.5rem 1rem; text-align: center; color: var(--ts-muted); font-size: .85rem; }
.dv-banner {
  padding: .5rem .75rem; font-size: .78rem; border-radius: .5rem; margin-bottom: .5rem;
  background: rgba(217, 119, 6, .12); color: #92400e;
}
[data-bs-theme="dark"] .dv-banner { color: #fcd34d; }

/* ---- tokens ----
   Declared here rather than inline so the dark values exist at all. The originals were hardcoded
   light hex chosen against white, which on a dark card gave black-on-navy text in the marks. */
.tstudio, .ts-tokens {
  --dv-del-bg: #fdecec;
  --dv-ins-bg: #e4f6e9;
  --dv-del-mark: #f7b9b9;
  --dv-ins-mark: #a9e5bd;
  --dv-del-ink: #6e1c1c;
  --dv-ins-ink: #06452c;
}
[data-bs-theme="dark"] .tstudio, [data-bs-theme="dark"] .ts-tokens {
  --dv-del-bg: rgba(248, 113, 113, .12);
  --dv-ins-bg: rgba(74, 222, 128, .12);
  --dv-del-mark: rgba(248, 113, 113, .34);
  --dv-ins-mark: rgba(74, 222, 128, .32);
  --dv-del-ink: #fecaca;
  --dv-ins-ink: #bbf7d0;
}

/* ---- the toolbar above the view ---- */
.dv-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .6rem; }
.dv-count { font-size: .78rem; font-variant-numeric: tabular-nums; }
.dv-count .add { color: #15803d; font-weight: 700; }
.dv-count .rem { color: #b91c1c; font-weight: 700; }
[data-bs-theme="dark"] .dv-count .add { color: #86efac; }
[data-bs-theme="dark"] .dv-count .rem { color: #fda4af; }

/* Merge arrows on a focused change. */
.dv-merge { display: inline-flex; gap: .3rem; }

@media (prefers-reduced-motion: reduce) {
  .dv tr.focus td { animation: none; }
}

/* ---- the two-column shell: filters in a left rail, work on the right ----
   The controls were a wrapped row above the panes. That is fine for three of them and wrong for
   ten: a horizontal row of ten mixed toggles, segments and selects has no grouping, reflows into
   two ragged lines, and pushes the thing the reader came for below the fold. A rail gives each
   group a heading and leaves the main column for the comparison. */
.dc-shell { display: grid; grid-template-columns: 15rem 1fr; gap: 1rem; align-items: start; }
@media (max-width: 991.98px) { .dc-shell { grid-template-columns: 1fr; } }

.dc-side {
  border: 1px solid var(--ts-line); border-radius: .75rem; background: var(--ts-card);
  padding: .85rem; position: sticky; top: 1rem;
}
@media (max-width: 991.98px) { .dc-side { position: static; } }
.dc-side h3 {
  font-size: .68rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ts-muted); margin: 1rem 0 .45rem;
}
.dc-side h3:first-of-type { margin-top: .25rem; }

/* Switch rows: label left, switch right, whole row clickable. */
.dc-switch {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  font-size: .8rem; color: var(--ts-ink); padding: .3rem 0; cursor: pointer; margin: 0;
}
.dc-switch .form-check { margin: 0; min-height: 0; padding-left: 0; }
.dc-switch .form-check-input { margin: 0; cursor: pointer; float: none; }
.dc-switch .form-check-input:checked { background-color: var(--ts-brand); border-color: var(--ts-brand); }

/* Segmented controls fill the rail rather than sitting inline. */
.dc-side .dc-seg { display: flex; width: 100%; }
.dc-side .dc-seg button { flex: 1 1 0; text-align: center; }

.dc-side select { width: 100%; font-size: .78rem; }

/* Collapsible groups - Ignore, Transform. <details> so they work with no script at all. */
.dc-fold { border-top: 1px solid var(--ts-line); margin-top: .75rem; padding-top: .5rem; }
.dc-fold > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between;
  font-size: .8rem; font-weight: 600; color: var(--ts-ink); padding: .2rem 0;
}
.dc-fold > summary::-webkit-details-marker { display: none; }
.dc-fold > summary::after {
  content: '\203a'; color: var(--ts-faint); font-size: 1.1rem; line-height: 1;
  transition: transform .15s;
}
.dc-fold[open] > summary::after { transform: rotate(90deg); }
.dc-fold .dc-switch { font-size: .78rem; padding-left: .25rem; }

/* ---- the main column ---- */
.dc-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .75rem; }
.dc-head .dc-title { font-size: 1rem; font-weight: 700; color: var(--ts-ink); margin: 0 auto 0 0; }
.dc-meta { font-size: .74rem; color: var(--ts-muted); }

/* The result header: removals on the left of the split, additions on the right, each with its own
   line count and copy - mirroring the two panes underneath it. */
.dc-summary {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: .5rem; align-items: center;
  border: 1px solid var(--ts-line); border-radius: .6rem; padding: .5rem .75rem;
  margin-bottom: .6rem; background: var(--ts-soft); font-size: .8rem;
}
@media (max-width: 767.98px) { .dc-summary { grid-template-columns: 1fr; } }
.dc-summary .half { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.dc-summary .half.right { justify-content: flex-end; }
.dc-summary .lines { color: var(--ts-muted); font-size: .76rem; margin-left: auto; }
.dc-summary .half.right .lines { margin-left: 0; margin-right: auto; }
.dc-swap-ico { color: var(--ts-faint); }
.dc-pill {
  display: inline-flex; align-items: center; gap: .35rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.dc-pill.rem { color: #b91c1c; }
.dc-pill.add { color: #15803d; }
[data-bs-theme="dark"] .dc-pill.rem { color: #fda4af; }
[data-bs-theme="dark"] .dc-pill.add { color: #86efac; }

/* "The two texts are identical" - said plainly, because an empty diff view and a broken diff view
   look exactly the same otherwise. */
.dc-same {
  display: flex; gap: .7rem; align-items: flex-start; padding: .8rem 1rem; border-radius: .6rem;
  background: var(--ts-brand-s); color: var(--ts-ink); font-size: .85rem; margin-bottom: .6rem;
}
.dc-same .fa-solid { color: var(--ts-brand-t); margin-top: .15rem; }
.dc-same b { display: block; }
.dc-same span em { font-style: normal; color: var(--ts-muted); font-size: .8rem; }

/* ---- the inline change panel ----
   Injected as a row directly under the change it belongs to. The controls for acting on a change
   belong AT that change: a merge button in a toolbar at the top of a long diff acts on something
   the reader cannot see while pressing it, which is how you merge the wrong hunk. */
.dv tr.dc-inline td { padding: .55rem .6rem; background: var(--ts-card); }
.dc-inline-box {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  border: 1px solid var(--ts-line); border-radius: .6rem; padding: .5rem .6rem;
  background: var(--ts-soft);
}
.dc-inline-box .which { font-size: .78rem; font-weight: 700; color: var(--ts-ink); }
.dc-inline-box .spacer { margin-left: auto; }
/* Direction is carried by colour AND by an arrow, never colour alone - the two buttons are
   otherwise identical and the consequence of picking the wrong one is somebody's file. */
.dc-mg-right { background: #c1543a; border-color: #c1543a; color: #fff; }
.dc-mg-right:hover { background: #a8462f; border-color: #a8462f; color: #fff; }
.dc-mg-left { background: var(--ts-brand); border-color: var(--ts-brand); color: #fff; }
.dc-mg-left:hover { background: var(--ts-brand-d); border-color: var(--ts-brand-d); color: #fff; }
.dc-inline-x {
  border: 0; background: transparent; color: var(--ts-faint); cursor: pointer;
  font-size: .95rem; line-height: 1; padding: .2rem .35rem;
}
.dc-inline-x:hover { color: var(--ts-ink); }
/* A change row is clickable, so it should look it. */
.dv tr.chg, .dv tr.ins, .dv tr.del { cursor: pointer; }

/* ---------- the permission dialog ---------- */

/* Shown before Share and before Explain - the two actions that move the reader's text off their
   machine. It replaced two window.confirm() calls, whose "localhost:8088 says" chrome is the same
   frame a scam popup gets; a consent prompt that looks untrustworthy trains people to click past
   consent prompts.
   Restrained on purpose. Nothing here animates or draws attention to the confirm button over the
   cancel one: the reader is being asked to make a decision about their own material, and the job
   of the design is to make the decision legible rather than to steer it. */
.dc-ask { border: 1px solid var(--ts-line); border-radius: 1rem; }
.dc-ask .modal-header { border-bottom: 1px solid var(--ts-line); gap: .6rem; align-items: center; }
.dc-ask .modal-footer { border-top: 1px solid var(--ts-line); gap: .5rem; }
.dc-ask .modal-title { font-size: 1rem; font-weight: 700; color: var(--ts-ink); }

.dc-ask-ico {
  width: 2rem; height: 2rem; flex: 0 0 2rem; border-radius: .6rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ts-soft); color: var(--ts-brand-t); font-size: .85rem;
}

.dc-ask-lead { color: var(--ts-ink); font-size: .9rem; margin-bottom: .75rem; }

/* A list, not a paragraph. The three facts here are independent - how long it is kept, who can
   read it, how big it may be - and a reader deciding whether to press the button scans them
   rather than reading them in order. */
.dc-ask-points {
  list-style: none; margin: 0 0 .85rem; padding: 0;
  font-size: .84rem; color: var(--ts-muted);
}
.dc-ask-points li { position: relative; padding-left: 1.15rem; margin-bottom: .35rem; }
.dc-ask-points li::before {
  content: ''; position: absolute; left: .3rem; top: .55rem;
  width: .3rem; height: .3rem; border-radius: 50%; background: var(--ts-brand-t);
}

/* The one line the reader must not skim, so it is the only thing here with a background. */
.dc-ask-warn {
  margin: 0; padding: .55rem .7rem; border-radius: .5rem;
  background: var(--ts-soft); border-left: 3px solid var(--ts-brand-t);
  font-size: .82rem; color: var(--ts-ink);
}

.dc-ask-upsell {
  margin: .7rem 0 0; font-size: .78rem; color: var(--ts-faint);
}
.dc-ask-upsell[hidden] { display: none; }
