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, the panes and the controls all 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%;
}

/* QRFlex 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.

   The accent is deliberately not a colour any code is likely to be drawn in: the codes on
   screen are the user's, in the user's colours, and the app's chrome should never be
   mistaken for part of one. */
:root {
    color-scheme: dark;
    --qf-bg: #0d1015;
    --qf-pane: #15191f;
    --qf-pane-alt: #11151a;
    --qf-input: #0f1318;
    --qf-chrome: #0a0d11;
    --qf-chrome-2: #12161c;
    --qf-chrome-hover: #1e242d;
    --qf-border: #262f3a;
    --qf-border-soft: #1a212a;
    --qf-text: #dde5ee;
    --qf-muted: #8391a2;
    --qf-faint: #56626f;
    --qf-accent: #2dd4bf;
    --qf-accent-bright: #5eead4;
    --qf-accent-ink: #04201d;
    --qf-accent-dim: rgba(45, 212, 191, 0.45);
    --qf-accent-wash: rgba(45, 212, 191, 0.14);

    --qf-danger: #f2777a;
    --qf-warn: #e0a458;
    --qf-ok: #4ade80;

    /* One hue per pane: what it says, how it looks, what that comes out as. */
    --qf-side-content: #4cb7ec;
    --qf-side-style: #e8a33c;
    --qf-side-code: #2dd4bf;

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

:root[data-theme="light"] {
    color-scheme: light;
    --qf-bg: #e7ebf0;
    --qf-pane: #ffffff;
    --qf-pane-alt: #f7f9fb;
    --qf-input: #ffffff;
    --qf-chrome: #f9fbfc;
    --qf-chrome-2: #f1f4f7;
    --qf-chrome-hover: #e4e9ef;
    --qf-border: #ccd5df;
    --qf-border-soft: #dde4eb;
    --qf-text: #18202a;
    --qf-muted: #5a6673;
    --qf-faint: #98a3af;
    --qf-accent: #0d7d72;
    --qf-accent-bright: #12968a;
    --qf-accent-ink: #ffffff;
    --qf-accent-dim: rgba(13, 125, 114, 0.45);
    --qf-accent-wash: rgba(13, 125, 114, 0.10);

    --qf-danger: #c02f33;
    --qf-warn: #96600f;
    --qf-ok: #197a3f;

    --qf-side-content: #1a86c4;
    --qf-side-style: #b3711a;
    --qf-side-code: #0d7d72;

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

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

        --qf-danger: #c02f33;
        --qf-warn: #96600f;
        --qf-ok: #197a3f;

        --qf-side-content: #1a86c4;
        --qf-side-style: #b3711a;
        --qf-side-code: #0d7d72;

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

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

/* 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 design is
   back in the panes. 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 QRFlex.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. */
.qf-boot {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    background: var(--qf-bg);

    /* The shell's typeface, repeated: the splash sits outside `.qf-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 workbench 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. */
.qf-boot-sheet {
    position: absolute;
    inset: 0;
}

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

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

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

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

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

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

.qf-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. */
.qf-boot.is-leaving .qf-boot-body {
    opacity: 0;
    transform: scale(0.97);
    transition: opacity 160ms ease, transform 220ms ease;
}

.qf-boot-mark {
    width: 4.75rem;
    height: 4.75rem;
    overflow: visible;
}

/* The three finder patterns, which are the part of a QR code that is the same in all of
   them - and the part this app lets you restyle. They come up in turn, the way a scanner
   locks onto them one after another. */
.qf-boot-eye {
    fill: none;
    stroke: var(--qf-muted);
    stroke-width: 1.5;
    animation: qf-boot-lock 2.1s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.qf-boot-eye:nth-of-type(2) {
    animation-delay: 0.16s;
}

.qf-boot-eye:nth-of-type(3) {
    animation-delay: 0.32s;
}

.qf-boot-pupil {
    fill: var(--qf-accent);
    opacity: 0;
    animation: qf-boot-pupil 2.1s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.qf-boot-pupil:nth-of-type(5) {
    animation-delay: 0.16s;
}

.qf-boot-pupil:nth-of-type(6) {
    animation-delay: 0.32s;
}

/* The data quarter, which is the part that is different in every code - so it flickers
   rather than settling, and it is the only thing on the mark that does. */
.qf-boot-data {
    fill: var(--qf-accent);
    opacity: 0;
    animation: qf-boot-data 2.1s steps(1, end) infinite;
}

.qf-boot-data:nth-of-type(8) {
    animation-delay: 0.35s;
}

@keyframes qf-boot-lock {
    0%, 100% { stroke: var(--qf-muted); }
    35%, 70% { stroke: var(--qf-accent); }
}

@keyframes qf-boot-pupil {
    0%, 100% { opacity: 0.25; }
    35%, 70% { opacity: 1; }
}

@keyframes qf-boot-data {
    0%, 24% { opacity: 0; }
    25%, 49% { opacity: 0.85; }
    50%, 74% { opacity: 0.2; }
    75%, 100% { opacity: 0.6; }
}

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

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

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

.qf-boot-fill {
    width: 0;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--qf-accent-dim), var(--qf-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. */
.qf-boot-rail.is-waiting::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 35%;
    border-radius: inherit;
    background: linear-gradient(90deg, transparent, var(--qf-accent-dim), transparent);
    animation: qf-boot-sweep 1.4s ease-in-out infinite;
}

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

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

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

.qf-boot.is-failed .qf-boot-status {
    color: var(--qf-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. */
.qf-boot.is-failed .qf-boot-fill {
    background: var(--qf-danger);
    opacity: 0.5;
}

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

@media (prefers-reduced-motion: reduce) {
    .qf-boot-eye,
    .qf-boot-pupil,
    .qf-boot-data,
    .qf-boot-rail.is-waiting::after {
        animation: none;
    }

    /* Still the glyph rather than a half-drawn one: park everything where the animation
       would have it at rest, which is the mark itself. */
    .qf-boot-pupil { opacity: 1; }
    .qf-boot-data { opacity: 0.6; }

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

    .qf-boot.is-leaving .qf-boot-sheet span,
    .qf-boot.is-leaving .qf-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."
}
