diff --git a/src/lib/components/bench/LineageCanvas.svelte b/src/lib/components/bench/LineageCanvas.svelte index 8795998..ae8f5a3 100644 --- a/src/lib/components/bench/LineageCanvas.svelte +++ b/src/lib/components/bench/LineageCanvas.svelte @@ -16,7 +16,7 @@ import Button from '../common/Button.svelte'; import Icon from '../common/Icon.svelte'; import type { WorldEntry } from '$lib/state'; - import { bench, canvas, prefersReducedMotion } from '$lib/state'; + import { bench, canvas, prefersReducedMotion, shell } from '$lib/state'; import { NODE_W, ESTIMATED_NODE_H, edgePath } from '$lib/lab/lineage'; interface Props { @@ -190,6 +190,15 @@ return { minX, minY, maxX, maxY }; } + /** + * On a phone the transport pill floats over the bottom of the canvas; reserve its band so a framed + * tree sits ABOVE it rather than with its lowest node tucked underneath. Desktop reserves nothing. + */ + const PILL_BAND = 120; + function bottomReserve() { + return shell.narrow ? PILL_BAND : 0; + } + /** Frame the whole tree in the viewport - the "recenter" action. */ function recenterTree() { const rect = container.getBoundingClientRect(); @@ -198,7 +207,7 @@ return; } const { minX, minY, maxX, maxY } = treeBounds(bench.worlds); - canvas.fitBox(minX, minY, maxX, maxY, rect.width, rect.height); + canvas.fitBox(minX, minY, maxX, maxY, rect.width, rect.height - bottomReserve()); } // Wheel must be a NON-PASSIVE listener or preventDefault is ignored and the page scrolls instead @@ -229,7 +238,8 @@ const s = Math.min(1, (rect.width - 2 * MARGIN) / NODE_W); // 1 on anything but a narrow phone canvas.scale = s; canvas.tx = (rect.width - (maxX - minX) * s) / 2 - minX * s; - canvas.ty = (rect.height - (maxY - minY) * s) / 2 - minY * s; + // Centre in the height ABOVE the phone's floating pill, so the opening frame clears it too. + canvas.ty = (rect.height - bottomReserve() - (maxY - minY) * s) / 2 - minY * s; } onMount(() => { diff --git a/src/lib/components/common/Segmented.svelte b/src/lib/components/common/Segmented.svelte index 19e9bbe..81675e9 100644 --- a/src/lib/components/common/Segmented.svelte +++ b/src/lib/components/common/Segmented.svelte @@ -110,15 +110,15 @@ color: var(--ink); } - /* The chosen segment lifts OFF the track. The track is `--chip` (panel tinted TOWARD the ink - a - little lighter in dark, a little darker in light), so a plain `--panel` fill lifts in light but - SINKS in dark, where it comes out darker than the track (the selected segment read as a recessed - hole with a leaking shadow). Mixing a touch of white in makes the thumb sit ABOVE the track in - both themes - unchanged white in light, a genuinely lighter grey in dark. */ + /* The chosen segment reads through a clean, FLAT fill that is a step lighter than the track. The + track is `--chip` (panel tinted toward the ink - lighter in dark, darker in light), so a plain + `--panel` fill lifts in light but SINKS in dark (the selected segment came out darker than the + track, a recessed hole). Mixing a touch of white in makes the thumb sit clearly ABOVE the track in + both themes. NO shadow: on the near-black dark track its blur leaked past the rounded corners and + read as a crooked/offset box - the lightness step alone is the signal. */ .checked { - background: color-mix(in srgb, var(--panel), #fff 15%); + background: color-mix(in srgb, var(--panel), #fff 18%); color: var(--ink); - box-shadow: var(--shadow-segment); } /* The panel's compact rows: eyebrow-sized type, tight padding - same roles, less chrome. */