/* Bikotic brand (F:\BIKOTIC BRANDING\brand-guide.md) - blue is the brand and runs every button and tool; sage and
   plum are only for bike data. Fonts: Oswald for words and labels, IBM Plex Sans for reading text, JetBrains Mono
   for numbers */
:root
{
	--ui: #2B4A66;
	--uiLine: #c5d3e0;
	--uiTint: #E9EFF5;
	--uiFaint: #f4f7fa;
	--border: #E3E7EB;
	--shadowRGB: 20, 37, 54;

	--head: "Oswald", "Arial Narrow", sans-serif;
	--body: "IBM Plex Sans", system-ui, sans-serif;
	--data: "JetBrains Mono", ui-monospace, monospace;

	/* the bike colour - sage for bike 1, plum whenever bike 2 is the one you're looking at (app.js and view3d.js
	   put .bike2 on the page), or on bike 2's own card. --accentText is the darker shade, for text on white */
	--accent: #5E8F6E;
	--accentRGB: 94, 143, 110;
	--accentDeep: #3F6A4E;
	--accentText: #3F6B4D;
	--tint1: #dce9e0;
}

html.bike2, .cardR
{
	--accent: #7A3F6E;
	--accentRGB: 122, 63, 110;
	--accentDeep: #4F2547;
	--accentText: #6A3460;
	--tint1: #ecdde8;
}

/* bike 1's own card stays sage whichever bike's in view */
.cardL
{
	--accent: #5E8F6E;
	--accentRGB: 94, 143, 110;
	--accentDeep: #3F6A4E;
	--accentText: #3F6B4D;
	--tint1: #dce9e0;
}

/* numbers keep a fixed width so they don't jiggle as they change */
.cardWins, .rowValue.num, .sizeBtn, .v3Label, #v3Readout, #v3Fit, #v3Setup, #galCount
{
	font-variant-numeric: tabular-nums;
}

html, body
{
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;
	background-color: #ffffff;
	font-family: var(--head);
}

/* the bike stage keeps the 16:9 to-scale frame and fits inside any screen */
#stage
{
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

#frame
{
	position: relative;
}

#bikes
{
	display: block;
	cursor: e-resize;
	touch-action: none;
}

#hud
{
	position: absolute;
	inset: 0;
	pointer-events: none;
}

#message
{
	position: absolute;
	color: #555555;
	font-size: 20px;
	text-align: center;
	padding: 16px;
	pointer-events: none;		/* it sits mid screen - never block drags or the wheel */
}

/* manufacturer and model - sized from the stage width (set as --sw by app.js) */
#title
{
	position: fixed;
	z-index: 10;
	pointer-events: none;
	text-transform: uppercase;
	opacity: 0;
}

#titleMan
{
	font-family: var(--head);
	font-weight: 600;
	font-size: max(11px, calc(var(--sw) * 0.0135));
	line-height: 1;
	letter-spacing: 0.04em;
	color: #141414;
}

/* the bike's year after the manufacturer - smaller and lighter, in both views */
.makerYear
{
	margin-left: 0.5em;
	font-size: 0.8em;
	font-weight: 500;		/* the lightest Oswald loaded (index.html) */
	color: #555555;
}
/* the maker's price after the year: "GIANT 2026 · £3,000" (shared.js makerPrice; Ben's legal fixes, 14 Nov 2026). The
   year's size and grey, the figure in the data font. Never broken in two: if the line is too narrow the whole price
   goes to the next line (a phone's landscape bar cuts the line short instead, below) */
.makerPrice
{
	margin-left: 0.4em;
	font: inherit;				/* Ben, 14 Nov 2026: exactly the year's font, size and weight (it sits inside the year's span, shared.js) ... */
	letter-spacing: inherit;
	color: inherit;
	opacity: 0.5;				/* ... at half its opacity */
	white-space: nowrap;
	display: inline-block;
}

/* the maker's own page for the bike - "View on Specialized's site" then an "opens another site" icon, under the model name, in both views */
.makerLink
{
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: 6px;
	font-family: var(--body);
	font-size: 13px;
	line-height: 1;
	text-transform: none;
	text-decoration: none;
	color: #555555;
	pointer-events: auto;
	transition: color 0.15s ease;
}

.makerLink svg
{
	display: block;
	flex: none;
}

.makerLink:hover
{
	color: var(--ui);
}

#titleModel
{
	font-family: var(--head);
	font-weight: 500;
	font-size: max(13px, calc(var(--sw) * 0.017));
	line-height: 1.2;
	letter-spacing: 0.09em;
	color: var(--accentText);
	margin-top: 0.2em;
}

/* "Photo failed accuracy to geometry tests" - directly under the name of a bike whose main photo failed (app.js
   PHOTO FAILED): quiet, in the bike's colour, in the words as written (the title is capitals, this isn't) */
#titleNote
{
	margin-top: 4px;
	font-family: var(--body);
	font-weight: 400;
	font-size: 13px;
	line-height: 1.3;
	letter-spacing: 0;
	text-transform: none;
	color: var(--accentText);
}
#titleNote[hidden] { display: none; }

/* PROTOTYPE, NOT FOR SALE (Ben, 13 Nov 2026): a prototype has no Buy anywhere - shared.js setBuyLabel puts this on
   every Buy button it words for one (title, cards, BIKES, Gallery, 3D, Browse's preview) */
.noBuy { display: none !important; }

/* ---------------------------------------------------------------- LEGACY CHIP (shared.js legacyChip) */

/* after the name of every bike from the old database - nearly all of them for now, so small and grey: white on the
   bike colour card headers, grey on white in the title. Its meaning shows on hover or tap (#legacyTip) */
.legacyChip
{
	display: inline-block;
	vertical-align: 0.2em;
	margin: 0 0 0 0.6em;
	padding: 1px 5px 0;
	font-family: var(--head);
	font-weight: 500;
	font-size: 10px;
	line-height: 1.4;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: inherit;
	background: none;
	border: 1px solid currentColor;
	border-radius: 3px;
	opacity: 0.8;
	cursor: help;
	pointer-events: auto;
	-webkit-tap-highlight-color: transparent;
}
.legacyChip:hover, .legacyChip:focus-visible { opacity: 1; }
.legacyChip:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }
#titleModel .legacyChip, #v3Model .legacyChip { color: #555555; font-size: 12px; padding: 1px 6px 0; }		/* a bit bigger beside the big title (the 3D title has it on phones only) */

#legacyTip
{
	position: fixed;
	z-index: 900;
	max-width: 240px;
	padding: 6px 10px;
	border-radius: 6px;
	background: #142536;
	color: #F2F5F8;
	font-family: var(--body);
	font-size: 13px;
	line-height: 1.4;
	letter-spacing: 0;
	text-transform: none;
	box-shadow: 0 6px 18px rgba(var(--shadowRGB), 0.25);
	pointer-events: none;
	visibility: hidden;
}
#legacyTip.show { visibility: visible; }

/* ---------------------------------------------------------------- COMPARE CARDS */

#topButtons
{
	position: fixed;
	top: var(--pad, 21px);
	right: var(--pad, 21px);
	z-index: 20;
	display: flex;
	gap: 10px;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.3s ease;
}

#topButtons.show
{
	opacity: 1;
	pointer-events: auto;
}

.topBtn
{
	font-family: var(--head);
	font-weight: 500;
	font-size: 13px;
	letter-spacing: 0.16em;
	color: var(--ui);
	background: #ffffff;
	border: 1px solid var(--ui);
	border-radius: 999px;
	padding: 6px 16px 7px 18px;
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}

.topBtn:hover
{
	background: var(--ui);
	color: #ffffff;
}

/* no photos for this bike - the button stays, greyed out */
.topBtn:disabled, .topBtn:disabled:hover
{
	color: #b8c0c8;
	border-color: #dde2e7;
	background: #ffffff;
	cursor: default;
}

