/* ==========================================================================
   Polk Sites - polksites.com

   Harbor, dark, on the Mesh ground. Chosen 2026-08-29 out of three palettes
   and three treatments of Harbor; the losing options and the switcher that
   compared them are gone, along with the colour-mode toggle. The page is dark,
   and that is not a runtime choice.

   The cursor is drawn as an inline SVG data URI so it follows the palette and
   stays crisp at any resolution.

   Type scale, the whole list. Four text sizes, five display clamps, and one
   exception (form inputs at exactly 16px, or iOS zooms on focus):
     13  the EN/ES toggle
     15  nav, small buttons, captions, notes, footer
     17  body and anything carrying the argument
     20  h3
   ========================================================================== */

@font-face {
  font-family: 'Archivo';
  src: url('assets/fonts/archivo-var-latin.woff2') format('woff2-variations');
  font-weight: 700 800;   /* 700 for h2/h3, 800 for the display */
  font-display: swap;
}
@font-face {
  font-family: 'Geist';
  src: url('assets/fonts/geist-var-latin.woff2') format('woff2-variations');
  font-weight: 400 600;   /* 300 was declared and never painted */
  font-display: swap;
}

/* --- constants shared by all three palettes ----------------------------- */
:root {
  --font-display: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body:    'Geist', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --t-xs:    0.8125rem;
  --t-sm:    0.9375rem;
  --t-body:  1.0625rem;
  --t-lg:    1.25rem;
  --t-lede:  clamp(1.125rem, 1.6vw, 1.35rem);
  --t-h2:    clamp(1.75rem, 3.1vw, 2.6rem);
  --t-h1:    clamp(2.4rem, 5.4vw, 4.1rem);
  --t-fig:   clamp(1.9rem, 3vw, 2.5rem);

  /* Line height is a role, not a constant. Text that never wraps wants a tight
     box; helper text wants a snug one; running prose wants room. Everything
     used to be 1.6, which made a nav link sit like a paragraph. */
  --lh-tight: 1.25;
  --lh-snug:  1.45;
  --lh-prose: 1.6;

  --wrap: 1160px;
  --gut: clamp(1.25rem, 4vw, 2.75rem);
  /* Section rhythm. One value everywhere gave every section the same
     weight; these give the page a cadence -- the stat strip is a beat, the
     content sections are the bars, and the close is the widest interval. */
  --block-tight: clamp(2rem, 4vw, 3.5rem);
  --block:       clamp(3.5rem, 7vw, 7rem);
  --block-loose: clamp(4.5rem, 8.5vw, 8.5rem);
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-pill: 999px;
  --bar: 68px;
  --ease: cubic-bezier(.16, 1, .3, 1);

  /* Spacing scale. Every padding, margin and gap in this file comes
     from here. A 4px base, because an 8-only scale has no useful step
     between 8 and 16 and you end up inventing 0.85rem again. */
  --s-4: 0.25rem;
  --s-8: 0.5rem;
  --s-12: 0.75rem;
  --s-16: 1rem;
  --s-20: 1.25rem;
  --s-24: 1.5rem;
  --s-28: 1.75rem;
  --s-32: 2rem;
  --s-36: 2.25rem;
  --s-40: 2.5rem;
  --s-44: 2.75rem;
  --s-48: 3rem;
  --s-52: 3.25rem;
  --s-56: 3.5rem;
  --s-64: 4rem;
  --s-88: 5.5rem;
  --s-96: 6rem;
  --s-112: 7rem;

  /* Form errors. This used to be a hardcoded red with a
     [data-theme='dark'] override -- which only fired when the visitor had
     explicitly toggled dark, not when their OS was simply set to it. On
     harbor's dark surface that was 2.3:1. A token flips in both cases. */
  --danger:#FF9C93;
}

/* ---------- the palette ------------------------------------------------
   Harbor, dark. These were the [data-theme='dark'] overrides while three
   palettes were compared. They are simply the palette now. */
:root {
  --paper:#0E1B2E; --surface:#16263C; --raised:#1B2E47;
  --ink:#EAF1F8; --muted:#A3B6CB; --line:#24374F;
  /* The hero is the only section with no background of its own, so its small
     text sits on the moving ground at full strength: 5.06:1, the thinnest
     margin on the page, on a product read outdoors in Florida sun. This is
     Night Body lifted toward Night Ink for that one place. */
  --muted-hi:#C7D6E6;
  --brand:#22D3EE; --on-brand:#06202B; --spark:#67E8F9;
  --glow: rgba(34, 211, 238, .12);
  --mark-a:#5B7FA8; --mark-b:#22D3EE;
}

