/* Root styles */

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

:root {
    /* UI colours. */
    --ui-black: hsla(0, 0%, 0%, 0.9); /* Almost black. */
    --ui-white: hsl(0, 0%, 96%); /* Almost white. */
    --ui-blue: hsl(200, 100%, 50%); /* Selection blue. */
    --ui-orange: hsl(30, 100%, 50%); /* Highlight orange. */

    --ui-background: transparent;
    --ui-canvas: white;
    --ui-grid: hsl(0 0% 80%);
    --ui-border: hsla(0, 0%, 100%, 0.2);
    --ui-hover: hsla(0, 0%, 100%, 0.1);
    --ui-active: hsla(0, 0%, 100%, 0.2);
    --ui-focus: var(--ui-blue);
    --ui-text: hsla(0, 0%, 100%, 0.6);
    --ui-error: hsl(0, 50%, 50%);
    --ui-warning: hsl(50, 100%, 70%);

    /* Cell states. */
    /* The default colour for labels and arrows, if no explicit colour has been set by the user. */
    --cell-colour: black;
    --cell-hover: hsla(0, 0%, 0%, 0.1);
    --cell-selected: hsla(0, 0%, 0%, 0.2);
    --cell-source: var(--cell-selected);
    --cell-target: var(--cell-source);
}

.dark-theme {
    --ui-canvas: hsl(250 20% 10%);
    --ui-grid: hsl(250 20% 25%);

    --cell-colour: hsl(250 20% 95%);
    --cell-hover: hsl(250 30% 60% / 0.25);
    --cell-selected: hsl(250 30% 60% / 0.5);
    --cell-source: var(--cell-selected);
    --cell-target: var(--cell-source);
}

body {
    position: absolute;
    width: 100%; height: 100%;
    margin: 0;
    overflow: hidden;

    background: var(--ui-canvas);

    font-family: Arial, sans-serif;

    /* Safari on iOS does not currently respect `none`: double-tap-to-zoom is not disabled. We are
    forced to prevent this in JavaScript instead. */
    touch-action: none;
}

body.embedded {
    background: transparent;
}

body:not(.modal) {
    user-select: none;
}

noscript {
    display: inline-block;
    position: fixed;
    left: 50%; top: 50%;
    padding: 6pt 8pt;
    transform: translate(-50%, -50%);

    background: var(--ui-black);
    border-radius: 8px;

    text-align: center;
    color: var(--ui-white);
}

/* We occasionally want to disable transitions on a particular element. */
.no-transition {
    transition: none !important;
}

/* Special elements */

.error-banner {
    position: fixed;
    width: 100%;
    left: 0; top: 0;
    z-index: 200;
    padding: 8px 0;

    background: var(--ui-error);
    color: var(--ui-white);

    text-align: center;

    user-select: none;

    transition: transform 0.2s;
}

.error-banner.hidden {
    transform: translateY(-100%);
}

.close {
    width: 28px; height: 28px;
    margin-left: 20px;

    background: transparent;
    border: none;
    border-radius: 100%;
    outline: none;

    text-align: center;
    color: white;
    font-size: 20px;
}

.close:hover {
    background: hsla(0, 0%, 100%, 0.2);
}

.close:active {
    background: hsla(0, 0%, 100%, 0.4);
}

.close::before {
    content: "×";
}

#logo-link {
    position: fixed;
    right: 16px; bottom: 16px;
    z-index: 90;

    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;

    color: var(--ui-black);
    text-decoration: none;

    opacity: 0.8;
}

a > .logo {
    width: 48px;
}

.logo-mode {
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: lowercase;
}

.dark-theme #logo-link {
    color: var(--ui-white);
}

.dark-theme a > .logo {
    filter: invert(1);
}

#logo-link:hover {
    opacity: 1;
}

.version.hidden {
    display: none;
}

.version {
    position: fixed;
    left: 50px; top: 48px;
    z-index: 90;

    font-size: 8pt;
    color: var(--ui-black);

    pointer-events: none;
}

.dark-theme .version {
    color: var(--ui-white);
}

.tooltip {
    display: block;
    position: absolute;
    left: 50%;
    padding: 8px 12px;
    line-height: 18pt;
    transform: translateX(-50%);
    z-index: 80;

    background: var(--ui-blue);
    border-radius: 4px;

    text-align: center;
    color: var(--ui-black);
}

.ui > .tooltip {
    /* Display just below the toolbar. */
    top: calc(16px + 48px + 8px);
}

kbd {
    white-space: pre;
}

.tooltip kbd, .pane kbd, .port kbd, .pane textarea {
    padding: 1px 6px;

    border-radius: 4px;
    box-shadow: hsla(0, 0%, 0%, 0.1) 0 2px 0px;
}

.tooltip kbd {
    background: var(--ui-white);
    border: hsla(0, 0%, 0%, 0.4) 1px solid;
}

.tooltip kbd + kbd, .pane kbd + kbd {
    margin-left: 4px;
}

.loading-screen {
    position: fixed;
    left: 0; right: 0;
    top: 0; bottom: 0;
    z-index: 400;

    background: hsl(0, 0%, 10%);
}

.loading-screen.hidden {
    opacity: 0;

    pointer-events: none;

    transition: opacity 0.6s;
}

.loading-screen .logo {
    position: fixed;
    left: 50%; top: 50%;
    width: 288px;
    transform: translate(-50%, -50%);
}

.loading-screen span {
    display: inline-block;
    position: fixed;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    margin-left: 20px; margin-top: 32px;

    color: var(--ui-blue);
    white-space: nowrap;
}

.embedded .loading-screen {
    background: white;
}

.embedded .loading-screen .logo {
    max-width: 60%;
}

.embedded .loading-screen span {
    /* Hide the "Loading diagram..." text in an `<iframe>`, because there's likely not to be enough
    space for it. */
    display: none;
}

/* The canvas of cells */

.container {
    position: fixed;
    width: 100%;
    height: 100%;
    z-index: 1;
}

.canvas {
    position: absolute;
    left: 50%;
    top: 50%;
    transform-origin: top left;
}

.ui.pan .container {
    cursor: grab;
}

.ui.pan .container:active {
    cursor: grabbing;
}

