.page {
    display: block;
    position: relative;
    width: 190mm;
    height: 277mm;
}

@media print {
    * {
        background: transparent;
    }

    @page {
        size: 21cm 29.7cm;
        margin-top: 10mm;
        margin-right: 10mm;
        margin-left: 10mm;
        margin-bottom: 10mm;
    }

    .pageContent {
        width: 100%;
        height: 100%;
    }

    .pagebreak {
        display: block;
        page-break-before: always;
    }

    .no-print {
        display: none;
    }
}

/* settings-preview dialog used for building, tree */
.settings-preview-dialog {
    display: flex;
    flex-direction: row;
    height: 100%;
    overflow: hidden;
}

.settings-preview-dialog .settings {
    flex: 0 0 auto;
    overflow: auto;
    border: 1px solid var(--separator-color);
}

.settings-preview-dialog .preview {
    flex: 1 1 auto;
    min-width: 200px;
    min-height: 200px;
    position: relative;
    border: 1px solid var(--separator-color);
}

.texture-select {
    max-height: 80vh;
}

.texture-select fieldset {
    max-height: 70vh;
    padding-bottom: 10px;
    overflow: hidden;
}

.texture-select fieldset > div {
    height: 100%;
    overflow: auto;
}

.texture-list-entry {
    cursor: pointer;
}

body:not(.no-hover) .texture-list-entry:hover {
    background-color: #ff0;
}

.raily-free-tier-limit-warning {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    background: #fff1d6;
    border: 1px solid #f2b84d;
    color: #8a4b00;
    padding: 6px 10px;
    margin: 0 4px 4px 4px;
    border-radius: 4px;
}

.raily-free-tier-limit-warning-icon {
    color: #d88200;
}

.raily-free-tier-limit-warning-text {
    font-size: 13px;
    line-height: 1.3;
}

.left-tool-panel {
    display: inline-flex;
    flex-flow: row;
    overflow: hidden;
    width: min-content;
    border-right: 1px solid var(--separator-color);
}

.raily-left-tools {
    flex: 0 0 200px;
    min-width: 200px;
    display: flex;
    flex-flow: column;
    overflow-y: auto;
    overflow-x: hidden;
}

.vertical-left-toolbar {
    display: flex;
    flex-flow: column;
    width: calc(10 + var(--button-size));
    overflow-y: auto;
    overflow-x: hidden;
    border-left: 1px solid var(--separator-color);
}

.editor {
    flex: 1;
    position: relative;
    height: 100%;
    min-width: 50px;
    min-height: 50px;
}

.right-tool-panel {
    flex: 0 0 200px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    height: 100%;
    min-height: 0;
    border-left: 1px solid var(--separator-color);
}

.right-tool-panel > .tab-container {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
}

.right-tool-panel .tab-content {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
}

.right-tool-panel .train-tab {
    height: 100%;
    min-height: 0;
    overflow: hidden;
}

/* One body per line: what kind it is, what it is called, what it is doing. The list used to grow the
   selected entry in place, which moved everything under it and put the controls somewhere different
   depending on how far down you had clicked. */
.driveable-row {
    display: flex;
    align-items: center;
    gap: 6px;
    /* The padding has to come OUT of the width, not be added to it: these are 100% wide, so without
       this the row is wider than the panel and whatever sits at its right end hangs off the edge. */
    box-sizing: border-box;
    padding: 2px 6px;
    min-height: 26px;
    cursor: pointer;
    border-bottom: 1px solid var(--separator-color);
}