/* ==========================================================================
   PALETTE NOTES
   Every palette defines the same eleven tokens, so nothing downstream needs
   to know which one is active.
     --paper    page ground        --ink      headings and strong text
     --surface  raised card        --muted    running text
     --raised   quiet fill         --line     hairlines
     --brand    the one accent that carries interactive weight
     --on-brand text on --brand
     --spark    the bright mark colour: logo, rings, small flourishes
     --glow     a low-alpha wash of the brand, for fields and halos
     --cursor / --cursor-tap  see CURSORS below
   ========================================================================== */

/* a ring with a filled centre, like a signal */
:root {
  --cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28'%3E%3Ccircle cx='14' cy='14' r='9' fill='none' stroke='%23FFFFFF' stroke-width='4' opacity='.85'/%3E%3Ccircle cx='14' cy='14' r='9' fill='none' stroke='%23132B4A' stroke-width='2'/%3E%3Ccircle cx='14' cy='14' r='2.5' fill='%2322D3EE'/%3E%3C/svg%3E") 14 14, auto;
  --cursor-tap: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28'%3E%3Ccircle cx='14' cy='14' r='11' fill='none' stroke='%23FFFFFF' stroke-width='4' opacity='.85'/%3E%3Ccircle cx='14' cy='14' r='11' fill='%2322D3EE' fill-opacity='.22' stroke='%23132B4A' stroke-width='2'/%3E%3Ccircle cx='14' cy='14' r='3.5' fill='%23132B4A'/%3E%3C/svg%3E") 14 14, pointer;
}

body { cursor: var(--cursor); }
a, button, summary, label, [role='button'] { cursor: var(--cursor-tap); }
input, textarea, select { cursor: text; }
:disabled { cursor: not-allowed; }

@media (hover: none) {
  body, a, button, summary, label, [role='button'] { cursor: auto; }
}

/* ==========================================================================
   BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--bar) + 16px);
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-prose);
  -webkit-font-smoothing: antialiased;
  transition: background-color .35s var(--ease), color .35s var(--ease);
}
img, svg { display: block; max-width: 100%; }
h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.028em;
  color: var(--ink);
}
/* 800 against h2/h3 at 700: the display step is a weight step too. 1.05
   rather than 1.03 because Spanish stacks accents on a four-line h1. */
h1 { font-size: var(--t-h1); font-weight: 800; line-height: 1.05; letter-spacing: -0.035em; }
h2 { font-size: var(--t-h2); line-height: 1.1; }
h3 { font-size: var(--t-lg); line-height: 1.25; letter-spacing: -0.02em; }
p { margin: 0; }
a { color: inherit; }
::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px; }
:root { scrollbar-color: var(--line) transparent; }
input, textarea { caret-color: var(--brand); }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.i { width: 20px; height: 20px; fill: currentColor; flex: none; }
.i-lg { width: 26px; height: 26px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--brand); color: var(--on-brand);
  padding: var(--s-12) var(--s-20); border-radius: 0 0 var(--r-sm) 0; text-decoration: none;
}
.skip:focus { left: 0; }

/* ==========================================================================

/* ==========================================================================
   HEADER
   ========================================================================== */
/* backdrop-filter makes this a containing block for any fixed descendant, and
   the palette switcher lives in here. Above 900 that is fine -- the switcher is
   static in the header. Below 900 it has to escape to the viewport corner, so
   the blur is scoped to the sizes that can afford it. */
