/* ============================================================================
   The Site House — stylesheet.

   Art direction: letterpress print-shop. The brand logo is a terracotta mark
   pressed into kraft paper, so the whole site is built like printed matter —
   warm paper ground, one ink (rust), hairline rules, numbered plates, and
   Fraunces (the studio's settled brand face) at high optical sizes.
   Premium = space + restraint, so there is exactly one accent colour and
   every ornament is a rule, a number, or a dot.

   Themes: body.theme-paper (default) / body.theme-ink   — full token swap,
           driven by the masthead icon toggle.
   Browse: body.browse-grid (default) / body.browse-index — collection layout,
           driven by the Index/Grid switch in the filter row.
   ========================================================================== */

/* ---- Fonts: local files only, no network. Both are variable fonts. ---- */
@font-face {
  font-family: 'Fraunces';
  src: url('../assets/fonts/fraunces.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Fraunces';
  src: url('../assets/fonts/fraunces-italic.woff2') format('woff2');
  font-weight: 100 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  src: url('../assets/fonts/jakarta.woff2') format('woff2');
  font-weight: 200 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  src: url('../assets/fonts/jakarta-italic.woff2') format('woff2');
  font-weight: 200 800; font-style: italic; font-display: swap;
}

/* ---- Design tokens ---- */
:root {
  --paper:      #F1E9D9;   /* page ground — a lighter, readable kraft */
  --paper-deep: #E6D9C0;   /* pressed panels, hover tints */
  --kraft:      #D9C7A8;   /* the logo's own ground */
  --ink:        #2B1B12;   /* warm near-black, never pure #000 */
  --ink-soft:   rgba(43,27,18,.74);
  --ink-faint:  rgba(43,27,18,.52);
  --rust:       #A0492A;   /* the logo mark */
  --rust-deep:  #7C3520;
  --line:       rgba(43,27,18,.22);
  --line-soft:  rgba(43,27,18,.12);
  --band:       var(--ink);        /* the inverted "start a project" band */
  --band-text:  var(--paper);
  --serif: 'Fraunces', Georgia, serif;
  --sans:  'Plus Jakarta Sans', system-ui, sans-serif;
  --shell: 1220px;
}
/* Ink theme: the same print, negative. Rust brightens a step for contrast. */
body.theme-ink {
  --paper:      #201209;
  --paper-deep: #2B1A0E;
  --kraft:      #33200F;
  --ink:        #F1E7D2;
  --ink-soft:   rgba(241,231,210,.78);
  --ink-faint:  rgba(241,231,210,.55);
  --rust:       #D0774F;
  --rust-deep:  #E08A60;
  --line:       rgba(241,231,210,.26);
  --line-soft:  rgba(241,231,210,.13);
  --band:       #A0492A;           /* the band flips to the mark's own rust */
  --band-text:  #F6EEDD;
}

/* ---- Reset-ish ---- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  font-optical-sizing: auto; /* lets Fraunces pick its display cuts */
}
img { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* Paper grain: one tiny inline SVG turbulence tile over everything.
   WHY: flat digital beige reads cheap; 4% grain reads like stock. */
body::after {
  content: ''; position: fixed; inset: 0; z-index: 90; pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

.shell { max-width: var(--shell); margin: 0 auto; padding: 0 clamp(20px, 4vw, 48px); }
.shell--narrow { max-width: 880px; }

/* ---- Shared atoms ---- */
.label {
  font: 700 11px/1 var(--sans);
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-soft);
  display: flex; align-items: center; gap: 10px;
}
.label__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--rust); flex: none; }