/* Grid interaction */

.grid {
    position: fixed;
    pointer-events: none;
    z-index: 0;

    color: var(--ui-grid);
}

.grid.hidden {
    display: none;
}

.focus-point {
    display: block;
    position: absolute;
    margin-left: 1px; margin-top: 1px;
    width: 126px; height: 126px;

    background: hsla(0, 0%, 0%, 0);

    text-align: center;
    font: 16px Arial, sans-serif;
    color: hsla(0, 0%, 0%, 0.4);
}

/* Freeform diagrams never use the legacy grid-cell focus point. */
.ui.freeform .focus-point {
    display: none;
}

.dark-theme .focus-point {
    color: hsl(250 20% 60%);
}

.focus-point.revealed {
    background: hsla(0, 0%, 0%, 0.1);
}

.dark-theme .focus-point.revealed {
    background: hsl(250 20% 20%);
}

.focus-point.focused {
    border: var(--ui-focus) 4px solid;
    border-radius: 8px;
}

.focus-point.smooth {
    transition: left 0.1s, top 0.1s, width 0.1s, height 0.1s, padding-top 0.1s;
}

.focus-point::before {
    content: "Add vertex";
    visibility: hidden;
    position: relative;
    top: -8px;
}

.focus-point.active::before {
    content: "Add edge";
}

.focus-point.revealed::before {
    visibility: visible;
}

.ui:not(.show-hints) .focus-point .tooltip, .focus-point:not(.focused) .tooltip, .tooltip:empty {
    display: none;
}

.focus-point .tooltip {
    bottom: 100%;
    margin-bottom: 24px;

    white-space: nowrap;
}

.focus-point .tooltip::before {
    content: "";
    position: absolute;
    top: 100%; left: calc(50% - 14px);
    /* Make sure there's not a subpixel gap between this and the tooltip. */
    margin-top: -1px;
    width: 0; height: 0;

    border: 14px solid transparent;
    border-top-color: var(--ui-blue);
    border-bottom: none;
}

.inline-error {
    position: absolute;
    padding: 4px 8px;
    transform: translate(-50%, -50%);
    z-index: 81;

    white-space: nowrap;

    background: var(--ui-error);
    color: var(--ui-white);

    border-radius: 2px;

    animation: error-appear 0.2s cubic-bezier(0.6, 1.4, 0.6, 1.4);
}

.inline-error.hidden {
    animation: error-disappear 0.1s forwards;
}

@keyframes error-appear {
    from {
        opacity: 0;
        transform: translate(-50%, -50%) scale(0.5, 0.5);
    }

    to {
        opacity: 1;
        transform: translate(-50%, -50%);
    }
}

@keyframes error-disappear {
    to {
        opacity: 0;
        transform: translate(-50%, -50%) scale(0.5, 0.5);
    }
}

/* Vertices */

.vertex {
    position: absolute;
    z-index: 2;
}

.ui.freeform.placing-node .canvas {
    cursor: crosshair;
}

.ui.freeform.placing-text .canvas {
    cursor: text;
}

.ui.default .vertex {
    cursor: move;
}

.vertex .content {
    position: absolute;
    transform: translate(-50%, -50%);
    z-index: 2;

    text-align: center;
}

/* Labels in the wiring editor are annotations. They must never resize a
   vertex or alter connected arrows. */
.ui.freeform .vertex .content,
.ui.freeform .vertex .label {
    overflow: visible;
}

/* A freeform label is an independent annotation anchored at its node centre.
   It never inherits a width constraint or a scale-to-fit rule from the old
   grid-cell renderer. */
.ui.freeform .vertex .label {
    /* Inline LaTeX follows its marker, as it would in ordinary math mode. */
    left: calc(100% + 6px);
    top: 50%;
    transform: translate(0, -50%);
    width: max-content;
    max-width: none;
    min-width: max-content;
    font-size: 26px;
    /* Labels deliberately overflow their symbol-sized hit area. Make that
       visible annotation participate in the same pointer handling as its
       parent node, so users can select or drag it by its text. */
    pointer-events: auto;
    cursor: move;
}

/* A leading LaTeX line break is an explicit request to place the annotation
   below its marker. This is syntax-driven, rather than a fixed presentation
   rule applied to every freeform label. */
.ui.freeform .vertex .label.below-marker {
    left: 50%;
    top: calc(100% + 6px);
    transform: translate(-50%, 0);
}

.ui.freeform .vertex .freeform-symbol {
    position: absolute;
    left: 50%; top: 50%;
    width: var(--freeform-symbol-size, 18px);
    height: var(--freeform-symbol-size, 18px);
    transform: translate(-50%, -50%);
    pointer-events: none;
    color: var(--cell-colour);
}

.ui.freeform .vertex .freeform-symbol[data-symbol="bullet"] {
    border-radius: 50%;
    background: currentColor;
}

.ui.freeform .vertex .freeform-symbol[data-symbol="square"] {
    background: currentColor;
}

.ui.freeform .vertex .freeform-symbol[data-symbol="circle"] {
    box-sizing: border-box;
    border: 2px solid currentColor;
    border-radius: 50%;
}

.ui.freeform .vertex.freeform-text .freeform-symbol {
    display: none;
}

.ui.freeform .vertex.freeform-text .label {
    left: 0;
    top: 50%;
    transform: translate(0, -50%);
}

.ui.freeform .vertex.freeform-text.selected .label {
    outline: 2px solid var(--ui-blue);
    outline-offset: 4px;
    border-radius: 3px;
}

/* Selection feedback for point-like freeform nodes. This is a halo around
   the marker, not a legacy vertex card or a change to its geometry. */
.ui.freeform .vertex.selected .freeform-symbol {
    box-shadow: 0 0 0 3px var(--ui-blue);
}

.ui.freeform.placing-free-arrow .canvas { cursor: crosshair; }
.freeform-free-arrow { position: absolute; left: 0; top: 0; width: 1px; height: 1px; overflow: visible; z-index: 20; pointer-events: none; }
.vertex.freeform-anchor { display: none; }
.free-arrow-line { stroke: transparent; stroke-width: 14; pointer-events: stroke; }
.free-arrow-head { display: none; }
.free-arrow-handle { display: none; fill: var(--ui-canvas); stroke: var(--ui-blue); stroke-width: 2; pointer-events: all; cursor: grab; }
.freeform-free-arrow.selected .free-arrow-handle { display: block; }