.site-head {
  position: sticky; top: 0; z-index: 40;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
/* Translucency and the blur that pays for it are gated together. They used to
   be separate: the background was made see-through unconditionally while the
   blur only engaged at 900px, so every phone read the page through an
   unblurred 86% band. */
@media (min-width: 900px) {
  .site-head {
    background: color-mix(in srgb, var(--paper) 88%, transparent);
    backdrop-filter: blur(12px);
  }
}
.head-inner { display: flex; align-items: center; gap: clamp(var(--s-12), 2vw, var(--s-32)); height: var(--bar); }
.brand {
  display: inline-flex; align-items: center; min-height: 44px; gap: var(--s-8);
  text-decoration: none; font-family: var(--font-display); font-weight: 700;
  letter-spacing: -0.02em; color: var(--ink);
}
.brand-mark { width: 26px; height: 26px; flex: none; }
.mark-a { stroke: var(--mark-a); }
.mark-b { stroke: var(--mark-b); }
.brand-name { font-size: var(--t-body); white-space: nowrap; }

.head-nav { display: none; gap: var(--s-20); margin-inline-start: auto; font-size: var(--t-sm); }
.head-nav a {
  white-space: nowrap; line-height: var(--lh-tight);
  display: inline-flex; align-items: center; min-height: 44px;
  text-decoration: none; color: var(--muted); transition: color .15s;
}
.head-nav a:hover { color: var(--ink); }
/* Nothing in the control cluster may be shrunk by flex. Adding the three
   Harbor treatments overfilled the header, and the first thing flex gave up
   was the logo -- it rendered at 5.9px instead of 26 before this. */
.brand, .lang, .head-cta, .head-wa { flex: none; }

/* The nav needs 1200 now, not 1000. At exactly 900 the Spanish CTA ("Solicita
   Estimado", 156px) plus brand, language, three swatches and the theme button
   already fill the row -- adding three nav links there put the header 23px over. */
@media (min-width: 1200px) { .head-nav { display: flex; } }

.head-tools { display: flex; align-items: center; gap: var(--s-8); margin-inline-start: auto; }
.head-nav + .head-tools { margin-inline-start: 0; }

.lang { display: inline-flex; align-items: center; padding: 2px; gap: 2px; background: var(--raised); border-radius: var(--r-pill); }
/* This is two links now, one per served page, not two buttons that swap
   text. The selected one used to be --surface on --raised: 1.11:1, the
   lowest-contrast control on a page whose whole wedge is that half the
   audience reads Spanish. Cyan is what this page means by interactive. */
.lang-btn {
  text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding: 0 var(--s-8);
  border: 0; background: transparent; color: var(--muted);
  font-family: var(--font-body); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: .04em; border-radius: var(--r-pill);
}
.lang-btn.is-on { background: var(--surface); color: var(--on-brand); }

/* ==========================================================================
   BUTTONS
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-8);
  min-height: 50px; padding: var(--s-12) var(--s-24);
  border: 1px solid transparent; border-radius: var(--r-pill);
  font-family: var(--font-body); font-size: var(--t-body); font-weight: 500;
  line-height: 1.1; text-decoration: none; white-space: nowrap;
  transition: transform .18s var(--ease), background-color .18s, border-color .18s, box-shadow .18s;
}
.btn-primary { background: var(--brand); color: var(--on-brand); }
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 22px var(--glow); }
.btn-primary:active { transform: translateY(1px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--ink); transform: translateY(-1px); }
.btn-ghost:active { transform: translateY(1px); }
.btn-sm { padding: var(--s-8) var(--s-16); font-size: var(--t-sm); min-height: 44px; }
.btn-block { display: flex; width: 100%; }
/* Outlined, not filled. Palmetto's brand IS green, so a filled WhatsApp
   button sat beside the primary and the two read as one control. The glyph
   carries the recognition instead, and this then behaves the same in all
   three palettes. */
@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:hover, .btn:active { transform: none; }
}

/* Which header button is on duty at which width. These live AFTER .btn on
   purpose: media queries add no specificity, so when they sat above it
   `.btn { display: inline-flex }` won on source order and neither button ever
   hid. That is what pushed the header 12px past a 390px viewport. */
@media (max-width: 719px) { .head-cta { display: none; } }
@media (min-width: 720px) { .head-wa { display: none; } }
/* "Llama" is wider than "Call" and put the header 3px over at 320. The gate
   was set at 460, which cost the label on every phone from 360 to 460 to
   solve a problem that only existed at the bottom of the range. Measured: the
   Spanish label fits from 380 up. Below that the icon carries it, and
   aria-label already names the action in both languages. */
@media (max-width: 379px) {
  .head-wa span { position: absolute; width: 1px; height: 1px; overflow: hidden;
                  clip-path: inset(50%); white-space: nowrap; }
  .head-wa { width: 44px; padding: 0; }
}

/* ==========================================================================
   HERO
   The eye-catcher. No screenshot of anybody's site: a headline set as large as
   the viewport allows, and an abstract device that shows the one behaviour the
   whole pitch rests on -- a number you tap.
   ========================================================================== */
.hero { position: relative; overflow: hidden; padding-block: clamp(var(--s-44), 6vw, var(--s-88)) clamp(var(--s-48), 6vw, var(--s-96)); }
.hero-grid > * { min-width: 0; }
.hero-grid {
  position: relative; z-index: 1;
  display: grid; gap: clamp(var(--s-36), 5vw, var(--s-56)); align-items: center;
}
@media (min-width: 860px) { .hero-grid { grid-template-columns: 1.06fr .94fr; } }

