/* border-box everywhere: without this, elements that set both an explicit height and padding
   (e.g. .navbar a / .filestatus a below - height: 3.5rem plus 14px top/bottom padding) render
   taller than their declared height (content-box adds padding on top of it), overflowing their
   grid row and producing a few pixels of page-wide vertical scroll that shouldn't be there. */
*, *::before, *::after {
	box-sizing: border-box;
}

/* Single accent color for anything diagnostic/technical-configuration-flavored across the app -
   the Simulate Data button/banner and the Settings panel (see "Settings panel identity" further
   down) both draw from this instead of each picking their own orange. Monospace stack is a
   deliberate system-font choice (no webfont import) for the same reason: this tool needs to keep
   working from a bench laptop with no guaranteed internet, and Cascadia Code/Consolas/SF Mono/
   Menlo already cover Windows and Mac with a genuinely technical, instrument-panel character. */
:root {
	--accent: #b35c00;
	--accent-dark: #8a4700;
	--accent-light: #d97300;
	--mono: ui-monospace, "Cascadia Code", Consolas, "SFMono-Regular", Menlo, monospace;
	--rule: #e8e1d6;

	/* Main-window "instrument panel" chrome (sidebar/filestatus/chart area - see .side, .gauge-*,
	   .filestatus, .main below) - deliberately plain charcoal/near-white, no copper anywhere here -
	   that stays scoped to Simulate Data/Settings only, per instruction. Dark is the default;
	   :root[data-theme="light"] below overrides these same tokens for light mode (toggled via
	   toggleTheme()/applyTheme() in shared.js, see "Light/dark mode" in CLAUDE.md) - every rule
	   using them re-themes automatically, no rule needs its own light-mode variant.
	   --panel-bg is the sidebar/filestatus chrome; --panel-bg-raised is the chart canvas, one
	   step lighter (dark mode) or one step whiter (light mode) for a little visual separation
	   between "chrome" and "content" without a hard border. --ok/--warn are semantic
	   (signal detected/not), not decorative accents - they get their own light-mode values too,
	   since the dark-mode green/red would wash out against a light ground. */
	--panel-bg: #121417;
	--panel-bg-raised: #1b1e22;
	--panel-ink: #f3f1ec;
	--panel-ink-dim: #83878c;
	--panel-ink-faint: #55585d;
	--panel-rule: #292c31;
	--ok: #5fc98a;
	--warn: #e2685c;

	/* Overridden inline on <html> by applyChartSettings() (radlogger.js) from the "Gauge text
	   size" Settings field - default here covers the page before that first runs / LogViewer.html
	   (no gauges, never sets this). */
	--gauge-scale: 1;
}

:root[data-theme="light"] {
	--panel-bg: #f3f1ec;
	--panel-bg-raised: #fbfaf7;
	--panel-ink: #17191c;
	--panel-ink-dim: #6b6f74;
	--panel-ink-faint: #9a9ea3;
	--panel-rule: #e0dbd0;
	--ok: #2f9e63;
	--warn: #c94a3d;
}

html, body, .viewcontainer  {

	width: 100%;
    height: 100vh;
	padding:0;
	margin:0;
	font-family: "Segoe UI", Helvetica, sans-serif;
	margin: 0;
}

/* Belt-and-suspenders against page-level scroll: html/body are sized to exactly 100vh above, and
   everything inside is now constrained to fit that (see .main/.graphdiv/.side's min-height: 0
   below) - this just makes that guarantee explicit rather than relying on every descendant never
   overflowing by a stray pixel. */
html, body {
	overflow: hidden;
}

/* filestatus's own grid row was removed 2026-08-04 - its content (connection status, data folder,
   the simulated-data banner) moved into .footer's left side instead (.footer-status below), so it
   no longer needs a dedicated row.

   footer's row briefly grew from 2rem to 3.5rem the same day, to fit .footer wrapping onto two
   lines when the simulated-data banner's full text didn't fit next to connection status/data
   folder/copyright at typical window widths (see the flex-wrap note on .footer). Reverted back to
   2rem once the banner's text itself was shortened instead ("SIMULATED DATA (<rate> Hz raw,
   <channel-mode>) — not connected to hardware" → "Simulated Data — not connected to hardware",
   startSimulation() in radlogger.js) - fixing the actual cause (too much text competing for one
   line) rather than just reserving more room for it. flex-wrap stays on .footer as a fallback for
   genuinely narrow windows, but no longer triggers at normal widths. */
