:root {
	--bg: #faf7f2;
	--surface: #ffffff;
	--ink: #2b2535;
	--ink-soft: #6a6275;
	--ink-faint: #9a93a3;
	--line: #ece7e0;
	--brand: #5b4b8a;
	--brand-soft: #efeaf7;
	--danger: #b5483a;
	/* The ✓ / ✕ pair on skills and impacts (2026-11-07): a dark green for a
	   positive log, maroon for a negative one. */
	--plus: #1f7a45;
	--minus: #7d1f35;
	--radius: 16px;
	--shadow: 0 6px 24px rgba(40, 30, 60, 0.12);
	--safe-top: env(safe-area-inset-top, 0px);
	--safe-bottom: env(safe-area-inset-bottom, 0px);
	/* Where the floating tab bar sits, and how much room content leaves for it
	   (Ross, 2026-09-24: "move that down if possible, all screens"). On a
	   phone with a home indicator (safe-bottom 34) the bar now sits 22px off
	   the edge instead of 44, reaching into the top of the safe area while
	   staying well clear of the indicator itself (which lives in the lowest
	   ~13px). Without one (SE, desktop) it keeps its old 10px. Everything
	   that has to clear the bar reads --tabs-clear, so the bar and the room
	   left for it cannot drift apart. */
	--tabs-lift: max(10px, calc(var(--safe-bottom) - 12px));
	--tabs-clear: calc(var(--tabs-lift) + 76px);
}

* {
	box-sizing: border-box;
	-webkit-tap-highlight-color: transparent;
}

html,
body {
	margin: 0;
	padding: 0;
	height: 100%;
}

body {
	background: var(--bg);
	color: var(--ink);
	font-family:
		-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, Helvetica, Arial,
		sans-serif;
	font-size: 16px;
	line-height: 1.4;
	-webkit-font-smoothing: antialiased;
}

#app {
	max-width: 560px;
	margin: 0 auto;
	min-height: 100%;
	display: flex;
	flex-direction: column;
	position: relative;
	z-index: 1;
}

/* Faint, high-res zoomed wheel crop behind the content — a different one per
   tab. Sits above the base bg, below all content; never intercepts taps. */
#bg-art {
	position: fixed;
	inset: 0;
	z-index: 0;
	background-position: center top;
	background-size: cover;
	background-repeat: no-repeat;
	opacity: 0.72;
	pointer-events: none;
	transition: opacity 0.35s ease;
	/* Very slow drift so the wheel feels alive without distracting. The base
	   scale (via --bg-scale-a/b below) overscans so the rotate/translate never
	   reveals the edges. */
	transform-origin: center;
	will-change: transform;
	animation: bg-drift var(--bg-duration, 120s) ease-in-out infinite;
	animation-direction: var(--bg-dir, normal);
	animation-delay: var(--bg-delay, 0s);
}
:root[data-theme='light'] #bg-art {
	opacity: 0.6;
}
@keyframes bg-drift {
	0%,
	100% {
		transform: scale(var(--bg-scale-a, 1.18)) translate(calc(var(--bg-tx, 3%) * -1), calc(var(--bg-ty, 2%) * -1))
			rotate(calc(var(--bg-rot, 4deg) * -1));
	}
	50% {
		transform: scale(var(--bg-scale-b, 1.22)) translate(var(--bg-tx, 3%), var(--bg-ty, 2%)) rotate(var(--bg-rot, 4deg));
	}
}
/* Each tab gets its own hue (some pulled toward yellow/green/pink so the top
   of the crop isn't the same colour everywhere), zoom range, drift speed,
   spin direction, and starting phase (negative delay, so they're not all
   mid-loop in sync) — set via data-view, JS-driven in switchView()/setBgArt().
   Feelings stays native (no hue shift) as the app's home colour. */
#bg-art[data-view='care'] {
	filter: hue-rotate(330deg) saturate(1.15);
	--bg-duration: 95s;
	--bg-dir: reverse;
	--bg-delay: -22s;
	--bg-scale-a: 1.2;
	--bg-scale-b: 1.27;
	--bg-rot: 6deg;
	--bg-tx: 4%;
	--bg-ty: 3%;
}
#bg-art[data-view='sessions'] {
	filter: hue-rotate(260deg) saturate(1.15);
	--bg-duration: 150s;
	--bg-delay: -70s;
	--bg-scale-a: 1.14;
	--bg-scale-b: 1.18;
	--bg-rot: 3deg;
	--bg-tx: 2%;
	--bg-ty: 2.5%;
}
#bg-art[data-view='insights'] {
	filter: hue-rotate(185deg) saturate(1.2);
	--bg-duration: 105s;
	--bg-dir: reverse;
	--bg-delay: -40s;
	--bg-scale-a: 1.19;
	--bg-scale-b: 1.25;
	--bg-rot: 5deg;
	--bg-tx: 3.5%;
	--bg-ty: 2.5%;
}
#bg-art[data-view='chat'] {
	filter: hue-rotate(95deg) saturate(1.15);
	--bg-duration: 135s;
	--bg-delay: -85s;
	--bg-scale-a: 1.17;
	--bg-scale-b: 1.23;
	--bg-rot: 4.5deg;
	--bg-tx: 3%;
	--bg-ty: 3%;
}
/* The capture/care/session donut is a self-contained circle (transparent
   field) so the hole shows the sheet through it in either theme. Each modal
   gets its own spin direction/speed/start point (the `rotate` property is
   animated standalone, alongside the static translate(-50%,-50%) centring
   transform, so it survives reduced-motion cleanly) plus a slow "pong ball"
   drift (also a standalone property, `translate`) so the three don't all
   move in lockstep. */
#modal .sheet::before,
#care-modal .sheet::before,
#sessions-modal .sheet::before {
	background: url('./icons/bg-capture.png') center / contain no-repeat;
	will-change: rotate, translate;
}
@keyframes donut-spin {
	from {
		rotate: 0deg;
	}
	to {
		rotate: 360deg;
	}
}
@keyframes donut-bounce-a {
	0%,
	100% {
		translate: -2% -1.5%;
	}
	50% {
		translate: 2% 1.5%;
	}
}
@keyframes donut-bounce-b {
	0%,
	100% {
		translate: -3% 2%;
	}
	33% {
		translate: 3% 3%;
	}
	66% {
		translate: 2% -3%;
	}
}
@keyframes donut-bounce-c {
	0%,
	100% {
		translate: 2% -2%;
	}
	40% {
		translate: -3% 1%;
	}
	70% {
		translate: 1% 3%;
	}
}
/* Capture — the original: clockwise, unhurried, centred, a light drift. */
#modal .sheet::before {
	transform: translate(-50%, -50%);
	animation:
		donut-spin 90s linear infinite,
		donut-bounce-a 26s ease-in-out infinite;
}
/* Care — counter-clockwise, slower, zoomed out a touch, starts mid-turn. */
#care-modal .sheet::before {
	transform: translate(-50%, -50%) scale(0.92);
	animation:
		donut-spin 130s linear infinite reverse,
		donut-bounce-b 21s ease-in-out infinite;
	animation-delay: -46s, -6s;
}
/* Sessions — clockwise but brisker, zoomed in a touch, starts elsewhere again. */
#sessions-modal .sheet::before {
	transform: translate(-50%, -50%) scale(1.08);
	animation:
		donut-spin 75s linear infinite,
		donut-bounce-c 16s ease-in-out infinite;
	animation-delay: -12s, -9s;
}
/* Respect users who ask for less motion. */
@media (prefers-reduced-motion: reduce) {
	#bg-art,
	#modal .sheet::before,
	#care-modal .sheet::before,
	#sessions-modal .sheet::before {
		animation: none;
	}
}

/* ---- App bar ---- */
/* Transparent bar — the background art shows through; each control sits in its
   own tips-style lozenge, which carries the contrast instead of the bar. */
.app-bar {
	position: sticky;
	top: 0;
	z-index: 10;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: calc(var(--safe-top) + 2px) 14px 8px;
	background: transparent;
}

/* ONE full-width lozenge for the whole header row — same recipe as .tips-bar
   and the cards, plus a blur so scrolled content stays legible underneath.
   Replaces the old per-control chrome (a pill for the brand, a rounded square
   for the menu), which read as three unrelated fragments above full-width
   content. The + keeps its solid purple circle: it is the primary action here
   and on every other screen, so it is the one thing that should stand out
   inside the lozenge rather than dissolve into it. */
.bar-inner {
	flex: 1; /* span the bar — the whole point is a FULL-width lozenge */
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 5px;
	border-radius: 18px;
	background: color-mix(in srgb, var(--brand) 10%, var(--surface));
	border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--line));
	backdrop-filter: saturate(1.3) blur(9px);
	-webkit-backdrop-filter: saturate(1.3) blur(9px);
}
.brand {
	display: flex;
	align-items: center;
	gap: 8px;
	height: 48px;
	/* Inside the lozenge now, so no chrome of its own — just the optical
	   padding that keeps the wheel mark off the rounded edge. */
	padding: 0 10px 0 3px;
	margin: 0;
	background: none;
	border: none;
	border-radius: 14px;
	color: inherit;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.brand:active {
	transform: scale(0.96);
}
.brand-mark {
	/* Optically matched to the 48px menu button rather than set equal to it:
	   a circle reads smaller than a square of the same nominal size, so 46px
	   is where the two look the same height (Ross, 2026-08-01). */
	width: 46px;
	height: 46px;
	display: block;
}
.brand-name {
	font-size: 21px;
	font-weight: 700;
	letter-spacing: -0.02em;
}

.bar-actions {
	display: flex;
	align-items: center;
	gap: 8px;
}
.icon-btn {
	width: 48px;
	height: 48px;
	color: var(--ink-soft);
	display: grid;
	place-items: center;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	/* Sits inside .bar-inner, so no fill of its own (same surface would be
	   invisible anyway) — but it keeps the soft border, which is what makes
	   it read as a button rather than a floating glyph (Ross, 2026-08-01). */
	background: none;
	border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--line));
	border-radius: 14px;
}
.icon-btn:active {
	background: var(--line);
}
/* `display: grid` above beats the UA's [hidden] rule, so until 1.1.1 the
   Needs 💬 showed on EVERY device, Apple Intelligence or not, and opened a
   chat that could not answer. The tests asserted the attribute, not what was
   on screen, so they passed (needs-hidden.mjs and nav.mjs now check
   visibility). */
