/* ==========================================================================
   JSON Formatter — Blueprint (dark) / Workspace (light) dual-mode styles.

   Color/typography tokens live ONLY under :root[data-theme="dark"] and
   :root[data-theme="light"]. theme.js is responsible for setting
   data-theme on <html> (the actual :root element) — never on a nested
   div — or none of these variables resolve and the UI renders unstyled.
   ========================================================================== */

* { box-sizing: border-box; }

/* The [hidden] attribute must always win over any author `display` rule
   (e.g. `.jf-body { display: flex }`, `.modal-overlay { display: flex }`).
   Per CSS cascade rules, UA-origin `[hidden]{display:none}` only outranks
   an author rule of equal specificity by origin — but loses to ANY author
   rule that also sets `display`, since author > UA regardless of order.
   Declaring it here, in the author stylesheet, restores that guarantee. */
[hidden] { display: none !important; }

:root {
  --font-cjk-fallback: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Malgun Gothic', 'Apple SD Gothic Neo',
    'Hiragino Kaku Gothic ProN', 'Yu Gothic', 'Microsoft YaHei', 'PingFang SC', sans-serif;
  --font-code: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius: 8px;
  --radius-sm: 6px;
}

/* ===== Blueprint — dark ===== */
:root[data-theme="dark"] {
  --bg: #0B1D2A;
  --bg-alt: #0F2536;
  --surface: #123047;
  --hairline: #1C3A4D;
  --text: #DCEAF2;
  --text-dim: #8FB0C2;
  --text-faint: #5D7E90;
  --accent: #7FD1FF;
  --accent-soft: rgba(127, 209, 255, .14);
  --on-accent: #062230;
  --k: #7FD1FF;
  --s: #8CE6B0;
  --n: #FFD479;
  --bo: #C9A6FF;
  --success: #63D3A6;
  --error: #FF6B6B;
  --dot-glow: rgba(99, 211, 166, .2);
  --error-glow: rgba(255, 107, 107, .22);
  --shadow: 0 16px 40px rgba(3, 10, 16, .45);
  --font-title: 'Space Grotesk', var(--font-cjk-fallback);
  --font-body: 'IBM Plex Sans', var(--font-cjk-fallback);
  --grid-texture:
    repeating-linear-gradient(0deg, rgba(127, 209, 255, .05) 0px, rgba(127, 209, 255, .05) 1px, transparent 1px, transparent 32px),
    repeating-linear-gradient(90deg, rgba(127, 209, 255, .05) 0px, rgba(127, 209, 255, .05) 1px, transparent 1px, transparent 32px);
  --radius-panel: 6px;
  --mark-bg: rgba(255, 212, 121, .35);
}

/* ===== Workspace — light ===== */
:root[data-theme="light"] {
  --bg: #F7F8FA;
  --bg-alt: #FFFFFF;
  --surface: #F2F3F6;
  --hairline: #E4E7EC;
  --text: #1F242C;
  --text-dim: #6B7280;
  --text-faint: #9AA1AC;
  --accent: #4F46E5;
  --accent-soft: rgba(79, 70, 229, .09);
  --on-accent: #FFFFFF;
  --k: #374151;
  --s: #0F9D6B;
  --n: #2563EB;
  --bo: #B45309;
  --success: #0F9D6B;
  --error: #D6392F;
  --dot-glow: rgba(15, 157, 107, .16);
  --error-glow: rgba(214, 57, 47, .18);
  --shadow: 0 1px 2px rgba(20, 25, 35, .04);
  --font-title: 'Manrope', var(--font-cjk-fallback);
  --font-body: 'Inter', var(--font-cjk-fallback);
  --grid-texture: none;
  --radius-panel: 10px;
  --mark-bg: rgba(37, 99, 235, .16);
}

html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  background: var(--bg);
  background-image: var(--grid-texture);
  color: var(--text);
  font-family: var(--font-body);
  transition: background-color .2s ease, color .2s ease;
}

/* Capping body's height (not just min-height) and hiding its own overflow
   is what makes this an app shell with internal scroll regions instead of
   a page that grows to fit content: every flex/grid layer between here and
   .jf-input / .code-rows / .jf-tree must carry min-height: 0 so it can
   actually be clamped to the space this assigns it, letting those inner
   panels do the scrolling instead of the whole document. Restored to
   normal page scrolling on small screens below, where panels stack. */
