/* ===========================================================================
   REFRESH ELECTRICAL — brand layer over the TitanPipe design system.

   styles.css is the template's stylesheet, kept verbatim; everything that
   makes it Refresh lives here and loads after it. The template keys almost
   every colour, face and weight off a custom property, so most of this file
   is a re-assignment of those properties to the Brand Kit values. Literal
   colours come from Brand Kit/02 Colour and web-v11 palette.css — nothing
   new is invented here.

     paper   #f4f2ef canvas · #fdfcfb raised · #edeae5 band · #dedad4 hairline
     ink     #17171a body · #43434a secondary · #5c5c64 tertiary
     orange  #f37321 fill, logo, links, and text on ink or a photograph
             #b54e00 text on paper (4.64:1; the fill is 2.58:1 there)
     The accent was copper (#b06d36 / #7f4720 / #e9a87c) until 2026-09-27,
     when Brisso asked for "a real orange, like Enphase": the small copper
     text read as bronze. The token NAMES still say copper because styles.css
     and every page read them; the values are orange.

   ONE FACE. The mono was dropped 2026-09-22 — see fonts.css for why. The
   technical register is still a register: 12px, 0.18em, uppercase, 500,
   tabular figures. It is now Archivo doing that job rather than DM Mono.
   =========================================================================== */
/* :root tokens live in tokens.css since 2026-10-18 (loaded before this file). */

html, body { background-color: var(--color--background-light); }
body { font-family: var(--font-family--primary-font); color: var(--color--dark); }
.page-wrapper { background-color: var(--color--background-light); }

/* Display type */
.large-title, .error-large-title, .display-two { font-weight: 500; letter-spacing: 0.03em; text-transform: uppercase; }
.large-title, .error-large-title { font-size: clamp(28px, 1.9vw + 20px, 46px); line-height: 1.1; text-wrap: balance; }
/* Titles on a photograph are bold (Brisso, 2026-09-16); titles on paper stay
   light. The variable Archivo carries 600 without another file. */
.large-title.white, .cta-wrap .large-title { font-weight: 600; letter-spacing: 0.03em; line-height: 1.08; }
h1, h2 { font-weight: 300; }
.roofing-content, .works-title, .materials-title, .team-title, .service-signle-card-title,
.service-overview-title, .service-choose-title, .service-conclusion-title,
.hero-service-title, .inner-title, .story-description { font-weight: 300; }
.strengthen-title-box .large-title { font-weight: 500; }
.hero-service-card-title, .step-card-title, .material-slide-title, .accordion-title,
.secondary-text-medium, .description-medium, .feature-project-text-medium, .step-text-medium,
.service-choose-text-medium, .slider-author-name { font-weight: 500; }
.secondary-text { letter-spacing: 0; line-height: 1.6; }

/* The technical register: eyebrows, labels, step numbers, every figure. */
.re-label,
.description.yollow, .description.primary-color,
.inner-sub-title-box .description, .slider-sub-title-box .description,
.stap, .footer-wized-title, .contact-address-title, .choose-name-text,
.slider-number-text, .hero-form-title, .labels, .service-single-card-list-title {
  font-family: var(--font-family--primary-font);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  line-height: 1.2;
}
.re-num { font-weight: 500; font-variant-numeric: tabular-nums; }
.description.yollow { color: var(--color--accent-on-dark); }
.description.primary-color { color: var(--color--accent-on-dark); background-color: var(--color--dark); }
.labels { color: var(--color--text-grey-primary); margin-bottom: 8px; }

/* LAST LINES. Running text avoids leaving one word alone on its last line,
   and headings share their words evenly across lines. Both are progressive:
   a browser without support wraps exactly as before. Added 2026-09-27 after
   the polish sweep found single-word last lines on 18 kinds of element. */
p, li, dd, figcaption { text-wrap: pretty; }
h1, h2, h3, h4 { text-wrap: balance; }

/* PILL BUTTONS (Brisso, 2026-10-08, from the plan's mock-up): every button
   on the site is a full pill, the sliding shadow inside it too. */
.primary-button-link, .secondary-button-link, .primary-button-shadow, .primary-button-shadow.second,
.re-submit, .re-actionbar a, .primary-button-link.error-button { border-radius: 999px; }