.icon-btn[hidden] {
	display: none;
}
.menu-dots {
	/* 27 → 30 alongside the wider gaps: together they let the glyph sit in
	   the button rather than float in the middle of it. */
	width: 32px;
	height: 30px;
	display: block;
}
.menu-dots .md-step {
	fill: var(--brand);
}
.menu {
	position: absolute;
	/* Clears the header lozenge: 2px bar padding + 60px lozenge (48px control
	   + 2x5px padding + borders) + a 6px gap. */
	top: calc(var(--safe-top) + 68px);
	/* Both edges on the header lozenge's 14px gutters (build 79, Ross). It used
	   to set only right: 16px, which was fine while the menu was a short list,
	   but the 1.1.1 disclaimer paragraph made it as wide as it could get, and
	   with no left edge that meant flush against the left of the screen. */
	left: 14px;
	right: 14px;
	z-index: 50;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 12px;
	box-shadow: var(--shadow);
	padding: 6px;
	display: flex;
	flex-direction: column;
	min-width: 170px;
}
.menu[hidden] {
	display: none;
}
.menu button {
	border: none;
	background: transparent;
	color: var(--ink);
	font: inherit;
	font-size: 15px;
	text-align: left;
	padding: 11px 12px;
	border-radius: 8px;
	cursor: pointer;
}
.menu button:active {
	background: var(--line);
}
.menu-section {
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-faint);
	padding: 8px 12px 4px;
}
.menu-link {
	display: block;
	width: calc(100% - 16px);
	margin: 0 8px 6px;
	padding: 9px 8px;
	background: none;
	border: 0;
	font: inherit;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--ink-soft);
	text-decoration: none;
	text-align: left;
	border-radius: 9px;
	cursor: pointer;
}
.menu-link:active {
	background: var(--line);
}
.theme-seg {
	display: flex;
	gap: 4px;
	padding: 2px 8px 4px;
}
.theme-seg button {
	flex: 1;
	text-align: center;
	padding: 7px 0;
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-soft);
	background: var(--bg);
	border: 1px solid var(--line);
	border-radius: 9px;
}
.theme-seg button.on {
	background: var(--brand);
	color: var(--on-brand, #fff);
	border-color: var(--brand);
}
:root[data-theme='dark'] .theme-seg button.on {
	color: #1a1622;
}
.add-btn {
	width: 48px;
	height: 48px;
	border-radius: 50%;
	border: none;
	background: var(--brand);
	color: #fff;
	font-size: 29px;
	line-height: 1;
	display: grid;
	place-items: center;
	cursor: pointer;
	box-shadow: 0 4px 16px color-mix(in srgb, var(--brand) 55%, transparent);
	transition: transform 0.08s ease;
}
.add-btn:active {
	transform: scale(0.92);
}
/* the hidden attribute must beat display:grid (used to hide + on the Care tab) */
.add-btn[hidden] {
	display: none;
}
.plus {
	margin-top: -2px;
}

/* ---- List ---- */
.list {
	flex: 1;
	padding: 12px 14px var(--tabs-clear);
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/* section dividers — "Since last session" vs "Historical" */
.section-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 10px;
	margin: 6px 2px 2px;
}
.section-head:not(:first-child) {
	margin-top: 18px;
}
.section-label {
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
}
/* Faint is fine on the dark field; light mode needs the darker tone to stay
   readable over the background art. */
:root[data-theme='dark'] .section-label {
	color: var(--ink-faint);
}
.expand-all {
	border: none;
	background: transparent;
	color: var(--brand);
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
	padding: 2px 4px;
}

/* Compact cards filled with their emotion's colour. Uniform height when
   collapsed; tap to expand sensations/note and Edit / Delete. */
.entry {
	background: var(--tint, var(--surface));
	border: 1px solid var(--edge, var(--line));
	border-radius: 14px;
	overflow: hidden;
}
.entry-row {
	display: flex;
	align-items: center;
	gap: 10px;
	min-height: 60px;
	padding: 10px 14px;
	cursor: pointer;
}
/* feeling + timestamp share one baseline so they read as a single line; the
   whole line is centred in the row, and the caret centres independently */
.entry-line {
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: baseline;
	gap: 10px;
}
.entry-main {
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: baseline;
	gap: 6px;
}
.entry-emotion {
	font-size: 16.5px;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--ink);
	white-space: nowrap;
	flex-shrink: 0;
}
/* The core › secondary path lives in the EXPANDED detail, directly under the
   feeling name and above the source emoji — not on the headline row. On the
   headline it competed with the (now weekday-bearing) timestamp for width and
   was the one that lost, truncating to "(Happy › Op…". Down here it has the
   full width and the collapsed card reads cleaner for losing it. */
.entry-path {
	font-size: 12px;
	color: var(--ink-soft);
	display: block;
	margin-bottom: 5px;
}
.entry-when {
	font-size: 12px;
	color: var(--ink-soft);
	white-space: nowrap;
	flex-shrink: 0;
}
/* On the light theme's solid colour tints, mid-grey vanishes — use near-ink. */
:root[data-theme='light'] .entry-path,
:root[data-theme='light'] .entry-when,
:root[data-theme='light'] .entry-note {
	color: color-mix(in srgb, var(--ink) 93%, transparent);
}
/* Follow-up flag, sat just left of the timestamp. Hollow + faint when unset;
   gold when flagged; muted once the entry has dropped below the session line
   (i.e. been discussed). */
.entry-flag {
	flex-shrink: 0;
	align-self: center;
	background: transparent;
	border: 0;
	margin: 0;
	padding: 4px;
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
	color: var(--ink-faint);
	opacity: 0.6;
	transition: color 0.15s ease, opacity 0.15s ease, transform 0.12s ease;
}
:root[data-theme='light'] .entry-flag {
	color: color-mix(in srgb, var(--ink) 75%, transparent);
	opacity: 0.9;
}
.entry-flag:active {
	transform: scale(0.88);
}
.entry-flag.on {
	color: #ffb300;
	opacity: 1;
	/* soft shadow lifts the star off the pale hued cards in light mode */
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
.entry-flag.on.discussed {
	color: var(--ink-faint);
	opacity: 0.85;
	text-shadow: none;
}
.entry-caret {
	align-self: center;
	width: 16px;
	height: 16px;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--c, var(--ink-faint));
	font-size: 13px;
	line-height: 1;
	flex-shrink: 0;
	transition: transform 0.18s ease;
}
.entry.open .entry-caret {
	transform: rotate(180deg);
}
.entry-detail {
	display: none;
	/* top padding traded for the path line, so the open card grows by little
	   more than the line itself */
	padding: 0 14px 12px;
}
.entry-detailleft {
	min-width: 0;
	flex: 1;
}
.entry.open .entry-detail {
	display: block;
}
/* pills on the left, Edit / Delete on the right — one row */
.entry-detailtop {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
}
.chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	min-width: 0;
}
.chip {
	font-size: 12.5px;
	padding: 3px 10px;
	border-radius: 999px;
	background: var(--tint, var(--surface));
	border: 1px solid var(--edge, var(--line));
	color: var(--ink);
	white-space: nowrap;
}
.entry-note {
	margin: 10px 0 0;
	font-size: 14.5px;
	color: var(--ink-soft);
}
/* bottom-right icon area: source indicator + edit + delete */
.entry-actions {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
}
.act-source {
	font-size: 16px;
	margin-right: 2px;
}
.entry-edit,
.entry-del {
	border: none;
	background: transparent;
	font-size: 11px;
	line-height: 1;
	padding: 6px;
	border-radius: 8px;
	cursor: pointer;
}
.entry-edit:active,
.entry-del:active {
	background: rgba(0, 0, 0, 0.08);
}
/* source icon, sitting just left of the first sensation chip */
.chip-source {
	font-size: 16px;
	line-height: 1;
	margin-right: 2px;
	align-self: center;
}
.loading {
	margin: 40px auto;
	text-align: center;
	color: var(--ink-faint);
	font-size: 14px;
}

.empty {
	margin: auto;
	padding: 60px 30px;
	text-align: center;
	color: var(--ink-faint);
	font-size: 15px;
	line-height: 1.7;
}

/* ---- Modal / sheet ---- */
/* Full-height panel; its bottom edge is lifted (in JS) above the keyboard so
   the search field, results, and footer are never overlapped. */
.modal {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: stretch;
	justify-content: center;
}
/* The hidden attribute must win over display:flex, or the modal (and its
   tap-stealing backdrop) sits over the page permanently. */
.modal[hidden] {
	display: none;
}
.modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(30, 22, 44, 0.42);
	animation: fade 0.18s ease;
}
.sheet {
	position: relative;
	width: 100%;
	max-width: 560px;
	height: 100%;
	background: var(--bg);
	box-shadow: var(--shadow);
	display: flex;
	flex-direction: column;
	padding-top: var(--safe-top);
	animation: slide-up 0.24s cubic-bezier(0.2, 0.9, 0.3, 1);
	overflow: hidden; /* clip the oversized donut layer; children scroll internally */
}
/* Faint wheel donut behind each capture/care/session sheet (matches the
   per-tab bg art). Transparent-field PNG, so the hole shows the sheet
   through it in either theme. transform is set per modal above (each has
   its own zoom), not here. */
#modal .sheet::before,
#care-modal .sheet::before,
#sessions-modal .sheet::before {
	content: '';
	position: absolute;
	/* A SQUARE layer sized to the donut, centred — a square rotating about its
	   centre never clips the inscribed circle (the old full-sheet rectangle
	   cropped its sides mid-rotation), and the smaller animated layer is far
	   cheaper for iOS to composite. */
	left: 50%;
	top: 46%;
	width: min(190%, 1080px);
	aspect-ratio: 1;
	z-index: 0;
	opacity: 0.5;
	pointer-events: none;
}
:root[data-theme='light'] #modal .sheet::before,
:root[data-theme='light'] #care-modal .sheet::before,
:root[data-theme='light'] #sessions-modal .sheet::before {
	opacity: 0.3;
}
/* Keep the sheet content above the donut layer. */
#modal .sheet > *,
#care-modal .sheet > *,
#sessions-modal .sheet > * {
	position: relative;
	z-index: 1;
}
@keyframes slide-up {
	from {
		transform: translateY(100%);
	}
}
@keyframes fade {
	from {
		opacity: 0;
	}
}

.sheet-head {
	display: grid;
	grid-template-columns: 40px 1fr 40px;
	align-items: center;
	padding: 14px 12px 4px;
}
.sheet-title {
	grid-column: 2;
	margin: 0;
	text-align: center;
	font-size: 17px;
	font-weight: 700;
}
.sheet-back,
.sheet-close {
	width: 40px;
	height: 40px;
	border: none;
	background: transparent;
	color: var(--ink-soft);
	font-size: 26px;
	line-height: 1;
	cursor: pointer;
	border-radius: 10px;
}
/* Pin the controls to the outer columns so hiding the back button (display:none)
   never lets the title slide into the 40px slot. */
.sheet-back {
	grid-column: 1;
	font-size: 30px;
}
.sheet-close {
	grid-column: 3;
}

.steps {
	display: flex;
	gap: 7px;
	justify-content: center;
	padding: 6px 0 10px;
}
/* Hidden on a med impact, whose steps run in a different order */
.steps[hidden] {
	display: none;
}
.dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--line);
	transition: background 0.15s ease, transform 0.15s ease;
}
.dot.active {
	background: var(--brand);
	transform: scale(1.25);
}
.dot.done {
	background: color-mix(in srgb, var(--brand) 45%, var(--line));
}

.sheet-body {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: 6px 16px 16px;
	-webkit-overflow-scrolling: touch;
}
/* Note step: content (chips + textarea) is short, so don't stretch to fill the
   sheet — that pinned the Save/Update button far below the content with a big
   empty gap between them, especially with the keyboard open. Hug the content
   instead so Save follows right after it; any true leftover space collects
   below the footer, near the keyboard, rather than above the button. */
.sheet-body.step-note {
	flex: 0 1 auto;
}
/* Pinned action bar — a sibling of the scroll area so the list scrolls freely
   beneath it. Collapses to nothing on steps with no actions. */
.sheet-foot {
	display: flex;
	gap: 10px;
	justify-content: center;
	padding: 12px 16px calc(var(--safe-bottom) + 14px);
	border-top: 1px solid var(--line);
	background: var(--bg);
}
.sheet-foot:empty {
	display: none;
}
/* A single pinned action (e.g. the note step's Save/Update) shouldn't stretch
   edge-to-edge — give it its own centred, comfortably-tappable width instead. */
.sheet-foot > .btn:only-child {
	flex: 0 1 280px;
}
/* Note step: let the sheet's donut art show continuously behind and below the
   Save/Update button instead of an opaque bar filling the space under it. Safe
   only here — the note-step body hugs its content (flex: 0 1 auto) so nothing
   ever scrolls up behind the button; on scrolling steps the opaque foot stays. */
.sheet-body.step-note ~ .sheet-foot {
	background: transparent;
	border-top: none;
}
/* Legal sheet: the iframe fills the sheet under the header (legal.html carries
   its own styling, incl. dark mode via prefers-color-scheme). */
.legal-frame {
	flex: 1;
	min-height: 0;
	width: 100%;
	border: 0;
	background: var(--bg);
}

/* Note step: the textarea is pinned in the foot, stacked above Save/Update —
   anchored to the bottom of the sheet so the keyboard can never cover it (the
   foot rides above the keyboard exactly like the button always has). */
.sheet-foot.note-foot {
	flex-direction: column;
	align-items: stretch;
}
.sheet-foot.note-foot > .btn {
	align-self: center;
	flex: 0 0 auto;
	width: min(280px, 100%);
}

