/* ============================================================
   Mapcove — design tokens & shared primitives
   Source of truth: "Mapcove App.dc.html" Claude Design bundle.
   Themes switch via data-th="…" on the app root.
   ============================================================ */

/* ---------- Theme tokens ----------
   From the design bundle, with two measured corrections:

   --onacc  foreground for surfaces filled with --acc. Split out of --oncat,
            which the bundle used for both. --oncat is right for the fixed
            category swatches (mid-tone, so near-white sits on them in every
            theme), but night lightens --acc to #FF8A64 while leaving --oncat
            near-white — 2.21:1 on the FAB and every primary CTA, under even
            the 3:1 floor for UI components. Night now uses a warm near-black
            (7.6:1); the light themes keep their old value, so nothing moves.

   --want   the "want to go" stat colour. Split out of --warntx, which it used to
            borrow: an aspirational count is not a warning, and one token meaning
            both would have made any future theme edit choose between them. Starts
            identical in every theme, so nothing moves today.

   --mut    darkened until it clears 4.5:1 on its own --paper (it measured
            3.30–3.80 across the five light themes, and it carries the search
            placeholder, every card meta line and every section caption).
            --mut2 moved down by the same proportion so the two-step muted
            hierarchy survives — in sage and terra the corrected --mut would
            otherwise have landed on top of the old --mut2.
   ---------------------------------------------------------------- */

