/* ============================================================================
   JSON Formatter — tool layer.

   base.css is loaded immediately before this file and owns everything
   structural: reset, body, links, focus rings, headings, forms, .panel,
   .btn, .chip, tables, details, header, footer, .wrap, .badge, .stat.

   This file sets the tool's accent and styles only what is specific to a
   JSON editor: the input bay, the error report, the output pane and the
   collapsible tree.

   Accent — sky. Measured against the shared palette:
     dark   #38bdf8 on --bg      8.74:1     on --surface   8.14:1
            #38bdf8 on --bg-sunk 9.14:1     on --accent-dim 6.30:1
     light  #0369a1 on --surface 5.93:1     on --bg        5.69:1
            #0369a1 on --accent-dim 5.13:1
   ========================================================================= */

:root {
  --accent:     #38bdf8;
  --accent-dim: rgba(56, 189, 248, 0.14);
}

@media (prefers-color-scheme: light) {
  :root {
    --accent:     #0369a1;
    --accent-dim: rgba(3, 105, 161, 0.10);
  }
}

/* --------------------------------------------------------------- utility -- */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

[hidden] { display: none !important; }

/* base.css only styles lists inside .prose; these pages use bare <ul>/<ol> */
main ul, main ol { padding-left: 1.25em; margin: 0 0 1.1em; }
main li { margin-bottom: 0.4em; }

.offscreen-copy { position: fixed; top: -1000px; left: 0; opacity: 0; }

/* -------------------------------------------------------- the instrument -- */
/* .tool carries .panel */

.tool { margin-bottom: 2rem; }

.tool-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  margin-bottom: 0.4rem;
}

.input-meta {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------- input + error mirror -- */
/* .json-input-wrap holds two stacked boxes: the real textarea on top, and a
   same-sized "mirror" div behind it that is invisible except for one
   highlighted character, used to mark a JSON parse-error position in place.

   ============================================================================
   ANTI-DRIFT CONTRACT — read this before touching either rule below.

   The mirror has to occupy the exact same box, at the exact same scroll
   offset, as the textarea in front of it, or the highlighted character will
   sit next to the wrong glyph instead of on top of it. Every property that
   affects box geometry or text layout — box-sizing, sizing, padding, border
   thickness, font metrics, whitespace/wrap handling — is declared exactly
   ONCE below, in a selector naming BOTH `.json-input` and `.json-mirror`.

   Never add one of those properties to `.json-input` or `.json-mirror`
   alone. If the two rules diverge even slightly, the mark silently drifts
   off the character it is meant to point at — nothing errors, it just looks
   subtly wrong, which is exactly the class of bug this site has shipped
   before. If a change seems to require different geometry for the two
   elements, that is a sign the design is wrong, not a reason to break the
   contract.

   Only properties that are INTENTIONALLY different — color, stacking,
   pointer handling, resize, clipping — belong in the element-specific rules
   that follow the shared one. */

.json-input,
.json-mirror {
  box-sizing: border-box;
  display: block;
  width: 100%;
  min-width: 0;
  padding: 0.5rem 0.7rem;
  border-width: 1px;
  border-style: solid;
  border-radius: var(--radius-sm);
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: normal;
  line-height: 1.55;
  letter-spacing: normal;
  word-spacing: normal;
  tab-size: 2;
  white-space: pre;
  overflow-wrap: normal;
}

.json-input-wrap {
  position: relative;
}

textarea.json-input {
  position: relative;
  z-index: 1;
  min-height: 11rem;
  max-height: 60vh;
  resize: vertical;
  overflow-x: auto;
  border-color: var(--line-strong);
  background: var(--bg-sunk);
  color: var(--text);
}

.json-mirror {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
  color: transparent;
  border-color: transparent;
  background: transparent;
}

/* While an error is live, the textarea's own background goes transparent so
   the mirror's highlighted-character swatch (painted on --bg-sunk) shows
   through in the right place; the rest of the mirror is indistinguishable
   from the textarea's normal resting background. */
.json-input-wrap--err textarea.json-input { background: transparent; }
.json-input-wrap--err .json-mirror { background: var(--bg-sunk); }

.json-mark {
  background: var(--err-bg);
  outline: 1px solid var(--err);
  outline-offset: -1px;
  border-radius: 2px;
}

/* Used instead of .json-mark when there is no character to shade: an
   unexpected end of input, or the offending character being a line break
   (a raw newline inside the span would collapse to zero width). A thin
   left border stands in as an insertion caret. */
.json-mark--caret {
  background: transparent;
  outline: none;
  border-left: 2px solid var(--err);
  margin-left: -1px;
}

/* ------------------------------------------------------------- controls -- */

.controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.2rem;
  margin-top: 0.9rem;
}

