/* Thieves Market LLC — design system.
 *
 * Built to the Countryside Amish Furniture model, measured off the live site on
 * 2026-08-20 rather than copied by eye. Their numbers, which this file holds to:
 *
 *   Bands alternate #FFFFFF and #FCF8F1. Nothing else.
 *   Display face is a tall condensed uppercase (Bebas Neue), never the body face.
 *   H1 64px · section heads 52px · sub heads 32/28/25px · card titles 22px
 *   Body 20px / 32px line-height
 *   Buttons: 8px radius, 14px 28px padding, 14px uppercase, 1.12px tracking
 *
 * WHAT WE DELIBERATELY DO NOT COPY:
 *   1. Their button. White on their #ABC578 measures 1.91:1, which fails AA outright,
 *      and Brass Armadillo makes the identical mistake at 1.92:1. This whole trade
 *      puts white text on a pale accent. We take the shape and keep our own sage
 *      #4E6151, where white measures 6.66:1. Same button, readable.
 *   2. Their handwriting script accent. The twelve-site pass that produced our palette
 *      found the one reference using a script face was the weakest in the set.
 *
 * Our own measured palette carries over unchanged and every ratio improved on the
 * white ground: ink 12.28:1, ink-2 5.97:1, brass-text 5.63:1.
 * Brass NEVER carries small text: it is 1.91:1 as a fill and 4.25:1 on the deep green.
 * Rules, hairlines and hover only. On dark, use --brass-lt (5.99:1).
 */

/* ------------------------------------------------------------------ tokens */
:root {
  --ground: #FFFFFF;
  --band: #FCF8F1;
  --ink: #3A342E;
  --ink-2: #6B6259;
  --rule: #E7E0D5;
  --sage: #4E6151;
  --sage-dk: #3E4F42;
  --deep: #2A342C;
  --brass: #C08A2E;
  --brass-lt: #D9A94E;
  /* Brass for text over a PHOTOGRAPH, which is a harder ground than the flat --deep
     that --brass-lt was measured on. Measured 2026-08-31: the kicker in --brass-lt
     failed on 18 of the 18 photo headers, worst 3.58:1 against a 4.5 threshold, because
     a scrimmed pale showroom is lighter than #2A342C however heavy the gradient is.

     Lightening the TEXT is the right lever rather than darkening the scrim. The h1,
     lede and breadcrumb on those same headers all passed comfortably, so darkening 18
     photographs further to rescue one small label would be paying for a whole page with
     the wrong currency. */
  --brass-hi: #F0D08A;
  --brass-text: #8A5F16;
  --alert: #9C3226;
  --white: #fff;

  /* WIDER THAN THE REFERENCE, on purpose. Countryside sits at 1220px, which on a
     1920px monitor leaves the page occupying under two thirds of the screen and
     reading as a column floating in space. At 1480 it fills a wide screen properly
     while every block of running text keeps its own max-width below, so the LINES do
     not get longer even though the page does: .prose 38rem, .shead 52rem, .phead
     46rem, .hero__in 40rem. Widening the page must never widen a paragraph.

     94vw rather than a flat number so the gutters stay proportional between about
     1250px and 1575px instead of pinning to the padding. */
  --wrap: min(94vw, 1480px);
  --pad: 20px;
  --r: 8px;
  --display: 'Bebas Neue', 'Arial Narrow', system-ui, sans-serif;
  --body: 'Work Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --shadow: 0 1px 2px rgba(42, 52, 44, .04), 0 10px 28px rgba(42, 52, 44, .07);
}

/* ------------------------------------------------------------------ base */
*, *::before, *::after { box-sizing: border-box; }
/* The browser default is `[hidden] { display: none }` at specificity 0,1,0, which any
   class rule setting display beats. The homepage "just in" band ships hidden and is
   unhidden by functions/_middleware.ts only when there is something to show, so this
   has to hold whatever else is styling the element. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}
@media (min-width: 760px) { body { font-size: 19px; line-height: 1.6; } }

img, picture, svg { max-width: 100%; }
img { height: auto; display: block; border: 0; }

/* The display face is uppercase by design: it has no lowercase worth setting and the
   reference uses it in caps everywhere. Tracking opens it up at large sizes. */
h1, h2, h3, .display {
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: .96;
  letter-spacing: .012em;
  margin: 0 0 .38em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.9rem, 7.4vw, 4.6rem); }
h2 { font-size: clamp(2.15rem, 4.6vw, 3.25rem); }
h3 { font-size: clamp(1.5rem, 2.6vw, 2rem); }
h4 {
  font-family: var(--display); font-weight: 400; text-transform: uppercase;
  font-size: 1.375rem; line-height: 1.05; letter-spacing: .012em; margin: 0 0 .3em;
}
p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0 0 1.05em; padding-left: 1.15em; }
li { margin-bottom: .3em; }
strong, b { font-weight: 600; }

a { color: var(--brass-text); text-decoration-thickness: 1px; text-underline-offset: .18em; }
a:hover { color: var(--sage); }

:where(a, button, summary, input, select, textarea):focus-visible {
  outline: 3px solid var(--sage); outline-offset: 2px; border-radius: 2px;
}

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad); }
@media (min-width: 760px) { .wrap { padding: 0 32px; } }

.skip { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--deep); color: var(--white); padding: .8rem 1.1rem; font-weight: 600; }
.skip:focus { left: 0; }

.vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* ------------------------------------------------------------------ buttons
   Countryside's exact geometry: 8px radius, 14/28 padding, 14px uppercase, 1.12px
   tracking. Their colour is the one thing we refuse. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-family: var(--body); font-weight: 600; font-size: 14px; line-height: 1.2;
  letter-spacing: 1.12px; text-transform: uppercase;
  padding: 14px 28px; border-radius: var(--r); border: 2px solid transparent;
  text-decoration: none; cursor: pointer; text-align: center;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn--primary { background: var(--sage); color: var(--white); border-color: var(--sage); }
.btn--primary:hover { background: var(--sage-dk); border-color: var(--sage-dk); color: var(--white); }
.btn--wire { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--wire:hover { border-color: var(--brass); color: var(--ink); background: rgba(192, 138, 46, .07); }
.btn--ghost { background: transparent; color: var(--white); border-color: rgba(255, 255, 255, .55); }
.btn--ghost:hover { background: rgba(255, 255, 255, .12); border-color: var(--white); color: var(--white); }
.btn--lg { font-size: 15px; padding: 17px 36px; }

.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.7rem; }
.btn-row.is-center { justify-content: center; }
@media (max-width: 519px) { .btn-row .btn { flex: 1 1 100%; } }

