/* Mona Sans, vendored rather than linked. It is OFL, which means the licence has to travel with
   the file - see wwwroot/fonts/README.md, which is also where the version came from. If the file
   ever goes missing this fails silently and everything falls through to the stack in --display. */
@font-face {
    font-family: "Mona Sans";
    src: url("fonts/MonaSans.woff2") format("woff2");

    /* Read off the fvar table of the file itself, not assumed: wght runs 200 to 900 and opsz 0 to
       100. This is the variable cut without the width axis, which halves the download and costs
       nothing, because nothing here varies width - the labels are tracked out, not condensed. */
    font-weight: 200 900;
    font-style: normal;
    font-display: swap;
}

/* The shell is achromatic on purpose. The field is the only thing on screen entitled to a colour,
   and the one exception - the inspector - takes its accent from whatever colour is selected, so
   nothing here has to invent one. */
:root {
    --ground: #000;
    --surface: rgba(10, 11, 13, 0.88);
    --raised: #121316;
    --line: #1e2024;
    --line-strong: #2b2e34;

    --dim: #4a4e57;
    --muted: #6a6f7a;
    --text: #cfd3dc;
    --bright: #f2f4f7;

    --display: "Mona Sans", ui-sans-serif, system-ui, "Segoe UI", Inter, sans-serif;
    --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, monospace;

    --inset: 1rem;
    --strip: 1.75rem;

    /* How far the back plaque's diagonal runs horizontally. Equal to its height, which makes the cut
       45 degrees - see Back.razor.css, where the hairline along it is sized from the same pair. */
    --cut: var(--strip);

    /* --flight mirrors FLIGHT in field/core.js. The camera and the panels move at related speeds or
       the page comes apart into two unrelated machines. */
    --flight: 480ms;
    --panel: 240ms;
    --hover: 120ms;

    /* The handover between the landing and the instrument, which is one thing leaving and another
       arriving and must never read as both at once. --handover is how long the arriving side waits
       for the leaving side to clear; --stagger is the beat between the pieces of one side, so a side
       assembles rather than appearing whole. Every delay on either side is built out of these two,
       which is what keeps the whole sequence readable from one place. See LANDING.md. */
    --handover: 200ms;
    --stagger: 40ms;

    --ease: cubic-bezier(0.22, 1, 0.36, 1);

    /* The slide from one exhibit's tile to the next, mirrored by SLIDE in Home.razor. Longer than
       anything else here, because it moves the whole window, and eased at both ends rather than only
       at the end: a field that left at full speed would look thrown rather than moved. */
    --slide: 900ms;
    --glide: cubic-bezier(0.65, 0, 0.35, 1);
}

/* The camera answers this for itself in field/core.js; this covers everything made of CSS. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --flight: 1ms;
        --panel: 1ms;
        --hover: 1ms;
        --slide: 1ms;

        /* Nothing waits for anything, because nothing takes any time to leave. */
        --handover: 0ms;
        --stagger: 0ms;
    }
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

body {
    margin: 0;
    background: var(--ground);
    color: var(--text);
    font-family: var(--display);

    /* Stated rather than left alone. The variable font defaults to the bottom of its own weight
       axis, so text with no weight of its own comes out ExtraLight and looks like a mistake. */
    font-weight: 400;

    /* opsz is on the axis list, so let it be driven: small text picks up the sturdier cut by itself. */
    font-optical-sizing: auto;

    overscroll-behavior: none;
}

h1:focus {
    outline: none;
}

/* The section divider, borrowed from the reference: uppercase, tracked out, and prefixed with a
   comment marker so a label never reads as content. Global rather than scoped because four
   different components draw one and they all have to draw the same one. */
