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
24 changes: 15 additions & 9 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -676,12 +676,13 @@ summary::-webkit-details-marker {
transparent 0%,
color-mix(in srgb, var(--background) 8%, transparent) 38%,
color-mix(in srgb, var(--background) 18%, transparent) 68%,
color-mix(in srgb, var(--background) 32%, transparent) 100%
color-mix(in srgb, var(--background) 32%, transparent) 88%,
color-mix(in srgb, var(--background) 42%, transparent) 100%
);
backdrop-filter: blur(2px) saturate(130%);
-webkit-backdrop-filter: blur(2px) saturate(130%);
mask-image: linear-gradient(180deg, rgb(0 0 0 / 20%) 0%, rgb(0 0 0 / 45%) 28%, black 60%, black 100%);
-webkit-mask-image: linear-gradient(180deg, rgb(0 0 0 / 20%) 0%, rgb(0 0 0 / 45%) 28%, black 60%, black 100%);
mask-image: linear-gradient(180deg, rgb(0 0 0 / 20%) 0%, rgb(0 0 0 / 45%) 28%, black 55%, black 100%);
-webkit-mask-image: linear-gradient(180deg, rgb(0 0 0 / 20%) 0%, rgb(0 0 0 / 45%) 28%, black 55%, black 100%);
}