/* ------------------------------------------------------------------ topbar */
/* MEASURED 2026-08-31, at 390px: this bar was 66px tall and the header under it 69px,
   so 135px of a phone screen was spent before the page began. It was not the height of
   the text doing that. The bar held the hours chip and the phone number, they did not
   fit on one line at 13.6px, flex-wrap dropped the number onto a second row, and the
   two rows sat 16px apart. Forty pixels of text in sixty-six pixels of bar.

   The fix is to stop carrying the number here on a phone rather than to squeeze the
   type. There is a FIXED "call the store" bar on screen the whole way down every page
   on mobile, plus the number in the menu, so this was the third copy of it and the
   only one charging rent at the top of the page. It stays on desktop, where there is
   no fixed bar and the two items fit on one line with room to spare.

   nowrap rather than wrap, so if a future hours phrase gets long it truncates the bar
   instead of silently growing a second row again. */
.topbar { background: var(--deep); color: var(--band); font-size: .85rem; line-height: 1.5; }
.topbar .wrap {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 34px; padding-top: .25rem; padding-bottom: .25rem; flex-wrap: nowrap;
}
.topbar__tel { display: none; flex: none; }
@media (min-width: 620px) {
  .topbar .wrap { min-height: 40px; padding-top: .3rem; padding-bottom: .3rem; }
  .topbar__tel { display: inline; }
}
.topbar a { color: var(--brass-lt); font-weight: 600; text-decoration: none; }
.topbar a:hover { color: var(--white); text-decoration: underline; }

.chip { display: inline-flex; align-items: center; gap: .5rem; }
.chip__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brass-lt); flex: none; display: none; }
.chip.is-live .chip__dot { display: block; }
.chip.is-open .chip__dot { background: #7FB08A; }
.chip.is-shut .chip__dot { background: #9A948C; }
.chip__txt { font-weight: 500; }
.chip.is-open .chip__txt { color: var(--white); }

/* ------------------------------------------------------------------ header */
.site-header { position: sticky; top: 0; z-index: 40; background: var(--ground); border-bottom: 1px solid var(--rule); }
.site-header .wrap { display: flex; align-items: center; gap: 1rem; min-height: 68px; padding-top: .5rem; padding-bottom: .5rem; }
@media (min-width: 900px) { .site-header .wrap { min-height: 84px; } }

/* Mark beside wordmark. The text is its own column so the two stack independently:
   the fox stays vertically centred against both lines rather than against the first. */
.brand { display: flex; flex-direction: row; align-items: center; gap: .7rem; text-decoration: none; color: var(--ink); margin-right: auto; }
.brand__txt { display: flex; flex-direction: column; }
/* Bigger than the old fox head needed to be. This mark is a fox INSIDE a chair, so
   the animal is only about a third of the box: at 42px the fox was an orange smudge.
   52px is the smallest size at which it still reads as what it is. */
.brand__mark { flex: none; width: 40px; height: 40px; display: block; }
@media (min-width: 900px) { .brand__mark { width: 52px; height: 52px; } }
/* Below 380px the mark is the first thing to go: the phone number in the top bar and
   the shop's name matter more than a logo on a 360px screen. */
@media (max-width: 379px) { .brand__mark { display: none; } }
.brand b {
  font-family: var(--display); font-weight: 400; font-size: 1.8rem; line-height: .92;
  letter-spacing: .02em; text-transform: uppercase;
}
.brand__txt span { font-size: .66rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-2); margin-top: .28rem; font-weight: 500; }
@media (min-width: 900px) { .brand b { font-size: 2.15rem; } }

.nav { display: none; gap: 1.9rem; align-items: center; }
@media (min-width: 960px) { .nav { display: flex; } }
.nav a {
  font-family: var(--display); font-weight: 400; font-size: 1.16rem; text-transform: uppercase;
  letter-spacing: .035em; color: var(--ink); text-decoration: none; padding: .3rem 0;
  border-bottom: 2px solid transparent;
}
.nav a:hover { color: var(--brass-text); border-bottom-color: var(--brass); }
.nav a[aria-current="page"] { border-bottom-color: var(--brass); }

.header-cta { display: none; }
@media (min-width: 960px) { .header-cta { display: inline-flex; } }

.mnav { position: relative; }
@media (min-width: 960px) { .mnav { display: none; } }
/* ICON ONLY, AND NO BOX. This said "MENU" with a caret, then it was a bordered 48px
   square, and both were too heavy.

   The box was the weight, not the icon. Measured in the header at 390px: the bordered
   square was 44px against a 40px logo mark and a 48px brand block, so the menu control
   was physically the largest thing up there and competed with her name for the eye. A
   bare icon is a utility control and reads like one.

   WHAT YOU SEE SHRANK AND WHAT YOU HIT DID NOT. The bars are 20px; the summary still
   claims 44x44, because that is the floor every touch guideline lands on and a thumb
   does not get more accurate to suit a lighter design. The invisible half of this rule
   is the important half: do not let min-width/min-height follow the icon down. */
.mnav > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;
  border: 0; border-radius: var(--r); color: var(--ink);
  transition: background-color .15s ease;
}
.mnav > summary::-webkit-details-marker { display: none; }
/* With the border gone the hover tint is the only thing left showing where the target
   actually reaches, so it is doing real work rather than decoration. Same brass wash
   .btn--wire uses, a shade stronger because there is no outline underneath it. */
.mnav > summary:hover { background: rgba(192, 138, 46, .10); }

/* Three bars from one element: the span is the middle bar, its two pseudo-elements are
   the outer two. Open, the middle fades and the outer two meet in the centre as an X,
   which is the one piece of feedback an icon-only control owes you. currentColor
   throughout, so it inherits whatever the header is set to.

   The global prefers-reduced-motion rule at the top of this file already collapses
   these transitions to .01ms, so there is nothing to special-case here. */
.burger { position: relative; display: block; width: 20px; height: 2px; border-radius: 2px;
  background: currentColor; transition: background-color .12s ease; }
.burger::before, .burger::after {
  content: ""; position: absolute; left: 0; width: 20px; height: 2px; border-radius: 2px;
  background: currentColor; transition: transform .18s ease;
}
.burger::before { top: -6px; }
.burger::after { top: 6px; }
.mnav[open] .burger { background: transparent; }
.mnav[open] .burger::before { transform: translateY(6px) rotate(45deg); }
.mnav[open] .burger::after { transform: translateY(-6px) rotate(-45deg); }
.mnav__panel {
  position: absolute; right: 0; top: calc(100% + .55rem); z-index: 45; width: min(82vw, 290px);
  background: var(--ground); border: 1px solid var(--rule); border-radius: var(--r);
  box-shadow: var(--shadow); padding: .5rem; display: flex; flex-direction: column;
}
/* :not(.btn) IS LOAD-BEARING. Without it these two rules repaint the call button that
   sits at the bottom of the open menu: `.mnav__panel a` is (0,2,1) and `.btn--primary`
   is (0,1,0), so the panel's ink colour won every time and the site's main phone-number
   button rendered #3A342E on the sage fill at 1.84:1. On a phone that button IS the
   navigation, and it was unreadable on every page of the site.

   Measured, not guessed: a pass over every .btn on all 14 pages put this one at 1.84:1
   and every other button at 6.66:1 or better. tools/qa-shots.mjs now runs that pass on
   each build so a descendant selector can never quietly out-specify a button again. */
