body {
    font-family: "Inter", sans-serif;
}

h1, h2, h3, h4 {
    font-family: "Playfair Display", serif;
}

ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

a {
    text-decoration: none;
}

/* Hero background image positioning */
.hero-bg-img {
    object-position: -150px center;
}

@media (min-width: 768px) {
    .hero-bg-img {
        object-position: center center;
    }
}

/* ------------------------------------------------------------------
   Accessibility
   ------------------------------------------------------------------ */

/* Skip link: visually hidden until it receives keyboard focus.
   Fixed (not absolute) so it is always at the top of the viewport, even when the page
   loaded scrolled to a hash like /#contact. */
.skip-link {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 100;
    padding: 0.75rem 1.25rem;
    background: #C9A84C;
    color: #1A2E4A;
    font-weight: 700;
    border-radius: 0 0 0.375rem 0;
    transform: translateY(-150%);
    transition: transform 0.15s ease;
}

.skip-link:focus {
    transform: translateY(0);
    outline: 3px solid #1A2E4A;
    outline-offset: -3px;
}

/* Visible keyboard focus indicator for all interactive elements.
   Navy on light backgrounds; light gold on navy backgrounds (nav, footer, navy sections). */
:focus-visible {
    outline: 3px solid #1A2E4A;
    outline-offset: 2px;
}

nav :focus-visible,
footer :focus-visible,
.bg-navy :focus-visible {
    outline-color: #F5D76E;
}

/* Targets that receive programmatic focus (skip link / in-page navigation) don't need a ring */
main:focus,
section[tabindex="-1"]:focus,
div[tabindex="-1"]:focus {
    outline: none;
}

/* Keep anchored sections and keyboard-focused controls from landing underneath the sticky
   header. scroll-padding-top on the scrollport covers every scroll-into-view, which includes
   fragment navigation, so it handles anchors and WCAG 2.2 SC 2.4.11 focus alike.

   There used to be a matching [id] { scroll-margin-top } rule here as well. The two stack --
   the margin is measured from the padding line, not from the viewport -- so every anchor on
   the site landed at twice the intended offset, leaving a header's worth of dead space above
   the heading the reader just jumped to. One mechanism is enough.

   --header-height is measured and set by site.js; the fallback covers the no-JS case. */
html {
    scroll-padding-top: calc(var(--header-height, 10rem) + 1rem);
}

/* Respect the user's reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        scroll-behavior: auto !important;
    }

    .btn-red-glass:hover,
    .btn-gold-glass:hover,
    .btn-white-glass:hover {
        transform: none;
    }
}

/* Server-side validation: highlight the erroring field itself, not just the message.
   Element-qualified selectors so they beat Tailwind's .border-gray-* utilities. */
input.input-validation-error,
select.input-validation-error,
textarea.input-validation-error {
    border-color: #b91c1c;
}

.field-validation-error {
    display: block;
    margin-top: 0.25rem;
}

/* Placeholder text at >= 4.5:1 on white (Tailwind's default #9CA3AF is 2.5:1) */
form input::placeholder,
form textarea::placeholder {
    color: #6B7280;
    opacity: 1;
}

/* ------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------ */