.viewcontainer {
    display: grid;
    grid-template-columns: 22rem 1fr;
    grid-template-rows: 3.5rem 1fr 2rem;
    grid-template-areas:
        "header header"
        "sidebar content"
        "footer footer"
		;
}
.container-fluid {
	font-size:10pt;
	padding-left: 10px;
	overflow:auto;
	margin: 0;
}

	
/* https://stackoverflow.com/questions/47882924/preventing-double-borders-in-css-grid */
.grid-container, .grid-status ,.grid-label{
	display: grid;
	grid-template-columns: auto;
	padding-bottom: 10px;
	margin: 20px;
}
.grid-item {
	background-color: rgba(255, 255, 255, 0.7);
	border: 1px solid rgba(0, 0, 0, 0.8);
	padding: 2px;
	font-size: 11px;
	margin:0 -1px -1px 0; 
	text-align: center;
}

.grid-header {
	background-color: rgba(255, 255, 255, 0.9);
	border: 1px solid rgba(0, 0, 0, 0.8);
	padding: 2px;
	font-size: 11px;
	margin:0 -1px -1px 0; 
	text-align: center;
}



ul, #basetable, #comptable, #difftable ,pre{
	padding: 10px;
	list-style-type: none;
		  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Old versions of Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome, Edge, Opera and Firefox */
}

#viewdata {
	font-size 16pt;
	font-weight: bold;
}
li {
	margin-bottom: 0.5rem;
	font-size:14px;
}

/* #fileHeader, #cfileHeader, */

.cardheader{
	margin: 0;
	padding: 0px;
	font-weight: bold;
}

.box {
cursor: pointer;
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}

.box::before {
content: "\25B6"; /*"\2610";*/
color: black;
display: inline-block;
margin-right: 4px;
}

.check-box::before {
content: "\25BC"; 
/* color: dodgerblue; */
}

.nested {
display: none;
}

.active {
display: block;
}



/* Style the top navigation bar */
.navbar {
	grid-area: header;
	overflow: hidden;
	background-color: #333;
}

/* Style the navigation bar links */
.navbar  a {
	float: left;
	display: block;
	color: white;
	height: 3.5rem;
	text-align: center;
	padding: 14px 20px;
	text-decoration: none;
	font-size: 14pt;
	pointer: none;
}

.noHover {
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Old versions of Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome, Edge, Opera and Firefox */
	font-size:16pt;
	/* pointer-events: none; */
	font-weight:bold;
}
.noHover:hover {
	background-color:green;
	
}
.noClick {
	visibility: hidden;
}

.noselect, .tabbutton{
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Old versions of Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome, Edge, Opera and Firefox */
}

.noselect.active { 
	background-color:#333;
	color: white;
	content: "showing diff";
}

/* Right-aligned link */
.navbar a.right {
  float: right;
}

/* Change color on hover */
/* ,.tablebutton a:hover */
.navbar a:hover {
	background-color: #f1f1f1;
	color: black;
}

/* Diagnostic "Simulate Data" toggle - deliberately distinct (orange) from every other navbar
   button, so it never reads as a normal connection control at a glance. */
.navbar a.simbutton {
	background-color: var(--accent);
}
.navbar a.simbutton:hover {
	background-color: var(--accent-light);
	color: white;
}

/* Light/dark mode toggle (themeToggle, both pages - see toggleTheme()/applyTheme() in shared.js).
   Deliberately quiet/outlined rather than filled like simbutton above - this isn't a diagnostic
   feature, just a display preference, so it shouldn't compete for attention in the navbar.
   box-sizing:border-box (see the top of this file) means the added border doesn't need a
   compensating padding reduction - .navbar a's height:3.5rem already accounts for it. */
.navbar a.themetoggle {
	border: 1px solid #4a4a4a;
}
.navbar a.themetoggle:hover {
	background-color: #444;
	color: white;
}

/* Banner shown in the footer's left side while simulation mode is active (see simBanner in
   radlogger.js, moved there from the old standalone filestatus bar 2026-08-04) - loud/
   warning-colored on purpose so simulated data can never be mistaken for a real capture at a
   glance; the bright orange reads fine against the footer's dark background too. */
.simbanner {
	background-color: var(--accent) !important;
	color: white !important;
	font-weight: bold;
}

.simhint {
	font-size: 12px;
	color: var(--panel-ink-dim);
	flex: 1;
}

