/* The workbench: the shell, the app bar, the toolbar, the editor, the tree and the status
   bar, one section per part of the page. Only the shell pages (the workbench and
   /not-found) load this.

   Converted from the Blazor version's scoped .razor.css files. Scoping is what used to
   keep each component's rules to its own markup; here every selector is a class that
   only that part of the page uses, and the rules that had to reach into markup no
   component rendered - CodeMirror's, which `::deep` was for - hang off the editor host
   instead. */

/* ==== Shell ================================================================= */

:root {
    --jf-statusbar-h: 1.75rem;
}

.jf-shell {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--jf-bg);
    color: var(--jf-text);
    font-family: 'Inter', 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, sans-serif;
    font-size: 0.875rem;
}

/* ==== App bar =============================================================== */

.appbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex: none;
    height: 2.75rem;
    padding: 0 0.625rem;
    background: var(--jf-chrome);
    border-bottom: 1px solid var(--jf-border);
}

.brand {
    display: flex;
    align-items: center;
    gap: 0.4375rem;
    flex: none;
    padding-right: 0.25rem;
}

.brand-mark {
    width: 1.25rem;
    height: 1.25rem;
    fill: none;
    stroke: var(--jf-muted);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.brand-mark-flex {
    stroke: var(--jf-accent);
}

.brand-name {
    color: var(--jf-text);
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.brand-name-accent {
    color: var(--jf-accent);
}

.appbar-spacer {
    flex: 1;
}

@media (max-width: 1228.78px) {
    .appbar {
        gap: 0.5rem;
    }
}

/* ==== Theme switch ========================================================== */

.theme-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    flex: none;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--jf-border);
    border-radius: 999px;
    background: transparent;
    color: var(--jf-muted);
    font: inherit;
    font-size: 0.75rem;
    cursor: pointer;
}

.theme-btn:hover:not(:disabled) {
    background: var(--jf-chrome-hover);
    color: var(--jf-text);
}

.theme-btn:disabled {
    cursor: default;
    opacity: 0.6;
}

/* The label is the first thing to go when the chrome tightens - the glyph on its
   own still says which way the switch will move. */
@media (max-width: 1228.78px) {
    .theme-btn-label {
        display: none;
    }
}

/* ==== Toolbar =============================================================== */

/* The editor fills the space below the app bar and above the status bar; the toolbar
   and, when there is one, the error banner sit above it. */
.jf {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.jf-toolbar {
    display: flex;
    align-items: center;
    flex: none;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: var(--jf-chrome-2);
    border-bottom: 1px solid var(--jf-border-soft);
}

/* `hidden` is an attribute the script sets on toolbar items that do not apply to the view
   on screen; every one of them sets a `display` of its own, which would otherwise win. */
.jf-toolbar [hidden] {
    display: none;
}

.jf-group {
    display: flex;
    flex: none;
    gap: 0.125rem;
    padding: 0.125rem;
    border-radius: 8px;
    background: var(--jf-pane-alt);
    border: 1px solid var(--jf-border-soft);
}

.seg-btn {
    flex: none;
    padding: 0.25rem 0.625rem;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--jf-muted);
    font: inherit;
    font-size: 0.8125rem;
    cursor: pointer;
}

.seg-btn:hover:not(:disabled) {
    color: var(--jf-text);
}

.seg-btn:disabled {
    cursor: default;
    opacity: 0.45;
}

.seg-btn.is-on {
    background: var(--jf-accent);
    color: var(--jf-accent-ink);
}

/* Beats `.seg-btn:hover` on specificity alone, but named explicitly rather than left to
   rely on that: without it, hovering the active segment swaps its ink for `--jf-text`,
   which is unreadable over the accent's own background. */
.seg-btn.is-on:hover:not(:disabled) {
    color: var(--jf-accent-ink);
}

.pane-btn {
    display: inline-flex;
    align-items: center;
    flex: none;
    padding: 0.3125rem 0.75rem;
    border: 1px solid var(--jf-border);
    border-radius: 6px;
    background: var(--jf-pane-alt);
    color: var(--jf-text);
    font: inherit;
    font-size: 0.8125rem;
    line-height: 1;
    cursor: pointer;
}

.pane-btn:hover:not(:disabled) {
    background: var(--jf-chrome-hover);
}

.pane-btn:disabled {
    cursor: default;
    opacity: 0.45;
}

.pane-btn.copy-success {
    border-color: var(--jf-accent);
    background: var(--jf-accent-wash);
    color: var(--jf-accent-bright);
}

.jf-sort-btn {
    gap: 0.375rem;
}

.jf-sort-icon {
    flex: none;
    color: currentColor;
}

