/* RESONANT PAINTING CO. // CORE STYLES v2026.3 */

:root {
    /* Brand Tokens */
    --accent: #29475E;
    --accent-glow: #64ffda;

    /* Light Theme (Public Site) */
    --bg: #ffffff;
    --card-bg: #fdfdfd;
    --border: #e6e6e6;
    --text-main: #1a1a1a;
    --text-muted: #666666;

    /* Dark Theme (Artisan Desk) */
    --bg-dark: #05070a;
    --card-dark: #0a111a;
    --border-dark: #1a2333;
    --text-bright: #e6edf3;
    --text-dim: #a8b2d1;

    --placeholder-bg: #808080;

    /* Motion — Board 18 §01 MOTION is the spec. Read by the MOTION block at the end of
       this file and by js/motion.js; nowhere else may carry these numbers. */
    --motion-ease-settle: cubic-bezier(0.61, 1, 0.88, 1);
    --motion-rise-distance: 48px;
    --motion-rise-duration: 1200ms;
    --motion-rise-stagger: 150ms;
    --motion-rise-threshold: 0.3;
    --motion-drift-rate: 0.2;
}

body {
    background-color: var(--bg);
    color: var(--text-main);
    font-family: 'Crimson Pro', serif;
    margin: 0;
    -webkit-font-smoothing: antialiased;
}

.container { padding: 0 10%; }
.grid-2-even { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; }
.grid-2-hero { display: grid; grid-template-columns: 1.4fr 0.6fr; gap: 80px; }
.grid-3-even { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 60px; align-items: stretch; }
.content-limit { max-width: 650px; }
.align-center { align-items: center; }
.align-end { align-items: flex-end; }
.py-xlarge { padding-top: 140px; padding-bottom: 140px; }
.pt-large { padding-top: 100px; }
.pt-medium { padding-top: 60px; }
.mb-small { margin-bottom: 1.5rem; }
.mb-large { margin-bottom: 3.5rem; }
.section-border { border-top: 1px solid var(--border); }
.bg-alt { background-color: #fafafa; }

/* PILLAR ALIGNMENT */
.grid-3-even .pillar {
    display: grid;
    grid-template-rows: auto auto 1fr;
    gap: 0;
}

.grid-3-even .pillar h3 {
    min-height: 3.5rem;
}

/* HERO ARCHITECTURE */
/* The home hero is a centred flex box holding ONE child. `.hero-stack` is that child now,
   so the logo and the CTA under it centre as a unit instead of the button becoming a
   second flex item beside the logo. */
.hero-stack {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 48px;
}

.hero-bg {
    height: 100vh;
    position: relative;
    isolation: isolate;
    display: flex;
    justify-content: center;
    align-items: center;
}

.logo-wrapper {
    /* Doubled the size for that premium landing feel */
    width: 560px;
    max-width: 80vw; /* Ensures it doesn't break on small screens */
    opacity: 1;
}

.logo-white {
    width: 100%;
    height: auto;
    display: block;
}

/* PROSE STYLES */
.hero-title { font-size: clamp(2.5rem, 6vw, 3.8rem); font-weight: 400; line-height: 1.1; margin: 0 0 2rem 0; }
.hero-subtitle { font-family: 'Inter', sans-serif; font-weight: 300; max-width: 650px; font-size: 1.15rem; line-height: 1.7; color: var(--text-muted); }

.photo-placeholder-square {
    width: 100%;
    aspect-ratio: 1 / 1;
    /*background-color: var(--placeholder-bg);*/
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.photo-placeholder-square img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.hero-detail-photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center top;
    display: block;
}

.placeholder-label { color: #fff; font-family: 'Inter', sans-serif; font-size: 0.85rem; opacity: 0.8; text-align: center; padding: 20px; }

/* ── THE HERO-TO-CONTENT BREATH ────────────────────────────────────────────────────
   The section directly after a hero gets LESS top padding than a section between two
   others. `.py-xlarge`'s 140px is a divider between two blocks of content and reads as
   too much against a full-bleed photo, which has already given the eye its pause.

   On About and The Work it reads heavier still: measured, the photo's edge is 140px from
   the `/ ABOUT` label but 179px from the H1, because a small quiet label sits in the gap
   and does not stop the eye. Home is a flat 140px to its H1.

   100px is `.pt-large`'s value, not a new number — the spacing scale here is 140/100/60
   and a one-off would make this a fourth step. The adjacent-sibling combinator is what
   keeps it contained: it can only ever reach a section that directly follows a hero, so
   no other `.py-xlarge` on the site moves. */
.hero-bg + section,
.about-hero-bg + section,
.craft-hero-bg + section {
    padding-top: 100px;
}

/* ── HERO PHOTOS ARE <img>, NOT CSS BACKGROUNDS ─────────────────────────────────────
   A background image is requested only after the stylesheet has downloaded AND been
   applied, so the LCP element of every page started late — and on The Work it finished
   LAST, behind 18 MB of below-the-fold photos that the HTML had already announced
   (measured 2026-09-17, slow 4G: 108 s). An <img> in the markup is found by the preload
   scanner, carries fetchpriority="high", and can offer AVIF/WebP through <picture>.

   The photo sits absolutely behind its band's content. `isolation: isolate` on each band
   keeps the negative z-indexes inside the band instead of dropping them behind the page.
   The bands keep their own heights; the photo only fills them. */
/* <picture> only chooses a source; it should not be a box. Without this it becomes a flex
   item in the hero, a grid item in the process steps, and an inline wrapper everywhere else
   — every <img> that used to sit directly in its layout would sit one level deeper. With
   it, each <img> lays out exactly as it did before <picture> arrived. */
picture {
    display: contents;
}

.hero-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    z-index: -2;
    border-radius: 0;
}

/* The home hero's 20% darkening, formerly the first layer of its background-image. It
   sits between the photo and the logo. */
.hero-bg::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.2);
    z-index: -1;
}