.btn {
  display: inline-block; padding: 15px 30px;
  font: 700 12px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none; border: 1.5px solid var(--ink);
  transition: background .22s ease, color .22s ease, border-color .22s ease;
}
.btn--ink   { background: var(--ink); color: var(--paper); }
.btn--ink:hover   { background: var(--rust); border-color: var(--rust); color: #F6EEDD; }
.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { border-color: var(--rust); color: var(--rust); }
/* buttons that sit on the dark band */
.btn--paper { background: var(--band-text); border-color: var(--band-text); color: #2B1B12; }
body.theme-ink .btn--paper { color: #A0492A; }
.btn--paper:hover { background: transparent; color: var(--band-text); }
.btn--ghostpaper { background: transparent; color: var(--band-text); border-color: var(--band-text); }
.btn--ghostpaper:hover { background: var(--band-text); color: #2B1B12; }
body.theme-ink .btn--ghostpaper:hover { color: #A0492A; }

/* The email button is the ONE button whose label is not a phrase but a real
   address, so it opts out of the uppercase every other .btn gets. WHY: an
   uppercased address reads as shouting and invites mistyping - and this one
   has a deliberate double "e" in "sitehousee" that must be read accurately.
   Tracking is eased off a little too, since .16em on an address is hard to
   scan. Approved by Rajat, session 15. */
.btn--email { text-transform: none; letter-spacing: .04em; }

/* An Instagram handle is ALWAYS lowercase - that is how the platform renders it
   and how people type it - so the handle alone opts out of the button's
   uppercase while the surrounding words ("DM ... on Instagram") stay uppercase
   like every other button label. Only the handle, deliberately: Rajat,
   session 15. */
.btn__handle { text-transform: none; }

/* Scroll reveal: JS adds .is-in. Kept tiny and calm on purpose. */
.reveal { opacity: 0; transform: translateY(16px); transition: opacity .6s ease, transform .6s ease; }
.reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ======================= MASTHEAD ======================= */
.masthead {
  position: sticky; top: 0; z-index: 80;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px clamp(20px, 4vw, 48px);
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line-soft);
}
.masthead__brand { display: flex; align-items: center; gap: 14px; text-decoration: none; }
.masthead__badge { border-radius: 50%; }
.masthead__word { font: 600 20px/1 var(--serif); letter-spacing: -.01em; }
.masthead__nav { display: flex; align-items: center; gap: clamp(16px, 3vw, 36px); }
.masthead__nav a {
  font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none; color: var(--ink-soft); transition: color .2s;
}
.masthead__nav a:hover { color: var(--rust); }
.masthead__cta { border: 1.5px solid var(--ink); padding: 11px 18px; color: var(--ink) !important; }
.masthead__cta:hover { background: var(--ink); color: var(--paper) !important; }
/* Theme toggle: bordered square icon button, same hairline vocabulary as the
   CTA and chips. The GLYPH mirrors the current theme: sun on paper, moon on
   ink, so the state is legible at a glance. */
.masthead__theme {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border: 1px solid var(--line);
  color: var(--ink-soft);
  transition: border-color .2s ease, color .2s ease;
}
.masthead__theme:hover { border-color: var(--rust); color: var(--rust); }
.masthead__theme svg { display: block; }
.masthead__theme .icon-moon { display: none; }
body.theme-ink .masthead__theme .icon-sun { display: none; }
body.theme-ink .masthead__theme .icon-moon { display: block; }
/* Keyboard focus for the icon-only controls: same rust as every hover state. */
.masthead__theme:focus-visible, .filters__view button:focus-visible {
  outline: 2px solid var(--rust); outline-offset: 2px;
}

/* ======================= HERO ======================= */
.hero { padding: clamp(84px, 12vh, 150px) 0 0; }
.hero__title {
  font: 500 clamp(52px, 8.6vw, 118px)/0.99 var(--serif);
  letter-spacing: -.022em;
  margin: 26px 0 30px;
  font-variation-settings: 'opsz' 144;
}
.hero__sub { max-width: 560px; font-size: 17.5px; color: var(--ink-soft); }
.hero__actions { display: flex; gap: 14px; flex-wrap: wrap; margin: 38px 0 0; }

/* Fact row — hairline top & bottom, printed-table feel. */
.facts {
  list-style: none; display: flex; flex-wrap: wrap;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  margin-top: clamp(56px, 8vh, 96px);
}
.facts li { flex: 1 1 140px; padding: 22px 24px 24px 0; }
.facts li + li { border-left: 1px solid var(--line-soft); padding-left: 24px; }
.facts b { display: block; font: 500 34px/1.1 var(--serif); letter-spacing: -.01em; }
.facts span { font: 700 10.5px/1.5 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); }

/* Hero film-strip: always on. */
.hero__strip {
  display: block; overflow: hidden; margin-top: clamp(48px, 7vh, 80px);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 18px 0;
}
.hero__striptrack { display: flex; gap: 18px; width: max-content; animation: strip 150s linear infinite; }
.hero__strip:hover .hero__striptrack { animation-play-state: paused; }
/* WHY height:auto: thumbnails carry width/height ATTRIBUTES for CLS in the
   grid, but a used height attribute beats aspect-ratio and would crop these
   wide shots into portrait slivers (240x350). height:auto lets aspect-ratio
   govern, so every tile renders 240x150 landscape, uncropped composition. */
.hero__strip img {
  width: 240px; height: auto; aspect-ratio: 1100 / 688; object-fit: cover;
  border: 1px solid var(--line); filter: saturate(.96);
}
@keyframes strip { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .hero__striptrack { animation: none; } }
.facts { margin-top: 0; border-top: 0; }

/* ======================= STANCE ======================= */
.stance { padding: clamp(110px, 16vh, 190px) 0 0; }
.stance__title {
  font: 500 clamp(36px, 4.8vw, 62px)/1.05 var(--serif);
  letter-spacing: -.018em; margin: 22px 0 28px;
}
.stance__body p { max-width: 640px; font-size: 17.5px; color: var(--ink-soft); }
.stance__body p + p { margin-top: 1.2em; }
.stance__body em { font-family: var(--serif); font-style: italic; color: var(--rust); font-size: 1.05em; }

/* ======================= COLLECTION ======================= */
.collection { padding: clamp(110px, 16vh, 190px) 0 0; }
.collection__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 32px; flex-wrap: wrap; margin-bottom: 34px;
}
.collection__title {
  font: 500 clamp(36px, 4.8vw, 62px)/1.05 var(--serif);
  letter-spacing: -.018em; margin-top: 22px;
}
.collection__note { max-width: 330px; font-size: 14.5px; color: var(--ink-faint); }

/* Filter row: trade chips on the left, Index/Grid view switch on the right. */
.filters {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px 24px; flex-wrap: wrap; margin-bottom: 8px;
}
.filters__chips { display: flex; flex-wrap: wrap; gap: 8px; }
.filters button {
  font: 700 11px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  padding: 10px 16px; border: 1px solid var(--line); color: var(--ink-soft);
  transition: all .18s ease;
}
.filters button:hover { border-color: var(--rust); color: var(--rust); }
.filters button.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.filters__view { display: flex; flex: none; margin-left: auto; }
/* Icon buttons: fixed square hit area; the svg centres itself. The active one
   inherits the chips' inverted .is-on block, so "which view is on" reads the
   same way as "which trade is on". */
.filters__view button {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 37px; padding: 0;
}
.filters__view button svg { display: block; }
.filters__view button + button { border-left: 0; }

.collection__empty { margin: 26px 0 8px; font: italic 500 17px/1.5 var(--serif); color: var(--rust); }

/* ---- Variant A: the index ---- */
body.browse-grid .indexer { display: none; }
.indexer { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 460px); gap: clamp(32px, 4vw, 72px); align-items: start; }
.indexer__list { list-style: none; border-top: 1px solid var(--line); }
.indexer__row {
  display: grid; grid-template-columns: 74px minmax(0,1fr) auto; gap: 18px; align-items: baseline;
  padding: 17px 10px 17px 2px; border-bottom: 1px solid var(--line-soft);
  cursor: pointer; transition: background .18s ease;
}
.indexer__row:hover, .indexer__row.is-live { background: color-mix(in srgb, var(--kraft) 42%, transparent); }
.indexer__no { font: italic 500 15px/1 var(--serif); color: var(--rust); }
.indexer__name {
  font: 500 clamp(19px, 2vw, 25px)/1.15 var(--serif); letter-spacing: -.012em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color .18s ease;
}
.indexer__row:hover .indexer__name, .indexer__row.is-live .indexer__name { color: var(--rust); }
.indexer__tag { font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); }
.indexer__row[hidden] { display: none; }