/* BUTTONS. The main action is filled orange with an ink label (6.21:1) and
   washes to ink with a paper label on hover (16:1), on paper and on a
   photograph alike. Until 2026-09-27 it was ink, with copper only on hover.
   One filled action per view: the header's button stays outlined. */
.primary-button-text { color: var(--color--background-light); font-weight: 500; }
.primary-button-link, .primary-button-link:has(.primary-button-shadow.second) { background-color: var(--re-accent); }
.primary-button-link .primary-button-text, .primary-button-link .primary-button-text.white { color: var(--color--dark); }
.primary-button-link .primary-button-shadow, .primary-button-link .primary-button-shadow.second { background-color: var(--color--dark); }
.primary-button-link .primary-button-hover-text, .primary-button-link .primary-button-hover-text.black { color: var(--color--background-light); }
.secondary-button-link .primary-button-shadow { background-color: var(--re-accent); }
.secondary-button-link .primary-button-hover-text { color: var(--color--dark); }
/* The template's .error-button is an outlined WHITE button — a variant the brand
   never adopted — and at .primary-button-link.error-button it out-specifies the
   rule above, so load order never gets a vote. Left alone it put the paper label
   on raised paper at 1.09:1 and the only way off /thanks was invisible. */
.primary-button-link.error-button { background-color: var(--re-accent); border: 0; }
.secondary-button-link.nav { border-color: var(--color--cool-gray); }
/* On a photograph the grey outline washes out against sky; paper holds. */
.navbar.black .secondary-button-link.nav { border-color: rgba(253, 252, 251, .6); }
.send-now-button { background-color: var(--re-accent); color: var(--color--dark); }
.send-now-button:hover { background-color: var(--color--dark); color: var(--color--background-light); }

/* Header. 76px, down from 104px on 2026-09-27: the bar was mostly padding
   and a 60px button, and it pushed every paper page head down with it. */
.navbar { padding-top: 14px; padding-bottom: 14px; }
.navbar .secondary-button-link.nav { padding: 11px 20px; }
.navbar-logo-link { display: inline-flex; align-items: center; }
.navbar-logo-image { height: 15px; width: auto; }
.navbar-logo-image.is-compact { display: none; height: 15px; }
/* The name-only lockup under 480px, where the full mark would fight the menu
   button for width. It was also used from 992 to 1199px while the header had
   six links; with four (2026-09-27) the full wordmark leaves 153px spare at
   992, measured. */
@media (max-width: 479px) {
  .navbar-logo-image.is-full { display: none; }
  .navbar-logo-image.is-compact { display: block; }
}
/* Logo left, the six pages and the button together on the right. The template
   centred the menu between the two; above the collapse point it now sits
   flush against the button. */
@media (min-width: 992px) {
  .navbar-content .nave-menu { margin-left: auto; }
  .nav-menu-content { gap: 36px; padding: 0 32px 0 0; }
}
.nav-link { font-weight: 400; }
.nav-link:not(.white) { color: var(--color--dark); }
.nav-link[aria-current="page"] { color: var(--color--accent-text); }
.nav-link.white[aria-current="page"] { color: var(--color--accent-on-dark); }
.nav-link:hover { color: var(--color--accent-text); }
.nav-link.white:hover { color: var(--color--accent-on-dark); }
.mobile-nav-wrap .nav-menu { font-weight: 400; }

/* Photographs, at native resolution (Photo's/HERO's, 4032 wide, ungraded).
   The homepage hero is its own markup since 2026-09-27 — the photograph full
   bleed with the type low on the left — and its layout and scrim live in
   index.astro. Only the ground it shares with the template stays here. */
.section.is-hero { background-image: none; background-color: var(--color--black); }
/* Heroes are <img> elements with srcset, not CSS backgrounds: a phone gets the
   1600px file and a Retina laptop the 3200px, and the fetch starts with the
   HTML rather than after the stylesheet. */