/* ABOUT PAGE */
.about-hero-placeholder { background: #d0d0d0; width: 100%; height: 60vh; border-radius: 0; }
.about-hero-bg {
    width: 100%;
    height: 60vh;
    position: relative;
    isolation: isolate;
    border-radius: 0;
}
.photo-placeholder-tall { background: #d0d0d0; width: 100%; aspect-ratio: 3 / 4; border-radius: 0; }
.about-portrait {
    width: 100%;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    border-radius: 0;
}
.about-portrait img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* CRAFT PAGE */
/* Same band as .about-hero-bg: full-bleed, 60vh, cover, centred. About had a photo hero and
   The Work did not, which read as an oversight rather than a decision. */
.craft-hero-bg {
    width: 100%;
    height: 60vh;
    position: relative;
    isolation: isolate;
    border-radius: 0;
}

.craft-hero-bg .hero-photo {
    /* 72% across, not centre. At desktop the band is wider than it is tall, so `cover`
       shows the FULL width and this value changes nothing. At phone width it shows only
       the middle ~half, and centred that half is the painter's trouser leg — the roller,
       the hand and the green wall all sit right of centre. One value, correct at both. */
    object-position: 72% center;
}

.process-steps-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto;
    gap: 60px 80px;
    border-radius: 0;
}
.process-steps-grid .process-step {
    display: grid;
    grid-template-rows: auto auto 1fr auto;
    gap: 0;
    padding: 40px 0;
    border-radius: 0;
}
.process-step { padding: 40px 0; border-radius: 0; }
.step-label { font-family: 'Inter', sans-serif; font-size: 0.7rem; letter-spacing: 2px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 12px; }
h3 { font-family: 'Crimson Pro', serif; font-size: 2rem; font-weight: 400; margin: 0 0 1rem 0; }
.craft-step-photo {
    width: 100%;
    height: 420px;
    object-fit: cover;
    object-position: center top;
    display: block;
    margin-top: 24px;
    border-radius: 0;
}
.process-steps-grid .craft-step-photo {
    width: 100%;
    height: 380px;
    object-fit: cover;
    object-position: center top;
    display: block;
    margin-top: 24px;
    align-self: end;
    border-radius: 0;
}
.photo-strip { display: flex; gap: 4px; margin: 60px 0; border-radius: 0; }
.photo-strip-item { flex: 1; height: 400px; background: #d0d0d0; border-radius: 0; overflow: hidden; }
.strip-photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* FORM ELEMENTS */
/* The nav is position: sticky, so a scroll that lands the section's top edge at y=0 puts
   that edge UNDER the bar. scroll-margin-top reserves the bar's height for every scroll
   into this section — the CTAs' and the browser's own hash jump alike. */
.intake-section { scroll-margin-top: 80px; }

.contact-card { max-width: 850px; margin: 0 auto; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
.input-group { margin-bottom: 25px; }
.input-label { display: block; font-family: 'Inter', sans-serif; font-size: 0.75rem; font-weight: 500; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; color: var(--accent); }

.artisan-input {
    width: 100%;
    background: #fff;
    border: 1px solid var(--border);
    padding: 14px;
    font-family: 'Inter', sans-serif;
    font-size: 1rem;
    box-sizing: border-box;
    border-radius: 0;
}

.artisan-input:focus { outline: none; border-color: var(--accent); }
/* The flyer-location question. Native select chrome (iOS draws a grey pill) replaced by
   the input's own box plus a chevron, so it reads as one of the fields above it. */
select.artisan-input {
    -webkit-appearance: none;
    appearance: none;
    color: inherit;
    padding-right: 40px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555' stroke-width='1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 16px center;
}

/* THE PUBLIC SITE'S PRIMARY BUTTON. One treatment, filled in the brand navy — the form's
   submit and both call-to-action buttons are the same component, not three lookalikes.

   The display/decoration/line-height lines below were added so an <a> can wear it: the CTAs
   are links to a place on this page, and a <button> that navigates is the wrong element for
   anyone expecting a link. They are inert on the <button> that already used this class.

   Square on purpose. Every rule on this site sets border-radius: 0; the 9px button radius
   in the design system governs the Desk, which is a different surface. */
.artisan-button {
    background: var(--accent);
    color: #fff;
    border: none;
    padding: 20px 50px;
    cursor: pointer;
    text-transform: uppercase;
    font-family: 'Inter', sans-serif;
    font-weight: 500;
    letter-spacing: 2px;
    font-size: 0.8rem;
    line-height: 1;
    display: inline-block;
    text-align: center;
    text-decoration: none;
    border-radius: 0;
    transition: background-color 0.2s ease;
}

.artisan-button:hover {
    background: #1d3547;   /* one step down from --accent */
}

/* A visible focus ring, because the CTA is the control on this site a keyboard user is now
   most likely to reach first and it had none. :focus-visible, not :focus, so a mouse click
   does not leave a ring behind. */
.artisan-button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* Sized for the nav bar rather than a page section — same fill, same type, less padding.
   A size variant, not a second button. */
.artisan-button--compact {
    padding: 12px 22px;
    font-size: 0.7rem;
    letter-spacing: 1.5px;
}

.label { font-family: 'Inter', sans-serif; font-weight: 500; font-size: 0.8rem; letter-spacing: 2px; color: var(--accent); }
h2 { font-family: 'Crimson Pro', serif; font-size: 2.5rem; font-weight: 400; margin: 0; }
.prose-base { font-family: 'Inter', sans-serif; font-weight: 300; font-size: 1.1rem; line-height: 1.7; color: var(--text-muted); }
/* /privacy/: section heads one step below h2, and links in body text that read as links. */
.privacy h2 { font-size: 1.75rem; margin: 2.5rem 0 0.75rem 0; }
.privacy .prose-base a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.footer-text { padding: 100px 0; font-family: 'Inter', sans-serif; font-size: 0.8rem; color: var(--text-muted); text-align: center; border-top: 1px solid var(--border); }

@media (max-width: 768px) {
    .grid-2-even, .grid-2-hero, .form-grid, .grid-3-even, .process-steps-grid { grid-template-columns: 1fr; }
    .hero-bg { height: 60vh; }
    /* 60px on a phone, the scale's bottom step: 100px is a quarter of the viewport here,
       where on a 900px desktop it is an eleventh. */
    .hero-bg + section,
    .about-hero-bg + section,
    .craft-hero-bg + section { padding-top: 60px; }
    .logo-wrapper { width: 300px; }
    .hero-stack { gap: 32px; }
    .photo-strip { display: grid; grid-template-columns: 1fr 1fr; }
}

/* ── Site Nav ── */

#site-nav {
    position: sticky;
    top: 0;
    z-index: 100;
}

.site-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 10%;
    background: #ffffff;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
}

.site-nav-logo {
    display: flex;
    align-items: center;
    text-decoration: none;
    border-radius: 0;
}

.site-nav-logo-img {
    width: 120px;
    height: auto;
    display: block;
    border-radius: 0;
}

/* Holds the menu, the CTA and the hamburger as ONE right-hand item. `.site-nav` is
   space-between with two children (logo | this); adding the button as a third direct child
   would have spread all three across the bar instead of pinning it to the right. */
.site-nav-right {
    display: flex;
    align-items: center;
    gap: 32px;
}

.site-nav-links {
    display: flex;
    gap: 32px;
    align-items: center;
}

/* Far right, after the links, so it reads as an action and not a fourth nav item. */
.site-nav-cta {
    white-space: nowrap;
}

.site-nav-link {
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--text-muted);
    text-decoration: none;
    border-radius: 0;
    transition: color 0.2s ease;
}