/* Sticky preview plate. Two stacked <img>s crossfade. */
.indexer__preview { position: sticky; top: 96px; }
.indexer__frame {
  position: relative; aspect-ratio: 1100 / 688; overflow: hidden;
  border: 1px solid var(--ink);
  box-shadow: 10px 10px 0 0 color-mix(in srgb, var(--kraft) 75%, transparent);
  background: var(--paper-deep);
}
.indexer__frame img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .35s ease;
}
.indexer__frame img.is-top { opacity: 1; }
.indexer__caption { margin-top: 14px; font: 700 10.5px/1.6 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); }

/* ---- Variant B: the grid ---- */
.grid { display: none; }
body.browse-grid .grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr));
  gap: clamp(36px, 4vw, 60px) clamp(28px, 3vw, 44px); margin-top: 26px;
}
.grid__card { cursor: pointer; }
.grid__frame {
  overflow: hidden; border: 1px solid var(--ink); aspect-ratio: 1100/688;
  box-shadow: 8px 8px 0 0 color-mix(in srgb, var(--kraft) 75%, transparent);
  background: var(--paper-deep);
}
.grid__frame img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.grid__card:hover .grid__frame img { transform: scale(1.025); }
.grid__meta { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; padding: 14px 2px 0; }
.grid__name { font: 500 21px/1.2 var(--serif); letter-spacing: -.012em; }
.grid__card:hover .grid__name { color: var(--rust); }
.grid__tag { font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); white-space: nowrap; }
.grid__card[hidden] { display: none; }