.mnav__panel a:not(.btn) {
  font-family: var(--display); font-size: 1.2rem; text-transform: uppercase; letter-spacing: .035em;
  color: var(--ink); text-decoration: none; padding: .7rem .8rem; border-radius: 4px;
}
.mnav__panel a:not(.btn):hover { background: var(--band); color: var(--brass-text); }
.mnav__panel .btn { margin: .55rem .3rem .3rem; }

/* ------------------------------------------------------------------ hero
   Countryside runs 720px with the type centred over a full-bleed photograph and
   exactly one button. Ours needs a heavier scrim than theirs because her showroom
   is lit with overhead tubes rather than a wall of windows: .48 puts white type at
   roughly 4.9:1 over the mid-tones in this frame, verified with tools/qa-shots.mjs. */
.hero { position: relative; isolation: isolate; background: var(--deep); overflow: hidden; }
.hero__img { position: absolute; inset: 0; z-index: -2; }
/* Pushed right of centre so the subject of the photograph sits in the half the
   headline does NOT cover. With left-aligned type this is doing real work, not
   taste: at 50% the table legs ended up behind the H1. */
.hero__img img { width: 100%; height: 100%; object-fit: cover; object-position: 62% 50%; }

/* THE SCRIM IS DIRECTIONAL ON DESKTOP AND VERTICAL ON A PHONE, and that is two
   different problems rather than one. On a phone the copy spans the full width, so
   the darkening has to as well. On a wide screen the copy sits in the left third and
   a flat overlay would pointlessly mute the right half of the photograph, which is
   the half anyone is actually looking at. The measured hero contrast check in
   tools/qa-shots.mjs samples the brightest 5% of pixels under each line and is what
   these numbers were tuned against, so re-run it after touching any of them. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to bottom, rgba(24, 30, 25, .36) 0%, rgba(24, 30, 25, .62) 48%, rgba(24, 30, 25, .70) 100%);
}
@media (min-width: 760px) {
  .hero::before {
    background:
      linear-gradient(to bottom, rgba(24, 30, 25, .10) 0%, rgba(24, 30, 25, .34) 100%),
      linear-gradient(100deg,
        rgba(20, 26, 21, .88) 0%, rgba(20, 26, 21, .80) 30%,
        rgba(20, 26, 21, .46) 58%, rgba(20, 26, 21, .16) 84%, rgba(20, 26, 21, .10) 100%);
  }
}
.hero .wrap {
  position: relative; min-height: 560px;
  padding-top: clamp(3.4rem, 8vw, 5.5rem); padding-bottom: clamp(3.4rem, 8vw, 5.5rem);
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
}
/* LEFT-ALIGNED, and this is not a preference. The twelve-site measured pass that
   produced this whole design system listed centred hero text as the generic-AI tell
   and found every strong reference left-aligned, and the approved plan said the same
   thing. The first build shipped centred anyway. */
@media (min-width: 760px) {
  .hero .wrap { align-items: flex-start; text-align: left; min-height: 720px; }
}
.hero__in { max-width: 56rem; }
@media (min-width: 760px) { .hero__in { max-width: 40rem; } }
.hero h1 { color: var(--white); margin-bottom: .3em; }
.hero__sub {
  color: var(--white); font-size: clamp(1.06rem, 1.9vw, 1.3rem); line-height: 1.55;
  max-width: 40rem; margin: 0 auto 1.9rem;
}
@media (min-width: 760px) { .hero__sub { max-width: 34rem; margin-left: 0; margin-right: 0; } }
.hero__meta {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem 1.6rem;
  margin-top: 2rem; color: #F1EDE5; font-size: .96rem;
}
@media (min-width: 760px) { .hero__meta { justify-content: flex-start; } }
/* The markup keeps `is-center` because a phone still wants the buttons centred under
   centred copy. Only the wide layout pulls them left, with the rest of the hero. */
@media (min-width: 760px) { .hero .btn-row.is-center { justify-content: flex-start; } }
.hero__meta a { color: var(--white); text-decoration-color: rgba(255, 255, 255, .5); }
.hero__meta a:hover { color: var(--brass-lt); }
.hero__meta .rev { color: var(--brass-lt); font-weight: 600; }

/* ------------------------------------------------------------------ sections
   Two grounds and no others, alternating: white and #FCF8F1. */
.section { padding: clamp(3.4rem, 7vw, 5.6rem) 0; }
.section--band { background: var(--band); }
.section--deep { background: var(--deep); color: var(--band); }
.section--deep h2, .section--deep h3, .section--deep h4 { color: var(--white); }
.section--deep a { color: var(--brass-lt); }
.section--deep a:hover { color: var(--white); }
.section--tight { padding: clamp(2.4rem, 4.5vw, 3.4rem) 0; }