/* Red Glass CTA Button (all gradient stops keep white text at >= 4.8:1) */
.btn-red-glass {
    background: linear-gradient(135deg, #dc2626 0%, #b91c1c 40%, #c62828 60%, #dc2626 100%);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: 0 4px 15px rgba(220, 38, 38, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    transition: all 0.3s ease;
}

.btn-red-glass:hover {
    background: linear-gradient(135deg, #c62828 0%, #dc2626 40%, #b91c1c 60%, #c62828 100%);
    box-shadow: 0 6px 20px rgba(220, 38, 38, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4);
    color: #fff;
    transform: translateY(-1px);
}

/* Gold Glass CTA Button */
.btn-gold-glass {
    background: linear-gradient(135deg, #d4af37 0%, #c9a84c 30%, #e8c84a 60%, #f5d76e 100%);
    color: #1A2E4A;
    border: 1px solid rgba(255, 255, 255, 0.3);
    box-shadow: 0 4px 15px rgba(201, 168, 76, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.4);
    text-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);
    transition: all 0.3s ease;
}

.btn-gold-glass:hover {
    background: linear-gradient(135deg, #f5d76e 0%, #e8c84a 30%, #d4af37 60%, #c9a84c 100%);
    box-shadow: 0 6px 20px rgba(201, 168, 76, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    color: #1A2E4A;
    transform: translateY(-1px);
}

/* White Glass CTA Button */
.btn-white-glass {
    background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 40%, #ffffff 70%, #f8f8f8 100%);
    color: #1A2E4A;
    border: 1px solid rgba(255, 255, 255, 0.6);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);
    text-shadow: none;
    transition: all 0.3s ease;
}

.btn-white-glass:hover {
    background: linear-gradient(135deg, #f8f8f8 0%, #ffffff 40%, #f0f0f0 70%, #ffffff 100%);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9);
    color: #1A2E4A;
    transform: translateY(-1px);
}

/* ---------------------------------------------------------------------------
   Multi-step form wizard. Shared by the skip trace order form and the registered
   agent order form, so it lives here rather than being repeated per page.
   --------------------------------------------------------------------------- */
/* Wizard steps. Only the current one is in flow; the incoming step animates in from
   the side it came from, so moving forward and going back feel different. */
.wizard-step { display: none; }

.wizard-step.is-current {
    display: block;
    animation: wizardInForward .34s cubic-bezier(.2, .7, .2, 1) both;
}

.wizard-step.is-current.from-back {
    animation-name: wizardInBack;
}

@keyframes wizardInForward {
    from { opacity: 0; transform: translateX(20px); }
    to   { opacity: 1; transform: none; }
}

@keyframes wizardInBack {
    from { opacity: 0; transform: translateX(-20px); }
    to   { opacity: 1; transform: none; }
}

/* Height is animated by the script so the card grows and shrinks between steps
   instead of snapping. */
#wizard-steps { transition: height .34s cubic-bezier(.2, .7, .2, 1); overflow: hidden; }

/* A field the reader skipped gets marked rather than relying on the browser bubble,
   which only ever points at one control. */
.wizard-step .field-invalid {
    border-color: #b91c1c;
    box-shadow: 0 0 0 1px #b91c1c;
}

@media (prefers-reduced-motion: reduce) {
    .wizard-step.is-current { animation: none; }
    #wizard-steps { transition: none; }
}

/* Progress fill. A slow sheen travels along it so the bar reads as live rather than
   a static block, and the easing overshoots very slightly before settling, which
   gives each advance a bit of weight. The track clips the overshoot. */
.wizard-fill {
    background-image: linear-gradient(90deg, #8B6914 0%, #C9A84C 42%, #E6D08A 52%, #C9A84C 62%, #8B6914 100%);
    background-size: 220% 100%;
    box-shadow: 0 0 10px rgba(201, 168, 76, 0.55);
    transition: width .62s cubic-bezier(.22, 1.15, .36, 1);
    animation: wizardSheen 2.6s linear infinite;
}

@keyframes wizardSheen {
    from { background-position: 110% 0; }
    to   { background-position: -110% 0; }
}

/* One node per step, sitting on the track. They scale up and turn gold as the fill
   reaches them. */
.wizard-node {
    position: absolute;
    top: 50%;
    width: 14px;
    height: 14px;
    margin-left: -7px;
    margin-top: -7px;
    border-radius: 9999px;
    background: #d1d5db;
    border: 2px solid #F8F8F6;
    transform: scale(.72);
    transition: transform .45s cubic-bezier(.34, 1.56, .64, 1), background-color .3s ease;
}

.wizard-node.is-done {
    background: #C9A84C;
    transform: scale(1);
}

/* Ripple fired the moment a node is reached. */
.wizard-node.just-done::after {
    content: "";
    position: absolute;
    inset: -3px;
    border-radius: 9999px;
    border: 2px solid #C9A84C;
    animation: wizardPing .65s ease-out forwards;
}

@keyframes wizardPing {
    from { transform: scale(.7); opacity: .85; }
    to   { transform: scale(2.4); opacity: 0; }
}

/* The step title lifts in when it changes, so the label reads as part of the move. */
.wizard-title { display: inline-block; }
.wizard-title.is-swapping { animation: wizardTitleIn .38s cubic-bezier(.2, .7, .2, 1) both; }

@keyframes wizardTitleIn {
    from { opacity: 0; transform: translateY(5px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .wizard-fill { animation: none; transition: none; }
    .wizard-node { transition: none; }
    .wizard-node.just-done::after { animation: none; content: none; }
    .wizard-title.is-swapping { animation: none; }
}

/* ---------------------------------------------------------------------------
   Order confirmation. The tick draws itself and the three steps arrive after it,
   so the panel resolves in front of the reader rather than simply appearing --
   which is what makes it read as "received" rather than as a static notice.
   --------------------------------------------------------------------------- */
.confirm-check {
    width: 78px;
    height: 78px;
}

.confirm-check-circle {
    stroke: #C9A84C;
    stroke-width: 2.5;
    stroke-dasharray: 151;
    stroke-dashoffset: 151;
    animation: confirmCircle .6s cubic-bezier(.65, 0, .45, 1) forwards;
}

.confirm-check-mark {
    stroke: #C9A84C;
    stroke-width: 4;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 36;
    stroke-dashoffset: 36;
    animation: confirmMark .34s cubic-bezier(.65, 0, .45, 1) .5s forwards;
}

@keyframes confirmCircle { to { stroke-dashoffset: 0; } }
@keyframes confirmMark { to { stroke-dashoffset: 0; } }

/* Staggered so they read top to bottom as a sequence. */
.confirm-step {
    opacity: 0;
    transform: translateY(8px);
    animation: confirmStepIn .45s cubic-bezier(.2, .7, .2, 1) forwards;
}

.confirm-step:nth-child(1) { animation-delay: .75s; }
.confirm-step:nth-child(2) { animation-delay: .88s; }
.confirm-step:nth-child(3) { animation-delay: 1.01s; }

@keyframes confirmStepIn {
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .confirm-check-circle,
    .confirm-check-mark { animation: none; stroke-dashoffset: 0; }
    .confirm-step { animation: none; opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------------------
   Registered agent chooser (/registered-agents)
   --------------------------------------------------------------------------- */

/* The entrance and everything afterwards live on different elements on purpose.
   An animation with fill-mode: both keeps its final transform at a higher
   priority than any rule the same element sets, so a hover transform here would
   simply be ignored once the entrance finished. The shell deals itself in; the
   card inside owns every later movement. */
.agent-card-shell {
    /* The per-card offsets in the markup describe the three-column grid, where every
       card starts pulled in towards the middle. One and two columns put those same
       cards somewhere else, and an offset meant to point inwards then points off the
       side of the screen instead — which showed up as the page scrolling sideways
       for the length of the animation on a phone. These two factors scale the spread
       back to what the current layout can hold. */
    --spread: 0;
    --twist: .3;
    animation: agentDeal .72s cubic-bezier(.22, 1.08, .36, 1) both;
    animation-delay: calc(var(--i, 0) * .085s);
}

@media (min-width: 1024px) {
    .agent-card-shell { --spread: 1; --twist: 1; }
}

/* Cards arrive from a loose stack rather than fading in place: each one starts
   pulled toward the middle of the grid and tilted, then settles into the slot it
   already occupies in the layout. Because the end state is "no transform", the
   browser does the positioning and nothing has to be measured. */
@keyframes agentDeal {
    from {
        opacity: 0;
        transform: translate3d(calc(var(--dx, 0rem) * var(--spread)), var(--dy, 2.5rem), 0)
                   rotate(calc(var(--rot, 0deg) * var(--twist)))
                   scale(.84);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Every movement is composed from custom properties so that the hover lift, the
   pointer tilt and the dimmed state can never overwrite each other. */
.agent-card {
    position: relative;
    overflow: hidden;
    transform: translateY(var(--lift, 0)) scale(var(--sc, 1))
               perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
    transition: transform .2s ease-out,
                box-shadow .35s ease,
                border-color .35s ease,
                opacity .35s ease;
}

.agent-card:hover,
.agent-card:focus-visible {
    --lift: -6px;
    border-color: #C9A84C;
    box-shadow: 0 18px 38px -14px rgba(26, 46, 74, .45);
}

/* A soft light follows the pointer across the card. The position comes from the
   script; with no script the properties keep their defaults and the layer sits
   at zero opacity, so nothing shows. */
.agent-card::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity .3s ease;
    background: radial-gradient(18rem circle at var(--mx, 50%) var(--my, 50%),
                                rgba(201, 168, 76, .16), transparent 60%);
}

.agent-card:hover::before {
    opacity: 1;
}

/* And a single highlight crosses the card once per hover, so it reads as
   catching the light rather than as something looping. */
.agent-card::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    background: linear-gradient(105deg, transparent 38%, rgba(201, 168, 76, .20) 50%, transparent 62%);
}

.agent-card:hover::after,
.agent-card:focus-visible::after {
    animation: agentSheen .85s ease-out;
}

@keyframes agentSheen {
    from { opacity: 1; transform: translateX(-130%); }
    to   { opacity: 1; transform: translateX(130%); }
}

.agent-go {
    transition: transform .3s cubic-bezier(.2, .7, .2, 1), background-color .3s ease, color .3s ease;
}

.agent-card:hover .agent-go,
.agent-card:focus-visible .agent-go {
    transform: translateX(4px);
    background-color: #C9A84C;
    color: #1A2E4A;
}

/* Chosen card. One keyframe set drives the lift and the ring together so the two
   cannot fight over `transform`, and the ring is a box-shadow rather than a
   scaled pseudo-element because the card clips its own children. */
.agent-card.is-selecting {
    border-color: #C9A84C;
    z-index: 2;
    animation: agentSelect .5s cubic-bezier(.2, .7, .2, 1) forwards;
}

@keyframes agentSelect {
    0% {
        transform: translateY(-6px) scale(1);
        box-shadow: 0 0 0 0 rgba(201, 168, 76, .6), 0 18px 38px -14px rgba(26, 46, 74, .45);
    }
    45% { transform: translateY(-10px) scale(1.045); }
    100% {
        transform: translateY(-8px) scale(1.03);
        box-shadow: 0 0 0 18px rgba(201, 168, 76, 0), 0 20px 44px -12px rgba(26, 46, 74, .5);
    }
}

/* The others step back so the choice reads as made. pointer-events stops a stray
   hover pulling one of them forward again mid-animation. */
.agent-card.is-dimmed {
    --sc: .97;
    opacity: .3;
    pointer-events: none;
}

.agent-check {
    position: absolute;
    top: .75rem;
    right: .75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 1.75rem;
    width: 1.75rem;
    border-radius: 9999px;
    background-color: #C9A84C;
    color: #1A2E4A;
    font-size: .7rem;
    opacity: 0;
    transform: scale(.4);
}

.agent-card.is-selecting .agent-check {
    animation: agentCheckIn .3s cubic-bezier(.2, .9, .3, 1.4) .08s forwards;
}

@keyframes agentCheckIn {
    to { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .agent-card-shell { animation: none; }
    .agent-card { transition: none; }
    .agent-card::before,
    .agent-card::after { display: none; }
    .agent-card:hover,
    .agent-card:focus-visible { --lift: 0; }
    .agent-card.is-selecting { animation: none; }
    .agent-check { display: none; }
}

/* ---------------------------------------------------------------------------
   Sticky call to action
   --------------------------------------------------------------------------- */

/* Clarity's scroll maps are what this exists for: half the homepage audience is
   gone by 15% of the page, and only about one in six ever reaches the contact
   form at the bottom. The bar keeps one way to order and one way to call within
   reach the whole way down, rather than asking people to scroll back. */
.sticky-cta {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 40;
    background-color: #1A2E4A;
    border-top: 1px solid rgba(201, 168, 76, .45);
    box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, .45);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    transform: translateY(110%);
    transition: transform .35s cubic-bezier(.2, .7, .2, 1);
}

.sticky-cta.is-visible {
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .sticky-cta { transition: none; }
}

/* The hero's background photograph is decoration, and it was absorbing more
   clicks than the "Get a Free Quote" button beside it. It should never be the
   thing a click lands on. */
.hero-bg-img {
    pointer-events: none;
}

/* Company tiles that start an order. They look like the plain tiles they
   replaced until you point at one, so the grid still reads as a list rather
   than a row of buttons. */
.company-pick {
    width: 100%;
    cursor: pointer;
    transition: background-color .25s ease, box-shadow .25s ease, transform .25s ease;
}

.company-pick:hover,
.company-pick:focus-visible {
    background-color: rgba(201, 168, 76, .16);
    box-shadow: 0 8px 18px -10px rgba(26, 46, 74, .5);
    transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
    .company-pick { transition: none; }
    .company-pick:hover,
    .company-pick:focus-visible { transform: none; }
}

/* A fixed bar has no business on paper. */
@media print {
    .sticky-cta { display: none; }
}

/*
    The FAQ filter box carries its own clear button, which stays put and is reachable by
    keyboard. WebKit draws a second one inside type="search" on top of it; suppress that
    rather than lose the semantics of a real search field.
*/
[data-faq-search-input]::-webkit-search-cancel-button,
[data-faq-search-input]::-webkit-search-decoration {
    -webkit-appearance: none;
    appearance: none;
}