.hero h1 { max-width: 15ch; text-wrap: balance; }
.hero h1 em { font-style: normal; color: var(--brand); }
.lede { margin-top: var(--s-20); color: var(--muted); font-size: var(--t-lede); max-width: 44ch; }
.hero-cta { display: flex; flex-wrap: wrap; gap: var(--s-12); margin-top: var(--s-32); }
.hero-tags {
  display: flex; flex-wrap: wrap; gap: var(--s-8) var(--s-16); margin-top: var(--s-24);
  font-size: var(--t-sm); color: var(--muted-hi);
}
.hero-tags span { display: inline-flex; align-items: center; gap: var(--s-8); }
.hero-tags .i { width: 16px; height: 16px; color: var(--brand); }

/* --- the abstract device ------------------------------------------------ */
/* The box wraps the artwork exactly. It used to be a centring grid while the
   svg carried the 470px cap, so above that width the box was wider than the
   drawing, and anything positioned against the box in percentages drifted off
   the shape it was meant to sit on. The viewBox gives the svg its aspect, so
   no min-height is needed to reserve the space. */
.hero-art { position: relative; width: min(100%, 470px); margin-inline: auto; }
.hero-art svg { display: block; width: 100%; height: auto; overflow: visible; }
.ring { fill: none; stroke: var(--brand); opacity: .18; }
.phone-shell { fill: var(--surface); stroke: var(--line); stroke-width: 2; }
.phone-head { fill: var(--brand); }
.bar-line { fill: var(--line); }
.bar-line.short { opacity: .7; }
.tap-pill { fill: var(--brand); }
.tap-text { fill: var(--on-brand); font-family: var(--font-body); font-weight: 600; font-size: 15px; }
.notch { fill: var(--line); }
.spark-dot { fill: var(--spark); }
.win-dot { fill: var(--line); }

/* ---- the focal moment -------------------------------------------------
   The device assembles once: shell, content painting top to bottom, then the
   call button landing last and hardest. Only the art moves -- no line of copy
   is ever hidden waiting on this, so a failed stylesheet costs nothing. */
@keyframes hero-part-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-tap-land {
  0%   { opacity: 0; transform: translateY(18px) scale(.94); }
  70%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}
@keyframes pulse-ring {
  0%   { transform: scale(.72); opacity: .30; }
  70%  { opacity: 0; }
  100% { transform: scale(1.18); opacity: 0; }
}
.hero-art [data-in] {
  transform-box: fill-box; transform-origin: center;
  animation: hero-part-in 460ms var(--ease) both;
  animation-delay: calc(110ms + var(--i, 0) * 26ms);
}
/* Paused until the art is actually on screen, and only when JS is there to
   unpause it. Delays survive the pause, so the stagger arrives intact. */
html.js .hero-art:not(.is-seen) [data-in],
html.js .hero-art:not(.is-seen) .tap-group { animation-play-state: paused; }
/* The call pill is a real link, laid over the artwork rather than inside it.
   x 290..434 of a 460 viewBox and y 268..348 of 420. The svg box matches the
   viewBox aspect exactly, so these track the drawn pill at every width. */
.tap-link {
  position: absolute;
  left: 63.043%; top: 63.810%; width: 31.304%; height: 19.048%;
  border-radius: var(--r-pill);
}
.tap-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; }
.hero-art:has(.tap-link:hover) .tap-pill { fill: var(--spark); }
.tap-group {
  transform-box: fill-box; transform-origin: center;
  animation: hero-tap-land 620ms var(--ease) both;
  animation-delay: 760ms;
}
.ring-a, .ring-b, .ring-c {
  transform-box: fill-box; transform-origin: center;
  animation: pulse-ring 4.2s var(--ease) infinite;
  animation-delay: calc(1180ms + var(--i, 0) * 1400ms);
}
/* a loop that nobody is looking at is just battery. */
.hero-art.is-idle .ring-a,
.hero-art.is-idle .ring-b,
.hero-art.is-idle .ring-c { animation-play-state: paused; }

/* ---- continuity: the palette swap ------------------------------------
   The one interaction this page exists for. Every colour crosses over
   together rather than snapping. Broad, but it lives for 420ms and only while
   the palette is fixed. */

/* ---- continuity: the FAQ --------------------------------------------- */
.faq-body { overflow: hidden; }