.step-hint {
	color: var(--ink-soft);
	font-size: 13.5px;
	margin: 0 2px 12px;
	text-align: center;
}
/* Specific step: the picked word's gloss pinned above the two buttons. The foot
   becomes a column so the line can span the width; the buttons keep their row. */
.sheet-foot.def-foot {
	flex-wrap: wrap;
}
.word-def {
	flex: 1 0 100%;
	min-height: 1.4em;
	font-size: 13.5px;
	line-height: 1.4;
	color: var(--ink);
	text-align: center;
	margin-bottom: 2px;
}
.word-def.muted {
	color: var(--ink-faint);
}
/* Add-your-own source form, mounted in the foot above the note field */
.note-foot .src-form {
	margin: 0 0 10px;
}

/* One-time confirmation after grafting a custom word onto the wheel — landing
   on the picker with it pre-selected wasn't a loud enough "this worked" on
   its own. */
.added-flash {
	background: var(--brand-soft);
	color: var(--brand);
	font-size: 13.5px;
	font-weight: 700;
	text-align: center;
	border-radius: 12px;
	padding: 10px 14px;
	margin: 0 2px 12px;
	animation: added-flash-in 0.2s ease;
}
@keyframes added-flash-in {
	from {
		opacity: 0;
		transform: translateY(-4px);
	}
}

/* search (step 0) — pinned to the top of the scroll area so it stays visible
   above the keyboard while results scroll beneath it */
.search-wrap {
	position: sticky;
	top: 0;
	z-index: 2;
	margin: 0 0 12px;
	padding: 6px 0;
	background: transparent;
}
.search {
	width: 100%;
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 12px;
	padding: 12px 14px;
	font: inherit;
	font-size: 16px;
}
.search:focus {
	outline: none;
	border-color: var(--brand);
}
.search-results {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
/* a display rule overrides the hidden attribute — restore it for the toggled
   sections so search results and the tile stack never show at once */
.search-results[hidden],
.core-stack[hidden] {
	display: none;
}
.search-result {
	display: flex;
	align-items: center;
	gap: 11px;
	border: 1px solid var(--line);
	background: var(--surface);
	border-radius: 12px;
	padding: 11px 14px;
	font: inherit;
	text-align: left;
	cursor: pointer;
}
.search-result:active {
	transform: scale(0.99);
}
.sr-dot {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	background: var(--c, var(--brand));
	flex-shrink: 0;
}
.sr-main {
	display: flex;
	flex-direction: column;
	min-width: 0;
}
.sr-word {
	font-size: 15.5px;
	font-weight: 600;
	color: var(--ink);
}
.sr-path {
	font-size: 12px;
	color: var(--ink-soft);
}
.no-results {
	color: var(--ink-faint);
	font-size: 14px;
	text-align: center;
	padding: 24px 0;
}
.no-results .nr-text {
	margin: 0;
}
.no-results .nr-sub {
	margin: 14px 0 8px;
	font-size: 13px;
}
.nr-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	justify-content: center;
}
.nr-add {
	flex: 0 0 auto;
}
/* The axis rule, stated once where people actually get stuck. Quiet enough
   not to compete with the suggested-home chip when there is one. */
.nr-hint {
	margin: 14px 4px 0;
	font-size: 12px;
	line-height: 1.45;
	color: var(--ink-faint);
}

/* placing a custom word: a simple grid of branch choices to graft onto */
.place-grid {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding-bottom: 4px;
}
.place-tile {
	flex: 0 0 auto;
	min-height: 50px;
	border: none;
	border-radius: 14px;
	font: inherit;
	font-size: 17px;
	font-weight: 700;
	color: #fff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.22);
	cursor: pointer;
	box-shadow: 0 2px 8px rgba(40, 30, 60, 0.14);
	transition: transform 0.08s ease, filter 0.12s ease;
}
.place-tile:active {
	transform: scale(0.98);
	filter: brightness(0.96);
}
/* Suggested-home chip above the manual picker */
.place-suggest-slot {
	margin-bottom: 6px;
}
.place-suggest {
	display: flex;
	align-items: center;
	gap: 11px;
	width: 100%;
	border: 1.5px solid color-mix(in srgb, var(--c) 55%, transparent);
	background: color-mix(in srgb, var(--c) 16%, var(--surface));
	border-radius: 14px;
	padding: 11px 12px;
	cursor: pointer;
	font: inherit;
	text-align: left;
	transition: transform 0.08s ease, filter 0.12s ease;
}
.place-suggest:active {
	transform: scale(0.99);
	filter: brightness(0.97);
}
.place-suggest .ps-dot {
	flex: 0 0 auto;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: var(--c);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 30%, transparent);
}
.place-suggest .ps-body {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 1px;
}
.place-suggest .ps-kicker {
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-faint);
}
.place-suggest .ps-path {
	font-size: 16px;
	font-weight: 700;
	color: var(--ink);
}
.place-suggest .ps-use {
	flex: 0 0 auto;
	font-size: 13px;
	font-weight: 700;
	color: #fff;
	background: var(--c);
	border-radius: 999px;
	padding: 6px 14px;
}
:root[data-theme='dark'] .place-suggest .ps-use {
	color: #1a1622;
}
.place-or {
	margin: 8px 2px 10px;
	font-size: 12px;
	color: var(--ink-faint);
	text-align: center;
}
.sr-section {
	margin: 14px 2px 6px;
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-faint);
}
.search-result.sensation .sr-glyph {
	width: 18px;
	text-align: center;
	color: var(--c, var(--brand));
	flex-shrink: 0;
}
.search-result.sensation {
	gap: 9px;
}
.sr-add {
	margin-left: auto;
	color: var(--ink-faint);
	font-size: 18px;
	flex-shrink: 0;
}
.search-result.sensation.on {
	border-color: var(--c, var(--brand));
}
.search-result.sensation.on .sr-add {
	color: var(--c, var(--brand));
}

/* work-backwards context: sensations attached before the feeling is chosen */
.body-context {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	margin-bottom: 12px;
	padding: 10px 12px;
	background: var(--brand-soft);
	border-radius: 12px;
}
.bc-label {
	font-size: 12px;
	font-weight: 700;
	color: var(--brand);
	text-transform: uppercase;
	letter-spacing: 0.04em;
}
.bc-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}
.bc-chip {
	border: 1px solid var(--c, var(--brand));
	background: transparent;
	color: var(--c, var(--brand));
	border-radius: 999px;
	padding: 4px 10px;
	font: inherit;
	font-size: 13px;
	cursor: pointer;
}

/* core emotion tiles (step 0) — height ∝ how many feelings each core holds,
   so Happy (9) towers over Disgusted (4), echoing the wheel's segments. The
   stack fills the sheet so the proportions read at a glance without scrolling. */
.core-stack {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding-bottom: 4px;
}
.core-tile {
	flex: 0 0 auto;
	min-height: calc(42px + var(--n, 1) * 5px);
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 14px;
	font: inherit;
	font-size: 18px;
	font-weight: 700;
	color: #fff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.22);
	cursor: pointer;
	transition: transform 0.08s ease, filter 0.12s ease;
	box-shadow: 0 2px 8px rgba(40, 30, 60, 0.14);
}
/* Core step: fill the sheet so all cores fit without scrolling. Tiles share the
   available height in proportion to their breadth (--n); when searching, the
   results pane takes over the space and scrolls instead. */
.sheet-body.step-core {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
.sheet-body.step-core .core-stack {
	flex: 1 1 auto;
	min-height: 0;
}
.sheet-body.step-core .core-tile {
	flex: var(--n, 1) 1 0;
	min-height: 0;
	font-size: 16px;
	overflow: hidden;
}
.sheet-body.step-core .search-results {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
}
.core-tile:active {
	transform: scale(0.98);
	filter: brightness(0.96);
}

/* secondary / tertiary picker (step 1) */
.sec-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.sec-item {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.ter-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	padding-left: 16px;
	animation: fade 0.16s ease;
}

.pills {
	display: flex;
	flex-wrap: wrap;
	gap: 9px;
}
.pill {
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 999px;
	padding: 9px 15px;
	font: inherit;
	font-size: 15px;
	cursor: pointer;
	transition: transform 0.07s ease;
}
.pill:active {
	transform: scale(0.95);
}
.pill.selected {
	background: var(--c, var(--brand));
	color: #fff;
	border-color: var(--c, var(--brand));
}
/* secondary + sensation pills carry the core colour: a tinted fill with a
   bold coloured underline so they read as part of that family even unselected */
.pill.sec {
	background: var(--tint, var(--surface));
	border-color: var(--edge, var(--line));
	border-bottom: 3px solid var(--c, var(--line));
	color: var(--ink);
}
.sec-list .pill.sec {
	display: block;
	width: 100%;
	text-align: center;
}
/* selected wins over the tinted .pill.sec fill (source order + specificity) */
.pill.sec.selected {
	background: var(--c, var(--brand));
	border-color: var(--c, var(--brand));
	color: #fff;
}
.pill.ter {
	font-size: 13.5px;
	padding: 7px 13px;
	background: transparent;
	border-style: dashed;
	border-color: var(--c, var(--line));
	color: var(--ink-soft);
}
.pill.ter.selected {
	background: var(--c, var(--brand));
	border-style: solid;
	border-color: var(--c, var(--brand));
	color: #fff;
}

.sense-group {
	margin-bottom: 16px;
}
.sense-group h3 {
	margin: 0 0 8px;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--c, var(--ink-faint));
	font-weight: 700;
}

/* note step */
.note-field {
	width: 100%;
	border: 1px solid var(--line);
	border-radius: 14px;
	padding: 14px;
	font: inherit;
	font-size: 16px;
	background: var(--surface);
	color: var(--ink);
	resize: none;
}
.note-field:focus {
	outline: none;
	border-color: var(--brand);
}
/* review card — mirrors the list card: whole card tinted the emotion colour */
.review-card {
	background: var(--tint, var(--surface));
	border: 1px solid var(--edge, var(--line));
	border-radius: 14px;
	padding: 12px 14px;
	margin-bottom: 16px;
}
.rc-head {
	display: flex;
	align-items: baseline;
	gap: 6px;
}
.review-card .chips {
	margin-top: 10px;
}

/* source picker */
.source-pick {
	margin-bottom: 14px;
}
.sp-label {
	display: block;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--ink-faint);
	margin: 0 2px 8px;
}
.sp-options {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}
.src-opt {
	display: flex;
	align-items: center;
	gap: 6px;
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 999px;
	padding: 7px 12px 7px 10px;
	font: inherit;
	font-size: 14px;
	cursor: pointer;
}
.src-opt .src-icon {
	font-size: 15px;
	line-height: 1;
}
.src-opt.on {
	background: var(--brand-soft);
	border-color: var(--brand);
	color: var(--brand);
}

.btn {
	flex: 1;
	border: none;
	border-radius: 13px;
	padding: 14px;
	font: inherit;
	font-size: 16px;
	font-weight: 600;
	cursor: pointer;
}
.btn-primary {
	background: var(--brand);
	color: #fff;
}
.btn:disabled {
	opacity: 0.4;
	cursor: default;
}
.btn-ghost {
	background: var(--surface);
	color: var(--ink-soft);
	border: 1px solid var(--line);
	flex: 0 0 auto;
	padding: 14px 18px;
}

/* Dark is the default identity — applied via [data-theme="dark"], which the
   head bootstrap sets (resolving the saved Auto/Dark/Light choice). Light lives
   in :root above and applies when data-theme="light". */
:root[data-theme='dark'] {
	--bg: #16131c;
	--surface: #211d2a;
	--ink: #f0ecf5;
	--ink-soft: #b3acc0;
	--ink-faint: #837c92;
	--line: #322c3e;
	--brand: #b6a6e6;
	--brand-soft: #2c2640;
	--shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
	/* Lifted a step so the frames still read on the dark field. */
	--plus: #2e9a5a;
	--minus: #a03553;
}
:root[data-theme='dark'] .add-btn,
:root[data-theme='dark'] .btn-primary,
:root[data-theme='dark'] .pill.selected,
:root[data-theme='dark'] .bb-btn.primary,
:root[data-theme='dark'] .intro-cta {
	color: #1a1622;
}
/* White reads crispest on the darker hued cards; amber would muddy here.
   (.on.discussed is more specific, so muted flags stay muted.) */