.shead { max-width: 52rem; margin-bottom: 2.6rem; }
.shead.is-center { margin-left: auto; margin-right: auto; text-align: center; }
.kicker {
  display: block; font-family: var(--body); font-weight: 600; font-size: .74rem;
  letter-spacing: .19em; text-transform: uppercase; color: var(--brass-text); margin-bottom: .9rem;
}
.section--deep .kicker { color: var(--brass-lt); }
.lede { color: var(--ink-2); font-size: 1.04rem; line-height: 1.62; }
.section--deep .lede { color: #E4DED2; }

/* ------------------------------------------------------------------ hours */
.hours { display: grid; gap: 2.2rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 880px) { .hours { grid-template-columns: 1.02fr .98fr; gap: 3.6rem; } }

.hours__table { width: 100%; border-collapse: collapse; font-size: 1.02rem; }
.hours__table th, .hours__table td { text-align: left; padding: .7rem 0; border-bottom: 1px solid var(--rule); }
.hours__table th { font-family: var(--display); font-weight: 400; font-size: 1.28rem; text-transform: uppercase; letter-spacing: .02em; width: 46%; }
.hours__table td { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.hours__table tr:last-child th, .hours__table tr:last-child td { border-bottom: 0; }
.hours__table tr.is-today th { color: var(--brass-text); }
.hours__table tr.is-today td { color: var(--ink); font-weight: 600; }

.factlist { list-style: none; margin: 0; padding: 0; }
.factlist li { display: grid; grid-template-columns: 1fr; gap: .1rem; padding: .78rem 0; border-bottom: 1px solid var(--rule); }
@media (min-width: 480px) { .factlist li { grid-template-columns: 8rem 1fr; gap: 1rem; align-items: baseline; } }
.factlist li:last-child { border-bottom: 0; }
.factlist b { font-family: var(--display); font-weight: 400; font-size: 1.16rem; text-transform: uppercase; letter-spacing: .02em; }
.factlist span { color: var(--ink-2); }
.factlist a { color: var(--brass-text); }

/* ------------------------------------------------------------------ category cards
   Countryside's featured row: image, 22px uppercase title under it, no boxed caption. */
/* THREE ACROSS AT THE TOP END, NOT SIX. At six the tiles came out around 200px wide,
   and 200px of a wide showroom aisle is not a dining set, it is a smudge. Three across
   roughly doubles them and the photograph starts carrying the label instead of the
   label carrying the photograph. Two rows of three also matches the section rhythm
   better than one long strip of six. */
.cats { display: grid; gap: 1.5rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 700px) { .cats { grid-template-columns: repeat(3, 1fr); gap: 1.8rem; } }
@media (min-width: 1040px) { .cats { gap: 2rem 1.9rem; } }

.cat { display: block; text-decoration: none; color: var(--ink); }
.cat__img { aspect-ratio: 1 / 1; overflow: hidden; border-radius: var(--r); background: var(--band); }
.cat__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .45s ease; }
.cat:hover .cat__img img { transform: scale(1.05); }
.cat h4 { margin: .9rem 0 .18rem; color: var(--ink); }
.cat:hover h4 { color: var(--brass-text); }
.cat span { display: block; color: var(--ink-2); font-size: .88rem; line-height: 1.45; }

/* ------------------------------------------------------------------ floors */
.floors { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 860px) { .floors { grid-template-columns: 1fr 1fr; gap: 2rem; } }
.floor { display: flex; flex-direction: column; border-radius: var(--r); overflow: hidden; background: var(--ground); border: 1px solid var(--rule); }
.floor--deep { background: var(--deep); border-color: var(--deep); color: #E4DED2; }
.floor--deep h3 { color: var(--white); }
.floor--deep .kicker { color: var(--brass-lt); }
.floor--deep .floor__more { color: var(--brass-lt); }
.floor__img { aspect-ratio: 16 / 10; background: var(--band); }
.floor__img img { width: 100%; height: 100%; object-fit: cover; }
.floor__body { padding: 1.9rem 1.6rem 2rem; display: flex; flex-direction: column; flex: 1; }
@media (min-width: 620px) { .floor__body { padding: 2.2rem 2.1rem 2.3rem; } }
.floor__more { margin-top: auto; padding-top: 1.3rem; font-family: var(--display); font-size: 1.2rem; text-transform: uppercase; letter-spacing: .035em; color: var(--brass-text); text-decoration: none; }
.floor__more:hover { text-decoration: underline; }
/* No verified booth photograph exists, so the second-floor card carries a brass rule
   instead of a first-floor shot pretending to be upstairs. */
.floor--deep .floor__body { border-top: 4px solid var(--brass); justify-content: center; }
.floor--deep .floor__more { margin-top: 1.3rem; }

/* ------------------------------------------------------------------ split */
.split { display: grid; gap: 2.2rem; grid-template-columns: 1fr; align-items: center; }
@media (min-width: 880px) { .split { grid-template-columns: 1fr 1fr; gap: 4rem; } }
.split--flip > *:first-child { order: 2; }
@media (max-width: 879px) { .split--flip > *:first-child { order: 0; } }
.split__img img { border-radius: var(--r); width: 100%; }

/* ONE PHOTOGRAPH ON ITS OWN, for the pages that are otherwise only words. Measured on
   2026-08-31: six pages carried no photograph at all while the 404 carried six, and the
   pages that work sit near 500px of page per picture.

   It goes INSIDE an existing section rather than in a section of its own, on purpose.
   The grounds alternate white / #FCF8F1 the whole way down every page, so a new section
   inverts every band below it and has to be paid for by flipping one further down. A
   figure at the end of a section costs nothing and cannot get that wrong.

   Capped at 1100px because that is what prep-images.mjs emits these at, and the
   container goes to 1480. Letting it fill would upscale by a third. */
.figband { margin: 2.6rem auto 0; max-width: 1100px; }
.figband img { width: 100%; border-radius: var(--r); display: block; }
.figband figcaption {
  margin-top: .8rem; font-size: .92rem; line-height: 1.55; color: var(--ink-2); text-align: center;
}

/* For a split whose text column runs much longer than the picture, which is every
   town page. Centred, the photograph floats in the middle of a column of white with
   dead space above and below it. Aligned to the top and made sticky it travels down
   with the reading instead. Sticky only where there are two columns: on a phone the
   photograph is between two blocks of text and pinning it would cover them. */
.split--sticky { align-items: start; }
@media (min-width: 880px) {
  /* The offset clears the sticky site header, which is 84px at this width. At top:2rem
     the photograph slid under it and lost its top edge. */
  .split--sticky .split__img { position: sticky; top: calc(84px + 1.5rem); }
}

/* ------------------------------------------------------------------ gallery
   EIGHT LARGE FRAMES, NOT SIXTEEN SMALL ONES. Countryside gets away with 85 pictures
   because theirs are product shots on white; ours are busy rooms shot on a phone, and
   at 640px square in a four-across grid they read as a contact sheet. Every frame had
   identical weight and nothing led the eye.

   One column on a phone, two everywhere else. Two-up is the whole point: at roughly
   570px each a room photograph finally has enough size to say what is in it. Resist
   putting a third column back. */
.gallery { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .gallery { grid-template-columns: repeat(2, 1fr); gap: 1.2rem; } }
@media (min-width: 980px) { .gallery { gap: 1.6rem; } }
.gallery img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r); display: block; }
.gallery figure { margin: 0; overflow: hidden; border-radius: var(--r); }
/* A slow, small push in. Enough to feel alive on hover, not enough to look like a
   slideshow. Anyone who has asked their system not to animate gets nothing. */
.gallery img { transition: transform .5s cubic-bezier(.2, .6, .2, 1); }
.gallery figure:hover img { transform: scale(1.035); }
@media (prefers-reduced-motion: reduce) {
  .gallery img { transition: none; }
  .gallery figure:hover img { transform: none; }
}

/* ------------------------------------------------------------------ cta strip
   Countryside runs a 135px full-width strip between the heavy sections. */