/* Freeform rectangular containers. They sit behind Quiver cells and make
   reusable problem definitions legible without becoming connectable nodes. */
.diagram-box {
    position: absolute;
    box-sizing: border-box;
    /* Keep only the box controls above diagram nodes. The container itself
       remains transparent to pointer events, so nodes inside stay editable. */
    /* The box body is below nodes; its controls carry their own z-index. */
    z-index: auto;
    pointer-events: none;
    min-width: 180px;
    min-height: 120px;

    border: 2px solid hsl(250 62% 48% / 0.78);
    border-radius: 10px;
    background: hsl(250 80% 98% / 0.72);
    box-shadow: 0 12px 30px hsl(250 35% 25% / 0.08);
    color: hsl(250 45% 28%);
    font: 600 14px Arial, sans-serif;
}

.diagram-box.selected {
    outline: 3px solid var(--ui-focus);
    outline-offset: 3px;
}

.dark-theme .diagram-box {
    border-color: hsl(250 75% 72% / 0.82);
    background: hsl(250 36% 18% / 0.72);
    color: hsl(250 75% 86%);
    box-shadow: none;
}

.diagram-box--problem-bank {
    border-style: solid;
    border-color: hsl(8 72% 48% / 0.82);
    background: hsl(8 80% 98% / 0.65);
}

.diagram-box__selection-surface {
    position: absolute;
    z-index: 1;
    inset: 30px 0 0;
    pointer-events: auto;
}

.diagram-box__header {
    position: relative;
    z-index: 3;
    display: flex;
    align-items: center;
    min-height: 30px;
    padding: 0 11px;
    border-bottom: 1px solid currentColor;
    background: hsl(0 0% 100% / 0.46);
    cursor: move;
    user-select: none;
    pointer-events: auto;
}

.dark-theme .diagram-box__header {
    background: hsl(250 25% 12% / 0.46);
}

.diagram-box__title {
    outline: none;
    white-space: nowrap;
}

.diagram-box__title:focus-visible {
    box-shadow: 0 0 0 2px var(--ui-focus);
}

.diagram-box__resize {
    position: absolute;
    z-index: 3;
    right: 5px;
    bottom: 5px;
    width: 13px;
    height: 13px;
    cursor: nwse-resize;
    pointer-events: auto;
    background: linear-gradient(135deg, transparent 42%, currentColor 43% 54%, transparent 55%);
}

.grid:not(.hidden) + .container .vertex.empty:not(.selected) .content:not(:hover) {
    border: var(--cell-hover) 2px solid;
}

/* We would like to animate the movement of vertices when moving them via the keyboard, but the
transitions are quite subtle, especially when interacting with the origin of the resizable grid, so
in the interest of time, I have decided to be satisfied with instantaneous movement for the timing
being. */
/* .ui.key-move .vertex, .ui.key-move .vertex .content {
    transition: left 0.1s, top 0.1s, width 0.1s, height 0.1s;
} */

.ui:not(.freeform).default .vertex .content {
    cursor: default;
}

/* This is so explicit because of the CSS specificity rules. */
.ui:not(.freeform).default .vertex:not(.selected):not(.source):not(.target) .content:hover,
.ui:not(.freeform).command .vertex:not(.selected):not(.source):not(.target) .content:hover {
    background: var(--cell-hover);
}

.ui:not(.freeform) .vertex.source .content {
    background: var(--cell-source);
}

.ui:not(.freeform) .vertex.target .content {
    background: var(--cell-target);
}

.ui:not(.freeform) .vertex.selected .content {
    background: var(--cell-selected);
}

.ui:not(.freeform) .vertex .content {
    border-radius: 16px;
}

.label {
    display: block;
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);

    text-align: center;
    font-family: inherit;
    font-size: 26px;
    white-space: nowrap;
    color: var(--cell-colour);
    fill: var(--cell-colour);

    pointer-events: none;
    user-select: none;
}

/* The overlay edge drawn while connecting cells. */
.overlay, .overlay .arrow-background, .overlay .arrow-background > *  {
    pointer-events: none !important;
}

/* The side panel */

.panel {
    position: fixed;
    z-index: 91;
    overflow-y: auto;

    font: 14px Arial, sans-serif;
    color: var(--ui-text);
}

.side.panel {
    width: calc(16px * 18);
    top: 16px;
    bottom: 16px;
    right: 16px;

    border-radius: 16px;

    transition: transform 0.1s;
}

.side.panel.hidden {
    /* We use `translateX` instead of `right` to avoid continually performing layout. */
    transform: translateX(calc(16px * 18 + 16px));
}

.ui.connect .panel {
    pointer-events: none;
}

.panel label, .panel button {
    user-select: none;
}

.global {
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    height: 46px;
    padding: 8px 12px;
    /* Display above the import/export pane. */
    z-index: 101;

    background: var(--ui-black);
    border-radius: 8px 8px 0 0;

    text-align: center;
    white-space: nowrap;
}

.panel > .wrapper {
    position: absolute;
    left: 0;
    width: 100%;
    padding: 8px 16px;

    background: var(--ui-black);
    border-radius: 16px;
}

.side label {
    display: block;
    margin-bottom: 8px;
}

.side:not(.nonloop) .nonloop {
    /* We choose to set the height to 0, rather than `display: none;`, since some element positions,
    e.g. keyboard hints, are based on size calculations, which are messed up if the element is not
    displayed. */
    height: 0;
    margin: 0;
    overflow: hidden;
}

.side.no-transition .nonloop .slider .thumb, .side.no-transition .loop .slider .thumb {
    transition: none;
}

.side:not(.loop) .loop {
    display: none;
}

label.inline {
    display: inline-block;
}

.label-input-container {
    position: fixed;
    /* This editor must sit above the canvas (z-index: 1), not merely be
       visible underneath it. */
    z-index: 92;
    left: 50%; height: 32px;
    /* Position 8px above the global panel. */
    bottom: calc(8px + 46px);
    width: 40%;
    min-width: 560px;
    transform: translateX(-50%);

    background: var(--ui-black);
    border-radius: 8px;

    color: var(--ui-white);

    transition: transform 0.1s;
}