:root[data-theme='dark'] .entry-flag.on {
	color: #ffffff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

/* ===== Tabs ===== */
/* A floating lozenge, same surface recipe as the header's .bar-inner — the
   full-bleed slab welded to the bottom edge stopped matching the app the day
   the header became a lozenge, and on vivid backgrounds (Needs pink) its
   safe-area padding read as a dead white foot under the labels. Floating
   above the home-indicator area lets the background art own that strip. */
.tabs {
	position: fixed;
	left: 14px;
	right: 14px;
	bottom: var(--tabs-lift);
	z-index: 40;
	display: flex;
	max-width: 532px; /* the 560px app column minus the 14px gutters */
	margin: 0 auto;
	background: color-mix(in srgb, var(--brand) 10%, var(--surface));
	border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--line));
	border-radius: 18px;
	backdrop-filter: saturate(1.3) blur(9px);
	-webkit-backdrop-filter: saturate(1.3) blur(9px);
}
/* Hidden while the chat composer is focused — see #view-chat.kb-open above. */
.tabs.kb-hidden {
	display: none;
}
.tab {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	padding: 8px 0 7px;
	border: none;
	background: transparent;
	color: var(--ink-faint);
	font: inherit;
	font-size: 11px;
	font-weight: 600;
	cursor: pointer;
	opacity: 0.65;
}
.tab-icon {
	width: 30px;
	height: 30px;
	display: block;
}
.tab.is-active {
	opacity: 1;
	color: var(--brand);
}
.tab.is-active .tab-icon {
	transform: translateY(-1px);
}

/* ===== Tips bar (rotating hints; also surfaces the backup nudge) ===== */
.tips-bar {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: 2px 14px 12px; /* side gutters match the header lozenges */
	padding: 10px 12px;
	border-radius: 13px;
	background: color-mix(in srgb, var(--brand) 10%, var(--surface));
	border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--line));
}
/* The hidden attribute must win over display:flex, or × never visually closes
   the bar (same trap as .modal[hidden]). */
.tips-bar[hidden] {
	display: none;
}
.tip-icon {
	font-size: 15px;
	flex-shrink: 0;
}
.tip-msg {
	flex: 1;
	min-width: 0;
	font-size: 12.5px;
	line-height: 1.4;
	color: var(--ink);
}
.tip-action {
	flex-shrink: 0;
	font: inherit;
	font-size: 12.5px;
	font-weight: 600;
	border-radius: 999px;
	padding: 6px 12px;
	cursor: pointer;
	border: none;
	background: var(--brand);
	color: #fff;
}
:root[data-theme='dark'] .tip-action {
	color: #1a1622;
}
.tip-next,
.tip-dismiss {
	flex-shrink: 0;
	width: 38px; /* comfortable thumb target */
	height: 38px;
	margin: -6px 0;
	border: none;
	background: transparent;
	color: var(--ink-faint);
	font-size: 21px;
	line-height: 1;
	cursor: pointer;
	border-radius: 50%;
}
.tip-dismiss:active {
	background: var(--line);
}
/* Reminders (1.1.1): only on a native build, where they exist. */
.notify-only[hidden] {
	display: none;
}
.menu-toggle {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 6px 12px;
	font-size: 14px;
	color: var(--ink);
	cursor: pointer;
}
.menu-toggle input {
	width: 18px;
	height: 18px;
	accent-color: var(--brand);
	flex: none;
}
.menu-note {
	margin: 4px 6px 2px;
	padding-top: 8px;
	border-top: 1px solid var(--line);
	font-size: 11.5px;
	line-height: 1.4;
	color: var(--ink-faint);
}
/* The backup note sits ABOVE the Export/Import buttons now — no divider. */
.menu-note.backup-note {
	border-top: none;
	padding-top: 0;
	margin: 2px 8px 8px;
	color: var(--ink-soft);
}

/* Text size preference — a pragmatic whole-UI scale. `zoom` is supported by
   WebKit/Blink; where unsupported it's simply ignored. */
:root[data-textsize='large'] body {
	zoom: 1.09;
}
:root[data-textsize='xl'] body {
	zoom: 1.18;
}

/* ===== Care + Sessions + Insights views ===== */
#view-care,
#view-sessions,
#view-insights,
#view-skills {
	padding: 8px 14px calc(var(--tabs-clear) + 4px);
}

/* ===== Chat ===== */
/* Unlike the other tabs (page-scroll, floating fixed tab bar), Chat fills the
   viewport like a modal sheet — its own internal scroll for messages, with
   the composer pinned right after them at rest, and docked fixed above the
   keyboard (.kb-open, see below) while focused. See app.js switchView() for
   the body-lock half of this. The [hidden] override is needed because
   #view-chat's own display:flex (an ID rule) would otherwise beat the
   browser's default [hidden]{display:none} on specificity. */
#view-chat {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
	padding: 8px 14px 0;
	overflow: hidden;
	position: relative;
}
#view-chat[hidden] {
	display: none;
}
.chat-top {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin: 6px 2px 10px;
	flex: 0 0 auto;
}
.chat-title {
	font-size: 24px;
	font-weight: 800;
	letter-spacing: -0.02em;
	color: var(--ink);
}
/* Dev-mock badge (web-only, ?mockai) — must read unmistakably as fake AI. */
.chat-mock-badge {
	display: inline-block;
	margin-left: 8px;
	padding: 2px 7px;
	border-radius: 999px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0;
	vertical-align: middle;
	color: #fff;
	background: #b4541e;
}
.chat-top-actions {
	display: flex;
	gap: 4px;
}
/* Crisis affordance — always present, deliberately quiet (never red, never
   alarmist), and never hidden with the other actions on an empty chat. */
.chat-help {
	/* Prominent on purpose (Ross, 2026-10-03, after the third 1.4.1
	   rejection): black on a white pill, in both themes. It used to be a
	   quiet outline that disappeared into the pink. */
	border: 1px solid rgba(0, 0, 0, 0.18);
	background: #fff;
	color: #111;
	font: inherit;
	font-size: 13px;
	font-weight: 700;
	border-radius: 999px;
	cursor: pointer;
	padding: 6px 13px;
	white-space: nowrap;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
}
/* An inline "link" that opens the crisis sheet from a sentence. */
.link-btn {
	background: none;
	border: none;
	padding: 0;
	margin: 0;
	font: inherit;
	color: var(--brand);
	font-weight: 700;
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}
.intro-med > span:last-child {
	line-height: 1.35;
}
/* The disclaimer at the foot of Care and Insights, and in the menu's About
   (1.1.1 brief §4): readable, quiet, never a banner. */
.tab-disclaimer {
	margin: 22px 6px 6px;
	font-size: 12.5px;
	line-height: 1.5;
	text-align: center;
	color: var(--ink-soft);
}
.menu-note.menu-disclaimer {
	font-size: 12px;
	line-height: 1.45;
	color: var(--ink-soft);
}
.chat-disclaimer {
	margin: 14px 8px 0;
	font-size: 13px;
	line-height: 1.4;
	text-align: center;
	color: var(--ink);
}
.chat-help-sheet {
	/* Fixed to the VIEWPORT, not the view: #view-chat can run taller than
	   the screen (measured 704px of view on a 667px SE), so a sheet sized to
	   it put its bottom edge off-screen and the tab-bar clearance with it.
	   The top edge is the app bar's bottom (safe-top + 68px, the same line
	   the menu popover drops from), so the bar stays crisp above the blur. */
	position: fixed;
	left: 0;
	right: 0;
	top: calc(var(--safe-top) + 68px);
	bottom: 0;
	z-index: 200; /* above the splash and every modal — it opens from the splash now */
	display: flex;
	/* Anchored to the top, and its box stops where the tab bar begins (Ross,
	   2026-09-24). Centred in the whole view, the sheet grew with the AU/NZ
	   rows until its Close button sat UNDER the floating tab bar — the one
	   control that dismisses the crisis sheet was the one you could not see.
	   With the bottom padding set to the tab bar's footprint, the card's
	   max-height:100% ends above the bar, and on a short screen the card
	   scrolls inside itself instead of running underneath it. */
	align-items: flex-start;
	justify-content: center;
	padding: 10px 18px var(--tabs-clear);
	background: color-mix(in srgb, var(--bg) 78%, transparent);
	backdrop-filter: blur(3px);
}
.chat-help-sheet[hidden] {
	display: none;
}
.chs-card {
	width: 100%;
	max-width: 420px;
	max-height: 100%;
	overflow-y: auto; /* two more helpline rows — must still fit an SE screen */
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 18px;
	padding: 18px 16px 14px;
	box-shadow: 0 18px 44px rgba(0, 0, 0, 0.28);
}
.chs-card h2 {
	margin: 0 0 6px;
	font-size: 17px;
	font-weight: 800;
	color: var(--ink);
}
.chs-card > p {
	margin: 0 0 12px;
	font-size: 13.5px;
	line-height: 1.45;
	color: var(--ink);
}
.chs-line {
	display: block;
	padding: 9px 11px;
	margin-bottom: 7px;
	border-radius: 12px;
	border: 1px solid var(--line);
	text-decoration: none;
	background: color-mix(in srgb, var(--brand) 7%, transparent);
}
.chs-line.chs-static {
	background: transparent;
}
.chs-where {
	/* Was var(--muted), a token that never existed, so inside the <a> it fell
	   back to the browser's link blue: unreadable on the dark theme, and it
	   was the region labels on the crisis sheet that Apple's reviewer saw
	   (2026-09-26). The brand colour keeps the accent in both themes. */
	display: block;
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--brand);
}
.chs-num {
	display: block;
	font-size: 15px;
	font-weight: 700;
	color: var(--ink);
	margin-top: 1px;
}
.chs-foot {
	margin: 10px 0 12px;
	font-size: 12.5px;
	color: var(--ink);
}
.chs-close {
	/* Sticks to the bottom of the card while the rows scroll on a short
	   screen, so dismissing never needs a scroll first. */
	position: sticky;
	bottom: 0;
	width: 100%;
	border: none;
	border-radius: 12px;
	background: var(--brand);
	color: #fff;
	font: inherit;
	font-size: 15px;
	font-weight: 700;
	padding: 11px;
	cursor: pointer;
}
:root[data-theme='dark'] .chs-close {
	color: #1a1622;
}
.chat-save,
.chat-copy,
.chat-clear {
	border: none;
	background: transparent;
	color: var(--brand);
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
	padding: 4px 6px;
	min-width: 44px;
}
.chat-save[hidden],
.chat-copy[hidden],
.chat-clear[hidden] {
	display: none;
}

.chat-messages {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 4px 2px 12px;
}
/* Manual text selection (long-press) inside a position:fixed + scrolling
   container is a known-fragile combo on iOS — the selection handles/loupe
   can get orphaned mid-gesture and stop tracking, which reads as the whole
   screen freezing. The Copy button above is the supported way to get text
   out of a conversation, so selection is disabled specifically while docked
   (kb-open) rather than trying to harden a fragile native interaction. */
#view-chat.kb-open .chat-messages {
	-webkit-user-select: none;
	user-select: none;
}
.msg {
	max-width: 82%;
	padding: 11px 14px;
	border-radius: 18px;
	font-size: 15px;
	line-height: 1.42;
	white-space: pre-wrap;
	word-break: break-word;
	animation: msg-in 0.16s ease;
}
@keyframes msg-in {
	from {
		opacity: 0;
		transform: translateY(6px);
	}
}
.msg.user {
	align-self: flex-end;
	background: var(--brand);
	color: #fff;
	border-bottom-right-radius: 6px;
}
:root[data-theme='dark'] .msg.user {
	color: #1a1622;
}
.msg.assistant {
	align-self: flex-start;
	background: var(--surface);
	border: 1px solid var(--line);
	color: var(--ink);
	border-bottom-left-radius: 6px;
}
.msg.assistant.error {
	color: var(--danger);
	border-color: color-mix(in srgb, var(--danger) 35%, var(--line));
}
/* System note (the one-time Save nudge) — quiet, centred, clearly not a
   message from Feels. */
