.homeView { width: 100%; } /* ── Page rhythm ─────────────────────────────────────────────────────────── The landing page owns the vertical gap between its bands; no band sets its own top or bottom margin. Previously each one did, and the gaps came out as 24 / 32 / 24 / 16 / 48 / 32 / 16px — no scale, which is most of the reason a designed page read as a stack of unrelated strips. If you add a band here, give it padding and a ground, never a margin. */ .landing { display: flex; flex-direction: column; gap: var(--section-gap); } /* The hero and the four reasons under it are one thought, so they sit closer to each other than to the next band. This is the only place that overrides the page gap, and it does it by grouping rather than by re-spacing. */ .heroGroup { display: flex; flex-direction: column; gap: 2rem; } /* ── Section header ──────────────────────────────────────────────────────── One pattern for every band: kicker, heading, optional aside. */ .sectionHead { display: grid; grid-template-columns: 1fr auto; align-items: baseline; column-gap: 1.5rem; margin-bottom: var(--section-head-gap); } .sectionKicker { grid-column: 1 / -1; font-size: var(--step--2); font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--brand-strong); margin: 0 0 0.35rem; } .sectionHeading { font-family: var(--font-display); font-size: var(--step-3); font-weight: 700; line-height: 1.2; letter-spacing: -0.02em; color: var(--text-primary); margin: 0; max-width: 24ch; text-wrap: balance; } .sectionAside { font-size: var(--step--1); color: var(--text-muted); margin: 0; text-align: right; } @media (max-width: 768px) { .sectionHead { grid-template-columns: 1fr; } .sectionHeading { font-size: var(--step-2); max-width: none; } .sectionAside { text-align: left; margin-top: 0.4rem; } } /* ── Hero ────────────────────────────────────────────────────────────────── A Sand panel: proposition and search on the left, the brand landscape bleeding to the panel's right edge. The illustration is decorative, so on phones it drops to a short band under the content rather than competing with the search for the fold. */ .heroPanel { display: grid; grid-template-columns: 1.12fr 0.88fr; align-items: stretch; background: var(--bg-secondary); border-radius: var(--radius-xl); overflow: hidden; } .heroContent { align-self: center; padding: 3rem 1.5rem 3rem 3rem; min-width: 0; } .heroArt { position: relative; min-width: 0; /* The scene needs height to read; without a floor it collapses to the content column's height on short viewports. */ min-height: 24rem; } /* Both compositions fill the slot; the breakpoint below picks one. */ .heroArtWide, .heroArtBand { position: absolute; inset: 0; } .heroArt svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; } /* The band composition only exists below the one-column breakpoint — above it the slot is tall and the wide drawing is the right picture. */ .heroArtBand { display: none; } .heroEyebrow { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--brand); background: rgba(var(--brand-rgb), 0.1); padding: 0.3rem 0.7rem; border-radius: 999px; margin-bottom: 1rem; } .heroEyebrowDot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--brand); } .heroTitle { font-size: var(--step-4); font-weight: 700; color: var(--text-primary); margin-bottom: 0.75rem; line-height: 1.12; letter-spacing: -0.03em; font-family: var(--font-display); max-width: 18ch; } /* The em is a colour change, not an emphasis change — Manrope has no italic in the loaded weights, so leaving font-style alone would synthesise a slant. */ .heroEmph { color: var(--brand); font-style: normal; } .heroDescription { font-size: var(--step-1); color: var(--text-secondary); margin: 0 0 1.5rem; max-width: 46ch; line-height: 1.55; } .heroDescription strong { color: var(--text-primary); font-weight: 600; } @media (max-width: 1024px) { .heroContent { padding: 2.25rem 1.5rem 2.25rem 2rem; } .heroArt { min-height: 20rem; } } /* One column: content first, then a short band of landscape. */ @media (max-width: 860px) { .heroPanel { grid-template-columns: 1fr; } .heroContent { padding: 2rem 1.5rem 1.75rem; } /* One column: the slot becomes a wide, short band, so swap in the drawing composed for that crop. See the note at the top of Illustration.tsx. */ .heroArt { min-height: 11rem; order: 2; } .heroArtWide { display: none; } .heroArtBand { display: block; } .heroTitle { font-size: var(--step-3); max-width: none; } .heroDescription { font-size: var(--step-0); max-width: none; } } /* Above the fold on phones, every line costs. Drop the eyebrow tag and the long coverage sentence, leaving the proposition and the search — the data provenance is carried by the "Official & trusted" prop immediately below the hero (audit P1.7, feeds the 46% home exit rate). */ @media (max-width: 640px) { .heroPanel { border-radius: var(--radius-lg); } .heroContent { padding: 1.25rem 1.1rem 1.5rem; } .heroEyebrow, .heroDescriptionFull { display: none; } .heroDescription { font-size: 0.9rem; line-height: 1.45; margin-bottom: 1rem; } .heroTitle { font-size: 1.75rem; margin-bottom: 0.5rem; } .heroArt { min-height: 8.5rem; } } /* ── Value props ─────────────────────────────────────────────────────────── Four reasons, straight from the guideline. The fourth is ours rather than theirs — see the comment on VALUE_PROPS in HomeView.tsx. */ .valueProps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; list-style: none; padding: 0; margin: 0; } .valueProp { display: flex; gap: 0.8rem; align-items: flex-start; } .propIcon { display: flex; align-items: center; justify-content: center; flex: 0 0 2.6rem; width: 2.6rem; height: 2.6rem; border-radius: var(--radius-md); } .propIconTrust { background: var(--brand-bg); color: var(--brand); } .propIconCompare { background: rgba(var(--sage-rgb), 0.42); color: var(--status-above); } .propIconContext { background: rgba(242, 201, 76, 0.26); color: var(--mustard-ink); } .propIconDeadline { background: rgba(221, 216, 245, 0.6); color: var(--phase-nursery); } .propTitle { font-family: var(--font-display); font-size: var(--step-0); font-weight: 700; color: var(--text-primary); margin: 0 0 0.15rem; letter-spacing: -0.01em; } .propBody { font-size: var(--step--1); color: var(--text-muted); line-height: 1.5; margin: 0; } @media (max-width: 900px) { .valueProps { grid-template-columns: repeat(2, 1fr); gap: 1rem 1.25rem; } } @media (max-width: 560px) { .valueProps { grid-template-columns: 1fr; gap: 0.85rem; } } /* View Toggle */ .viewToggle { display: flex; gap: 0.2rem; background: var(--bg-secondary); padding: 0.2rem; border-radius: 6px; } .viewToggleBtn { display: flex; align-items: center; gap: 0.25rem; padding: 0.375rem 0.625rem; font-size: 0.8125rem; font-weight: 500; background: transparent; border: none; border-radius: 5px; cursor: pointer; color: var(--text-secondary); transition: all 0.2s ease; } .viewToggleBtn:hover { color: var(--text-primary); } .viewToggleBtn.active { background: var(--bg-card); color: var(--brand-strong); box-shadow: 0 2px 4px rgba(var(--shadow-rgb), 0.08); } .viewToggleBtn svg { flex-shrink: 0; } .results { margin-top: 1rem; } .mapViewResults { margin-top: 0; } /* Map View Layout */ .mapViewContainer { display: grid; grid-template-columns: 1fr 340px; gap: 1rem; height: calc(100dvh - 280px); min-height: 520px; max-height: 800px; } .mapContainer { border-radius: 10px; overflow: hidden; border: 1px solid var(--border); height: 100%; } .compactList { display: flex; flex-direction: column; gap: 0.5rem; overflow-y: auto; height: 100%; padding-right: 0.375rem; } .compactList::-webkit-scrollbar { width: 6px; } .compactList::-webkit-scrollbar-track { background: var(--bg-secondary); border-radius: 3px; } .compactList::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .compactList::-webkit-scrollbar-thumb:hover { background: var(--text-muted); } /* Compact School Item */ .compactItem { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 0.625rem 0.75rem; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; transition: all 0.2s ease; } .compactItem:hover { border-color: var(--brand); box-shadow: 0 2px 6px rgba(var(--shadow-rgb), 0.05); } .compactItemContent { flex: 1; min-width: 0; } .compactItemHeader { display: flex; align-items: center; gap: 0.375rem; margin-bottom: 0.125rem; } .compactItemName { font-weight: 600; font-size: 0.8125rem; color: var(--text-primary); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .compactItemName:hover { color: var(--brand-strong); } .distanceBadge { flex-shrink: 0; padding: 0.0625rem 0.375rem; font-size: 0.6875rem; font-weight: 600; background: var(--brand); color: var(--brand-on); border-radius: 3px; } .compactItemMeta { display: flex; gap: 0.375rem; font-size: 0.6875rem; color: var(--text-secondary); margin-bottom: 0.25rem; } .compactItemMeta span:not(:last-child)::after { content: '·'; margin-left: 0.375rem; color: var(--text-muted); } .compactItemStats { display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--text-secondary); } .compactStat strong { color: var(--text-primary); } .compactItemActions { display: flex; flex-direction: column; gap: 0.25rem; flex-shrink: 0; } .sectionHeader { margin-bottom: 1rem; } .sectionHeader h2 { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.5rem; color: var(--text-primary); font-family: var(--font-display); } /* Decorative coral bar under section headings */ .sectionHeader h2::after { content: ''; display: block; width: 50px; height: 2px; background: var(--brand); border-radius: 1px; margin-top: 0.5rem; } .sectionDescription { font-size: 0.9375rem; color: var(--text-secondary); margin: 0; line-height: 1.5; } .schoolList { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.25rem; } /* Staggered fade-in for rows */ .schoolList > *:nth-child(1) { animation-delay: 0ms; } .schoolList > *:nth-child(2) { animation-delay: 30ms; } .schoolList > *:nth-child(3) { animation-delay: 60ms; } .schoolList > *:nth-child(4) { animation-delay: 90ms; } .schoolList > *:nth-child(5) { animation-delay: 120ms; } .schoolList > *:nth-child(6) { animation-delay: 150ms; } .schoolList > *:nth-child(7) { animation-delay: 180ms; } .schoolList > *:nth-child(8) { animation-delay: 210ms; } .schoolList > *:nth-child(9) { animation-delay: 240ms; } .schoolList > *:nth-child(n+10) { animation-delay: 270ms; } .emptyState { text-align: center; padding: 2.5rem 1.5rem; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; } .emptyStateTitle { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); margin-bottom: 0.375rem; font-family: var(--font-display); } .emptyStateDescription { font-size: 0.9375rem; color: var(--text-secondary); max-width: 380px; margin: 0 auto; } @media (max-width: 768px) { .resultsHeader { flex-direction: column; align-items: flex-start; } .resultsHeaderActions { width: 100%; justify-content: space-between; } .viewToggle { justify-content: center; flex-shrink: 0; } /* The sort