/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

/* Fills the code pane as a flex column (tab bar + editor body) so the editor
   grows with the pane instead of staying a fixed height. This is what lets the
   editor expand when the results pane is collapsed on mobile. */
.editorContainer {
    position: relative;
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* The Monaco editor fills the space below the tab bar. The editor relayouts on
   discrete events (see onMount), so during the results-pane collapse animation
   its canvas briefly lags the growing/shrinking container; overflow:hidden clips
   that lag instead of letting the canvas spill past the pane. */
.editorBody {
    flex: 1 1 0;
    min-height: 0;
    overflow: hidden;
}

/* The Try playground is always light (its Monaco syntax theme is fixed at
   load), so these colors are unconditional — the same in both site light/dark
   modes. The tab bar is a mid-tone slate that bridges the dark navbar above and
   the light editor below, giving a step-down transition rather than a hard
   dark→white edge. */
.toolbar {
    display: flex;
    align-items: center;
    height: var(--tf-toolbar-h);
    box-sizing: border-box;
    flex-shrink: 0;
    background: var(--tf-bar);
    border-bottom: 1px solid rgba(0, 0, 0, 0.2);
    color: var(--tf-text-on-bar);
    font-size: 14px;
}

.tryEditorConfig {
    position: absolute;
    /* Fill the area below the tab bar (top) down to the pane bottom, so the
       config panel grows with the pane just like the editor does. */
    top: var(--tf-toolbar-h);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    background-color: white;
    /* Dark text on the white config panel, pinned so it stays readable in dark
       site mode (the playground doesn't follow the theme toggle). color-scheme
       keeps the native checkboxes, selects, and scrollbar light to match the
       white panel — otherwise the page's dark color-scheme renders them dark. */
    color: #1c1e21;
    color-scheme: light;
    overflow: auto;
}

/* The config "[docs]" links use the light deep-gold rather than the dark-mode
   brand yellow, which is too low-contrast on the white panel. */
.tryEditorConfig a {
    color: var(--tf-accent);
}

/* Boolean options render as toggle switches instead of native checkboxes. */
.tryEditorConfig input[type='checkbox'] {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 34px;
    height: 20px;
    margin: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.18);
    cursor: pointer;
    vertical-align: middle;
    transition: background-color 0.15s;
}
.tryEditorConfig input[type='checkbox']::before {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    transition: transform 0.15s;
}
.tryEditorConfig input[type='checkbox']:checked {
    background: var(--tf-accent);
}
.tryEditorConfig input[type='checkbox']:checked::before {
    transform: translateX(14px);
}
.tryEditorConfig input[type='checkbox']:focus-visible,
.tryEditorConfig select:focus-visible {
    outline: 2px solid var(--tf-accent);
    outline-offset: 2px;
}

/* Custom select for enum options, to match the toggles. */
.tryEditorConfig select {
    appearance: none;
    -webkit-appearance: none;
    padding: 4px 26px 4px 10px;
    border: 1px solid var(--tf-hairline);
    border-radius: var(--tf-radius-sm);
    background-color: #fff;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M3 4.5 6 7.5 9 4.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
    color: var(--tf-text);
    font: inherit;
    cursor: pointer;
}

.tryEditorConfig table {
    display: table;
    width: 100%;
    margin-bottom: 0;
}

.tryEditorConfig tr:first-child {
    border: none;
}

.tryEditorConfig td {
    border: none;
}

/* No row borders or zebra striping; the option rows read as a clean list. */
.tryEditorConfig tr,
.tryEditorConfig tr:nth-child(2n) {
    border: none;
    background-color: transparent;
}

.tryEditorConfig h3 {
    margin-bottom: 0;
    margin-top: 5px;
    margin-bottom: 5px;
    /* Headings normally take Infima's --ifm-heading-color, which is light in a
       dark site theme (e.g. embedded in staticdocs) and would vanish on this
       always-white panel. Pin it to the panel's dark text so it never inherits
       an ambient theme color. */
    color: var(--tf-text);
}

.tryEditorConfigInputCell {
    text-align: right;
}

.tryEditorConfigLabelCell {
    width: 100%;
}

.tryEditorConfigLabelCell a {
    padding-left: 5px;
}

.tryEditorConfigLabel {
    font-weight: bold;
}