/* ---- feedback: the language knob -------------------------------------- */
.lang { position: relative; }
.lang::before {
  content: ''; position: absolute; z-index: 0;
  top: 2px; left: 2px; width: 44px; height: 44px;
  border-radius: var(--r-pill); background: var(--brand);
  transition: transform 260ms var(--ease);
}
.lang[data-on='es']::before { transform: translateX(46px); }
.lang-btn { position: relative; z-index: 1; width: 44px; padding: 0; }
.lang-btn.is-on { background: transparent; }

/* ---- feedback: an error should arrive, not blink into being ----------- */
@keyframes err-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.err { animation: err-in 200ms var(--ease) both; }

/* ==========================================================================
   PROOF BAND
   ========================================================================== */
.band { background: var(--surface); border-block: 1px solid var(--line); padding-block: var(--block-tight); }
.band-grid { display: grid; gap: clamp(var(--s-28), 3vw, var(--s-40)); }
@media (min-width: 760px) { .band-grid { grid-template-columns: repeat(3, 1fr); } }
.band-item { border-top: 2px solid var(--brand); padding-top: var(--s-16); }
.band-fig { font-family: var(--font-display); font-weight: 700; font-size: var(--t-fig); letter-spacing: -0.03em; line-height: 1; color: var(--ink); }
.band-txt { margin-top: var(--s-8); color: var(--muted); font-size: var(--t-sm); max-width: 34ch; }

/* ==========================================================================
   SECTIONS
   ========================================================================== */
.sec-head { max-width: 46ch; margin-bottom: clamp(var(--s-32), 4vw, var(--s-48)); }
.sec-head p { margin-top: var(--s-16); color: var(--muted); }
.included, .process, .faq { padding-block: var(--block); }
.contact { padding-block: var(--block-loose); }

/* The gutter belongs to the section, not to the wrap inside it, so a section
   with its own background has real inset on every side. */
section { padding-inline: var(--gut); }
section > .wrap { padding-inline: 0; }
.process { background: var(--surface); border-block: 1px solid var(--line); }
.faq { background: var(--raised); }