.control-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.control-group > .field-label { margin-right: 0.15rem; }

.actions { margin-left: auto; }

.btn:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }

.copy-status {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ok);
  min-width: 4.5rem;
}
.copy-status.is-err { color: var(--err); }

/* ------------------------------------------------------------- verdict --- */

.verdict {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1rem;
  margin-top: 1.25rem;
}

.status-note {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--text-dim);
  max-width: 62ch;
}

.big-notice {
  margin: 0.8rem 0 0;
  font-size: 0.875rem;
  color: var(--text-dim);
  border-left: 2px solid var(--accent);
  padding-left: 0.8rem;
}

/* --------------------------------------------------------- error report -- */
/* The most valuable surface in the tool, so it gets its own block rather
   than a one-line message: what is wrong, exactly where, and what to do. */

.error-panel {
  margin-top: 1rem;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--err);
  border-radius: var(--radius-sm);
  background: var(--err-bg);
}

.error-panel .err-message {
  margin: 0;
  font-weight: 640;
  font-size: 1.0625rem;
  line-height: 1.35;
  color: var(--text);
  overflow-wrap: anywhere;
}

.error-panel .err-where {
  margin: 0.3rem 0 0.8rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--err);
  font-variant-numeric: tabular-nums;
}

.error-panel pre {
  margin: 0;
  background: var(--bg-sunk);
  border-color: var(--line-strong);
  font-size: 0.8125rem;
  line-height: 1.45;
  tab-size: 2;
  color: var(--text);
  white-space: pre;
}

.error-panel .err-hint {
  margin: 0.85rem 0 0;
  font-size: 0.9375rem;
  color: var(--text-dim);
  max-width: 68ch;
}

/* ---------------------------------------------------------------- stats -- */

.stats-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(8.5rem, 100%), 1fr));
  gap: 0.75rem 1rem;
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.stats-row .stat-value { font-size: 1.0625rem; }

/* --------------------------------------------------------------- output -- */

.view-bay {
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}

.tabs {
  display: inline-flex;
  gap: 0.2rem;
  margin: 0 0 1rem;
  padding: 0.2rem;
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: 999px;
}

.tab {
  font-family: var(--mono);
  font-size: 0.78125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0.35rem 0.85rem;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.12s ease, background-color 0.12s ease;
}
.tab:hover { color: var(--text); }
.tab[aria-selected="true"] {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-dim);
}

.out-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.5rem;
}
.out-head .field-label { margin-right: auto; }

.output {
  margin: 0;
  max-height: 32rem;
  overflow: auto;
  tab-size: 2;
  white-space: pre;
  font-size: 0.8125rem;
  line-height: 1.55;
}
.output code { display: block; }

.out-trunc {
  margin: 0.6rem 0 0;
  font-size: 0.8125rem;
  color: var(--text-dim);
}

/* ----------------------------------------------------------------- tree -- */

.tree-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.75rem;
}
.tree-bar .search-field { flex: 1 1 14rem; min-width: 0; }
.tree-bar input[type="search"] { font-size: 0.875rem; }

