Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/lib/components/bench/FirstRunHint.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,20 @@
color: var(--ink);
}

/* On a phone the footer disclaimer owns the bottom edge - stack the hint above it. */
@media (max-width: 640px) {
/* On a phone the floating transport pill (SidebarRail) owns the bottom-centre - lift the hint clear
of it so the two never stack on top of each other. */
@media (max-width: 900px) {
.hint {
bottom: 54px;
bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);
/* Centre on the viewport with auto margins, NOT translateX(-50%): the fade-up animation ends
on `transform: none`, which nukes the translate and left the pill pinned by its left edge -
overflowing the right side and widening the mobile layout viewport (which then threw off
every 100vw sheet on the page). Margin-centring survives the animation and can't overflow. */
left: 0;
right: 0;
width: max-content;
max-width: calc(100vw - 2 * var(--sp-4));
margin-inline: auto;
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/lib/components/bench/LineageCanvas.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -422,4 +422,17 @@
.add:hover {
border-color: var(--accent);
}

/* Phone: the floating transport pill (SidebarRail) owns the bottom-centre and already carries an
"add environment" button, so the canvas's own Add pill stands down and the zoom cluster lifts
clear of the transport pill instead of sharing the bottom edge with it. */
@media (max-width: 900px) {
.add {
display: none;
}

.controls {
bottom: calc(env(safe-area-inset-bottom, 0px) + 76px);
}
}
</style>
9 changes: 9 additions & 0 deletions src/lib/components/common/FooterPill.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,13 @@
font-weight: var(--fw-medium);
color: var(--ink3);
}

/* Phone: the floating transport pill owns the bottom edge, and this line would collide with it. The
disclaimer is still carried by the intro and by the controls sheet's foot, so the floating copy
stands down on the narrow layout. */
@media (max-width: 900px) {
.pill {
display: none;
}
}
</style>
7 changes: 5 additions & 2 deletions src/lib/components/research/ResearchStage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,11 @@
@media (max-width: 768px) {
.console,
.console.has-design {
display: flex;
flex-direction: column;
/* A plain block, NOT a flex column: flex children shrink by default, so once the stacked
zones are taller than the viewport (a real phone) the rail box shrank below the subject
card and its fixed-height canvas overflowed into - or was clipped by - the next zone. As a
block each zone simply takes its natural height and the console scrolls past them. */
display: block;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
Expand Down
28 changes: 27 additions & 1 deletion src/lib/components/shell/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
<aside
class="sidebar"
class:overlay={shell.narrow}
style:width="{shell.width}px"
style:width={shell.narrow ? null : `${shell.width}px`}
aria-label="lab controls"
>
<div class="panel">
Expand Down Expand Up @@ -516,4 +516,30 @@
line-height: var(--leading-body);
color: var(--ink3);
}

/*
* Phone: the full controls arrive as a BOTTOM SHEET, not a left drawer - the transport now lives in
* a floating bottom pill (SidebarRail), and the sheet rises from the same edge the "more" button
* sits on. Full width, rounded top, capped height with the panel scrolling inside it.
*/
@media (max-width: 900px) {
.overlay {
top: auto;
left: 0;
right: 0;
bottom: 0;
width: 100%;
max-width: 100%;
height: auto;
max-height: 88vh;
border-right: none;
border-top: 1px solid var(--line);
border-radius: var(--radius-modal) var(--radius-modal) 0 0;
animation: slide-in-up var(--dur-enter) var(--ease) both;
}

.overlay .panel {
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--sp-6));
}
}
</style>
59 changes: 58 additions & 1 deletion src/lib/components/shell/SidebarRail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
title="expand the controls"
onclick={() => shell.toggle()}
>
<Icon name="chevron-right" />
<!-- Desktop: a chevron that opens the panel docked to the right. Phone: the pill is horizontal
and the full controls arrive as a bottom sheet, so a "sliders" glyph reads as "more" better
than a sideways chevron would. -->
<Icon name={shell.narrow ? 'sliders' : 'chevron-right'} />
</Button>