.hero-bg, .hero-bg.hero, .hero-bg.service, .hero-bg.about { background-image: none; }
.re-hero-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero-bg::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, rgba(12,10,8,.42) 0, rgba(12,10,8,0) 200px),
    linear-gradient(to right, rgba(12,10,8,.78) 0%, rgba(12,10,8,.66) 30%, rgba(12,10,8,.22) 58%, rgba(12,10,8,0) 78%);
}
@media (max-width: 991px) {
  .hero-bg::after {
    background:
      linear-gradient(to bottom, rgba(12,10,8,.42) 0, rgba(12,10,8,0) 200px),
      linear-gradient(to top, rgba(12,10,8,.72) 0%, rgba(12,10,8,.66) 45%, rgba(12,10,8,.58) 100%);
  }
}
/* Inner photo heroes (services, contact, the nine service pages): the text
   block sits bottom-left like the homepage, on the darkest part of the
   frame, instead of centred mid-frame as the template had it. */
.is-service-banner .section-hero-full.is-service .hero-container { display: flex; flex-direction: column; justify-content: flex-end; min-height: clamp(560px, 82vh, 780px); }
.is-service-banner .hero-sticky { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; }
.is-service-banner .hero-wrap { width: 100%; }
.is-service-banner .hero-title.service-hero { width: 100%; max-width: 1320px; margin: 0 auto; padding: 150px 16px clamp(56px, 7vw, 88px); }
.is-service-banner .inner-hero-wrap.center.solution { text-align: left; align-items: flex-start; margin-left: 0; max-width: 820px; }
.is-service-banner .inner-hero-title-box.solution { max-width: none; margin-bottom: 20px; }
/* In em, and wide enough for capitals: at 18ch the caps titles ("COMMERCIAL
   BATTERIES", "GENERAL ELECTRICAL") broke one word per line at every width.
   Longer titles still balance over two lines (2026-09-27). */
.is-service-banner .large-title { max-width: 15em; }
.is-service-banner .inner-hero-description-box.solution { max-width: 48ch; }
.is-service-banner .inner-hero-description-box.solution .secondary-text { text-align: left; }
/* Scrim for bottom-left type: darkest at the bottom and the left. */
.hero-bg.service::after, .hero-bg.about::after {
  background:
    linear-gradient(to bottom, rgba(12,10,8,.42) 0, rgba(12,10,8,0) 200px),
    linear-gradient(to top, rgba(12,10,8,.82) 0%, rgba(12,10,8,.60) 38%, rgba(12,10,8,.22) 70%, rgba(12,10,8,.10) 100%),
    linear-gradient(to right, rgba(12,10,8,.45) 0%, rgba(12,10,8,.20) 45%, rgba(12,10,8,0) 75%);
}
@media (max-width: 991px) {
  .hero-bg.service::after, .hero-bg.about::after {
    background:
      linear-gradient(to bottom, rgba(12,10,8,.42) 0, rgba(12,10,8,0) 160px),
      linear-gradient(to top, rgba(12,10,8,.84) 0%, rgba(12,10,8,.66) 45%, rgba(12,10,8,.30) 100%);
  }
}
/* The template's centred ledes — the dark band and the inner heroes — carried
   no measure and ran to 80 characters at 18px. Capped, and kept centred. */
.strengthen-description-box .secondary-text,
.inner-hero-description-box .secondary-text { max-width: var(--re-measure); margin-inline: auto; text-wrap: balance; }
/* …and the closing bands, which are left-aligned below, keep theirs at the left. */
.section.strengthen .strengthen-description-box .secondary-text,
.re-cta .inner-hero-description-box .secondary-text { margin-inline: 0; }
/* …except the service banners, which the brand re-aligned to the left. */
.is-service-banner .inner-hero-description-box .secondary-text { margin-inline: 0; }

/* The template's "light-gray" lede is the hairline grey; over a photograph it reads as paper. */
.secondary-text.light-gray { color: var(--color--background-light); }
.is-service-banner .large-title, .section.is-hero .large-title { text-wrap: balance; }
/* The homepage headline: two lines from 768px up, on Brisso's own break;
   four on a 390px phone and five on a 360px one (measured 2026-09-27; a 30px
   floor was tried and bought no line back). The measure is set in index.astro. */