/* Phone widths: drop the shared two-column table grid so the controls don't all
   line up in one column (a wide enum select otherwise forces the column wide and
   squeezes the labels). Each row becomes its own flex row: the control on the
   left sized to its own content, its label text right beside it. Rows are
   independent, so a wide control only widens its own row. */
@media (max-width: 600px) {
    .tryEditorConfig {
        font-size: 14.5px;
    }
    .tryEditorConfig table,
    .tryEditorConfig tbody {
        display: block;
        width: auto;
    }
    .tryEditorConfig tr {
        display: flex;
        align-items: flex-start;
        gap: 8px;
    }
    .tryEditorConfig td {
        display: block;
    }
    .tryEditorConfigInputCell {
        flex: none;
        text-align: left;
    }
    .tryEditorConfigLabelCell {
        flex: 1;
        width: auto;
    }
}

.resetBanner {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* The recover prompt and the Clear button are mutually exclusive and share this
   slot. Both are stacked in one grid cell and right-aligned, so the slot keeps
   the width of the wider (recover) group and the Copy buttons to its right don't
   shift when the two swap; the swap itself cross-fades (see .swapActive below). */
.swapSlot {
    display: grid;
    justify-items: end;
}
.swapSlot > * {
    grid-area: 1 / 1;
}
.swapActive {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.18s ease;
}
.swapInactive {
    opacity: 0;
    /* Stay rendered through the fade-out, then drop from the tab order and the
       accessibility tree (visibility flips after the opacity transition). */
    visibility: hidden;
    transition: opacity 0.18s ease, visibility 0s 0.18s;
}

.tryEditor {
    display: flex;
    flex: 1;
    /* Pull up 1px to sit under the navbar's translucent bottom border
       (rgba(255,255,255,0.08)). In light site mode that border composites over
       the white page body and shows as a 1px white line between the dark navbar
       and the slate tab bar; in dark mode it composites over the dark body and
       blends in. Covering it with the slate bar removes the seam so light and
       dark look the same. The +1px on the height keeps the pane bottom flush. */
    margin-top: -1px;
    height: calc(100vh - var(--ifm-navbar-height) + 1px);

    /* Playground design tokens. The Try playground is always light, so these are
       unconditional; a future dark-aware pass can override them under a selector. */
    --tf-bar: #454e5a;
    --tf-bar-track: rgba(0, 0, 0, 0.22);
    --tf-text-on-bar: rgba(255, 255, 255, 0.72);
    --tf-surface: #ffffff;
    --tf-surface-muted: #f7f8fa;
    --tf-hairline: rgba(0, 0, 0, 0.1);
    --tf-text: #1c1e21;
    --tf-text-muted: rgba(0, 0, 0, 0.55);
    --tf-accent: #8a6300;
    --tf-danger: #e5534b;
    --tf-radius: 10px;
    --tf-radius-sm: 7px;
    --tf-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 6px 20px rgba(0, 0, 0, 0.06);
    /* Single source of truth for the tab-bar height: the toolbar is pinned to
       it, the editor/results bodies subtract it, and the body divider starts at
       it, so the divider can't poke into the bar and the bodies can't overflow. */
    --tf-toolbar-h: 52px;
    /* Height of one pane's body (the pane minus its tab bar). The narrow-screen
       media query halves it when the two panes stack vertically. */
    --tf-editor-h: calc(
        100vh - var(--ifm-navbar-height) - var(--tf-toolbar-h)
    );
    /* Editor/results split ratio, set inline from the draggable splitter. */
    --tf-split: 0.5;
}

.code,
.results {
    flex-basis: 0;
    flex-shrink: 1;
    min-width: 0;
    min-height: 0;
}
/* Side-by-side layout only: keep the editor pane >=400px and the results pane
   >=450px when dragging the splitter. The stacking breakpoint below (850px) sits
   above 400+450+splitter, so both minimums always fit before the panes stack. The
   mouse/keyboard handlers clamp the ratio to the same minimums; this is the
   resize-safety floor. */
@media (min-width: 851px) {
    .code {
        min-width: 400px;
    }
    .results {
        min-width: 450px;
    }
}
.code {
    flex-grow: var(--tf-split);
}
.results {
    flex-grow: calc(1 - var(--tf-split));
}

.code {
    background: #fff;
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* Query container so the toolbar buttons can respond to the editor pane's own
       width — which changes with the splitter and the stacked layout — rather
       than the viewport. As the pane narrows, buttons slim down (full label ->
       short label -> icon only) so they keep fitting instead of being clipped. */
    container: tf-editor / inline-size;
}

.results {
    position: relative;
    font-size: 12px;
    background: #f7f7f7;
    /* Dark text on the light results surface, pinned so errors/JSON/AST stay
       readable in dark site mode. The results-side tab bar reasserts its own
       light text via .toolbar above. */
    color: #1c1e21;
    /* Query container so the "Copy errors" button slims to its icon based on the
       results pane's own width (like the editor pane does for its buttons). */
    container: tf-results / inline-size;
}

/* Draggable splitter between the panes. The hit area spans the full height
   (so it can be grabbed anywhere), but the visible divider line is drawn only
   below the tab bars, so the two slate bars still read as one continuous bar. */
.splitter {
    position: relative;
    flex: 0 0 1px;
    cursor: col-resize;
    z-index: 2;
    /* Slate fill so the splitter's top section blends into the two tab bars
       (continuous bar, no gap); the divider line below is drawn by ::before. */
    background: var(--tf-bar);
}
/* The 1px divider line, drawn only below the tab bars. */
.splitter::before {
    content: '';
    position: absolute;
    top: var(--tf-toolbar-h);
    bottom: 0;
    left: 0;
    width: 1px;
    background: #ddd;
    transition: background-color 0.15s;
}
/* A wider transparent grab zone so the 1px line is still easy to drag. */
.splitter::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: -4px;
    width: 9px;
}
.splitter:hover::before,
.splitter:active::before,
.splitter:focus-visible::before {
    background: var(--tf-accent);
}
/* The 1px line is too thin to show a normal ring, so the divider itself thickens
   and brightens on keyboard focus. */
