Files
exodevices/medcenter/src/components/LaunchSection.tsx
T
yuriy.pandClaude Opus 5 cd51cb5632 Fix horizontal truncation across the phone layer
Four defects, all of them real clipping rather than styling taste.

The header's two action pills were `shrink-0` at 288px inside a 343px
container, so flexbox took the difference out of the logo, which carried
`min-w-0`. The brand mark rendered 37px wide at 375px and 0px at 320px,
and the CTA ran past the right edge. The wordmark stays legible now and
the call button goes square and icon-only below 760px; the CTA moves to
a new MobileActionBar, which has room for it and puts it in the thumb
zone. It waits for the hero's own buttons to scroll away and drops back
down while the form is on screen, so it never covers the fields it
points at.

`.scroll-cards` bleeds to the viewport edge with a 16px inline padding
and a matching negative margin, but had no `scroll-padding-inline`, so
`scroll-snap-align: start` resolved to scroll position 16 and mandatory
snapping forced that scroll on load: the first card sat on the bezel
with no gutter, and one row had drifted a full card over. Matching the
padding fixes the resting position on all seven rows.

Card tracks move from a percentage of the row to `calc(100vw - 62px)`.
A percentage peek grows with the screen, and at 82-90% it exposed ~68px
of the next card -- enough legible half-words to read as broken text.
The value stays in the markup as `auto-cols-*` so it keeps its sort
position against `sm:grid-still`, which has to win at the breakpoint.

The market stat card pinned its source line to the card bottom while the
longest label wraps to four lines at phone width, running text through
text by 75px. The source is in flow under `mt-auto` instead.

The header CTA switches on `max-md` rather than the `narrow` variant:
`narrow` is max-width 760 and the bar is min-width 760, so at exactly
760px both would have hidden and the page would have had no CTA at all.

Verified at 320 / 375 / 759 / 760 / 800 and desktop: no horizontal page
scroll, no in-flow overflow, desktop grids unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 11:51:56 +06:00

77 lines
3.7 KiB
TypeScript

import { formats, timeline } from '../data/content'
import { useReveal } from '../hooks/useReveal'
import { cx } from '../lib/cx'
import { Container, Section, SectionHead, SmallNote } from './Layout'
export function LaunchSection() {
const grid = useReveal<HTMLDivElement>()
const steps = useReveal<HTMLDivElement>()
return (
<Section id="launch" tone="white">
<Container>
<SectionHead
split
eyebrow="Три модели усиления"
title="Можно начать точечно — без длительного закрытия кабинета"
lead="Формат определяется после аудита оборудования, расписания и клинических пробелов. Расширение происходит только после того, как пилот подтверждает назначения, загрузку, результат и экономику."
/>
<div
ref={grid.ref}
className={cx('scroll-cards auto-cols-[calc(100vw-62px)] gap-[12px] sm:grid-still sm:grid-cols-3', grid.revealClass)}
>
{formats.map((format) => (
<article
key={format.area}
className="relative overflow-hidden rounded-[22px] border border-navy/[0.12] bg-white p-[24px]"
>
<span
aria-hidden="true"
className="absolute -top-[30px] -right-[30px] size-[110px] rounded-full bg-teal/[0.08]"
/>
<span className="mb-[9px] block text-[clamp(36px,5vw,52px)] leading-none font-[850] tracking-[-0.055em] text-navy">
{format.area}
</span>
<h3 className="text-[20px]">{format.title}</h3>
<p className="text-[14px] text-muted">{format.text}</p>
</article>
))}
</div>
<SmallNote className="mt-[16px]">
Финальная конфигурация определяется по техническому состоянию аппаратов, документации, площади, лицензии,
штату, потоку пациентов и текущей загрузке.
</SmallNote>
<h3 className="mt-[30px] mb-[38px] text-[clamp(24px,3vw,34px)] font-[850] tracking-[-0.03em] text-navy">
Пошаговый план модернизации кабинета за 8 недель
</h3>
<div
ref={steps.ref}
className={cx('scroll-cards auto-cols-[calc(100vw-62px)] gap-[14px] md:grid-still md:grid-cols-2', steps.revealClass)}
>
{timeline.map((step) => (
<article key={step.week} className="rounded-[20px] border border-teal/[0.16] bg-ice px-[20px] py-[22px]">
<span className="mb-[8px] block text-[12px] font-black tracking-[0.11em] text-teal-ink uppercase">
{step.week}
</span>
<strong className="mb-[10px] block text-[18px] leading-[1.2] text-navy">{step.title}</strong>
<span className="mb-[14px] block text-[13px] leading-[1.55] text-muted italic">{step.lead}</span>
<ul className="grid list-disc gap-[8px] pl-[18px] text-[13px] leading-[1.55] text-muted">
{step.items.map((item) => (
<li key={item.title}>
<strong className="inline text-[13px] text-navy">{item.title}</strong>
{'text' in item && item.text ? `: ${item.text}` : null}
</li>
))}
</ul>
</article>
))}
</div>
</Container>
</Section>
)
}