.strip { background: var(--sage); color: var(--white); }
.strip .wrap {
  min-height: 135px; padding-top: 2rem; padding-bottom: 2rem;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 1rem 2rem; text-align: center;
}
.strip h2 { color: var(--white); margin: 0; font-size: clamp(1.7rem, 3.2vw, 2.4rem); }
.strip p { color: #E8EFE9; margin: 0; }
.strip .btn-row { margin-top: 0; }

/* ------------------------------------------------------------------ prose */
.prose { max-width: 38rem; }
/* h2 as well as h3. The town pages and /financing use h2 inside .prose, and without
   this a heading that follows a .btn-row sits flush against the button: "Financing,
   which does" collided with the layaway button directly above it. The extra margin
   after a button row is because a button carries its own visual weight and needs more
   air beneath it than a paragraph does. */
.prose h2, .prose h3 { margin-top: 2.4rem; }
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose .btn-row + h2, .prose .btn-row + h3 { margin-top: 3.2rem; }

/* ------------------------------------------------------------------ cta band */
.cta { background: var(--deep); color: #E4DED2; }
.cta .wrap {
  padding-top: clamp(2.8rem, 5vw, 4rem); padding-bottom: clamp(2.8rem, 5vw, 4rem);
  display: grid; gap: 1.7rem; align-items: center;
}
@media (min-width: 880px) { .cta .wrap { grid-template-columns: 1.3fr auto; gap: 3rem; } }
.cta h2 { color: var(--white); margin-bottom: .34em; }
.cta p { color: #E4DED2; margin: 0; }
.cta .btn-row { margin-top: 0; }

/* ------------------------------------------------------------------ footer */
.foot { background: var(--band); border-top: 1px solid var(--rule); }
.foot .wrap { padding-top: 3.2rem; padding-bottom: 1.6rem; }
.foot__top { display: grid; gap: 2.3rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .foot__top { grid-template-columns: 1.5fr 1fr 1fr; gap: 2.6rem; } }
@media (min-width: 1000px) { .foot__top { grid-template-columns: 1.7fr 1fr 1fr 1fr; } }
.foot h4 { font-family: var(--body); font-weight: 600; font-size: .74rem; letter-spacing: .19em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 1rem; }
.foot ul { list-style: none; margin: 0; padding: 0; }
.foot li { margin-bottom: .52rem; }
.foot a { color: var(--ink); text-decoration: none; }
.foot a:hover { color: var(--brass-text); text-decoration: underline; }
.foot__brand b { display: block; font-family: var(--display); font-weight: 400; font-size: 1.9rem; text-transform: uppercase; letter-spacing: .02em; margin-bottom: .6rem; }
.foot__brand p { font-size: .95rem; color: var(--ink-2); }
.foot__social { display: flex; gap: 1.1rem; margin-top: 1rem; font-weight: 500; }
.foot__bottom {
  margin-top: 2.6rem; padding-top: 1.3rem; border-top: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; gap: .5rem 1.4rem; justify-content: space-between;
  font-size: .86rem; color: var(--ink-2);
}

/* ------------------------------------------------------------------ call bar */
.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: grid; grid-template-columns: 1fr 1fr;
  background: var(--deep); border-top: 1px solid rgba(255, 255, 255, .16);
}
@media (min-width: 960px) { .callbar { display: none; } }
.callbar a {
  padding: .95rem .6rem; text-align: center; text-decoration: none;
  font-family: var(--display); font-size: 1.2rem; text-transform: uppercase; letter-spacing: .04em; color: var(--white);
}
.callbar a + a { border-left: 1px solid rgba(255, 255, 255, .16); }
.callbar a:first-child { background: var(--sage); }
body { padding-bottom: 54px; }
@media (min-width: 960px) { body { padding-bottom: 0; } }

/* ------------------------------------------------------------------ page header
   Interior pages open on a cream band rather than a photographic hero. Countryside
   does the same: the 720px hero is a homepage device, not a site-wide one. */
.phead { background: var(--band); border-bottom: 1px solid var(--rule); }
.phead .wrap { padding-top: clamp(2.6rem, 5.5vw, 4.2rem); padding-bottom: clamp(2.6rem, 5.5vw, 4.2rem); }
.phead .inner { max-width: 46rem; }
.phead h1 { margin-bottom: .3em; }
.phead .lede { font-size: 1.1rem; }

/* ------------------------------------------------------------------ photo header
   Every subpage used to open on the cream band above while the homepage opened on a
   full-bleed photograph, and that gap was the main reason the rest of the site felt
   thinner than the front of it. Countryside, the reference this whole system is
   measured to, puts display type over a photograph on every page it has.

   THE SCRIM IS HEAVIER THAN THE HERO'S, deliberately. The hero has one photograph that
   was tuned against by hand; this runs over eighteen different frames, some of them
   pale rooms under fluorescent tube lighting, and the same gradient has to hold on all
   of them. The header is also a third of the hero's height, so muting the picture costs
   far less here than it would there.

   Directional on desktop, flat on a phone, for the same reason as the hero: the copy
   sits in the left half on a wide screen and spans the full width on a narrow one.

   tools/qa-shots.mjs measures the brightest 5% of pixels under every line of every
   header. Re-run it after touching any number in this block. */
.phead--photo { position: relative; isolation: isolate; background: var(--deep); border-bottom: 0; overflow: hidden; }
.phead--photo__img { position: absolute; inset: 0; z-index: -2; }
.phead--photo__img img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.phead--photo::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to bottom, rgba(22, 28, 23, .70) 0%, rgba(22, 28, 23, .80) 100%);
}
@media (min-width: 760px) {
  .phead--photo::before {
    background:
      linear-gradient(to bottom, rgba(22, 28, 23, .26) 0%, rgba(22, 28, 23, .46) 100%),
      linear-gradient(100deg,
        rgba(18, 24, 19, .90) 0%, rgba(18, 24, 19, .84) 34%,
        rgba(18, 24, 19, .52) 64%, rgba(18, 24, 19, .18) 90%, rgba(18, 24, 19, .12) 100%);
  }
}
.phead--photo .wrap {
  position: relative; min-height: 260px; display: flex; flex-direction: column; justify-content: center;
  padding-top: clamp(2.8rem, 6vw, 4.6rem); padding-bottom: clamp(2.8rem, 6vw, 4.6rem);
}
@media (min-width: 760px) { .phead--photo .wrap { min-height: 340px; } }
.phead--photo h1, .phead--photo .lede { color: var(--white); }
.phead--photo .kicker { color: var(--brass-hi); }
.phead--photo .crumbs, .phead--photo .crumbs a { color: rgba(255, 255, 255, .82); }
.phead--photo .crumbs a:hover { color: var(--brass-hi); }
.crumbs { font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 1.1rem; }
.crumbs a { color: var(--ink-2); text-decoration: none; }
.crumbs a:hover { color: var(--brass-text); text-decoration: underline; }