/* ======================= PROCESS ======================= */
.process { padding: clamp(110px, 16vh, 190px) 0 0; }
.process__title { font: 500 clamp(36px, 4.8vw, 62px)/1.05 var(--serif); letter-spacing: -.018em; margin: 22px 0 46px; }
.steps { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(24px, 3vw, 44px); }
.steps__item { border-top: 1px solid var(--line); padding-top: 22px; }
.steps__no { font: italic 500 30px/1 var(--serif); color: var(--rust); display: block; margin-bottom: 16px; }
.steps__item h3 { font: 600 21px/1.2 var(--serif); letter-spacing: -.01em; margin-bottom: 10px; }
.steps__item p { font-size: 14.5px; color: var(--ink-soft); }

/* ======================= START (inverted band) ======================= */
.start {
  margin-top: clamp(110px, 16vh, 190px);
  background: var(--band); color: var(--band-text);
  padding: clamp(90px, 12vh, 150px) 0;
}
.start .label { color: color-mix(in srgb, var(--band-text) 75%, transparent); }
.start .label__dot { background: var(--band-text); }
body.theme-paper .start .label__dot { background: #D0774F; }
.start__title { font: 500 clamp(40px, 5.4vw, 72px)/1.02 var(--serif); letter-spacing: -.02em; margin: 22px 0 24px; }
.start__sub { max-width: 560px; font-size: 17.5px; color: color-mix(in srgb, var(--band-text) 82%, transparent); }
.start__list { list-style: none; margin: 34px 0 40px; max-width: 560px; }
.start__list li {
  padding: 13px 0 13px 26px; position: relative;
  border-top: 1px solid color-mix(in srgb, var(--band-text) 20%, transparent);
  font-size: 15.5px;
}
.start__list li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--band-text) 20%, transparent); }
.start__list li::before {
  content: ''; position: absolute; left: 2px; top: 22px;
  width: 7px; height: 7px; border-radius: 50%; background: #D0774F;
}
body.theme-ink .start__list li::before { background: #F6EEDD; }
.start__actions { display: flex; gap: 14px; flex-wrap: wrap; }

/* ======================= FOOTER ======================= */
.footer { padding: 54px 0 70px; }
.footer__row { display: flex; justify-content: space-between; gap: 40px; align-items: center; flex-wrap: wrap; }
.footer__brand { display: flex; align-items: center; gap: 16px; text-decoration: none; }
.footer__brand img { border-radius: 50%; }
.footer__brand span { font: 600 19px/1.25 var(--serif); }
.footer__brand small { font: 500 11px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }
.footer__fine { max-width: 480px; font-size: 12.5px; color: var(--ink-faint); }

/* ======================= PLATE OVERLAY ======================= */
.plate {
  position: fixed; inset: 0; z-index: 100; background: var(--paper);
  display: flex; flex-direction: column;
}
.plate[hidden] { display: none; }
.plate__chrome {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px clamp(20px, 4vw, 48px); border-bottom: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--paper) 92%, transparent); backdrop-filter: blur(8px);
}
.plate__close, .plate__nav button {
  font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft); padding: 10px 14px; border: 1px solid var(--line);
  transition: all .18s ease;
}
.plate__close:hover, .plate__nav button:hover { border-color: var(--rust); color: var(--rust); }
.plate__nav { display: flex; align-items: center; gap: 12px; }
.plate__nav span { font: italic 500 14px/1 var(--serif); color: var(--ink-faint); }
.plate__scroll { overflow-y: auto; flex: 1; overscroll-behavior: contain; }
.plate__head { padding-top: clamp(48px, 7vh, 84px); }
.plate__title { font: 500 clamp(38px, 5.4vw, 74px)/1.02 var(--serif); letter-spacing: -.02em; margin: 20px 0 22px; }
.plate__desc { max-width: 620px; font-size: 17px; color: var(--ink-soft); }
.plate__spec {
  display: flex; flex-wrap: wrap; gap: 0 44px; margin-top: 30px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.plate__spec > div { padding: 16px 0; display: flex; align-items: center; gap: 10px; }
.plate__spec dt { font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); }
.plate__spec dd { font: italic 500 16px/1 var(--serif); }
.plate__swatch { width: 15px; height: 15px; border-radius: 50%; border: 1px solid var(--line); display: inline-block; }
.plate__honest { margin-top: 18px; font: italic 500 14.5px/1.6 var(--serif); color: var(--rust); }
.plate__page { padding: clamp(36px, 5vh, 60px) 0 clamp(70px, 9vh, 110px); }
.plate__browser { max-width: 940px; margin: 0 auto; border: 1px solid var(--ink); box-shadow: 12px 12px 0 0 color-mix(in srgb, var(--kraft) 75%, transparent); }
.plate__browserbar {
  display: flex; align-items: center; gap: 7px; padding: 11px 14px;
  border-bottom: 1px solid var(--line); background: var(--paper-deep);
}
.plate__browserbar i { width: 9px; height: 9px; border-radius: 50%; border: 1px solid var(--ink-faint); }
.plate__browserbar span { margin-left: 10px; font: 500 11.5px/1 var(--sans); letter-spacing: .06em; color: var(--ink-faint); }
.plate__open {
  margin-left: auto; font: 700 10.5px/1 var(--sans); letter-spacing: .14em;
  text-transform: uppercase; text-decoration: none; color: var(--rust);
  padding: 6px 10px; border: 1px solid var(--line); white-space: nowrap;
}
.plate__open:hover { border-color: var(--rust); }