.label-input-container.hidden {
    /* We use `translate` instead of `bottom` to avoid continually performing layout. */
    transform: translate(-50%, calc(16pt + 4px * 2 + 16px + 8px + 46px));
}

.ui.connect .label-input-container {
    pointer-events: none;
}

.ui.command .label-input-container {
    background: var(--ui-orange);
}

.label-input {
    width: 100%; height: 32px;
    padding: 4px 6px;
    padding-right: calc(24px + 6px);

    background: transparent;
    border: none;
    border-radius: 8px;
    outline: none;

    font-family: monospace;
    font-size: 16pt;
    color: inherit;
    text-align: center;

    transition: width 0.1s;
}

.label-input:hover:not(:disabled):not(:focus) {
    background: hsla(0, 0%, 15%, 0.9);
}

.label-input:focus {
    background: var(--ui-focus);

    color: black;
}

.input-mode {
    display: inline-block;
    vertical-align: bottom;
    width: 0px; height: 32px;
    line-height: 32px;
    overflow: hidden;

    border-radius: 8px 0 0 8px;

    font-size: 12pt;
    text-align: center;

    transition: width 0.1s;
}

.command .input-mode {
    width: 80px;
}

.command .label-input:focus {
    width: calc(100% - 80px);

    background: var(--ui-orange);
    /* We override this value to make `shadow-flash` the correct colour. This isn't really
    semantic naming. */
    --ui-blue: var(--ui-orange);
}

.label-input::selection {
    background-color: white;
}

.label-input-container .colour-indicator {
    position: absolute;
    top: 50%;
    right: 4px;
    width: 24px;
    transform: translateY(-50%);
    border-radius: 4px;
}

.command .label-input-container .colour-indicator {
    display: none;
}

.panel input[type="text"],
.pane input[type="text"],
.port .options input[type="number"] {
    padding: 2px 4px;

    background: var(--ui-background);
    border: var(--ui-border) solid 1px;
    border-radius: 2px;
    outline: none;

    font-size: inherit;
    font-family: monospace;
    color: var(--ui-white);
}

.panel input[type="text"]::placeholder,
.pane input[type="text"]::placeholder,
.port .options input[type="number"]::placeholder {
    color: hsla(0, 0%, 100%, 0.4);
}

.panel input[type="text"]:hover:not(:disabled):not(:focus),
.pane input[type="text"]:hover:not(:disabled):not(:focus),
.port .options input[type="number"]:hover:not(:disabled):not(:focus) {
    background: var(--ui-hover);
}

.panel input[type="text"]:focus,
.pane input[type="text"]:focus,
.port .options input[type="number"]:focus {
    background: var(--ui-focus);
    border-color: var(--ui-focus);

    color: hsl(0, 0%, 16%);
}

.panel input[type="text"]:focus::placeholder,
.pane input[type="text"]:focus::placeholder,
.port .options input[type="number"]:focus::placeholder {
    color: hsla(0, 0%, 0%, 0.4);
}

input[type="text"].flash {
    animation: shadow-flash 0.2s;
}

@keyframes shadow-flash {
    from {
        box-shadow: 0 0 0 4px var(--ui-blue);
    }

    to {
        box-shadow: 0 0 0 var(--ui-blue);
    }
}

.panel .options {
    position: relative;
    margin: 8px 0;

    text-align: center;
}

.panel input[type="radio"] {
    appearance: none;
    display: inline-block;
    width: 48px; height: 48px;

    background-color: var(--ui-background);
    background-repeat: no-repeat;
    background-position: center;
    /* We use stacked backgrounds for the background image, */
    /* to allow us to change the image directly via CSS. */
    background-size: 0%, auto;
    border: var(--ui-border) solid 1px;
    border-radius: 2px;
    outline: none;
}

.panel .large {
    white-space: nowrap;
}

.panel .large input[type="radio"] {
    width: 72px;
    height: 64px;
}

.panel .options.focused input[type="radio"]:not(:checked) {
    background-size: auto, 0%;
    background-color: var(--ui-white);
    border-color: var(--ui-focus);
}

.panel input.hidden, .panel div.hidden, .panel label.hidden {
    display: none;
}

.panel .centred {
    text-align: center;
}

.panel .vertical {
    display: inline-block;
    width: calc(100% - (20% + 4px) * 2);
    vertical-align: top;
}

.panel .vertical + .vertical:not(.centre) {
    margin-left: 4px;
}

.panel .short {
    width: 20%;
}

.panel .centre {
    position: relative;
    left: 50%;
    transform: translateX(-50%);
}

.panel .vertical input[type="radio"] {
    position: relative;
    display: block;
    width: 100%; height: 32px;
    left: 50%;
    transform: translateX(-50%);
    margin: 0; margin-top: -1px;

    border-radius: 0;
}

.panel .vertical input[type="radio"]:first-of-type {
    margin-top: 0;

    border-radius: 2px 2px 0 0;
}

.panel .vertical input[type="radio"]:not(.short):last-of-type {
    border-radius: 0 0 2px 2px;
}

.panel .vertical input[type="radio"].short {
    display: inline-block;
    width: 50%;
    left: 0;
    transform: none;
    /* To be honest, I'm not quite sure why the following is necessary, but it produces the right
    effect on the browsers I tested on. */
    margin-bottom: -3px;
}

.panel .vertical input[type="radio"].short.start-of-line + input[type="radio"].short {
    border-left-width: 0;
}

.panel .vertical input[type="radio"].short.start-of-line:nth-last-of-type(2) {
    border-bottom-left-radius: 2px;
}

.panel .vertical input[type="radio"].short:last-of-type {
    border-bottom-right-radius: 2px;
}

.panel .options:not(.focused) input[type="radio"]:hover:not(:disabled):not(:active):not(:checked) {
    background-color: var(--ui-hover);
}

.panel .options.focused input[type="radio"]:active:not(:disabled) {
    background-color: var(--ui-blue);
}

.panel input[type="radio"]:active:not(:disabled) {
    background-color: var(--ui-active);
}