.splitter:focus-visible {
    outline: none;
}
.splitter:focus-visible::before {
    width: 3px;
}

/* Narrow screens: stack the panes vertically, halve each body's height so both
   fit below the navbar, and hide the (horizontal-only) splitter. Breakpoint is
   above the 400px + 450px per-pane minimums (plus the splitter), so we stack
   before the side-by-side panes would be forced below their minimum width. */
@media (max-width: 850px) {
    .tryEditor {
        flex-direction: column;
        --tf-editor-h: calc(
            (100vh - var(--ifm-navbar-height)) / 2 - var(--tf-toolbar-h)
        );
    }
    .code,
    .results {
        flex: 1 1 0;
        min-height: 0;
        width: auto;
    }
    .splitter {
        display: none;
    }
    .results {
        border-top: 1px solid #ddd;
        /* Animate the grow/basis change when the pane collapses to its tab bar
           so the editor expands into the freed space smoothly. */
        transition: flex-grow 0.25s ease, flex-basis 0.25s ease;
    }
    /* Collapsed: shrink the results pane to just its tab bar (the editor's
       flex:1 grows to fill the rest), and hide every body variant so nothing
       overflows below the bar. The tab bar gets a pointer cursor as the affordance
       to re-expand. */
    .results.collapsed {
        flex-grow: 0;
        flex-basis: var(--tf-toolbar-h);
    }
    .results.collapsed .toolbar {
        cursor: pointer;
    }
    .results.collapsed .resultBody,
    .results.collapsed .astBody,
    .results.collapsed .noErrors,
    .results.collapsed .emptyState,
    .results.collapsed .loader {
        display: none;
    }
}

/* Collapse toggle (results pane, stacked layout only — hidden by default, shown
   in the media query above). The chevron points down when the pane is open and
   rotates to point up when collapsed. */
.collapseChevron {
    display: none;
    align-items: center;
    justify-content: center;
    appearance: none;
    -webkit-appearance: none;
    border: none;
    margin: 0;
    padding: 4px;
    border-radius: var(--tf-radius-sm);
    background: transparent;
    color: var(--tf-text-on-bar);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}
.collapseChevron:hover {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}
.collapseChevron:focus-visible {
    outline: 2px solid var(--flow-yellow);
    outline-offset: 2px;
}
.collapseChevron svg {
    width: 16px;
    height: 16px;
    /* Matches the results-pane collapse duration so the chevron and the pane it
       controls finish together. */
    transition: transform 0.25s ease;
}
.collapsed .collapseChevron svg {
    transform: rotate(180deg);
}
/* The collapse toggle is only meaningful when the panes are stacked. Placed after
   the base rule above so this show wins over its display:none (media queries add
   no specificity — source order decides). */
