Redesign the phone layer of the landing
Three layout bugs and a mobile experience that had no wayfinding. The `#proposal` section was clipped, not merely cramped: the submit button carried `whitespace-nowrap` from the shared button base, giving it a min-content width of 358px, which sized the section's implicit `auto` grid track to 406px inside a 343px container. `overflow-hidden` then sliced the copy and every form field off the right edge of a 375px screen. Fixed at the root — `whitespace-nowrap` moves out of `base` and into `headerSize`, where buttons are sized by their label — plus an explicit `grid-cols-[minmax(0,1fr)]` on the section. The market stat cards put their source line in absolute position against a guessed min-height, so the longest label ran underneath it. It is in the flow now, pinned by `mt-auto`. Below 980px the header nav is not rendered, which left seventeen screens of scroll with no way to navigate them. The header gives that width back to the page — logo and call button only — and <ThumbBar /> takes over: scroll progress, a live section indicator that opens a section sheet, and one short CTA, hidden over the hero and over the form. Card rows now say what they are. Rows of unordered peers keep scrolling sideways and gain a measured segment indicator (<Rail />), replacing the two prose "swipe sideways" hints. The launch timeline goes the other way: sideways scrolling hid week 3-5 from someone reading week 1-2, so below 900px it becomes four disclosures on a spine — which also recovers the scroll length the taller hero spent. Below 560px the type scale steps down half a level and the display tracking opens back up, and the hero photo zooms past `cover` onto the wave wall, since fitting by height left only ceiling tiles above the copy. Also: the route card inside the market section split into two 165px columns at 760px, so that split now waits for 980px. Desktop above 980px is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { marketNotes, marketStats, routeWith, routeWithout } from '../data/conte
|
||||
import { useReveal } from '../hooks/useReveal'
|
||||
import { cx } from '../lib/cx'
|
||||
import { Container, Eyebrow, Section, SectionHead, SmallNote } from './Layout'
|
||||
import { Rail } from './Rail'
|
||||
|
||||
export function MarketSection() {
|
||||
const stats = useReveal<HTMLDivElement>()
|
||||
@@ -17,39 +18,41 @@ export function MarketSection() {
|
||||
lead="Забудьте о расходах на привлечение новых клиентов. Мы встраиваем готовое направление физиотерапии в вашу структуру, превращая лояльную базу пациентов и текущий штат врачей в источник новой прибыли."
|
||||
/>
|
||||
|
||||
<div className="grid gap-[24px] md:grid-cols-[minmax(0,1.08fr)_minmax(360px,0.92fr)] md:items-stretch">
|
||||
<div
|
||||
ref={stats.ref}
|
||||
className={cx(
|
||||
'scroll-cards auto-cols-[minmax(255px,82%)] gap-[12px] sm:grid-still sm:grid-cols-2',
|
||||
stats.revealClass,
|
||||
)}
|
||||
<div className="grid grid-cols-[minmax(0,1fr)] gap-[24px] md:grid-cols-[minmax(0,1.08fr)_minmax(360px,0.92fr)] md:items-stretch">
|
||||
<Rail
|
||||
outerRef={stats.ref}
|
||||
wrapperClassName={stats.revealClass}
|
||||
label="Рыночные показатели"
|
||||
className="scroll-cards auto-cols-[minmax(255px,82%)] gap-[12px] sm:grid-still sm:grid-cols-2"
|
||||
>
|
||||
{marketStats.map((stat) => (
|
||||
/* The source line used to be absolutely positioned against the
|
||||
card's min-height, so the longest label ran underneath it.
|
||||
It is in the flow now, pinned to the bottom by `mt-auto`. */
|
||||
<article
|
||||
key={stat.label}
|
||||
className="relative min-h-[185px] overflow-hidden rounded-lg border border-navy/[0.12] bg-white p-[24px] shadow-[0_16px_48px_rgb(4_29_46/0.06)]
|
||||
phone:min-h-[172px] phone:p-[20px] sm:min-h-[205px]"
|
||||
className="relative flex min-h-[185px] flex-col overflow-hidden rounded-lg border border-navy/[0.12] bg-white p-[24px] shadow-[0_16px_48px_rgb(4_29_46/0.06)]
|
||||
phone:min-h-[168px] phone:p-[20px] sm:min-h-[205px]"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="teal-bloom-soft absolute -right-[36px] -bottom-[48px] size-[150px] rounded-full"
|
||||
/>
|
||||
<strong className="mb-[8px] block text-[clamp(34px,5vw,54px)] leading-none font-[830] tracking-[-0.06em] text-navy">
|
||||
<strong className="relative mb-[8px] block text-[clamp(34px,5vw,54px)] leading-none font-[830] tracking-[-0.06em] text-navy">
|
||||
{stat.value}
|
||||
</strong>
|
||||
<p className="max-w-[240px] text-[14px] leading-[1.45] text-muted">{stat.label}</p>
|
||||
<span className="absolute bottom-[19px] left-[24px] text-[10px] tracking-[0.09em] text-[#7E93A3] uppercase phone:bottom-[15px] phone:left-[20px]">
|
||||
<p className="relative max-w-[240px] text-[14px] leading-[1.45] text-muted">{stat.label}</p>
|
||||
<span className="relative mt-auto pt-[14px] text-[10px] tracking-[0.09em] text-[#7E93A3] uppercase">
|
||||
{stat.source}
|
||||
</span>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Rail>
|
||||
|
||||
<article
|
||||
ref={gap.ref}
|
||||
className={cx(
|
||||
'gap-card-surface relative grid gap-[16px] overflow-hidden rounded-xl p-[27px] text-white shadow-deep',
|
||||
'gap-card-surface relative grid gap-[16px] overflow-hidden rounded-xl p-[27px] text-white shadow-deep compact:p-[20px]',
|
||||
gap.revealClass,
|
||||
)}
|
||||
>
|
||||
@@ -60,12 +63,15 @@ export function MarketSection() {
|
||||
|
||||
<div className="relative z-1">
|
||||
<Eyebrow tone="dark">Исходная точка</Eyebrow>
|
||||
<h3 className="max-w-[600px] text-[clamp(27px,3.5vw,42px)]">
|
||||
<h3 className="max-w-[600px] text-[clamp(27px,3.5vw,42px)] compact:text-[22px] compact:tracking-[-0.02em]">
|
||||
Маршрут пациента: как перестать отдавать прибыль конкурентам.
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="relative z-1 grid gap-[12px] md:grid-cols-2">
|
||||
{/* Not `md:` — at 760 the card is already sharing the row with the
|
||||
stats, so splitting it again gives two 165px columns of
|
||||
four-word lines. It waits for 980, where the card is wide. */}
|
||||
<div className="relative z-1 grid gap-[12px] lg:grid-cols-2">
|
||||
<RouteColumn sign="—" title="Пока физиотерапии нет" items={routeWithout} />
|
||||
<RouteColumn sign="+" title="После запуска направления" items={routeWith} accent />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user