{#if !shell.open}
Expand Down Expand Up @@ -137,4 +140,58 @@
.rail :global(.story) {
color: var(--accent);
}

/*
* Phone (the narrow layout, matching the shell's overlay switch): the left rail becomes a FLOATING
* BOTTOM PILL. No docked strip eating the width and no per-button boxes - one glass pill of bare
* icons, thumb-reachable at the bottom. The "more" button (the expand control) moves to the end and
* opens the full controls as a bottom sheet. It keeps the shell's z-index, so its own scrim covers
* it when the sheet is up.
*/
@media (max-width: 900px) {
.rail {
position: fixed;
top: auto;
left: 50%;
bottom: calc(env(safe-area-inset-bottom, 0px) + var(--sp-4));
transform: translateX(-50%);
flex-direction: row;
align-items: center;
width: auto;
max-width: calc(100vw - 2 * var(--sp-4));
height: auto;
padding: var(--sp-1) var(--sp-2);
gap: 2px;
border: 1px solid var(--line);
border-radius: var(--radius-pill);
background: var(--glass);
backdrop-filter: blur(var(--blur-glass));
box-shadow: var(--shadow-pill);
}

/* The rail's group dividers are a vertical-stack affordance; the compact pill spaces its icons
evenly instead, with the "more" button set off at the end. */
.rail .divider {
display: none;
}

/* Bare icons in the pill - the glass IS the container, so the buttons drop their own boxes. */
.rail :global(.btn) {
border-color: transparent;
background: none;
box-shadow: none;
}

.rail :global(.btn:not(:disabled):hover) {
background: var(--chip);
}

/* "More" (the expand button, the first child) to the end of the pill, with a hairline before it. */
.rail :global(.btn:first-child) {
order: 1;
margin-left: var(--sp-1);
border-left: 1px solid var(--line);
border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
}
</style>
29 changes: 29 additions & 0 deletions src/lib/components/topbar/TopBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@
}

@media (max-width: 640px) {
header {
flex-wrap: nowrap;
gap: var(--sp-3);
padding: var(--sp-3) var(--sp-4);
}

/* Logo only: the mark is identity enough on a phone, so the wordmark, tag and scenario drop out
and the mode switch + app icons get a single, uncrowded row instead of wrapping to two. */
.wordmark,
.lab,
.divider {
display: none;
Expand All @@ -218,5 +227,25 @@
header :global(.live-tag) {
display: none;
}

/* The GitHub source link is the one control that is not a phone action - it drops out here, both
to declutter and because a third 44px touch target tipped the single row over the viewport
width (a fixed row that overflows widens the mobile layout viewport). Theme + settings stay. */
.gh {
display: none;
}

/* Bare icons, no boxes - on a phone the app controls read cleaner as plain glyphs than a row of
bordered chips. The 44px coarse-pointer tap target (Button) is untouched. */
header :global(.btn) {
border-color: transparent;
background: none;
box-shadow: none;
}

.gh:hover,
header :global(.btn:not(:disabled):hover) {
background: var(--chip);
}
}
</style>
10 changes: 10 additions & 0 deletions src/lib/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ textarea,
}
}

/* The control panel arriving as a bottom sheet on a phone - up from the bottom edge it now docks to. */
@keyframes slide-in-up {
from {
transform: translateY(100%);
}
to {
transform: none;
}
}

/* A scene arriving: it settles into place rather than sliding, so a cut reads as a cut. */
@keyframes scene-in {
from {
Expand Down
7 changes: 6 additions & 1 deletion src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@
* this they anchor under the sidebar and read as centred on a page they are not centred on. An
* overlay panel is not counted: it is on top of the bench, not beside it.
*/
const gutter = $derived(shell.open && !shell.narrow ? `${shell.width}px` : 'var(--rail-width)');
// Bench-centred floats (the first-run hint) offset by whatever the sidebar leaves on the LEFT. On a
// phone the sidebar is a floating bottom pill, not a left rail, so it leaves nothing on the left -
// the gutter is 0 there, and the offset only applies to the docked/rail desktop layouts.
const gutter = $derived(
shell.narrow ? '0px' : shell.open ? `${shell.width}px` : 'var(--rail-width)'
);

// One dialog, for whichever world asked for it. It resolves to `undefined` the instant that world
// is removed, so the dialog cannot outlive the thing it edits.
Expand Down
Loading