/* Column container */
.row {  
	/* display: flex; */
	/* flex:1; */
    /* height: 100%; */
}

/* Create two unequal columns that sits next to each other */
/* Sidebar/left column */
/* Sidebar gauge panel - dark "instrument panel" chrome (concept 02, underline readouts grouped by
   channel, from the gauge-redesign review). Replaces the old light-gray boxed-gauge look
   (.textguage/.diglabel/.diggauge, removed - RadLogger.html was their only user). Text sizes here
   are a first pass - may need to become user-adjustable via Settings if they read too small/large
   on the bench. */
.side {
	grid-area: sidebar;
	background-color: var(--panel-bg);
	color: var(--panel-ink);
	padding: 0.9rem;
	overflow: auto;
	/* Without this, a grid item's default min-height:auto sizes it to fit ALL its content (the
	   growing list of gauges) regardless of overflow:auto above, which forces the grid row - and
	   so the whole page - taller than 100vh instead of actually scrolling internally. */
	min-height: 0;
}

.gauge-group-label {
	font-family: var(--mono);
	font-size: 0.62rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--panel-ink-faint);
	margin: 1rem 0 0.5rem;
}

.gauge-group-label:first-child {
	margin-top: 0;
}

.gauge-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.6rem;
	padding: 0.4rem 0;
	border-bottom: 1px solid var(--panel-rule);
}

.gauge-label {
	/* --gauge-scale (default 1, see :root) is set from Settings' "Gauge text size" field
	   (settingGaugeTextScale, radlogger.js) - lets label/value size scale together without
	   touching group labels/the diagnostics line, which stay small/muted regardless. */
	font-size: calc(0.8rem * var(--gauge-scale, 1));
	color: var(--panel-ink-dim);
}

.gauge-value {
	font-family: var(--mono);
	font-variant-numeric: tabular-nums;
	font-size: calc(1rem * var(--gauge-scale, 1));
	color: var(--panel-ink);
}

.gauge-value.ok { color: var(--ok); }
.gauge-value.warn { color: var(--warn); }

/* Collapsed diagnostics (update rate/frame count/dropped/Diag1-6) - bench-debug fields, not
   things read at a glance during a pull, so one quiet line instead of full-weight rows. */
.gauge-diag {
	margin-top: 1rem;
	padding-top: 0.7rem;
	border-top: 1px solid var(--panel-rule);
	font-family: var(--mono);
	font-size: 0.68rem;
	color: var(--panel-ink-faint);
	line-height: 1.7;
}

/* Main column - a flex column so the graphs (.graphdiv, flex:1 below) divide up whatever space
   is actually left in this grid cell after the view-toggle bar's (and, in LogViewer, the scrubbar's)
   own height, rather than each independently claiming a fixed vh - which was the real cause of the
   page-level vertical scroll: two .graphdivs at height:40vh each is 80% of the WHOLE browser
   window, on top of (not "within") whatever the header/filestatus/footer rows already take, so it
   was structurally guaranteed to overflow regardless of any per-element box-sizing fix.
   min-height: 0 overrides the grid item's default min-height:auto (which otherwise sizes to fit
   content and can force the grid row itself to grow past its intended 1fr share - the actual
   mechanism behind the overflow, not just a symptom of it). */
.main {
	grid-area: content;
	background-color: var(--panel-bg-raised);
	padding: 0px;
	margin: 0px;
	min-height: 0;
	display: flex;
	flex-direction: column;
}

/* .filestatus (connection status/data folder/simulated-data banner, its own dedicated bar between
   the navbar and the sidebar/charts) was removed 2026-08-04 - folded into .footer's left side
   instead (.footer-status below) to give back that row's vertical space, on the reasoning that
   this is routine, glanceable-when-needed status rather than something that needs a permanently
   reserved strip right under the navbar. */

.tablebutton {
	/* visibility:hidden; */
	display:none;
	overflow: hidden;
	background-color: 'white';
	margin-top: 20px;
	text-align: center;
}

.tablebutton a{
	background-color: #333;
	color: white; /* White text */
	padding: 10px 24px; /* Some padding */
	margin: 0px;
	width: 5rem;
	display: inline-block;
	text-align: center;
	text-decoration: none;
	font-size: 14pt;
}



/* Create an active/current tablink class */
.tablebutton a.active {
	background-color: #f1f1f1; 
	color:black;
}


