/* * Header and mobile tab bar, on the schoolcompare brand. * * The header is a white card band closed by a single hairline rule — no * shadow. The guideline's chrome is quiet; the colour on this surface is the * wordmark and the one active nav item, and nothing else. */ .header { position: sticky; top: 0; z-index: 1000; background: var(--bg-card); border-bottom: 1px solid var(--border); } .container { max-width: 1400px; margin: 0 auto; /* Add the safe-area inset to horizontal padding so the header content clears the notch in landscape on iPhones. */ padding-inline: max(1.5rem, env(safe-area-inset-left)) max(1.5rem, env(safe-area-inset-right)); display: flex; justify-content: space-between; align-items: center; gap: 2rem; /* 64px desktop / 56px mobile is load-bearing: the school detail page's sticky section nav docks against it with a hardcoded `top` in SchoolDetailShell.module.css and schoolSections.module.css, and the scroll-spy observers use it as a rootMargin. Change it in all four places or not at all. */ height: 64px; } .logo { display: flex; align-items: center; gap: 0.625rem; /* Padded hit area so the logo link is ≥44×44 on touch */ margin: -0.375rem -0.5rem; padding: 0.375rem 0.5rem; text-decoration: none; color: var(--text-primary); transition: color var(--transition); -webkit-tap-highlight-color: transparent; } .logo:hover { color: var(--brand-stronger); } /* * The mark colours itself — pin in var(--brand), leaf knocked out in * var(--bg-card), which is this header's own ground. It takes no colour from * currentColor, so there is deliberately no `color` here. */ .logoIcon { display: inline-flex; flex: 0 0 auto; width: 36px; height: 36px; } .logoIcon svg { width: 100%; height: 100%; } /* * "schoolcompare", one word, lower case. Manrope 700 pulled tight — the * negative tracking is what makes the two halves read as a single word rather * than as two. */ .logoText { font-family: var(--font-display); font-size: var(--step-2); font-weight: 700; line-height: 1; letter-spacing: -0.035em; } .logoTextAccent { color: var(--brand); } .logo:hover .logoTextAccent { color: var(--brand-stronger); } .nav { display: flex; align-items: center; gap: 0.125rem; } .navLink { position: relative; display: flex; align-items: center; gap: 0.4375rem; padding: 0.5rem 0.875rem; font-family: var(--font-ui); font-size: var(--step--1); font-weight: 500; color: var(--text-secondary); text-decoration: none; border-radius: var(--radius-md); transition: color var(--transition), background-color var(--transition); } .navLink:hover { color: var(--text-primary); background: var(--bg-secondary); } /* The current page is marked twice — brand hue and a heavier weight — so the state never rests on colour alone. */ .navLink.active { color: var(--brand); background: var(--brand-bg); font-weight: 600; } .badge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.125rem; height: 1.125rem; padding: 0 0.375rem; font-family: var(--font-ui); font-size: var(--step--2); font-weight: 700; line-height: 1; color: var(--brand-on); background: var(--brand); border-radius: 9999px; animation: badgePop 0.3s ease-out; } @keyframes badgePop { 0% { transform: scale(0.6); opacity: 0; } 50% { transform: scale(1.2); } 100% { transform: scale(1); opacity: 1; } } /* ─── Bottom tab bar (mobile only) ──────────────────────────────── */ .bottomBar { display: none; } .tab { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.25rem; flex: 1; min-height: 56px; padding: 0.375rem 0.25rem; font-family: var(--font-ui); color: var(--text-secondary); text-decoration: none; font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.01em; transition: color var(--transition); -webkit-tap-highlight-color: transparent; } .tabActive { color: var(--brand); font-weight: 600; } .tabIconWrap { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; } /* The active tab's icon sits in a brand-tinted stadium — the same rounded geometry as the rest of the system, and a second cue beyond hue. Drawn as a pseudo-element so it can bleed wider than the 24px icon box without moving the badge's anchor. */ .tabIconWrap::before { content: ''; position: absolute; inset: -2px -9px; border-radius: var(--radius-xl); background: transparent; transition: background-color var(--transition); } .tabActive .tabIconWrap::before { background: var(--brand-bg); } .tab:active .tabIconWrap::before { background: var(--brand-bg); } .tabIcon { position: relative; width: 22px; height: 22px; } .tabLabel { line-height: 1; } .tabBadge { position: absolute; top: -7px; right: -7px; display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; font-family: var(--font-ui); font-size: 0.6875rem; font-weight: 700; line-height: 1; color: var(--brand-on); background: var(--brand); border: 2px solid var(--bg-card); border-radius: 9999px; animation: badgePop 0.3s ease-out; } @media (max-width: 640px) { .container { padding: 0 1rem; height: 56px; } /* Hide the top text nav; the bottom bar takes over */ .nav { display: none; } .logoIcon { width: 34px; height: 34px; } .logoText { font-size: var(--step-1); } .bottomBar { display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000; background: var(--bg-card); border-top: 1px solid var(--border); box-shadow: 0 -2px 12px rgba(var(--shadow-rgb), 0.06); /* Respect iPhone home-indicator (bottom) and notch (left/right in landscape) insets so the tab content never sits under system UI. */ padding-bottom: env(safe-area-inset-bottom, 0); padding-inline: env(safe-area-inset-left, 0) env(safe-area-inset-right, 0); /* Compensate for iOS Chrome's auto-hiding URL bar — Navigation.tsx writes the offset based on the Visual Viewport API. translate3d (instead of translateY) forces hardware compositing so the bar doesn't lag/flicker during the toolbar animation. */ transform: translate3d(0, var(--mobile-bar-offset, 0px), 0); } }