2026-05-12 01:04:17 -05:00
|
|
|
@tailwind base;
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
|
|
|
|
|
|
|
|
|
* {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html {
|
|
|
|
|
scroll-behavior: smooth;
|
2026-05-27 12:42:25 -05:00
|
|
|
overflow-x: hidden;
|
2026-05-12 01:04:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
font-family: 'Inter', sans-serif;
|
|
|
|
|
color: #0F172A;
|
|
|
|
|
background-color: #F8FAFC;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
-webkit-font-smoothing: antialiased;
|
2026-05-27 12:42:25 -05:00
|
|
|
overflow-x: hidden;
|
2026-05-12 01:04:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 19:40:18 -05:00
|
|
|
.brand-logo-on-dark {
|
|
|
|
|
filter:
|
|
|
|
|
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.6))
|
|
|
|
|
drop-shadow(1px 0 0 rgba(255, 255, 255, 0.45));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 01:04:17 -05:00
|
|
|
a {
|
|
|
|
|
color: #0EA5E9;
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 00:29:45 -05:00
|
|
|
/* Container - custom max-width */
|
2026-05-12 01:04:17 -05:00
|
|
|
.container {
|
2026-05-12 02:26:18 -05:00
|
|
|
max-width: 1280px;
|
2026-05-12 01:04:17 -05:00
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Section spacing - mobile first */
|
|
|
|
|
.section {
|
|
|
|
|
padding: 4rem 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Desktop section spacing */
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.section {
|
|
|
|
|
padding: 6rem 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hero section styling */
|
|
|
|
|
.hero {
|
|
|
|
|
min-height: 70vh;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
background: linear-gradient(135deg, #0B2A3C 0%, #071A2A 100%);
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 4rem 0 5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Light section background */
|
|
|
|
|
.section-alt {
|
|
|
|
|
background: #EEF6FB;
|
|
|
|
|
}
|
fix(a11y): visible focus indicator, aria-expanded, and six Label-in-Name failures
Batch 10, and only three of its six issues were real. The other three are
answered on the tracker with the arithmetic.
#189 — focus indicator. Footer and Home carried zero focus styling across 17
links. Added one global :focus-visible rule rather than sprinkling classes,
because the issue's own scope was "all interactive <a> and <Link> elements".
Not the ring the issue asked for. It suggested ring-primary-cyan; #22D3EE is
6.26:1 on navy and 2.38:1 on white, so on this light-first design that ring
would have failed WCAG 1.4.11 across most of the site. Two rings instead — white
inside, navy outside — so the white carries the dark bands and the navy carries
the light sections. Worst case across every background in the palette is
13.62:1. box-shadow so both rings follow each element's own border-radius, plus
a transparent outline for forced-colors mode.
#192 — aria-expanded={isOpen} and aria-controls on the mobile nav trigger, and
the id on SheetContent it now points at. Confirmed as "false" in the
prerendered HTML.
#193 — INVERTED. The issue asked for aria-labels to be added consistently to
hero CTAs. The aria-labels already there were WCAG 2.5.3 Label in Name failures,
Level A: "Schedule a consultation" is not a superset of the visible "Schedule
Consultation", so a voice-control user saying what they see cannot activate the
link. Doing what the issue asked would have spread a Level A failure. Removed
the six that broke it; kept the eight that genuinely add context and do contain
their visible text, including the icon-only header logo link that needs one.
Audit now reports 0 failures across Home, Services, Footer and Header.
Verified: npm run build; the ring is in the built CSS, aria-expanded="false" and
id="mobile-nav-content" are in the prerendered HTML, all six removed labels are
absent from it, and the visible CTA text still renders.
NOT verified: nothing has been walked keyboard-only in a browser, and no screen
reader has been used. That gap is recorded in docs/qa/ClaudeQACoverage.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 02:31:13 -05:00
|
|
|
|
|
|
|
|
@layer base {
|
|
|
|
|
/*
|
|
|
|
|
* Visible keyboard focus — WCAG 2.4.7 (Focus Visible) and 1.4.11 (Non-text
|
|
|
|
|
* Contrast, which wants 3:1 for the indicator itself).
|
|
|
|
|
*
|
|
|
|
|
* Two rings, not one, and that is the whole point. This is a light-first
|
|
|
|
|
* design with dark navy bands through it, so no single colour clears 3:1
|
|
|
|
|
* everywhere: primary-cyan manages 6.26:1 on navy and only 2.38:1 on white,
|
|
|
|
|
* which is why the obvious `ring-primary-cyan` was not used. The white ring
|
|
|
|
|
* carries the dark sections and the navy ring carries the light ones, and on
|
|
|
|
|
* every background in the palette at least one of them clears 3:1 by a wide
|
|
|
|
|
* margin — the worst case is 13.62:1.
|
|
|
|
|
*
|
|
|
|
|
* box-shadow rather than outline so both rings stack predictably and follow
|
|
|
|
|
* each element's own border-radius. The transparent outline is the standard
|
|
|
|
|
* escape hatch for Windows High Contrast / forced-colors mode, where
|
|
|
|
|
* box-shadow is discarded and an outline is not.
|
|
|
|
|
*
|
|
|
|
|
* :focus-visible, not :focus, so a mouse click does not leave a ring behind.
|
|
|
|
|
* :where() keeps specificity at zero, so any component can still override it.
|
|
|
|
|
*/
|
|
|
|
|
:where(a, button, [role="button"], summary, input, select, textarea):focus-visible {
|
|
|
|
|
box-shadow: 0 0 0 2px #FFFFFF, 0 0 0 5px #0B2A3C;
|
|
|
|
|
outline: 2px solid transparent;
|
|
|
|
|
outline-offset: 2px;
|
|
|
|
|
}
|
|
|
|
|
}
|