/* ---------------------------------------------------------------- CLOSE (.xBtn): every close in the app
   Ben, 12 Nov 2026 ("I don't want floating things"): an X in a bordered button styled like the other buttons - the
   rounded border, edge, height and radius of BIKES and the hamburger (.pbBtn, .pbIcon). ONE style for every close: the
   cards, BIKES, Browse and its preview, Gallery, the 3D view (desktop and phones), the Fit and Dimensions sheets and
   the Buy panel. 56 x 44 (44 wide in the 3D dock's tight row), so always 44px to tap. On the blue bars it's the
   hamburger's white edge (.onDark); on white, BIKES' blue edge. Each close's own rule only says where it goes */
.xBtn
{
	flex: none;
	box-sizing: border-box;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 44px;
	padding: 0;
	font-family: var(--body);
	font-size: 17px;
	line-height: 1;
	color: var(--ui);
	background: #ffffff;
	border: 1px solid var(--ui);
	border-radius: 999px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	transition: background-color 0.15s ease, color 0.15s ease;
}
.xBtn[hidden] { display: none; }
.xBtn:active { background: var(--uiTint); }
.xBtn.onDark { color: #ffffff; background: none; border-color: rgba(255, 255, 255, 0.45); }		/* secondary button on dark, as BIKES and the hamburger */
.xBtn.onDark:active { background: rgba(255, 255, 255, 0.14); }
.xBtn:focus-visible { outline: 2px solid var(--ui); outline-offset: 2px; }
.xBtn.onDark:focus-visible { outline-color: #ffffff; }
@media (hover: hover)
{
	.xBtn:hover { background: var(--ui); color: #ffffff; }
	.xBtn.onDark:hover { background: rgba(255, 255, 255, 0.14); }
}

body.cardsUp #topButtons
{
	opacity: 0;
	pointer-events: none;
}

#cards
{
	position: fixed;
	inset: 0;
	z-index: 500;
	display: none;
	align-items: center;
	justify-content: center;
	overflow: auto;
	padding: 24px 16px;
	box-sizing: border-box;
	background: rgba(255, 255, 255, 0.82);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}

#cards.open
{
	display: flex;
	animation: cardsFadeIn 0.25s ease both;
}

/* closing: the cards get swept off either side, then the backdrop fades */
#cards.closing
{
	overflow: hidden;		/* no scrollbars flashing as the cards fly out */
	animation: cardsBackdropOut 0.45s ease-out both;	/* the white goes over the whole move of the cards */
}

#cards.closing .geoSizes
{
	animation: fadeAway 0.3s ease-out both;
}

#cards.closing .cardL
{
	animation: cardSweepLeft 0.45s ease-out both;
}

#cards.closing .cardR
{
	animation: cardSweepRight 0.45s ease-out both;
}

#cardsInner
{
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: flex-start;
	gap: clamp(16px, 3vw, 48px);
	perspective: 1400px;
	margin: auto;
}

#cardsClose		/* the shared close (.xBtn); this is only where it goes */
{
	position: fixed;
	top: var(--pad, 21px);
	right: var(--pad, 21px);
}



.card
{
	width: min(420px, calc(50vw - 48px));
	min-width: 300px;
	background: #ffffff;
	border-radius: 18px;
	overflow: hidden;
	box-shadow: 0 18px 50px rgba(var(--shadowRGB), 0.18), 0 2px 6px rgba(0, 0, 0, 0.08);
	transform-origin: 50% 100%;
	animation: cardDeal 0.65s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.cardR
{
	animation-delay: 0.12s;
}

.cardHead
{
	position: relative;
	padding: 14px 18px 13px;
	background: linear-gradient(135deg, var(--accent) 0%, var(--accentDeep) 100%);
	color: #ffffff;
	text-transform: uppercase;
}

.cardMan
{
	font-weight: 500;
	font-size: 12px;
	letter-spacing: 0.2em;
	opacity: 0.85;
}

.cardModel
{
	font-weight: 600;
	font-size: clamp(18px, 1.6vw, 24px);
	line-height: 1.1;
	letter-spacing: 0.02em;
	margin-top: 2px;
	padding-right: 64px;
}

.cardYear
{
	font-weight: 500;
	font-size: 12px;
	letter-spacing: 0.2em;
	margin-top: 3px;
	opacity: 0.85;
}

.cardWins
{
	position: absolute;
	top: 14px;
	right: 16px;
	text-align: center;
	font-family: var(--data);
	font-weight: 700;
	font-size: 26px;
	line-height: 1;
	animation: winsPop 0.4s cubic-bezier(0.3, 1.6, 0.5, 1) both;
}

.cardWins small
{
	display: block;
	font-family: var(--head);
	font-weight: 500;
	font-size: 10px;
	letter-spacing: 0.2em;
	margin-top: 4px;
}

.cardImg
{
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	background: #ffffff;
	border-bottom: 1px solid var(--border);
}

.cardRows
{
	padding: 4px 0 10px;
}

.cardNote
{
	padding: 0 18px 12px;
	font-family: var(--body);
	font-weight: 400;
	font-size: 11px;
	letter-spacing: 0.06em;
	color: #555555;
	text-align: right;
}

.row
{
	position: relative;
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: center;
	gap: 12px;
	padding: 6px 18px;
	border-bottom: 1px solid #eef0f2;
	box-sizing: border-box;
}

.row:last-child
{
	border-bottom: 0;
}

.rowLabel
{
	font-weight: 500;
	font-size: 12px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: #555555;
	white-space: nowrap;
}

/* the maker's trail (with their tyres on) under the rim trail label.. */
.geoMaker
{
	display: block;
	margin-top: 2px;
	font-size: 10px;
	letter-spacing: 0.08em;
	text-transform: none;
	color: #767676;
}

.geoMaker:empty
{
	display: none;
}

.rowValue
{
	font-weight: 500;
	font-size: 15px;
	color: #141414;
	text-align: right;
}

.rowValue.num
{
	font-family: var(--data);
	font-weight: 500;
	font-size: 14px;
}

/* the weight's cost per gram saved, "(£5.03/g saved)": kept whole, never split across two lines.. */
.rowExtra
{
	white-space: nowrap;
}

/* pointing at a stat highlights it on both cards (box-shadow, so it shows over a winner's background) */
.row
{
	transition: box-shadow 0.15s ease;
}

.row.hl
{
	box-shadow: inset 4px 0 0 var(--accent), inset 0 0 0 999px rgba(var(--accentRGB), 0.10);
}

.row.hl .rowLabel
{
	color: var(--accentText);
	font-weight: 600;
}

/* the better stat of the two lights up */
.row.win
{
	animation: rowWin 0.45s ease both;
}

.row.win .rowValue
{
	color: var(--accentText);
	font-weight: 600;
}

.row.win::before
{
	content: "\25B2";
	position: absolute;
	left: 5px;
	top: 50%;
	transform: translateY(-50%);
	font-size: 8px;
	color: var(--accent);
}

/* ---------------------------------------------------------------- GEOMETRY MODE */

.geoSizes
{
	flex-basis: 100%;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px;
	animation: cardsFadeIn 0.4s ease 0.3s both;
}

.sizeBtn
{
	font-family: var(--data);
	font-weight: 700;
	font-size: 14px;
	min-width: 48px;
	padding: 7px 10px;
	border-radius: 999px;
	border: 1px solid #bfd6c6;
	background: #ffffff;
	color: #3F6B4D;
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.sizeBtn:hover
{
	border-color: #5E8F6E;
}

.sizeBtn.active
{
	background: #5E8F6E;
	border-color: #5E8F6E;
	color: #ffffff;
	transform: scale(1.08);
}

/* one size row per bike, stacked, with VIEW SIZING IN 3D under them */
.geoSizes
{
	flex-direction: column;
	align-items: center;
}

.view3dGo
{
	margin-top: 4px;
}

.geoNone
{
	font-weight: 500;
	letter-spacing: 0.1em;
	color: #555555;
}

.rowSection
{
	padding: 10px 18px 4px;
	font-weight: 500;
	font-size: 11px;
	letter-spacing: 0.24em;
	text-transform: uppercase;
	color: var(--accentText);
	border-bottom: 1px solid var(--tint1);
}

.rowDiff
{
	display: inline-block;
	min-width: 4.6em;
	margin-left: 8px;
	font-size: 12px;
	color: var(--accentText);
	text-align: right;
}

.row.differ .geoNum
{
	color: var(--accentText);
	font-weight: 700;
}

.row.differ
{
	background-color: rgba(var(--accentRGB), 0.05);
}

.row.flash .rowValue
{
	animation: geoFlash 0.45s ease;
}

.geoTally .geoTallySize
{
	display: block;
}
.geoTally.geoTallyWord .geoTallySize { font-size: 17px; }		/* a word, not a number (a legacy bike's LISTED) */

/* ---------------------------------------------------------------- CARDS LEVEL SIDE BY SIDE (COMPARE and GEOMETRY) */

/* Side by side, the two cards share one grid and each card is a subgrid of it: header, photo, every row and the
   footnote each take one track across both cards, as tall as the taller side, so a wrapped name, LEGACY chip or
   value keeps everything level. cards.js placeCard() / gridRow puts each piece in its track. Below 652px the cards
   stack (where the old flex row wrapped) and each keeps its natural height. Not the BIKES picker's cards. */
@media (min-width: 652px)
{
	#cardsInner
	{
		display: grid;
		grid-template-columns: auto auto;
		row-gap: 0;		/* subgrids take this between their tracks */
	}

	#cardsInner > .geoSizes
	{
		grid-column: 1 / -1;
		grid-row: 1;
		margin-bottom: clamp(16px, 3vw, 48px);		/* the old gap above the cards */
	}

	#cardsInner > .cardL { grid-column: 1; }
	#cardsInner > .cardR { grid-column: 2; }

	#cardsInner > .card,
	#cardsInner > .card > .cardRows
	{
		display: grid;
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: subgrid;
	}

	#cardsInner > .card > .cardHead { grid-row: 1; }
	#cardsInner > .card > .cardImg { grid-row: 2; }
	#cardsInner > .card > .cardNote { grid-row: -2 / -1; }		/* the last track, on both cards */
}

@keyframes geoFlash
{
	from { opacity: 0.15; transform: translateY(-4px); }
	to { opacity: 1; transform: none; }
}

@keyframes cardsFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeAway { from { opacity: 1; } to { opacity: 0; } }

@keyframes cardsBackdropOut
{
	from { background-color: rgba(255, 255, 255, 0.82); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
	to { background-color: rgba(255, 255, 255, 0); backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0); }
}

@keyframes cardDeal
{
	from { opacity: 0; transform: translateY(60px) rotateX(18deg) rotateY(-22deg) scale(0.9); }
	to { opacity: 1; transform: none; }
}

@keyframes cardSweepLeft
{
	from { opacity: 1; transform: none; }
	to { opacity: 0; transform: translateX(-100px); }
}

@keyframes cardSweepRight
{
	from { opacity: 1; transform: none; }
	to { opacity: 0; transform: translateX(100px); }
}

@keyframes rowWin
{
	from { background-color: rgba(var(--accentRGB), 0); }
	40% { background-color: rgba(var(--accentRGB), 0.22); }
	to { background-color: rgba(var(--accentRGB), 0.08); }
}

@keyframes winsPop
{
	from { opacity: 0; transform: scale(0.4); }
	to { opacity: 1; transform: scale(1); }
}

/* ---------------------------------------------------------------- 3D VIEW */

#view3d
{
	position: fixed;
	inset: 0;
	z-index: 600;
	display: none;
	background: #ffffff;
	overflow: hidden;
}

#view3d.open
{
	display: block;
	animation: fadeIn3d 0.35s ease both;
}

#view3d.closing
{
	animation: fadeAway 0.3s ease both;
}

#v3Stage, #v3Stage canvas
{
	position: absolute;
	inset: 0;
	display: block;
	touch-action: none;
}

#v3Hud
{
	position: absolute;
	inset: 0;
	pointer-events: none;
}

#v3Labels
{
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.v3Label
{
	position: absolute;
	transform: translate(-50%, -140%);
	font-family: var(--data);
	font-weight: 700;
	font-size: 12px;
	color: #555555;
	white-space: nowrap;
}

.v3Label.main
{
	color: #3F6B4D;
	font-size: 15px;
}

.v3Label.main.b2
{
	color: #6A3460;
}

/* same corner and type as the main title, clear of the marker */
#v3Title
{
	position: absolute;
	top: var(--pad, 21px);
	left: var(--pad, 21px);
	text-transform: uppercase;
	pointer-events: none;
}

#v3Man
{
	font-weight: 600;
	line-height: 1;
	font-size: max(11px, calc(var(--sw) * 0.0135));
	letter-spacing: 0.04em;
	color: #141414;
}

#v3Model
{
	font-weight: 500;
	font-size: max(13px, calc(var(--sw) * 0.017));
	line-height: 1.2;
	letter-spacing: 0.09em;
	color: var(--accentText);
	margin-top: 0.2em;
}

/* the size box, and the view cube under it */
#v3Right
{
	position: absolute;
	top: calc(var(--pad, 21px) + 56px);
	right: var(--pad, 21px);
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 10px;
	pointer-events: none;
}

#v3Cube
{
	align-self: center;		/* centred under the size box..*/
	width: 104px;
	height: 104px;
	pointer-events: auto;
}

#v3Cube canvas
{
	display: block;
}

/* the ruler button - an icon, not a word */
.v3Icon
{
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 7px 12px;
}

#v3Readout
{
	min-width: 170px;
	padding: 10px 14px;
	border-radius: 12px;
	background: rgba(255, 255, 255, 0.85);
	border: 1px solid var(--border);
	font-family: var(--data);
	font-size: 13px;
	color: #141414;
	pointer-events: none;
}

#v3Readout .rl
{
	font-family: var(--head);
	font-weight: 500;
	font-size: 11px;
	letter-spacing: 0.14em;
	color: #555555;
	margin-right: 10px;
}

#v3Readout div
{
	display: flex;
	justify-content: space-between;
	padding: 2px 0;
}

/* a bike that can't be drawn (no wheelbase, say): "No geometry for 3D" in the box, and where the bike would be (view3d.js) */
#v3Readout .rNone { display: block; font-family: var(--body); color: #555555; }
html:not(.phone) #v3None
{
	display: block;
	position: absolute;
	left: 50%;
	top: 46%;
	transform: translate(-50%, -50%);
	margin: 0;
	font-family: var(--body);
	font-size: 18px;
	color: #555555;
	white-space: nowrap;
	pointer-events: none;
}
html:not(.phone) #v3None[hidden] { display: none; }

#v3Readout .rh
{
	font-family: var(--head);
	font-weight: 600;
	letter-spacing: 0.1em;
	color: var(--accentText);
	margin-bottom: 4px;
}

/* the rider's fit, under the title, and YOUR SETUP under that */
#v3Left
{
	--fitTop: calc(var(--pad, 21px) + max(60px, calc(var(--sw) * 0.045)) + 81px);	/* below the title, its link and Buy; view3d.js fitTop3 replaces this with the real measure. Nothing may cover Buy (CLAUDE.md) */
	position: absolute;
	top: var(--fitTop);
	left: var(--pad, 21px);
	width: 250px;
	max-width: calc(100vw - 32px);
	display: flex;
	flex-direction: column;
	gap: 10px;
	pointer-events: none;
	max-height: calc(100vh - var(--fitTop) - 120px);	/* clear of the buttons at the bottom..*/
	overflow-y: auto;
	scrollbar-width: none;		/* scrolls with the wheel - a bar would squeeze the panels..*/
}

#v3Left::-webkit-scrollbar
{
	display: none;
}

#v3Fit, #v3Setup
{
	display: none;
	box-sizing: border-box;
	padding: 10px 14px 12px;
	border-radius: 12px;
	background: rgba(255, 255, 255, 0.92);
	border: 1px solid var(--border);
	font-family: var(--data);
	font-size: 12px;
	color: #141414;
	pointer-events: auto;
}

#v3Fit.open, #v3Setup.open
{
	display: block;
	animation: fadeIn3d 0.25s ease both;
}

/* the Fit disclaimer: heads the Fit panel and is always in view while it's open - it stays put at the top while
   YOUR FIT and YOUR SETUP scroll under it, and it isn't part of anything that folds away (desktop and phones) */
.fitWarn
{
	display: none;
	position: sticky;
	top: 0;
	z-index: 1;
	flex: none;
	box-sizing: border-box;
	margin: 0;
	padding: 8px 12px;
	border-radius: 12px;
	border: 1px solid var(--uiLine);
	background: var(--uiTint);
	font-family: var(--body);
	font-weight: 500;
	font-size: 12px;
	line-height: 1.4;
	color: var(--ui);
	pointer-events: auto;
}

.fitWarn.open
{
	display: block;
}

#v3SetupRows
{
	margin-top: 6px;
}

.setRow
{
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
	padding: 4px 0 0;
}

.setRow span
{
	font-family: var(--head);
	font-weight: 500;
	font-size: 11px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: #555555;
}

.setRow b
{
	font-weight: 700;
	color: var(--ui);
}

/* YOUR SETUP is the bike's answer, in brand blue with a dashed edge - set apart from the rider's own numbers above */
#v3Setup .fitHead
{
	color: var(--ui);
}

#v3Setup
{
	border-style: dashed;
	border-color: var(--uiLine);
}

.setMore
{
	padding: 0 0 2px;
	text-align: right;
	font-family: var(--body);
	font-weight: 400;
	font-size: 11px;
	letter-spacing: 0.03em;
	color: #555555;
}

#v3Fit.min .fitBody
{
	display: none;
}

.fitHead
{
	display: flex;
	justify-content: space-between;
	align-items: center;
	font-family: var(--head);
	font-weight: 600;
	letter-spacing: 0.1em;
	color: var(--ui);
}

.fitHead button
{
	border: 0;
	background: none;
	color: var(--ui);
	font-size: 16px;
	line-height: 1;
	padding: 0 4px;
	cursor: pointer;
}

.fitBody
{
	margin-top: 6px;
}

.fitRow
{
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 3px 0;
}

.fitRow span
{
	flex: 1;
	font-family: var(--head);
	font-weight: 500;
	font-size: 11px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: #555555;
}

.fitRow input
{
	width: 62px;
	font: inherit;
	text-align: right;
	padding: 3px 6px;
	border: 1px solid var(--uiLine);
	border-radius: 6px;
	color: #141414;
	background: #ffffff;
}

/* hints, not numbers - and the boxes still to fill get a blue edge */
.fitRow input::placeholder
{
	color: #cfcfcf;
	font-style: italic;
}

.fitRow input.need
{
	border-color: var(--ui);
	background: var(--uiFaint);
}

#v3FitState.need
{
	color: var(--ui);
}

.fitRow select
{
	max-width: 150px;
	font: inherit;
	padding: 3px 4px;
	border: 1px solid var(--uiLine);
	border-radius: 6px;
	color: #141414;
	background: #ffffff;
}

.fitRow input:disabled
{
	color: var(--ui);
	background: var(--uiFaint);
	border-color: var(--uiTint);
}

.fitWho
{
	padding-bottom: 6px;
	margin-bottom: 4px;
	border-bottom: 1px solid var(--border);
}

.fitRow input:focus, .fitRow select:focus
{
	outline: none;
	border-color: var(--ui);
}

.fitRow i
{
	width: 18px;
	font-style: normal;
	color: #767676;
}

#v3Fit summary
{
	cursor: pointer;
	padding: 6px 0 2px;
	font-family: var(--head);
	font-size: 11px;
	letter-spacing: 0.12em;
	color: var(--ui);
}

.fitHint
{
	margin-top: 8px;
	font-family: var(--body);
	font-weight: 400;
	font-size: 11px;
	letter-spacing: 0.03em;
	line-height: 1.45;
	color: #555555;
}

/* how to measure - a sketch and four short lines, folded away till asked for */
.fitHow svg
{
	display: block;
	width: 100%;
	height: auto;
	max-height: 140px;
	margin: 6px 0 4px;
}

.fitHow ol
{
	margin: 0;
	padding-left: 16px;
	font-family: var(--body);
	font-weight: 400;
	font-size: 11px;
	letter-spacing: 0.03em;
	line-height: 1.45;
	color: #6f6f6f;
}

.fitHow li
{
	margin-bottom: 4px;
}

.fitHow li::marker
{
	color: var(--ui);
	font-weight: 600;
}

.fitHow b
{
	font-weight: 600;
	color: var(--ui);
}

.fitFoot
{
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 8px;
	margin-top: 8px;
	font-family: var(--head);
	font-size: 11px;
	color: #555555;
}

.fitFoot .v3Toggle
{
	padding: 4px 10px;
	font-size: 11px;
}

#v3Bar
{
	position: absolute;
	left: 50%;
	bottom: var(--pad, 21px);
	transform: translateX(-50%);
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 10px 18px;
	width: max-content;
	max-width: calc(100vw - 32px);
}

#v3Views, #v3Sizes, #v3Toggles
{
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 6px;
}

.v3Toggle
{
	font-family: var(--head);
	font-size: 12px;
	letter-spacing: 0.14em;
	padding: 6px 12px;
	border-radius: 999px;
	border: 1px solid var(--uiLine);
	background: #ffffff;
	color: var(--ui);
	cursor: pointer;
}

.v3Toggle.on
{
	background: var(--ui);
	border-color: var(--ui);
	color: #ffffff;
}

/* a row of sizes per bike, each led by the bike's colour */
#v3Sizes
{
	flex-direction: column;
	align-items: center;
}

.v3SizeRow
{
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 6px;
}

.v3SizeRow .v3Name
{
	min-width: 44px;
	font-weight: 600;
	font-size: 12px;
	letter-spacing: 0.12em;
	text-align: right;
	margin-right: 4px;
}

.v3SizeRow.b2 .sizeBtn
{
	color: #6A3460;
	border-color: #d9bfd2;
}

.v3SizeRow.b2 .sizeBtn.active
{
	background: #7A3F6E;
	border-color: #7A3F6E;
	color: #ffffff;
}

/* the closest fit to the other bike's size - a quiet ring, not a selection */
.sizeBtn.fit
{
	box-shadow: 0 0 0 2px #ffffff, 0 0 0 3.5px #5E8F6E;
}

.v3SizeRow.b2 .sizeBtn.fit
{
	box-shadow: 0 0 0 2px #ffffff, 0 0 0 3.5px #7A3F6E;
}

/* the bike you're not looking at is quieter - but its closest fit ring stays clear */
.v3SizeRow.dim .v3Name,
.v3SizeRow.dim .sizeBtn:not(.fit)
{
	opacity: 0.45;
}

#v3Close		/* the shared close (.xBtn) */
{
	position: absolute;
	top: var(--pad, 21px);
	right: var(--pad, 21px);
}



@keyframes fadeIn3d { from { opacity: 0; } to { opacity: 1; } }

/* ---------------------------------------------------------------- GALLERY */

#gallery
{
	--galAccent: var(--ui);
	position: fixed;
	inset: 0;
	z-index: 40;
	display: none;
	background: #ffffff;
	user-select: none;
	touch-action: none;			/* no page scroll or zoom behind it - the photo has its own pinch and pan (gallery.js) */
	overscroll-behavior: none;
}

#galCanvas, #galHud
{
	position: absolute;
	top: 0;
	left: 0;
	touch-action: none;
}

#galHud
{
	pointer-events: none;
}

#gallery.open
{
	display: block;
	animation: fadeIn3d 0.45s ease both;
}

#gallery.closing
{
	animation: galOut 0.45s ease both;
}

@keyframes galOut { from { opacity: 1; } to { opacity: 0; } }

#galHead
{
	position: absolute;
	top: var(--pad, 21px);
	left: var(--pad, 21px);
	right: calc(var(--pad, 21px) + 72px);		/* a long name wraps before the close button */
	text-transform: uppercase;
	z-index: 2;
}

#galMan
{
	font-family: var(--head);
	font-weight: 600;
	font-size: 15px;
	letter-spacing: 0.04em;
	color: #141414;
}

#galModel
{
	font-family: var(--head);
	font-weight: 500;
	font-size: 24px;
	letter-spacing: 0.12em;
	line-height: 1.2;
}

#galCount
{
	margin-top: 8px;
	font-family: var(--data);
	font-size: 12px;
	letter-spacing: 0.1em;
	color: #555555;
}

/* the Gallery covers the whole screen, so it carries its own Buy for the bike on show, at the head's right (buy.css) */
#galHead { box-sizing: border-box; padding-right: 92px; }

/* the box each photo is fitted into, whole, whatever its shape (gallery.js measures it; the photo is drawn on #galCanvas) */
#galStage
{
	position: absolute;
	top: 96px;
	bottom: 132px;
	left: 96px;
	right: 96px;
	pointer-events: none;
}

.galArrow
{
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	border: 1px solid var(--galAccent);
	background: rgba(255, 255, 255, 0.9);
	color: var(--galAccent);
	font-size: 30px;
	line-height: 1;
	padding-bottom: 4px;
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}

.galArrow:hover
{
	background: var(--galAccent);
	color: #ffffff;
}

#galPrev { left: var(--pad, 21px); }
#galNext { right: var(--pad, 21px); }

#galThumbs
{
	position: absolute;
	left: 50%;
	bottom: var(--pad, 21px);
	transform: translateX(-50%);
	max-width: calc(100vw - 2 * var(--pad, 21px));
	display: flex;
	gap: 10px;
	padding: 8px 6px 6px;
	overflow-x: auto;
	scrollbar-width: none;
	z-index: 2;
	touch-action: pan-x;
	overscroll-behavior: contain;
}

#galThumbs::-webkit-scrollbar
{
	display: none;
}

.galThumb
{
	flex: 0 0 auto;
	width: 104px;
	height: 68px;
	border-radius: 10px;
	border: 2px solid transparent;
	background-color: #ffffff;
	background-size: contain;		/* the whole picture on white, any shape */
	background-repeat: no-repeat;
	background-position: center;
	box-shadow: 0 4px 12px rgba(var(--shadowRGB), 0.1);
	opacity: 0.55;
	cursor: pointer;
	transition: opacity 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

.galThumb:hover
{
	opacity: 0.9;
}

.galThumb.on
{
	opacity: 1;
	border-color: var(--galAccent);
	transform: translateY(-4px);
}

#galClose		/* the shared close (.xBtn) */
{
	position: absolute;
	top: var(--pad, 21px);
	right: var(--pad, 21px);
	z-index: 2;
}



/* phones - the photo gets the width, the arrows tuck in over it */
@media (max-width: 700px)
{
	#galStage { left: 12px; right: 12px; top: 110px; bottom: 110px; }
	.galArrow { width: 44px; height: 44px; font-size: 24px; }
	.galThumb { width: 76px; height: 50px; }
}


#marker
{
	position: fixed;
	top: 0;
	left: 0;
	z-index: 1000;
	pointer-events: none;
}
/* phones: no marker (Ben, 12 Nov 2026) - it's for the auto editor, which reads desktop recordings only. Desktop keeps it exactly as it was */
html.phone #marker { display: none; }

/* ---------------------------------------------------------------- WATERMARK (Ben, 12 Nov 2026) */

/* Desktop only: the navy wordmark, faint, in the bottom-left corner of the 2D and the 3D view, so a screenshot or a
   video of the app says whose it is (phones have the logo in their bar). 96px wide (the brand guide's least is 80), never
   clickable, so it never catches a drag. Bottom-left, so it's nowhere near the edit marker (top-left). Under the title
   and every overlay. The 3D view's own is hidden whenever the bottom bar reaches it (view3d.js markRoom) */
.wmark
{
	position: fixed;
	left: var(--pad, 21px);
	bottom: var(--pad, 21px);
	z-index: 4;
	display: block;
	width: 96px;
	height: auto;
	opacity: 0.28;
	pointer-events: auto;		/* a link to the site home page since 14 Nov 2026 (Ben): full strength under the mouse or the keyboard */
	line-height: 0;
	transition: opacity 0.15s ease;
	-webkit-user-select: none;
	user-select: none;
}
#view3d .wmark { position: absolute; z-index: auto; }
.wmark img { display: block; width: 100%; height: auto; pointer-events: none; -webkit-user-drag: none; }
.wmark:hover, .wmark:focus-visible { opacity: 1; }
.wmark:focus-visible { outline: 2px solid #142536; outline-offset: 4px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) { .wmark { transition: none; } }
.wmark.clash { visibility: hidden; }
html.phone .wmark { display: none; }


/* ---------------------------------------------------------------- PHONE LAYOUT (2026-11-05, app.js PHONE LAYOUT) */

/* Only with html.phone (app.js: up to 700px wide, or a touch screen up to 500px tall). Desktop never gets any of it.
   Portrait: blue bar (logo, BIKES, menu), the title right under it, the bike, the orange Best price in thumb reach.
   Landscape: a slimmer bar holding Best price too, the bike gets the height, the title sits small over its top left.
   Layers: MEASURE ON 28, menu 29, bar 30, title in the bar 31 - all under the Gallery (40), cards and BIKES (500), Browse and 3D (600).
   The bar (and so Buy, which lives in it) is over the menu's dim on purpose: nothing may cover Buy (CLAUDE.md). */
#phoneBar, #phoneMenu, #measureChip, #phoneBuy { display: none; }
html.phone #topButtons { display: none; }		/* their jobs are in the bar and the menu */

html.phone #phoneBar, html.phone #v3PhoneBar		/* #v3PhoneBar: the same bar over the 3D view (3D VIEW ON PHONES, below) */
{
	display: flex;
	align-items: center;
	gap: 8px;
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 30;
	height: 56px;
	padding: 0 6px 0 16px;
	box-sizing: border-box;
	background: linear-gradient(135deg, #2B4A66 0%, #142536 100%);		/* the hero gradient */
}
html.phone.land #phoneBar, html.phone.land #v3PhoneBar { height: 52px; }

.pbLogo { display: flex; align-items: center; min-height: 44px; padding: 0 6px; margin-left: -6px; }		/* 44px to tap, the logo 92px wide */
.pbLogo img { display: block; height: 20px; width: auto; }
.pbFill { flex: 1; }

.pbBtn
{
	min-height: 44px;
	padding: 0 18px;
	font-family: var(--head);
	font-weight: 500;
	font-size: 15px;
	letter-spacing: 1px;
	color: #ffffff;
	background: none;
	border: 1px solid rgba(255, 255, 255, 0.45);		/* secondary button on dark */
	border-radius: 999px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.pbBtn:active { background: rgba(255, 255, 255, 0.14); }

/* the menu button: in the same rounded border as BIKES (Ben, 12 Nov 2026), same height, edge and radius, so the bar
   doesn't look lopsided; 10px in from the screen's edge when it's the bar's last button */
.pbIcon
{
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 56px;
	height: 44px;
	padding: 0;
	color: #ffffff;
	background: none;
	border: 1px solid rgba(255, 255, 255, 0.45);		/* secondary button on dark, as BIKES */
	border-radius: 999px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.pbIcon:last-child { margin-right: 4px; }
.pbIcon:active, .pbIcon[aria-expanded="true"] { background: rgba(255, 255, 255, 0.14); }

/* the 3D view's bar (3D VIEW ON PHONES): the menu, then the X that closes the 3D view - the shared close (.xBtn.onDark),
   the hamburger's twin, 10px in from the screen's edge. What one finger does (fade or orbit) is the switch in the dock */
.pbClose { margin-right: 4px; }

/* Best price: portrait under the bike, centred near the bottom; landscape in the bar. Never in the menu */
html.phone #phoneBuy
{
	display: flex;
	justify-content: center;
	position: fixed;
	left: 16px;
	right: 16px;
	bottom: 20px;
	pointer-events: none;
}
html.phone #phoneBuy .buyBtn { pointer-events: auto; min-height: 48px; padding: 0 20px; box-sizing: border-box; font-size: 16px; }
html.phone.land #phoneBuy { position: static; }
html.phone.land #phoneBuy .buyBtn { min-height: 44px; padding: 0 14px; font-size: 15px; }
#phoneBuy .buyBtn[hidden] { display: none; }

/* the title under the bar, sized for a phone (year and link included) */
html.phone #title { right: 16px; }
html.phone #titleMan { font-size: 16px; }
html.phone #titleMan .makerYear { font-size: 13px; }
html.phone #titleModel { font-size: 18px; line-height: 1.15; margin-top: 4px; }
html.phone #titleLink { position: relative; margin-top: 8px; font-size: 14px; }
html.phone #titleLink::after { content: ""; position: absolute; left: -6px; right: -6px; top: 50%; height: 44px; transform: translateY(-50%); }
/* landscape: the title goes in the bar (app.js phoneTitle puts it between the logo and the buttons): maker and year
   over the name, white on the blue, a sage or plum stripe saying which bike, the maker's site as an icon */
html.phone.land #title, html.phone.land #v3Title		/* the 3D view's title goes in its bar the same way */
{
	z-index: 31;
	right: auto;
	display: grid;
	grid-template-columns: 4px minmax(0, 1fr) 44px;
	column-gap: 8px;
	align-items: center;
}
html.phone.land #title::before, html.phone.land #v3Title::before { content: ""; grid-row: 1 / span 2; align-self: stretch; margin: 2px 0; border-radius: 2px; background: #8DBB9B; }
html.phone.land.bike2 #title::before, html.phone.land.bike2 #v3Title::before { background: #B36BA2; }		/* bike colours on dark */
html.phone.land #titleMan, html.phone.land #v3Man { grid-column: 2; font-size: 12px; color: #A9C4DC; }
html.phone.land #titleMan .makerYear, html.phone.land #v3Man .makerYear { font-size: 12px; color: #A9C4DC; }
html.phone.land #titleMan, html.phone.land #v3Man { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }		/* one line in the bar: a line too long for it is cut short, as the name under it is */
html.phone.land #titleModel, html.phone.land #v3Model { grid-column: 2; display: flex; align-items: center; min-width: 0; margin-top: 3px; font-size: 15px; line-height: 1.15; color: #ffffff; }
html.phone.land #titleModel .tName, html.phone.land #v3Model .tName { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
html.phone.land #titleModel .legacyChip, html.phone.land #v3Model .legacyChip { flex: none; color: #A9C4DC; }
html.phone.land #titleLink, html.phone.land #v3Link { grid-column: 3; grid-row: 1 / span 2; justify-content: center; width: 44px; height: 44px; margin: 0; font-size: 0; color: #ffffff; }
html.phone.land #titleLink::after, html.phone.land #v3Link::after { display: none; }
html.phone.land #titleLink svg, html.phone.land #v3Link svg { width: 18px; height: 18px; }
/* landscape: the name's in the bar, so the failed photo note goes on its own slim line straight under the bar, under
   the name, and the bike starts below it (app.js phoneBox leaves the room, phoneTitle sets --noteTop) */
html.phone.land #titleNote { position: absolute; left: 12px; top: var(--noteTop, 100%); margin: 0; font-size: 12px; line-height: 16px; white-space: nowrap; }

/* the menu: drops from the bar on the right, a tap outside (the dimmed back) closes it */
html.phone #phoneMenu { display: block; position: fixed; inset: 0; z-index: 29; }
html.phone #phoneMenu[hidden] { display: none; }
.pmBack { position: absolute; inset: 0; background: rgba(20, 37, 54, 0.18); }
.pmPanel
{
	position: absolute;
	top: 62px;
	right: 8px;
	min-width: 230px;
	max-height: calc(100% - 70px);
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	padding: 6px 0;
	background: #ffffff;
	border-radius: 12px;
	box-shadow: 0 12px 32px rgba(var(--shadowRGB), 0.25);
}
html.phone.land .pmPanel { top: 56px; }
.pmPanel button
{
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	min-height: 48px;
	padding: 0 18px;
	font-family: var(--head);
	font-weight: 500;
	font-size: 15px;
	letter-spacing: 1px;
	text-align: left;
	color: var(--ui);
	background: none;
	border: 0;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.pmPanel button:active { background: var(--uiTint); }
.pmPanel button:disabled { color: #9aa6b2; cursor: default; }
.pmPanel button[hidden] { display: none; }
.pmState
{
	min-width: 30px;
	padding: 3px 10px;
	font-size: 12px;
	text-align: center;
	color: #555555;
	border: 1px solid var(--uiLine);
	border-radius: 999px;
}
.pmPanel button.on .pmState { color: #ffffff; background: var(--ui); border-color: var(--ui); }
/* Measure on a photo that failed its accuracy tests: still listed, greyed, saying why (app.js phoneSync) */
.pmLabel { display: flex; flex-direction: column; gap: 3px; }
.pmWhy { font-family: var(--body); font-weight: 400; font-size: 12px; line-height: 1.2; letter-spacing: 0; text-transform: none; color: #555555; }
.pmWhy[hidden] { display: none; }
.pmPanel button.unavailable { padding-top: 7px; padding-bottom: 7px; }
.pmPanel button.unavailable .pmState { color: #6b7785; }

/* while Measure is on: a tap turns it off, no need to open the menu */
html.phone #measureChip, html.phone #v3MeasureChip		/* #v3MeasureChip: the 3D view's own */
{
	display: flex;
	align-items: center;
	gap: 8px;
	position: fixed;
	right: 12px;
	z-index: 28;
	min-height: 44px;
	padding: 0 16px;
	font-family: var(--head);
	font-weight: 500;
	font-size: 14px;
	letter-spacing: 1px;
	color: #ffffff;
	background: var(--ui);
	border: 0;
	border-radius: 999px;
	box-shadow: 0 4px 12px rgba(var(--shadowRGB), 0.25);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
html.phone #measureChip[hidden], html.phone #v3MeasureChip[hidden] { display: none; }

/* iOS Safari ignores user-scalable=no: a pinch or double tap that starts on the phone's own controls mustn't zoom the
   page either (taps still click); app.js also stops iOS's gesture events on them. The menu list can still scroll */
html.phone #phoneBar, html.phone #measureChip, html.phone .pmBack, html.phone #phoneBuy .buyBtn, html.phone #v3PhoneBar, html.phone #v3MeasureChip { touch-action: none; }
html.phone .pmPanel { touch-action: pan-y; }

/* mobile pass: 44px to tap (or a 44px tap area round it), and no text under 12px - the 3D view's own are under 3D VIEW ON PHONES */
html.phone #cards { padding-top: 72px; }		/* the cards start below the close button, so it never sits on a size button */
html.phone #cards .sizeBtn { min-width: 44px; min-height: 44px; }
html.phone #cards .topBtn, html.phone #picker .topBtn, html.phone #browse .topBtn { min-height: 44px; }
html.phone .pkLogo, html.phone :is(#title, #cards, #picker, #browse, #v3Title) .legacyChip { position: relative; }
html.phone .pkLogo::after { content: ""; position: absolute; left: -6px; right: -6px; top: 50%; height: 44px; transform: translateY(-50%); }
html.phone :is(#title, #cards, #picker, #browse, #v3Title) .legacyChip::after { content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, 44px); height: 44px; transform: translate(-50%, -50%); }
html.phone :is(#cards, #picker, #browse) .legacyChip,
html.phone .cardWins small, html.phone .geoMaker, html.phone .cardNote, html.phone .rowSection,
html.phone .pkCard .rowValue small, html.phone .legacySmall, html.phone .chipRow .lab, html.phone .fOn, html.phone .brBrand { font-size: 12px; }


/* ---------------------------------------------------------------- 3D VIEW ON PHONES (2026-11-05, view3d.js PHONE) */

/* Only with html.phone (app.js PHONE LAYOUT sets it); desktop never gets any of it. The 2D view's blue bar and menu
   over the 3D view; the title under the bar (in it, in landscape); the bike full width under the title; and a dock
   along the bottom (down the right in landscape) with FIT and DIMENSIONS and one sideways scrolling row of sizes per
   bike. FIT or DIMENSIONS opens a sheet in the dock, one at a time: the dock is then the lower half of the screen and
   no more (landscape: the right hand side, full height), so the bike is still there to be seen changing. The view
   cube, the size box and the bottom bar are gone: ON RIMS, ALL SIZES and Measure are in the menu, and FINGER (what a
   one-finger drag does: fade or orbit) is a switch at the dock's left.
   Layers inside #view3d: dock 20, MEASURE ON 28, menu 29, bar 30, title (with Buy) 31, so the menu's dim never covers Buy (CLAUDE.md). */
#v3PhoneBar, #v3Menu, #v3MeasureChip, #v3Dock, #v3None, .shRow { display: none; }

html.phone #v3Right, html.phone #v3Bar, html.phone #v3Close { display: none; }
html.phone #view3d { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
html.phone #view3d input { -webkit-user-select: text; user-select: text; }

html.phone #v3Menu { display: block; position: absolute; inset: 0; z-index: 29; }
html.phone #v3Menu[hidden] { display: none; }
/* portrait: Buy is at the title's right, where the menu used to drop down - it opens below the title instead (view3d.js layout3 sets --menuTop) */
html.phone:not(.land) #v3Menu .pmPanel { top: var(--menuTop, 150px); max-height: calc(100% - var(--menuTop, 150px) - 8px); }
html.phone .pmPanel button.on .pmState, html.phone .shRow.on .pmState { color: #ffffff; background: var(--ui); border-color: var(--ui); }

/* the title: the 2D phone layout's sizes, under the bar (view3d.js layout3 sets its top; in landscape it's in the bar) */
html.phone #v3Title { top: 66px; left: 16px; right: 16px; z-index: 31; }
html.phone #v3Man { font-size: 16px; }
html.phone #v3Man .makerYear { font-size: 13px; }
html.phone #v3Model { font-size: 18px; line-height: 1.15; margin-top: 4px; }
html.phone #v3Link { position: relative; margin-top: 8px; font-size: 14px; }
html.phone #v3Link::after { content: ""; position: absolute; left: -6px; right: -6px; top: 50%; height: 44px; transform: translateY(-50%); }
html.phone.land #v3Title { right: auto; }

/* a bike with no geometry can't be drawn: say so where it would be */
html.phone #v3None
{
	display: block;
	position: absolute;
	left: 16px;
	right: 16px;
	margin: 0;
	font-family: var(--body);
	font-size: 15px;
	line-height: 1.5;
	text-align: center;
	color: #555555;
	pointer-events: none;
}
html.phone #v3None[hidden] { display: none; }

/* the size over the head tube: 12px at least */
html.phone .v3Label { font-size: 12px; }
html.phone .v3Label.main { font-size: 15px; }

/* the dock */
html.phone #v3Dock
{
	display: flex;
	flex-direction: column;
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 20;
	box-sizing: border-box;
	padding: 6px 0 6px;
	background: #ffffff;
	border-top: 1px solid var(--border);
	box-shadow: 0 -6px 18px rgba(var(--shadowRGB), 0.08);
	touch-action: none;		/* no page zoom or scroll from here - the sheet and the size rows scroll themselves */
}
html.phone #v3Dock.open { height: 50%; }

.dkTabs
{
	flex: none;
	display: flex;
	align-items: center;
	gap: 6px;
	min-height: 48px;
	padding: 0 8px 2px;
}

/* THE FINGER SWITCH (Ben, 12 Nov 2026; no word over it since later that day): what a one-finger drag does, as a two-position switch at the row's left. A white pill the
   height of FIT, DIMENSIONS and SIDE with the same edge; FADE (a gradient) on the left, ORBIT (an x-y-z compass) on the
   right, each with its word; a flat brand-blue knob slides onto the one that's on and turns it white. No gloss, no
   shadow. The whole pill is the tap area (96 x 44, 88 wide where the row is tight); a tap goes to the other side */
.dkFinger { flex: none; display: flex; }

.fgSwitch
{
	position: relative;
	display: grid;
	grid-template-columns: 1fr 1fr;
	box-sizing: border-box;
	width: 96px;
	height: 44px;
	padding: 0;
	color: var(--ui);
	background: #ffffff;
	border: 1px solid var(--uiLine);
	border-radius: 999px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.fgKnob
{
	position: absolute;
	top: 2px;
	bottom: 2px;
	left: 2px;
	width: calc(50% - 2px);
	border-radius: 999px;
	background: var(--ui);
	transition: transform 0.16s ease;
}
.fgSwitch[data-mode="rotate"] .fgKnob { transform: translateX(100%); }
.fgSide
{
	position: relative;		/* over the knob */
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 2px;
	min-width: 0;
	transition: color 0.16s ease;
}
.fgSide svg { display: block; }
.fgSide small { font-family: var(--head); font-weight: 500; font-size: 12px; line-height: 12px; letter-spacing: 0.5px; }
.fgSwitch[data-mode="fade"] .fgFade, .fgSwitch[data-mode="rotate"] .fgOrbit { color: #ffffff; }
@media (prefers-reduced-motion: reduce) { .fgKnob, .fgSide { transition: none; } }

.dkTab
{
	flex: none;
	min-width: 48px;
	min-height: 44px;
	padding: 0 12px;
	font-family: var(--head);
	font-weight: 500;
	font-size: 15px;
	letter-spacing: 1px;
	color: var(--ui);
	background: #ffffff;
	border: 1px solid var(--uiLine);
	border-radius: 999px;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.dkTab.on { background: var(--ui); border-color: var(--ui); color: #ffffff; }
/* SIDE, next to DIMENSIONS: it only ever says SIDE (Ben, 12 Nov 2026) - a tap snaps to the side view */
.dkView { flex: none; box-sizing: border-box; width: 58px; min-width: 0; padding: 0; white-space: nowrap; }

/* closes the Fit or Dimensions sheet: the shared close (.xBtn) with the row's own edge, 44px wide so the row still fits a 360px phone */
#dkClose
{
	margin-left: auto;
	width: 44px;
	border-color: var(--uiLine);
}

#dkSheet
{
	flex: 1 1 auto;
	min-height: 0;
	margin-top: 6px;
	border-top: 1px solid var(--border);
	border-bottom: 1px solid var(--border);
	overflow: hidden;
}
#dkSheet[hidden] { display: none; }
#dkSizes { flex: none; padding-top: 4px; }

/* the two sheets: each scrolls inside the dock */
html.phone #v3Left, html.phone #v3DimSheet
{
	position: static;
	display: block;
	box-sizing: border-box;
	width: auto;
	max-width: none;
	height: 100%;
	max-height: none;
	padding: 0 16px 12px;
	overflow-x: hidden;
	overflow-y: auto;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
	touch-action: pan-y;
	pointer-events: auto;
	scrollbar-width: thin;
	scrollbar-color: var(--uiLine) transparent;
}
html.phone #v3Left::-webkit-scrollbar { display: block; width: 4px; }
html.phone #v3Left::-webkit-scrollbar-thumb { background: var(--uiLine); border-radius: 2px; }
html.phone #v3Left[hidden], html.phone #v3DimSheet[hidden] { display: none; }

/* the disclaimer: a strip across the top of the Fit sheet that never scrolls away */
html.phone .fitWarn
{
	display: block;
	margin: 0 -16px;
	padding: 8px 16px;
	border-width: 0 0 1px;
	border-radius: 0;
	font-size: 13px;
}

/* SHOW ON BIKE / LABELS ON BIKE: a row with its state at the right, like the menu's */
html.phone .shRow
{
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	width: 100%;
	min-height: 48px;
	margin: 0;
	padding: 0;
	font-family: var(--head);
	font-weight: 500;
	font-size: 14px;
	letter-spacing: 1px;
	text-align: left;
	color: var(--ui);
	background: none;
	border: 0;
	border-bottom: 1px solid var(--border);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

/* YOUR FIT and YOUR SETUP, flat in the sheet; every box 44px to tap, 16px in the boxes so an iPhone doesn't zoom in */
html.phone #v3Fit, html.phone #v3Setup
{
	display: block;
	padding: 12px 0 4px;
	border: 0;
	border-radius: 0;
	background: none;
	font-size: 14px;
	animation: none;
}
html.phone #v3Setup { display: none; border-top: 1px dashed var(--uiLine); }
html.phone #v3Setup.open { display: block; }
html.phone #v3Fit.min .fitBody { display: block; }		/* a desktop's "minimised" doesn't hide it in the sheet */
html.phone #v3Fit .fitHead { display: none; }		/* the FIT tab says it - the room goes to the boxes */
html.phone #v3Fit { padding-top: 4px; }
html.phone .fitHead { font-size: 15px; letter-spacing: 1px; }
html.phone .fitBody { margin-top: 2px; }
html.phone .fitRow { gap: 8px; min-height: 48px; padding: 0; }
html.phone .fitRow span { font-size: 13px; letter-spacing: 0.08em; }
html.phone .fitRow input { box-sizing: border-box; width: 88px; height: 44px; padding: 0 8px; font-size: 16px; }
html.phone .fitRow select { box-sizing: border-box; max-width: 200px; height: 44px; padding: 0 6px; font-size: 16px; }
html.phone .fitRow i { width: 22px; font-size: 13px; }
html.phone .fitWho { padding-bottom: 4px; }
html.phone #v3Fit summary { padding: 13px 0; font-size: 13px; line-height: 18px; }
html.phone .fitHow svg { max-height: 250px; }
html.phone .fitHow svg g { font-size: 11.5px; }		/* its 1 to 4 come out at 12px or more, even in the narrow landscape sheet */
html.phone .fitHow ol { padding-left: 20px; font-size: 14px; color: #555555; }
html.phone .fitFoot { min-height: 48px; margin-top: 4px; font-size: 13px; }
html.phone .fitFoot .v3Toggle { min-height: 44px; padding: 0 16px; font-size: 13px; }
html.phone .setRow { padding: 7px 0 0; }
html.phone .setRow span { font-size: 13px; }
html.phone .setRow b { font-size: 15px; }
html.phone .setMore { font-size: 13px; }

/* the Dimensions sheet: every number for the bike and size in view */
.dmHead
{
	padding: 12px 0 4px;
	font-family: var(--head);
	font-weight: 600;
	font-size: 15px;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--accentText);
}

.dmRow
{
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
	padding: 7px 0;
	border-bottom: 1px solid #eef0f2;
}
.dmRow:last-child { border-bottom: 0; }

.dmRow span
{
	font-family: var(--head);
	font-weight: 500;
	font-size: 13px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: #555555;
}

.dmRow b
{
	font-family: var(--data);
	font-weight: 700;
	font-size: 15px;
	font-variant-numeric: tabular-nums;
	text-align: right;
	color: #141414;
}

.dmRow small
{
	display: block;
	margin-top: 2px;
	font-family: var(--data);
	font-weight: 500;
	font-size: 12px;
	color: #555555;
}

/* a row of sizes per bike: the model at the left in the bike's colour (a tap brings that bike into view), its sizes
   scrolling sideways beside it - never wrapping, however many there are */
html.phone #v3Sizes { display: block; }

html.phone #v3Dock .v3SizeRow
{
	display: flex;
	flex-wrap: nowrap;
	justify-content: flex-start;
	align-items: center;
	gap: 0;
	height: 44px;		/* a row is exactly its buttons' tap height, so the two rows' tap areas meet and never overlap */
	padding-left: 12px;
}

html.phone #v3Dock .v3Name
{
	flex: none;
	box-sizing: content-box;
	width: var(--nameW, auto);		/* view3d.js buildBar: the longer of the two names, a third of the row at most */
	min-width: 44px;
	max-width: 34%;
	min-height: 44px;
	margin: 0;
	padding: 0 8px 0 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	font-family: var(--head);
	font-weight: 600;
	font-size: 13px;
	letter-spacing: 0.06em;
	text-align: left;
	text-transform: uppercase;
	background: none;
	border: 0;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
html.phone #v3Dock .v3Name:disabled { cursor: default; }

.v3Strip
{
	position: relative;
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: 6px;
	height: 100%;
	padding: 0 12px 0 4px;
	overflow-x: auto;
	overflow-y: hidden;
	scrollbar-width: none;
	overscroll-behavior-x: contain;
	-webkit-overflow-scrolling: touch;
	touch-action: pan-x;
}
.v3Strip::-webkit-scrollbar { display: none; }
/* more sizes off the edge: the row fades out that way */
.v3Strip.moreR { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
.v3Strip.moreL { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px); mask-image: linear-gradient(to right, transparent, #000 28px); }
.v3Strip.moreL.moreR { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); }

/* small to look at (32px tall), 44px to tap: the tap area is an invisible box round each button. 6px between buttons at least 40px wide keeps neighbours' tap areas apart; the strip clips them at the row's edge */
html.phone #v3Dock .sizeBtn { position: relative; flex: none; min-width: 40px; height: 32px; padding: 0 9px; font-size: 13px; }
html.phone #v3Dock .sizeBtn::after { content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, 44px); height: 44px; transform: translate(-50%, -50%); }
html.phone #v3Dock .sizeBtn.active { transform: scale(1.06); }
.v3NoGeo { font-family: var(--body); font-size: 13px; color: #555555; white-space: nowrap; }

/* MEASURE ON: over the top right of the bike's space (view3d.js layout3 sets its top) */
html.phone #v3MeasureChip { position: absolute; }

/* landscape: the bike gets the height, the dock goes down the right hand side - closed, just FIT, DIMENSIONS and the
   size rows in the bottom corner; open, the sheet fills the side from the bar down */
html.phone.land #v3Dock
{
	left: auto;
	width: 300px;		/* FINGER, FIT, DIMENSIONS and SIDE in one row */
	border-left: 1px solid var(--border);
	border-radius: 12px 0 0 0;
	box-shadow: -6px 0 18px rgba(var(--shadowRGB), 0.08);
}
html.phone.land #v3Dock.open { top: 52px; width: 356px; height: auto; border-top: 0; border-radius: 0; }		/* wider while a sheet's open: the row gains its X */
/* the sheet's X goes to the row's LEFT end here: at the right it sat straight under the bar's X that closes the 3D view, and the two read as one */
html.phone.land #dkClose { order: -1; margin-left: 0; margin-right: 2px; }
html.phone.land .dkTabs { gap: 4px; min-height: 44px; padding: 0 6px 2px; }
html.phone.land .fgSwitch { width: 88px; }
html.phone.land #v3Dock { padding: 4px 0 2px; }
html.phone.land #dkSheet { margin-top: 4px; }
html.phone.land #dkSizes { padding-top: 2px; }
html.phone.land .dkTab { min-width: 44px; padding: 0 10px; font-size: 14px; }
html.phone.land .dkView { width: 48px; padding: 0; }
html.phone.land #v3Dock .v3SizeRow { padding-left: 10px; }
html.phone.land #v3Left, html.phone.land #v3DimSheet { padding: 0 12px 10px; }
html.phone.land .fitWarn { margin: 0 -12px; padding: 5px 12px; font-size: 12px; line-height: 1.3; }
html.phone.land .fitRow input { width: 76px; }
html.phone.land .fitRow select { max-width: 150px; }

/* iOS: a pinch or double tap on the dock or the menu mustn't zoom the page (view3d.js stops its gesture events too) */
html.phone .dkTabs, html.phone #v3None { touch-action: none; }

/* a 360px phone: FINGER, FIT, DIMENSIONS, SIDE and the sheet's X in one row */
@media (max-width: 380px)
{
	html.phone:not(.land) .fgSwitch { width: 88px; }
	html.phone:not(.land) .dkTabs { gap: 4px; padding: 0 6px 2px; }
	html.phone:not(.land) .dkTab { min-width: 44px; padding: 0 10px; font-size: 14px; }
	html.phone:not(.land) .dkView { width: 48px; padding: 0; }
}