.jf-sort-btn.is-on {
    border-color: var(--jf-accent);
    background: var(--jf-accent-wash);
    color: var(--jf-accent-bright);
}

/* Same fix as `.seg-btn.is-on:hover` above: without this, hovering the active sort button
   swaps its accent-coloured text (and icon, which tracks it via `currentColor`) for
   `--jf-text`. */
.jf-sort-btn.is-on:hover:not(:disabled) {
    color: var(--jf-accent-bright);
}

.jf-filter {
    flex: 0 1 16rem;
    min-width: 8rem;
    padding: 0.3125rem 0.625rem;
    border: 1px solid var(--jf-border);
    border-radius: 6px;
    background: var(--jf-pane-alt);
    color: var(--jf-text);
    font: inherit;
    font-size: 0.8125rem;
}

.jf-filter::placeholder {
    color: var(--jf-faint);
}

.jf-find-count {
    flex: none;
    color: var(--jf-muted);
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Present for the label's `for` to click, invisible itself - the label is the button,
   styled like every other action in this bar. */
.jf-file-input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
}

.jf-toolbar-spacer {
    flex: 1;
}

.jf-error {
    flex: none;
    padding: 0.5rem 0.875rem;
    background: color-mix(in srgb, var(--jf-danger) 14%, var(--jf-pane));
    color: var(--jf-danger);
    font-size: 0.8125rem;
    border-bottom: 1px solid var(--jf-border-soft);
}

.jf-error[hidden] {
    display: none;
}

.jf-body {
    display: flex;
    flex: 1;
    min-height: 0;
    background: var(--jf-pane);
}

/* ==== Text view ============================================================= */

/* Hosts the CodeMirror instance created by js/codemirror-editor.js. CodeMirror renders
   its own gutter (line numbers plus the fold-arrow column) and content inside this
   element; the rules below style that markup through the host, which is what the Blazor
   version's `::deep` did. */
.jf-editor-host {
    flex: 1;
    min-width: 0;
    min-height: 0;
}

.jf-editor-host[hidden] {
    display: none;
}

.jf-editor-host .cm-editor {
    height: 100%;
    background: transparent;
    color: var(--jf-text);
    font-family: 'Cascadia Code', 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Consolas, monospace;
    font-size: 0.8125rem;
}

.jf-editor-host .cm-scroller {
    line-height: 1.6;
}

.jf-editor-host .cm-content {
    padding: 0.75rem 0.875rem;

    /* No `drawSelection()` extension is loaded, so there is no custom `.cm-cursor`
       layer to style - the caret is the browser's own native one, rendered in
       `caret-color`, which defaults to black rather than following `.cm-editor`'s
       `color` and is invisible against this app's dark background. */
    caret-color: var(--jf-text);
}

.jf-editor-host .cm-editor.cm-focused {
    outline: none;
}

.jf-editor-host .cm-gutters {
    border-right: 1px solid var(--jf-border-soft);
    background: var(--jf-pane-alt);
    color: var(--jf-faint);
}

.jf-editor-host .cm-activeLineGutter {
    background: var(--jf-chrome-hover);
}

.jf-editor-host .cm-foldGutter {
    color: var(--jf-faint);
}

.jf-editor-host .cm-foldGutter .cm-gutterElement:hover {
    color: var(--jf-text);
    cursor: pointer;
}

.jf-editor-host .cm-placeholder {
    color: var(--jf-faint);
}

/* Search panel styling - matches JsonFlex's color scheme. */
.jf-editor-host .cm-search {
    background: var(--jf-pane-alt);
    border: 1px solid var(--jf-border-soft);
    border-top: none;
}

.jf-editor-host .cm-search label {
    color: var(--jf-muted);
    font-size: 0.8125rem;
}

.jf-editor-host .cm-search input {
    background: var(--jf-pane);
    border: 1px solid var(--jf-border);
    color: var(--jf-text);
    font-size: 0.8125rem;
}

.jf-editor-host .cm-search input::placeholder {
    color: var(--jf-faint);
}

.jf-editor-host .cm-search button {
    background: var(--jf-pane-alt);
    border: 1px solid var(--jf-border);
    color: var(--jf-text);
    cursor: pointer;
}

.jf-editor-host .cm-search button:hover {
    background: var(--jf-chrome-hover);
}

.jf-editor-host .cm-searchMatch {
    background-color: var(--jf-accent-wash);
    outline: 1px solid var(--jf-accent-dim);
}

.jf-editor-host .cm-searchMatch-selected {
    background-color: var(--jf-accent-dim);
}