body {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.app-shell {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 20px 22px 26px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

button, input, select, textarea { font-family: inherit; color: inherit; }
button { cursor: pointer; }

/* ===== Header ===== */
.jf-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; flex: 0 0 auto; }
.jf-brand { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.mode-toggle {
  display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;
  border-radius: var(--radius-sm); border: 1px solid var(--hairline); background: var(--surface);
  color: var(--accent); font-size: 12.5px; font-weight: 600; min-width: max-content;
}
.mode-toggle:hover { border-color: var(--accent); }
.mode-toggle .mt-icon { font-size: 14px; line-height: 1; }

.jf-logo { font-family: var(--font-title); font-weight: 700; font-size: 24px; display: flex; align-items: center; gap: 5px; }
.jf-logo .brace { color: var(--accent); }
.jf-dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--text-faint); box-shadow: 0 0 0 4px transparent;
  transition: background-color .15s ease, box-shadow .15s ease;
}
.jf-dot.valid { background: var(--success); box-shadow: 0 0 0 4px var(--dot-glow); }
.jf-dot.invalid { background: var(--error); box-shadow: 0 0 0 4px var(--error-glow); }
.jf-dot.small { width: 8px; height: 8px; }
.jf-dot.pulse { animation: jf-pulse .5s ease; }
@keyframes jf-pulse { 0% { transform: scale(1); } 40% { transform: scale(1.6); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .jf-dot.pulse { animation: none; } }

.jf-titles .t1 { font-family: var(--font-title); font-weight: 600; font-size: 16px; }
.jf-titles .t2 { font-size: 12px; color: var(--text-dim); margin-top: 1px; }

.jf-intro { font-size: 12.5px; line-height: 1.55; color: var(--text-dim); margin: -4px 0 12px; max-width: 900px; flex: 0 0 auto; }

.jf-header-right { display: flex; align-items: center; gap: 10px; }
.icon-btn {
  height: 32px; min-width: 32px; padding: 0 8px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: 1px solid var(--hairline); background: var(--surface); font-size: 13px;
}
.icon-btn:hover { border-color: var(--accent); color: var(--accent); }

.lang-pill { display: flex; border: 1px solid var(--hairline); border-radius: var(--radius-sm); overflow: hidden; }
.lang-pill span { padding: 6px 10px; font-size: 12px; font-weight: 600; color: var(--text-dim); white-space: nowrap; }
.lang-pill span:hover { color: var(--text); }
.lang-pill span.on { background: var(--accent); color: var(--on-accent); }

/* ===== Toolbar ===== */
.jf-toolbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; background: var(--bg-alt);
  border: 1px solid var(--hairline); border-radius: var(--radius-panel); padding: 9px 11px; margin-bottom: 12px;
  flex: 0 0 auto;
}
.jf-toolbar .grp { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.jf-toolbar .sep { width: 1px; height: 20px; background: var(--hairline); margin: 0 4px; }
.jf-toolbar .grow { flex: 1; }

.jbtn {
  height: 32px; padding: 0 12px; border-radius: var(--radius-sm); border: 1px solid var(--hairline);
  background: var(--surface); color: var(--text); font-size: 12.8px; font-weight: 500;
  display: flex; align-items: center; gap: 5px; white-space: nowrap; min-width: max-content;
}
.jbtn:hover { border-color: var(--accent); }
.jbtn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.jbtn.active, .jbtn[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.jbtn.ghost { background: transparent; }
.jbtn:disabled { opacity: .45; cursor: not-allowed; }

.jsel {
  height: 32px; border-radius: var(--radius-sm); border: 1px solid var(--hairline); background: var(--surface);
  color: var(--text); font-size: 12.8px; padding: 0 8px;
}

.jswitch-label { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-dim); white-space: nowrap; }
.jswitch { width: 30px; height: 17px; border-radius: 20px; background: var(--accent-soft); position: relative; display: inline-block; }
.jswitch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.jswitch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%;
  background: var(--text-faint); transition: left .15s ease, background-color .15s ease;
}
.jswitch:has(input:checked) { background: var(--accent-soft); }
.jswitch:has(input:checked)::after { left: 15px; background: var(--accent); }