.panel input[type="radio"]:checked:not(:disabled) {
    background-color: var(--ui-focus);
    background-size: auto, 0%;
    border-color: var(--ui-blue);
}

.panel label {
    position: relative;
}

/* A non-native range input, supporting multiple thumbs. */
div.slider {
    display: inline-block;
    vertical-align: middle;
    position: absolute;

    touch-action: none;
}

.linked-sliders {
    display: inline-block;
}

.panel div.slider {
    width: 55%;
    left: 56px;
}

.options div.slider {
    position: relative;
    width: 80px;
    margin: -14px 8px 0 4px;
}

.slider-values {
    display: inline-block;
    position: absolute;
    right: 0;

    pointer-events: none;
}

.options .slider-values {
    position: relative;
    width: 54px;
}

.slider-values .slider-value {
    display: inline-block;
    /* Try to keep each value fixed-width. This will technically be font-dependent, but it's simpler
       to pick a fixed width manually. */
    min-width: 24px;

    text-align: right;
}

.slider .thumb {
    display: inline-block;
    position: absolute;
    width: 20px;
    height: 20px;
    margin-top: -3px;
    top: 0;
    transform: translateX(-50%);
    z-index: 1;

    background: hsl(0, 0%, 10%);
    border: var(--ui-border) solid 1px;
    border-radius: 20px;

    transition: left 0.1s;

    pointer-events: none;
}

/* Disable transitions when the user is dragging the thumb. */
.thumb.active, .linked-sliders.linked.active .thumb {
    transition: none;
}

.slider.focused .thumb {
    background: var(--ui-white);
    border-color: var(--ui-blue);
}

.slider .thumb.focused, .slider.symmetric.focused .thumb {
    background: var(--ui-blue);
}

.slider:not(.disabled):not(.focused) .thumb.hover,
.slider.symmetric:not(.disabled):not(.focused):hover .thumb,
.linked-sliders.linked:hover .thumb
{
    background: hsl(0, 0%, 20%);
}

.slider:not(.disabled):not(.focused) .thumb.active,
.slider.symmetric:not(.disabled):not(.focused).active .thumb,
.linked-sliders.linked.active .thumb {
    background: hsl(0, 0%, 30%);
}

.slider .track {
    display: inline-block;
    vertical-align: middle;
    width: 100%;
    height: 14px;
    position: absolute;

    background: var(--ui-background);
    border: var(--ui-border) solid 1px;
    border-radius: 14px;
}

.slider.focused .track {
    background: var(--ui-white);
    border-color: var(--ui-blue);
}

.panel button, .pane button, .port button, select {
    position: relative;
    display: block;
    width: 100%; height: 30px;
    margin: 8px 0;

    background: transparent;
    border: var(--ui-border) solid 1px;
    border-radius: 2px;
    outline: none;

    font: inherit;
    color: var(--ui-white);
    text-align: center;
}

select {
    appearance: none;
    display: inline-block;
    width: auto;
    margin: 0;
    padding: 0 8pt;

    text-align: left;
}

.global button {
    display: inline-block;
    width: 72px;
    margin: 0 2px;
}

.global button.short {
    width: 56px;
}

.global button + label, select + label {
    margin-left: 12px;
}

.panel button:hover:not(:disabled):not(:active),
.pane button:hover:not(:disabled):not(:active),
.port button:hover:not(:disabled):not(:active) {
    background: var(--ui-hover);
}

.panel button:not(:disabled):not(:active):active,
.pane button:not(:disabled):not(:active):active,
.port button:not(:disabled):not(:active):active {
    background: var(--ui-active);
}

kbd.hint {
    position: absolute;
    min-width: 20px;
    padding: 2px 4px;

    background: var(--ui-orange);
    border-radius: 2px;

    color: var(--ui-black);
    text-align: center;

    pointer-events: none;
}

.colour-indicator {
    display: inline-block;
    vertical-align: middle;
    position: relative;
    width: 48px;
    height: 24px;

    border: var(--ui-border) solid 1px;
    border-radius: 2px;

    cursor: pointer;
}

/* Highlight the hints when their codes are (partially) typed. */
kbd.hint.focused, kbd.hint .focused {
    color: var(--ui-white);
}

.ui:not(.command).show-queue kbd.hint.queue {
    background: var(--ui-white);
    border: var(--ui-blue) 1px solid;

    color: var(--ui-blue);
}

.ui:not(.command).show-queue .cell.selected kbd.hint.queue {
    background: var(--ui-blue);

    color: var(--ui-white);
}

.ui:not(.command):not(.show-queue):not(.show-hints) kbd.hint.queue {
    display: none;
}

.ui:not(.show-hints) kbd.hint:not(.queue) {
    display: none;
}

kbd.hint.button, kbd.hint.input {
    left: 0; top: 0;
    transform: translate(-25%, -25%);
}

.label-input-container kbd.hint.colour {
    right: 0; top: 0;
    transform: translate(25%, -25%);
}

/* Hide the keyboard shortcut for changing colour when the user is typing, as it's irrelevant. We
   don't do this for every keyboard shortcut, but the one in the label input is particularly
   prominent. */
.label-input-container .label-input:focus ~ kbd.hint.colour {
    display: none;
}

kbd.hint.colour {
    transform: translate(-25%, -25%);
}

kbd.hint.slider {
    left: calc(56px + 55%); top: 0;
    transform: translate(-25%, -75%);
}

.ui.command .cell kbd.hint {
    display: block;
}

.vertex kbd.hint {
    left: 50%; top: 0;
    transform: translateX(-50%);
}

.arrow kbd {
    z-index: 10;
    transform: translate(-50%, -50%);
}

/* `.kbd-requires-focus` and `.triggers-focus` are mutually exclusive classes. The first is
intended to trigger the latter. */
.kbd-requires-focus:not(.focused) kbd.hint:not(.triggers-focus) {
    display: none;
}

.kbd-requires-focus:not(.next-to-focus) kbd.hint.triggers-focus {
    display: none;
}

/* We always display shortcuts for lists when they're focused. */
.kbd-requires-focus.focused kbd.hint {
    display: block;
}

/* Display a semicolon symbol for the usual keyboard shortcut symbol. This is not necessary when the
user has already pressed semicolon, placing us in command mode. */
.ui:not(.command) .cell kbd.hint::before {
    content: ";";
}