@media (max-width: 850px) {
    .collapseChevron {
        display: inline-flex;
    }
}

.resultBody {
    display: flex;
    flex: 1;
    height: var(--tf-editor-h);
    overflow: auto;
    padding: 14px 16px;
    margin-bottom: 0;
    /* Errors/JSON render in a <pre>, which Infima paints with a theme-dependent
       background; pin it to the light results surface so it doesn't flip with
       the site toggle. Keep the scrollbar light to match. */
    background: #f7f7f7;
    color-scheme: light;
}

/* Tabs render as a segmented control: a rounded inset track on the slate bar
   with each tab a pill segment, and the active segment a raised light pill. */
.tabs {
    display: flex;
    list-style: none;
    margin: 6px 8px;
    padding: 3px;
    gap: 2px;
    background: var(--tf-bar-track);
    border-radius: var(--tf-radius-sm);
}

.tab {
    display: flex;
    align-items: center;
    gap: 6px;
    /* Reset native <button> chrome; the segment is styled entirely below. */
    appearance: none;
    -webkit-appearance: none;
    border: none;
    background: transparent;
    margin: 0;
    font-family: inherit;
    font-size: inherit;
    border-radius: calc(var(--tf-radius-sm) - 3px);
    color: var(--tf-text-on-bar);
    cursor: pointer;
    font-weight: 600;
    padding: 5px 13px;
    transition: background-color 0.15s, color 0.15s;
}

.tab:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.08);
}

.resultsActions {
    margin-left: auto;
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    align-items: center;
    margin-right: 10px;
}

.versionWarning {
    color: red;
}

.selectedTab,
.selectedTab:hover {
    /* The active segment is a raised light pill. */
    background: var(--tf-surface);
    color: var(--tf-text);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

/* Error-count badge on the Errors tab. */
.tabBadge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--tf-danger);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
}

/* Fixed-width slot for the Errors indicator so the tab width doesn't shift
   between the check and a single-digit count badge (both occupy 18px). */
.errorIndicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
}

/* Tab icons inherit the tab's text color (light on the bar, dark when active). */
.tab svg {
    width: 14px;
    height: 14px;
    flex: none;
}

/* Buttons on the slate bar (copy code, copy link, recover, ignore): matching
   pills. */
.toolbarButton {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: var(--tf-radius-sm);
    background: rgba(255, 255, 255, 0.08);
    color: var(--tf-text-on-bar);
    font: inherit;
    font-size: 13px;
    padding: 4px 10px;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
    /* Never wrap to a second line (which would make the button grow vertically
       and overflow the fixed-height bar). When the bar is tight the button
       shrinks and its label truncates with an ellipsis instead. min-width:0 lets
       it shrink below its content width inside the flex toolbar. */
    min-width: 0;
    white-space: nowrap;
    /* Consistent height whether the button shows a label or only its icon —
       otherwise an icon-only button shrinks to the icon's height and reads as a
       smaller pill than its labeled siblings. */
    box-sizing: border-box;
    min-height: 31px;
}

/* The button's text label. Holds the ellipsis so the icon stays full-size while
   only the text truncates. */