.answer-footer-search-dock .answer-footer-search-backdrop::before,
Expand All @@ -702,8 +703,8 @@ summary::-webkit-details-marker {
.answer-footer-search-dock .answer-footer-search-backdrop::after {
backdrop-filter: blur(22px) saturate(140%);
-webkit-backdrop-filter: blur(22px) saturate(140%);
mask-image: linear-gradient(180deg, transparent 0%, transparent 55%, black 72%, black 100%);
-webkit-mask-image: linear-gradient(180deg, transparent 0%, transparent 55%, black 72%, black 100%);
mask-image: linear-gradient(180deg, transparent 0%, transparent 50%, black 68%, black 100%);
-webkit-mask-image: linear-gradient(180deg, transparent 0%, transparent 50%, black 68%, black 100%);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
Expand Down Expand Up @@ -1307,17 +1308,22 @@ summary::-webkit-details-marker {
}

@media (max-width: 639px) {
.dashboard-composer-edge.answer-footer-search-edge {
.edge-glass-header {
padding-left: max(0px, var(--safe-area-left));
padding-right: max(0px, var(--safe-area-right));
}

.dashboard-composer-edge.answer-footer-search-edge:not(.answer-footer-search-dock) {
width: min(calc(100vw - 8px - var(--safe-area-left) - var(--safe-area-right)), 400px);
}

.document-mobile-search-edge {
.document-mobile-search-edge:not(.answer-footer-search-dock) {
left: max(0.375rem, var(--safe-area-left));
right: max(0.375rem, var(--safe-area-right));
bottom: max(0.5rem, calc(var(--safe-area-bottom) + 0.375rem));
}

.document-mobile-search-edge.answer-footer-search-edge {
.document-mobile-search-edge.answer-footer-search-edge:not(.answer-footer-search-dock) {
left: 50%;
right: auto;
bottom: max(0.45rem, calc(var(--safe-area-bottom) + 0.35rem));
Expand All @@ -1327,7 +1333,7 @@ summary::-webkit-details-marker {

/* Compact search/result views: no chip row below the pill, so the pill
itself hugs the bottom edge and the scrim shrinks to match. */
.document-mobile-search-edge.answer-footer-search-edge.document-mobile-search-compact {
.document-mobile-search-edge.answer-footer-search-edge.document-mobile-search-compact:not(.answer-footer-search-dock) {
bottom: max(0.4rem, calc(var(--safe-area-bottom) + 0.3rem));
}

Expand Down
13 changes: 12 additions & 1 deletion src/components/ClinicalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4258,6 +4258,13 @@ export function ClinicalDashboard({
const compactMobileBottomSearch = hasMobileBottomSearch && modeSearchSubmitted;
const differentialsCompareAddonActive =
searchMode === "differentials" && modeSearchSubmitted && Boolean(query.trim());
const mobileComposerReserve = !hasMobileBottomSearch
? "0px"
: compactMobileBottomSearch
? differentialsCompareAddonActive
? "calc(8.75rem + env(safe-area-inset-bottom))"
: "calc(5rem + env(safe-area-inset-bottom))"
: "calc(5.25rem + env(safe-area-inset-bottom))";
const renderDegradedNotice = () => (
<UtilityDrawer
icon={!isOnline ? WifiOff : AlertCircle}
Expand Down Expand Up @@ -4376,6 +4383,7 @@ export function ClinicalDashboard({
{
"--clinical-sidebar-width": sidebarCollapsed ? "5.25rem" : "20rem",
"--clinical-sidebar-width-md": "5.25rem",
"--mobile-composer-reserve": mobileComposerReserve,
} as CSSProperties
}
>
Expand Down Expand Up @@ -4482,6 +4490,7 @@ export function ClinicalDashboard({
<div
className={cn(
"mx-auto max-w-7xl space-y-4 overflow-x-hidden px-3 py-4 sm:space-y-5 sm:px-4 sm:py-5 lg:px-8",
compactMobileModeHome && "max-sm:px-0",
// Centred mode homes carry little content, so drop the large
// mobile bottom padding (the fixed composer already has its own
// reserved margin on <main>) to avoid a needless scrollbar.
Expand Down Expand Up @@ -4523,7 +4532,9 @@ export function ClinicalDashboard({

<section
className={cn(
"min-h-[calc(100dvh-12.5rem)] sm:min-h-[calc(100dvh-11rem)]",
compactMobileModeHome
? "max-sm:flex max-sm:min-h-0 max-sm:flex-1 max-sm:flex-col max-sm:justify-center"
: "min-h-[calc(100dvh-12.5rem)] sm:min-h-[calc(100dvh-11rem)]",
centeredModeHome || (activeModeResultKind === "answer" && !answer && !loading)
? // On tall phones the centred home leans slightly toward the
// bottom composer (matches the committed vertical-weighting
Expand Down
6 changes: 4 additions & 2 deletions src/components/clinical-dashboard/favourites-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ export function FavouritesHomePage({ query = "" }: FavouritesHomePageProps) {
const filterQuery = queryOverride?.source === query ? queryOverride.value : query;

return (
<main className="min-h-[calc(100dvh-4rem)] bg-[color:var(--background)] px-3 py-4 text-[color:var(--text)] sm:px-4 sm:py-5 lg:px-8">
<FavouritesHub
<main className="flex min-h-0 w-full flex-1 flex-col bg-[color:var(--background)] px-0 py-4 text-[color:var(--text)] max-sm:pt-[clamp(1.25rem,4vh,2.25rem)] sm:min-h-[calc(100dvh-4rem)] sm:px-4 sm:py-5 lg:px-8">
<div className="px-4 sm:px-0">
<FavouritesHub
query={filterQuery}
onClearQuery={() => {
setQueryOverride({ source: query, value: "" });
Expand All @@ -28,6 +29,7 @@ export function FavouritesHomePage({ query = "" }: FavouritesHomePageProps) {
desktopComposerSlotId={modeHomeDesktopComposerSlotId}
headingLevel={1}
/>
</div>
</main>
);
}
27 changes: 17 additions & 10 deletions src/components/clinical-dashboard/global-mockup-search-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ function GlobalMockupSearchShellClient({
const effectiveSidebarCollapsed = isDifferentialPresentationWorkflow ? true : sidebarCollapsed;
const effectiveSidebarWidth = shouldShowDesktopSidebar ? (effectiveSidebarCollapsed ? "5.25rem" : "20rem") : "0px";
const shouldShowSearchComposer = searchComposerVisible && !isDifferentialPresentationWorkflow;
const mobileComposerReserve = !shouldShowSearchComposer
? "2rem"
: searchMode === "answer"
? "calc(9rem + env(safe-area-inset-bottom))"
: useCompactBottomSearch
? "calc(5.5rem + env(safe-area-inset-bottom))"
: "calc(9rem + env(safe-area-inset-bottom))";

useEffect(() => {
// Re-derive the mode and query from the URL, but only when the search string
Expand Down Expand Up @@ -312,7 +319,7 @@ function GlobalMockupSearchShellClient({
return (
<div
className={cn(
"min-h-dvh bg-[color:var(--background)] text-[color:var(--text)]",
"min-h-dvh max-sm:h-dvh max-sm:overflow-hidden bg-[color:var(--background)] text-[color:var(--text)]",
shouldShowDesktopSidebar && "md:grid md:grid-cols-[5.25rem_minmax(0,1fr)]",
shouldShowDesktopSidebar && "motion-safe:transition-[grid-template-columns] motion-safe:duration-200 motion-safe:ease-out",
shouldShowDesktopSidebar &&
Expand All @@ -322,6 +329,7 @@ function GlobalMockupSearchShellClient({
{
"--clinical-sidebar-width": effectiveSidebarWidth,
"--clinical-sidebar-width-md": shouldShowDesktopSidebar ? "5.25rem" : "0px",
"--mobile-composer-reserve": mobileComposerReserve,
} as CSSProperties
}
>
Expand All @@ -348,7 +356,7 @@ function GlobalMockupSearchShellClient({
</div>
) : null}

<div className="flex min-h-dvh min-w-0 flex-col">
<div className="flex min-h-dvh min-w-0 flex-col max-sm:h-dvh max-sm:min-h-0 max-sm:overflow-hidden">
{/* max-sm:contents lets the header's own `sticky top-0` engage against
the document scroll on phones (a plain wrapper div otherwise caps
its sticking range at its own height), which the phone
Expand Down Expand Up @@ -415,17 +423,16 @@ function GlobalMockupSearchShellClient({
id="main-content"
tabIndex={-1}
className={cn(
// Phone: fill the space under the header exactly (the header is
// taller than the 4rem the calc assumed, which forced a phantom
// scrollbar on every standalone page). sm+ keeps the original calc.
"min-w-0 overflow-x-hidden focus:outline-none max-sm:flex-1 sm:min-h-[calc(100dvh-4rem)]",
// Phone: flex column fills the viewport under the header; composer
// clearance comes from --mobile-composer-reserve on the shell.
"min-w-0 overflow-x-hidden focus:outline-none max-sm:flex max-sm:min-h-0 max-sm:flex-1 max-sm:flex-col max-sm:pb-[var(--mobile-composer-reserve)] sm:min-h-[calc(100dvh-4rem)]",
!shouldShowSearchComposer
? "pb-8"
? "sm:pb-8"
: searchMode === "answer"
? "pb-[calc(9rem+env(safe-area-inset-bottom))]"
? "sm:pb-[calc(9rem+env(safe-area-inset-bottom))]"
: useCompactBottomSearch
? "pb-[calc(5.5rem+env(safe-area-inset-bottom))] sm:pb-8"
: "pb-[calc(9rem+env(safe-area-inset-bottom))] sm:pb-8",
? "sm:pb-[calc(5.5rem+env(safe-area-inset-bottom))] sm:pb-8"
: "sm:pb-[calc(9rem+env(safe-area-inset-bottom))] sm:pb-8",
)}
>
<ClientHydrationBoundary
Expand Down
18 changes: 11 additions & 7 deletions src/components/clinical-dashboard/master-search-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1198,13 +1198,17 @@ export function MasterSearchHeader({
? "floating-composer-edge dashboard-composer-edge fixed z-40 mx-auto max-w-3xl lg:max-w-4xl"
: usesMobileBottomStyle
? cn(
"document-mobile-search-edge universal-top-search-edge fixed z-40 mx-auto max-w-3xl sm:z-20 sm:w-full sm:px-4 sm:py-3 lg:max-w-4xl",
// Hero-placement mode-homes (services/forms) portal the composer into
// the hero from sm up. Hide the default (non-portaled) composer at sm+
// so it never briefly flashes as an overlapping float over the hero
// before the portal activates; the mobile fixed-bottom slot still shows
// below sm. Other homes keep a sticky bar until the portal lifts it.
isHeroDesktopComposer ? "sm:hidden" : "sm:sticky sm:top-[calc(4.75rem+env(safe-area-inset-top))]",
usesPhoneFooterDock
? "document-mobile-search-edge universal-top-search-edge fixed z-40 w-full"
: cn(
"document-mobile-search-edge universal-top-search-edge fixed z-40 mx-auto max-w-3xl sm:z-20 sm:w-full sm:px-4 sm:py-3 lg:max-w-4xl",
// Hero-placement mode-homes (services/forms) portal the composer into
// the hero from sm up. Hide the default (non-portaled) composer at sm+
// so it never briefly flashes as an overlapping float over the hero
// before the portal activates; the mobile fixed-bottom slot still shows
// below sm. Other homes keep a sticky bar until the portal lifts it.
isHeroDesktopComposer ? "sm:hidden" : "sm:sticky sm:top-[calc(4.75rem+env(safe-area-inset-top))]",
),
)
: "universal-top-search-edge sticky top-[calc(4.75rem+env(safe-area-inset-top))] z-20 mx-auto box-border w-full px-3 py-3 sm:px-4",
usesBottomComposerPlacement && "answer-footer-search-edge",
Expand Down
27 changes: 13 additions & 14 deletions src/components/mode-home-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function ModeHomeHero({

return (
<section
className={cn("grid justify-items-center sm:gap-4", compact ? "gap-2" : "gap-3")}
className={cn("grid justify-items-center px-4 sm:gap-4 sm:px-0", compact ? "gap-2" : "gap-3")}
aria-labelledby={`${testId ?? "mode-home"}-title`}
>
<span
Expand Down Expand Up @@ -129,14 +129,10 @@ export function ModeHomeMain({
<main
data-testid={testId}
className={cn(
// Phone: content is vertically centred (not bottom-anchored) so the page
// no longer collapses all its slack into a void above the hero. The
// standalone shell (#main-content) already reserves 9rem of bottom
// padding for the fixed mobile composer, so this main only subtracts
// that space from its min-height instead of re-adding the padding —
// otherwise short homes scroll by exactly the duplicated reservation.
// From sm up the composer moves into the hero, so the reserve drops away.
"grid min-h-[calc(100dvh-13.75rem-env(safe-area-inset-bottom))] items-center justify-items-center bg-[color:var(--background)] px-4 pb-4 pt-[clamp(1.25rem,4vh,2.25rem)] text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-6 sm:pb-[clamp(1.75rem,5vh,3.25rem)] sm:pt-[clamp(1.75rem,5vh,3.25rem)] lg:px-8",
// Phone: stretch to fill the shell's flex column; the shell reserves
// composer clearance via --mobile-composer-reserve. Background is
// full-bleed; inner template content carries safe-area-aware inset.
"flex min-h-0 w-full flex-1 flex-col items-center justify-center bg-[color:var(--background)] px-0 pt-[clamp(1.25rem,4vh,2.25rem)] pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-6 sm:pb-[clamp(1.75rem,5vh,3.25rem)] sm:pt-[clamp(1.75rem,5vh,3.25rem)] lg:px-8",
className,
)}
>
Expand Down Expand Up @@ -228,20 +224,23 @@ export function ModeHomeTemplate({
<div
data-testid={testId}
className={cn(
"mode-home-template mx-auto box-border flex w-full max-w-[64rem] flex-col items-center justify-center gap-3.5 px-0 text-center sm:gap-6 lg:gap-7",
"mode-home-template mx-auto box-border flex w-full max-w-none flex-col items-center justify-center gap-3.5 px-0 text-center sm:max-w-[64rem] sm:gap-6 lg:gap-7",
className,
)}
>
<ModeHomeHero testId={testId} title={title} subtitle={subtitle} icon={icon} headingLevel={headingLevel} compact />

{desktopComposerSlotId ? (
<div id={desktopComposerSlotId} className="mode-home-composer-slot hidden w-full sm:[&:not(:empty)]:block" />
<div
id={desktopComposerSlotId}
className="mode-home-composer-slot hidden w-full px-4 sm:px-0 sm:[&:not(:empty)]:block"
/>
) : null}

{actions.length ? (
<section
aria-label={actionsLabel}
className="grid w-full max-w-3xl overflow-hidden rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] lg:max-w-none lg:grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] lg:gap-5 lg:overflow-visible lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
className="grid w-full max-w-none overflow-hidden rounded-none border-y border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] sm:max-w-3xl sm:rounded-xl sm:border sm:shadow-[var(--shadow-card)] lg:max-w-none lg:grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] lg:gap-5 lg:overflow-visible lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
>
{actions.map((action, index) => {
const ActionIcon = action.icon;
Expand Down Expand Up @@ -294,7 +293,7 @@ export function ModeHomeTemplate({
) : null}

{pills?.length ? (
<section className="grid w-full max-w-none self-stretch gap-3 border-t border-[color:var(--border)] pt-3 sm:gap-4 sm:pt-6">
<section className="grid w-full max-w-none self-stretch gap-3 border-t border-[color:var(--border)] px-4 pt-3 sm:gap-4 sm:px-0 sm:pt-6">
{pillsTitle || pillsAction ? (
<div
className={cn(
Expand Down Expand Up @@ -341,7 +340,7 @@ export function ModeHomeTemplate({
</section>
) : null}

{footer ? <div className="w-full">{footer}</div> : null}
{footer ? <div className="w-full px-4 sm:px-0">{footer}</div> : null}
</div>
);
}