/* ------------------------------------------------------------------ steps
   Numbered because this genuinely is a sequence. Do not reuse for lists that are not
   ordered: a number that encodes nothing is decoration. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.6rem; grid-template-columns: 1fr; counter-reset: step; }
@media (min-width: 780px) { .steps { grid-template-columns: repeat(2, 1fr); gap: 2.2rem 2.6rem; } }
.steps li { counter-increment: step; display: grid; grid-template-columns: 3.1rem 1fr; gap: 1.1rem; margin: 0; align-items: start; }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--display); font-size: 2.5rem; line-height: .85; color: var(--brass);
}
.steps h3 { font-size: 1.4rem; margin-bottom: .35rem; }
.steps p { color: var(--ink-2); font-size: .98rem; margin: 0; }

/* ------------------------------------------------------------------ terms
   Policy pages. The label column is the term, the value column is what it means, and
   nothing is buried in a paragraph where it can be missed. */
.terms { margin: 0; display: grid; grid-template-columns: 1fr; }
.terms > div { display: grid; grid-template-columns: 1fr; gap: .2rem; padding: 1.15rem 0; border-bottom: 1px solid var(--rule); }
@media (min-width: 620px) { .terms > div { grid-template-columns: 15rem 1fr; gap: 1.6rem; align-items: baseline; } }
.terms > div:last-child { border-bottom: 0; }
.terms dt { font-family: var(--display); font-size: 1.28rem; text-transform: uppercase; letter-spacing: .02em; }
.terms dd { margin: 0; color: var(--ink-2); }

/* ------------------------------------------------------------------ brands */
.brands { display: grid; gap: 1.3rem; grid-template-columns: 1fr; }
@media (min-width: 780px) { .brands { grid-template-columns: repeat(3, 1fr); gap: 1.6rem; } }
.brand-card { border: 1px solid var(--rule); border-radius: var(--r); padding: 1.7rem 1.6rem; background: var(--ground); display: flex; flex-direction: column; }
.brand-card h3 { font-size: 1.55rem; margin-bottom: .5rem; }
.brand-card p { color: var(--ink-2); font-size: .97rem; }
.brand-card .out { margin-top: auto; padding-top: 1.1rem; font-family: var(--display); font-size: 1.1rem; text-transform: uppercase; letter-spacing: .035em; text-decoration: none; }
.brand-card .out:hover { text-decoration: underline; }
.brand-card .noout { margin-top: auto; padding-top: 1.1rem; font-size: .88rem; color: var(--ink-2); }

/* ------------------------------------------------------------------ notes
   A plain honesty block. Deliberately not styled as a warning: these are ordinary
   facts a customer deserves before they commit, not scare copy. */
.note-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.1rem; }
.note-list li { margin: 0; padding-left: 1.4rem; position: relative; color: var(--ink-2); }
.note-list li::before { content: ""; position: absolute; left: 0; top: .62em; width: 8px; height: 2px; background: var(--brass); }
.note-list b { color: var(--ink); }

/* ------------------------------------------------------------------ item grid
   The public "just in" grid, rendered from R2 by functions/_middleware.ts. */
.items { display: grid; gap: 1.4rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .items { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .items { grid-template-columns: repeat(3, 1fr); gap: 1.8rem; } }
.item { border: 1px solid var(--rule); border-radius: var(--r); overflow: hidden; background: var(--ground); display: flex; flex-direction: column; }
.item__img { aspect-ratio: 1 / 1; background: var(--band); }
.item__img img { width: 100%; height: 100%; object-fit: cover; }
.item__body { padding: 1.3rem 1.3rem 1.4rem; display: flex; flex-direction: column; flex: 1; }
.item__cat { font-family: var(--body); font-weight: 600; font-size: .7rem; letter-spacing: .17em; text-transform: uppercase; color: var(--brass-text); margin-bottom: .5rem; }
.item h3 { font-size: 1.5rem; margin-bottom: .4rem; }
.item h3 a { color: inherit; text-decoration: none; }
.item h3 a:hover, .item h3 a:focus-visible { color: var(--brass-text); text-decoration: underline; }
/* The photograph links to the same place as the heading above it. It carries
   aria-hidden and tabindex="-1" in the markup so a keyboard or a screen reader gets
   one link per card rather than two identical ones. */
.item__img a { display: block; width: 100%; height: 100%; }
.item p { color: var(--ink-2); font-size: .96rem; }
/* These two are <p> inside .item, so they need the same specificity as `.item p` or
   the generic rule wins and the price silently renders at body size. It did. */
.item p.item__price { font-family: var(--display); font-size: 2rem; color: var(--ink); margin: .8rem 0 .2rem; letter-spacing: .01em; line-height: 1; }
/* No price on this one. Set in the display face at price size, "Ask us" outweighed
   the card's own title and read as a heading. Same fix as .piece__price--ask. */
.item p.item__price--ask { font-family: var(--body); font-weight: 600; font-size: 1rem; letter-spacing: 0; line-height: 1.4; color: var(--ink-2); margin: .7rem 0 .2rem; }
/* The date stamp is the honest half of the price. It never says "in stock", because  check:allow(in-stock)
   her register holds no inventory and nothing here can verify that claim. */
.item p.item__stamp { margin-top: auto; padding-top: .8rem; font-size: .84rem; color: var(--ink-2); }
.item p.item__stamp b { color: var(--alert); font-weight: 600; }

/* ------------------------------------------------------------------ town list
   The six drive-time links on /visit. Whole rows are the target, not the two words
   of the town name, because half the people reading this are on a phone. */
.townlist { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .townlist { grid-template-columns: repeat(2, 1fr); gap: .8rem 1.4rem; } }
@media (min-width: 980px) { .townlist { grid-template-columns: repeat(3, 1fr); } }
.townlist li { margin: 0; }
/* Grid, not flex. With flex-wrap the short rows sit beside the town name and the
   long ones drop below it, so Tyrone came out one line high and the five around it
   two, and the row of cards stopped lining up. A grid puts every one on its own row
   whatever the length. */
.townlist a {
  display: grid; gap: .25rem; align-content: start; text-decoration: none;
  border: 1px solid var(--rule); border-radius: var(--r); background: var(--ground);
  padding: .95rem 1.1rem; color: var(--ink);
}
.townlist a:hover, .townlist a:focus-visible { border-color: var(--sage); }
.townlist b { font-family: var(--display); font-size: 1.35rem; text-transform: uppercase; letter-spacing: .02em; font-weight: 400; }
.townlist span { font-size: .88rem; color: var(--ink-2); }

/* ------------------------------------------------------------------ one piece
   /piece/<id>. The page behind a link she texts a customer, so it opens on a big
   photograph and the price, and everything else comes after. */
.piece { display: grid; gap: 2rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 860px) { .piece { grid-template-columns: 1.1fr 1fr; gap: 3.2rem; } }
.piece__lead { aspect-ratio: 1 / 1; background: var(--band); border-radius: var(--r); overflow: hidden; }
.piece__lead img { width: 100%; height: 100%; object-fit: cover; }
.piece__more { display: grid; grid-template-columns: repeat(3, 1fr); gap: .7rem; margin-top: .7rem; }
.piece__more img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 4px; background: var(--band); }
.piece__body h1 { font-size: clamp(2.1rem, 6vw, 3.1rem); margin-bottom: .1em; }
/* The price MUST stay smaller than the h1 above it and keep the rule between them.
   The first cut set it larger, in the same condensed face, hard against the title,
   and "Olive green sofa" over "Ask us" read as a single two-line headline. A price is
   the second thing on this page, not the first. */