.toolbarButtonLabel {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.toolbarButton:hover {
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
}

/* When the results pane is narrow, "Copy errors" collapses to just its icon
   (keyed to the results pane's own width). Each error card still has its own copy
   button, so the action isn't lost. */
@container tf-results (max-width: 500px) {
    .iconOnly .toolbarButtonLabel {
        display: none;
    }
}

/* First slim step as the editor pane narrows: the recover prompt drops to
   "Recover last saved" -> "Recover" and hides Ignore (typing dismisses the prompt
   anyway). Keyed to the pane's own width via the container query above. */
@container tf-editor (max-width: 720px) {
    .labelExtra {
        display: none;
    }
    .phoneHidden {
        display: none;
    }
}

/* Final slim step: when the pane is narrow, Clear / Recover / Copy code collapse
   to just their icon. Copy link keeps its label as the primary sharing action.
   Same 500px pane threshold as "Copy errors" on the results pane. */
@container tf-editor (max-width: 500px) {
    .iconOnly .toolbarButtonLabel {
        display: none;
    }
}

/* Very narrow panes — only reachable when stacked, since side-by-side panes keep
   their minimum widths (editor 400px, results 450px). On phones the toolbars run
   out of room even for icons, so drop whole buttons by priority. Thresholds are
   calibrated so everything fits down to a 375px floor (narrower isn't supported).
   The order is driven by what physically fits: the results tabs are wider than
   the editor's, so the version selector goes before the editor's Clear/Copy code. */

/* Results pane below 450px (all phones): hide "Copy errors" — each error card
   still has its own copy button. 449.98 (not 450) so it never triggers at the
   side-by-side results min of exactly 450px. */
@container tf-results (max-width: 449.98px) {
    .copyErrors {
        display: none;
    }
}

/* Results pane <=410px: also drop the version selector (a real version is
   preselected; the tabs and collapse chevron stay). */
@container tf-results (max-width: 410px) {
    .resultsActions select,
    .versionWarning {
        display: none;
    }
}

/* Editor pane <=380px: drop the Clear/recover slot and Copy code; keep the
   Code/Config tabs and Copy link (the primary sharing action). */
@container tf-editor (max-width: 380px) {
    .swapSlot,
    .copyCode {
        display: none;
    }
}
.toolbarButton svg {
    width: 14px;
    height: 14px;
    flex: none;
}

/* Right-aligned cluster of toolbar actions (recover/ignore + copy buttons). */
.toolbarActions {
    margin-left: auto;
    margin-right: 8px;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Version dropdown: a pill on the slate bar, the same shape as the config's
   custom select but themed for the dark bar (translucent fill, light chevron). */
.resultsActions select {
    appearance: none;
    -webkit-appearance: none;
    padding: 4px 26px 4px 10px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: var(--tf-radius-sm);
    background-color: rgba(255, 255, 255, 0.08);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23c8ccd0' d='M3 4.5 6 7.5 9 4.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
    color: var(--tf-text-on-bar);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
    /* Cap the width so a long development-only label ("main (rust port)") can't
       push the toolbar wide. A real release like "v0.317.0" fits comfortably and
       is what's selected in ~all cases; the rare long dev label just truncates,
       which is fine. */
    max-width: 92px;
    text-overflow: ellipsis;
}
.resultsActions select:hover {
    background-color: rgba(255, 255, 255, 0.14);
    color: #fff;
}

/* Focus-visible rings. Elements on the slate bar use the brand yellow (visible
   on the dark bar); the white config controls use the deep gold. */
.tab:focus-visible,
.toolbarButton:focus-visible,
.resultsActions select:focus-visible {
    outline: 2px solid var(--flow-yellow);
    outline-offset: 2px;
}

.loader {
    display: flex;
    justify-content: center;
    padding: 16px;
}

.spinner {
    width: 22px;
    height: 22px;
    border: 2px solid var(--tf-hairline);
    border-top-color: var(--tf-accent);
    border-radius: 50%;
    animation: tf-spin 0.7s linear infinite;
}

@keyframes tf-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Shown while the playground's code chunk (Monaco + Flow services) is still
   loading, in place of a bare "Loading..." text flash. Fills the playground
   area and centers a spinner; the colors are self-contained (the --tf-* tokens
   live on .tryEditor, which isn't mounted yet). */
.suspenseLoader {
    display: flex;
    align-items: center;
    justify-content: center;
    height: calc(100vh - var(--ifm-navbar-height));
    background: #fff;
}
.suspenseSpinner {
    width: 28px;
    height: 28px;
    border: 3px solid rgba(0, 0, 0, 0.1);
    border-top-color: #8a6300;
    border-radius: 50%;
    animation: tf-spin 0.7s linear infinite;
}

/* Reduced motion: collapse the results pane near-instantly — a tiny non-zero
   duration (rather than `none`) keeps the transitionend that drives the editor
   relayout — and swap the spinners' rotation for a gentle opacity pulse, which
   doesn't trigger vestibular motion the way spinning does. */
@media (prefers-reduced-motion: reduce) {
    .results {
        transition-duration: 0.01ms;
    }
    /* The chevron rotation is part of the same collapse gesture, so flatten it
       too (it's the transform/rotation kind of motion this block targets). */
    .collapseChevron svg {
        transition-duration: 0.01ms;
    }
    .spinner,
    .suspenseSpinner {
        animation: tf-pulse 1.2s ease-in-out infinite;
    }
}

@keyframes tf-pulse {
    50% {
        opacity: 0.4;
    }
}

/* The AST tree (react-json-view) gets its own block scroll container rather
   than the flex .resultBody: a flex item recomputes its width as nodes expand
   and collapse, which drives a ResizeObserver feedback loop. A fixed-height
   block with overflow scroll keeps the container size stable. */
.astBody {
    height: var(--tf-editor-h);
    overflow: auto;
    padding: 14px 16px;
    color-scheme: light;
}

.errors ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Each top-level error is a raised card with a danger-colored left accent.
   Clicking the card jumps to the error's location in the editor. */
.errors > ul > li {
    position: relative;
    cursor: pointer;
    background: var(--tf-surface);
    border: 1px solid var(--tf-hairline);
    border-left: 3px solid var(--tf-danger);
    border-radius: var(--tf-radius-sm);
    padding: 10px 12px;
    box-shadow: var(--tf-shadow);
    transition: box-shadow 0.15s, border-color 0.15s, transform 0.15s;
}
/* Hover emphasizes the card: it lifts slightly with a stronger shadow. Keep the
   left border red (the border-color shorthand would otherwise grey it out). */
.errors > ul > li:hover {
    border-color: rgba(0, 0, 0, 0.12);
    border-left-color: var(--tf-danger);
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.12);
}

/* Transparent full-card overlay button: makes the whole card a single
   keyboard-focusable jump target. It sits beneath the copy button (which raises
   itself with z-index) so that button stays independently clickable. */
.errorCardJump {
    position: absolute;
    inset: 0;
    z-index: 0;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    cursor: pointer;
}
.errorCardJump:focus-visible {
    outline: 2px solid var(--tf-danger);
    outline-offset: -2px;
    border-radius: var(--tf-radius-sm);
}

/* Card header: an error-code chip on the left, a copy button (on hover) right. */
.errorCardHeader {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}
.errorCode {
    font-family: var(--ifm-font-family-monospace);
    font-size: 11px;
    color: var(--tf-text-muted);
    background: rgba(0, 0, 0, 0.05);
    border: 1px solid var(--tf-hairline);
    border-radius: 4px;
    padding: 1px 6px;
}
.errorCopy {
    /* Raise above the .errorCardJump overlay so the copy button stays clickable
       and keyboard-focusable as a sibling, not nested inside the jump target. */
    position: relative;
    z-index: 1;
    margin-left: auto;
    opacity: 0;
    border: 1px solid var(--tf-hairline);
    border-radius: 4px;
    background: #fff;
    color: var(--tf-text-muted);
    font: inherit;
    font-size: 11px;
    padding: 1px 8px;
    cursor: pointer;
    transition: opacity 0.15s, color 0.15s;
}
.errors > ul > li:hover .errorCopy,
.errorCopy:focus-visible,
.errorCopied {
    opacity: 1;
}
.errorCopy:hover {
    color: var(--tf-text);
}

.errors > ul > li + li {
    margin-top: 10px;
}

.errors li ul li,
.errors li ul li + li {
    padding: inherit;
    padding-left: 20px;
    margin: inherit;
    border: none;
    background: none;
    box-shadow: none;
}

/* Wrap long error text instead of forcing a horizontal scroll, at every pane
   width. The cards render in a <pre>, which defaults to white-space: pre (no
   wrap), so long source-context lines and long error messages would otherwise
   overflow the pane and scroll sideways. pre-wrap plus overflow-wrap (both
   inherited down to the message divs/spans) lets them break to fit. The caret
   arrow under a wrapped context line no longer aligns, the same trade-off the
   site's code blocks make by hiding their squiggles when they wrap. */
.errors {
    white-space: pre-wrap;
    overflow-wrap: break-word;
}

/* Empty state shown when there are no errors. Sits near the top, where the eye
   scans for the first error, rather than vertically centered in the pane. */
.noErrors {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 16px;
    color: var(--tf-text-muted);
    font-size: 14px;
}

.noErrorsCheck {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: rgba(46, 160, 67, 0.15);
    color: #2ea043;
    font-weight: 700;
}
.noErrorsCheck svg {
    width: 14px;
    height: 14px;
}

/* Generic empty state for the JSON and AST tabs (plain muted text, no icon). */
.emptyState {
    padding: 16px;
    color: var(--tf-text-muted);
    font-size: 14px;
}

.msgHighlight,
.msgType {
    cursor: pointer;
}

.refinedValueDecoration {
    background-color: rgba(156, 204, 44, 0.25) !important;
}