.card {
	display:none;
	box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
	/* transition: 0.3s; */
	width: 30%;
	padding-top: 16px;
	padding-left: 16px;
	margin:20px;
}

#basedata, #compdata, #diffdata {
	padding-bottom: 16px;
}


/* Flex row with two groups: .footer-status (left - connection status/data folder/simulated-data
   banner, moved here from the old standalone .filestatus bar 2026-08-04) and .footer-links (right
   - version/copyright, unchanged). Stays hardcoded dark (#333) in both themes, like the navbar -
   not built from the --panel-* tokens, so it's structurally unaffected by the light/dark toggle.
   flex-wrap:wrap (and the grid row below using auto height, not a fixed 2rem) lets .footer-links
   drop to a second line if .footer-status's content (which grows/shrinks with connection status,
   filename, and the simulated-data banner) doesn't leave it enough room on one line at the current
   window width - the alternative, letting the groups shrink and clip via overflow:hidden, was
   tried first and silently cut off the simulated-data banner's text, which defeats the point of it
   being a hard-to-miss warning. */
.footer {
	grid-area: footer;
	background-color: #333;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
}

.footer-status, .footer-links {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
}

.footer-status a, .footer-links a, .footer-status label {
	display: block;
	color: darkgray;
	white-space: nowrap;
	text-align: center;
	padding: 5px 20px;
	text-decoration: none;
	font-size: 11pt;
}



/* Modal stuff below here */
.modal {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    opacity: 0;
    visibility: hidden;
    transform: scale(1.1);
    transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;
}
.HowTo {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    opacity: 0;
    visibility: hidden;
    transform: scale(1.1);
    transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;
}

.modal-content {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: var(--panel-bg-raised);
    padding: 1rem 1.5rem;
    width: 35rem;
	font-size: 24px;
    /* border-radius: 0.5rem; */
}

.close-button, .htclose-button{
	float: right;
	width: 1.5rem;
	line-height: 1.5rem;
	text-align: center;
	cursor: pointer;
	font-size: 28px;
	font-weight: bold;
	color: var(--panel-ink-faint);
}

.close-button:hover {
    color: var(--panel-ink);
}

.show-modal {
    opacity: 1;
    visibility: visible;
    transform: scale(1.0);
    transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;
}

/* Settings panel identity: a technical "instrument panel" look - monospace section labels/tab
   labels/numeric fields, plus the shared copper accent (--accent, see the top of this file) also
   used by the Simulate Data button/banner in the navbar. Copper stays constant in both themes
   (h3 headers, active tab, primary Apply button, focus rings) - only the background/body
   text/hairlines follow the same --panel-* tokens as the sidebar/filestatus/charts, via
   toggleTheme() (shared.js), so the panel switches with the rest of the page instead of staying
   permanently light.

   Settings panel (reuses the .modal/.modal-content/.close-button/.show-modal pattern above).
   Shared by RadLogger.html's full panel (.settings-panel.wide - tabbed, see .settings-tabs below,
   since it has enough sections that one long page used to need scrolling) and LogViewer.html's
   narrower one (.settings-panel alone - just the 7 axis-scaling groups, short enough to stay
   untabbed at the base width). */
.settings-panel {
	font-size: 14px;
	width: 35rem;
	max-width: 95vw; /* on a narrow window, shrink rather than overflow horizontally - the columns
	                    below wrap to stacked on their own once space runs out */
	/* max-height/overflow-y stay on as a fallback for small/zoomed windows rather than letting
	   content get cut off outright - normally unneeded now that tabs keep each page short. */
	max-height: 92vh;
	overflow-y: auto;
	background-color: var(--panel-bg-raised);
	color: var(--panel-ink);
	border-top: 3px solid var(--accent);
	padding-top: 0.85rem;
}

/* Wider, not taller - RadLogger's tabbed panel (see .settings-tabs below). */
.settings-panel.wide {
	width: 50rem;
}

.settings-panel h2 {
	margin: 0 0 12px 0;
	font-family: var(--mono);
	font-size: 18px;
	font-weight: 600;
	letter-spacing: 0.01em;
	color: var(--panel-ink);
}

/* Section labels - small, uppercase, letter-spaced, monospace, with a thin rule beneath. Groups
   related rows without the visual weight a default <h3> carries in a panel this dense. */