/* ===== Main grid ===== */
/* min-height: 0 (not a positive floor) is required here: a grid container's
   implicit auto-sized row otherwise grows to fit its tallest child's
   content — e.g. a textarea full of many lines — instead of being clamped
   to the height flex-grow assigned it, which breaks internal scrolling and
   pushes the whole page taller. Same class of bug as the .jf-body / .jf-panel
   min-height: 0 rules below. */
.jf-main { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; flex: 1 1 auto; min-height: 0; }
.jf-panel { background: var(--bg-alt); border: 1px solid var(--hairline); border-radius: var(--radius-panel); overflow: hidden; box-shadow: var(--shadow); position: relative; display: flex; flex-direction: column; min-height: 0; height: 100%; }
.jf-panel-head {
  display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid var(--hairline);
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim); flex: 0 0 auto;
}

.jf-seg { display: flex; background: var(--surface); border: 1px solid var(--hairline); border-radius: 7px; padding: 2px; }
.jf-seg span { padding: 4px 9px; border-radius: 5px; font-size: 11.5px; font-weight: 600; color: var(--text-dim); text-transform: none; letter-spacing: normal; cursor: pointer; }
.jf-seg span.on { background: var(--accent); color: var(--on-accent); }
.jf-seg span.disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }

.jf-body { display: flex; flex: 1 1 auto; min-height: 0; position: relative; }
.jf-body-code { overflow: hidden; }

.jf-lines {
  font-family: var(--font-code); font-size: 12.5px; line-height: 20px; color: var(--text-faint);
  padding: 12px 0; text-align: right; border-right: 1px solid var(--hairline); min-width: 38px;
  overflow: hidden; user-select: none; flex: 0 0 auto;
  display: flex; flex-direction: column;
}
.jf-lines .line-no-cell { padding: 0 8px 0 0; }

.jf-input {
  font-family: var(--font-code); font-size: 12.5px; line-height: 20px; padding: 12px 14px; flex: 1; min-width: 0;
  white-space: pre-wrap; overflow-wrap: anywhere; overflow-x: hidden; overflow-y: auto;
  resize: none; border: none; outline: none; background: transparent; color: var(--text);
  tab-size: 2;
}

/* Off-screen twin of .jf-input: same font/padding/width so measuring its
   per-line rendered height tells us exactly how tall each logical line
   wraps to inside the real textarea. See line-gutter.js. */
.line-mirror {
  position: fixed; top: -100000px; left: 0; visibility: hidden; height: auto; pointer-events: none; z-index: -1;
  overflow: visible; resize: none;
}
.line-mirror > div { white-space: pre-wrap; overflow-wrap: anywhere; }

.code-rows { flex: 1 1 auto; min-height: 0; overflow: auto; font-family: var(--font-code); font-size: 12.5px; line-height: 20px; padding: 12px 0; }
.code-row { display: flex; padding: 0 14px; }
.cline-no { flex: 0 0 auto; width: 30px; margin-right: 10px; padding-right: 8px; text-align: right; border-right: 1px solid var(--hairline); color: var(--text-faint); user-select: none; }
.cline-content { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text); }
.k { color: var(--k); }
.s { color: var(--s); }
.n { color: var(--n); }
.b { color: var(--bo); }
mark { background: var(--mark-bg); color: inherit; border-radius: 3px; padding: 0 1px; }

.empty-state { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; color: var(--text-faint); font-size: 13px; text-align: center; padding: 20px; pointer-events: none; }
.jf-panel.is-empty .empty-state { display: flex; }
/* diffEmptyState's visibility is driven directly by JS via the hidden
   attribute (compareAst set or not) rather than the shared .is-empty
   ancestor toggle the other two empty-states use — needs higher
   specificity than the bare .empty-state default to actually show. */
#diffEmptyState:not([hidden]) { display: flex; }
.jf-panel.is-empty .code-rows, .jf-panel.is-empty .jf-tree { visibility: hidden; }

.drop-overlay {
  position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
  background: var(--accent-soft); border: 2px dashed var(--accent); color: var(--accent);
  font-size: 14px; font-weight: 600; z-index: 5;
}
#inputPanel.drag-over .drop-overlay { display: flex; }

