/* The Industry design system, as in the desktop viewer: paper or deep blue ground, steel accent,
   condensed labels, square corners, hairlines. */
@import url("https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600&family=Barlow:wght@400;500&display=swap");

:root {
    /* Paper, but paper in a room rather than a lightbox: the old #f2f2f3 was the brightest thing
       on the screen and the model had to compete with it. */
    --ground: #e1e5ea;
    --stage-top: #e4e9ef;
    --stage-bottom: #b3c0d0;
    --ink: #1d1f20;
    --muted: #424244;
    --body: #5d5d60;
    --divider: rgba(29, 31, 32, 0.16);
    --accent-text: #416180;
    --bar: #1d2d3d;
    --bar-rule: rgba(242, 242, 243, 0.22);
    --bar-text: #d6ebff;
    --bar-strong: #f2f2f3;
    --bar-hover: rgba(242, 242, 243, 0.16);
    --bar-active: #f2f2f3;
    --bar-active-text: #1d2d3d;
}

:root[data-theme="dark"] {
    --ground: #1d2d3d;
    --stage-top: #26394e;
    --stage-bottom: #121d28;
    --ink: #f2f2f3;
    --muted: #b5c9dc;
    --body: #98abbd;
    --divider: rgba(242, 242, 243, 0.14);
    --accent-text: #94bce3;
    --bar: #14202c;
    --bar-rule: rgba(242, 242, 243, 0.18);
    --bar-active: #d6ebff;
    --bar-active-text: #14202c;
}

* { box-sizing: border-box; }

html, body, #app {
    height: 100%;
    margin: 0;
}

/* A phone's address bar comes and goes, and 100% of the viewport then includes the part hidden
   behind it. dvh is the height actually on screen. */
@supports (height: 100dvh) {
    html, body, #app { height: 100dvh; }
}

body {
    background: var(--ground);
    color: var(--ink);
    font-family: Barlow, system-ui, sans-serif;
    font-size: 14px;
}

#app {
    display: grid;
    /* bar, tabs, the view, the status line. The tab row is always present, and zero high when
       nothing is open, so the rows never shift meaning. */
    grid-template-rows: auto auto minmax(0, 1fr) auto;
    min-width: 0;
}

.kicker, .wordmark, .tool, h1 {
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
}

.kicker {
    font-size: 12px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
}

.kicker.accent { color: var(--accent-text); }

/* The bar follows the window at any width: the controls keep their size and move to a line of
   their own when they no longer fit beside the name, rather than running off the right edge. */
.bar {
    background: var(--bar);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 6px 16px;
    padding: 7px 14px;
    min-height: 50px;
    min-width: 0;
}