.micro {
    margin: 0;
    color: var(--dim);
    font: 500 0.6875rem/1 var(--display);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Anything that is a value keeps the monospace face. Numbers never change shape between panels. */
.value {
    font: 400 0.75rem/1.4 var(--mono);
    letter-spacing: 0.02em;
}

/* Text that happens to be clickable, which is the only kind of button this interface has. */
.action {
    padding: 0;
    border: 0;
    background: none;
    color: var(--dim);
    font: 400 0.75rem/1 var(--mono);
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: color var(--hover) var(--ease);
}

    .action:hover,
    .action:focus-visible {
        color: var(--bright);
    }

    .action:disabled {
        color: #2b2e34;
        cursor: default;
    }

/* A tab in a top corner of the window: the same height, the same translucent surface and the same
   hairline as the status strip, so a plaque and the strip read as the top and the bottom edge of one
   frame rather than as two unrelated things stuck to opposite corners.

   There are two, one per stage, in opposite corners - `back` on the left once the field is engaged,
   the exhibit tabs and profile marks on the right while it is not. Neither is ever on screen with
   the other, and the frame is what keeps the strip from being the only piece of chrome on either
   view. Global, because two components draw one and the whole point is that they draw the same one. */
.plaque {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    height: var(--strip);

    /* The width of its own contents is where it ends, which is the whole of what this element is. The
       run of the diagonal is added to the padding on the cut side, so the cut clears what is inside
       rather than shaving it. */
    width: max-content;
    padding: 0 calc(var(--inset) + var(--cut)) 0 var(--inset);

    border: 0;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
    backdrop-filter: blur(10px);

    /* Top edge longest: the cut falls away from the outer top corner back toward the middle, so the
       plaque reads as receding into the corner it came out of. The border above is clipped by this
       same polygon, which is what stops the bottom hairline exactly where the diagonal begins. */
    clip-path: polygon(0 0, 100% 0, calc(100% - var(--cut)) 100%, 0 100%);

    /* Parked above the top edge. Each plaque sets its own --delay twice - once for arriving and once
       for leaving - because the two of them are on opposite sides of the same handover. */
    --delay: 0ms;
    transform: translateY(-100%);
    transition: transform var(--panel) var(--ease) var(--delay);
}

.plaque.shown {
    transform: translateY(0);
}

    /* The other half of the hairline: the border runs the bottom as far as the cut, and this runs the
       cut. Hung from the top corner the diagonal starts at and rotated onto it - the run equals the
       height, so the angle is 45 degrees and the line is the height times root two. Its outer edge
       lies along the axis it turns about and the rest of it is inboard, so the clip takes none of it
       back off. */
    .plaque::after {
        content: "";
        position: absolute;
        top: 0;
        right: 0;
        width: 1px;
        height: calc(var(--strip) * 1.4143);
        background: var(--line);
        transform-origin: 100% 0;
        transform: rotate(45deg);
    }

/* The mirror image, for the plaque in the other corner. Everything that has a side to it is stated
   twice and nothing else is, so the two corners cannot drift apart. */
.plaque.right {
    left: auto;
    right: 0;
    padding: 0 var(--inset) 0 calc(var(--inset) + var(--cut));
    clip-path: polygon(0 0, 100% 0, 100% 100%, var(--cut) 100%);
}

    .plaque.right::after {
        right: auto;
        left: 0;
        transform-origin: 0 0;
        transform: rotate(-45deg);
    }

/* The emphatic link, and the only text in this interface allowed to be bigger than a label. Underlined
   rather than boxed, because nothing here is a box with a border around it - the rule and the extra
   eighth of a rem are the whole of the emphasis. Global because the landing and the 404 both draw one
   and they have to draw the same one. */
.go {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
    width: max-content;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--line-strong);
    color: var(--text);
    font: 400 0.875rem/1 var(--mono);
    letter-spacing: 0.02em;
    text-decoration: none;
    transition: color var(--hover) var(--ease), border-color var(--hover) var(--ease);
}

    .go:hover,
    .go:focus-visible {
        color: var(--bright);
        border-bottom-color: var(--bright);
    }

    .go .arrow {
        transition: transform var(--hover) var(--ease);
    }

    .go:hover .arrow,
    .go:focus-visible .arrow {
        transform: translateX(0.25rem);
    }

:focus-visible {
    outline: 1px solid var(--line-strong);
    outline-offset: 2px;
}

/* The names a field puts up over its canvas - see relabel() in field/core.js. Global because the
   renderer makes these elements itself, outside anything a scoped stylesheet can reach. Anchored at
   the point they name and set off to its right, so the name never sits on the star it is naming. */
.field-labels {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

.field-label {
    position: absolute;
    top: 0;
    left: 0;
    margin: -0.4em 0 0 0.75rem;
    color: var(--muted);
    font: 400 0.6875rem/1 var(--mono);
    letter-spacing: 0.02em;
    white-space: nowrap;

    /* A stroke of the background around the letters rather than a box behind them, so a name crossing
       a crowd of stars stays readable without hiding any of them. */
    text-shadow: 0 0 3px #000, 0 0 6px #000;

    /* Names come and go as the view turns; they fade rather than blink. The renderer sets the opacity,
       which is also how strong a name is: see relabel() in field/core.js. */
    transition: opacity 320ms var(--ease);
}

    .field-label.chosen {
        color: var(--bright);
    }

    /* The scale on the floor rather than a star. Dimmer, and set right on its anchor: it labels a
       ring, which is a line, and there is nothing at the anchor to keep clear of. */
    .field-label.ring {
        margin-left: 0.25rem;
        color: var(--dim);
    }

.loading {
    color: var(--muted);
    font: 400 0.75rem/1 var(--mono);
    padding: 1rem;
}

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

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

/* Moved out of MainLayout's scoped stylesheet. The banner now lives in index.html, outside the
   Blazor root, so a scoped selector would no longer match it. */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

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