/* --- what you get ------------------------------------------------------- */
.bento { display: grid; gap: var(--s-16); }
@media (min-width: 700px) { .bento { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .bento { grid-template-columns: repeat(3, 1fr); } }
.cell {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: clamp(var(--s-24), 2.4vw, var(--s-32));
}
.cell h3 { margin-top: var(--s-16); }
.cell p { margin-top: var(--s-8); color: var(--muted); font-size: var(--t-sm); }
.cell .i-lg { color: var(--brand); }
/* Six equal cells. The lead cell used to span two columns, which left one
   cell alone on the last row at both 1440 (2/3/1) and 768 (1/2/2/1). Equal
   cells divide cleanly at every breakpoint: 3x2, 2x3, 1x6. */
/* The bento cells are <article> except this one, which is an <a> that dials.
   Cyan is the page's promise that a thing is interactive, and this is the
   loudest cyan on the page; it had better keep that promise. */
a.cell { display: block; text-decoration: none; color: inherit; }
a.cell:hover { border-color: var(--brand); }
.cell-brand { background: var(--brand); border-color: var(--brand); }
a.cell-brand:hover { background: var(--spark); border-color: var(--spark); }
a.cell-brand:active { transform: translateY(1px); }
.cell-try {
  display: block; margin-top: var(--s-16);
  font-size: var(--t-sm); font-weight: 500; text-decoration: underline;
  text-underline-offset: 3px;
}
.cell-brand h3, .cell-brand p, .cell-brand .i-lg, .cell-brand .cell-try { color: var(--on-brand); }

/* --- how it works ------------------------------------------------------- */
.process-grid { display: grid; gap: clamp(var(--s-32), 4vw, var(--s-56)); }
.process-grid > * { min-width: 0; }
@media (min-width: 900px) { .process-grid { grid-template-columns: .85fr 1.15fr; align-items: start; } }
@media (min-width: 900px) { .process-head { position: sticky; top: calc(var(--bar) + 2rem); } }
.process-head p { margin-top: var(--s-16); color: var(--muted); max-width: 34ch; }
.process-head .btn { margin-top: var(--s-24); }
.process-list { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.process-list li { padding-block: var(--s-24); border-top: 1px solid var(--line); }
.process-list li:first-child { border-top-color: var(--ink); }
.process-list li::before {
  /* DESIGN.md bans 01 02 03 by name. These are a real sequence in an <ol>,
     not a decorative eyebrow, so the digit stays and the tracked zero-padded
     styling that made it read as one goes. */
  counter-increment: step; content: counter(step);
  display: block; margin-bottom: var(--s-8);
  font-family: var(--font-display); font-weight: 700; font-size: var(--t-sm);
  color: var(--brand);
}
.process-list p {
  margin-top: var(--s-8); color: var(--muted); font-size: var(--t-sm);
  max-width: 54ch;   /* ran to 82ch at 1440 */
}

/* --- questions ---------------------------------------------------------- */
/* The cap used to sit on .wrap itself, which also carries margin-inline:
   auto, so the whole column centred at x=330 while every other section
   started at x=140. The cap belongs to the content, not to the container. */
.faq-wrap { max-width: var(--container, 1160px); }
.faq-wrap > h2, .faq-list { max-width: 780px; }
.faq-list { margin-top: clamp(var(--s-24), 3vw, var(--s-36)); }
.faq-list details { border-top: 1px solid var(--line); }
.faq-list details:last-child { border-bottom: 1px solid var(--line); }
.faq-list summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-16);
  padding-block: var(--s-20); min-height: 44px;
  font-family: var(--font-body); font-size: var(--t-body); font-weight: 500;
  line-height: var(--lh-tight); max-width: 46ch;   /* ran to 87ch at 1440 */
  color: var(--ink); list-style: none;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary .i { color: var(--muted); transition: transform .2s var(--ease); }
.faq-list details[open] summary .i { transform: rotate(180deg); }
.faq-list p { padding-bottom: var(--s-20); color: var(--muted); font-size: var(--t-sm); max-width: 54ch; }

/* --- contact ------------------------------------------------------------ */
.contact-grid { display: grid; gap: clamp(var(--s-32), 4vw, var(--s-56)); }
.contact-grid > * { min-width: 0; }
.direct strong { min-width: 0; overflow-wrap: anywhere; }
@media (min-width: 900px) { .contact-grid { grid-template-columns: 1fr 1fr; } }
.contact-copy p { margin-top: var(--s-16); color: var(--muted); max-width: 40ch; }
.contact-direct { margin-top: var(--s-32); display: grid; gap: var(--s-8); }
.direct {
  display: flex; align-items: center; gap: var(--s-12);
  min-height: 60px; padding: var(--s-12) var(--s-16);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  text-decoration: none; color: var(--ink);
  transition: border-color .18s, transform .18s var(--ease);
}
.direct:hover { border-color: var(--brand); transform: translateX(2px); }

/* One number, two actions. The row dials; the button on its right end opens a
   message. The row is a <div> rather than an <a> because an anchor cannot
   contain an anchor, so .direct-main carries the padding and the whole row
   still reads as one control. */
.direct-split { padding: 0; gap: 0; }
.direct-main {
  display: flex; align-items: center; gap: var(--s-12);
  flex: 1; min-width: 0; min-height: 58px; padding: var(--s-12) var(--s-16);
  text-decoration: none; color: inherit; border-radius: var(--r-sm) 0 0 var(--r-sm);
}
/* This was a grey glyph on the row's own background with no label, so the
   row promised two actions and only one of them looked like an action. */
.direct-sms {
  display: flex; align-items: center; justify-content: center; gap: var(--s-8);
  width: 56px; min-height: 58px; flex: none; align-self: stretch;
  border-left: 1px solid var(--line);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--raised); color: var(--brand); text-decoration: none;
  transition: background-color .18s, color .18s;
}
/* This label was gated to 720px and up, which put it on the one device class
   that did not need it: on a phone the row still said "Call or text" next to
   an unlabelled glyph. It shows at every width now. */
.sms-k { font-size: var(--t-xs); font-weight: 500; letter-spacing: .04em; }
.direct-sms { width: auto; padding-inline: var(--s-12); }
@media (min-width: 720px) { .direct-sms { padding-inline: var(--s-16); } }
.direct-sms:hover { background: var(--brand); color: var(--on-brand); }
.direct-split:hover { transform: none; }
.direct-main:hover ~ .direct-sms { border-left-color: var(--brand); }

/* A phone number is one token and must never break: `overflow-wrap: anywhere`
   was added for the email address and was splitting (407) 720-4434 across
   three lines at 320px. */
.direct-split strong { white-space: nowrap; overflow-wrap: normal; }

/* Under 420 the label and the value stop sharing a line and stack beside the
   icon instead, which is the only way the number fits whole. */