.chat-note {
	align-self: center;
	max-width: 88%;
	text-align: center;
	font-size: 12.5px;
	color: var(--ink);
	padding: 2px 10px;
	animation: msg-in 0.16s ease;
}
/* The indicator arrives in stages (Ross, 2026-07-26 — an instant answer reads
   as canned): chat.js holds it back ~0.7s after send (the message is being
   read), then the bubble eases in with the three dots sitting still and dim —
   a plain "…" — and only after a further beat do they start the wave. The
   staging is all animation-delay: a delayed keyframe animation shows its 0%
   frame while it waits, which for think-bounce is exactly the static dim dot. */
.msg.thinking {
	align-self: flex-start;
	background: var(--surface);
	border: 1px solid var(--line);
	border-bottom-left-radius: 6px;
	display: flex;
	gap: 4px;
	align-items: center;
	padding: 15px 16px;
	animation: think-arrive 0.3s ease-out both;
}
.msg.thinking span {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--ink-faint);
	animation: think-bounce 1.1s ease-in-out 0.9s infinite;
}
.msg.thinking span:nth-child(2) {
	animation-delay: 1.05s;
}
.msg.thinking span:nth-child(3) {
	animation-delay: 1.2s;
}
@keyframes think-arrive {
	from {
		opacity: 0;
		transform: translateY(4px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}
@keyframes think-bounce {
	0%,
	60%,
	100% {
		transform: translateY(0);
		opacity: 0.5;
	}
	30% {
		transform: translateY(-4px);
		opacity: 1;
	}
}

/* Top-justified (launch prep, Ross): centering meant the whole block got
   squeezed upward when the keyboard docked the messages box, leaving only the
   bubble's tail poking out. Anchored to the top, the bubble + intro line stay
   put and the keyboard simply covers the un-tapped starters below. The tight
   top padding also reclaims the dead band under the header. */
.chat-empty {
	flex: 1;
	min-height: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	text-align: center;
	gap: 12px;
	padding: 6px 8px 20px;
	color: var(--ink-soft);
}
.ce-head {
	display: inline-flex;
	align-items: center;
	gap: 8px;
}
.chat-empty .ce-icon {
	font-size: 34px;
}
/* Heavier + full-ink: the soft grey was near-invisible over the pink donut
   background art. Matches the weight/contrast of copy on the other tabs. */
.chat-empty p {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	color: var(--ink);
	max-width: 300px;
}
.chat-starters {
	display: flex;
	flex-direction: column;
	gap: 8px;
	width: 100%;
	max-width: 320px;
}
.chat-starter {
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 12px;
	padding: 11px 14px;
	font: inherit;
	font-size: 13.5px;
	text-align: left;
	cursor: pointer;
}
.chat-starter:active {
	background: var(--line);
}

/* Needs chips — the engine's 2-3 candidate needs under a just-sent message.
   Suggestions only: tapping one pre-fills the composer, the user decides. */
.needs-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	justify-content: flex-end; /* hugs the user bubble it annotates */
	padding: 0 4px;
	animation: msg-in 0.25s ease;
}
.needs-hint {
	font-size: 11px;
	color: var(--ink-soft);
}
.need-chip {
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 999px;
	padding: 4px 11px;
	font: inherit;
	font-size: 12.5px;
	cursor: pointer;
}
.need-chip:active {
	background: var(--line);
}

.chat-composer {
	flex: 0 0 auto;
	display: flex;
	align-items: flex-end;
	gap: 8px;
	padding: 10px 2px var(--tabs-clear);
}
/* While the composer is focused (keyboard open), dock it exactly like the tab
   bar — plain fixed, bottom:0 — instead of trying to compute the keyboard's
   height and pad around it. WebKit reliably keeps a bottom:0 fixed element
   above the on-screen keyboard (that's how .tabs itself stays visible above
   it with zero JS); computing the keyboard height via visualViewport is not
   reliable once the Keyboard plugin's native resize is involved, and got it
   wrong enough that the composer was rendering off-screen. The tab bar is
   hidden meanwhile since it isn't reachable mid-type anyway. */
#view-chat.kb-open .chat-composer {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 45;
	max-width: 560px;
	margin: 0 auto;
	background: var(--bg);
	border-top: 1px solid var(--line);
	padding: 10px 14px calc(var(--safe-bottom) + 10px);
}
/* .chat-messages needs the same fixed-position treatment, or its scroll box
   keeps thinking it has the full (keyboard-unaware) view height, so
   overflow-y:auto never kicks in and the tail of a reply is unreachable.
   top/bottom are set inline (see chat.js dockForKeyboard()) from two stable,
   keyboard-agnostic measurements — .chat-top's rendered bottom edge and
   .chat-composer's own height — rather than any keyboard-height estimate. */
#view-chat.kb-open .chat-messages {
	position: fixed;
	left: 0;
	right: 0;
	max-width: 560px;
	margin: 0 auto;
	padding: 4px 14px 12px;
}
.chat-input {
	flex: 1;
	resize: none;
	max-height: 120px;
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 18px;
	padding: 11px 16px;
	font: inherit;
	font-size: 16px;
	line-height: 1.35;
}
.chat-input:focus {
	outline: none;
	border-color: var(--brand);
}
.chat-send-btn {
	flex: 0 0 auto;
	width: 42px;
	height: 42px;
	border-radius: 50%;
	border: none;
	background: var(--brand);
	color: #fff;
	display: grid;
	place-items: center;
	cursor: pointer;
	transition: opacity 0.15s ease, transform 0.1s ease;
}
:root[data-theme='dark'] .chat-send-btn {
	color: #1a1622;
}
.chat-send-btn:disabled {
	opacity: 0.35;
	cursor: default;
}
.chat-send-btn:active:not(:disabled) {
	transform: scale(0.92);
}

/* ===== Insights ===== */
.ins-top {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin: 6px 2px 14px;
}
.ins-title {
	font-size: 24px;
	font-weight: 800;
	letter-spacing: -0.02em;
	color: var(--ink);
}
.ins-priv {
	font-size: 11px;
	font-weight: 600;
	color: var(--ink-soft);
}
/* AI reflections card (native only) */
.ai-card .ai-onthis {
	text-transform: none;
	letter-spacing: 0;
	color: var(--ink-faint);
	font-weight: 600;
}
.ai-card .ai-body {
	font-size: 14.5px;
	line-height: 1.5;
	color: var(--ink-soft);
	margin: 8px 0 12px;
}
.ai-card .ai-body p,
.ai-card .ai-body .ai-list {
	margin: 0 0 8px;
}
.ai-card .ai-body p:last-child,
.ai-card .ai-body .ai-list:last-child {
	margin-bottom: 0;
}
.ai-card .ai-body .ai-list {
	padding-left: 18px;
}
.ai-card .ai-body li {
	margin-bottom: 4px;
}
.ai-card .ai-body strong {
	color: var(--ink);
	font-weight: 700;
}
.ai-card .ai-go {
	width: 100%;
}
/* .btn sets its own display, which beats [hidden] (1.1.1: a fallback or a
   crisis card must not offer the button again). */
.ai-card .ai-go[hidden],
.ai-card .ai-label[hidden] {
	display: none;
}
/* The session form's model buttons say what happened here, not in an alert
   (1.1.1): the summary label, nothing found, the crisis route. */
/* "Check in after (weeks)": the number with its unit beside it (1.1.1). */
.f-unit-row {
	display: flex;
	align-items: center;
	gap: 8px;
}
.f-unit-row .f-input {
	flex: 1;
	min-width: 0;
}
.f-unit {
	font-size: 14px;
	color: var(--ink-soft);
}
.s-ai-note {
	margin: 6px 2px 12px;
	font-size: 12.5px;
	line-height: 1.45;
	color: var(--ink-soft);
}
.s-ai-note[hidden] {
	display: none;
}
/* Under the card, always (Ross's brief §2f). */
.ai-card .ai-label {
	margin: 10px 0 0;
	font-size: 12px;
	line-height: 1.4;
	color: var(--ink-faint);
}
/* An entry needed a person, not the model (§2e): calm, not an alarm. */
.ai-card.ai-crisis .ai-body {
	color: var(--ink);
}
.ai-card .ai-help {
	margin-top: 10px;
	width: 100%;
	background: var(--surface);
	color: var(--ink);
	border: 1px solid color-mix(in srgb, var(--brand) 40%, var(--line));
	font-weight: 700;
}
/* AI info readout in the Care forms (med effects / timeframe) */
.ai-info {
	white-space: pre-wrap;
	margin-top: 8px;
	padding: 10px 12px;
	border-radius: 12px;
	font-size: 13px;
	line-height: 1.5;
	color: var(--ink-soft);
	background: color-mix(in srgb, var(--brand) 8%, var(--surface));
	border: 1px solid color-mix(in srgb, var(--brand) 20%, var(--line));
}
.ai-note {
	display: block;
	margin-top: 6px;
	font-size: 11px;
	color: var(--ink-faint);
}
:root[data-theme='dark'] .ins-priv {
	color: var(--ink-faint);
}
.ins-stats {
	display: flex;
	gap: 10px;
	margin-bottom: 11px;
}
.ins-stat {
	flex: 1;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 14px;
	padding: 11px 8px;
	text-align: center;
}
.ins-stat .n {
	font-size: 20px;
	font-weight: 800;
	color: var(--ink);
}
.ins-stat .l {
	font-size: 11px;
	color: var(--ink-faint);
	margin-top: 2px;
}
.ins-card {
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 16px;
	padding: 14px 15px;
	margin-bottom: 11px;
}
.ins-label {
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--ink-faint);
	margin-bottom: 7px;
}
.ins-head {
	font-size: 17px;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--ink);
	margin-bottom: 10px;
}
.ins-bar {
	display: flex;
	height: 14px;
	border-radius: 7px;
	overflow: hidden;
	gap: 2px;
}
.ins-bar span {
	display: block;
	min-width: 3px;
}
.ins-sub {
	font-size: 12.5px;
	color: var(--ink-soft);
	margin-top: 9px;
	line-height: 1.45;
}
.ins-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 7px;
}
.ins-chip {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink);
	background: color-mix(in srgb, var(--brand) 13%, var(--surface));
	border: 1px solid var(--line);
	border-radius: 999px;
	padding: 5px 11px;
}
.ins-chip b {
	color: var(--ink-soft);
	font-weight: 700;
	margin-left: 5px;
}
.ins-session {
	border-color: color-mix(in srgb, var(--brand) 45%, var(--line));
}
.ins-session .ins-label {
	color: var(--brand);
}
/* Moods — the morphing arch + period chapters (MOODS_CHARTER §7–9) */
.ma-top {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
.ma-info-btn {
	border: none;
	background: none;
	color: var(--ink-faint);
	font-size: 15px;
	cursor: pointer;
	padding: 0 2px;
	line-height: 1;
}
.ma-info {
	font-size: 12.5px;
	color: var(--ink-soft);
	line-height: 1.5;
	border-bottom: 1px solid var(--line);
	padding-bottom: 9px;
	margin-bottom: 6px;
}
.ma-info p {
	margin: 0 0 7px;
}
/* "More detail": the method behind the three short sentences (ruling 6). */
.ma-more > summary {
	cursor: pointer;
	list-style: none;
	color: var(--brand);
	font-weight: 600;
	margin: 0 0 7px;
	width: fit-content;
}
.ma-more > summary::-webkit-details-marker {
	display: none;
}
.ma-more > summary::before {
	content: '›';
	display: inline-block;
	width: 0.8em;
	transition: transform 0.15s;
}
.ma-more[open] > summary::before {
	transform: rotate(90deg);
}
.ma-arch {
	display: block;
	width: 100%;
}
.ma-arch .ma-grid {
	stroke: var(--ink);
	opacity: 0.07;
}
.ma-arch .ma-grid-even {
	opacity: 0.16;
	stroke-dasharray: 3 4;
}
.ma-arch .ma-petal {
	opacity: 0.92;
}
.ma-arch .ma-lb {
	font-size: 9.5px;
	font-weight: 600;
	fill: var(--ink-soft);
}
.ma-arch .ma-end {
	font-size: 9px;
	fill: var(--ink-faint);
}
.ma-controls {
	display: flex;
	gap: 9px;
	align-items: center;
	margin-top: 6px;
}
.ma-play {
	font-size: 12px;
	font-weight: 600;
	font-family: inherit;
	color: var(--brand);
	background: var(--brand-soft);
	border: none;
	border-radius: 999px;
	padding: 5px 12px;
	cursor: pointer;
	white-space: nowrap;
}
.ma-scrub {
	flex: 1;
	min-width: 0;
	accent-color: var(--brand);
}
.ma-dates {
	font-size: 12px;
	color: var(--ink-soft);
	margin-top: 6px;
	font-variant-numeric: tabular-nums;
}
.ma-ranges {
	display: flex;
	gap: 6px;
	margin-top: 8px;
}
.ma-range {
	font-size: 11.5px;
	font-weight: 600;
	font-family: inherit;
	color: var(--ink-soft);
	background: none;
	border: 1px solid var(--line);
	border-radius: 999px;
	padding: 4px 11px;
	cursor: pointer;
}
.ma-range.on {
	color: var(--brand);
	border-color: var(--brand);
	background: var(--brand-soft);
}
.ma-p-head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
}
.ma-p-name {
	font-size: 15px;
	font-weight: 700;
	color: var(--ink);
	display: inline-flex;
	align-items: center;
	gap: 7px;
}
.ma-p-name i {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	flex: none;
}
.ma-p-name i + i {
	margin-left: -13px;
}
/* The period card's ⓘ sits after its dates (1.1.1 brief §5). */
.ma-p-right {
	display: inline-flex;
	align-items: baseline;
	gap: 6px;
}
.ma-p-info {
	margin-top: 8px;
}
.ma-p-dates {
	font-size: 12px;
	color: var(--ink-soft);
	white-space: nowrap;
}
.ma-share {
	display: flex;
	gap: 4px;
	align-items: flex-end;
	height: 34px;
	margin-top: 10px;
}
.ma-share span {
	flex: 1;
	border-radius: 3px;
	background: var(--line);
}
.ma-axis {
	display: flex;
	justify-content: space-between;
	font-size: 9.5px;
	color: var(--ink-faint);
	margin-top: 3px;
}
/* Session notes: preserve the pasted formatting (paragraphs, line breaks). */
/* Sessions grouped by therapist — same voice as the Feelings dividers */
.session-group {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	margin: 16px 2px 6px;
	padding: 4px 2px;
	background: none;
	border: 0;
	font: inherit;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
	text-align: left;
	cursor: pointer;
}
:root[data-theme='dark'] .session-group {
	color: var(--ink-faint);
}
.session-group:first-of-type {
	margin-top: 4px;
}
/* Caret points down when the group is open, right when collapsed. */
.sg-caret {
	display: inline-block;
	font-size: 15px;
	line-height: 1;
	transform: rotate(90deg);
	transition: transform 0.15s ease;
}
.session-group.is-collapsed .sg-caret {
	transform: rotate(0deg);
}
.sg-name {
	flex: 1 1 auto;
}
/* A quiet count of how many sessions the group holds. */
/* Bulk-rename affordance on a therapist group header. Quiet by default — it
   sits beside a count, not next to an action — but a real 44px-ish tap
   target, since it is nested inside the collapse button. */