/* Mirrors the tree view's palette (jf-key, the jf-value-<kind> classes, jf-bracket and
   jf-colon) exactly, via the classes tools/codemirror-entry.js's highlight style assigns -
   so a value looks the same colour whether it's read in Tree or Text. The Blazor
   version's copy of this comment spelt the value classes with a `*` followed by a `/`,
   which ended the comment early and took the key colour rule below down with it; keys
   are coloured in Text view now, as was intended. */
.jf-editor-host .cm-jf-key {
    color: var(--jf-side-left);
}

.jf-editor-host .cm-jf-string {
    color: var(--jf-side-result);
}

.jf-editor-host .cm-jf-number {
    color: var(--jf-side-right);
}

.jf-editor-host .cm-jf-bool {
    color: var(--jf-accent-bright);
}

.jf-editor-host .cm-jf-null {
    color: var(--jf-faint);
}

.jf-editor-host .cm-jf-punct {
    color: var(--jf-faint);
}

/* Error line and column highlighting */
.jf-editor-host .cm-error-line {
    background-color: color-mix(in srgb, var(--jf-danger) 8%, transparent);
}

.jf-editor-host .cm-error-column {
    background-color: color-mix(in srgb, var(--jf-danger) 22%, transparent);
    text-decoration: underline wavy var(--jf-danger);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

/* ==== Tree view ============================================================= */

/* One fixed height for every row, so the virtual list in js/treeview.js can work out
   which rows are on screen, and where to put each one, by arithmetic alone. Tall enough
   for the key and value boxes, whose padding and border sit on top of the line height. */
.jf-tree {
    --jf-row-h: 1.55rem;

    position: relative;
    flex: 1;
    overflow: auto;
    padding: 0.625rem 0.875rem;
    font-family: 'Cascadia Code', 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Consolas, monospace;
    font-size: 0.8125rem;
    line-height: 1.6;
}

.jf-tree[hidden] {
    display: none;
}

/* As tall as every row together, so the scrollbar is the whole document's; the rows in
   view are placed inside it by their index. */
.jf-tree-spacer {
    position: relative;
}

.jf-node {
    position: absolute;
    left: 0;
    right: 0;
    display: block;
    height: var(--jf-row-h);
    overflow: hidden;
}

.jf-row {
    display: flex;
    align-items: baseline;
    gap: 0.3em;
    height: 100%;
    padding-left: calc(var(--jf-depth) * 1.25em);
    border-radius: 3px;
    white-space: nowrap;
}

.jf-row:hover {
    background: var(--jf-chrome-hover);
}

.jf-row.is-current-match {
    background: var(--jf-accent-wash);
    outline: 1px solid var(--jf-accent);
    outline-offset: -1px;
}

.jf-row.is-current-match mark {
    background: var(--jf-accent-bright);
    color: var(--jf-accent-ink);
}

.jf-toggle {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5em;
    height: 1.5em;
    margin: -0.25em 0;
    padding: 0;
    border: none;
    border-radius: 3px;
    background: none;
    color: var(--jf-faint);
    font: inherit;
    font-size: 0.85em;
    line-height: 1;
    cursor: pointer;
}

.jf-toggle:hover {
    color: var(--jf-text);
    background: var(--jf-chrome-hover);
}

.jf-toggle-spacer {
    flex: none;
    width: 1.5em;
}

.jf-key {
    color: var(--jf-side-left);
}

.jf-key-editor {
    flex: none;
    min-width: 0;
    padding: 0.05rem 0.2rem;
    border: 1px solid transparent;
    border-radius: 3px;
    background: transparent;
    color: var(--jf-side-left);
    font: inherit;
    line-height: inherit;
}

.jf-key-editor:focus {
    border-color: var(--jf-accent);
    background: var(--jf-accent-wash);
    outline: none;
}

.jf-colon {
    color: var(--jf-faint);
}

.jf-bracket {
    color: var(--jf-faint);
}

.jf-summary {
    margin: 0 0.35em;
    color: var(--jf-faint);
    font-style: italic;
}

.jf-value-editor {
    flex: 1;
    min-width: 0;
    padding: 0.05rem 0.2rem;
    border: 1px solid transparent;
    border-radius: 3px;
    background: transparent;
    color: inherit;
    font: inherit;
    line-height: inherit;
}

.jf-value-editor:focus {
    border-color: var(--jf-accent);
    background: var(--jf-accent-wash);
    outline: none;
}

.jf-value-string {
    color: var(--jf-side-result);
}

.jf-value-number {
    color: var(--jf-side-right);
}

.jf-value-bool {
    color: var(--jf-accent-bright);
}

.jf-value-null {
    color: var(--jf-faint);
}

.jf-tree mark {
    padding: 0 1px;
    border-radius: 2px;
    background: var(--jf-accent-dim);
    color: inherit;
}

/* ==== Status bar ============================================================ */

.statusbar {
    display: flex;
    align-items: center;
    flex: none;
    gap: 0.75rem;
    box-sizing: border-box;
    /* Fixed, so the privacy sheet can sit exactly on top of it. */
    height: var(--jf-statusbar-h);
    padding: 0 0.875rem;
    background: var(--jf-chrome);
    border-top: 1px solid var(--jf-border);
    color: var(--jf-muted);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
}

.statusbar [hidden] {
    display: none;
}

.statusbar-item {
    flex: none;
}

.statusbar-warn {
    flex: none;
    color: var(--jf-danger);
}

.statusbar-spacer {
    flex: 1;
}

.statusbar-note {
    flex: none;
    color: var(--jf-faint);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The privacy notice. Faint, like the summary, because it is a standing control rather
   than anything about the document - but it stays at every width. The chevron points up
   to open the sheet and down once it is open. */
.statusbar-link {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    flex: none;
    padding: 0;
    border: 0;
    background: none;
    color: var(--jf-faint);
    font: inherit;
    white-space: nowrap;
    cursor: pointer;
}

.statusbar-link:hover,
.statusbar-link:focus-visible,
.statusbar:has(+ .privacy-sheet:popover-open) .statusbar-link {
    color: var(--jf-text);
}

.statusbar-link:hover span,
.statusbar-link:focus-visible span {
    text-decoration: underline;
}

.statusbar-chevron {
    width: 0.625rem;
    height: 0.625rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 0.2s ease;
}

.statusbar:has(+ .privacy-sheet:popover-open) .statusbar-chevron {
    transform: rotate(180deg);
}

/* ==== Privacy sheet ========================================================= */

/* Slides up out of the status bar, on the right under the button that opened it. A
   popover, so it lives in the top layer and needs no z-index; the UA's centring and
   border are undone. */
.privacy-sheet {
    position: fixed;
    inset: auto 0.75rem var(--jf-statusbar-h) auto;
    box-sizing: border-box;
    width: min(26rem, calc(100vw - 1.5rem));
    max-height: calc(100vh - var(--jf-statusbar-h) - 3.5rem);
    margin: 0;
    padding: 0.875rem 1rem 0.75rem;
    overflow: auto;
    background: var(--jf-pane);
    border: 1px solid var(--jf-border);
    border-bottom: 0;
    border-radius: 0.5rem 0.5rem 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.22);
    color: var(--jf-text);
    font-family: 'Inter', 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, sans-serif;
    font-size: 0.8125rem;
    line-height: 1.5;

    transform: translateY(100%);
    opacity: 0;
    transition:
        transform 0.22s ease,
        opacity 0.22s ease,
        overlay 0.22s allow-discrete,
        display 0.22s allow-discrete;
}

.privacy-sheet:popover-open {
    transform: none;
    opacity: 1;
}

@starting-style {
    .privacy-sheet:popover-open {
        transform: translateY(100%);
        opacity: 0;
    }
}

/* Clear, so the workbench stays readable behind it - it is a note, not a dialog. */
.privacy-sheet::backdrop {
    background: transparent;
}

.privacy-sheet-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.privacy-sheet-title {
    flex: 1;
    margin: 0;
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.privacy-sheet-close {
    display: grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 0.25rem;
    background: none;
    color: var(--jf-muted);
    cursor: pointer;
}

.privacy-sheet-close:hover,
.privacy-sheet-close:focus-visible {
    border-color: var(--jf-border);
    background: var(--jf-chrome-hover);
    color: var(--jf-text);
}

.privacy-sheet-close svg {
    width: 0.75rem;
    height: 0.75rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.privacy-sheet-list {
    margin: 0;
    padding-left: 1.125rem;
    color: var(--jf-muted);
}

.privacy-sheet-list li + li {
    margin-top: 0.4375rem;
}

.privacy-sheet-list strong {
    color: var(--jf-text);
    font-weight: 600;
}

.privacy-sheet-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-top: 0.75rem;
    padding-top: 0.625rem;
    border-top: 1px solid var(--jf-border-soft);
    font-size: 0.75rem;
}

.privacy-sheet-foot a {
    color: var(--jf-accent);
    text-decoration: none;
}

.privacy-sheet-foot a:hover,
.privacy-sheet-foot a:focus-visible {
    text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
    .privacy-sheet,
    .statusbar-chevron {
        transition: none;
    }
}

/* ==== Not found ============================================================= */

.missing {
    flex: 1;
    display: grid;
    place-items: center;
    padding: 2rem;
    text-align: center;
}

.missing h1 {
    margin: 0 0 0.5rem;
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.missing p {
    margin: 0 0 1rem;
    color: var(--jf-muted);
}

.missing a {
    color: var(--jf-accent);
}