.section.is-hero .large-title { font-size: clamp(28px, 1.6vw + 20px, 46px); }
.section.cta { background-image: url('/assets/photos/dusk-4032.webp'); background-position: 50% 100%; background-color: var(--color--background-light); }
.section.cta::before { content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom, rgba(12,10,8,0) 30%, rgba(12,10,8,.50) 62%, rgba(12,10,8,.62) 100%); }
.section.cta .container { position: relative; z-index: 1; }
.section.cta .cta-wrap { position: relative; }
.linear { background-image: linear-gradient(#f4f2ef 25%, #f4f2ef00 56%); }
.contact-shape-box { background-image: linear-gradient(#f4f2ef 20%, #f4f2ef00); }
.exhibition-section::after {
  content: '';
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(to right, rgba(12,10,8,.80) 0%, rgba(12,10,8,.64) 38%, rgba(12,10,8,.24) 72%, rgba(12,10,8,.14) 100%);
}
@media (max-width: 767px) {
  /* On phones the template moves the slide text to the TOP of the frame, over
     the sky, so the phone scrim is weighted the other way from the desktop one. */
  .exhibition-section::after { background: linear-gradient(to bottom, rgba(12,10,8,.80) 0%, rgba(12,10,8,.72) 42%, rgba(12,10,8,.28) 100%); }
  /* The template parks the eyebrow in the top corner on phones, the brightest part of every frame. */
  .exhibition-section .description.yollow { display: none; }
}
/* Copper at 12px cannot reach 4.5:1 over any photograph without blacking the
   frame out, so the slide eyebrow is paper; copper eyebrows stay on paper grounds. */
.exhibition-section .description.yollow { color: var(--color--background-light); }
.exhibition-section .exhibition-container { position: relative; z-index: 1; }
.hero-line-block-inner { border-color: rgba(253,252,251,.14); }

/* The marquee word */
.roofing { letter-spacing: -0.04em; font-weight: 300; color: rgba(253,252,251,.92); }
.roofing.is-stats { color: var(--color--paper-band); }

/* Cards and wells */
.hero-service-single-card, .works-single-card-wrap { background-color: var(--color--white); }
.step-card-box { background-color: var(--color--dark); }

/* Footer: ink ground, paper type */
.section.footer { background-color: var(--color--dark); }
.footer-menu { font-weight: 400; color: var(--color--background-light); }
.footer-menu:hover { color: var(--color--accent-on-dark); }
.footer-bottom-content { font-weight: 400; color: var(--color--text-grey-primary); }
.footer-bottom-content .footer-menu { color: var(--color--background-light); }
.footer-logo { height: 18px; width: auto; }
.re-creds { color: var(--color--cool-gray); font-size: 13px; line-height: 1.6; }
.re-creds span { white-space: nowrap; }

/* Utility */
.re-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.re-initials {
  display: grid; place-items: center; width: 100%; aspect-ratio: 1; border-radius: 6px;
  background-color: var(--color--paper-band); color: var(--color--accent-text);
  font-size: 28px; font-weight: 500; letter-spacing: 0.08em;
}

/* ONE FORM LANGUAGE for the quote form, wherever it appears (/contact, the
   homepage hero, the /go/ landing pages): mono label, hairline field, ink button. The fields are 44px so they are the same
   box whether they hold text, a select or the first line of a textarea; the
   16px type is what stops iOS zooming into the field on focus. */
/* Any block of running copy can take .re-measure, a lede .re-measure-lede.
   Page rules set the same tokens directly — see --re-measure above for why
   these are em and not ch. */
.re-measure { max-width: var(--re-measure); }
.re-measure-lede { max-width: var(--re-measure-lede); }

/* Section headings are set on a deliberately narrow measure — the house look —
   but the measure was on the WRAPPER, in `ch`, and so resolved at the wrapper's
   16px rather than the heading's 34px. A 34ch wrapper gave a 34px heading about
   16 characters a line, which is why a long one ran to seven lines and orphaned
   its last word ("…what your / switchboard can / carry."). The measure belongs
   on the heading, in em, where it holds ~26 characters at any heading size.
   `text-wrap: balance` is here too but only does anything up to ~6 lines, which
   is exactly the case this fixes. */
.re-how-title, .re-heading, .re-svc-heading, .re-go-heading, .re-gear-heading,
.re-places-heading {
  max-width: var(--re-measure-heading);
  text-wrap: balance;
}
.re-say-lead { text-wrap: balance; }
/* The About page's section heading is a template class, so it takes the caps
   treatment here rather than in a page's scoped styles. */
.service-overview-title { font-size: var(--re-heading); font-weight: var(--re-h-weight); line-height: 1.2; letter-spacing: var(--re-h-tracking); text-transform: uppercase; }


.re-form { display: grid; gap: 16px; min-width: 0; }
.re-form-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.re-form-full { grid-column: 1 / -1; }
/* align-content: start — without it a field beside one showing an error is
   stretched to the same height and its input drops ~13px (seen on /contact,
   2026-09-27). */
.re-field { display: grid; gap: 8px; min-width: 0; align-content: start; }
.re-field .labels { margin: 0; }
.re-opt { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--color--text-grey-primary); }
.re-input {
  width: 100%; min-width: 0; height: 44px; margin: 0; padding: 0 12px;
  border: 1px solid var(--re-field-border); border-radius: 4px;
  background-color: var(--color--white); color: var(--color--dark);
  font-family: var(--font-family--primary-font); font-size: 16px; line-height: 1.4;
  transition: border-color .2s var(--re-ease);
}
.re-input::placeholder { color: var(--color--text-grey-primary); opacity: .8; }
.re-input:hover { border-color: var(--color--cool-gray); }
.re-input:focus-visible { border-color: var(--color--dark); }
/* Inline errors, written by the form script under the field that needs
   fixing. The brand has no red; copper text on the raised paper is 7.4:1. */
.re-input[aria-invalid="true"] { border-color: var(--re-error); }
.re-error { margin: 0; font-size: 14px; line-height: 1.4; color: var(--re-error); }
textarea.re-input { height: auto; min-height: 132px; padding: 10px 12px; resize: vertical; }
select.re-input {
  appearance: none; -webkit-appearance: none; padding-right: 40px; cursor: pointer;
  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 1.5l5 5 5-5' fill='none' stroke='%2317171a' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
.re-fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.re-fieldset legend { padding: 0; margin-bottom: 8px; }
.re-checks { display: flex; flex-wrap: wrap; gap: 8px; }
.re-check {
  display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 14px;
  border: 1px solid var(--color--line-gray); border-radius: 4px; background: var(--color--white);
  font-size: 15px; cursor: pointer; transition: border-color .2s var(--re-ease);
}
.re-check:hover { border-color: var(--color--cool-gray); }
.re-check:has(:checked) { border-color: var(--color--dark); }
.re-check input { margin: 0; accent-color: var(--color--accent-text); }
.re-submit {
  display: block; width: 100%; height: 48px; margin: 0; padding: 0 24px; border: 0; border-radius: 4px;
  background-color: var(--re-accent); color: var(--color--dark);
  font-family: var(--font-family--primary-font); font-size: 16px; font-weight: 500; cursor: pointer;
  transition: background-color .3s var(--re-ease), color .3s var(--re-ease), scale 120ms var(--re-ease);
}
.re-submit:hover { background-color: var(--color--dark); color: var(--color--background-light); }
/* Sending: locked against a second tap, and it must not light up on hover. */
.re-submit:disabled { background-color: var(--re-accent); color: var(--color--dark); cursor: progress; }
.re-form-note { margin: 0; font-size: 14px; line-height: 1.5; color: var(--color--text-grey-primary); }
.hero-form-title { text-align: left; color: var(--color--text-grey-primary); margin-bottom: 20px; }
.re-form-note a { color: var(--color--dark); text-decoration: none; border-bottom: 1px solid var(--color--primary-color); }
@media (max-width: 479px) {
  .re-form-2 { grid-template-columns: minmax(0, 1fr); }
}


/* Fixes found on the rendered page */
.navbar-logo-link { max-width: none; }

/* Outlined header button on a paper page: ink text, not paper text. */
.secondary-button-link.nav .primary-button-text:not(.white) { color: var(--color--dark); }
.secondary-button-link.nav { border-width: 1px; }

/* The dark band is a solid ink ground everywhere. Only the homepage hero and
   its closing band put type on a photograph — see refresh-visual-direction.
   The template padded it 235px each side; half of that is plenty. */
.section.strengthen { background-image: none; background-color: var(--color--dark); padding-top: clamp(80px, 9vw, 120px); padding-bottom: clamp(80px, 9vw, 120px); }

/* Footer wordmark: the template sized this box for a 114px logo. */
.footer-logo-box, .footer-link-blog { max-width: none; width: auto; }
.footer-logo { max-width: none; height: 18px; width: auto; }

/* Small type on the ink footer: the template's mid greys measure 2.6-3.8:1
   there. --color--paper-400 (#c7c1b8, from the palette) is 9.9:1. */
.section.footer .footer-wized-title, .section.footer .small-text.gray, .section.footer .re-creds,
.section.footer .footer-bottom-content, .section.footer .footer-social-menu { color: var(--color--paper-400); }

/* Keyboard focus. The template strips the outline on form fields, so a
   keyboard user tabbing into the enquiry form could not see where they were. */
:is(a, button, input, select, textarea, summary, [tabindex]):focus-visible { outline: 2px solid var(--color--accent-text); outline-offset: 3px; border-radius: 2px; }
/* The ring has to hold 3:1 against whatever it is drawn on. The paper-side
   copper measures 2.4:1 on ink and about 2.7:1 on a scrimmed photograph, so
   every dark ground that has something focusable on it takes the on-dark
   copper (8.9:1 on ink). Measured 2026-09-27; add a ground here when one is
   added to the site. */
:is(.navbar.black, .re-ftr, .re-cta, .section.is-hero, .section.strengthen, .re-steps, .is-service-banner) :is(a, button, input, select, textarea):focus-visible { outline-color: var(--color--accent-on-dark); }
/* No form sits on a photograph any more — the homepage and /go/ forms moved
   to the paper enquiry band on 2026-09-27 — so no paper card needs an
   exception from the on-dark ring. Add one back if a form returns to a
   photograph: the on-dark copper is 2:1 against paper. */
.re-form :is(input, select, textarea):focus-visible { outline-offset: 0; }

/* Skip link: the first stop for the Tab key on every page, parked above the
   viewport until it has focus. A paper chip reads on the photographs and on
   paper alike; its ring sits on its own edge, where it is 7:1 either way. */
.re-skip {
  position: absolute; top: 12px; left: max(16px, env(safe-area-inset-left)); z-index: 1001;
  padding: 12px 18px; border-radius: 4px;
  background-color: var(--color--white); color: var(--color--dark);
  font-size: 15px; font-weight: 500; text-decoration: none;
  transform: translateY(calc(-100% - 24px));
}
.re-skip:focus { transform: none; }
.re-skip:focus-visible { outline-offset: 0; border-radius: 4px; }

/* ===========================================================================
   MOTION — retuned 2026-09-22 (Brisso: "not over the top").

   The template runs on a 0.5–1.2s clock and reveals every block with a
   blur-fade. On a 7,800px homepage that reads as the page waiting on itself.
   Reveals now land in 280ms, hovers in 180–220ms, and nothing animates
   `filter` anywhere.

   THE TRAVEL CAME DOWN WITH THE DURATION, and it had to. The template parks a
   revealing block 100px below where it belongs; covering that in 280ms is a
   snap, not a rise. 18px is the "small rise" the brand guidelines actually
   describe, and it is the distance that still reads as arriving.

   `will-change` is released. The template declares it on every revealing
   element, so a long page carries a hundred-odd permanently promoted layers
   for the sake of one 280ms transition — a cost paid on every page, all the
   time, to save a few milliseconds once.
   =========================================================================== */
.js .reveal-up, .js .reveal-left, .js .reveal-right, .js .reveal-grow,
.js .reveal-grow-big, .js .reveal-blur, .js .reveal-blur-fast {
  transition: opacity 280ms var(--re-ease),
              transform 280ms var(--re-ease);
  will-change: auto;
}
.js .reveal-up { transform: translateY(18px); }
.js .reveal-left { transform: translateX(-18px); }
.js .reveal-right { transform: translateX(18px); }
.js .reveal-grow { transform: scale(.97); }
/* The blur presets keep their class names — the markup uses them — but not the
   blur. It is the one effect here that reads as decoration rather than as the
   page arriving, and it is also the most expensive thing on the list. */
.js .reveal-blur, .js .reveal-blur-fast {
  transform: translateY(18px);
  filter: none;
  transition-duration: 280ms;
  transition-delay: 0s;
}

/* Hovers. Half a second for a colour change feels broken, and `all` animates
   properties nobody meant to — the trap that once made a hidden nav link take
   half a second to stop being readable by the contrast checker. */
.nav-link, .nav-menu, .footer-menu { transition: color 180ms var(--re-ease); }
.primary-button-text, .join-button-text, .nav-button-text, .secondary-button-two-text { transition: transform 220ms var(--re-ease); }
.primary-button-hover-text, .join-button-hover-text, .nav-button-hover-text, .secondary-button-two-hover-text { transition: transform 220ms var(--re-ease), opacity 220ms var(--re-ease); }
.primary-button-icon, .primary-button-hover-icon { transition: transform 220ms var(--re-ease); }

/* PRESSED. platform.css switches off WebKit's grey tap flash, and a phone
   applies :hover only once the finger lifts, so until 2026-09-27 nothing
   answered a tap while it was happening. Buttons give a 4% press (0.96, the
   better-ui value; 0.98 moved a 48px button under a pixel). `scale`, not
   `transform`, so it composes with anything a button already transforms. The
   full-width rows are left to their hover colour: 2% of a 1280px row is 25px
   of movement, which reads as the page lurching, not a press. */
.primary-button-link, .secondary-button-link, .menu-button { transition: scale 120ms var(--re-ease); }
@media (prefers-reduced-motion: no-preference) {
  :is(.primary-button-link, .secondary-button-link, .re-submit, .menu-button):active:not(:disabled) { scale: .96; }
}

/* The FAQ accordion was the last thing still on the old clock: half a second
   for the +/- icon to cross-fade, so the control looked like it had not
   registered the click, and 0.4s for the panel. Height is not a cheap property
   to animate, but rebuilding the accordion to animate grid-template-rows is a
   bigger change than this is worth — 260ms is short enough that it does not
   matter here. */
/* THE +/- ICON is drawn in CSS the way the menu icon is: two 1.5px lines in
   currentColor. Opening a question turns the vertical line a quarter-turn
   onto the horizontal one, so the plus becomes a minus. A transition, so a
   second click mid-turn reverses it. It was two image files (plus.svg and
   plus-none.svg) in a fixed #2A2A28 at a 2px stroke until 2026-09-27. */
.re-acc-icon span { position: absolute; left: 5px; top: calc(50% - 0.75px); width: 14px; height: 1.5px; border-radius: 1px; background-color: currentColor; transition: transform 260ms var(--re-ease); }
.re-acc-icon span:nth-child(2) { transform: rotate(90deg); }
[data-accordion].is-open .re-acc-icon span:nth-child(2) { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .re-acc-icon span { transition: none; } }

/* INNER-PAGE ENTRANCES on the homepage's clock. The template flew every inner
   headline in from half size with a 10-degree skew while zooming the whole
   hero, photo and text together, from 150%. Now the wrapper stays still, the
   photo settles from 1.04 and the text rises 12px: the same two moves as the
   homepage and the /go/ pages (2026-09-27, better-ui). styles.css already
   switches both off under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .js .hero-zoom { animation: none; }
  .js .hero-zoom .re-hero-img { animation: re-settle 1400ms var(--re-ease) both; }
  .js .hero-intro { animation: re-rise 600ms var(--re-ease) backwards; }
}
@keyframes re-settle { from { transform: scale(1.04); } to { transform: none; } }
@keyframes re-rise { from { opacity: 0; transform: translateY(12px); } }

/* Inline photographs on paper get a neutral ring just inside the edge, so a
   bright sky doesn't bleed into the ground. Pure black at 10%, never a tinted
   neutral. Full-bleed heroes don't need it. */
.re-owner-portrait img { outline: 1px solid rgb(0 0 0 / 0.1); outline-offset: -1px; }
/* A closed panel is only 0px tall, which hides the answer from the eye but
   not from a screen reader — every answer was read out under a button saying
   "collapsed". `visibility` takes it out of the accessibility tree too, and is
   delayed on close so the text stays painted while the height runs out. */
.accordion-description-wrap { visibility: hidden; transition: height 260ms var(--re-ease), visibility 0s linear 260ms; }
[data-accordion].is-open .accordion-description-wrap { visibility: visible; transition: height 260ms var(--re-ease), visibility 0s; }

/* THE FAILSAFE HAS TO BE RESTATED HERE. The template declares its
   prefers-reduced-motion block inside styles.css, and everything above loads
   after it at equal specificity — so without this, the overrides would win and
   reduced motion would silently stop working. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal-up, .js .reveal-left, .js .reveal-right, .js .reveal-grow,
  .js .reveal-grow-big, .js .reveal-blur, .js .reveal-blur-fast,
  .js .reveal-heading .animation-content {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
  .accordion-description-wrap,
  [data-accordion].is-open .accordion-description-wrap { transition: none; }
}

/* ===========================================================================
   THE CLOSING CALL TO ACTION, LEFT-ALIGNED — 2026-09-22.

   Every section on this site is left-aligned and sits on a rule. The template's
   two closing treatments were the exception: `.strengthen-wrap` (the ink band
   on nine pages) and `.inner-hero-wrap.center.cta` (the homepage photograph)
   both centre their text in a 638–683px column. It read as a section borrowed
   from another site, and it is the last thing anyone sees before deciding
   whether to call.

   The measure that the centring provided is kept — it just comes from the type
   now rather than from a centred column, so the heading holds ~26 characters
   and the line under it 62, the same as every other heading and lede.
   =========================================================================== */
.section.strengthen .strengthen-wrap,
.re-cta .inner-hero-wrap.center.cta {
  text-align: left;
  align-items: flex-start;
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}
.section.strengthen .strengthen-title-box .large-title,
.re-cta .inner-hero-title-box.cta .large-title { max-width: var(--re-measure-heading); }
.section.strengthen .strengthen-button-box,
.re-cta .cta-button-box { justify-content: flex-start; }
/* .re-cta is a flex row, so its .container is a flex item and shrink-wraps to
   whatever the text measures — which, once the centring came off, left the
   whole block floating in the middle of the frame on a 376px margin. It has to
   claim the line before its contents can sit at the page's left edge. */
.re-cta .container { width: 100%; }

/* The hamburger was a 30px target — the only way to navigate on a phone, and
   under the 44px floor. The button had no size of its own; it was as tall as
   the icon inside it. The icon is unchanged, the hit area is not.

   SCOPED TO THE TEMPLATE'S OWN BREAKPOINT ON PURPOSE. The template hides this
   button at desktop and only gives it `display: flex` under 992px, so setting
   display here unconditionally puts a hamburger next to the desktop nav. Only
   the size is ours; the showing and hiding stays the template's. */
@media (max-width: 991px) {
  .menu-button {
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
  }
}

/* ===========================================================================
   NO ALTERNATING BACKGROUNDS (Brisso, 2026-10-18, colour review option 1).
   Every section sits on paper; the white/paper alternation was 1.09:1, too
   faint to read as structure. A hairline marks where one section ends.
   None under the page head or a photo hero, and none against an ink band,
   which has its own edge. Raised things (tiles, cards, form panels) are white.

   THE TWO ORANGES, one job each: --color--primary-color (#f37321) fills what
   you press and what a chart shows; --color--accent-text (#b54e00) is small
   text only (eyebrows, numbers, tags). Icons are ink. The tokens were named
   "copper" until the same day.
   =========================================================================== */
main > section.section + section.section { border-top: 1px solid var(--color--line-gray); }
main > section.section:is(.padding-none, .is-hero, .strengthen, .re-steps, .re-cta) + section.section,
main > section.section + section.section:is(.strengthen, .re-steps, .re-cta) { border-top: 0; }

/* ===========================================================================
   SPLIT-LETTER HEADLINES (2026-10-18, borrowed from STUDIO.ARCH for the live
   site). src/scripts/split-reveal.ts wraps each letter of a heading marked
   data-split-reveal; the letters rise out of their word's clip, one after
   another, once the reveal observer marks the heading is-visible. Words never
   break across lines. Still under reduced motion (the script doesn't split)
   and when the script fails (no-js-fallback).
   =========================================================================== */
.sr-w { display: inline-block; white-space: nowrap; overflow: hidden; vertical-align: top; padding-bottom: .08em; margin-bottom: -.08em; }
.sr-c { display: inline-block; }
@media (prefers-reduced-motion: no-preference) {
  html.js:not(.no-js-fallback) .is-split .sr-c { transform: translateY(112%); transition: transform .75s cubic-bezier(.16, .84, .3, 1) calc(var(--i) * 14ms + 60ms); }
  html.js:not(.no-js-fallback) .is-split.is-visible .sr-c { transform: none; }
}