.search-meta {
  margin: 0 0 0.6rem;
  font-size: 0.8125rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* --surface-2 rather than --bg-sunk: in the light theme the sunk surface drops
   --ok (the string colour) to 4.41:1, below AA for 13px text. On --surface-2
   every value colour clears 4.5:1. */
.match-list {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  max-height: 16rem;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.match-list li { margin: 0; border-bottom: 1px solid var(--line); }
.match-list li:last-child { border-bottom: 0; }

.match {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.9rem;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 0.45rem 0.7rem;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.78125rem;
  line-height: 1.4;
}
.match:hover { background: var(--surface); }
.match-path { color: var(--accent); overflow-wrap: anywhere; }
.match-val { color: var(--text-dim); overflow-wrap: anywhere; }

.tree {
  margin: 0;
  padding: 0.6rem 0.7rem;
  list-style: none;
  max-height: 34rem;
  overflow: auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.6;
}

.tree ul { list-style: none; margin: 0; padding: 0 0 0 1.15rem; }
.tree ul { border-left: 1px solid var(--line); margin-left: 0.32rem; }
.tree li { margin: 0; }

.tree-row {
  display: block;
  padding: 0.02rem 0.2rem;
  border-radius: 3px;
  white-space: pre;
  overflow-wrap: normal;
}

.tree-node:hover > .tree-row { background: var(--surface); }
.tree-node:focus { outline: none; }
.tree-node:focus > .tree-row {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* A ring rather than a background tint: tinting the row would drop the value
   colours below 4.5:1 against the composited surface in the light theme. */
.tree-node.is-hit > .tree-row {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.tree-toggle {
  display: inline-block;
  width: 1.05em;
  color: var(--text-faint);
  cursor: pointer;
  user-select: none;
}
.tree-toggle.is-leaf { color: var(--line-strong); cursor: default; }

.tree-more > .tree-row { color: var(--accent); cursor: pointer; padding-left: 1.25em; }

/* value colouring — every colour is inherited from base.css, so each one is
   already contrast-checked against both themes */
.t-key    { color: var(--text); }
.t-idx    { color: var(--text-faint); }
.t-punc   { color: var(--text-faint); }
.t-sum    { color: var(--text-faint); }
.t-string { color: var(--ok); }
.t-number { color: var(--accent); }
.t-boolean, .t-null { color: var(--warn); }
.t-object, .t-array { color: var(--text-faint); }

/* -------------------------------------------------------- privacy callout -- */

.callout {
  margin: 1.4em 0;
  padding: 0.9rem 1.1rem;
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--accent-dim);
  font-size: 0.9375rem;
  color: var(--text-dim);
}
.callout > :last-child { margin-bottom: 0; }
.callout strong { color: var(--text); }

.callout-quiet {
  border-left-color: var(--line-strong);
  background: var(--surface-2);
}

/* -------------------------------------------------------- content layer --- */

.content p, .content ul, .content ol { max-width: 68ch; }
.content .card-links { max-width: none; }
.content h2:first-child { margin-top: 0; }

/* .example carries .panel-sunk */
.example { margin-bottom: 1rem; }
.example h3 { margin-top: 0; }
.example > :last-child { margin-bottom: 0; }
.example pre { background: var(--surface); }

details .answer > :last-child { margin-bottom: 0; }

.bad-good {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 0.75rem;
  margin: 0 0 1.2em;
}
.bad-good figure { margin: 0; }
.bad-good figcaption {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 0.35rem;
}
.bad-good .is-bad figcaption { color: var(--err); }
.bad-good .is-good figcaption { color: var(--ok); }
.bad-good pre { margin: 0; height: 100%; }

.sources { font-size: 0.9375rem; }
.sources li { margin-bottom: 0.6em; }
.source-note { color: var(--text-dim); }

/* ------------------------------------------------------------ card links -- */

.card-links {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
  gap: 0.75rem;
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
}
.card-links li { margin: 0; }

.card-links a {
  display: block;
  height: 100%;
  padding: 0.7rem 0.9rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.card-links a:hover { border-color: var(--accent); background: var(--surface-2); }
.card-links strong { display: block; font-size: 0.9375rem; }
.card-links span {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.8125rem;
  color: var(--text-dim);
}
.card-links a:hover span { color: var(--accent); }

/* --------------------------------------------------------- escape widget -- */

.esc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: 1rem;
}
.esc-grid textarea { min-height: 8rem; font-family: var(--mono); font-size: 0.8125rem; }
.esc-out {
  margin: 0;
  min-height: 8rem;
  max-height: 20rem;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 0.8125rem;
}

@media (max-width: 30rem) {
  .actions { margin-left: 0; }
  .tree { font-size: 0.75rem; }
}