.piece__price {
  font-family: var(--display); font-size: clamp(1.9rem, 4.6vw, 2.4rem); line-height: 1;
  color: var(--ink); margin: 0 0 .7rem; padding-top: 1.1rem; border-top: 1px solid var(--rule);
}
/* "Ask us" is a sentence, not a number, and set in the display face at price size it
   looked like one. It gets the body face and drops back to a normal reading size. */
.piece__price--ask { font-family: var(--body); font-weight: 600; font-size: 1.08rem; line-height: 1.5; color: var(--ink-2); }
/* Not styled as an alarm. A sold piece is ordinary news and the useful part of this
   page is the sentence under it about ordering another. */
.piece__flag { font-family: var(--display); font-size: clamp(1.8rem, 5vw, 2.3rem); line-height: 1.1; color: var(--brass-text); margin: .5rem 0 .6rem; text-transform: uppercase; letter-spacing: .02em; }
.piece__stamp { font-size: .92rem; color: var(--ink-2); margin-bottom: 1rem; }
.piece__body .hint { margin-top: 1.4rem; }

/* ------------------------------------------------------------------ her tool
   /post. A phone-first form, not a page anyone browses. Big targets, one column,
   nothing clever. */
body.bare { padding-bottom: 0; background: var(--ground); }
body.bare .wrap { max-width: 720px; }

.tool-form { display: grid; gap: 1.5rem; margin: 2rem 0 3rem; }
.tool-form .field { display: grid; gap: .45rem; }
.tool-form label { font-family: var(--display); font-size: 1.3rem; text-transform: uppercase; letter-spacing: .02em; }
.tool-form input[type="text"], .tool-form input[type="password"], .tool-form input[type="file"],
.tool-form select, .tool-form textarea {
  font-family: var(--body); font-size: 17px; line-height: 1.4;
  padding: .85rem .9rem; border: 2px solid var(--rule); border-radius: var(--r);
  background: var(--ground); color: var(--ink); width: 100%;
}
.tool-form textarea { resize: vertical; }
.tool-form input:focus, .tool-form select:focus, .tool-form textarea:focus { border-color: var(--sage); outline: none; }
.hint { font-size: .87rem; color: var(--ink-2); margin: 0; }

.shots { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .3rem; }
.shots img { width: 78px; height: 78px; object-fit: cover; border-radius: 4px; border: 1px solid var(--rule); }