/* The stage: poster thumbnail underneath, live iframe on top once loaded.
   The iframe is inserted by JS on open and removed on close. */
.plate__stage { position: relative; background: var(--paper-deep); }
.plate__stage--live { height: clamp(420px, 74vh, 920px); }
.plate__poster { position: absolute; inset: 0; overflow: hidden; }
.plate__poster img { width: 100%; height: 100%; object-fit: cover; object-position: top; filter: saturate(.9); }
.plate__loading {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
  font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); background: color-mix(in srgb, var(--paper) 88%, transparent);
  padding: 8px 14px; border: 1px solid var(--line);
}
.plate__frame {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
  background: #fff; opacity: 0; transition: opacity .3s ease;
}
/* WHY the fade: the poster covers the stage until the iframe fires load, so
   there is never a white flash or a half-painted site on screen. */
.plate__stage.is-ready .plate__frame { opacity: 1; }
.plate__stage.is-ready .plate__poster { visibility: hidden; }
/* Fallback for sites without a live build yet: the tall full-page capture. */
.plate__stage--tall { height: auto; }
.plate__stage--tall .plate__full img { width: 100%; height: auto; }

/* ======================= PAGER (bounded-DOM collection) ======================= */
.pager { display: flex; align-items: center; justify-content: center; gap: 16px; margin-top: 40px; }
.pager button {
  font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft); padding: 11px 16px; border: 1px solid var(--line);
  transition: all .18s ease;
}
.pager button:hover:not(:disabled) { border-color: var(--rust); color: var(--rust); }
.pager button:disabled { opacity: .35; cursor: default; }
.pager__state { font: italic 500 15px/1 var(--serif); color: var(--ink-faint); }

/* ======================= CASE PAGES (static, /case/<slug>/) ======================= */
.case { padding-bottom: 40px; }
.case__open { margin: 30px 0 26px; }
.case__shot { border: 1px solid var(--ink); box-shadow: 10px 10px 0 0 color-mix(in srgb, var(--kraft) 75%, transparent); }
.case__shot img { width: 100%; height: auto; }
.case__back { margin-top: 34px; }
.case__back a { font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft); text-decoration: none; }
.case__back a:hover { color: var(--rust); }