@media (max-width: 420px) {
  .direct-main, a.direct {
    display: grid; grid-template-columns: auto 1fr;
    column-gap: var(--s-12); align-items: center;
  }
  .direct-main > .i, a.direct > .i { grid-row: 1 / 3; }
  .direct-k { min-width: 0; grid-column: 2; }
  .direct-main > strong, a.direct > strong { grid-column: 2; }
}
.direct .i { color: var(--brand); }
/* A field label, so it is set like one: the small step plus tracking. At
   15px/400 it read as more body copy sitting next to the value. */
.direct-k {
  font-size: var(--t-xs); letter-spacing: .04em; line-height: var(--lh-tight);
  color: var(--muted); min-width: 4.5rem;
}
.direct strong { font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .direct:hover { transform: none; } }

.form { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: clamp(var(--s-24), 3vw, var(--s-32)); }
.field { margin-bottom: var(--s-16); scroll-margin-top: calc(var(--bar) + 20px); }
.field label {
  display: block; font-size: var(--t-sm); font-weight: 500;
  line-height: var(--lh-tight); color: var(--ink); margin-bottom: var(--s-8);
}
.field input, .field textarea {
  width: 100%; padding: var(--s-12) var(--s-16); min-height: 50px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: 16px;   /* exactly 16px, or iOS zooms the page on focus */
  color: var(--ink);
  transition: border-color .18s;
}
.field textarea { min-height: 104px; resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--brand); }
.field [aria-invalid='true'] { border-color: var(--danger); }
.hint { margin-top: var(--s-8); font-size: var(--t-sm); line-height: var(--lh-snug); color: var(--muted); }
.err { margin-top: var(--s-8); font-size: var(--t-sm); line-height: var(--lh-snug); color: var(--danger); }
.form-msg { margin-top: var(--s-16); font-size: var(--t-sm); line-height: var(--lh-snug); color: var(--muted); }
/* A result the visitor worked for should not be 15px of grey. Success and
   failure both get weight and a colour that means something. */
.form-msg.is-ok, .form-msg.is-bad {
  font-size: var(--t-body); font-weight: 500;
  padding: var(--s-12) var(--s-16); border-radius: var(--r-sm);
  border: 1px solid;
}
.form-msg.is-ok  { color: var(--ink); border-color: var(--brand); background: var(--raised); }
.form-msg.is-bad { color: var(--danger); border-color: var(--danger); background: transparent; }
/* One primary at a time. While the fallback is up, the submit that failed
   is a ghost, so the cyan in the block belongs to the route that works. */
.form.has-fallback #quote-submit {
  background: transparent; color: var(--ink); border-color: var(--line);
}
.form.has-fallback #quote-submit:hover { border-color: var(--brand); }
.form-fallback { display: flex; flex-wrap: wrap; gap: var(--s-12); margin-top: var(--s-16); }
/* .btn is nowrap, and flex-wrap cannot shrink a single button below its own
   nowrap width. "Llama o escribe al (407) 720-4434" does not fit the form
   column at 320, so the page grew 78px wider than the phone. These two wrap,
   full width, and only the number itself is held together. */
.form-fallback .btn {
  flex: 1 1 100%; min-width: 0; max-width: 100%;
  white-space: normal; text-align: center; line-height: var(--lh-tight);
}
.form-fallback[hidden] { display: none; }
#quote-submit[aria-busy='true'] { opacity: .7; }
.botcheck { position: absolute; left: -9999px; opacity: 0; height: 0; width: 0; font-size: inherit; }

/* --- the no-JavaScript thank-you page ---------------------------------- */
.thanks { display: flex; align-items: center; min-height: 100vh; padding-block: var(--block); }
.thanks-wrap { max-width: 46ch; }
.thanks .brand-mark { width: 40px; height: 40px; margin-bottom: var(--s-32); }
.thanks h1 { font-size: var(--t-h2); }
.thanks .lede { max-width: none; }
.thanks .lede[lang='es'] { font-size: var(--t-body); color: var(--muted); }
.thanks .lede[lang='es'] strong { color: var(--ink); font-weight: 600; }
.thanks-cta { display: flex; flex-wrap: wrap; gap: var(--s-12); margin-top: var(--s-32); }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.site-foot { border-top: 1px solid var(--line); padding-block: clamp(var(--s-40), 5vw, var(--s-64)) var(--s-112); font-size: var(--t-sm); }
@media (min-width: 720px) { .site-foot { padding-bottom: var(--s-96); } }
.foot-grid { display: grid; gap: var(--s-36); }
@media (min-width: 800px) { .foot-grid { grid-template-columns: 2fr 1.4fr 1fr; } }
.foot-brand .brand-mark { width: 30px; height: 30px; }
.foot-name { margin-top: var(--s-12); font-family: var(--font-display); font-weight: 700; color: var(--ink); }
.foot-slogan { margin-top: var(--s-4); color: var(--muted); max-width: 30ch; }
.foot-h { font-weight: 500; color: var(--ink); margin-bottom: var(--s-8); }
.foot-col p { color: var(--muted); max-width: 34ch; }
.foot-col a {
  display: inline-flex; align-items: center; min-height: 44px;
  line-height: var(--lh-tight); color: var(--muted); text-decoration: none;
}
.foot-col a:hover { color: var(--ink); }
.foot-bottom {
  display: flex; flex-wrap: wrap; gap: var(--s-8) var(--s-24); justify-content: space-between;
  margin-top: clamp(var(--s-40), 4vw, var(--s-56)); padding-top: var(--s-24);
  border-top: 1px solid var(--line); color: var(--muted); font-size: var(--t-sm);
}