.driveable-row.selected {
    background: var(--primary-color, #5b6ee1);
}

body:not(.no-hover) .driveable-row:not(.selected):hover {
    background: var(--hover-background-color, rgba(255, 255, 255, 0.07));
}

.driveable-row .driveable-kind {
    flex: 0 0 auto;
    opacity: 0.85;
    pointer-events: none;
}

.driveable-row .driveable-name {
    flex: 1;
    width: auto;
    /* The row is a centring FlexPanel; a name reads down a list only if the names line up. */
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The live figure. Dimmed against the name, because it is the thing that changes and the name is the
   thing you are looking for. */
.driveable-row .driveable-value {
    flex: 0 0 auto;
    width: auto;
    font-size: 0.9em;
    color: var(--text-secondary-color);
    white-space: nowrap;
}

.driveable-row.selected .driveable-value {
    color: inherit;
}

/* The controls for whatever is selected, under the list and always in the same place.
   It may not grow until the list above it is gone: a panel showing one body's controls and none of the
   bodies is a panel you cannot get out of. So the cockpit takes what it needs and then scrolls, and
   the list keeps a floor of two rows whatever else is asking for room. */
.driveable-cockpit {
    flex: 0 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    /* The room for a scrollbar is kept whether one is showing or not. Content appearing down here --
       the switch ahead coming into range -- would otherwise summon one and narrow everything by its
       width, which resizes the speed slider under a thumb that is dragging it. */
    scrollbar-gutter: stable;
    border-top: 1px solid var(--dialog-border-color);
}

/* Pin left, name centre, bin right: the two things every kind offers, in the two places they are
   always in, so they do not move about from body to body. */
.driveable-cockpit-head {
    display: flex;
    align-items: center;
    gap: 6px;
    box-sizing: border-box;
    padding: 1px 6px;
    border-bottom: 1px solid var(--separator-color);
}

/* The card is as narrow as the panel, so every row is worth a few pixels: the dials are the only thing
   on it that has to be a particular size. */
.driveable-cockpit .cockpit-gauge {
    margin: 0;
}

.driveable-cockpit-head .driveable-cockpit-name {
    flex: 1;
    width: auto;
    text-align: center;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.right-tool-panel .train-repository-list {
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* A floor of about two rows, and it takes what the cockpit under it does not need. The two share one
   panel, and a panel showing one body's controls and none of the bodies is one you cannot get out
   of -- which is what a min-height of 0 here allowed. */
.right-tool-panel .train-panel .train-list {
    /* Shrinks but never grows: growing, it took room the cockpit then had to scroll for, and a list of
       three rows does not get better for being three hundred pixels tall.
       And it shrinks FIRST -- a far larger shrink factor than the cockpit's -- so a panel too short
       for both takes the room out of the list, down to its floor, before the controls start scrolling.
       Sharing the squeeze evenly left the last row of the cockpit cut off while four list rows sat
       above it in full. */
    flex: 0 100 auto;
    min-height: 52px;
    overflow-y: auto;
    overflow-x: hidden;
}

.right-tool-panel .train-panel {
    min-height: 0;
    overflow: hidden;
}

/* The app's own bottom bar. Named, because the cockpit bar has to sit ON it rather than over it, and
   two literals for the same edge would drift apart. border-box, so the border is inside the 45 and the
   content keeps the 44 it had. */
:root {
    --footer-height: 45px;
}

.footer {
    flex: 0 0 auto;
    background: var(--main-menu-bar-background-color);
    border-top: 1px solid var(--separator-color);
    box-sizing: border-box;
    height: var(--footer-height);
    line-height: 14px;
    font-size: 14px;
}


.short-context-help {
    background-color: var(--context-help-background-color);
    color: var(--context-help-foreground-color);
    height: 100%;
}

.status-footer-content {
    align-items: stretch;
    gap: 8px;
    /* Footer must host two-line blocks; let children fill the height. */
    height: 100%;
    /* Reserve space on the right edge for the floating (position:fixed)
       feedback button so the rightmost status info doesn't slide under it.
       left-content-right sets width:100%, so without border-box this padding is
       added to that width instead of taken out of it -- the box grows past the
       footer and the reservation buys nothing. */
    box-sizing: border-box;
    padding-right: 8em;
}

.status-footer-content .left {
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.status-footer-content .content {
    /* Spacer flexes to push the right zone to the edge. */
    min-width: 0;
}

/* Center zone: keeps the working-task pill centred in the footer. */
.status-footer-center {
    align-items: center;
    justify-content: center;
    min-width: 0;
}

.status-footer-right {
    align-items: center;
    gap: 8px;
}

/* Left zone: name (top) / modified (bottom) when idle; context help when editing. */
.name-modified-panel {
    justify-content: center;
    gap: 0;
}

.name-modified-panel label {
    /* Make the name look like a heading line. */
    font-size: 14px;
    font-weight: 700;
    line-height: 18px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 22em;
}

.name-modified-panel .name-row {
    align-items: center;
    gap: 4px;
}

.name-modified-panel label.renamable {
    cursor: pointer;
}

/* The rename button appears on hover, the way the file dialogs offer their per-row actions — and on
   keyboard focus, which is the only way to reach it without a pointer.
   Hidden by opacity, not by visibility or display: those two take the button out of the tab order, so
   the focus rule below could never fire and there would be no keyboard path to renaming at all. It
   keeps its box either way, so the name does not shift sideways when it appears, and pointer-events
   are off while invisible so the gap beside the name is not a click target nobody can see. */
.name-modified-panel .rename-plan {
    opacity: 0;
    pointer-events: none;
    flex: 0 0 auto;
}

.name-modified-panel .name-row:hover .rename-plan,
.name-modified-panel .name-row:focus-within .rename-plan {
    opacity: 1;
    pointer-events: auto;
}

.name-modified-panel > .modified {
    font-size: 11px;
    line-height: 14px;
    color: var(--context-help-foreground-color);
}

/* The save badge, and the disk and word that pulse beside it when a backup lands. They keep their
   place at zero opacity, so the badge does not shift sideways each time they appear. */
.name-modified-panel .save-row {
    align-items: center;
    gap: 6px;
}

.backup-flash {
    opacity: 0;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    line-height: 14px;
    white-space: nowrap;
}

.backup-flash.active {
    animation: backup-pulse 1s ease-in-out 2;
}

@keyframes backup-pulse {
    0%, 100% { opacity: 0; }
    50% { opacity: 0.75; }
}

/* Save-state badge: a plain colour-coded pill so the working state is obvious
   at a glance — orange when there are unsaved changes, blue once saved. */
.save-state {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    line-height: 14px;
    white-space: nowrap;
    color: #fff;
}

/* Dark text on the amber, not white: white on this orange is 2.7:1, which is under the 4.5:1 that
   11px text needs to be read at a glance. Near-black brown keeps the badge amber and reaches 6.2:1. */
.save-state.modified {
    background: #e8842f;
    color: #2b1a00;
}

.save-state.saved {
    background: #3d6fd6;
}

/* Context help (shown instead of name/modified while editing). Scoped to the footer, which is what
   it is for: unscoped, `.top-content-bottom.left > div` is every child of every vertical panel that
   happens to be on the left of something, and it centred the names in the texture picker's list. */
.status-footer-content .top-content-bottom.left > div,
.status-footer-content .left .left-content-right {
    align-items: center;
}

/* Position info: vertical — coordinates on top, element-info string below. */
.position-info {
    width: 14em;
    justify-content: center;
}

.position-info-coord {
    font-size: 12px;
    line-height: 16px;
    white-space: nowrap;
    color: var(--context-help-foreground-color);
}

.position-info-element {
    font-size: 11px;
    line-height: 14px;
    white-space: nowrap;
    color: var(--context-help-foreground-color);
    opacity: 0.8;
}

/* Working-task pill: prominent so pending background work (CSG, meshing …) is
   obvious. Accent background with a white wave animation. */
.work-status {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 12px 3px 10px;
    border-radius: 14px;
    background: #3d6fd6;
    color: #fff;
    box-shadow: 0 0 8px rgba(61, 111, 214, 0.6);
    width: auto;
}

.work-status > :first-child {
    flex-shrink: 0;
}

.work-status-text {
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
    color: #fff;
}

.floating-feedback {
    position: fixed;
    /* Above dialogs (10000) and teaching bubbles (10001) so the button stays
       visible and is captured in feedback screenshots. */
    z-index: 10002;
    bottom: 6px;
    right: 10px;
    cursor: pointer;
    font-size: 12px;
    color: var(--link-color);
    text-decoration: underline;
    white-space: nowrap;
    background: rgba(0, 0, 0, 0.25);
    padding: 4px 8px;
    border-radius: 4px;
}

body:not(.no-hover) .floating-feedback:hover {
    opacity: 0.7;
}

.expand-handle {
    display: flex;
    height: 27px;
    width: 27px;
    bottom: 30px;
    background-color: rgba(0, 0, 255, 0.55);
    align-content: center;
    justify-content: center;
    position: absolute;
    overflow: hidden;
    cursor: pointer;
}

.expand-handle > div {
    color: white;
}

.expand-handle.left {
    right: 0;
}

.expand-handle.right {
    left: 0;
}

@media screen and (max-width: 600px) {
    #tools {
        display: none;
    }
}

#shortcuts {
    flex: 0 0 auto;
}

body:not(.no-hover) .scene-graph-entry-header:hover {
    background-color: var(--default-hover-background-color);
    color: var(--default-hover-foreground-color);
    cursor: pointer;
}

.hintPanelWrap {
    left: 50%;
    top: 10%;
    position: absolute;
}

.hintPanel {
    left: -50%;
    padding: 8px;
    border: 1px solid #888;
    opacity: 0.5;
    background-color: #fff;
    border-radius: 8px;
    position: relative;
}

.controls {
    position: absolute;
    top: 20px;
    right: 20px;
    border: 1px solid var(--separator-color);
    background-color: color-mix(in srgb, var(--panel-background-color) 68%, transparent);
    border-radius: 5px;
}

.view-mode-overlay {
    display: flex;
    flex-direction: column;
}

.view-mode-button-row {
    display: flex;
}

/* The driving sticks straddle the bottom of the plan, one under each thumb, with the middle left
   clear: a stick is reached without looking, and a tablet is held by its lower corners. `none` on the
   row rather than on each stick, so the gap between them stays the plan's and passes clicks through. */
.riding-sticks {
    position: absolute;
    left: 0;
    right: 0;
    /* The clearance is written by updateRidingStickClearance: it is how far the driving bar reaches up
       into the plan, on the windows where the bar is too wide to sit between the sticks. */
    bottom: calc(12px + var(--riding-sticks-clearance, 0px));
    display: flex;
    justify-content: space-between;
    padding: 0 14px;
    pointer-events: none;
}

.riding-sticks .joystick {
    pointer-events: auto;
}

.view-mode-release-info {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border-top: 1px solid var(--separator-color);
    padding: 3px 6px 4px;
}

.view-mode-release-tag {
    border-radius: 999px;
    font-size: 11px;
    padding: 1px 7px;
    font-weight: 700;
    color: #fff;
}

.view-mode-release-tag.alpha {
    background-color: #c62828;
}

.view-mode-release-tag.beta {
    background-color: #1565c0;
}

.view-mode-release-info-icon {
    opacity: 0.85;
}

.drag-image {
    position: absolute;
    pointer-events: none;
    z-index: 1000;
}

.bubble {
    position: absolute;
    background-color: var(--dialog-background-color);
    border: 2px solid var(--separator-color);
    color: var(--default-foreground-color);
    border-radius: 5px;
    box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;
    display: inline-block;
    padding: 18px 18px;
    vertical-align: top;
    left: 100px;
    top: 100px;
    z-index: 10000;
}

.bubble.bottom:before {
    content: " ";
    position: absolute;
    box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;
    transform: rotate(45deg);
    bottom: -10.5px;
    left: 37px;
    width: 17px;
    height: 17px;
    background-color: var(--dialog-background-color);
    border-width: 2px;
    border-style: solid;
    border-color: transparent var(--separator-color) var(--separator-color) transparent;
}

.bubble.top:before {
    content: " ";
    position: absolute;
    transform: rotate(-135deg);
    top: -10.5px;
    left: 37px;
    width: 17px;
    height: 17px;
    background-color: var(--dialog-background-color);
    border-width: 2px;
    border-style: solid;
    border-color: transparent var(--separator-color) var(--separator-color) transparent;
}

.repository-list-entry {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: row;
    border: 1px solid var(--separator-color);
    /* A drag down the list would be the browser's to scroll with, but the entry's own touchstart
       handler cancels it and starts a drag-out at once (object-repository-list.ts). */
    touch-action: pan-y;
}

/* The picture is rendered at the panel's own width; a panel dragged narrower scales it down rather
   than cropping it. */
.repository-list-entry img {
    max-width: 100%;
    height: auto;
}

.element-name-title {
    font-weight: 700;
}

.strip-selector {
    height: auto;
    max-height: 10em;
    min-width: 18em;
    overflow-y: auto;
}

/* Train composition editor (instrument panel): horizontal strip of wagon cards and the buttons between them. */
.train-composition-panel {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
}

.train-wagon-card {
    border: 1px solid var(--dialog-border-color);
    border-radius: 4px;
    padding: 6px 8px;
    min-width: 96px;
    background: var(--panel-background-color);
    align-items: center;
    flex-shrink: 0;
    gap: 4px;
}

.train-card-title {
    font-weight: 700;
    white-space: nowrap;
}

.train-card-info {
    font-size: 10px;
    color: var(--disabled-foreground-color);
    white-space: nowrap;
}

.train-card-row {
    gap: 4px;
    align-items: center;
}

/* Train control panel: the switch-ahead widget and the compact speed input. */
.train-panel .train-switch-button {
    width: 52px;
    height: 52px;
    flex-shrink: 0;
}

/* The height a switch schematic needs, when there is one. Hidden while there is not: see
   populateSwitchRow for why it no longer has to hold the space open. */
.train-panel .train-switch-row {
    height: 56px;
    flex-shrink: 0;
    justify-content: center;
}


/* The driving controls along the bottom while riding a train, fullscreen or windowed. Above the
   footer rather than across it: windowed, the plan's name and its saved state are read while driving,
   and even in fullscreen the footer is still drawn. */
.cockpit-bar {
    position: fixed;
    /* Over the PLAN, not the window: see updateCockpitBarPlacement. Falls back to the window's middle
       before anything has measured, which is what it used to do always. */
    left: var(--cockpit-bar-centre, 50%);
    bottom: var(--footer-height);
    transform: translateX(-50%);
    align-items: center;
    gap: 12px;
    padding: 6px 16px 3px 16px;
    background: var(--panel-background-color);
    border: 1px solid var(--dialog-border-color);
    border-bottom: none;
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.35);
    z-index: 500;
    width: auto;
}

/* The gauge and the readout hold their size like the buttons beside them. Left shrinkable while
   everything else refuses, they are what a bar too narrow for its contents takes the difference out
   of -- and the gauge went to zero width before anything started scrolling, so the one part that is
   read at a glance was the first to disappear. */
.cockpit-bar .cockpit-gauge,
.cockpit-bar .cockpit-speed-digits {
    flex-shrink: 0;
}

.cockpit-bar .cockpit-speed-digits {
    font-size: 22px;
    font-weight: 700;
    min-width: 110px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.cockpit-bar .cockpit-reverse {
    font-size: 14px;
    font-weight: 700;
    color: var(--primary-color, #e33);
}

.cockpit-bar .cockpit-switch-button,
.cockpit-bar .cockpit-stop-button {
    width: 62px;
    height: 62px;
    flex-shrink: 0;
}

/* Not one of the driving controls, and sized so it cannot be mistaken for one: a 72 px X beside a
   72 px stop is an invitation to press the wrong one. In the corner, where a window's close is. */
.cockpit-bar .cockpit-dismiss-button {
    flex-shrink: 0;
    align-self: flex-start;
    opacity: 0.65;
}

body:not(.no-hover) .cockpit-bar .cockpit-dismiss-button:hover {
    opacity: 1;
}

/* The two halves of one question, so they sit closer to each other than to whatever is beside them:
   a gap like the rest would read as two unrelated buttons rather than a choice between two. Shared by
   the cockpit bar and the train panel, which is why it is not scoped to either. */
.drive-mode-pair {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
    /* As wide as the two buttons and no wider: full width, it filled the row it sits in and left the
       row's centring nothing to centre. */
    width: auto;
}

/* The simulation clock stacks its date over its time (see makeSimClockDisplay): a toolbar's horizontal
   space is shared with every button on it, and a date is nearly twice as wide as the time it qualifies. */
.sim-clock-display {
    white-space: pre-line;
    line-height: 1.1;
    text-align: center;
}

/* Trader information, shown in the status bar's centre while nothing is being
   worked on. Deliberately quiet: it has to be reachable, not noticed. */
/* One line, always. The links row is a flex item that would otherwise take the
   full width and push the close button onto a second line -- which made the bar
   taller than the footer and left the text sitting above the pointer position
   instead of beside it. Narrow screens get the compact form, not a wrap. */
.raily-legal-notice {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 12px;
    font-size: 12px;
    line-height: 16px;
    opacity: 0.65;
}

body:not(.no-hover) .raily-legal-notice:hover {
    opacity: 1;
}

.raily-legal-notice-full {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    flex: 0 1 auto;
    min-width: 0;
    /* Without this the nowrap row keeps its full width and simply paints past
       the container -- straight over the close button sitting after it. */
    overflow: hidden;
    gap: 12px;
}

/* basic.css sets "label { font-size: smaller }", which shrank these to ~9px
   against the 11px around them. Stated outright, and matched to the pointer
   position across the footer so the two read as one row. */
.raily-legal-notice-link,
.raily-legal-notice-full > label {
    font-size: 12px;
    line-height: 16px;
}

.raily-legal-notice-link {
    flex: 0 0 auto;
    width: auto;
    white-space: nowrap;
    text-decoration: underline;
    cursor: pointer;
}

/* One entry instead of a sentence and four links once the footer is too narrow
   to hold them on one line. The threshold is where the full form starts to
   wrap, not a device class. */
.raily-legal-notice-compact {
    display: none;
}

@media (max-width: 900px) {
    .raily-legal-notice-full {
        display: none;
    }

    .raily-legal-notice-compact {
        display: inline;
    }
}

/* The dialog is the document itself, so it gets room to be read. */
.raily-legal-dialog {
    width: 100%;
    height: 100%;
    min-height: 60vh;
}

/* Never shrinks and never gets overlapped: it is the only way to dismiss the
   bar, so it keeps its box whatever the links do. */
.raily-legal-notice-close {
    flex: 0 0 auto;
    transform: scale(0.7);
    z-index: 1;
}

/*
 * The driving bar in a window narrower than it is.
 *
 * Every part of it is a fixed size -- a 150px gauge, a 110px readout at 22px, and 72px buttons with
 * 20px between them -- which comes to about 610px at rest, 700 once a switch is ahead and nearly 900
 * while derailed, when both respawn buttons appear. It is centred with translateX(-50%), so below its
 * own width it does not merely crowd: it hangs off BOTH edges, and the controls at the ends are the
 * first to go.
 *
 * So it shrinks in two steps, on the widths the rest of the app already turns at. Everything keeps its
 * proportions -- a gauge is read by where the needle points, not by how big it is -- and the touch
 * targets are the last thing given up: 56px at a tablet's width and 44px on a phone, which is the
 * smallest a finger should be asked to hit.
 */
/* Middle step. Set from the PLAN's width rather than the window's -- see updateCockpitBarPlacement:
   the bar lies over the plan, and with both tool panels open the plan is a few hundred pixels
   narrower than the window it was being measured against. */
.cockpit-bar.narrow {
    gap: 10px;
    padding: 6px 12px 3px 12px;
}

.cockpit-bar.narrow .cockpit-gauge {
    width: 112px;
    height: 74px;
}

.cockpit-bar.narrow .cockpit-speed-digits {
    font-size: 18px;
    min-width: 84px;
}

.cockpit-bar.narrow .cockpit-switch-button,
.cockpit-bar.narrow .cockpit-stop-button {
    width: 56px;
    height: 56px;
}

/* The last resort, at the width where the bar no longer fits the plan at all. */
.cockpit-bar.tight {
    gap: 6px;
    padding: 4px 8px 2px 8px;
    /* It stops being centred and becomes the full width it has, so what overflows can be reached by
       scrolling it rather than being off-screen on both sides at once. */
    left: 0;
    transform: none;
    width: 100%;
    max-width: 100%;
    /* With the default content-box the padding is added to the 100%, which puts the bar back over the
       edge it was just brought inside. */
    box-sizing: border-box;
    overflow-x: auto;
    border-radius: 0;
    justify-content: flex-start;
}

.cockpit-bar.tight .cockpit-gauge {
    width: 84px;
    height: 55px;
}

.cockpit-bar.tight .cockpit-speed-digits {
    font-size: 15px;
    min-width: 64px;
}

.cockpit-bar.tight .cockpit-switch-button,
.cockpit-bar.tight .cockpit-stop-button {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
}

/* Sits at the end of the warning strip, so the sentence reads first and the
   offer is where the eye already is. */
.raily-free-tier-limit-warning-recheck,
.raily-free-tier-limit-warning-buy {
    flex: 0 0 auto;
    margin-left: auto;
}

/* The two buttons sit together at the right edge; only the first of them takes the free space. */
.raily-free-tier-limit-warning-recheck + .raily-free-tier-limit-warning-buy {
    margin-left: 0;
}

/* The unlicensed mark. Bottom-right, above the footer strip and clear of the
   feedback link, faint enough to read past while planning. It is meant to be
   present, not consulted. */
.raily-watermark {
    /* In the plan's box, like the view buttons and the riding sticks. */
    position: absolute;
    /* Below .eni-modal (100), which is where dialogs actually live -- the
       comment on .floating-feedback claiming 10000 does not describe them. The
       mark belongs on the plan, so anything the user opened covers it. */
    z-index: 50;
    right: 12px;
    bottom: 12px;
    /* FlexPanel.vertical brings top-content-bottom, which is 100% x 100%.
       Absolute positioning turns that into a box the size of the plan that
       happens to look right only because the text sits at its top. */
    width: max-content;
    height: auto;
    max-width: 15em;
    padding: 5px 9px;
    border-radius: 4px;
    /* Light theme: a pale wash over the plan, dark text. Stated rather than
       inherited, because the mark sits on the drawing and not on a panel, so
       the panel variables are the wrong reference. */
    background: rgba(255, 255, 255, 0.62);
    color: #303030;
    cursor: pointer;
    opacity: 0.55;
    transition: opacity 0.4s ease;
    pointer-events: auto;
}

/* On a dark plan the same wash inverts; a translucent black box on a dark
   background was legible but muddy. */
@media (prefers-color-scheme: dark) {
    .raily-watermark {
        background: rgba(20, 20, 20, 0.62);
        color: #e6e6e6;
    }
}

:root[data-force-theme="dark"] .raily-watermark {
    background: rgba(20, 20, 20, 0.62);
    color: #e6e6e6;
}

:root[data-force-theme="light"] .raily-watermark {
    background: rgba(255, 255, 255, 0.62);
    color: #303030;
}

/* The look stick takes the corner while a body is ridden: 132px of stick over
   the 12px it stands off the edge, and a little air. It goes up with the stick,
   which the driving bar can push higher still. */
.riding-sticks-shown .raily-watermark {
    bottom: calc(160px + var(--riding-sticks-clearance, 0px));
}

body:not(.no-hover) .raily-watermark:hover {
    opacity: 0.95;
}

.raily-watermark-title {
    font-size: 12px;
    line-height: 16px;
    font-weight: 600;
    white-space: nowrap;
}

.raily-watermark-usage {
    font-size: 11px;
    line-height: 14px;
}

/* One slow swell every half hour. Opacity only: moving or resizing it would
   pull the eye off the plan, which is the thing the user is actually doing. */
@keyframes raily-watermark-pulse {
    0%   { opacity: 0.55; }
    50%  { opacity: 1; }
    100% { opacity: 0.55; }
}

.raily-watermark-pulse {
    animation: raily-watermark-pulse 4s ease-in-out;
}

/* No animation for anyone who has asked the system for less motion. */
@media (prefers-reduced-motion: reduce) {
    .raily-watermark-pulse {
        animation: none;
    }
}

/* The mesh-cache trade at the foot of the save dialog. Quiet, and absent
   entirely on a plan where it would save nothing worth mentioning. */
.raily-save-cache {
    gap: 2px;
    padding: 6px 4px 2px 4px;
}

.raily-save-cache-detail {
    font-size: 12px;
    opacity: 0.75;
    padding-left: 26px;
}

/* Qualifying notes in the buy dialog: what a subscription leaves behind, and
   what a one-off purchase does not cover. Quieter than the offer itself, but
   deliberately not hidden. */
.raily-buy-note {
    font-size: 12px;
    line-height: 1.35;
    opacity: 0.8;
    padding: 2px 0 6px 0;
}

/* Scrollbars.
 *
 * Three platforms, three defaults, because nothing here ever asked for anything: macOS overlays them
 * and hides them until they are used, Linux usually does the same, and Windows draws the classic
 * Chromium ones -- permanently, and wide enough to be the loudest thing in a narrow panel.
 *
 * Styling is the portable half of the answer. Every engine the app ships on understands these
 * pseudo-elements (WebView2, WKWebView, WebKitGTK), so one rule quietens all three. It does narrow
 * the gutter -- Chromium reserves 15px by default, this reserves 10 -- so a panel gains five pixels
 * of content width. That is a layout change, but a bounded one: where these rules apply the scrollbar
 * still reserves space, so the dialog and popup-menu code that measures itself against a gutter still
 * finds one — and where they do not, it measures a gutter of zero, which is equally true.
 *
 * Deliberately not scrollbar-width/scrollbar-color: from Chromium 121 those take precedence wherever
 * both are set, so mixing the two forms would silently discard everything below.
 *
 * NOT on macOS, which is the one platform that had already solved this: it overlays its scrollbars
 * and hides them until they are used, so styling one there takes back a gutter the system had given
 * up and makes the app the loud one. `data-platform` is stamped on the root by setPlatform().
 */
:root:not([data-platform="mac"]) ::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

:root:not([data-platform="mac"]) ::-webkit-scrollbar-track,
:root:not([data-platform="mac"]) ::-webkit-scrollbar-corner {
    background: transparent;
}

/* The transparent border is what makes it a slim bar with air around it rather than a block filling
   the gutter; background-clip keeps the colour inside that border. */
:root:not([data-platform="mac"]) ::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.28);
    background-clip: padding-box;
    border: 2px solid transparent;
    border-radius: 5px;
}

:root:not([data-platform="mac"]) ::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.45);
    background-clip: padding-box;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-platform="mac"]) ::-webkit-scrollbar-thumb {
        background: rgba(255, 255, 255, 0.26);
        background-clip: padding-box;
    }

    :root:not([data-platform="mac"]) ::-webkit-scrollbar-thumb:hover {
        background: rgba(255, 255, 255, 0.42);
        background-clip: padding-box;
    }
}

/* The explicit theme wins over the system one, as elsewhere in this file. */
:root:not([data-platform="mac"])[data-force-theme="dark"] ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.26);
    background-clip: padding-box;
}

:root:not([data-platform="mac"])[data-force-theme="dark"] ::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.42);
    background-clip: padding-box;
}

:root:not([data-platform="mac"])[data-force-theme="light"] ::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.28);
    background-clip: padding-box;
}

:root:not([data-platform="mac"])[data-force-theme="light"] ::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.45);
    background-clip: padding-box;
}

.rail-lines-bracket {
    border: 1.5px solid var(--separator-color);
    border-left: 0;
    width: 6px;
}

.rail-lines-color {
    align-self: center;
}
