html, body {
    height: 100%;
}

/* The whole app is drawn a fifth larger than a browser's default, the same scale as
   TextFlex, JwtFlex and CronFlex - the *Flex apps are drawn at one size.

   Every size in this app is in `rem`, so this one number is the app's scale and nothing
   else has to know about it - change it and the type and the editor move together. A
   percentage rather than a pixel count so that a reader who has already set a larger
   default font in their browser keeps the increase.

   The media queries are the exception and cannot use it: `rem` inside a media query is
   always the browser's initial 16px, whatever this says. So every breakpoint in this app
   is written at 1.2x the width it would otherwise be - the content is 1.2x, so it runs
   out of room 1.2x sooner. */
:root {
    font-size: 120%;
}

/* JsonFlex theme.
   Dark is the default (this is a workbench, and it is what most users of it
   want); light applies when the user picks it, or when the system asks for it
   and the user has not overridden that. Every token is defined here on bare
   `:root` so no colour exists only inside a media query. */
:root {
    color-scheme: dark;
    --jf-bg: #0d1015;
    --jf-pane: #15191f;
    --jf-pane-alt: #11151a;
    --jf-chrome: #0a0d11;
    --jf-chrome-2: #12161c;
    --jf-chrome-hover: #1e242d;
    --jf-border: #262f3a;
    --jf-border-soft: #1a212a;
    --jf-text: #dde5ee;
    --jf-muted: #8391a2;
    --jf-faint: #56626f;
    --jf-accent: #22d3ee;
    --jf-accent-bright: #67e8f9;
    --jf-accent-ink: #062024;
    --jf-accent-dim: rgba(34, 211, 238, 0.45);
    --jf-accent-wash: rgba(34, 211, 238, 0.14);

    --jf-danger: #f2777a;

    /* Used sparingly in the tree view, to tell a string from a number from the rest.
       Keys keep a blue family of their own, shifted towards indigo so they read apart
       from the accent's cyan rather than blending into it. */
    --jf-side-left: #7c8cf8;
    --jf-side-right: #e8a33c;
    --jf-side-result: #22d3ee;

    --jf-shadow: 0 1px 0 rgba(255, 255, 255, 0.03);
}

:root[data-theme="light"] {
    color-scheme: light;
    --jf-bg: #e7ebf0;
    --jf-pane: #ffffff;
    --jf-pane-alt: #f7f9fb;
    --jf-chrome: #f9fbfc;
    --jf-chrome-2: #f1f4f7;
    --jf-chrome-hover: #e4e9ef;
    --jf-border: #ccd5df;
    --jf-border-soft: #dde4eb;
    --jf-text: #18202a;
    --jf-muted: #5a6673;
    --jf-faint: #98a3af;
    --jf-accent: #0e7490;
    --jf-accent-bright: #0891b2;
    --jf-accent-ink: #ffffff;
    --jf-accent-dim: rgba(14, 116, 144, 0.45);
    --jf-accent-wash: rgba(14, 116, 144, 0.10);

    --jf-danger: #c02f33;

    --jf-side-left: #3b4fd1;
    --jf-side-right: #b3711a;
    --jf-side-result: #0e7490;

    --jf-shadow: 0 1px 2px rgba(24, 32, 42, 0.06);
}

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        color-scheme: light;
        --jf-bg: #e7ebf0;
        --jf-pane: #ffffff;
        --jf-pane-alt: #f7f9fb;
        --jf-chrome: #f9fbfc;
        --jf-chrome-2: #f1f4f7;
        --jf-chrome-hover: #e4e9ef;
        --jf-border: #ccd5df;
        --jf-border-soft: #dde4eb;
        --jf-text: #18202a;
        --jf-muted: #5a6673;
        --jf-faint: #98a3af;
        --jf-accent: #0e7490;
        --jf-accent-bright: #0891b2;
        --jf-accent-ink: #ffffff;
        --jf-accent-dim: rgba(14, 116, 144, 0.45);
        --jf-accent-wash: rgba(14, 116, 144, 0.10);

        --jf-danger: #c02f33;

        --jf-side-left: #3b4fd1;
        --jf-side-right: #b3711a;
        --jf-side-result: #0e7490;

        --jf-shadow: 0 1px 2px rgba(24, 32, 42, 0.06);
    }
}

/* The editor owns the viewport; nothing outside it should scroll. */
body:has(.jf-shell) {
    overflow: hidden;
    margin: 0;
    background: var(--jf-bg);
}

/* A standing document - the privacy notice - scrolls, so the page background has to be
   the theme's rather than the browser's white: otherwise overscroll, and any gap under a
   short document, flashes white behind a dark page. Set on body rather than on the .doc
   element itself, because it is body's background that propagates to the canvas the
   overscroll paints. */
body:has(.doc) {
    background: var(--jf-bg);
}

/* The privacy notice is server-rendered with no WebAssembly on it, so there is nothing
   for the boot splash to wait for - `jfBoot.ready()` is never called on this page, and
   without this rule the splash would sit over it until its own stall backstop fired. */
body:has(.doc) .jf-boot {
    display: none;
}

/* Blazor's unhandled-error bar. Hidden until the framework sets `display: block`
   on it by hand, which is why the `display: none` here is load-bearing rather
   than tidiness. Global, not scoped: every layout renders its own copy of this
   div, so a rule in any one .razor.css file would reach exactly one of them. */