.site-nav-link:hover {
    color: var(--text-main);
}

.site-nav-link.active {
    color: var(--text-main);
    font-weight: 500;
}

/* Mobile nav */
.nav-hamburger {
    display: none;
    background: none;
    border: none;
    font-size: 1.4rem;
    cursor: pointer;
    color: var(--text-main);
    padding: 0;
    border-radius: 0;
}

@media (max-width: 768px) {
    .site-nav {
        padding: 12px 5%;
    }

    .site-nav-logo-img {
        width: 90px;
    }

    .nav-hamburger {
        display: block;
    }

    /* THE CTA DOES NOT COLLAPSE. Only #nav-links is hidden behind the hamburger; the
       button is a sibling of that menu, not a member of it, and stays on the bar at every
       width. Tighter padding and a smaller gap so the logo, the button and the hamburger
       all fit a 320px phone. */
    .site-nav-right {
        gap: 14px;
    }

    .site-nav-cta {
        padding: 11px 16px;
        font-size: 0.65rem;
        letter-spacing: 1px;
    }

    .site-nav-links {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background: #ffffff;
        border-bottom: 1px solid var(--border);
        padding: 20px 10%;
        flex-direction: column;
        gap: 20px;
    }

    .site-nav-links.nav-open {
        display: flex;
    }

    .site-nav {
        position: relative;
    }

    .site-nav-link {
        font-size: 0.7rem;
        letter-spacing: 1px;
    }
}