.settings-panel h3 {
	margin: 18px 0 6px 0;
	padding-bottom: 5px;
	font-family: var(--mono);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--accent-dark);
	border-bottom: 1px solid var(--panel-rule);
}

.settings-panel h3:first-child {
	margin-top: 0;
}

/* Tab bar - RadLogger's wide panel only (see settings-tabpanel below); LogViewer's shorter panel
   doesn't use these classes at all and just renders its sections in a plain flow. Switching is
   handled by switchSettingsTab() in radlogger.js (a small visual toggle, nothing to do with the
   existing settings read/apply logic - every field keeps the same ID regardless of which tab
   panel currently contains it, so applyChartSettings()/collectSettingsForStorage() etc. work
   completely unmodified whether their tab is visible or not). */
.settings-tabs {
	display: flex;
	gap: 22px;
	margin-bottom: 4px;
	border-bottom: 1px solid var(--panel-rule);
}

.settings-tab {
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	padding: 6px 2px 10px 2px;
	color: var(--panel-ink-faint);
	cursor: pointer;
	border-bottom: 2px solid transparent;
}

.settings-tab:hover {
	color: var(--panel-ink);
}

.settings-tab.active {
	/* Overrides .noselect.active's dark fill (elsewhere used for the Acceleration/Velocity toggle)
	   - these tabs use an underline, not a filled chip, for their active state. */
	background-color: transparent;
	color: var(--accent-dark);
	border-bottom-color: var(--accent);
}

/* All three tab panels are stacked in the same grid cell (grid-area: 1/1) rather than shown one
   at a time via display:none - a grid track sizes itself to the tallest item placed in it
   regardless of visibility (visibility:hidden still participates in layout, unlike display:none),
   so the modal's overall height ends up fixed to whichever tab has the most content instead of
   growing/shrinking - and visibly jumping - as you switch tabs. */
.settings-tabpanels {
	display: grid;
}

.settings-tabpanel {
	grid-area: 1 / 1;
	visibility: hidden;
}

/* Scoped to .show-modal (not just .settings-tabpanel.active alone) - this is what was actually
   breaking the Acceleration/Velocity toggle: visibility:visible on a descendant overrides an
   ancestor's visibility:hidden, so an unscoped rule here kept the active tab's content (and
   whatever real page content happened to sit at the same screen coordinates behind the now-closed,
   invisible-but-still-hit-testable modal) silently intercepting clicks even after Settings was
   closed. Requiring the modal's own .show-modal class here means the tabpanel only becomes
   hit-testable while the modal itself is actually showing. */
.show-modal .settings-tabpanel.active {
	visibility: visible;
}

.settings-columns {
	display: flex;
	flex-wrap: wrap; /* falls back to stacked single-column on a narrow window instead of clipping */
	gap: 24px;
}

.settings-col {
	flex: 1 1 18rem;
	min-width: 0;
}

.settingsrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 6px 0;
	border-bottom: 1px solid var(--panel-rule);
}

.settingsrow label {
	flex: 1;
	color: var(--panel-ink);
}

.settingsrow input[type="number"] {
	width: 5.5rem;
	padding: 4px 6px;
	font-family: var(--mono);
	font-size: 13px;
	box-sizing: border-box;
	border: 1px solid var(--panel-rule); /* explicit border - -moz-appearance below can otherwise drop the browser's own one */
	border-radius: 3px;
	background-color: var(--panel-bg);
	color: var(--panel-ink);
	-moz-appearance: textfield; /* hide Firefox's spinner arrows - the value text was crowded against them */
}

.settingsrow input[type="number"]::-webkit-outer-spin-button,
.settingsrow input[type="number"]::-webkit-inner-spin-button {
	-webkit-appearance: none; /* hide Chrome/Edge/Safari's spinner arrows, same reason */
	margin: 0;
}

.settingsrow input[type="checkbox"] {
	margin-right: 6px;
	accent-color: var(--accent);
}

.settingsrow input[type="number"]:focus-visible,
.settingsrow input[type="checkbox"]:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
}

#settingsApply, #settingsChooseFolder, #zeroCaptureButton, #zeroClearButton, #mcuConfigApplyButton, #bootResetButton {
	display: inline-block;
	padding: 6px 20px;
	background-color: #333;
	color: white;
	cursor: pointer;
	text-align: center;
	border-radius: 3px;
}

#settingsApply:hover, #settingsChooseFolder:hover, #zeroCaptureButton:hover, #zeroClearButton:hover, #mcuConfigApplyButton:hover {
	background-color: #555;
}