/* the floating WhatsApp button, phone only -- the switcher owns the middle,
   so this sits right and clear of it */

/* ==========================================================================
   REDUCED MOTION
   Fewer and gentler, not switched off. Spatial movement and the loop go;
   colour and opacity feedback stay, because they are what tells you the
   palette changed, the answer opened, and the field was rejected.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-art [data-in], .tap-group {
    animation-name: none; opacity: 1; transform: none;
  }
  .ring-a, .ring-b, .ring-c { animation: none; opacity: .14; }
  .lang::before { transition: none; }
  .err { animation-name: none; }
  .btn:hover, .btn:active, .direct:hover { transform: none; }
}

/* Light type on a dark ground reads tighter than it measures: a touch more
   leading and a hair of tracking put it back. Applies to every dark path --
   the toggle, the OS preference, and Harbor/Deep, which is dark regardless. */
/* light type on a dark ground reads tighter than it measures */
body { line-height: 1.65; letter-spacing: .006em; }

/* ==========================================================================
   THE GROUND
   A slow cyan-and-blue field behind everything. Sections that carry text stay
   mostly opaque so the moving colour cannot eat their contrast; the hero has
   no section background, so that is where it reads at full strength.

   Every effect here is a layer behind or around content. No text colour is
   touched by any of them, so contrast cannot regress by construction.
   Animation is transform and opacity on fixed layers only -- never
   background-image, which is what makes gradient pages stutter.
   ========================================================================== */

.hx-ground {
  position: fixed; inset: -12%; z-index: -1; pointer-events: none;
  opacity: 0; transition: opacity 600ms var(--ease);
}

/* ---- mesh: the ground becomes the palette ---------------------------- */
.hx-ground {
  opacity: 1;
  /* Navy radials went muddy grey over a light ground, so the second colour is
     a clean mid blue instead. Alphas are up from the first pass, which barely
     read at all. */
  background:
    radial-gradient(36% 30% at 10% 8%,  rgba(34, 211, 238, .46), transparent 70%),
    radial-gradient(42% 34% at 90% 20%, rgba(59, 130, 246, .26), transparent 72%),
    radial-gradient(48% 36% at 22% 80%, rgba(34, 211, 238, .30), transparent 72%),
    radial-gradient(40% 32% at 82% 96%, rgba(59, 130, 246, .22), transparent 70%);
  animation: hx-mesh 22s ease-in-out infinite alternate;
}
/* the hero's own navy halo fights the ground here */
@keyframes hx-mesh {
  from { transform: translate3d(-3%, -2%, 0) scale(1) rotate(0deg); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.1) rotate(4deg); }
}
/* The ground reads at full strength in the hero, which has no section
   background of its own. Sections that carry text stay mostly opaque so the
   moving colour underneath cannot eat their contrast, and every surface is
   derived from the palette tokens rather than hardcoded white -- the first
   version of this used rgba(255,255,255,.55) and was unreadable the moment
   anyone opened it in dark mode.

   .cell-brand is excluded on purpose: it is the one cell that carries
   --on-brand text on --brand, and painting it white made that 1.00:1. */
.band,
.process {
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(7px);
}
/* .faq is the one raised surface. All three used to be overridden to the same
   paper mix here, which quietly deleted the tonal rhythm the sections declare
   above and left process -> faq as a hairline between two identical fields. */
.faq {
  background: color-mix(in srgb, var(--raised) 88%, transparent);
  backdrop-filter: blur(7px);
}
.cell:not(.cell-brand),
.form,
.direct { background: var(--surface); }

@media (prefers-reduced-motion: reduce) {
  .hx-ground { animation: none !important; }
}