.ui.command kbd.hint:empty::after,
.cell kbd.hint:empty:not(.queue)::after,
.ui:not(.show-queue) kbd.hint:empty::after {
    content: attr(data-code);
}

/* When cells are queued, we can press Tab to select them. */
.ui:not(.command).show-queue .cell kbd.queue.hint::before {
    content: "⇥";
}

/* The colour picker */

.colour-panel {
    position: fixed;
    width: calc(16px * 20);
    padding: 8px 16px;
    padding-top: calc(192px + 16px * 2);
    max-height: calc(100% - (8px + 46px + 32px) * 2 - 16px * 2);
	overflow-y: auto;

    background: var(--ui-black);
    border-radius: 16px;

    font: 14px Arial, sans-serif;
    color: var(--ui-text);

    transition: opacity 0.2s;
}

.colour-panel.target-label {
    bottom: calc(8px + 46px + 32px + 8px);
    right: calc(50% - 20%);
}

/* The colour panel position when selecting a colour for an edge. */
.colour-panel.target-edge {
    top: 50%;
    right: calc(16px * 20);
    transform: translateY(-50%);
}

.colour-panel.hidden {
    opacity: 0;

    pointer-events: none;

    transition: opacity 0.1s;
}

.colour-wheel {
    position: absolute;
    left: 50%; top: 16px;
    transform: translateX(-50%);

    border-radius: 50%;
}

.colour-picker {
    position: absolute;
    width: 32px; height: 32px;
    transform: translate(-50%, -50%);

    background: var(--ui-black);
    border: var(--ui-black) 2px solid;
    border-radius: 50%;

    transition: background 0.1s, left 0.1s, top 0.1s;

    pointer-events: none;
}

.colour-panel.active .colour-picker, .colour-panel.active .thumb {
    transition: none;
}

.colour-panel > .wrapper {
    position: relative;
}

.colour-panel div.slider {
    width: 55%;
    left: 80px;
}

.colour-panel label {
    display: block;
    margin: 8px 0;
}

.palette {
    margin-top: -2px;
}

.palette label {
    margin: 4px 0;
}

.palette label .empty {
    margin-left: 4px;

	opacity: 0.6;
}

.palette .colour {
    display: inline-block;
    vertical-align: middle;
    width: 20px; height: 20px;
    margin: 2px 2px;

    border: var(--ui-border) solid 1px;
    border-radius: 2px;

	cursor: pointer;
}

.palette label .colour:first-of-type {
    margin-left: 4px;
}

/* The toolbar */

.toolbar {
    position: fixed;
    height: 60px;
    left: 50%; top: 16px;
    transform: translateX(-50%);
    z-index: 90;

    background: var(--ui-black);
    border-radius: 8px;

    color: var(--ui-white);
    white-space: nowrap;

    transition: opacity 0.1s;
}

/* Pan is intentionally a persistent tool, so keep the toolbar interactive and visible to let the
   user click the Hand button again. Other transient modes still suppress unrelated controls. */
.ui:not(.default):not(.modal):not(.pan) .toolbar {
    opacity: 0.2;

    pointer-events: none;
}

.toolbar .action {
    position: relative;
    vertical-align: middle;
    width: 56px; height: 60px;

    background: transparent;
    border: none;
    outline: none;

    font: inherit;
    text-align: center;
    font-size: 20px;
    color: inherit;
    white-space: nowrap;

    cursor: default;
    user-select: none;
}

.toolbar .action:first-of-type {
    border-radius: 8px 0 0 8px;
}

.toolbar .action:last-of-type {
    border-radius: 0 8px 8px 0;
}

.toolbar .action:hover {
    background: hsl(0, 0%, 28%);
}

.toolbar .action:not(:disabled):not(.dropdown):active {
    background: var(--ui-blue);
}

/* The hand tool is a persistent mode, not a momentary button. Keep it visibly latched until
   the user clicks it again, while the cursor communicates the corresponding canvas action. */
.toolbar .action.active:not(:disabled) {
    background: var(--ui-blue);
}

.toolbar .action.active .name,
.toolbar .action.active .shortcut {
    color: var(--ui-white);
}

.toolbar .action img {
    width: 28px;
    vertical-align: middle;
    pointer-events: none;
}

.toolbar .action:disabled {
    background: transparent;
}

.toolbar .action:disabled img {
    opacity: 0.6;
}

.toolbar .action:disabled .name, .toolbar .action:disabled .shortcut {
    color: hsl(0, 0%, 64%);
}

.toolbar .action.dropdown > .shortcut:empty::before {
    content: "▾";
}

.toolbar .action .name s {
    text-decoration-thickness: 2px;
}

.toolbar .action .subtoolbar {
    display: none;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);

    background: var(--ui-black);
    border-radius: 0 0 8px 8px;
}

.toolbar .action:not(:disabled):hover .subtoolbar {
    display: block;
}

.subtoolbar .action:first-of-type {
    border-radius: 0 0 0 8px;
}

.subtoolbar .action:last-of-type {
    border-radius: 0 0 8px 0;
}

/* Arrow creation offers two distinct gestures. Render this submenu as a short vertical menu so
   descriptive labels do not overlap their icons or each other. */
.toolbar .action .subtoolbar.arrow-menu,
.toolbar .action .subtoolbar.text-menu {
    min-width: 152px;
}

.toolbar .action .subtoolbar.arrow-menu .action,
.toolbar .action .subtoolbar.text-menu .action {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 152px;
    height: 42px;
    padding: 0 12px;
    text-align: left;
}

.toolbar .action .subtoolbar.arrow-menu .action:first-of-type,
.toolbar .action .subtoolbar.text-menu .action:first-of-type {
    border-radius: 0;
}

.toolbar .action .subtoolbar.arrow-menu .action:last-of-type,
.toolbar .action .subtoolbar.text-menu .action:last-of-type {
    border-radius: 0 0 8px 8px;
}

.toolbar .action .subtoolbar.arrow-menu .action .symbol,
.toolbar .action .subtoolbar.text-menu .action .symbol {
    display: flex;
    width: 28px;
    flex: 0 0 28px;
}