#settingsApply {
	margin-top: 14px;
	font-family: var(--mono);
	font-size: 13px;
	letter-spacing: 0.04em;
	background-color: var(--accent);
}

#settingsApply:hover {
	background-color: var(--accent-dark);
}

#settingsChooseFolder, #zeroCaptureButton, #zeroClearButton, #mcuConfigApplyButton, #bootResetButton {
	padding: 4px 14px;
	font-size: 13px;
	white-space: nowrap;
}

/* Distinct warning color, unlike the other Settings buttons above - resetting to the bootloader
   ends the current connection, so it shouldn't blend in with routine actions like "Apply to MCU". */
#bootResetButton {
	background-color: #a13a2f;
}
#bootResetButton:hover {
	background-color: #c24638;
}

#zeroStatus, #mcuConfigStatus {
	font-family: var(--mono);
	font-size: 12px;
	color: var(--panel-ink-dim);
	flex: 1;
}

/* flex:1 (not a fixed vh height - see .main above for why that overflowed) means the two
   .graphdivs in a page's .main split whatever vertical space is actually available equally;
   min-height:0 is required for a flex child to be allowed to shrink below its content's natural
   size - Plotly's internal SVG otherwise has an intrinsic size that would prevent that. */
.graphdiv {
	box-sizing: border-box;
	flex: 1;
	min-height: 0;
	padding: 0px;
	margin-top: 0px;
	margin-bottom: 0px;
}

/* Acceleration/Velocity toggle above the bar chart (roadmap item 4). Flex row so the FFT chart's
   legend (.fft-legend, a plain HTML legend - NOT Plotly's built-in one, see below) can sit beside
   the buttons instead of costing its own vertical strip inside the chart (reported 2026-08-03).
   justify-content:flex-start (changed from space-between 2026-08-05, once the Orbit toggle button
   landed at the end of the button group) - space-between pushed the legend all the way to the far
   right edge, leaving a wide empty gap between it and the buttons; flex-start plus the existing
   gap:12px clusters the legend right after the last button instead, closer to the Orbit toggle it's
   most related to (both concern the orbit panel that now sits below/right of this row). Row height
   is still governed by the buttons (30px content + this element's own padding), not the legend
   (only ~24px tall) - moving the legend around doesn't change how tall this row is either way. */
.viewtoggle {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	gap: 12px;
	padding: 6px 10px;
	background-color: var(--panel-bg-raised);
	/* border-top (not border-bottom) so the rule separates this from the line chart ABOVE it
	   instead of the FFT bar chart below - visually groups the Acceleration/Velocity toggle with
	   the chart it actually controls, rather than looking like a divider between it and the FFT
	   chart (reported 2026-08-03). */
	border-top: 1px solid var(--panel-rule);
}

.viewtoggle a {
	display: inline-block;
	padding: 5px 14px;
	margin-right: 6px;
	background-color: var(--panel-rule);
	color: var(--panel-ink-dim);
	text-decoration: none;
	cursor: pointer;
	font-size: 11pt;
}

.viewtoggle a.active {
	background-color: #333;
	color: white;
}

/* Hand-rolled FFT chart legend, living in the .viewtoggle row instead of Plotly's own built-in
   legend (bargraph now sets showlegend:false) - frees the vertical strip that cost, since this
   row already existed for the Acceleration/Velocity buttons. Swatch colors are hardcoded to match
   the trace colors in buildBarTraces()/redrawBarChart() (radlogger.js) and buildBarTraces()/
   setScrubIndex() (logviewer.js) - if a trace color ever changes there, update the matching swatch
   here too, they're not derived from one shared source. */
/* Bordered box matches the line chart's own Plotly legend directly above it (legendAbovePlot in
   radlogger.js/logviewer.js: bgcolor var(--panel-bg-raised), 1px var(--panel-rule) border) - same
   tokens, same border width, so the two legends read as one consistent style down the page rather
   than the FFT chart's looking like plain unstyled text next to a boxed one (requested 2026-08-04). */
.fft-legend {
	display: flex;
	align-items: center;
	gap: 14px;
	font-size: 11px;
	color: var(--panel-ink-dim);
	flex-wrap: wrap;
	justify-content: flex-end;
	background-color: var(--panel-bg-raised);
	border: 1px solid var(--panel-rule);
	padding: 4px 10px;
}

.fft-legend-item {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	white-space: nowrap;
}