[data-th="cove"] { --ink: #1C3A33; --paper: #F8F3E7; --card: #FFFDF6; --soft: #FBF7EC; --chip: #F0EAD9; --line: #E7DFCE; --line2: #E3DAC5; --handle: #E0D6BC; --dash: #D6CBAF; --mut: #786E5A; --mut2: #5D5544; --chev: #C4B896; --acc: #E85D42; --danger: #C0392B; --link: #2F6DB2; --good: #2E6B47; --goodbg: #DFF0E2; --warn: #B05A2A; --warntx: #96702A; --want: #96702A; --warnbg: #F7ECD4; --scrim: rgba(23,32,28,.4); --oncat: #FFFDF6; --onacc: #FFFDF6; --mpaper: #F5EFE1; --mstreet: #E6DCC5; --mmajor: #E2D7BE; --mriver: #B7D3DE; --mriverhi: #C8DEE7; --mpark: #DCE8C9; --mbridge: #D9CCAF; --mhill: #E7DBBE; --mtower: #CBB98F; --mcath: #FFFDF6; --mcathbd: #D8CCB0; --mlabel: #AC9F82; --mwater: #8FB5C6; }
[data-th="sage"] { --ink: #22362B; --paper: #F0F4EB; --card: #FBFDF8; --soft: #F4F7EF; --chip: #E6EDDC; --line: #DEE6D2; --line2: #D2DDC2; --handle: #D5DFC6; --dash: #C2D0AC; --mut: #67735B; --mut2: #525F49; --chev: #AEBE9B; --acc: #D96D3F; --danger: #C0392B; --link: #35766B; --good: #2E6B47; --goodbg: #E0EFDC; --warn: #A9502B; --warntx: #8F6A26; --want: #8F6A26; --warnbg: #EFEBD2; --scrim: rgba(20,30,20,.45); --oncat: #FBFDF8; --onacc: #FBFDF8; --mpaper: #ECF1E3; --mstreet: #DFE7D1; --mmajor: #D6E0C5; --mriver: #AFCEC4; --mriverhi: #C2DCD3; --mpark: #CFE3B9; --mbridge: #CBD6B8; --mhill: #DFE5C8; --mtower: #A9BC93; --mcath: #FBFDF8; --mcathbd: #C8D4B4; --mlabel: #9CAC88; --mwater: #86A896; }
[data-th="rose"] { --ink: #402A2E; --paper: #F9EFEC; --card: #FFFAF7; --soft: #FBF3F0; --chip: #F3E2DC; --line: #EDD9D2; --line2: #E4CBC2; --handle: #E1CCC3; --dash: #D4B6AB; --mut: #82685F; --mut2: #6A544C; --chev: #C9A99D; --acc: #C94F35; --danger: #C0392B; --link: #9C5068; --good: #3E7048; --goodbg: #E4EEDE; --warn: #A9502B; --warntx: #8F6A26; --want: #8F6A26; --warnbg: #F2E7CE; --scrim: rgba(40,22,22,.42); --oncat: #FFFAF7; --onacc: #FFFAF7; --mpaper: #F5E9E4; --mstreet: #EADAD2; --mmajor: #E2CFC5; --mriver: #B9CDCB; --mriverhi: #CCDCDA; --mpark: #D5E0BE; --mbridge: #DAC4B8; --mhill: #EBD9C6; --mtower: #C3A188; --mcath: #FFFAF7; --mcathbd: #DCC4B8; --mlabel: #B79A8D; --mwater: #8FAFAC; }
[data-th="lilac"] { --ink: #332B40; --paper: #F2EFF6; --card: #FCFBFE; --soft: #F5F2F9; --chip: #E9E3F1; --line: #E1DAEA; --line2: #D4CBE1; --handle: #D7CFE2; --dash: #C2B6D3; --mut: #726983; --mut2: #5D546E; --chev: #B0A3C4; --acc: #D95970; --danger: #C0392B; --link: #6B5BAA; --good: #3C6B52; --goodbg: #E2EDE4; --warn: #A9502B; --warntx: #8F6A26; --want: #8F6A26; --warnbg: #EFE8D6; --scrim: rgba(28,22,40,.45); --oncat: #FCFBFE; --onacc: #FCFBFE; --mpaper: #EEEAF3; --mstreet: #E1DBE9; --mmajor: #D8D0E2; --mriver: #B8BFDB; --mriverhi: #CACFE6; --mpark: #CFDDC4; --mbridge: #CFC6DC; --mhill: #DFD8E5; --mtower: #A99DC0; --mcath: #FCFBFE; --mcathbd: #CFC4DE; --mlabel: #A395B8; --mwater: #8D9BC0; }
[data-th="terra"] { --ink: #43301C; --paper: #F7EFE3; --card: #FFFBF3; --soft: #FBF4E7; --chip: #F1E4CD; --line: #EADCC4; --line2: #E0D0B2; --handle: #DDCEAF; --dash: #D2BF9E; --mut: #7C6B4F; --mut2: #64563E; --chev: #C7B491; --acc: #C75B2B; --danger: #C0392B; --link: #A8622D; --good: #4E7040; --goodbg: #E5EDD8; --warn: #A9502B; --warntx: #8F6A26; --want: #8F6A26; --warnbg: #F2E6CB; --scrim: rgba(42,30,16,.42); --oncat: #FFFBF3; --onacc: #FFFBF3; --mpaper: #F3EAD7; --mstreet: #E7D9BD; --mmajor: #DFD0B0; --mriver: #B9CFC9; --mriverhi: #CBDDD6; --mpark: #D9E0B7; --mbridge: #D6C5A3; --mhill: #E5D6B6; --mtower: #C4AC81; --mcath: #FFFBF3; --mcathbd: #D9C7A6; --mlabel: #B3A182; --mwater: #8FB3AB; }
[data-th="night"] { --ink: #F2F5EF; --paper: #101714; --card: #1B2521; --soft: #212D27; --chip: #2A3831; --line: #303F37; --line2: #3D4E45; --handle: #495B51; --dash: #56695E; --mut: #9EB1A5; --mut2: #C2CFC5; --chev: #6A7D71; --acc: #FF8A64; --danger: #F28B7D; --link: #8FC0F0; --good: #98DDB0; --goodbg: #24402E; --warn: #F0A873; --warntx: #EBD094; --want: #EBD094; --warnbg: #403927; --scrim: rgba(0,0,0,.62); --oncat: #FFF9F2; --onacc: #2A1408; --mpaper: #131C18; --mstreet: #25322B; --mmajor: #2D3D34; --mriver: #2E5A75; --mriverhi: #3B6F8D; --mpark: #1E4030; --mbridge: #3B5045; --mhill: #34493D; --mtower: #5D7567; --mcath: #2B3B33; --mcathbd: #4C6457; --mlabel: #6C8375; --mwater: #6494AC; }

/* ---------- Base ---------- */

:root {
	--font-display: "Bricolage Grotesque", "Segoe UI", system-ui, sans-serif;
	--font-body: "Figtree", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body {
	margin: 0;
	padding: 0;
	height: 100%;
	overscroll-behavior: none;
	background: #101714;
	/* The shell is a fixed, full-viewport stage that scrolls internally — the
	   document itself never should. Belt and braces for desktop WebViews, where
	   a single stray pixel of overflow shows a real scrollbar. */
	overflow: hidden;
}

/* The system-bar insets live here, on the shell, and nowhere else.
   They cannot go on .mc-viewport: every child of it — each screen, the nav pill,
   every sheet — is absolutely positioned, and an absolutely positioned box
   resolves its offsets against its container's *padding* box, so padding there
   is invisible to all of them. (That is why the padding-bottom this replaces
   never actually lifted anything, on any platform.) .mc-viewport is an in-flow
   flex child, so padding here really does shrink it, and the absolute children
   inset with it — one rule moves the top bar, the nav pill, every sheet and
   every pinned CTA together.

   --sat/--sab are pushed in by the host when the WebView can't work them out
   itself; on Android, Chromium reports only the display cutout through env(),
   never the status or navigation bars (see IDeviceServices.GetSafeAreaInsets).
   iOS resolves env() properly and never sets them. The padding band paints in
   --paper, so it reads as the app's own paper in every theme. */
.mc-app {
	position: fixed;
	inset: 0;
	overflow: hidden;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--font-body);
	display: flex;
	flex-direction: column;
	padding-top: var(--sat, env(safe-area-inset-top));
	padding-bottom: var(--sab, env(safe-area-inset-bottom));
	-webkit-tap-highlight-color: transparent;
	user-select: none;
	-webkit-user-select: none;
}

/* No scroller inside the shell paints scrollbar chrome. Phone WebViews draw
   overlay scrollbars that fade away; desktop ones (WebView2, WKWebView on macOS)
   draw classic 15px bars with arrow buttons that sit *in* the layout — a grey
   rail down the side of a login screen, and width stolen from every sheet. The
   app is phone-shaped in both, so the phone behaviour is the correct one
   everywhere. Scrolling itself is untouched: wheel, drag and keys all still work. */
.mc-app,
.mc-app * {
	scrollbar-width: none;
	-ms-overflow-style: none;
}

	.mc-app::-webkit-scrollbar,
	.mc-app *::-webkit-scrollbar {
		width: 0;
		height: 0;
		display: none;
	}

.mc-app input,
.mc-app textarea {
	user-select: text;
	-webkit-user-select: text;
}

/* The stage every screen and sheet positions against. Deliberately carries no
   safe-area padding of its own — see .mc-app, which is where it has to live. */
.mc-viewport {
	flex: 1;
	position: relative;
	overflow: hidden;
}

/* --mut, not a hardcoded hex: placeholders carry real instructions ("What went
   wrong?"), and --mut is already contrast-tuned per theme (4.5:1+ on --paper,
   and every --soft input fill is lighter than its --paper). */
.mc-app input::placeholder,
.mc-app textarea::placeholder { color: var(--mut); opacity: 1; }

.mc-app a { color: var(--ink); }
.mc-app a:hover { color: var(--acc); }

.mc-app input,
.mc-app textarea,
.mc-app button { font-family: inherit; }

.mc-app input:focus,
.mc-app textarea:focus { outline: 2px solid var(--ink); outline-offset: -1px; }

.mc-app button { -webkit-tap-highlight-color: transparent; }

/* ---------- Animations ---------- */

@keyframes mcSlideUp { from { transform: translateY(72px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes mcFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes mcPop { 0% { transform: scale(.3); opacity: 0; } 70% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } }
/* The reverse of mcPop, minus the overshoot: leaving should read as quicker and
   less attention-seeking than arriving. */
@keyframes mcPopOut { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(.3); opacity: 0; } }
@keyframes mcPulse { 0% { transform: scale(.5); opacity: .7; } 100% { transform: scale(2.4); opacity: 0; } }
@keyframes mcToast { from { transform: translate(-50%, 14px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } }

/* ---------- Shared primitives ---------- */

/* Uppercase section label, e.g. TAGS / YOUR NOTE / CATEGORY */
.mc-section-label {
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 1.4px;
	color: var(--mut);
}

/* Bottom sheet base — pair with a screen-specific height/padding class */
.mc-sheet {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	background: var(--card);
	border-radius: 26px 26px 0 0;
	box-shadow: 0 -16px 44px rgba(15, 25, 20, .28);
	animation: mcSlideUp .36s cubic-bezier(.22, 1.1, .36, 1);
}

.mc-sheet-handle {
	width: 36px;
	height: 4px;
	border-radius: 2px;
	background: var(--handle);
	margin: 0 auto;
}

/* Fixed, not absolute: .mc-viewport stops at the safe-area padding, and a scrim
   that leaves the status-bar band (now full-bleed map on the map tab) at full
   brightness reads as a seam across the top of the screen. */
.mc-backdrop {
	position: fixed;
	inset: 0;
	background: var(--scrim);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	z-index: 8;
	animation: mcFade .2s ease;
}

/* Round icon button (close buttons on sheets). Draws 34px; the pseudo-element
   lifts the tap target to 44px, same technique as .mc-switch below. */
.mc-close-btn {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: none;
	background: var(--chip);
	color: var(--ink);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
}

.mc-close-btn::after {
	content: "";
	position: absolute;
	inset: -5px;
}

/* Coral gradient CTA (Save / Log in / Try Pro / onboarding Next) */
.mc-btn-accent {
	border: none;
	background: linear-gradient(180deg, color-mix(in srgb, var(--acc) 86%, #FFF6EC), var(--acc));
	color: var(--onacc);
	font-weight: 700;
	cursor: pointer;
	box-shadow: 0 10px 24px color-mix(in srgb, var(--acc) 42%, transparent), inset 0 1px 0 rgba(255, 255, 255, .28);
}

/* Solid ink pill button */
.mc-btn-ink {
	border: none;
	border-radius: 999px;
	background: var(--ink);
	color: var(--paper);
	font-weight: 700;
	cursor: pointer;
}

/* Outlined pill/box button */
.mc-btn-outline {
	border: 1.5px solid var(--line2);
	background: none;
	color: var(--ink);
	font-weight: 700;
	cursor: pointer;
}

/* Standard input */
.mc-input {
	width: 100%;
	background: var(--soft);
	border: 1px solid var(--line);
	border-radius: 13px;
	padding: 0 13px;
	font-size: 13.5px;
	font-weight: 500;
	color: var(--ink);
}

/* Horizontal scroller with hidden scrollbar */
.mc-scroll-x {
	display: flex;
	overflow-x: auto;
	scrollbar-width: none;
}
.mc-scroll-x::-webkit-scrollbar { display: none; }

/* Selectable chip (base look; active variants set inline or in scoped css) */
.mc-chip {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	gap: 6px;
	border-radius: 999px;
	font-weight: 600;
	cursor: pointer;
	background: var(--card);
	color: var(--ink);
	border: 1.5px solid var(--line2);
}

/* The two selected states, as classes rather than the inline style strings this
   used to be built from — one definition, so a chip looks the same wherever it
   is used.

   .on   the user deliberately filtered by this. Full ink: loud on purpose.
   .soft current, but only because it is the default — the "All" chip while
         nothing is selected. Emphasis has to mark a *deviation* from the
         default, otherwise the loudest control on the map is the one that
         means "nothing is filtered", and the eye keeps landing on it.

   The soft state differs from a plain chip by its *edge*, not by its fill.
   Filling it with --chip instead looked right on paper and vanished on the
   map: measured against the basemap it is 1.05:1, next to 1.13:1 for the
   --card the other chips use — a chip the same colour as the ground it floats
   on, with no border to give it a shape. Keeping --card and firming the border
   to --chev (1.72:1 vs --line2's 1.21:1) reads as "this one is current"
   without borrowing any of the ink fill's weight. */
.mc-chip.on {
	background: var(--ink);
	color: var(--paper);
	border-color: var(--ink);
}

.mc-chip.soft {
	background: var(--card);
	color: var(--ink);
	border-color: var(--chev);
}

/* Switch toggle (dark theme, link sharing) */
.mc-switch {
	width: 40px;
	height: 24px;
	border-radius: 999px;
	position: relative;
	border: none;
	cursor: pointer;
	padding: 0;
	transition: background .2s;
	background: var(--dash);
	flex: 0 0 auto;
}

/* The switch paints 40×24 — the size the design draws. 24 already meets the AA
   target-size minimum (WCAG 2.5.8); 44 is the Apple HIG / AAA figure, and this
   is the one control here small enough in both axes to be worth taking there.
   Expanded with a pseudo-element rather than by growing the track, which would
   look wrong. */
.mc-switch::after {
	content: "";
	position: absolute;
	left: -2px;
	right: -2px;
	top: -10px;
	bottom: -10px;
}
.mc-switch.on { background: var(--good); }
.mc-switch .knob {
	position: absolute;
	top: 2px;
	left: 2px;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: var(--card);
	box-shadow: 0 1px 3px rgba(28, 44, 38, .3);
	transition: left .2s;
}
.mc-switch.on .knob { left: 18px; }

/* Small square checkbox (visible-to-friends, list membership) */
.mc-checkbox {
	border-radius: 6px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--card);
	border: 1.5px solid var(--dash);
	flex: 0 0 auto;
}
.mc-checkbox.on { background: var(--ink); border-color: var(--ink); }

/* Display-face heading */
.mc-display {
	font-family: var(--font-display);
	font-weight: 800;
	letter-spacing: -0.4px;
}

/* ---------- MapLibre marker DOM (created by map.js — must be global CSS) ---------- */

.mlpin {
	width: 44px;
	height: 44px;
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	/* MapLibre positions markers with `position: absolute; top:0; left:0` + a
	   transform from the container origin — do NOT override that `position`
	   (relative would anchor the transform to the in-flow slot and the pins
	   drift as the map re-projects on zoom). `absolute` still establishes the
	   containing block for the name label. */
	transition: opacity .25s;
}

.mlpin.dim {
	opacity: 0.18;
	pointer-events: none;
}

/* Filtered out: the marker stays in the DOM just long enough to play the exit,
   and map.js removes it when the animation ends. Untappable in the meantime —
   it is already gone as far as the app is concerned. */
.mlpin.leaving {
	pointer-events: none;
}

	.mlpin.leaving .mlpin-body {
		animation: mcPopOut .2s ease forwards;
	}

.mlpin-body {
	width: 44px;
	height: 44px;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	animation: mcPop .35s ease;
}

.mlpin-drop {
	width: 30px;
	height: 30px;
	border-radius: 50% 50% 50% 0;
	transform: rotate(-45deg);
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 3px 9px rgba(28, 44, 38, .32);
	transition: width .18s, height .18s;
	border: 2.5px solid var(--oncat);
}

.mlpin.big .mlpin-drop {
	width: 38px;
	height: 38px;
}

.mlpin-drop svg {
	transform: rotate(45deg);
}

/* Cluster disc. Not a teardrop: a teardrop points at a place, and this points
   at several. Outer box is fixed at 60x60 and only .mclus-body resizes —
   MapLibre computes an anchor:"center" marker's offset from the element's
   measured box, so resizing the outer element makes every disc visibly drift
   as its count crosses a tier. Same "leave MapLibre's positioning alone" rule
   as .mlpin above. */
.mclus {
	width: 60px;
	height: 60px;
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: opacity .25s;
}

.mclus-body {
	position: relative;
	min-width: 46px;
	height: 46px;
	padding: 0 10px;
	border-radius: 999px;
	background: var(--card);
	border: 2.5px solid var(--line2);
	box-shadow: 0 3px 9px rgba(28, 44, 38, .32);
	display: flex;
	align-items: center;
	justify-content: center;
	animation: mcPop .35s ease;
	transition: min-width .18s, height .18s;
}

/* A hairline second ring — enough to read as "more behind this one" without
   drawing a fake stack of discs. */
.mclus-body::before {
	content: "";
	position: absolute;
	inset: 4px;
	border-radius: 999px;
	border: 1px solid var(--line);
}

/* A pill, not a circle: "999+" grows sideways instead of spilling out. */
.mclus.s2 .mclus-body { min-width: 52px; height: 52px; }
.mclus.s3 .mclus-body { min-width: 60px; height: 60px; }

.mclus-n {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: 15px;
	letter-spacing: -.3px;
	line-height: 1;
	color: var(--ink);
}

.mclus.s2 .mclus-n { font-size: 16px; }
.mclus.s3 .mclus-n { font-size: 17px; }

.mclus.leaving {
	pointer-events: none;
}

	.mclus.leaving .mclus-body {
		animation: mcPopOut .2s ease forwards;
	}

/* Legs of a fanned-out pile, drawn from the hub outward. One svg per disc
   rather than an element per member. */
.mcspider-legs {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	overflow: visible;
	pointer-events: none;
}

	.mcspider-legs line {
		stroke: var(--dash);
		stroke-width: 1.5;
		opacity: .55;
	}

/* Pin names have no styles here: they are drawn by MapLibre's `mc-pin-labels`
   symbol layer (see map.js) so they get collision detection, which DOM labels
   never do. Their colour comes from --ink / --card, read off .mc-app. */

.mluser {
	width: 28px;
	height: 28px;
	/* Same as .mlpin: leave MapLibre's absolute positioning intact. */
	pointer-events: none;
}

.mluser-pulse {
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: rgba(47, 109, 178, .25);
	animation: mcPulse 1.8s ease-out infinite;
}

.mluser-core {
	position: absolute;
	left: 7px;
	top: 7px;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: #2F6DB2;
	border: 3px solid var(--card);
	box-sizing: border-box;
	box-shadow: 0 1px 4px rgba(28, 44, 38, .4);
}

/* The map host is full-bleed (MapScreen's .map-host), so MapLibre's own corners
   now reach under the home indicator / navigation bar. Lift the bottom ones back
   out: the attribution lives there and has to stay readable — it is the licence. */
.maplibregl-ctrl-bottom-left,
.maplibregl-ctrl-bottom-right {
	padding-bottom: var(--sab, env(safe-area-inset-bottom));
}

/* Keep MapLibre chrome subtle and on-theme */
.maplibregl-ctrl-attrib {
	font-size: 10px;
	background: color-mix(in srgb, var(--card) 75%, transparent) !important;
	color: var(--mut);
}
.maplibregl-ctrl-attrib a { color: var(--mut); }