.toolbar .action .subtoolbar.arrow-menu .action .name,
.toolbar .action .subtoolbar.text-menu .action .name {
    position: static;
    transform: none;
    font-size: 12px;
    line-height: 1;
}

.toolbar .action .subtoolbar.arrow-menu .action .shortcut,
.toolbar .action .subtoolbar.text-menu .action .shortcut {
    display: none;
}

button.flash {
    animation: background-flash 0.4s ease-in;
}

@keyframes background-flash {
    from {
        background: var(--ui-blue);
    }
}

.toolbar .action .name, .toolbar .action .shortcut {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);

    font-size: 9.5px;
    line-height: 9.5px;
    color: hsl(0, 0%, 80%);
}

.toolbar .action .name {
    top: 6px;
}

.toolbar .action .shortcut {
    bottom: 6px;
}

/* Keep the complete toolbar and its dropdowns visible on narrower windows. A scrolling
   toolbar clips its menus vertically, so scale the control strip as one unit instead. */
@media (max-width: 1280px) {
    .toolbar {
        left: 50%;
        right: auto;
        transform: translateX(-50%) scale(0.9);
        transform-origin: top center;
        overflow: visible;
    }
}

/* The logo yields to the controls before the compact toolbar would overlap it. */
@media (max-width: 1120px) {
    #logo-link {
        display: none;
    }

    .toolbar {
        transform: translateX(-50%) scale(0.8);
    }
}

@media (max-width: 800px) {
    .toolbar {
        transform: translateX(-50%) scale(0.6);
    }
}

/* On phone-sized canvases the editor controls take priority over the decorative outbound logo. */
@media (max-width: 480px) {
    .toolbar {
        left: 50%;
        right: auto;
        transform: translateX(-50%) scale(0.38);
        transform-origin: top center;
        overflow: visible;
        top: 0;
        border-radius: 0;
    }

}

/* The welcome / keyboard shortcuts / about panes */

.pane {
    position: fixed;
    /* Display just below the toolbar. */
    left: 50%;
    top: calc(16px + 60px + 8px);
    transform: translateX(-50%);
    width: 50%;
    /* Display at most to just above the global panel. */
    max-height: calc(100% - (16px + 60px + 8px + 16pt + 4px * 2 + 16px + 8px));
    padding: 8pt 20pt;
    overflow-y: auto;
    z-index: 90;

    opacity: 1;
    background: var(--ui-black);
    border-radius: 16px;

    color: var(--ui-white);

    transition: opacity 0.2s, transform 0.2s;
}

.pane.hidden {
    pointer-events: none;

    transform: scale(0.75, 0.75) translateX(calc(-1 / 0.75 * 50%));

    opacity: 0;

    transition: opacity 0.1s, transform 0.1s;
}

.pane h1 {
    margin: 0;

    border-bottom: hsla(0, 0%, 100%, 0.1) 1px solid;
    padding-bottom: 6px;

    font-size: 18pt;
    font-weight: normal;
    text-align: center;
}

.pane h1 .right {
    margin-left: 16pt;
}

.pane h1 kbd {
    font-size: 12pt;
}

.pane kbd, .port kbd {
    display: inline-block;
    height: 16.5pt;
    vertical-align: middle;

    border: hsla(0, 0%, 100%, 0.4) 1px solid;
}

.pane kbd {
    background: hsl(0, 0%, 0%, 0.9);
}

.port kbd {
    background: hsl(0, 0%, 100%, 0.9);
}

.pane h2 {
    margin-bottom: 6pt;

    font-size: 14pt;
    font-weight: normal;
}

.pane table {
    width: 100%;

    border-spacing: 0;

    color: hsl(0, 0%, 76%);
    white-space: pre-line;
}

.pane table td {
    width: 50%;
    padding: 4pt 0;
}

.pane table tr:first-child td {
    padding-top: 0;
}

.pane table kbd {
    height: 14.5pt;

    font-size: 10pt;
    color: var(--ui-white);
}

.pane a {
    color: var(--ui-white);
}

.pane ul {
    margin: 0;
    padding: 0 20pt;
}

.pane p, .pane li {
    color: hsl(0, 0%, 76%);
}

.pane li {
    margin: 4pt 0;
}

.pane button {
    width: 50%;
    margin-left: auto;
    margin-right: auto;
    margin-top: 12pt;
}

.pane footer {
    margin-top: 8pt;
    padding-top: 8pt;

    border-top: hsla(0, 0%, 100%, 0.1) 1px solid;

    text-align: center;
}

/* Modal elements */

.port {
    position: fixed;
    width: 100%; height: 100%;
    left: 0; top: 0;
    z-index: 100;
    padding: 20px 24px;
    padding-bottom: calc(46px + 8px);
    overflow-y: auto;

    background: hsla(0, 0%, 10%, 0.8);

    backdrop-filter: blur(4px);
}

.port a {
    color: var(--ui-white);
}

.port .tip, .port div.warning, .port div.error {
    display: inline-block;
    width: 100%;
    padding: 8pt;
    margin-bottom: 8pt;

    border-radius: 2px;

    cursor: default;
}

.port .tip {
    background: var(--ui-black);

    color: var(--ui-white);
}

.port .tip > code {
    cursor: text;
}

.port .tip .update {
    padding: 0pt 4pt;
    padding-bottom: 1pt;
    margin-left: 8pt;

    background: var(--ui-blue);
    border-radius: 2px;

    color: var(--ui-black);
    font-variant: small-caps;
	font-size: 10pt;
}

.port .tip .update time {
    font-size: 8pt;
}

.port div.error {
    color: var(--ui-white);
    background: var(--ui-error);
}

.port div.warning {
    background: var(--ui-warning);
}

.port .hidden {
    display: none !important;
}

.port div.error ul, .port div.warning ul {
    margin: 4pt 0;

    list-style-type: circle;
}

.port.import div.error ul li:hover,
.port.import div.error ul li.highlight,
.port.import div.warning ul li:hover,
.port.import div.warning ul li.highlight {
    list-style-type: disc;
}

.port div.warning a {
    color: var(--ui-black);
}