.fft-legend-swatch {
	display: inline-block;
	width: 10px;
	height: 10px;
	flex-shrink: 0;
}

.fft-legend-swatch.fft-legend-line {
	width: 14px;
	height: 2px;
}

/* Channel Y items only shown once the chart is actually in dual-channel mode - toggled via the
   .dual class on #fftLegend (updateFftLegendVisibility(), shared.js), same data both pages already
   track (dualChannelEnabled). */
.fft-legend-y {
	display: none;
}

.fft-legend.dual .fft-legend-y {
	display: inline-flex;
}

/* Orbit panel (roadmap item 6) - sits beside the FFT bar chart rather than replacing it or living
   in the sidebar, per the "split view, side by side" layout chosen after reviewing three candidate
   placements in an artifact mockup (2026-08-05): both the spectrum and the shaft-motion orbit stay
   visible and correlated in real time, at the cost of permanently narrowing the FFT chart's own
   width. .chart-area takes over the flex:1/min-height:0 role .graphdiv normally plays alone
   (see the note above .graphdiv) so the row as a whole still splits available height evenly with
   the line chart above it; #bargraph keeps flex:1 within the row so it still gets the lion's share
   of the width, with .orbit-panel taking a fixed-ish share alongside it.
   Deliberately NOT flex-wrap, unlike .settings-columns/.footer's narrow-window pattern elsewhere in
   this stylesheet (no media queries anywhere in it) - those live in free-flowing containers that can
   just grow taller to fit a wrapped second line. .chart-area sits inside .main, a strictly
   height-constrained flex column with zero scroll budget by design (see the note above .main about
   the page-level vertical scroll that caused) - a wrapped second row here has nowhere to put its
   extra height and just overflows past .main's box into the footer below. Tried wrapping first
   (2026-08-05) and hit exactly that when the orbit panel's width grew enough to trigger it on a
   real window size, reported as "the FFT now overlaps the footer" - removed rather than patched,
   since no width threshold here can ever be truly safe against every window size the same way a
   free-flowing wrap can. Narrow windows instead rely on ordinary flex-shrink (#bargraph's
   min-width:0, .orbit-panel's min-width:200px below) squeezing both items down within the one row,
   never spawning a line that needs room .main doesn't have. */
.chart-area {
	flex: 1;
	min-height: 0;
	display: flex;
}

.chart-area .graphdiv {
	flex: 1 1 320px;
	min-width: 0;
}

/* justify-content:center - the canvas below is genuinely square (verified via
   getBoundingClientRect()), but .orbit-panel itself stretches to match the FFT chart's full row
   height (.chart-area) - so a top-aligned square canvas can leave empty panel background below it
   if the panel ends up narrower than that row is tall. Centering distributes any such leftover
   space evenly above/below instead of all beneath, so the square doesn't read as pinned oddly to
   the top.
   flex-basis 340px (bumped from 260px 2026-08-05, "make the orbit viewer a bit bigger") - the
   canvas's aspect-ratio:1/1 sizes it from the panel's WIDTH, not chart-area's height, so it was
   genuinely capped by this number (measured 259x259 in a 260-wide/370-tall panel, i.e. ~111px of
   unused vertical room going begging) - a taller viewtoggle row above wouldn't have grown it at
   all, only a wider panel does. 340px isn't chosen to exactly match chart-area's height (that
   varies with window size - only the FFT chart's own row is a stable anchor here, this panel isn't),
   just enough bigger to noticeably close the gap without crowding out #bargraph on a typical
   window. */
.orbit-panel {
	flex: 0 1 340px;
	min-width: 200px;
	display: flex;
	flex-direction: column;
	justify-content: center;
	border-left: 1px solid var(--panel-rule);
	background-color: var(--panel-bg-raised);
}

.orbit-panel-head {
	display: flex;
	justify-content: space-between;
	gap: 8px;
	padding: 6px 10px 2px;
	font-family: var(--mono);
	font-size: 0.65rem;
	color: var(--panel-ink-dim);
	font-variant-numeric: tabular-nums;
}

/* Two-trace legend (drawOrbitPlot(), radlogger.js - the trusted 1x-bin calc vs. the experimental
   multi-order quadrature calc) - reuses .fft-legend-item/.fft-legend-swatch (styles.css, further
   down) for the swatch+label pairs, same convention as the FFT chart's own legend.
   .orbit-panel's justify-content:center (above) centers ALL its children as a group, which would
   otherwise pull this row (and .orbit-panel-head above it) away from the top edge along with the
   canvas. margin-bottom:auto on this element - the last one before the canvas - claims all the
   leftover space on its own trailing side, pinning both header rows to the top while the canvas
   still centers in what's left below. */