.sg-rename {
	flex: 0 0 auto;
	margin-left: 2px;
	padding: 4px 9px;
	border: 1px solid var(--line);
	border-radius: 999px;
	background: none;
	color: var(--ink-faint);
	font: inherit;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-transform: none;
	cursor: pointer;
}
.sg-rename:active {
	background: var(--line);
	color: var(--ink);
}
.sg-count {
	flex: 0 0 auto;
	font-variant-numeric: tabular-nums;
	opacity: 0.7;
}
.session-notes {
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	font-size: 14.5px;
	line-height: 1.5;
	color: var(--ink-soft);
	margin-top: 4px;
}
.session-area {
	min-height: 180px;
	line-height: 1.5;
}
/* An author `display` beats the UA sheet's [hidden]{display:none} outright, so
   the AI buttons (rendered `hidden`, unhidden only when the on-device model
   answers) were VISIBLE on the PWA — offering "Summarize on this iPhone" on a
   platform that has no such model. Same trap already documented for #view-chat
   and .tips-bar; this instance was missed when the button shipped. Found by an
   e2e assertion added 2026-09-03, not by eye. */
.session-import[hidden] {
	display: none;
}
.session-import {
	display: inline-block;
	margin-top: 10px;
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink-soft);
	border-radius: 999px;
	padding: 7px 14px;
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
}
.care-section {
	margin-bottom: 22px;
}
.care-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin: 10px 2px 10px;
}
/* Feelings is the one view that gives its children their own side gutters
   (#list and .tips-bar each carry 14px) rather than padding the view, so the
   2px default above left the heading and its + hanging outside the column
   every other element lines up on. */
#view-feelings .care-head {
	margin-left: 14px;
	margin-right: 14px;
}
.care-h-label {
	/* Match the Insights title — the section headers are page-level headings. */
	font-size: 24px;
	font-weight: 800;
	letter-spacing: -0.02em;
	color: var(--ink);
}
.care-add {
	width: 32px;
	height: 32px;
	flex-shrink: 0;
	border: none;
	background: var(--brand);
	color: #fff;
	border-radius: 50%;
	padding: 0;
	font: inherit;
	font-size: 21px;
	line-height: 1;
	font-weight: 500;
	display: grid;
	place-items: center;
	cursor: pointer;
	box-shadow: 0 3px 10px color-mix(in srgb, var(--brand) 45%, transparent);
}
.care-add:active {
	transform: scale(0.92);
}
:root[data-theme='dark'] .care-add {
	color: #1a1622;
}
/* Same readability treatment as the Needs empty state (launch prep): faint
   grey over the background art was hard to read on every tab, not just Needs. */
.care-empty {
	color: var(--ink);
	font-weight: 600;
	font-size: 15px;
	padding: 4px 2px 8px;
}
.care-card {
	display: flex;
	align-items: center;
	gap: 10px;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 14px;
	padding: 13px 14px;
	margin-bottom: 9px;
	cursor: pointer;
}
.care-card.ended {
	opacity: 0.62;
}
.cc-main {
	flex: 1;
	min-width: 0;
}
.cc-title {
	font-size: 16px;
	font-weight: 700;
	color: var(--ink);
}
.cc-badge {
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-faint);
	border: 1px solid var(--line);
	border-radius: 999px;
	padding: 1px 7px;
	vertical-align: middle;
}
.cc-sub {
	font-size: 12.5px;
	color: var(--ink-soft);
	margin-top: 2px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.cc-eff {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
}
.cc-face {
	font-size: 20px;
}
.cc-caret {
	color: var(--ink-faint);
	font-size: 18px;
}

/* ===== Care detail ===== */
.detail-rows {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-bottom: 18px;
}
.drow {
	display: flex;
	gap: 12px;
}
.dl {
	flex: 0 0 38%;
	font-size: 13px;
	color: var(--ink-faint);
}
.dv {
	flex: 1;
	font-size: 14.5px;
	color: var(--ink);
	white-space: pre-wrap; /* short values can still contain line breaks */
}
/* Long-form field (Goals, Objectives, Treatment plan, Scope, Between-session
   work): the label sits above and the value runs the full width, because a
   real treatment plan squeezed into the 62% value column became a tall
   ribbon. pre-wrap is the other half of the fix — without it the stored line
   breaks were dropped and a numbered list rendered as one run-on paragraph. */
.dblock {
	display: flex;
	flex-direction: column;
	gap: 3px;
}
.dbl {
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-faint);
}
.dbv {
	font-size: 14.5px;
	line-height: 1.5;
	color: var(--ink);
	white-space: pre-wrap;
}
.detail-eff {
	border-top: 1px solid var(--line);
	padding-top: 14px;
}
.de-head {
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ink-faint);
	margin-bottom: 10px;
}
.rev {
	display: flex;
	gap: 10px;
	padding: 9px 0;
	border-bottom: 1px solid var(--line);
}
.rev-face {
	font-size: 22px;
	flex-shrink: 0;
}
.rev-main {
	flex: 1;
	min-width: 0;
}
.rev-top {
	display: flex;
	justify-content: space-between;
	gap: 10px;
}
.rev-rating {
	font-size: 14px;
	font-weight: 600;
	color: var(--ink);
}
.rev-date {
	font-size: 12px;
	color: var(--ink-faint);
}
.rev-note {
	font-size: 14px;
	color: var(--ink-soft);
	margin-top: 3px;
}
.care-del {
	display: block;
	margin: 22px auto 4px;
	border: none;
	background: transparent;
	color: var(--ink-faint);
	font: inherit;
	font-size: 13px;
	cursor: pointer;
}
.care-del:hover {
	color: var(--danger);
}

/* ===== Care forms ===== */
.f-label {
	display: block;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--ink-faint);
	margin: 14px 2px 7px;
}
.f-input {
	width: 100%;
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 12px;
	padding: 12px 14px;
	font: inherit;
	font-size: 16px;
}
.f-input:focus {
	outline: none;
	border-color: var(--brand);
}
.f-area {
	resize: none;
}
select.f-input {
	appearance: none;
	-webkit-appearance: none;
}
/* iOS WebKit renders date inputs through a shadow control that ignores the
   normal box model — the field overflowed the sheet's right edge and centred
   its value. appearance:none makes it obey width like every other .f-input;
   that also collapses its height, hence the explicit min-height, and the
   shadow value element needs its own left-align. */