.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.brand:hover .wordmark { color: #fff; }
.brand:focus-visible { outline: 1px solid var(--bar-text); outline-offset: 4px; }
.mark { display: block; }
.wordmark { color: var(--bar-strong); font-size: 18px; letter-spacing: 0.06em; }
.brand .kicker { color: var(--bar-text); }
.rule { width: 1px; height: 16px; background: var(--bar-rule); display: inline-block; }
.file { color: var(--bar-strong); font-family: "Barlow Condensed", Barlow, sans-serif; font-weight: 600; letter-spacing: 0.12em; font-size: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

.tools { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
/* A flex item shrinks before it wraps, so a row too long for the window squashes its controls and
   still hangs some off the edge. Refusing to shrink is what makes them wrap. */
.tools > * { flex: 0 0 auto; }

.tool {
    background: transparent;
    border: 0;
    color: var(--bar-text);
    font-size: 12px;
    letter-spacing: 0.12em;
    padding: 8px 10px;
    cursor: pointer;
}

.tool:hover:not(:disabled) { background: var(--bar-hover); color: var(--bar-strong); }
.tool:disabled { opacity: 0.4; cursor: default; }
.tool.on { background: var(--bar-active); color: var(--bar-active-text); }

/* the OPEN control is a file input wearing a button */
label.tool { position: relative; overflow: hidden; }
label.tool input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
label.tool.disabled { opacity: 0.4; pointer-events: none; }

/* OPEN is the one thing a first visit has to find, so it is the only solid block on the bar. */
.tool.primary {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: var(--bar-active);
    color: var(--bar-active-text);
    padding: 8px 13px;
}
.tool.primary:hover { background: #fff; color: var(--bar-active-text); }
.tool.primary svg { opacity: 0.8; }

/* The views are one control with four positions, not four buttons that happen to sit together:
   a hairline box round them, and the one being looked through is lit. Before this there was no
   way to tell which view you were in. */
.segmented {
    display: inline-flex;
    border: 1px solid var(--bar-rule);
    margin: 0 2px;
}
.segmented .tool { padding: 7px 9px; }
.segmented .tool + .tool { border-left: 1px solid var(--bar-rule); }

/* Verbs and switches read differently from labels: square, glyph, no word. */
.tool.icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 30px;
    padding: 0;
    font-size: 13px;
}

body.dropping .stage::after {
    content: "";
    position: absolute;
    inset: 10px;
    border: 2px dashed var(--accent-text);
    pointer-events: none;
}

/* The shortcut list, over the drawing rather than beside it: it is read once and dismissed. */
.keys {
    position: absolute;
    right: 18px;
    top: 18px;
    background: var(--bar);
    border: 1px solid var(--bar-rule);
    padding: 14px 16px 15px;
    min-width: 232px;
}
.keys .kicker { color: var(--bar-text); margin: 0 0 9px; }
.keys dl { margin: 0; display: grid; gap: 6px; }
.keys dl > div { display: flex; align-items: baseline; gap: 12px; }
.keys dt {
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    letter-spacing: 0.1em;
    color: var(--bar-strong);
    min-width: 62px;
}
.keys dd { margin: 0; color: var(--bar-text); opacity: 0.82; font-size: 13px; }

/* One tab a drawing, in the bar's colour so the strip reads as part of it, with the open tab
   dropping into the view below like a folder tab. */
.tabs {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    padding: 0 10px;
    background: var(--bar);
    overflow-x: auto;
    scrollbar-width: none;
    min-width: 0;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs.none { height: 0; padding: 0; }

.tab {
    flex: 0 1 auto;
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    max-width: 260px;
    height: 32px;
    padding: 0 6px 0 12px;
    background: rgba(242, 242, 243, 0.06);
    color: var(--bar-text);
    cursor: pointer;
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    font-size: 12px;
    letter-spacing: 0.1em;
    border-top: 2px solid transparent;
}
.tab:hover { background: rgba(242, 242, 243, 0.12); color: var(--bar-strong); }
.tab.on {
    background: var(--stage-top);
    color: var(--ink);
    border-top-color: var(--accent-text);
}
.tab-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.tab-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
}
.tab-close:hover { opacity: 1; background: rgba(127, 127, 127, 0.18); }
.tabs-budget {
    margin-left: auto;
    align-self: center;
    padding-left: 14px;
    white-space: nowrap;
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    font-size: 11px;
    letter-spacing: 0.12em;
    color: var(--bar-text);
    opacity: 0.6;
}

/* Problems still get a panel: they need reading and a decision. */
.problem {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(440px, calc(100% - 32px));
    padding: 20px 22px 18px;
    background: var(--bar);
    border: 1px solid var(--bar-rule);
    color: var(--bar-strong);
    z-index: 5;
}

/* Loading does not. A hairline along the top edge of the view and the stage in small caps in the
   corner: enough to see that something is happening and how far along, without covering the
   drawing already open. */
.loading {
    position: absolute;
    inset: 0 0 auto 0;
    z-index: 5;
    pointer-events: none;
}
.loadbar {
    position: relative;
    height: 2px;
    background: transparent;
    overflow: hidden;
}
.loadbar span {
    display: block;
    height: 100%;
    background: var(--accent-text);
    transition: width 0.3s ease-out;
}
/* A faint sheen crossing the line. It is a transform, which the compositor animates on its own,
   so it keeps moving through the meshing step, when the page's thread cannot repaint. */
.loadbar::after {
    content: "";
    position: absolute;
    inset: 0;
    width: 22%;
    background: linear-gradient(90deg, transparent, var(--accent-text), transparent);
    opacity: 0.55;
    animation: sheen 1.6s ease-in-out infinite;
    will-change: transform;
}
@keyframes sheen {
    from { transform: translateX(-110%); }
    to { transform: translateX(560%); }
}
.loading-name { display: none; }
.loading .kicker {
    position: absolute;
    left: 14px;
    top: 10px;
    margin: 0;
    font-size: 11px;
    color: var(--muted);
    opacity: 0.85;
}
.stage.busy canvas { opacity: 0.85; transition: opacity 0.2s; }
/* The number is what people actually read. Fixed width, so the text beside it does not shuffle
   sideways as it counts. */
.loading-percent {
    display: inline-block;
    min-width: 3.4ch;
    margin-right: 6px;
    color: var(--accent-text);
    font-variant-numeric: tabular-nums;
}

.problem:focus { outline: none; }
.problem .close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    padding: 0;
    background: none;
    border: 0;
    color: var(--bar-text);
    font-size: 20px;
    line-height: 28px;
    cursor: pointer;
}
.problem .close:hover { color: var(--bar-strong); background: var(--bar-hover); }
.problem .kicker { margin: 0 0 8px; color: var(--bar-strong); }
.problem .body { margin: 0 0 14px; color: var(--bar-text); font-size: 14px; line-height: 1.45; }
.problem-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* Written out in full: the button's look used to live only under .empty, so here it fell back to
   the browser's own white button with white text on it. */
.problem .sample {
    margin: 0;
    padding: 9px 14px;
    background: transparent;
    border: 1px solid var(--bar-rule);
    color: var(--bar-strong);
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}
.problem .sample:hover { background: var(--bar-hover); border-color: var(--bar-text); }
/* The way to open a drawing that is nearly always the one to pick: amber, as the measure tool's
   accent is, with a bolt for speed. */
.problem .sample.fast { display: inline-flex; align-items: center; gap: 6px; border-color: #f5a524; color: #f5a524; }
.problem .sample.fast:hover { background: rgba(245, 165, 36, 0.12); border-color: #f5a524; }
.problem .sample.fast .bolt { width: 14px; height: 14px; fill: currentColor; }
/* The detail setting, folded away under a quiet toggle until asked for. */
.problem .more {
    margin: -4px 0 12px;
    padding: 0;
    background: none;
    border: none;
    color: var(--bar-text);
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    cursor: pointer;
}
.problem .more:hover { color: var(--bar-strong); }
.problem .detail {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: -4px 0 16px;
    color: var(--bar-text);
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
/* The slider drawn by hand: a hairline track and a small amber mark, rather than the browser's
   thick bar and knob. */
.problem .detail input[type=range] {
    flex: 1;
    min-width: 80px;
    height: 14px;
    margin: 0;
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    cursor: pointer;
}
.problem .detail input[type=range]:focus { outline: none; }
/* Amber up to the mark, as far as the detail goes; --fill is set on the element from the level. */
.problem .detail input[type=range]::-webkit-slider-runnable-track {
    height: 2px;
    border-radius: 1px;
    background: linear-gradient(to right, #f5a524 var(--fill, 50%), var(--bar-rule) var(--fill, 50%));
}
.problem .detail input[type=range]::-moz-range-track { height: 2px; background: var(--bar-rule); border-radius: 1px; }
.problem .detail input[type=range]::-moz-range-progress { height: 2px; background: #f5a524; border-radius: 1px; }
.problem .detail input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 10px;
    height: 10px;
    margin-top: -4px;
    border: none;
    border-radius: 50%;
    background: #f5a524;
}
.problem .detail input[type=range]::-moz-range-thumb { width: 10px; height: 10px; border: none; border-radius: 50%; background: #f5a524; }
.problem .detail input[type=range]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px rgba(245, 165, 36, 0.3); }
.problem .detail > span:not(.detail-count) { color: var(--bar-text); opacity: 0.7; font-size: 11px; }
.problem .detail-count { min-width: 104px; text-align: right; color: var(--bar-strong); font-size: 11px; }

/* The centre of rotation while turning: a small ring where the cursor was pressed. */
.pivot {
    position: absolute;
    left: -5px;
    top: -5px;
    width: 10px;
    height: 10px;
    border: 1.5px solid var(--accent-text);
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.25);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.12s;
    z-index: 4;
}
.pivot.on { opacity: 0.9; }

/* The view sits on a gradient rather than a flat fill: lighter above, deeper below, the way a
   room falls off towards the floor. The canvas itself is transparent, so this is what shows
   through and there is one background rather than two to keep in step. */
.stage {
    position: relative;
    overflow: hidden;
    background: linear-gradient(180deg, var(--stage-top) 0%, var(--stage-bottom) 100%);
}
.stage canvas { display: block; width: 100%; height: 100%; }

.empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    text-align: center;
    pointer-events: none;
}

.empty h1 { font-size: 40px; margin: 0; letter-spacing: -0.02em; }
.empty .body { color: var(--body); margin: 0; }

/* The one thing on the empty state that can be clicked, so it is the only thing that is pointed at. */
.empty .sample {
    pointer-events: auto;
    margin-top: 12px;
    background: transparent;
    border: 1px solid var(--divider);
    color: var(--accent-text);
    font-family: "Barlow Condensed", Barlow, sans-serif;
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    padding: 10px 16px;
    cursor: pointer;
}
.empty .sample:hover { border-color: var(--accent-text); }

.statusbar {
    border-top: 1px solid var(--divider);
    padding: 9px 16px;
    display: flex;
    justify-content: space-between;
    gap: 24px;
}

.statusbar .kicker { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* What the drawing contains matters more than the mouse hints, so the hints go first when the
   window is narrow. They were being cut off mid-word before. */
.statusbar .kicker:first-child { flex: 1 1 auto; min-width: 0; }
.statusbar .kicker.accent { flex: 0 1 auto; min-width: 0; }
/* Nothing in the page may make it wider than the window: one long line of small caps used to, and
   then everything right-aligned, the toolbar included, slid off the edge with it. */
#app, .statusbar { max-width: 100vw; overflow-x: hidden; }
/* Not the bar: clipping overflow makes an element a scroll container, and the layout may then squash
   a scroll container below its content - the bar grew a scrollbar and hid a row of buttons. */

/* Say "drag" to a mouse and "finger" to a screen, rather than guessing from the width: a laptop
   with a touchscreen has both, and a narrow window is not a phone. */
.touch { display: none; }
@media (hover: none) and (pointer: coarse) {
    .mouse { display: none; }
    .touch { display: revert; }
}

@media (max-width: 1180px) {
    .file { display: none; }
    .statusbar .kicker.accent { display: none; }
}

/*
  A phone. The bar keeps the name and wraps the controls onto a line of their own rather than
  squeezing eight of them into 390px beside the wordmark, and everything grows to a size a thumb
  can actually hit - 44px, which is what the platform asks for and what the 30px desktop row is
  not.
*/
@media (max-width: 720px) {
    body { font-size: 15px; }

    /* The bar is two rows here, so its row has to be as tall as it turns out to be. Left at the
       desktop's fixed 50px the second line has nowhere to go and is simply cut off. */

    .bar {
        flex-wrap: wrap;
        /* Lined up with the first row of controls rather than floating between the two: with the
           rows centred, the mark sat in the gap and belonged to neither. */
        align-items: flex-start;
        padding: 9px 10px;
        gap: 9px 12px;
    }

    .brand { margin-top: 9px; }

    /* Wrapping, not squeezing. Ten controls do not fit across a phone on one line, and a flex row
       that cannot wrap does not shrink them - it runs off the edge and takes the last four with it.
       They start beside the mark rather than below it: a row of their own left the mark sitting
       alone across the top of the screen, and the stage paid for it. */
    .tools {
        /* May be narrower than its content, which is what lets the first controls sit beside the
           mark and the rest wrap under them. Left at auto the whole group is wider than the space
           left on that line, so it took a line of its own and the mark kept one to itself. */
        flex: 1 1 0;
        min-width: 0;
        flex-wrap: wrap;
        justify-content: flex-start;
        gap: 6px;
    }

    /* A flex item shrinks before it wraps, so a row that is too long squashes its controls down
       to nothing and then still hangs off the edge. Refusing to shrink is what makes it wrap. */
    .tools > * { flex: 0 0 auto; }

    /* A grid track of 1fr will not go below its content's own minimum unless it is told it may,
       and then the status line is pushed off the bottom of the screen. */
    .stage { min-height: 0; }

    .tools .rule { display: none; }
    .tool { padding: 12px 10px; font-size: 13px; }
    .tool.primary { padding: 12px 15px; }
    .tool.icon { width: 44px; height: 44px; }
    .segmented { margin: 0; }
    .segmented .tool { padding: 12px 9px; }

    .empty h1 { font-size: 27px; }
    .empty .body { font-size: 14px; padding: 0 26px; }

    .statusbar { padding: 8px 12px; flex-direction: column; gap: 3px; }
    .statusbar .kicker { white-space: normal; }

    /* The key list is a sheet across the foot of the screen, where a thumb is, not a card pinned
       to a top corner it cannot reach. */
    .keys {
        right: 10px;
        left: 10px;
        top: auto;
        bottom: 10px;
        min-width: 0;
    }
    .keys dl { grid-template-columns: 1fr 1fr; }
}

/* Narrower still: the wordmark goes and the mark carries the brand on its own. */
@media (max-width: 420px) {
    .wordmark, .brand .rule, .brand .kicker { display: none; }
    .tool { padding: 12px 8px; letter-spacing: 0.08em; }
    .keys dl { grid-template-columns: 1fr; }
}

/* The graphics card kept taking the view's memory back; see viewer.js. Said plainly, over the view. */
body[data-gpu-gone] .stage::before {
    content: "The graphics card ran short of memory for this view. Reload the page to see the drawings again.";
    position: absolute;
    inset: auto 16px 16px 16px;
    z-index: 5;
    padding: 10px 14px;
    background: rgba(0, 0, 0, 0.72);
    color: #f2f2f3;
    font: 13px/1.4 inherit;
}

/* What is selected, and what can be done with it: along the bottom of the view, where a thumb
   reaches, since a phone has no right click. */
.objectbar {
    position: absolute;
    left: 50%;
    bottom: 14px;
    transform: translateX(-50%);
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 6px;
    max-width: calc(100% - 32px);
    padding: 6px 8px;
    background: var(--bar);
    border: 1px solid rgba(255, 255, 255, 0.14);
}
.objectbar-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin: 0 6px 0 2px;
}
.objectmenu {
    position: fixed;
    z-index: 50;
    display: flex;
    flex-direction: column;
    min-width: 170px;
    padding: 4px;
    background: var(--bar);
    border: 1px solid rgba(255, 255, 255, 0.16);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.objectmenu .tool { justify-content: flex-start; text-align: left; }
.objectmenu-name { padding: 6px 8px 4px; opacity: 0.8; }

/* The bar, the menu over the view and the panel keep the dark ground in both themes, so their
   text has to come from the bar's palette and not the page's: a kicker's muted ink is near-black
   in light mode, and a solid's name went dark grey on dark blue. */
.objectbar .kicker,
.objectmenu .kicker,
.objectpanel .kicker { color: var(--bar-text); }

/* Layers and parts: a button over the top left of the view, and the panel it opens beside it. */

.objectpanel {
    position: absolute;
    top: 52px;
    left: 10px;
    bottom: 66px;
    z-index: 4;
    display: flex;
    flex-direction: column;
    width: min(340px, calc(100% - 20px));
    background: var(--bar);
    border: 1px solid var(--bar-rule);
}
.objectpanel-head { display: flex; align-items: center; gap: 4px; padding: 6px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
.objectpanel-gap { flex: 1; }
.objectpanel-list { flex: 1; overflow-y: auto; padding: 4px 0; }
.objectpanel-row {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 30px;
    padding: 0 8px;
    cursor: pointer;
    font-size: 13px;
    color: #dfe7ef;
}
.objectpanel-row:hover { background: rgba(255, 255, 255, 0.06); }
.objectpanel-row.part { padding-left: 34px; font-size: 12.5px; }
.objectpanel-row.hidden .objectpanel-name { opacity: 0.45; }
.objectpanel-row.selected { background: rgba(245, 158, 11, 0.22); }
.objectpanel-row.more { padding-left: 42px; opacity: 0.6; cursor: default; }
.objectpanel-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.objectpanel-count { opacity: 0.6; font-variant-numeric: tabular-nums; font-size: 12px; }
.objectpanel .eye, .objectpanel .twist, .objectpanel .isolate {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
}
.objectpanel .eye.off { opacity: 0.45; }
@media (max-width: 600px) {
    .panelbutton span { display: none; }
    .objectpanel { width: calc(100% - 20px); bottom: 70px; }
}
.objectpanel .isolate { opacity: 0.55; }
.objectpanel .isolate:hover { opacity: 1; }

/* Measure and the axes switch, beside LAYERS. */
.viewtools { position: absolute; top: 10px; left: 10px; z-index: 4; display: flex; gap: 6px; }
.measurelayer { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 3; pointer-events: none; overflow: visible; }
.measure-halo { stroke: rgba(10, 16, 22, 0.55); stroke-width: 5; stroke-linecap: round; }
.measure-line { stroke: #f5a524; stroke-width: 2; stroke-linecap: round; }
.measure-end { fill: var(--bar); stroke: #f5a524; stroke-width: 2.5; }
.measurelabel {
    position: absolute;
    z-index: 4;
    transform: translate(-50%, calc(-100% - 12px));
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 6px 10px 7px;
    background: var(--bar);
    border: 1px solid var(--bar-rule);
    border-top: 2px solid #f5a524;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3);
    color: var(--bar-strong);
    white-space: nowrap;
    pointer-events: none;
}
.measurelabel b { font-size: 16px; font-weight: 600; letter-spacing: 0.02em; font-variant-numeric: tabular-nums; }
.measurelabel b small { font-size: 11px; font-weight: 500; margin-left: 4px; opacity: 0.7; text-transform: lowercase; }
.measurelabel div { display: flex; gap: 10px; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--bar-text); }
.measurelabel .ax i { font-style: normal; font-weight: 700; margin-right: 4px; }
.measurelabel .ax.x i { color: #ef6b6f; }
.measurelabel .ax.y i { color: #5fbf73; }
.measurelabel .ax.z i { color: #5aa7e6; }
.axislabel {
    position: absolute;
    z-index: 3;
    transform: translate(-50%, -50%);
    font: 700 11px/1 ui-monospace, monospace;
    pointer-events: none;
}
@media (max-width: 600px) {
    .viewtools .measurebutton span { display: none; }
}

/* The pieces that float over the view take the top bar's own colours, in both themes, and keep them
   under the pointer: the bar's hover is a veil meant for its own dark ground, and over the light
   theme's stage it turned a floating button white. */
.panelbutton, .viewtools .tool, .objectbar .tool, .objectmenu .tool {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--bar);
    border: 1px solid var(--bar-rule);
}
.panelbutton:hover:not(:disabled), .viewtools .tool:hover:not(:disabled),
.objectbar .tool:hover:not(:disabled), .objectmenu .tool:hover:not(:disabled) {
    background: color-mix(in srgb, var(--bar) 82%, #ffffff);
    color: var(--bar-strong);
}
.panelbutton.on, .viewtools .tool.on { background: var(--bar-active); color: var(--bar-active-text); }
.objectmenu .tool { border: 0; }
.objectpanel .objectpanel-head .tool { border: 0; background: transparent; }
.objectpanel .objectpanel-head .tool.on { background: var(--bar-active); color: var(--bar-active-text); }
.objectpanel-row { color: var(--bar-text); }
.objectbar, .objectmenu, .objectpanel { color: var(--bar-text); border-color: var(--bar-rule); }

/* The foot of the page: a load's progress on the left, the drawing's weight and the look switches
   on the right. */
.statusbar { align-items: center; padding-top: 4px; padding-bottom: 4px; min-height: 38px; }
.status-left { display: flex; align-items: baseline; gap: 10px; flex: 1 1 auto; min-width: 0; }
.status-left .loading-percent {
    min-width: 3.4ch;
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--ink);
}
.status-left .loading-stage { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.status-right { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.status-right .status-stats { margin-right: 6px; cursor: default; }
.status-right .tool { color: var(--muted); }
.status-right .tool:hover:not(:disabled) { background: var(--divider); color: var(--ink); }
.status-right .tool.on { background: var(--ink); color: var(--ground); }
@media (max-width: 600px) {
    .statusbar { flex-direction: row; }
}
.objectpanel { user-select: none; -webkit-user-select: none; }

/* The load line runs along the top of the footer, beside the percentage and stage it goes with. */
.loading { inset: auto 0 0 0; }

/* Installing the app. The browser decides whether that is possible at all and says so once, so
   the button waits for the mark viewer.js puts on the document and is not there otherwise - an
   Install that cannot install is worse than no Install. */
.tool.install { display: inline-flex; gap: 6px; }
html[data-installed] .tool.install { display: none; }
.tool.install svg { flex: none; }