.orbit-legend {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	padding: 2px 10px 4px;
	margin-bottom: auto;
	/* .fft-legend-item doesn't set its own font-size (normally inherits 11px from its usual parent,
	   .fft-legend) - set it explicitly here since this is a different parent. */
	font-size: 11px;
	color: var(--panel-ink-dim);
}

/* Same "plot area indistinguishable from its own margin" issue the Plotly charts already ran into
   (see "Plot area border" further down) - the canvas draws its background the same
   --panel-bg-raised as .orbit-panel itself, so with no visible edge, a tall .orbit-panel with a
   top-aligned square canvas read as one non-square rectangle (reported 2026-08-05: "looks a bit
   taller than wide"). A border makes the true square boundary visible regardless of how much empty
   panel space surrounds it. box-sizing:border-box keeps the border from pushing the element past
   the 100% width the aspect-ratio calculation is based on. */
.orbit-canvas {
	display: block;
	box-sizing: border-box;
	width: 100%;
	aspect-ratio: 1 / 1;
	max-height: 100%;
	border: 1px solid var(--panel-rule);
}

/* Plotly's default zoom-mode cursor is the OS-native "crosshair", which some cursor
   themes/settings render white (invisible against a light chart background). Force an explicit
   crosshair instead - drawn with a white halo behind a black line so it stays visible against
   EITHER a light or dark chart background (--panel-bg-raised, light/dark mode - see :root above),
   rather than needing a separate cursor per theme. Needs !important + targeting descendants since
   Plotly sets its own inline cursor style on the SVG drag layer, which otherwise wins over an
   inherited value. */
.graphdiv, .graphdiv * {
	cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><line x1='10' y1='0' x2='10' y2='20' stroke='white' stroke-width='3'/><line x1='0' y1='10' x2='20' y2='10' stroke='white' stroke-width='3'/><line x1='10' y1='0' x2='10' y2='20' stroke='black' stroke-width='1'/><line x1='0' y1='10' x2='20' y2='10' stroke='black' stroke-width='1'/></svg>") 10 10, crosshair !important;
}

/* LogViewer.html: file list in the sidebar */
.logfile-heading {
	display: block;
	font-weight: bold;
	margin-bottom: 8px;
}

.logfile-list {
	display: flex;
	flex-direction: column;
}

.logfile-list a {
	display: block;
	padding: 8px 6px;
	color: var(--panel-ink);
	text-decoration: none;
	cursor: pointer;
	border-bottom: 1px solid var(--panel-rule);
	font-size: 12px;
	word-break: break-all;
}

.logfile-list a:hover {
	background-color: var(--panel-rule);
}

/* Selected-file chip stays #333/white regardless of theme, same as .noselect.active/.viewtoggle
   a.active elsewhere - reads fine as a filled pill against either a light or dark sidebar. */
.logfile-list a.active {
	background-color: #333;
	color: white;
}

.logfile-empty {
	font-size: 13px;
	color: var(--panel-ink-faint);
}

/* LogViewer.html: playback/scrub controls below the bar chart - part of the chart area, so
   themed the same as .main/.viewtoggle rather than staying a fixed light gray. */
.scrubbar {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 8px 14px;
	background-color: var(--panel-bg-raised);
	color: var(--panel-ink);
	border-top: 1px solid var(--panel-rule);
}

#playPauseButton {
	display: inline-block;
	padding: 5px 14px;
	background-color: #333;
	color: white;
	cursor: pointer;
	text-align: center;
	border-radius: 2px;
	font-size: 11pt;
}

#playPauseButton:hover {
	background-color: #555;
}

#frameSlider {
	flex: 1;
}

.framestep {
	display: inline-block;
	padding: 5px 10px;
	background-color: var(--panel-rule);
	color: var(--panel-ink-dim);
	cursor: pointer;
	text-align: center;
	border-radius: 2px;
	font-size: 11pt;
}

.framestep:hover {
	background-color: var(--panel-ink-faint);
	color: var(--panel-bg);
}

.scrublabel {
	white-space: nowrap;
	font-size: 12px;
	min-width: 13rem;
}