/* ===== Tree view ===== */
.jf-body-tree { flex-direction: column; padding: 0; }
.tree-toolbar { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--hairline); flex-wrap: wrap; flex: 0 0 auto; }
.tree-search { height: 28px; flex: 1; min-width: 120px; border-radius: var(--radius-sm); border: 1px solid var(--hairline); background: var(--surface); color: var(--text); font-size: 12px; padding: 0 9px; }
.jf-tree { font-family: var(--font-code); font-size: 12.5px; padding: 10px 12px; flex: 1; overflow: auto; }

.tree-node { position: relative; }
.tree-row { display: flex; align-items: flex-start; gap: 6px; padding: 3px 4px; border-radius: 4px; }
.tree-row:hover { background: var(--surface); }
.tree-row:hover .copy-path-btn { opacity: 1; }
.tree-row.tree-match { background: var(--accent-soft); }
.tree-row > .tri, .tree-row > .tree-key, .tree-row > .tree-colon, .tree-row > .badge, .tree-row > .imghint, .tree-row > .copy-path-btn { flex: 0 0 auto; }
.tree-row > .s, .tree-row > .n, .tree-row > .b { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.tree-children { margin-left: 17px; border-left: 1px dashed var(--hairline); padding-left: 4px; }
.tree-node.tree-dim > .tree-row { display: none; }
.tree-node.tree-dim > .tree-children { display: none; }

.tri {
  width: 0; height: 0; border-left: 5px solid var(--text-faint); border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .12s ease; flex: 0 0 auto; cursor: pointer; margin-top: 4px;
}
.tri.open { transform: rotate(90deg); }

.tree-key { white-space: nowrap; }
.tree-colon { color: var(--text-faint); }
.badge { color: var(--text-faint); font-size: 11px; white-space: nowrap; }
.imghint { color: var(--text-faint); font-size: 11px; white-space: nowrap; cursor: default; }

.copy-path-btn {
  opacity: 0; margin-left: auto; border: none; background: transparent; color: var(--text-faint);
  font-size: 12px; padding: 2px 5px; border-radius: 4px; flex: 0 0 auto;
}
.copy-path-btn:hover { color: var(--accent); background: var(--accent-soft); opacity: 1; }

/* ===== Diff view ===== */
.jf-body-diff { flex-direction: column; padding: 0; }
.jf-diff { font-family: var(--font-code); font-size: 12.5px; padding: 10px 12px; flex: 1; overflow: auto; display: flex; flex-direction: column; gap: 4px; }
.diff-empty { color: var(--text-faint); padding: 20px; text-align: center; }
.diff-row { display: flex; align-items: baseline; gap: 8px; padding: 5px 8px; border-radius: 4px; flex-wrap: wrap; }
.diff-row.diff-added { background: color-mix(in srgb, var(--success) 10%, transparent); }
.diff-row.diff-removed { background: color-mix(in srgb, var(--error) 10%, transparent); }
.diff-row.diff-changed { background: color-mix(in srgb, var(--n) 12%, transparent); }
.diff-badge { flex: 0 0 auto; width: 14px; text-align: center; font-weight: 700; }
.diff-row.diff-added .diff-badge { color: var(--success); }
.diff-row.diff-removed .diff-badge { color: var(--error); }
.diff-row.diff-changed .diff-badge { color: var(--n); }
.diff-path { flex: 0 0 auto; color: var(--accent); }
.diff-label { flex: 0 0 auto; color: var(--text-faint); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
.diff-value { color: var(--text-dim); word-break: break-word; }
.diff-value.diff-old { text-decoration: line-through; }
.diff-value.diff-new { color: var(--text); }
.diff-arrow { color: var(--text-faint); flex: 0 0 auto; }

/* ===== Status bar / footer ===== */
.jf-status {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap; background: var(--bg-alt); border: 1px solid var(--hairline);
  border-radius: var(--radius-panel); padding: 8px 13px; margin-top: 12px; font-size: 11.8px; color: var(--text-dim);
  flex: 0 0 auto;
}
.jf-status .ok { color: var(--success); font-weight: 600; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.jf-status .ok.is-invalid { color: var(--error); }
.jf-status .ok.is-empty { color: var(--text-dim); }
.error-link { border: none; background: transparent; color: var(--error); font-size: 11.8px; text-decoration: underline; text-align: left; padding: 0; cursor: pointer; }
.error-link:hover { opacity: .8; }
.jf-status .stats { margin-left: auto; display: flex; gap: 12px; font-family: var(--font-code); flex-wrap: wrap; }

.jf-foot { display: flex; align-items: center; justify-content: center; gap: 6px; text-align: center; font-size: 11px; color: var(--text-faint); margin-top: 10px; flex: 0 0 auto; }

/* ===== Toast ===== */
.toast {
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); background: var(--surface); color: var(--text);
  border: 1px solid var(--hairline); border-radius: var(--radius-sm); padding: 10px 16px; font-size: 13px; box-shadow: var(--shadow);
  z-index: 60; animation: toast-in .15s ease;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ===== Image hover preview ===== */
.img-preview {
  position: fixed; z-index: 70; border: 1px solid var(--hairline); border-radius: var(--radius-sm); background: var(--bg-alt);
  box-shadow: var(--shadow); padding: 6px; pointer-events: none;
}
.img-preview img { display: block; max-width: 220px; max-height: 220px; border-radius: 4px; }

/* ===== Modals ===== */
.modal-overlay { position: fixed; inset: 0; background: rgba(4, 10, 16, .5); display: flex; align-items: center; justify-content: center; z-index: 80; padding: 20px; }
.modal {
  background: var(--bg-alt); border: 1px solid var(--hairline); border-radius: var(--radius-panel); box-shadow: var(--shadow);
  width: 100%; max-width: 420px; padding: 18px 20px 20px;
}
.modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.modal-head h2 { font-family: var(--font-title); font-size: 16px; margin: 0; }
.shortcut-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.shortcut-list li { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.shortcut-list li span:last-child { margin-left: auto; color: var(--text-dim); }
kbd {
  font-family: var(--font-code); font-size: 11px; background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 4px; padding: 2px 6px;
}
.url-form { display: flex; gap: 8px; }
.url-form input {
  flex: 1; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--hairline); background: var(--surface);
  color: var(--text); font-size: 13px; padding: 0 10px;
}

.modal-lg { max-width: 640px; }
.convert-options { display: flex; gap: 10px; }
.convert-options .jbtn { flex: 1; justify-content: center; height: 44px; font-size: 14px; }

.modal-textarea {
  width: 100%; height: 220px; resize: vertical; border-radius: var(--radius-sm); border: 1px solid var(--hairline);
  background: var(--surface); color: var(--text); font-family: var(--font-code); font-size: 12.5px; line-height: 1.5;
  padding: 10px 12px; white-space: pre-wrap; overflow-wrap: anywhere; box-sizing: border-box;
}
.modal-actions { display: flex; justify-content: flex-end; margin-top: 10px; }

.schema-results { margin-top: 14px; max-height: 260px; overflow: auto; border-top: 1px solid var(--hairline); padding-top: 10px; }
.schema-results.ok { color: var(--success); font-size: 13px; font-weight: 600; }
.schema-summary { font-size: 13px; font-weight: 600; margin-bottom: 8px; }
.schema-summary.is-valid { color: var(--success); }
.schema-summary.is-invalid { color: var(--error); }
.schema-violation-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.schema-violation-list li { font-size: 12.5px; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--surface); }
.schema-violation-list .path { font-family: var(--font-code); color: var(--accent); margin-right: 6px; }

@media (max-width: 860px) {
  /* Stacked panels intentionally exceed one viewport's height on small
     screens, so the page itself should scroll here — undo the fixed-height
     clipped shell from the base body rule. */
  body { height: auto; min-height: 100vh; min-height: 100dvh; overflow: visible; }
  .app-shell { padding: 14px 12px 18px; }
  .jf-main {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(240px, 1fr) minmax(240px, 1fr);
    min-height: 520px;
  }
  .jf-status .stats { margin-left: 0; width: 100%; }
}

@media (max-width: 480px) {
  .app-shell { padding: 10px 8px 14px; }
  .jf-titles .t2 { display: none; }
  .jf-toolbar { padding: 7px 8px; gap: 6px; }
  .jbtn, .jsel, .icon-btn { height: 30px; font-size: 12px; }
  .jf-status { font-size: 11px; gap: 10px; }
}