#blazor-error-ui {
    position: fixed;
    bottom: 0;
    left: 0;
    z-index: 1000;
    display: none;
    box-sizing: border-box;
    width: 100%;
    padding: 0.6rem 1.25rem 0.7rem;
    background: lightyellow;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    /* The bar has a fixed light background, so its text and controls must not be
       re-coloured by a dark theme. */
    color-scheme: light only;
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    cursor: pointer;
}

/* The boot splash: what covers the page until WebAssembly is up and the last session
   is back in the editor. It lives here, in the stylesheet the document already blocks
   on, rather than in a scoped component file - the markup is in App.razor and is on
   screen long before JsonFlex.styles.css means anything.

   Only theme tokens are used, so a first visit gets the right palette from the inline
   theme script in App.razor and never flashes the other one. */
.jf-boot {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    background: var(--jf-bg);

    /* The shell's typeface, repeated: the splash sits outside `.jf-shell` and so
       inherits nothing from it, and the browser default is a serif. */
    font-family: 'Inter', 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, sans-serif;
}

/* The reveal, in the manner of a stage curtain cut on the diagonal: the top-left and
   bottom-right halves slide apart along the perpendicular of the split. Each triangle
   overshoots the diagonal by a couple of pixels so no seam of the editor shows through
   the join while they are still closed.

   Taken out of flow, because an in-flow child would be a second grid item and
   `place-items` centres each item in its own row - which puts the wordmark half a
   screen below the middle. */
.jf-boot-sheet {
    position: absolute;
    inset: 0;
}

.jf-boot-sheet span {
    position: absolute;
    inset: 0;
    background: var(--jf-bg);
}

.jf-boot-sheet span:first-child {
    clip-path: polygon(0 0, calc(100% + 2px) 0, 0 calc(100% + 2px));
}

.jf-boot-sheet span:last-child {
    clip-path: polygon(100% -2px, 100% 100%, -2px 100%);
}

.jf-boot.is-leaving .jf-boot-sheet span {
    transition: transform 560ms cubic-bezier(0.66, 0, 0.34, 1);
}

.jf-boot.is-leaving .jf-boot-sheet span:first-child {
    transform: translate(-100%, -100%);
}

.jf-boot.is-leaving .jf-boot-sheet span:last-child {
    transform: translate(100%, 100%);
}

.jf-boot-body {
    position: relative;
    display: grid;
    justify-items: center;
    gap: 0;
    padding-bottom: 3vh;
    text-align: center;
}

/* The wordmark goes first and fast, so the curtain opens on an empty stage rather
   than dragging the notice off with it. */
.jf-boot.is-leaving .jf-boot-body {
    opacity: 0;
    transform: scale(0.97);
    transition: opacity 160ms ease, transform 220ms ease;
}

.jf-boot-mark {
    width: 4.75rem;
    height: 4.75rem;
    overflow: visible;
    fill: none;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.jf-boot-mark-a {
    stroke: var(--jf-muted);
}

.jf-boot-mark-b {
    stroke: var(--jf-accent);
}

/* A slow breath rather than the Venn-diagram "meeting" SetFlex used - there are no
   two things to bring together here, just braces holding a document. */
.jf-boot-mark {
    animation: jf-boot-breathe 2.4s ease-in-out infinite;
}

@keyframes jf-boot-breathe {
    0%, 100% { opacity: 0.75; }
    50% { opacity: 1; }
}

.jf-boot-name {
    margin: 0.5rem 0 1.5rem;
    font-size: 1.375rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--jf-text);
}

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

.jf-boot-rail {
    position: relative;
    width: 13.75rem;
    max-width: 60vw;
    height: 3px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--jf-chrome-2);
    box-shadow: inset 0 0 0 1px var(--jf-border-soft);
}

.jf-boot-fill {
    width: 0;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--jf-accent-dim), var(--jf-accent-bright));
    transition: width 240ms ease;
}

/* Before the first file is even asked for there is no fraction to draw, and a rail
   sitting at zero is the one thing this screen exists to avoid saying. */
.jf-boot-rail.is-waiting::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 35%;
    border-radius: inherit;
    background: linear-gradient(90deg, transparent, var(--jf-accent-dim), transparent);
    animation: jf-boot-sweep 1.4s ease-in-out infinite;
}

@keyframes jf-boot-sweep {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(386%); }
}

.jf-boot-status {
    margin: 0.875rem 0 0;
    font-size: 0.8125rem;
    color: var(--jf-muted);
}

/* Fixed height, so the counts appearing under the rail do not shift the wordmark up
   the screen a beat after it arrived. */
.jf-boot-detail {
    margin: 0.1875rem 0 0;
    min-height: 1.1rem;
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    color: var(--jf-faint);
}

.jf-boot.is-failed .jf-boot-status {
    color: var(--jf-danger);
}

/* The files that were counted may all have arrived - the runtime's own imports go
   around the count - so the rail can be sitting at a confident 100% under a message
   saying the opposite. It should not look like progress. */
.jf-boot.is-failed .jf-boot-fill {
    background: var(--jf-danger);
    opacity: 0.5;
}

.jf-boot.is-failed .jf-boot-mark {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .jf-boot-mark,
    .jf-boot-rail.is-waiting::after {
        animation: none;
    }

    /* No curtain: the whole cover fades, which is the same information without the
       movement across the viewport. */
    .jf-boot.is-leaving {
        opacity: 0;
        transition: opacity 200ms ease;
    }

    .jf-boot.is-leaving .jf-boot-sheet span,
    .jf-boot.is-leaving .jf-boot-body {
        transition: none;
        transform: none;
        opacity: 1;
    }
}

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}