/* ── Site Footer ── */

.site-footer {
    padding: 100px 10%;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    color: var(--text-muted);
    text-align: center;
    border-top: 1px solid var(--border);
    border-radius: 0;
}

.site-footer__line {
    margin: 0;
    line-height: 2;
}

.site-footer a {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

.site-footer a:hover,
.site-footer a:focus-visible {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Social profiles. The SVGs are INLINE with fill="currentColor", not <img>: an image cannot
   inherit a CSS colour, so the four marks would render in their own brand colours. Here
   they are all --accent, one step darker on hover, the same treatment as .artisan-button.
   Each link is a 44px tap target around a 20px mark. */
.site-footer__social {
    list-style: none;
    margin: 24px 0 0;
    padding: 0;
    display: flex;
    justify-content: center;
    gap: 8px;
}

.site-footer .site-footer__social-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 9px;
    color: var(--accent);
}

.site-footer .site-footer__social-link:hover {
    color: #1d3547;   /* one step down from --accent, as .artisan-button:hover */
}

.site-footer .site-footer__social-link:focus-visible {
    color: #1d3547;
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* ── INTAKE FAILURE PANEL ──────────────────────────────────────────────────────────
   Shown inside the contact form when a submission throws. It is the only place on the
   public site that hands out Joshua's direct line, and it appears at the exact moment
   the automated path has failed — so it has to be READ, not skimmed past.

   Deliberately restrained rather than alarming: a red-alert box reads as "you did
   something wrong", and the customer did not. A left rule in the accent, a tinted card
   and a plain message carry it. The contact links are the loudest thing in the panel,
   because they are the actual next step.

   Sits ABOVE the submit button inside the existing form — the form is never replaced on
   failure, since everything the customer typed is still the only copy that exists. */
.intake-error {
    margin: 0 0 24px;
    padding: 20px 22px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
}
.intake-error__label {
    display: block;
    margin-bottom: 10px;
}
.intake-error__body {
    font-family: 'Inter', sans-serif;
    font-weight: 300;
    font-size: 1rem;
    line-height: 1.65;
    color: var(--text-main);
    margin: 0 0 10px;
}
.intake-error__contact {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 28px;
    margin: 14px 0 0;
}
/* Tap targets first: this panel exists because someone needs to reach a human, and most
   of them are on a phone. 44px min-height is the accessible minimum for a touch target. */
.intake-error__contact a {
    font-family: 'Inter', sans-serif;
    font-weight: 500;
    font-size: 1.05rem;
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 3px;
    display: inline-flex;
    align-items: center;
    min-height: 44px;
}

/* ── MOTION ────────────────────────────────────────────────────────────────────────
   EVERY motion rule on the public site lives inside this one block, so reduced motion
   is not a set of overrides that must each be remembered — the rules simply do not
   exist for that visitor, and the page renders exactly as it did before motion arrived.
   Only transform and opacity move: neither changes layout, so nothing can shift.
   test/motion enforces both. */
@media (prefers-reduced-motion: no-preference) {

    /* HERO DRIFT. The photo trails the page: translateY = rate × scroll, for the first
       200vh — past any band's bottom edge, since every band is at most 100vh and sits
       directly under the nav. Each band starts flush under the sticky nav, so its top
       scrolls under the bar faster than the photo moves: the gap drift opens above the
       photo is never on screen, and the crop at rest is unchanged. What drift costs is a
       strip at the BOTTOM, pushed under the band's lower edge — clipped here.
       js/motion.js carries the same formula for engines without scroll timelines.

       DESKTOP ONLY — a mouse or trackpad, `(hover: hover) and (pointer: fine)`. On iOS
       Safari 26.6 the photo slid and then jumped on every scroll, and no shape of this rule
       fixed it: five variants on the device (tools/images/drift-lab.html, 2026-09-24) all
       jittered. Phones get the still photo. js/motion.js applies the same query. */
    .hero-bg, .about-hero-bg, .craft-hero-bg { overflow: clip; }

    @keyframes hero-drift {
        to { transform: translateY(calc(200vh * var(--motion-drift-rate))); }
    }

    @supports (animation-timeline: scroll()) {
        @media (hover: hover) and (pointer: fine) {
            .hero-bg .hero-photo, .about-hero-bg .hero-photo, .craft-hero-bg .hero-photo {
                animation: hero-drift linear both;
                animation-timeline: scroll(root block);   /* after the shorthand, which resets it */
                animation-range: 0 200vh;
                will-change: transform;
            }
        }
    }

    /* SECTION RISE. js/motion.js sets [data-rise] only on blocks that are OFF screen at
       load, so hiding is instant and unseen; .is-risen carries the transition, so only
       the reveal animates. Blocks side by side rise together, left to right: js/motion.js
       gives each its place in the row as --rise-order, and the delay is that × the stagger. */
    [data-rise] {
        opacity: 0;
        transform: translateY(var(--motion-rise-distance));
    }
    [data-rise].is-risen {
        opacity: 1;
        transform: none;
        transition: opacity var(--motion-rise-duration) var(--motion-ease-settle),
                    transform var(--motion-rise-duration) var(--motion-ease-settle);
        transition-delay: calc(var(--rise-order, 0) * var(--motion-rise-stagger));
    }
}

/* The postcard visitor's note above the form, and their thank-you after it. */
.move-in-note {
    margin: 0 0 28px;
    padding: 18px 22px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    font-family: 'Inter', sans-serif;
    font-size: 1.05rem;
    line-height: 1.6;
    color: var(--text-main);
}
.move-in-thanks {
    font-family: 'Crimson Pro', serif;
    font-size: 1.5rem;
    line-height: 1.5;
    margin: 0;
    color: var(--text-main);
}