.port .options {
    margin-bottom: 8pt;
    padding: 6pt 4pt;
    line-height: 18pt;

    background: var(--ui-black);
    border-radius: 2px;

    color: var(--ui-white);

    user-select: none;
}

.port .options label {
    position: relative;
    top: 1px;
    margin-right: 20pt;
}

.port .note {
    display: inline-block;
    width: 100%;
    padding: 8pt;
    margin-bottom: 8pt;

    background: var(--ui-blue);
    border-radius: 2px;

    color: var(--ui-black);

    user-select: none;
}

.port .note.hidden {
    display: none;
}

.port .options label kbd.hint {
    /* Leave some space for the checkbox itself. */
    transform: translate(-50%, -50%);
}

.port .options input[type="number"] {
    width: 64px;
    margin-right: -12pt;
}

.port .options select {
    margin-left: 8pt;
}

/* Disable the up/down buttons for `type="number"` inputs. */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    appearance: textfield;
}

label input[type="checkbox"] {
    margin-top: -1px;
    margin-right: 6pt;
    width: 12pt;
    height: 12pt;
    vertical-align: middle;

    -webkit-appearance: none;
    appearance: none;
    background: var(--ui-white);
    border-radius: 2px;
    outline: none;
}

label input[type="checkbox"]:hover {
    background: hsl(0, 0%, 80%);
}

label input[type="checkbox"]:checked {
    background: var(--ui-focus);

    text-align: center;
}

label input[type="checkbox"]:checked::before {
    content: "✓";

    color: var(--ui-black);
}

.port .code, .port div[contenteditable], .pane textarea {
    font: 16px monospace;
    color: var(--ui-white);

    white-space: pre-wrap;
    overflow-wrap: break-word;
    tab-size: 4;
    -moz-tab-size: 4;
}

.port div[contenteditable], .pane textarea {
    width: 100%;
    resize: none;
    padding: 6pt 8pt;

    background: hsla(0, 0%, 0%, 0.2);

    border: none;
    outline: none;
}

.port div[contenteditable] {
    height: 252pt;
    overflow-y: auto;
}

.pane textarea {
    height: 180pt;
}

/* We pad empty fragments so that we can see their highlight. */
.port .fragment:empty::before {
    content: " ";
}

.port .fragment.error {
    text-decoration: var(--ui-error) wavy underline;
}
.port .fragment.error:hover, .port .fragment.error.highlight {
    background: var(--ui-error);
}

.port .fragment.warning {
    text-decoration: var(--ui-warning) wavy underline;
}
.port .fragment.warning:hover, .port .fragment.highlight.warning {
    background: var(--ui-warning);

    color: var(--ui-black);
}

/* Arrows */

.arrow {
    color: var(--cell-colour);
}

.arrow > svg {
    position: absolute;
    left: 0;
    top: 0;
    overflow: visible;
    pointer-events: none;
}

.arrow.invalid, .arrow .invalid {
    display: none;
}

.arrow > svg:first-child {
    z-index: 1;
}

.arrow > svg:nth-child(2) {
    z-index: 4;
}

.arrow-background {
    color: hsla(0 0% 0%); /* --cell-selected, but with no alpha. */
    opacity: 0.2;
}

.dark-theme .arrow-background {
    color: hsl(250 30% 60%); /* --cell-selected, but with no alpha. */
    opacity: 0.5;
}

.arrow:not(.reconnecting) > .arrow-background > path,
.arrow:not(.reconnecting) > .arrow-background > circle {
    pointer-events: stroke;
}

.arrow:not(.target):not(.selected) > .arrow-background:not(:hover),
.ui.connect .cell:not(.target):not(.selected) > .arrow-background:hover,
.ui.pointer-move .cell:not(.selected) > .arrow-background:hover {
    opacity: 0;
}

.arrow:not(.target):not(.selected) > .arrow-background:hover {
    opacity: 0.1;
}

.dark-theme .arrow:not(.target):not(.selected) > .arrow-background:hover {
    opacity: 0.25;
}

.arrow-endpoint {
    display: none;
}

.ui.default .arrow:hover .arrow-endpoint, .ui.default .arrow.selected .arrow-endpoint {
    display: block;
}

/* Keep both reconnection anchors visible while an endpoint is being dragged. */
.ui.connect .arrow.reconnecting .arrow-endpoint {
    display: block;
}

.arrow-endpoint {
    position: absolute;
    z-index: 3;

    background: var(--ui-canvas);
}

/* Endpoint handles in the wiring editor are connection controls, not substitute nodes. A
   transparent ring preserves their hit area while leaving the real black node visible beneath. */
.ui.freeform .arrow-endpoint {
    box-sizing: border-box;
    background: transparent;
    /* Orange dashed rings mean “reconnect this arrow”, distinct from a selected node's blue halo. */
    border: var(--ui-orange) 2px dashed;
}

.ui.freeform .arrow-endpoint:hover {
    border-color: var(--ui-orange);
}

.arrow-endpoint:hover {
    border: var(--ui-blue) 2px solid;
}

/* Arrow labels */

.arrow .katex, .arrow .katex-error {
    font-size: 1em;
}

.typst-error {
  color: var(--ui-error);
}

/* Embedded view */

.ui.embedded .panel,
.ui.embedded .toolbar,
.ui.embedded .grid,
.ui.embedded .focus-point,
.ui.embedded .arrow:not(.target):not(.selected) > svg:first-child:hover {
    display: none;
}

.ui.embedded #logo-link {
    right: 12px; bottom: 12px;
    opacity: 0;
    transition: opacity 0.1s;
}

.ui.embedded:hover #logo-link {
    opacity: 0.2;
}

.ui.embedded #logo-link:hover {
    opacity: 0.4;
}

.ui.embedded #welcome-pane {
    display: none;
}

/* Some interface elements are only relevant to certain renderers. */
body:has(select[name="renderer"] > option[value="typst"]:checked) .katex-only,
body:has(select[name="renderer"] > option[value="katex"]:checked) .typst-only {
    display: none;
}

/* Animation for loading a slow renderer. */

.breathe {
    animation: breathe .5s ease-in-out infinite alternate;
}

@keyframes breathe {
    from {
        color: hsl(0, 0%, 0%);
    }
    to {
        color: hsl(0, 0%, 60%);
    }
}