/* ======================= RESPONSIVE ======================= */
@media (max-width: 1020px) {
  .indexer { grid-template-columns: 1fr; }
  .indexer__preview { display: none; }   /* hover preview is a pointer idea; touch gets the plate view */
  .steps { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .steps { grid-template-columns: 1fr; }
  .indexer__row { grid-template-columns: 50px minmax(0,1fr); }
  .indexer__tag { grid-column: 2; }
  .masthead__word { display: none; }
  .facts li { flex-basis: 45%; }
  .facts li + li { border-left: 0; padding-left: 0; }
}

/* ---- Trade hubs (SEO-PLAN 2.1) ----------------------------------------
   ADDITIVE ONLY. Nothing above this line was altered when the hubs landed:
   the hub pages reuse .shell, .label, .btn, .masthead and .footer as they are.
   Every rule here is namespaced .hub__* (or scoped under .hub) so it cannot
   reach the homepage, the plates or the case pages. */
.hub { padding-bottom: 40px; }
.hub__title { font: 600 clamp(30px, 5vw, 46px)/1.12 var(--serif); letter-spacing: -.02em; margin: 10px 0 26px; }

/* The prose half: the drafts' own headings and paragraphs, set for reading.
   Measure is held by .shell--narrow on the wrapper, not by a width here. */
.hub__prose h2 { font: 600 clamp(22px, 3vw, 29px)/1.22 var(--serif); letter-spacing: -.015em; margin: 38px 0 12px; }
.hub__prose h3 { font: 600 19px/1.3 var(--serif); margin: 28px 0 8px; }
.hub__prose p { margin: 0 0 16px; color: var(--ink-soft); max-width: 66ch; }
.hub__prose ul { margin: 0 0 16px 22px; color: var(--ink-soft); max-width: 66ch; }
.hub__prose li { margin-bottom: 7px; }
.hub__prose strong { color: var(--ink); font-weight: 600; }

.hub__gallery-title { font: 600 clamp(24px, 3.4vw, 33px)/1.18 var(--serif); letter-spacing: -.015em; margin: 54px 0 10px; }
/* The speculative-concept disclosure. Quiet, but never hidden: it carries the
   same claim the case pages and the /work/ badge make. */
.hub__disclosure { font: 500 13.5px/1.6 var(--sans); color: var(--ink-faint); margin: 0 0 30px; max-width: 70ch; }

.hub__gallery { display: grid; gap: clamp(26px, 3vw, 40px); grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.hub__card { border: 1px solid var(--line); background: color-mix(in srgb, var(--paper-deep) 42%, transparent); display: flex; flex-direction: column; }
.hub__shot { border-bottom: 1px solid var(--line); }
.hub__shot img { width: 100%; height: auto; }
.hub__card-body { padding: 20px clamp(18px, 2vw, 24px) 24px; display: flex; flex-direction: column; flex: 1; }
.hub__card-name { font: 600 21px/1.25 var(--serif); letter-spacing: -.01em; margin-bottom: 6px; }
.hub__card-trade { display: flex; align-items: center; gap: 8px; font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 14px; }
.hub__card-body p { margin: 0 0 13px; color: var(--ink-soft); font-size: 15px; }
/* Buttons sit at the card foot so a short and a long commentary still line up.
   🔴 The selector must stay at least as specific as `.hub__card-body p` above.
   It is a <p>, so that rule's `margin` SHORTHAND resets margin-top to 0 and the
   auto never applies: measured, the buttons sat up to 74px short of the card
   foot while the cards themselves were exactly equal height. Every automated
   check passed through it, because nothing was erroring or broken - it just
   looked untidy. Do not simplify this back to `.hub__card-links`. */
.hub__card-body .hub__card-links { margin-top: auto; padding-top: 8px; display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 0; }
.hub__card-links .btn { padding: 10px 16px; font-size: 11px; }

/* Case page -> its trade hub. The other half of the 2.4 linking spine. */
.hub__back, .case__hub { margin-top: 34px; }
.case__hub { font-size: 15px; color: var(--ink-soft); }
.case__hub a { color: var(--rust); }
.hub__back a { font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft); text-decoration: none; }
.hub__back a:hover { color: var(--rust); }

@media (max-width: 620px) {
  .hub__gallery { grid-template-columns: 1fr; }
}

/* The homepage's crawlable route into the hubs. Namespaced .hub__* like the
   rest of this block; the trade chips beside it are JS-built buttons, so these
   anchors are the only thing a crawler can follow from home to a hub. */
.hub__index { margin: 6px 0 30px; }
.hub__index-lead { font: 700 10.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 14px; }
.hub__index-list { display: flex; flex-wrap: wrap; gap: 9px; list-style: none; }
.hub__index-list a { display: inline-block; padding: 8px 14px; border: 1px solid var(--line); border-radius: 999px; font: 500 13.5px/1 var(--sans); color: var(--ink-soft); text-decoration: none; }
.hub__index-list a:hover { border-color: var(--rust); color: var(--rust); }

/* ═══ session 22: /about/, /contact/, pricing + FAQ blocks ═══════════════════
   APPENDED ONLY. Everything above line 481 is byte-identical to what shipped;
   md5 of the first 481 lines is ae097c660291a30aa15cb51e7c84e866. */

/* --- generic static page (about, contact) --- */
.page__title { font: 400 clamp(30px, 4.6vw, 46px)/1.14 var(--serif); margin: 10px 0 18px; }
.page__lede  { font-size: 18px; line-height: 1.6; color: var(--ink-soft); max-width: 62ch; }
.page__prose { max-width: 66ch; }
.page__prose p { line-height: 1.72; margin: 0 0 18px; }
.page__prose h2 { font: 400 24px/1.2 var(--serif); margin: 42px 0 14px; }
.page__cta { margin: 34px 0 90px; }

/* --- pricing block: on the trade pages, never in the nav --- */
.pricing { border-top: 1px solid var(--line-soft); margin-top: 72px; padding: 56px 0 8px; }
.pricing__title { font: 400 clamp(24px, 3.2vw, 34px)/1.2 var(--serif); margin: 0 0 28px; }
.pricing__grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.pricing__tier {
  border: 1px solid var(--line); border-radius: 10px; padding: 22px 20px 24px;
  background: var(--paper); display: flex; flex-direction: column;
}
/* The recommended tier is the reason the ladder exists. Make it obvious. */
.pricing__tier--rec { border-color: var(--rust); box-shadow: 0 0 0 1px var(--rust) inset; }
.pricing__flag {
  font: 600 11px/1 var(--sans); letter-spacing: .09em; text-transform: uppercase;
  color: var(--rust); margin: 0 0 10px;
}
.pricing__name  { font: 400 21px/1.2 var(--serif); margin: 0 0 6px; }
.pricing__from  { font-size: 15px; color: var(--ink-soft); margin: 0 0 12px; }
.pricing__from strong { font-size: 26px; color: var(--ink); font-weight: 500; }
.pricing__blurb { font-size: 15px; line-height: 1.6; color: var(--ink-soft); margin: 0 0 14px; }
.pricing__spec  { list-style: none; margin: 0 0 4px; padding: 0; font-size: 14.5px; }
.pricing__spec li { padding: 5px 0 5px 18px; position: relative; color: var(--ink-soft); }
.pricing__spec li::before { content: ''; position: absolute; left: 0; top: 12px; width: 8px; height: 1px; background: var(--rust); }
.pricing__care { margin: 26px 0 0; font-size: 15px; line-height: 1.6; color: var(--ink-soft); max-width: 66ch; }
.pricing__cta  { margin: 24px 0 0; }

/* --- FAQ: <details>, so the answers are in the HTML for a crawler
       whether or not they are open, and it works with no JS --- */
.faq { padding: 52px 0 10px; }
.faq__title { font: 400 clamp(24px, 3.2vw, 34px)/1.2 var(--serif); margin: 0 0 20px; }
.faq__item { border-bottom: 1px solid var(--line-soft); }
.faq__q {
  cursor: pointer; list-style: none; padding: 16px 30px 16px 0; position: relative;
  font-size: 16.5px; line-height: 1.45;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: '+'; position: absolute; right: 4px; top: 14px;
  font: 300 22px/1 var(--sans); color: var(--rust);
}
.faq__item[open] .faq__q::after { content: '\2013'; }
.faq__q:hover { color: var(--rust); }
.faq__a p { margin: 0 0 18px; max-width: 66ch; line-height: 1.7; color: var(--ink-soft); }

/* --- contact form --- */
.form { margin: 30px 0 44px; max-width: 560px; }
.form__row { margin: 0 0 18px; }
.form__row label { display: block; font-size: 14px; margin-bottom: 6px; color: var(--ink-soft); }
.form__opt { color: var(--ink-faint); }
.form input, .form select, .form textarea {
  width: 100%; box-sizing: border-box; font: 16px/1.4 var(--sans);
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: 7px; padding: 11px 12px;
}
.form input:focus, .form select:focus, .form textarea:focus {
  outline: none; border-color: var(--rust); box-shadow: 0 0 0 3px rgba(160, 82, 45, .13);
}
.form textarea { resize: vertical; }
/* The honeypot. Off-screen rather than display:none, because some bots skip
   fields that are display:none and fill the ones that are merely hidden. */
.form__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form__note { font-size: 14px; color: var(--ink-faint); margin: 12px 0 0; }
.form__sent {
  border: 1px solid var(--rust); border-radius: 8px; padding: 14px 16px;
  margin: 24px 0 0; font-size: 15.5px; background: var(--ghostpaper);
}
.contact__direct { margin: 0 0 10px; }

/* Session 22: the masthead gained an About link, taking the homepage nav to
   four items plus the theme toggle, which overflowed a 390px phone (measured:
   439px against a 390px viewport). Below 640px the SAME-PAGE ANCHORS drop out
   and About and the CTA stay, because those two are real pages and an anchor is
   reachable by scrolling anyway. Appended, not edited into the 640px block
   above: the first 481 lines are byte-frozen at ae097c660291a30aa15cb51e7c84e866. */
@media (max-width: 640px) {
  .masthead__nav .nav-secondary { display: none; }
}

/* The empty flag placeholder on non-recommended tiers needs a height, or an
   empty <p> collapses and the alignment problem it exists to fix comes back.
   Measured: without this the recommended card's price sat ~22px lower than its
   neighbours. */
.pricing__flag { min-height: 11px; }

/* Session 22, Rajat: three tiers across read congested, because each blurb had
   only a ~280px column and wrapped to four lines. Two across gives each card
   ~431px inside the 880px shell and the blurb settles on two.
   The THIRD tier spans the full width rather than sitting as an orphan beside
   an empty cell: measured both, and the empty cell reads as unfinished. It also
   happens to be the honest grouping, the two priced tiers together and the
   quote-only one below. Phone stays one per row. */
.pricing__grid { grid-template-columns: repeat(2, 1fr); }
.pricing__tier:last-child { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .pricing__grid { grid-template-columns: 1fr; }
  .pricing__tier:last-child { grid-column: auto; }
}

/* Session 22, Rajat: three gallery cards across read congested; one per row.
   The card turns SIDEWAYS rather than just becoming one column. Measured both:
   stacked at full width ran the commentary to ~1050px lines, far past a
   readable measure, and gave each design a huge image with a thin strip of text
   under it. Sideways keeps the text in a ~490px column, which is a normal
   measure, and still shows the design bigger than it was at three across.
   Every thumb is a uniform 560x350, so the image column cannot surprise us.
   object-position is TOP: the hero is the recognisable part of a design. */
.hub__gallery { grid-template-columns: 1fr; }
.hub__card { flex-direction: row; align-items: stretch; }
.hub__shot { border-bottom: 0; border-right: 1px solid var(--line); flex: 0 0 56%; }
.hub__shot a, .hub__shot picture, .hub__shot img { display: block; height: 100%; }
.hub__shot img { object-fit: cover; object-position: top center; }
/* Below this the two columns get too narrow to read, so the card stacks again. */
@media (max-width: 860px) {
  .hub__card { flex-direction: column; }
  .hub__shot { border-right: 0; border-bottom: 1px solid var(--line); flex: none; }
  .hub__shot a, .hub__shot picture, .hub__shot img { height: auto; }
}

/* 🔴 FIX, session 22. The rule above set the thumb to height:100% + object-fit:
   cover, so each image stretched to ITS OWN card's height. Card height follows
   the commentary length, which differs per design, so every thumbnail was
   cropped to a different shape: MEASURED 4 distinct ratios across 10 cards
   (1.13 to 1.30) against a source that is uniformly 560x350 = 1.60.
   Thumbnails must look the same everywhere. The image now holds the source
   ratio and sits at the top of the column; the column still stretches so the
   divider border runs the full height of the card. */
.hub__shot a, .hub__shot picture { display: block; height: auto; }
.hub__shot img { height: auto; aspect-ratio: 560 / 350; object-fit: cover; object-position: top center; }

/* ── THE PRICE ANCHOR ────────────────────────────────────────────────────────
   Ruling 14, as amended session 31: the three tier CARDS come off the hub page
   and one "from $1,000" anchor replaces them. TIERS still lives in
   tools/site-copy.mjs and still answers the FAQ, so the countable bound on the
   floor price survives where a customer who asks can find it.

   WHY THE PRICE IS THIS BIG. site-copy.mjs argues that an anchor has to be
   VISIBLE to anchor. It was not: measured on the live page, the largest text in
   the pricing section was the .pricing__title heading at 34px while the price
   was 26px, so the section's own heading out-sized the number it exists to
   state. At clamp(44px, 7vw, 68px) the price is the largest thing in the
   section at every width, which is the whole point of a floor price.

   The .pricing__grid, .pricing__tier, .pricing__flag, .pricing__name,
   .pricing__from, .pricing__blurb and .pricing__spec rules above are now dead.
   They stay: they sit inside the append-only first 481 lines that carry the
   md5 guard, and dead CSS is cheaper than breaking that hash. */
.pricing__anchor { padding: 4px 0 0; }
.pricing__anchor p { margin: 0 0 10px; font-size: 15px; color: var(--ink-soft); }
/* display:block puts the figure on its own line under the word "from", so the
   qualifier reads as a label instead of competing with the number. */
.pricing__anchor p strong { display: block; font: 400 clamp(44px, 7vw, 68px)/1 var(--serif); color: var(--ink); letter-spacing: -0.01em; }
.pricing__say { margin: 0; max-width: 54ch; font-size: 16px; line-height: 1.65; color: var(--ink-soft); }

/* ═══ session 32: the trade chips are gone ═══════════════════════════════════
   APPENDED, like everything after 481. The first 481 lines are still byte-frozen
   at ae097c660291a30aa15cb51e7c84e866 - verified before and after this append.

   .filters was `justify-content: space-between` with two children: the trade
   chips on the left, the view switcher pushed right by its own margin-left:auto.
   The chips are deleted (Rajat, 2026-09-15 - every design is a template that
   fits any business, so sorting the collection by trade argued against the
   offer), which leaves one child in a space-between row.

   🔴 NOT deleting .filters, .filters button, or .filters button.is-on, even
   though they read as chip rules. .filters__view button INHERITS its border,
   padding and transition from `.filters button`, and `#viewSwitch .is-on` uses
   the same is-on rule - deleting them strips the view switcher bare. That is the
   single easiest thing to get wrong here, so this overrides instead. */
.filters--solo { justify-content: flex-end; }

/* ⛔ .hub__*, .hub__index* and the .pricing__tier family are now dead: the 12
   trade pages that used them are deleted. Left in place on purpose - see the
   note at 695 - but note that note is WRONG about WHERE they sit. .pricing__grid
   and friends are at 558-575, OUTSIDE the guarded 481, so the hash is not what
   keeps them. They are simply not worth the churn to remove. */