.flash { padding: .9rem 1.1rem; border-radius: var(--r); margin: 0 0 1.6rem; font-weight: 500; }
.flash--ok { background: #EAF1EC; color: #2A4A34; border: 1px solid #C4DACB; }
.flash--bad { background: #FBECEA; color: #7A2318; border: 1px solid #EFC9C3; }

.tool-section { margin: 2.6rem 0; }
.tool-section h2, .tool-note h2 { font-size: 1.9rem; margin-bottom: 1rem; }
.tool-note { margin: 2.6rem 0; padding: 1.4rem 1.4rem 1.6rem; border: 2px solid var(--brass); border-radius: var(--r); background: var(--band); }
.tool-note p { color: var(--ink-2); font-size: .95rem; margin-bottom: 1.2rem; }

.tool-items { display: grid; gap: .8rem; }
.tool-item { display: grid; grid-template-columns: 72px 1fr; gap: .9rem; align-items: center;
  border: 1px solid var(--rule); border-radius: var(--r); padding: .8rem; background: var(--ground); }
@media (min-width: 560px) { .tool-item { grid-template-columns: 72px 1fr auto; } }
.tool-item img, .tool-item__noimg { width: 72px; height: 72px; object-fit: cover; border-radius: 4px; background: var(--band); }
.tool-item__body { display: grid; gap: .15rem; min-width: 0; }
.tool-item__body b { font-size: 1.02rem; overflow-wrap: anywhere; }
.tool-item__body span { font-size: .85rem; color: var(--ink-2); }
.tool-item__actions { display: flex; flex-wrap: wrap; gap: .4rem; grid-column: 1 / -1; }
@media (min-width: 560px) { .tool-item__actions { grid-column: auto; justify-content: flex-end; } }
.tool-item__actions form { margin: 0; }
.tool-btn {
  font-family: var(--body); font-weight: 600; font-size: 13px; letter-spacing: .06em; text-transform: uppercase;
  padding: .6rem .85rem; border-radius: var(--r); border: 2px solid var(--rule);
  background: var(--ground); color: var(--ink); cursor: pointer;
}
.tool-btn:hover { border-color: var(--sage); }
/* Same shape as the buttons beside it but it is an <a>, which needs the text
   centred and the line height matched or it sits a pixel high in the row. */
.tool-btn--link { display: inline-block; text-decoration: none; line-height: 1.15; margin-right: auto; }
.tool-btn--danger { color: var(--alert); }
.tool-btn--danger:hover { border-color: var(--alert); }
.tool-signout { margin: 3rem 0 4rem; }

/* ------------------------------------------------------------------ public forms
   Shares the tool's input styling. The honeypot is positioned off screen rather than
   display:none, because some bots skip hidden inputs and a real screen reader user
   never reaches it: it is aria-hidden and out of the tab order. */
.pform { display: grid; gap: 1.4rem; margin-top: .5rem; }
.pform .field { display: grid; gap: .4rem; }
.pform label { font-family: var(--display); font-size: 1.25rem; text-transform: uppercase; letter-spacing: .02em; }
.pform label .opt { font-family: var(--body); font-size: .7rem; letter-spacing: .14em; color: var(--ink-2); }
.pform input, .pform select, .pform textarea {
  font-family: var(--body); font-size: 17px; line-height: 1.4;
  padding: .85rem .9rem; border: 2px solid var(--rule); border-radius: var(--r);
  background: var(--ground); color: var(--ink); width: 100%;
}
.pform textarea { resize: vertical; }
.pform input:focus, .pform select:focus, .pform textarea:focus { border-color: var(--sage); outline: none; }
.pform button { margin-top: .4rem; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ------------------------------------------------------------------ upload tiles
   Shared shape with the intake uploader, restyled for the public forms. The tile swaps
   its local preview for the STORED image once the upload lands, which doubles as proof
   to the person that their photograph actually arrived. */
.ph-grid { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .6rem; }
.ph { position: relative; width: 84px; height: 84px; border-radius: 4px; overflow: hidden; border: 1px solid var(--rule); background: var(--band); }
.ph img { width: 100%; height: 100%; object-fit: cover; }
.ph-s {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 2px 4px;
  font-size: .62rem; letter-spacing: .06em; text-transform: uppercase; text-align: center;
  background: rgba(42, 52, 44, .78); color: #fff;
}
.ph.ok .ph-s { background: rgba(78, 97, 81, .9); }
.ph.bad .ph-s { background: rgba(156, 50, 38, .9); }

.field--check label { display: flex; gap: .6rem; align-items: flex-start; font-family: var(--body); font-size: 1rem; text-transform: none; letter-spacing: 0; line-height: 1.5; cursor: pointer; }
.field--check input { width: auto; flex: none; margin-top: .2rem; transform: scale(1.3); }

/* ------------------------------------------------------------------ reveal on scroll
 * REMOVED, and this is a note so nobody adds it back without knowing the cost.
 *
 * A CSS-only scroll reveal was built here with `animation-timeline: view()`, chosen
 * over the usual IntersectionObserver because it needs no inline script (our CSP is
 * `script-src 'self'`) and hides nothing if JavaScript never arrives. It worked:
 * opacity measured 1 in view and 0 out of it, exactly as designed.
 *
 * It came out anyway, for two reasons found by testing rather than by taste.
 *
 *   1. IT DOES NOT REACH HER CUSTOMERS. `animation-timeline` is Chromium-only today.
 *      Safari does not support it. This is a small-town Pennsylvania furniture shop
 *      and a large share of that traffic is iPhone, so the majority of real visitors
 *      would have seen precisely nothing while everyone paid the risk.
 *
 *   2. IT BLINDED OUR OWN QA. Chromium stitches a full-page screenshot by re-scrolling,
 *      so anything outside the captured viewport sits at the start of its keyframe.
 *      The /layaway capture came back with the entire "Before you start one" block
 *      rendered as an empty cream rectangle. Reviewing this site by eye is how the
 *      price-size bug, the collided heading and the floating photographs were all
 *      caught, and a decoration that costs that is not worth having.
 *
 * The motion that survives is the kind that works everywhere and cannot hide content:
 * the image push-in on .gallery figures and .cat tiles, and the button transitions.
 *
 * The invisible-content assertion in tools/qa-shots.mjs stays. It found this in one
 * run and it now guards against anything else ever shipping transparent.
 */

/* ------------------------------------------------------------------ google reviews
   Built to match the Solev live-reviews section: Google's colours, white cards,
   amber stars, a G on every card. On the cream band rather than white so the page
   keeps alternating grounds and the cards lift off it. */
.grev-sec { padding-bottom: clamp(2.6rem, 5vw, 4rem); }
.grev__head { display: flex; align-items: center; justify-content: center; gap: .7rem; }
.grev__head span { font-family: var(--display); font-size: 1.5rem; letter-spacing: .04em; text-transform: uppercase; color: var(--ink); }
.grev__agg { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: 1.1rem; }
.grev__agg b { font-family: var(--display); font-weight: 400; font-size: clamp(3rem, 7vw, 4rem); line-height: .85; color: var(--ink); }
.grev__agg > div { display: grid; gap: .15rem; justify-items: start; }
.grev__stars { color: #FBBC04; letter-spacing: .1em; font-size: 1.05rem; line-height: 1; }
.grev__stars--off { color: #DCD6CA; }
.grev__count { font-size: .84rem; color: var(--ink-2); }

/* THE MARQUEE.
   Full bleed, so the row reads as crossing the screen rather than sitting in a box.
   Two identical sets translated by exactly -50% is what makes the wrap seamless, and
   the spacing MUST be a right margin on the card rather than a flex gap: with `gap`,
   half a gap falls inside the 50% and the loop jumps by that much every cycle. It is
   invisible for the first lap and obvious after a minute.

   Sixty seconds is deliberately slow. This is a strip somebody glances at, not a
   thing they are meant to chase. */
.grev { overflow: hidden; margin-top: 2.4rem; padding: .35rem 0 .6rem; }
@supports (mask-image: linear-gradient(90deg, #000, transparent)) {
  .grev {
    mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
  }
}
.grev__track { display: flex; width: max-content; animation: grev-run 60s linear infinite; }
.grev__set { display: flex; }
/* Stop while somebody is reading one, by pointer or by keyboard. */
.grev:hover .grev__track, .grev__track:focus-within { animation-play-state: paused; }
@keyframes grev-run { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* Reduced motion gets a plain scrollable row rather than a frozen one, so the
   reviews past the fold are still reachable. */
@media (prefers-reduced-motion: reduce) {
  .grev { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .grev__track { animation: none; }
}

.review {
  flex: none; width: min(80vw, 380px); margin: 0 1.25rem 0 0;
  display: flex; flex-direction: column; gap: 1rem;
  background: var(--ground); border: 1px solid var(--rule); border-radius: 16px;
  padding: 1.5rem 1.5rem 1.35rem; box-shadow: var(--shadow);
}
.review__top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.review blockquote { margin: 0; color: var(--ink-2); font-size: .97rem; line-height: 1.62; flex: 1; }
.review figcaption { display: flex; align-items: center; gap: .75rem; min-width: 0; }
.review__pic {
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; background: var(--sage); color: var(--white);
  font-family: var(--display); font-size: 1.25rem; line-height: 1;
}
.review__who { display: grid; gap: .1rem; min-width: 0; font-weight: 600; font-size: .92rem; color: var(--ink); }
.review__who a { color: var(--ink); text-decoration: none; }
.review__who a:hover { color: var(--brass-text); text-decoration: underline; }
.review__who span { font-weight: 400; font-size: .78rem; color: var(--ink-2); }

/* The one control on the site deliberately NOT in our palette: white ground, dark
   text, so it reads as a door out to a third party rather than one of our buttons. */
.btn--google {
  background: var(--white); color: #1F1F1F; border-color: var(--rule);
  gap: .65rem; font-weight: 600;
}
.btn--google:hover { background: #F4F2ED; border-color: #CFC7B8; color: #1F1F1F; }
.gmark { flex: none; display: block; }