input[type='date'].f-input {
	appearance: none;
	-webkit-appearance: none;
	display: block;
	min-width: 0;
	max-width: 100%;
	min-height: 46px;
}
input[type='date'].f-input::-webkit-date-and-time-value {
	text-align: left;
}
/* known-name quick-pick under the therapist field (tap to fill) */
.name-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 8px;
}
.name-chip {
	font: inherit;
	font-size: 13px;
	padding: 6px 12px;
	border-radius: 999px;
	border: 1px solid var(--line);
	background: color-mix(in srgb, var(--brand) 7%, transparent);
	color: var(--ink);
	cursor: pointer;
}
.name-chip:active {
	background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.opt-row {
	display: flex;
	gap: 8px;
}
.opt-row.wrap {
	flex-wrap: wrap;
}
.copt {
	border: 1px solid var(--line);
	background: var(--surface);
	color: var(--ink);
	border-radius: 999px;
	padding: 8px 13px;
	font: inherit;
	font-size: 14px;
	cursor: pointer;
}
.copt.on {
	background: var(--brand);
	border-color: var(--brand);
	color: #fff;
}
.f-toggle {
	display: flex;
	align-items: center;
	gap: 9px;
	margin: 16px 2px 2px;
	font-size: 15px;
	color: var(--ink);
}
.f-toggle input {
	width: 20px;
	height: 20px;
	accent-color: var(--brand);
}

/* efficacy face picker */
.face-row {
	display: flex;
	gap: 6px;
}
.face {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	border: 1px solid var(--line);
	background: var(--surface);
	border-radius: 12px;
	padding: 10px 2px;
	font: inherit;
	cursor: pointer;
}
.face-emoji {
	font-size: 24px;
	line-height: 1;
}
.face-label {
	font-size: 10px;
	color: var(--ink-soft);
	text-align: center;
	line-height: 1.2;
}
.face.on {
	border-color: var(--brand);
	background: var(--brand-soft);
}
.btn-danger {
	background: transparent;
	color: var(--danger);
	border: 1px solid var(--line);
	flex: 0 0 auto;
	padding: 14px 18px;
}

/* ===== First-run intro ===== */
.intro-overlay {
	position: fixed;
	inset: 0;
	z-index: 100;
	background: var(--bg);
	display: flex;
	align-items: center;
	justify-content: center;
	/* 8px over the safe areas, not 24 (Ross, 2026-10-05): on a 402×874 phone
	   the 83px of padding above the wheel plus the generous gaps below it ran
	   the card 15px past the screen and clipped "Maybe later". The splash has
	   to fit the 6.1" and 6.3" phones without scrolling; the SE still scrolls. */
	padding: calc(var(--safe-top) + 8px) 26px calc(var(--safe-bottom) + 6px);
	/* Scrollable, and flex-start + auto margins rather than align-items:center
	   — a centred flex child that overflows has its TOP clipped and cannot be
	   scrolled back to. The auto margin still centres it whenever it fits. */
	align-items: flex-start;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}
/* Big off-centre donut behind the splash content (same transparent art as the
   capture modal), slowly turning. */
.intro-overlay::before {
	content: '';
	position: absolute;
	left: 56%;
	top: 44%;
	width: min(215%, 1200px);
	aspect-ratio: 1;
	transform: translate(-50%, -50%);
	background: url('./icons/bg-capture.png') center / contain no-repeat;
	opacity: 0.4;
	pointer-events: none;
	animation: donut-spin 120s linear infinite;
	will-change: rotate;
}
:root[data-theme='light'] .intro-overlay::before {
	opacity: 0.28;
}
.intro-overlay > * {
	position: relative;
	z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
	.intro-overlay::before {
		animation: none;
	}
}
.intro-overlay[hidden] {
	display: none;
}
.intro-card {
	/* Anchored to the top, centred across. It used to centre vertically too,
	   which was invisible while the card filled the screen; with the Needs row
	   gone (1.1.1) the card is ~70px shorter and centring dropped the wheel
	   ~35px, back into the air above it Ross asked to lose on 2026-10-05. The
	   slack now goes below "Maybe later", and an overflowing card still
	   scrolls cleanly from its top. */
	margin: 0 auto auto;
	width: 100%;
	max-width: 360px;
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
}
.intro-wheel {
	/* Doubled (Ross, 2026-08-01) — the wheel IS the product, so it leads.
	   Capped against viewport height as well: 216px is right on a modern
	   iPhone (30vh there is ~260), but on a 667pt SE a fixed 216 pushed the
	   card to 763px inside a 667px overlay and the CTA went off-screen. */
	width: min(162px, 18.5vh); /* 25% down from 216 (Ross, 2026-10-03) to make room for the disclaimer line */
	height: min(162px, 18.5vh); /* 18.5vh keeps 162 on a 6.3" phone and only scales on shorter ones */
	margin-bottom: 6px; /* was 18 — the gap under the donut (Ross, 2026-10-05) */
}
.intro-title {
	font-size: 32px;
	font-weight: 800;
	letter-spacing: -0.02em;
	color: var(--ink);
	margin: 0;
	line-height: 1.1; /* the default leading added ~10px of air above the title */
}
.intro-sub {
	font-size: 14.5px;
	line-height: 1.45;
	color: var(--ink-soft);
	margin: 6px 0 6px;
}
/* The "why it exists" line — quieter than the tagline above it, but not as
   faint as the old privacy footnote, which is now a bullet in its own right. */
.intro-why {
	font-size: 13px;
	line-height: 1.45;
	color: var(--ink-faint);
	margin: 0 0 14px;
}
.intro-list {
	list-style: none;
	margin: 0 0 14px;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
	align-self: stretch;
}
.intro-list li {
	display: flex;
	align-items: center;
	gap: 12px;
	font-size: 14px;
	color: var(--ink);
	text-align: left;
}
.il-dot {
	width: 34px;
	height: 34px;
	flex-shrink: 0;
	border-radius: 10px;
	background: var(--surface);
	border: 1px solid var(--line);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 16px;
}
.intro-priv {
	font-size: 11.5px;
	color: var(--ink-faint);
	margin: 0 0 20px;
}
.intro-cta {
	align-self: stretch;
	background: var(--brand);
	color: #fff;
	border: none;
	border-radius: 14px;
	padding: 15px;
	font: inherit;
	font-size: 16px;
	font-weight: 700;
	cursor: pointer;
	box-shadow: 0 6px 18px color-mix(in srgb, var(--brand) 50%, transparent);
}
.intro-cta .plus {
	font-size: 19px;
	margin-right: 4px;
}
.intro-skip {
	margin-top: 8px;
	background: none;
	border: none;
	color: var(--ink-faint);
	font: inherit;
	font-size: 13.5px;
	cursor: pointer;
}

/* ---- Skills: the merged skill card (Skills tab), section + form ----------- */

/* One card per skill (Ross, 2026-09-14 — the separate practice cards
   duplicated the list rows). A LIVE skill's card is tinted like the old
   practice card so the homework in flight stands out from Coming up /
   Finished, and its right column stacks ✓ Log over the period count. */
.skill-card.is-live {
	background: color-mix(in srgb, var(--brand) 8%, var(--surface));
	border-color: color-mix(in srgb, var(--brand) 20%, var(--line));
}
/* A slot has passed with nothing against it. Emphasis only — deliberately not
   red and never a count of misses (see the no-guilt rule in data/skills.js). */
.skill-card.is-due {
	background: color-mix(in srgb, var(--brand) 15%, var(--surface));
	border-color: color-mix(in srgb, var(--brand) 38%, var(--line));
}
.skill-card.is-done {
	background: var(--surface);
	border-color: var(--line);
}
.skill-card.is-live .cc-eff {
	flex-direction: column;
	align-items: flex-end;
	gap: 4px;
}
.skill-card.is-live .cc-caret {
	display: none; /* the whole card opens the detail; the column is Log + count */
}
.skill-card .pc-log {
	margin: 0;
}
.pc-log {
	flex: none;
	border: 0;
	background: var(--brand);
	color: #fff;
	border-radius: 999px;
	padding: 6px 13px;
	font: inherit;
	font-size: 12.5px;
	font-weight: 700;
	line-height: 1;
	cursor: pointer;
}
.skill-card.is-done .pc-log {
	background: color-mix(in srgb, var(--brand) 55%, var(--surface));
}
/* ✕ Log sits under ✓ Log (Ross, 2026-11-07) on skills, impacts and
   medications: maroon, and it keeps its colour when the period is done so it
   still reads as the "didn't" button. Same width as the ✓ so the pair reads
   as one control. */
.pc-log.pc-log-minus,
.skill-card.is-done .pc-log.pc-log-minus {
	background: var(--minus);
	color: #fff;
}
.skill-card.is-live .pc-log,
.log-pair .pc-log {
	min-width: 72px;
	text-align: center;
}
.log-pair {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 4px;
}
.pc-sub {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin-top: 2px;
	min-width: 0;
}
.pc-count {
	flex: none;
	font-size: 12px;
	font-weight: 600;
	color: var(--ink-soft);
	white-space: nowrap;
}
.pc-prompt {
	flex: 1;
	min-width: 0;
	font-size: 12.5px;
	line-height: 1.3;
	color: var(--ink-soft);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.pc-undo {
	flex: none;
	border: 0;
	background: none;
	color: var(--ink-faint);
	font: inherit;
	font-size: 12px;
	text-decoration: underline;
	cursor: pointer;
}

.skills-section {
	margin-bottom: 6px;
}
/* The summary is a SENTENCE — the one you read out at the next session — so it
   wraps, unlike the one-line facts .cc-sub is built for. Truncating it to
   "…3 with a feeling named (Awk…" defeats the whole point of having it. */
.skill-sum {
	margin-top: 3px;
	white-space: normal;
	line-height: 1.35;
}
/* Skills list rows are two lines, hard — a long title ellipsises rather than
   wrapping the section into a screenful (Ross, 2026-09-03). */
.skill-card .cc-title {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
/* Session form: the share-notes hint and the button pair under it */
.s-hint {
	font-size: 12px;
	color: var(--ink-soft);
	margin: -2px 0 6px;
	line-height: 1.4;
}
.s-row {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}
.s-row .session-import {
	flex: 1 1 auto;
}

/* Frequency / time-window rows in the skill form. */
.sk-freq {
	display: flex;
	align-items: center;
	gap: 8px;
}
.sk-count {
	width: 78px;
	flex: none;
}
.sk-unit {
	width: auto;
	flex: none;
}
.sk-time {
	flex: 1;
	min-width: 0;
}
.sk-times {
	font-size: 14px;
	color: var(--ink-soft);
	flex: none;
}
.sk-note {
	margin: 12px 0 0;
	font-size: 12.5px;
	line-height: 1.4;
	color: var(--ink-faint);
}

.practice-log {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.pl-row {
	display: flex;
	gap: 10px;
	font-size: 13.5px;
	padding: 3px 0;
	border-bottom: 1px solid var(--line);
}
.pl-when {
	flex: none;
	width: 46%;
	color: var(--ink-soft);
}
.pl-what {
	flex: 1;
	min-width: 0;
	color: var(--ink);
}
.pl-bare {
	color: var(--ink-faint);
}

/* An entry logged as a skill practice. The frame is monochrome (ink, not the
   core colour) so practices stand out at a glance when scrolling the log with
   a therapist — the card keeps its emotion tint, because which feeling came up
   is the point of having logged it. */
.entry.is-practice {
	border-color: color-mix(in srgb, var(--ink) 45%, var(--edge, var(--line)));
	border-width: 1.5px;
}
.entry-practice {
	flex: none;
	margin-right: 5px;
	font-size: 12px;
}

/* Arrived from a notification tap: a brief ring so the right card is obvious
   without anything being logged on the person's behalf. */
.skill-card.is-focus {
	animation: pc-focus 2.2s ease-out 1;
}
@keyframes pc-focus {
	0%,
	60% {
		box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 55%, transparent);
	}
	100% {
		box-shadow: 0 0 0 0 transparent;
	}
}

/* Permission is asked for at skill creation, so a skill made before reminders
   existed (or one whose prompt was declined) has no other route to the ask.
   This row is that route. Same lozenge family as the skill cards, quieter. */
.notify-nudge {
	margin-bottom: 9px;
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 13px;
	border-radius: 13px;
	background: var(--surface);
	border: 1px dashed color-mix(in srgb, var(--brand) 34%, var(--line));
}
.nn-msg {
	flex: 1;
	min-width: 0;
	font-size: 12.5px;
	line-height: 1.35;
	color: var(--ink-soft);
}
.nn-on {
	flex: none;
	border: 0;
	background: var(--brand);
	color: #fff;
	border-radius: 999px;
	padding: 7px 14px;
	font: inherit;
	font-size: 13px;
	font-weight: 700;
	cursor: pointer;
}

/* ---- Sources step (capture flow reorder, 2026-08-23) ----------------------
   Feeling > Sources > Body & mind: both trailing steps optional, Sources
   first. The foot stacks note field over a two-button row mirroring the
   Specific screen's ghost-continue / primary-save treatment. */
.note-foot .foot-row {
	display: flex;
	gap: 10px;
}
/* "When it happened" — a quiet line under the review card on the Sources step.
   Collapsed it is a button stating the stamp; open it is a date + time pair. */
.when-row {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	margin: 10px 0 2px;
	padding: 8px 12px;
	border: 1px solid var(--line);
	border-radius: 12px;
	background: var(--surface);
	color: var(--ink-soft);
	font: inherit;
	font-size: 13px;
	text-align: left;
	cursor: pointer;
}
.when-row.open {
	cursor: default;
	gap: 6px;
}
.when-ico {
	font-size: 14px;
	flex-shrink: 0;
}
.when-label {
	flex: 1;
	min-width: 0;
	color: var(--ink);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.when-edit {
	flex-shrink: 0;
	color: var(--brand);
	font-weight: 600;
}
.when-row .when-date,
.when-row .when-time {
	flex: 1 1 0;
	min-width: 0;
	font-size: 14px;
	padding: 6px 8px;
}
/* One box for both (Ross, 2026-09-23: "different heights and horizontally
   unaligned"). The date input's own rule (`input[type='date'].f-input`,
   min-height 46 for the Care/Session forms) outranks the pair rule above on
   specificity, so the date field kept 46 while the time field sat at 38,
   and the row's align-items:center then floated the shorter one mid-way.
   Selectors here match that specificity and pin an explicit height, not a
   min — iOS gives the two shadow controls different intrinsic heights, and
   a min only ever lets the taller one win. */
.when-row input[type='date'].when-date,
.when-row input[type='time'].when-time {
	height: 40px;
	min-height: 40px;
	line-height: 26px;
	box-sizing: border-box;
	align-self: stretch;
}
input[type='time'].f-input {
	appearance: none;
	-webkit-appearance: none;
	display: block;
	min-width: 0;
	max-width: 100%;
	min-height: 46px;
}
input[type='time'].f-input::-webkit-date-and-time-value {
	text-align: left;
}
/* Add-your-own chip reads as an affordance, not a tag */
.src-opt.src-add {
	border-style: dashed;
	color: var(--ink-soft);
}
/* Remove badge on custom chips — only rendered in manage mode */
.src-del {
	margin-left: 6px;
	font-weight: 700;
	color: var(--danger);
}
.src-form {
	display: flex;
	gap: 8px;
	margin-top: 10px;
}
.src-form .src-emoji {
	flex: 0 0 64px;
	text-align: center;
}
.src-form .src-name {
	flex: 1;
	min-width: 0;
}
.src-form .btn {
	flex: 0 0 auto;
	padding: 10px 18px;
}

/* Body & mind pills track the text-size setting: smaller at the default size,
   and the large/xl zoom (1.09 / 1.18 on body) scales them back up — at XL a
   13px pill renders at ~15.3px, i.e. the previous fixed size. Scoped to the
   sensation groups; the feeling pills on the Specific screen keep their size. */
.sense-group .pill {
	font-size: 13px;
	padding: 7px 12px;
}

/* A body-only headline is the sensation list itself, so it can run long —
   ellipsise it rather than let it run under the timestamp (a wider 💊 badge
   made this visible; long lists overflowed before too). */
.entry.body-only .entry-emotion {
	flex-shrink: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* ===== Med impacts (Ross, 2026-09-12) =====
   An entry logged off a medication's ✓ Log in Care. Red frame + 💊 so these
   can be found in a hurry, scrolling the log in front of a psychiatrist; the
   card keeps its emotion tint, the ★ keeps being the follow-up flag. */
.entry.is-med {
	border-color: var(--danger);
	border-width: 1.5px;
}
.entry-med {
	flex: none;
	margin-right: 5px;
	font-size: 13px;
}
.chip-med {
	align-self: center;
	font-size: 12px;
	line-height: 1;
	padding: 4px 8px;
	border-radius: 999px;
	border: 1px solid color-mix(in srgb, var(--danger) 55%, transparent);
	color: var(--ink);
	white-space: nowrap;
	margin-right: 4px;
}
/* The ✓ Log on an active medication card — same lozenge as the practice
   cards on home. */
.care-card .cc-log {
	margin-right: 2px;
}
/* Sources step on an impact: Meds locked on, everything else greyed. */
.src-opt:disabled {
	opacity: 0.38;
	cursor: default;
}
.src-opt.locked {
	cursor: default;
}
.sp-note {
	font-size: 12.5px;
	color: var(--ink-soft);
	text-align: center;
	margin: 10px 2px 0;
}
.rc-med {
	display: block;
	font-size: 12px;
	color: var(--ink-soft);
	margin-top: 4px;
}
:root[data-theme='light'] .rc-med {
	color: color-mix(in srgb, var(--ink) 93%, transparent);
}
/* Body & mind: each group's own "+ Add" chip, and the form it opens in the
   pinned foot (same reasoning as the source form — the foot rides above the
   keyboard by construction). */
.pill.pill-add {
	border: 1px dashed var(--edge, var(--line));
	background: transparent;
	color: var(--ink-soft);
}
.sheet-foot.sense-foot {
	flex-wrap: wrap;
}
.sense-foot > .btn {
	flex: 1 1 120px;
}
.sense-form {
	flex: 1 0 100%;
	margin: 0 0 4px;
	align-items: center;
}
.sense-group-tag {
	flex: none;
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--c, var(--ink-soft));
	max-width: 88px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.sense-form .sense-name {
	flex: 1;
	min-width: 0;
}
/* Care › medication detail: the impacts logged against it */
.de-count {
	margin-left: 8px;
	font-size: 11px;
	font-weight: 700;
	color: var(--ink-faint);
	border: 1px solid var(--line);
	border-radius: 999px;
	padding: 1px 7px;
}
.imp-row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 10px;
	padding: 8px 0;
	border-top: 1px solid var(--line);
	font-size: 14px;
	color: var(--ink);
}
.imp-what {
	flex: 1;
	min-width: 0;
}
.imp-sub {
	font-size: 12px;
	color: var(--ink-soft);
	margin-top: 2px;
}
.imp-date {
	flex: none;
	font-size: 12px;
	color: var(--ink-soft);
	white-space: nowrap;
}

/* ===== People (Care › People, Ross, 2026-10-05) =====
   "Who was there?" on the Sources step reuses the source picker's own label
   and chip look; a person chip on an open card is the med chip's shape in the
   brand colour, so it reads as a tag and not as a warning. */
.people-pick {
	margin: 4px 0 14px;
}
.chip-person {
	align-self: center;
	font-size: 12px;
	line-height: 1;
	padding: 4px 8px;
	border-radius: 999px;
	border: 1px solid color-mix(in srgb, var(--brand) 55%, transparent);
	color: var(--ink);
	white-space: nowrap;
	margin-right: 4px;
}
/* A person's page: the feelings that come up most around them, counted. */
.ppl-top {
	font-size: 13.5px;
	line-height: 1.45;
	color: var(--ink-soft);
	margin: 6px 0 8px;
}
.ppl-top b {
	color: var(--ink);
	font-weight: 600;
}

/* ===== Learning (Skills › Learning, Ross, 2026-10-05) =====
   Takeaways each carry their own 🎯; captures (text read from screenshots, or
   pasted) clamp to a few lines in the detail and open on a tap; on the form
   each capture is a removable row. */
.tk-list {
	display: flex;
	flex-direction: column;
}
.tk-row {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	padding: 8px 0;
	border-top: 1px solid var(--line);
}
.tk-row:first-child {
	border-top: none;
	padding-top: 2px;
}
.tk-text {
	flex: 1;
	min-width: 0;
	font-size: 14.5px;
	line-height: 1.4;
	color: var(--ink);
	white-space: pre-wrap;
}
.tk-promote {
	flex: none;
	font: inherit;
	font-size: 16px;
	line-height: 1;
	padding: 6px 9px;
	border-radius: 999px;
	border: 1px solid var(--line);
	background: var(--surface);
	cursor: pointer;
}
.tk-promote:active {
	background: var(--brand-soft);
}
.ln-open {
	margin-bottom: 14px; /* air before the Takeaways label */
}
.ln-capsum {
	font-size: 12.5px;
	color: var(--ink-soft);
	margin: 4px 0 6px;
}
.cap-row {
	border-top: 1px solid var(--line);
	padding: 8px 0;
}
.cap-text {
	font-size: 14px;
	line-height: 1.45;
	color: var(--ink);
	white-space: pre-wrap;
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
	cursor: pointer;
}
.cap-text.open {
	display: block;
	-webkit-line-clamp: unset;
}
.ln-caps {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-top: 8px;
}
.ln-cap {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 13px;
	color: var(--ink);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 12px;
	padding: 8px 10px;
}
.ln-cap-text {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.ln-cap-del {
	flex: none;
	font: inherit;
	font-size: 17px;
	line-height: 1;
	border: none;
	background: none;
	color: var(--ink-faint);
	cursor: pointer;
	padding: 2px 6px;
}
#ln-paste-box {
	margin-top: 8px;
}
.ln-note {
	text-align: left;
}

/* ===== App-bar Needs + Help, Skills tab (Ross, 2026-09-12) =====
   Needs left the tab bar for the app bar (a 💬 beside the heart menu) and a
   ? beside it opens the user guide; the freed tab slot went to Skills. */
.bar-glyph svg {
	/* 24 → 29 and 23 → 28 (Ross, 2026-09-19: "20% bigger sitting next to
	   the hamburger heart") — the heart glyph is 32×30 in the same 48px
	   button, and at 24 the two beside it read as a size down. */
	width: 29px;
	height: 29px;
	display: block;
	color: var(--brand);
}
/* A bare ? (Ross, 2026-09-14: the ring made it harder to read). */
.help-glyph {
	font-size: 28px;
	font-weight: 800;
	line-height: 1;
	color: var(--brand);
}
/* Lit while the Needs view is showing — it has no tab to be active. */
#needs-btn.is-on {
	background: color-mix(in srgb, var(--brand) 18%, var(--surface));
	border-color: color-mix(in srgb, var(--brand) 55%, var(--line));
}
/* The Skills tab borrows Sessions' image with its own hue and drift so it
   reads as its own screen. */
#bg-art[data-view='skills'] {
	filter: hue-rotate(40deg) saturate(1.2);
	--bg-duration: 125s;
	--bg-dir: reverse;
	--bg-delay: -55s;
	--bg-scale-a: 1.16;
	--bg-scale-b: 1.22;
	--bg-rot: 4deg;
	--bg-tx: 3%;
	--bg-ty: 2.5%;
}
/* ===== Impacts (Ross, 2026-09-14) =====
   Things tracked for their effect on you, under the skills on the Skills
   tab; logged like a med impact, so the entry shares the red frame and the
   badge slot — with the impact's own emoji in it. */
.entry.is-impact {
	border-color: var(--danger);
	border-width: 1.5px;
}
/* How a practice or an impact went (Ross, 2026-11-07): 2px wider than the
   other frames, dark green for ✓, maroon for ✕. Declared after is-practice
   and is-impact so it wins over both. */
.entry.is-plus {
	border-color: var(--plus);
	border-width: 3.5px;
}
.entry.is-minus {
	border-color: var(--minus);
	border-width: 3.5px;
}
.impacts-section {
	margin-top: 14px;
}
.imp-name-row {
	display: flex;
	gap: 8px;
}
.imp-name-row .imp-emoji {
	flex: 0 0 64px;
	text-align: center;
}
.imp-name-row .imp-name {
	flex: 1;
	min-width: 0;
}
/* The impact drawn as the locked chip at the head of the Sources grid. */
.src-opt.src-impact {
	border-color: color-mix(in srgb, var(--danger) 55%, transparent);
}

/* ===== Tab bar: Feelings anchored in the middle (Ross, 2026-09-14) =====
   Care · Sessions · Feelings · Skills · Insights. The home tab is drawn a
   touch larger, on a soft disc that fills when it is active, so the eye has
   a fixed point to come back to. */
.tab-home .tab-icon {
	width: 34px;
	height: 34px;
	margin: -2px 0;
	padding: 3px;
	opacity: 1;
	/* The halo path is drawn larger than the viewBox; let it out. */
	overflow: visible;
}
/* The disc became a HEART (Ross, 2026-09-19: "a same shaped heart shadow
   instead of the circle"): a second copy of the same path, scaled up behind
   the glyph, at the tints the disc used — so the anchor reads as a heart
   with a heart's shadow rather than a heart pinned to a coin. */
.tab-home .tab-halo {
	fill: color-mix(in srgb, var(--brand) 14%, transparent);
	transform-box: fill-box;
	transform-origin: center;
	transform: rotate(-4deg) scale(1.42);
	transition: fill 0.2s;
}
.tab-home.is-active .tab-halo {
	fill: color-mix(in srgb, var(--brand) 28%, transparent);
}
.tab-home.is-active .tab-icon {
	transform: none;
}

/* Feature flags (feature-flags.js). Content behind a flag is hidden unless the
   page is marked on, so a switch that never runs fails closed. !important
   because several of these elements set their own display, which beats the
   UA's [hidden] rule (the trap this file has hit three times before). */
html:not(.needs-on) [data-needs],
html:not(.reflections-on) [data-reflections] {
	display: none !important;
}
