/* ==========================================================================
   NovaFusion — "batch record"

   Direction: each soap is a documented batch, not a catalogue entry. The page
   itself is deliberately colourless — cool archival paper, near-black ink — and
   the ONLY colour on any product page is the pigment of that specific soap.
   Cocoa browns Cocoa Noir; beetroot reddens Beetroot Bloom; charcoal greys
   London Noir. The colour comes from the product, never from a UI palette.

   Type is inverted from the usual: a heavy grotesque for display, a serif for
   reading, a monospace for anything measured. That inversion, plus the bar
   profile instrument, is where the boldness is spent. Everything else stays
   quiet.

   Fonts are self-hosted (SIL Open Font License) so the strict CSP holds and no
   request leaves this server.
   ========================================================================== */

@font-face {
  font-family: "Archivo";
  src: url("/assets/fonts/archivo-display.woff2") format("woff2");
  font-weight: 600 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/newsreader.woff2") format("woff2");
  font-weight: 300 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/newsreader-italic.woff2") format("woff2");
  font-weight: 300 500;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "DM Mono";
  src: url("/assets/fonts/dmmono.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- The typefaces, settled 2026-07-30 --------------------------------------
   Diana designs her product images in Montserrat, Roller Coaster Serif and
   Above the Beyond Script. Montserrat is open source and is used as-is. The
   other two are Canva fonts: their licence covers use inside Canva, not
   redistribution from a web server, so they are NOT installed. Fraunces (a
   wonky, soft-edged serif) and Allura (an elegant script) stand in for them.

   Put in as a trial and **approved by Diana on 2026-07-30** — she saw them live
   and kept them. This is the house type now: Fraunces for display, Montserrat
   for reading, Allura for the one handwritten line, DM Mono for anything
   measured. Do not swap them out without asking her.

   The Archivo and Newsreader faces below are the previous pairing. They are no
   longer referenced by any rule and browsers never download them; they stay only
   as the way back if she ever changes her mind. */
@font-face {
  font-family: "Montserrat";
  src: url("/assets/fonts/montserrat.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("/assets/fonts/fraunces.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Allura";
  src: url("/assets/fonts/allura.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* This site is light and only light. Without this line, Samsung Internet and
     Chrome's auto-dark invert it: the wordmark is dark ink on transparency, so
     it vanishes on a dark background, and the ink-filled buttons go black on
     black. Diana caught it on her own Samsung. Declaring the scheme tells those
     browsers to leave the page alone.

     If a real dark mode is ever wanted it has to be designed, not inverted —
     the whole direction is pigment-on-paper. */
  color-scheme: light;

  /* Cool archival paper. Explicitly not cream — cream plus serif plus
     terracotta is the house style of every AI-built brand site. */
  --paper:  #eff0ec;
  --card:   #f8f9f6;
  --ink:    #191b18;
  --muted:  #6a6e68;
  --rule:   #d5d8d0;
  --hair:   #e4e7e0;

  /* Overridden per soap below. Neutral fallback so nothing breaks. */
  --accent-ink:  #4a4f48;
  --accent-wash: #e9ebe6;

  /* TRIAL — see the note above the @font-face block to revert. */
  --display: "Fraunces", Georgia, serif;
  --serif:   "Montserrat", ui-sans-serif, system-ui, sans-serif;
  --script:  "Allura", "Snell Roundhand", cursive;
  --mono:    "DM Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  --measure: 34rem;
  --wrap:    72rem;
}

/* --- Per-soap accent -------------------------------------------------------
   Each soap tints its own page with its own pigment, kept pastel and low in
   saturation. Set as classes rather than inline style attributes so style-src
   can stay 'self' with no 'unsafe-inline'. */
[data-soap="lavande"]            { --accent-ink: #7b6a96; --accent-wash: #f1eef6; } /* lavender mica    */
[data-soap="golden-oat"]         { --accent-ink: #9a7c42; --accent-wash: #f7f2e6; } /* oats and honey   */
[data-soap="cocoa-noir"]         { --accent-ink: #7d5334; --accent-wash: #f4ece4; } /* light chocolate  */
[data-soap="sakura-rice"]        { --accent-ink: #b06a7c; --accent-wash: #faeef0; } /* cherry blossom   */
[data-soap="coconut-silk"]       { --accent-ink: #6f7370; --accent-wash: #f2f3f1; } /* coconut white    */
[data-soap="golden-calendula"]   { --accent-ink: #a8842f; --accent-wash: #f9f3e2; } /* calendula gold   */
[data-soap="turmeric-aloe"]      { --accent-ink: #a37b2b; --accent-wash: #f9f2e0; } /* turmeric         */
[data-soap="avocado-eucalyptus"] { --accent-ink: #6f8065; --accent-wash: #f0f3ec; } /* pale grey-green  */
[data-soap="green-goddess"]      { --accent-ink: #5f7a5c; --accent-wash: #eef2ec; } /* rosemary         */
[data-soap="london-noir"]        { --accent-ink: #4e555c; --accent-wash: #eceef0; } /* charcoal grey    */
[data-soap="cucumber-mist"]      { --accent-ink: #6d8578; --accent-wash: #eff3f0; } /* cucumber, pale   */
[data-soap="colombian-roast"]    { --accent-ink: #6f4a2c; --accent-wash: #f3ebe3; } /* coffee           */
[data-soap="cappuccino"]         { --accent-ink: #7a4f2a; --accent-wash: #f4e9dd; } /* browner, warmer  */
[data-soap="beetroot-bloom"]     { --accent-ink: #a8617a; --accent-wash: #f8eef1; } /* beetroot         */
[data-soap="beach-at-sunset"]    { --accent-ink: #b0764e; --accent-wash: #faf0e6; } /* sunset sand      */
[data-soap="naples-luxe"]        { --accent-ink: #b06d78; --accent-wash: #faeeef; } /* rose clay        */

/* The wash tints the whole page, so the colour of the soap is felt before it is
   noticed. */
body[data-soap] { background: var(--accent-wash); }

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: clamp(.9375rem, .9rem + .2vw, 1rem);
  font-weight: 400;
  line-height: 1.7;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem; z-index: 100;
  background: var(--ink); color: var(--paper); padding: .75rem 1rem;
}

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
}

/* --- Monospace utility label ---------------------------------------------- */

.tag {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- Header ---------------------------------------------------------------- */

/* --- Header ----------------------------------------------------------------
   Sticks to the top and travels down with the reader. A floating bar rather
   than a full-width band: it sits over the page instead of cutting it in two,
   which keeps the product photography uninterrupted. */

.head {
  position: sticky; top: 0; z-index: 40;
  padding: .7rem 1.25rem;
  /* The accent wash of whichever soap is on screen, so the bar belongs to the
     page it floats over. */
  background: color-mix(in srgb, var(--accent-wash) 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.head-in {
  width: min(100% , var(--wrap));
  margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .6rem clamp(.9rem, 2.2vw, 1.8rem);
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--card);
  padding: .6rem clamp(1rem, 2.5vw, 1.6rem);
  box-shadow: 0 1px 2px rgb(25 27 24 / .04), 0 8px 24px -12px rgb(25 27 24 / .12);
}

.head-mark { display: block; line-height: 0; }
.head-mark img { width: clamp(140px, 18vw, 200px); height: auto; }

.head-nav {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .45rem clamp(.8rem, 2vw, 1.6rem);
}
.head-nav a {
  text-decoration: none; font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .11em; text-transform: uppercase;
  padding-block: .2rem;
  border-bottom: 1px solid transparent;
}
.head-nav a:hover { border-bottom-color: var(--ink); }

.nav-lang {
  display: inline-flex; align-items: center; gap: .45rem;
  border: 1px solid var(--rule) !important;
  border-radius: 999px;
  padding: .25rem .7rem !important;
  color: var(--muted);
}
.nav-lang:hover { border-color: var(--ink) !important; color: var(--ink); }

/* The flag is the only saturated colour allowed in the chrome, so it is kept
   small and given a hairline edge — a stamp on the page, not a sticker. It
   greys back until the pill is hovered, so it never competes with the soap. */
.nav-flag {
  display: block; flex: 0 0 auto;
  filter: saturate(.75);
  transition: filter .2s ease;
}
.nav-lang:hover .nav-flag, .nav-lang:focus-visible .nav-flag { filter: saturate(1); }

.nav-cart { display: inline-flex; align-items: center; gap: .4rem; }
.nav-ico { display: block; flex: 0 0 auto; }

@supports not (backdrop-filter: blur(4px)) {
  .head { background: var(--accent-wash); }
}

/* --- Product hero ---------------------------------------------------------- */

.crumb { padding: 1.5rem 0 0; }
.crumb a { text-decoration: none; font-family: var(--mono); font-size: .75rem;
  letter-spacing: .08em; color: var(--muted); }
.crumb a:hover { color: var(--ink); }

.hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1.75rem, 4vw, 3.5rem);
  padding: clamp(1.5rem, 3vw, 2.25rem) 0 clamp(1.5rem, 3vw, 2.25rem);
  align-items: start;
}
@media (min-width: 54rem) {
  .hero { grid-template-columns: 1.12fr 1fr; }
}

.hero-img {
  border: 1px solid var(--rule);
  background: var(--card);
}

.hero-empty {
  aspect-ratio: 4 / 5;
  display: grid; place-items: center;
  margin: 0;
  font-family: var(--mono); font-size: .75rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  background: repeating-linear-gradient(45deg,
    var(--card) 0 10px, transparent 10px 20px);
}

/* The name is the loudest thing on the page and the only thing set this large.
   Archivo at 800 with negative tracking reads as a stamp, not a headline. */
.name {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.1rem, 1.3rem + 3.6vw, 3.9rem);
  line-height: 1;
  letter-spacing: .01em;
  margin: 0 0 1rem;
  text-wrap: balance;
}

.meta {
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: .7rem 0;
  margin-bottom: 1.5rem;
  display: flex; flex-wrap: wrap; gap: .5rem 1.1rem;
}
.meta .price { color: var(--ink); }

.pitch {
  font-family: var(--script);
  font-size: clamp(1.6rem, 1.3rem + 1.1vw, 2.15rem);
  line-height: 1.25;
  color: var(--accent-ink);
  margin: 0 0 1.5rem;
  max-width: 26rem;
}

.desc { max-width: var(--measure); margin-bottom: 1.75rem; }

/* Ingredient-base disclosure. Plain, adjacent to the price, never a badge that
   could be mistaken for a marketing sticker. */
.base {
  border-left: 2px solid var(--accent-ink);
  padding: .1rem 0 .1rem .9rem;
  margin-bottom: 1.75rem;
  max-width: var(--measure);
}
.base b {
  display: block;
  font-family: var(--mono); font-weight: 400;
  font-size: .6875rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent-ink);
  margin-bottom: .25rem;
}
.base span { font-size: .9375rem; color: var(--muted); }

.cta {
  display: inline-block;
  font-family: var(--mono);
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none;
  background: var(--ink); color: var(--paper);
  padding: .85rem 1.5rem;
  border: 0; cursor: pointer;
}
.cta:hover { background: var(--accent-ink); }
.cta:disabled {
  background: transparent; color: var(--muted);
  border: 1px solid var(--rule); cursor: not-allowed;
}
.cta:disabled:hover { background: transparent; }
.cta.is-max { background: var(--accent-ink); }

/* .btn is the same object under the name the home, shop and prose templates
   already use — one appearance, two names, rather than two appearances. */
.btn {
  display: inline-block;
  font-family: var(--mono);
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none;
  background: var(--ink); color: var(--paper);
  padding: .85rem 1.5rem; border: 0; cursor: pointer;
}
.btn:hover { background: var(--accent-ink); }

.btn-quiet {
  background: transparent; color: var(--ink);
  border: 1px solid var(--rule);
}
.btn-quiet:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.prose.center, .center { text-align: center; }

/* --- Stock and buy --------------------------------------------------------
   Bars cure for four weeks, so batches are genuinely small. Saying how many are
   left is honest rather than a scarcity trick — and it stops someone ordering
   ten of a soap Diana has four of. */

.buy { border-top: 1px solid var(--rule); padding-top: 1.25rem; }

.stock {
  font-family: var(--mono);
  font-size: .8125rem; letter-spacing: .06em; text-transform: uppercase;
  margin: 0 0 .85rem;
}
.stock b { font-weight: 400; color: var(--accent-ink); }
.stock.is-low { color: var(--accent-ink); }
/* "Sold out" and "Coming soon" used to be the faintest line on the page, which
   is backwards — it is the one thing a shopper must not miss. It now reads as a
   notice, and the dead button below it stops pretending to be pressable. */
.stock.is-out {
  display: block;
  color: var(--ink);
  background: var(--accent-wash);
  border-left: 2px solid var(--accent-ink);
  padding: .6rem .8rem;
}

.stock-note { font-size: .8125rem; color: var(--muted); margin: .85rem 0 0; max-width: 24rem; }

/* --- "Tell me when it's back" ----------------------------------------------
   Offered only on a bar with none left. Deliberately quiet — it is a courtesy
   to someone who arrived too late, not a device for harvesting addresses. */
.waitlist { margin-top: 1.5rem; padding-top: 1.35rem; border-top: 1px solid var(--rule); }
.wl-title {
  font-family: var(--serif); font-size: 1rem; font-weight: 500;
  margin: 0 0 .8rem; letter-spacing: 0;
}
.wl-form { display: flex; flex-wrap: wrap; gap: .5rem; align-items: stretch; }
.wl-form input[type="text"], .wl-form input[type="email"] {
  flex: 1 1 11rem; min-width: 0;
  font-family: var(--serif); font-size: .9375rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
  padding: .7rem .8rem;
}
.wl-form input:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; border-color: transparent; }
.wl-form .cta { flex: 0 0 auto; }
/* Off-screen rather than display:none — some bots skip hidden fields but fill
   positioned ones, which is exactly what gives them away. */
.wl-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.wl-privacy { font-size: .8125rem; color: var(--muted); margin: .7rem 0 0; }
.wl-done {
  border-left: 2px solid var(--accent-ink);
  background: var(--accent-wash);
  padding: .7rem .9rem; margin: 0; font-size: .9375rem;
}
.wl-error { border-left: 2px solid #a8324a; color: #8d2a3e; padding: .5rem .9rem; margin: 0 0 .8rem; font-size: .9375rem; }

/* Choose the quantity before adding, so nobody has to add one bar four times. */
.buy-row { display: flex; flex-wrap: wrap; align-items: stretch; gap: .75rem; }

.qty { display: flex; align-items: stretch; border: 1px solid var(--rule); background: var(--card); }
.qty .step { border: 0; width: 2.6rem; }
.qty-input {
  width: 3.2rem;
  font-family: var(--mono); font-size: 1rem; text-align: center;
  color: var(--ink); background: transparent;
  border: 0; border-inline: 1px solid var(--rule);
  padding: .5rem 0;
  -moz-appearance: textfield;
  appearance: textfield;
}
.qty-input::-webkit-outer-spin-button,
.qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-input:focus { outline: 2px solid var(--accent-ink); outline-offset: -2px; }

/* The count carries the accent of whichever soap is on screen, so the cart
   belongs to the page it is sitting on rather than to a generic UI. */
.cart-count {
  font-family: var(--mono); font-size: .625rem;
  background: var(--accent-ink); color: var(--paper);
  border-radius: 999px; padding: .1rem .4rem; margin-left: .35rem;
}

/* --- Cart page ------------------------------------------------------------- */

.cart-list { border-top: 1px solid var(--rule); margin: 1.5rem 0; }
.cart-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .35rem 1rem;
  align-items: center;
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--hair);
}
@media (min-width: 40rem) {
  .cart-row { grid-template-columns: 1fr 8rem 6rem; }
}
.cart-name { font-family: var(--serif); font-size: 1.125rem; margin: 0; }
.cart-unit { font-family: var(--mono); font-size: .75rem; color: var(--muted); margin: 0; grid-column: 1; }
/* The number needs room to breathe between the two buttons — Diana found it
   cramped, and a 2-digit quantity was pushing against both. */
.cart-controls { display: flex; align-items: center; gap: .9rem; }
.cart-qty {
  font-family: var(--mono); font-size: 1rem;
  min-width: 2.5ch; text-align: center;
}
.cart-line { font-family: var(--mono); font-size: .9375rem; margin: 0; text-align: right; }

.step {
  font-family: var(--mono); font-size: 1rem; line-height: 1;
  width: 2rem; height: 2rem;
  background: transparent; color: var(--ink);
  border: 1px solid var(--rule); cursor: pointer;
}
.step:hover:not(:disabled) { border-color: var(--ink); }
.step:disabled { opacity: .35; cursor: not-allowed; }

.cart-total {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; padding-top: .5rem;
}
.cart-sum { font-family: var(--mono); font-size: 1.375rem; margin: 0; }

/* --- Checkout form --------------------------------------------------------- */

.checkout { max-width: 38rem; }

.notice, .error {
  border-left: 2px solid var(--accent-ink);
  padding: .7rem 0 .7rem .9rem;
  font-size: .9375rem;
  margin-bottom: 1.75rem;
}
.error { border-left-color: #a8324a; color: #8d2a3e; }

.co-empty { color: var(--muted); }

.co-summary { border-block: 1px solid var(--rule); padding: 1rem 0; margin-bottom: 1.75rem; }
.co-line { font-family: var(--mono); font-size: .8125rem; margin: 0 0 .3rem; }
.co-sum { font-size: 1.125rem; margin-top: .6rem; }

.field { margin-bottom: 1.1rem; }
.field label {
  display: block;
  font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  margin-bottom: .35rem;
}
.field input, .field textarea {
  width: 100%;
  font-family: var(--serif); font-size: 1rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
  padding: .7rem .8rem;
}
.field input:focus, .field textarea:focus {
  outline: 2px solid var(--accent-ink); outline-offset: 1px; border-color: transparent;
}
.field textarea { resize: vertical; }

.field-row { display: grid; grid-template-columns: 1fr; gap: 0 1rem; }
@media (min-width: 34rem) { .field-row { grid-template-columns: 2fr 1fr 1fr; } }

/* Honeypot: off-screen rather than display:none, because some bots skip
   hidden fields but not positioned ones. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.ref { font-family: var(--mono); font-size: 1.25rem; margin: 0 0 1.5rem; color: var(--accent-ink); }

/* --- Contact --------------------------------------------------------------- */

.contact-page { padding: clamp(2.5rem, 6vw, 4.5rem) 0; }
.contact-grid { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 5vw, 4rem); }
@media (min-width: 52rem) { .contact-grid { grid-template-columns: 1fr 1fr; } }
.contact-grid .lede { max-width: 30rem; }
.contact-ig { margin-top: 2rem; }
.ct-ok { font-family: var(--serif); font-size: 1.125rem; margin-bottom: 1.25rem; }

/* --- Home ------------------------------------------------------------------
   The hero is two columns: the sentence on the left, the photograph of four
   finished bars on the right. The bars are named underneath in the order they
   stand in the picture, so the key is information — which bar is which — and
   the way most people will first walk into the catalogue.

   Note the two-class selector: `.hero` alone belongs to the product page, where
   it is a 1.12fr / 1fr grid. `.hero.hero-home` outranks it on specificity, so
   the home page can set its own layout without that rule being touched. */

.hero { border-bottom: 1px solid var(--rule); }

.hero.hero-home { display: block; }

.hero-home-in {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: center;
  padding: clamp(2.5rem, 6vw, 4.5rem) 0 clamp(2rem, 4vw, 3rem);
}
@media (min-width: 56rem) {
  .hero-home-in { grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); }
}

/* The wording and the type treatment are exactly as Diana approved them before
   the photograph arrived — only the column they sit in is new. */
.hero-home h1 {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2rem, 1.3rem + 3.4vw, 3.75rem);
  line-height: .95; letter-spacing: -.03em;
  margin: 0 0 1rem; text-wrap: balance;
}
.hero-home .lede { margin: 0 0 2rem; max-width: 32rem; }

.hero-shot { margin: 0; }
.hero-shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--rule); background: var(--card);
}

.hero-key { margin-top: .75rem; }
.hero-key-label {
  display: block;
  font-family: var(--mono); font-size: .625rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}
.hero-key-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: .35rem;
  font-family: var(--mono); font-size: .625rem; line-height: 1.35;
  letter-spacing: .02em; text-align: center; color: var(--muted);
}
/* A hairline rises from each name to the edge of the photograph, so the label
   points at its own bar rather than floating under the picture. */
.hero-key-list li { position: relative; padding-top: 1rem; }
.hero-key-list li::before {
  content: ""; position: absolute; top: 0; left: 50%;
  width: 1px; height: .6rem; background: var(--rule);
}
.hero-key-list a {
  color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--rule);
}
.hero-key-list a:hover { border-bottom-color: var(--ink); }

/* Four across on a phone too. Diana, 2026-07-30: two by two breaks the one
   thing this element is for — each name sitting under its own bar in the
   photograph. So the type shrinks to fit instead of the grid folding, and the
   hairline ticks stay, because they are what ties a name to a bar. */
@media (max-width: 30rem) {
  /* 9px in DM Mono: "Mermaid Shore" is the longest at 13 characters, about
     70px, and a column on a 430px phone is about 94px — so it sits on one
     line. On a very narrow phone it wraps at the space onto two lines, which
     is still four columns and still one name per bar. */
  .hero-key-list { font-size: .5625rem; letter-spacing: 0; gap: .3rem .25rem; }
  .hero-key-list li { padding-top: .8rem; }
  .hero-key-list li::before { height: .45rem; }
  .hero-key-label { font-size: .5625rem; letter-spacing: .1em; }
}

.band { padding: 2.25rem 0; border-bottom: 1px solid var(--hair); }
.marks {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .75rem clamp(1.5rem, 4vw, 3rem);
  font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}
.marks li { display: flex; align-items: center; gap: .5rem; }
.marks li::before {
  content: ""; width: .3rem; height: .3rem; border-radius: 50%;
  background: var(--accent-ink);
}

.section { padding: clamp(2.5rem, 6vw, 4.5rem) 0; }
.section-title {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.375rem, 1.15rem + .9vw, 2rem);
  letter-spacing: -.02em; text-align: center; margin-bottom: 2.25rem;
}
.section-more { text-align: center; margin-top: 2.5rem; }
.page-title {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2rem, 1.4rem + 2.6vw, 3.25rem);
  line-height: .96; letter-spacing: -.03em; margin-bottom: .75rem;
}

.story { border-block: 1px solid var(--rule); background: var(--card); }
.story-inner { padding: clamp(2.5rem, 6vw, 4.5rem) 0; max-width: 42rem; }
.story-inner h2 {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.375rem, 1.15rem + .9vw, 1.875rem); letter-spacing: -.02em;
}

.link { color: var(--accent-ink); text-underline-offset: 3px; }
.empty { color: var(--muted); padding: 2rem 0; }

/* --- Product grid ---------------------------------------------------------- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15.5rem, 100%), 1fr));
  gap: clamp(1rem, 2.5vw, 1.75rem);
}

.card { background: var(--card); border: 1px solid var(--rule); }
.card-link { text-decoration: none; display: block; height: 100%; }
.card-media { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: var(--paper); }
/* A bar that is curing rather than sold: says so on the photograph, quietly. */
.card-flag {
  position: absolute; top: .6rem; left: .6rem; z-index: 1;
  font-family: var(--mono); font-size: .625rem;
  letter-spacing: .1em; text-transform: uppercase;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--rule); padding: .3rem .5rem;
}
.card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.card-link:hover .card-media img { transform: scale(1.03); }
.card-media.is-empty {
  display: grid; place-items: center;
  background: repeating-linear-gradient(45deg, var(--paper) 0 10px, var(--card) 10px 20px);
}
.card-empty {
  font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.card-body { padding: .9rem 1rem 1.1rem; }
.card-name {
  font-family: var(--serif); font-weight: 400; font-size: 1.125rem;
  letter-spacing: 0; margin: 0 0 .15rem;
}
.card-scent { font-size: .8125rem; color: var(--muted); margin-bottom: .8rem; }
.card-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; flex-wrap: wrap; margin: 0;
}
.price { font-family: var(--mono); font-size: 1rem; }

/* --- Shop filters ---------------------------------------------------------- */

.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1.75rem 0 2.5rem; }
.filter {
  display: inline-flex; align-items: center; gap: .5rem;
  text-decoration: none;
  font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .09em; text-transform: uppercase;
  border: 1px solid var(--rule); background: var(--card);
  padding: .55rem 1rem;
}
.filter:hover { border-color: var(--ink); }
.filter.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.filter-count { opacity: .6; }

/* --- Body butters ---------------------------------------------------------- */

.butters { padding: clamp(2rem, 5vw, 3.5rem) 0 4rem; }

.bb-hero {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center;
  margin-bottom: 1rem;
}
@media (min-width: 52rem) { .bb-hero { grid-template-columns: 1fr 1fr; } }

.bb-copy .lede { max-width: 32rem; }
.bb-shot { border: 1px solid var(--rule); background: var(--card); }

/* --- Signature: the bar profile -------------------------------------------
   Diana measures every batch. Most soap brands cannot show this because they
   do not have the numbers; she does. Drawn as an instrument, not a rating. */

.profile {
  border-block: 1px solid var(--ink);
  padding: clamp(1.15rem, 2.2vw, 1.6rem) 0;
  margin-bottom: clamp(1.25rem, 2.5vw, 2rem);
}
.profile-head {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 1rem;
  margin-bottom: 1.1rem;
}
.profile-note { font-size: .8125rem; color: var(--muted); max-width: 30rem; margin: 0; }

.scales { display: grid; gap: .85rem; }

/* label | min | track | max | value — the numbers live in HTML so they stay
   crisp while the SVG track stretches. */
.scale {
  display: grid;
  grid-template-columns: 9.5rem 2ch 1fr 2ch 3ch;
  gap: .6rem .8rem;
  align-items: center;
}
@media (max-width: 40rem) {
  .scale { grid-template-columns: 1fr 2ch 3ch; }
  .scale-label { grid-column: 1 / -1; }
  .scale-track { grid-column: 1; }
}

.scale-label { font-family: var(--mono); font-size: .75rem; letter-spacing: .09em;
  text-transform: uppercase; }
.scale-end { font-size: .625rem; }
.scale-val {
  font-family: var(--mono); font-size: .9375rem;
  color: var(--accent-ink); text-align: right;
}

.scale-track { width: 100%; height: 24px; display: block; }
.scale-track .tr { stroke: var(--rule);       stroke-width: 1; }
.scale-track .tk { stroke: var(--hair);       stroke-width: 1; }
.scale-track .mk { stroke: var(--accent-ink); stroke-width: 3; }

/* Heading kept for structure and screen readers; the eyebrow carries it
   visually. */
.sr-h {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* --- Sections -------------------------------------------------------------- */

.sect { padding: clamp(1.5rem, 3vw, 2.25rem) 0; border-top: 1px solid var(--hair); }
.sect:first-of-type { border-top: 0; }

.sect-head { margin-bottom: 1rem; }
.sect h2 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.35rem, 1.15rem + .7vw, 1.75rem);
  letter-spacing: -.015em;
  margin: .35rem 0 0;
}

.sect-body { max-width: var(--measure); }

/* --- Notes on the bar -----------------------------------------------------
   Scent, texture, base and origin. There is deliberately no "skin" row: stating
   what a bar does to skin is the claim pattern that cost Diana her Instagram
   ads, and "restores the lipid barrier" — on the page this borrows from — is a
   drug claim. Every row here states a fact about the bar itself. */

.notes-sect { display: grid; grid-template-columns: 1fr; gap: clamp(1.15rem, 2.2vw, 2.25rem); }
@media (min-width: 54rem) {
  .notes-sect { grid-template-columns: 0.72fr 1fr; }
  .notes-intro { position: sticky; top: 2rem; }
}
.notes-lede { font-size: .9375rem; color: var(--muted); margin-top: 1rem; max-width: 24rem; }

.rows { margin: 0; }
.row {
  display: grid; grid-template-columns: 1fr; gap: .35rem 1.75rem;
  padding: 1rem 0; border-top: 1px solid var(--hair);
}
.row:first-child { border-top: 0; padding-top: 0; }
@media (min-width: 34rem) { .row { grid-template-columns: 7rem 1fr; } }

.row dt { padding-top: .35rem; }
.row dd { margin: 0; }
.row h3 {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.15rem, 1.05rem + .4vw, 1.375rem);
  letter-spacing: 0; line-height: 1.25; margin: 0 0 .4rem;
}
.row p { margin: 0; font-size: .9375rem; color: var(--muted); }

/* --- Gallery --------------------------------------------------------------- */

.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: 1rem; }
.gallery img { border: 1px solid var(--rule); background: var(--card); }
/* --- Process clip ---------------------------------------------------------- */

.clip { margin: 0; max-width: 44rem; }
.clip video {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--rule);
  background: var(--card);
}
.clip figcaption { margin-top: .6rem; }

.gallery-empty {
  border: 1px dashed var(--rule);
  padding: 2.5rem 1.25rem;
  text-align: center;
  color: var(--muted);
  font-family: var(--mono); font-size: .75rem; letter-spacing: .09em; text-transform: uppercase;
}

/* --- Compliance panels -----------------------------------------------------
   FDA cosmetic labelling. Always open, always full, never behind a toggle. */

.panel {
  background: var(--card);
  border: 1px solid var(--rule);
  padding: clamp(1.1rem, 2.4vw, 1.6rem);
  margin-top: .9rem;
}
.panel h2 {
  font-family: var(--mono); font-weight: 400;
  font-size: .75rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 1rem;
}
.inci { font-size: .9375rem; line-height: 1.8; margin: 0 0 .9rem; }
.foot-note { font-size: .8125rem; color: var(--muted); margin: 0; }
.warn { margin: 0; padding-left: 1.15rem; font-size: .9375rem; }
.warn li { margin-bottom: .35rem; }

/* --- Reviews --------------------------------------------------------------- */

.reviews-empty {
  border: 1px dashed var(--rule);
  padding: 2rem 1.25rem;
  text-align: center;
  color: var(--muted);
  max-width: var(--measure);
}
.reviews-empty p { margin: 0; font-size: .9375rem; }

/* --- Footer ---------------------------------------------------------------- */

.foot { background: var(--ink); color: var(--paper); margin-top: 3rem;
  padding: clamp(2.25rem, 4vw, 3rem) 0 1.25rem; }
.foot-in { display: flex; flex-wrap: wrap; gap: 1.5rem 3rem; justify-content: space-between;
  padding-bottom: 1.75rem; border-bottom: 1px solid rgb(255 255 255 / .14); }
.foot p { margin: 0; }
.foot .tag { color: rgb(239 240 236 / .55); }
.foot-name { font-family: var(--display); font-weight: 700; font-size: 1.25rem; letter-spacing: -.02em; }
.foot a { color: inherit; text-underline-offset: 3px; }
.copy { text-align: center; padding-top: 1.25rem; font-family: var(--mono);
  font-size: .6875rem; letter-spacing: .06em; color: rgb(239 240 236 / .45); }

/* --- Scroll reveal ---------------------------------------------------------
   Guarded by .js-reveal, which reveal.js sets only once it is running. Without
   JavaScript nothing is ever hidden. One short settle per element, never
   replayed — repeating motion is what makes a page feel machine-made. */

.js-reveal .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .6s cubic-bezier(.22, .61, .36, 1),
              transform .6s cubic-bezier(.22, .61, .36, 1);
}
.js-reveal .reveal.is-in { opacity: 1; transform: none; }

/* Rows arrive one after another rather than all at once. */
.js-reveal .row:nth-child(2) { transition-delay: .07s; }
.js-reveal .row:nth-child(3) { transition-delay: .14s; }
.js-reveal .row:nth-child(4) { transition-delay: .21s; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .js-reveal .reveal { opacity: 1; transform: none; }
}

/* --- Diana's private waitlist page -----------------------------------------
   Not part of the shop. Plain, dense and readable: it exists to answer one
   question, which is what to make next. */
.admin { padding: clamp(2rem, 5vw, 3.5rem) 0 4rem; max-width: 52rem; }
.admin h1 { font-family: var(--display); font-weight: 400; margin: .3rem 0 1rem; }
.admin-block { border-top: 1px solid var(--rule); padding-top: 1.25rem; margin-top: 2rem; }
.admin-block h2 {
  font-family: var(--serif); font-size: 1.125rem; font-weight: 500;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .75rem; margin: 0 0 .3rem;
}
.admin-count {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--paper); background: var(--accent-ink);
  padding: .25rem .55rem;
}
.admin-stock { font-size: .875rem; color: var(--muted); margin: 0 0 1rem; }
.admin-table { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.admin-table th {
  text-align: left; font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  border-bottom: 1px solid var(--rule); padding: .4rem .5rem .4rem 0;
}
.admin-table td { padding: .5rem .5rem .5rem 0; border-bottom: 1px solid var(--hair); }
.admin-table a { color: var(--ink); }
.admin-foot { margin-top: 2.5rem; font-size: .8125rem; color: var(--muted); }

/* Figures line up on their decimal point so a column can be read down rather
   than word by word — Diana's note, 2026-08-03: the prose version was "demasiada
   letra". The tax column is the one that is not hers, so it carries the weight. */
.admin-table th.num, .admin-table td.num { text-align: right; white-space: nowrap; }
.admin-table td.num { font-variant-numeric: tabular-nums; }
.admin-table td.is-mine { font-weight: 600; }
/* The soap column is the one she is looking for: of everything that arrives,
   it is the only part she keeps. Postage goes to USPS, tax to Florida. */

/* --- Phone layout ----------------------------------------------------------
   Diana, 2026-07-30, from a screenshot of her own phone: the nav broke into
   three ragged rows pinned to the left while the wordmark above them sat
   centred, which read as untidy rather than composed. Everything inside the
   floating bar is centred now. The pill also loses its extreme radius: at three
   rows tall a 999px radius turns the short sides into half-circles, which is
   what made the bar look like a lozenge instead of a header. */
@media (max-width: 52rem) {
  .head { padding: .55rem .75rem; }
  .head-in {
    border-radius: 1.25rem;
    padding: .8rem .9rem;
    gap: .55rem .9rem;
  }
  /* Bumped from 150px on 2026-08-01 at Diana's request. The bar has room:
     at 390px wide, minus the pill's padding, about 330px are available, so
     185px leaves the mark comfortably centred without crowding the edges. */
  .head-mark img { width: 185px; }
  .head-nav { width: 100%; justify-content: center; row-gap: .6rem; }
}

/* One column on a phone, so the type is centred with the page rather than
   hanging left underneath a centred wordmark. */
@media (max-width: 56rem) {
  .hero-home-text { text-align: center; }
  .hero-home .lede { margin-inline: auto; }
}

/* The four names must stay in one row, one under each bar in the photograph —
   that correspondence IS the element, and two columns destroyed it. So the type
   shrinks to fit instead: at 390px each column has about 5.5rem, which holds
   "Cappuccino" on one line at this size. */


/* --- Reviews ---------------------------------------------------------------
   The form is offered to everyone; nothing reaches this page until Diana has
   read it. The stars are a radio group drawn in reverse DOM order so a plain
   CSS sibling selector can light up the chosen star and everything before it —
   no script, which keeps the strict CSP intact. */
.rv-stars { font-size: 1rem; letter-spacing: .1em; color: var(--accent-ink); margin: 0 0 .3rem; }

.rv-form-wrap { border-top: 1px solid var(--rule); margin-top: 2.5rem; padding-top: 1.5rem; max-width: 34rem; }
.rv-form { display: grid; gap: .6rem; }

.rv-rating { border: 0; margin: 0; padding: 0; display: flex; flex-direction: row-reverse; justify-content: flex-end; }
.rv-rating legend { font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); margin-bottom: .3rem; float: left; width: 100%; }
.rv-rating input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.rv-rating label {
  font-size: 1.5rem; line-height: 1; cursor: pointer;
  color: var(--rule); padding: 0 .1rem; transition: color .15s ease;
}
.rv-rating input:checked ~ label,
.rv-rating label:hover, .rv-rating label:hover ~ label { color: var(--accent-ink); }
.rv-rating input:focus-visible + label { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.rv-fields { display: grid; gap: .6rem; }
@media (min-width: 34rem) { .rv-fields { grid-template-columns: 1fr 1fr; } }

.rv-form input[type="text"], .rv-form input[type="email"], .rv-form textarea {
  width: 100%;
  font-family: var(--serif); font-size: .9375rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
  padding: .7rem .8rem;
}
.rv-form textarea { resize: vertical; min-height: 6rem; }
.rv-form input:focus, .rv-form textarea:focus {
  outline: 2px solid var(--accent-ink); outline-offset: 1px; border-color: transparent;
}
.rv-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; margin: .2rem 0 0; }
.rv-note { font-size: .8125rem; color: var(--muted); flex: 1 1 14rem; }

/* --- Review moderation, on Diana's private page ---------------------------- */
.admin-h2 { font-family: var(--serif); font-size: 1.125rem; font-weight: 500; margin: 3rem 0 0; }
.admin-warn {
  border-left: 2px solid #a8324a; background: #fbf1f3;
  padding: .8rem 1rem; margin: 0 0 1.5rem; font-size: .875rem; line-height: 1.6;
}
.admin-review { border: 1px solid var(--rule); background: var(--card); padding: 1rem; margin-bottom: 1rem; }
.admin-review.is-live { background: transparent; }
.admin-review-head { margin: 0 0 .5rem; font-size: .9375rem; display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.admin-review-head .rv-stars { font-size: .875rem; margin: 0; }
.admin-when { font-family: var(--mono); font-size: .6875rem; color: var(--muted); }
.admin-review-body { margin: 0 0 .8rem; padding-left: .9rem; border-left: 2px solid var(--rule); font-size: .9375rem; }
.admin-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0; }
.admin-no {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  background: transparent; color: var(--muted);
  border: 1px solid var(--rule); padding: .85rem 1.5rem; cursor: pointer;
}
.admin-no:hover { border-color: #a8324a; color: #8d2a3e; }

/* --- Money breakdown in the cart -------------------------------------------
   Subtotal, shipping and total on their own lines. The nudge under it is the
   one piece of persuasion on the page, and it earns its place: with bars at
   $10, someone buying three usually adds a fourth to clear the threshold. */
.cart-extra { margin-top: .6rem; }
.cart-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; margin: .3rem 0;
  font-family: var(--mono); font-size: .8125rem; color: var(--muted);
}
.cart-row-total {
  color: var(--ink); font-size: 1rem;
  border-top: 1px solid var(--rule); padding-top: .5rem; margin-top: .5rem;
}
.cart-nudge {
  font-size: .875rem; color: var(--ink);
  background: var(--accent-wash); border-left: 2px solid var(--accent-ink);
  padding: .6rem .8rem; margin: .9rem 0 0;
}

/* --- Surviving a browser that forces dark mode -----------------------------
   Samsung Internet can be set to darken every site regardless of what the page
   declares, and `color-scheme: light` does not stop it. What it does is invert
   backgrounds and text but NOT images — so the photographs stay right and the
   wordmark, which is dark ink on transparency, vanishes into the dark
   background. Diana found this on her own phone.

   Inverting the wordmark when the browser reports a dark preference turns it
   white, which is exactly what it needs to be on a dark background. The soap
   photographs are deliberately left alone: inverting those would be a disaster.

   This is a rescue, not a dark theme. A real one would have to be designed —
   the whole direction is pigment on paper. */
@media (prefers-color-scheme: dark) {
  .head-mark img,
  .foot-mark img { filter: invert(1); }
}

/* The media query above only fires if the browser announces a dark preference.
   Samsung Internet's forced dark mode does not announce anything — it just
   darkens — so the wordmark still vanished on Diana's phone.

   A soft light halo behind the mark works either way and asks nothing of the
   browser: on the paper background it is invisible, and on a darkened one it
   traces the letters so the name still reads. Not elegant, but it survives a
   browser doing whatever it likes. */
.head-mark img {
  filter: drop-shadow(0 0 1.5px rgba(255, 255, 255, .95))
          drop-shadow(0 0 3px rgba(255, 255, 255, .55));
}

/* --- How to pay, on the confirmation page ---------------------------------- */
.pay-box {
  border: 1px solid var(--rule); background: var(--card);
  padding: 1rem 1.1rem; margin-top: 1.5rem;
}
.pay-line { margin: .2rem 0; font-size: 1.0625rem; }
.pay-box .foot-note { margin-top: .7rem; }
.pay-note-box {
  font-family: var(--mono); font-size: 1rem; line-height: 1.5;
  background: var(--accent-wash); border-left: 2px solid var(--accent-ink);
  padding: .7rem .9rem; margin: .6rem 0 0;
}

/* --- Reserve and made-to-order --------------------------------------------- */
.buy-note { font-size: .875rem; color: var(--muted); margin: .4rem 0 .8rem; max-width: 26rem; }
.ships-box {
  border-left: 2px solid var(--accent-ink); background: var(--accent-wash);
  padding: .8rem 1rem; margin: 1.25rem 0 0;
}
.ships-box b { font-size: 1.125rem; }

/* --- Orders on Diana's panel ------------------------------------------------ */
.admin-order { border: 1px solid var(--rule); background: var(--card); padding: 1rem; margin-bottom: 1rem; }
.admin-order.is-done { background: transparent; opacity: .72; }
.admin-order-line { margin: .15rem 0; font-size: .9375rem; }
.admin-order-total { margin: .5rem 0 .3rem; font-family: var(--mono); font-size: 1.0625rem; }
.order-state {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .1em;
  text-transform: uppercase; padding: .2rem .5rem; border: 1px solid var(--rule);
}
.order-pending_payment { background: #fbf1f3; border-color: #a8324a; color: #8d2a3e; }
.order-paid      { background: #eef2ec; border-color: #5f7a5c; color: #3f5a3c; }
.order-cancelled { color: var(--muted); }
.order-tag {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--paper); background: var(--accent-ink);
  padding: .15rem .45rem; margin-left: .4rem;
}
.order-ships {
  font-size: .875rem; background: var(--accent-wash);
  border-left: 2px solid var(--accent-ink); padding: .5rem .7rem; margin: .5rem 0;
}

/* --- The homepage settling in ----------------------------------------------
   One movement, once, on load. The headline, the line under it, the button and
   the photograph arrive a beat apart rather than all at once — the same order
   someone reads them in.

   Deliberately only here. A page where everything slides in from somewhere is
   the house style of every template on earth; a page with one composed opening
   and then stillness reads as considered. The other moment on this site is the
   needle in the bar profile, and that one means something.

   The reduced-motion block above collapses these to nothing, and the elements
   end up exactly where they belong either way. */
@keyframes settle {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

.hero-home-text h1,
.hero-home-text .lede,
.hero-home-text .btn,
.hero-home .hero-shot {
  animation: settle .75s cubic-bezier(.22, .61, .36, 1) both;
}
.hero-home-text h1     { animation-delay: .06s; }
.hero-home-text .lede  { animation-delay: .18s; }
.hero-home-text .btn   { animation-delay: .30s; }
.hero-home .hero-shot  { animation-delay: .14s; animation-duration: .9s; }

/* --- The header, on a desktop ----------------------------------------------
   Diana, 2026-08-01: on a laptop the bar read as lost against everything else
   on screen. It was sized for a phone and never given room to grow. The
   wordmark gains about a fifth, the links go from 11px to 13px, and the bar
   itself gets taller — enough to hold the page down without becoming a banner.

   Only above 54rem. The phone layout is untouched: she approved that one. */
@media (min-width: 54rem) {
  .head { padding: 1.1rem 1.25rem; }
  /* Wider than the 72rem the rest of the page uses. The bar is the one element
     that should feel as broad as the screen it sits on, and the extra room is
     what lets the type grow without the links wrapping. */
  /* Wordmark centred on its own line, menu beneath it. Diana saw this happen
     by accident on the Spanish pages — the longer Spanish words push the menu
     onto a second row — and preferred it to the English one-line version. She
     is right: a mark with its own line reads as a masthead, a mark tucked
     beside the links reads as a toolbar. Now it is deliberate and identical in
     both languages. */
  .head-in {
    width: min(100% - 2.5rem, 84rem);
    flex-direction: column;
    align-items: center;
    padding: 1.1rem 2rem 1rem;
    gap: .75rem;
  }
  .head-nav { justify-content: center; }
  .head-mark img { width: clamp(220px, 18vw, 290px); }
  .head-nav { gap: .5rem clamp(1.2rem, 2.2vw, 2.2rem); }
  .head-nav a { font-size: .9375rem; letter-spacing: .08em; padding-block: .35rem; }
  .nav-lang { padding: .4rem 1rem !important; gap: .55rem; }
  .nav-flag { width: 22px; height: auto; }
  .nav-ico { width: 26px; height: 26px; }
  .cart-count { font-size: .75rem; padding: .18rem .5rem; }
}

/* --- The one handwritten line ----------------------------------------------
   Allura was installed with the rest of the type and deliberately kept unused
   until a line deserved it. This is the line: Diana's daughter's name for the
   room she works in. It closes the story page like a signature, which is what
   it is.

   Only here. A script face used twice stops being special. */
.signature {
  margin-top: 1.75rem;
  /* Same ink as every other paragraph. It went grey by accident and Diana
     caught it — the line is part of the story, not a caption. */
  color: var(--ink);
}
.signature span {
  /* Diana tried the script face and an editorial italic and wanted neither:
     the same body face, bold and a size up. She was right. The line is her
     daughter's words, not a flourish, and it lands harder said plainly.

     No font-family here on purpose — it inherits the page's, so if the type
     ever changes this follows without being touched. */
  font-weight: 700;
  font-size: 1.25em;
}


/* --- How would you like to pay -----------------------------------------------
   Two big targets rather than a dropdown: this is the last decision before the
   money, and it should be readable at arm's length on a phone. */
.pay-choice { border: 0; padding: 0; margin: 1.75rem 0 0; }
.pay-choice legend {
  font-family: var(--serif); font-size: 1rem; font-weight: 500;
  padding: 0; margin-bottom: .7rem;
}
.pay-option {
  display: flex; gap: .7rem; align-items: flex-start;
  border: 1px solid var(--rule); background: var(--card);
  padding: .85rem 1rem; margin-bottom: .6rem; cursor: pointer;
}
.pay-option:has(input:checked) { border-color: var(--accent-ink); }
.pay-option input { margin-top: .2rem; accent-color: var(--accent-ink); }
.pay-option b { display: block; font-weight: 600; font-size: .9375rem; }
.pay-option small { display: block; color: var(--muted); font-size: .8125rem; margin-top: .15rem; }
.pay-option.is-testing { border-style: dashed; }
.pay-option .pay-testing { color: #8d2a3e; font-weight: 600; }

/* The saving is the reason to choose Zelle, so it reads before the explanation
   rather than after it. */
.pay-option .pay-saving { color: var(--accent-ink, var(--ink)); font-weight: 600; }
.cart-row.is-saving span:last-child { color: var(--accent-ink, var(--ink)); }

/* Posting a parcel: the tracking number and the button that files it. Its own
   block rather than a row of buttons, because typing 22 digits from a receipt
   deserves a field wide enough to check them in. */
.admin-ship { margin-top: .9rem; }
.admin-ship-label { display: block; font-size: .75rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); margin-bottom: .35rem; }
.admin-ship-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.admin-ship-row input {
  flex: 1 1 16rem; min-width: 0;
  font-family: var(--mono); font-size: .875rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0; padding: .6rem .7rem;
}
.admin-ship-row input:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
.admin-ship .admin-when { margin-top: .4rem; }
.admin-ship-row select {
  flex: 0 0 auto;
  font-family: var(--mono); font-size: .875rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0; padding: .6rem .5rem;
}

/* The till — selling to somebody standing in front of her.
   Sized for a phone held in one hand at a market stall: the quantity boxes are
   deliberately large, because the commonest error here is a mis-tap that sells
   the wrong number of bars to somebody already walking away. */
.till { margin-top: 1rem; }
.till h2 { font-size: .8rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin: 1.6rem 0 .5rem; }
.till-hint { font-size: .85rem; color: var(--muted); margin: 0 0 .7rem; }
.till-list { list-style: none; margin: 0; padding: 0; }
.till-row { display: flex; align-items: center; gap: .75rem;
  padding: .55rem 0; border-bottom: 1px solid var(--rule); }
.till-row label { flex: 1 1 auto; min-width: 0; cursor: pointer; }
.till-name { display: block; }
.till-meta { display: block; font-size: .8rem; color: var(--muted); }
.till-qty {
  flex: 0 0 4.5rem; width: 4.5rem; text-align: center;
  font-family: var(--mono); font-size: 1.15rem; padding: .55rem .3rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
}
.till-qty:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
.till-field { display: block; margin-bottom: .7rem; }
.till-field span { display: block; font-size: .8rem; color: var(--muted); margin-bottom: .25rem; }
.till-field input {
  width: 100%; box-sizing: border-box;
  font-size: 1rem; padding: .6rem .7rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
}
.till-field input:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
.till-check { display: block; margin: 1rem 0; font-size: .9rem; }
.till-check small { display: block; color: var(--muted); margin-left: 1.6rem; }
.till-pay { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .8rem; }
.till-pay .cta { flex: 1 1 8rem; text-align: center; line-height: 1.35; }
.till-pay .cta small { font-weight: 400; opacity: .85; font-size: .72rem; }
.admin-pay-link { word-break: break-all; font-family: var(--mono); font-size: .8rem; }

/* The code the customer scans. White plate under it on purpose: a QR printed
   straight onto a warm paper background loses contrast, and this one gets read
   off a phone screen at arm's length. */
.till-qr { margin: .9rem 0; padding: .75rem; background: #fff;
  display: inline-block; line-height: 0; border: 1px solid var(--rule); }
.till-qr svg { display: block; width: 100%; height: auto; max-width: 17rem; }
.till-qr-note { margin-top: 0; }

/* The title reads like the calculator's, not like the storefront's — Diana
   asked for that on 2026-08-06, and it is the same reasoning she gave for the
   calculator on 2026-08-05: these are her workshop tools, not shop pages, and
   they should look like each other rather than like the window display.
   Scoped to this page so the orders panel is left alone. */
.till-page h1 {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: -.01em;
}

/* The address the receipt went to. Set in mono and large enough to read out
   loud: a mistyped one is silent, and the customer has already walked away by
   the time anybody finds the bounce. */
.till-sentto { margin: .8rem 0 .2rem; font-size: 1.05rem; }
.till-sentto b { font-family: var(--mono); word-break: break-all; }

/* What the card's bank said about a payment. Placed above the shipping
   controls: a bad signal has to be read before a parcel goes in the post, not
   after. Colour carries the same order everywhere on this panel — a left bar
   rather than a filled box, so a green one does not shout. */
.admin-cardcheck { border-left: 3px solid var(--rule); padding: .55rem .8rem;
  margin: .9rem 0; background: var(--card); }
.admin-cardcheck-head { margin: 0; font-size: .9rem; font-weight: 600; }
.admin-cardcheck-note { margin: .35rem 0 0; font-size: .85rem; color: var(--muted); }
.admin-cardcheck.is-ok      { border-left-color: #2f7d52; }
.admin-cardcheck.is-odd     { border-left-color: #b8791f; }
.admin-cardcheck.is-failed  { border-left-color: #9d2f2f; }
.admin-cardcheck.is-failed .admin-cardcheck-note { color: var(--ink); }

/* The Zelle details, printed large next to the code. The camera is the happy
   path; this is for when it will not focus and Diana has to read the number
   out, which at a market is often. */
.till-zelle { border: 1px solid var(--rule); padding: .9rem 1rem; margin: .9rem 0; }
.till-zelle-line { margin: 0 0 .3rem; font-size: 1.05rem; }
.till-zelle-note { margin: .7rem 0 0; padding-top: .7rem; border-top: 1px solid var(--hair);
  font-family: var(--mono); font-size: .95rem; line-height: 1.5; }

/* "Already arrived" is the quieter of the two Zelle buttons: showing the
   customer where to send it is the one she will reach for at a stall, and the
   other is for money that turned up before she rang it in. */
.till-pay-quiet { opacity: .72; }
.till-pay-quiet:hover, .till-pay-quiet:focus { opacity: 1; }

/* Writing an order to post, for a customer she has but bars she does not.
   Folded away: the four payment buttons above are the fast path at a stall,
   and this is the deliberate one. */
.till-ship { margin-top: 1.6rem; border-top: 1px solid var(--rule); padding-top: 1rem; }
.till-ship > summary { cursor: pointer; font-weight: 600; padding: .4rem 0; }
.till-ship > summary:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.till-ship[open] > summary { margin-bottom: .6rem; }
.till-ship-row { display: flex; flex-wrap: wrap; gap: .6rem; }
.till-ship-row .till-field { flex: 1 1 7rem; }
.till-ship-row select {
  width: 100%; box-sizing: border-box; font-size: 1rem; padding: .6rem .5rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
}

/* The second way to settle an unpaid order. */
.pay-or { margin: 1rem 0 .6rem; color: var(--muted); font-size: .85rem;
  text-transform: uppercase; letter-spacing: .08em; }

/* The two ways out of an unmatched Zelle. Confirming is the primary action and
   keeps the cta styling; dismissing is quieter, because it is the one she will
   press most and it should not feel like the point of the section. */
.admin-zelle-actions { margin-top: .9rem; }
.admin-zelle-actions select {
  flex: 1 1 18rem; min-width: 0;
  font-family: var(--serif); font-size: .875rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0; padding: .6rem .7rem;
}
.admin-zelle-dismiss { margin-top: .5rem; }

/* When nothing matches, assigning to an order is the rare case, so it folds
   away and "not a shop payment" is what she sees.
   It must still LOOK pressable, though: Diana read the quiet grey version as
   "no me estás dando ninguna opción" and did not try clicking it. Underlined
   and in the accent colour, it reads as a link, which is what it is. */
.admin-assign { margin-top: .7rem; }
.admin-assign summary {
  cursor: pointer;
  font-size: .875rem;
  color: var(--accent-ink, var(--ink));
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: .35rem 0;
  list-style: none;
}
.admin-assign summary::-webkit-details-marker { display: none; }
.admin-assign summary::before { content: "▸ "; text-decoration: none; }
.admin-assign[open] summary::before { content: "▾ "; }
.admin-assign[open] summary { margin-bottom: .6rem; text-decoration: none; color: var(--ink); }

/* --- Private soap calculator ----------------------------------------------
   /calculadora/<token>. Diana's replacement for soapcalc.net, built 2026-08-04.

   It borrows the site's typefaces and palette rather than inventing its own:
   it is her tool, in her shop's clothes, and a second visual language for one
   private page would be noise. The numbers are set in the mono face because
   they are read as figures, not prose — and because a misread weight of lye is
   not a cosmetic problem.

   No JavaScript anywhere on this page. The oil picker is a plain <datalist>,
   which the browser filters as she types. */

.calc { padding-block: 2.5rem 4rem; }
.calc-head h1 { font-family: var(--display); font-weight: 400; margin: 0; }
.calc-sub { color: var(--muted); margin: .25rem 0 2rem; }
.calc-sub2 { color: var(--muted); font-size: .8rem; margin: .5rem 0 0; }

.calc-alert, .calc-ok, .calc-warn {
  padding: .85rem 1rem; margin: 0 0 1.5rem;
  border-left: 3px solid var(--rule); background: var(--card);
  font-size: .9rem;
}
.calc-alert { border-left-color: #a33; }
.calc-ok    { border-left-color: var(--accent-ink); }
.calc-warn  { border-left-color: #a33; margin-block: 1.25rem; }

/* Which formula the page is sitting on top of. It has to read as a warning
   without shouting: "update" from here replaces the recipe it names. */
.calc-editing {
  padding: .85rem 1rem; margin: 0 0 1.5rem;
  border-left: 3px solid #b8860b; background: var(--card);
  font-size: .9rem;
}
.calc-editing strong { font-size: 1.05rem; }
.calc-editing .calc-sub2 { margin-top: .25rem; }

/* The made-on date read back mes/día/año, because the native date box follows
   the Mac's language and hers offers día/mes/año. */
.calc-madeon-us { display: block; margin-top: .35rem; letter-spacing: .04em; }
.calc-madeon-us:empty { display: none; }
.calc-madeon-us.is-bad { color: #a33; }

/* Month, day, year — three boxes of ours, so no browser gets to reorder them.
   They sit in a row and stay in a row: read as a date, not as three questions.
   The form's own rule makes every field display:block at full width, which is
   what was stacking them, so this overrides it for these three only. */
.calc-madeon { display: block; margin: 1rem 0 0; max-width: 30rem; }
.calc-mdy {
  display: flex; gap: .4rem; align-items: center;
  flex-wrap: nowrap; margin-top: .25rem;
}
.calc-mdy select, .calc-mdy input {
  display: inline-block; width: auto; margin-top: 0;
}
.calc-mdy select[name="made_on_m"] { flex: 0 1 11rem; min-width: 6.5rem; }
.calc-mdy select[name="made_on_d"] { flex: 0 0 4.5rem; }
.calc-mdy input[name="made_on_y"]  { flex: 0 0 5rem; }

.calc-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.calc fieldset {
  border: 1px solid var(--hair); background: var(--card);
  padding: 1rem 1.15rem 1.15rem; margin: 0;
}
.calc legend {
  font-family: var(--mono); font-size: .7rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink);
  padding-inline: .4rem;
}
.calc label { display: block; margin-block: .55rem; font-size: .9rem; }
.calc input[type="text"], .calc select {
  display: block; width: 100%; margin-top: .25rem;
  font-family: var(--mono); font-size: .95rem;
  padding: .5rem .6rem; background: var(--paper);
  border: 1px solid var(--rule); color: var(--ink);
}
.calc input[type="radio"], .calc input[type="checkbox"] { margin-right: .4rem; }
.calc input:focus, .calc select:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }

.calc-oils { margin-top: 1rem; }
.calc-entry { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-bottom: .75rem; }
.calc-entry label { margin: 0; }

.calc-table { width: 100%; border-collapse: collapse; margin-block: .75rem 0; }
.calc-table th {
  text-align: left; font-family: var(--mono); font-weight: 400;
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--rule); padding: .4rem .5rem;
}
.calc-table td { padding: .3rem .5rem; border-bottom: 1px solid var(--hair); vertical-align: top; }
.calc-table .num, .calc-table th.num { text-align: right; }
.calc-table .num input { text-align: right; }
.calc-inci { color: var(--muted); font-size: .72rem; font-style: italic; }

.calc-actions {
  display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap;
  margin-block: 1.5rem;
}
.calc-name { flex: 1 1 14rem; margin: 0 !important; }
.calc-load {
  display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
  padding-top: 1rem; border-top: 1px solid var(--hair);
  font-size: .85rem; color: var(--muted);
}
.calc-load label { margin: 0; }

.calc-result { margin-top: 3rem; border-top: 2px solid var(--ink); padding-top: 1.5rem; }
.calc-result h2 { font-family: var(--display); font-weight: 400; margin: 0 0 1rem; }
.calc-result h3 {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent-ink);
  margin: 2rem 0 0; padding-bottom: .3rem;
}

.calc-numbers {
  display: grid; gap: 1px; background: var(--rule);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  border: 1px solid var(--rule);
}
.calc-big { background: var(--card); padding: .9rem 1rem; }
.calc-label {
  display: block; font-family: var(--mono); font-size: .65rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.calc-big strong { font-family: var(--mono); font-size: 1.15rem; font-weight: 500; }
.calc-alt { color: var(--muted); font-size: .75rem; font-weight: 400; }

.calc-qual .num strong { font-family: var(--mono); }
.calc-verdict { font-family: var(--mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; }
.calc-qual .v-ok   .calc-verdict { color: var(--accent-ink); }
.calc-qual .v-low  .calc-verdict,
.calc-qual .v-high .calc-verdict { color: #a33; }
.calc-acids { max-width: none; }

/* Printed, it becomes the sheet that goes on the workbench beside the pot. */
@media print {
  .calc-form, .site-header, .site-footer, .calc-sub { display: none; }
  .calc-result { border-top: 0; }
  .calc { padding: 0; }
}

/* Additions after Diana used it on her phone, 2026-08-05: a totals row she was
   otherwise working out on a calculator, and room for additives and notes. */
.calc textarea {
  display: block; width: 100%; margin-top: .25rem;
  font-family: var(--serif); font-size: .9rem; line-height: 1.5;
  padding: .5rem .6rem; background: var(--paper);
  border: 1px solid var(--rule); color: var(--ink); resize: vertical;
}
.calc textarea:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
.calc-total td {
  border-top: 2px solid var(--ink); border-bottom: 0;
  font-family: var(--mono); font-weight: 500; padding-top: .5rem;
}
.calc-free {
  background: var(--card); border-left: 3px solid var(--hair);
  padding: .75rem 1rem; margin: .5rem 0 0; font-size: .9rem;
}

/* Calculator, second pass after she used it — 2026-08-05.
   The recipe name moved to the top where a recipe starts, a searchable oil
   list like SoapCalc's, and a grams column that fills in as she types. The
   .calc-jsonly cells stay hidden until the script proves it is running, so a
   failed download leaves no empty columns behind. */
.calc-title-field { margin: 0 0 1.5rem !important; max-width: 30rem; }
.calc-title-field input { font-family: var(--display) !important; font-size: 1.1rem !important; }

.calc-jsonly { display: none; }
.calc-jsonly-on { display: table-cell; }
tfoot.calc-jsonly-on { display: table-footer-group; }

.calc-browse {
  display: grid; gap: .5rem; margin-bottom: 1rem;
  padding: .9rem 1rem; background: var(--paper); border: 1px solid var(--hair);
}
.calc-browse label { margin: 0 !important; }
.calc-browse select {
  width: 100%; font-family: var(--mono); font-size: .85rem;
  background: var(--card); border: 1px solid var(--rule); color: var(--ink);
  padding: .25rem;
}
.calc-browse select option { padding: .2rem .4rem; }
.calc-browse-foot {
  display: flex; gap: 1rem; align-items: center; justify-content: space-between;
  flex-wrap: wrap;
}
.calc-browse-foot span {
  font-family: var(--mono); font-size: .72rem; color: var(--muted);
}
.calc-off { color: #a33; }
.calc-when {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .06em;
  color: var(--muted); margin: -.5rem 0 1rem;
}
@media print { .calc-when { color: var(--ink); } }

/* The oil profile panel — her ask, 2026-08-05: see what an oil brings before
   putting it in, the way SoapCalc's section 5 does. Its "One" and "All"
   columns are named here for what they actually are; she said she did not
   know the difference, and a label is cheaper than a legend. */
.calc-profile { margin-top: .75rem; font-size: .8rem; }
.calc-profile th { font-size: .62rem; padding: .3rem .4rem; }
.calc-profile td { padding: .18rem .4rem; }
.calc-profile [data-one] { font-family: var(--mono); color: var(--accent-ink); }
.calc-profile [data-all] { font-family: var(--mono); font-weight: 500; }
.calc-profile .calc-range { font-size: .7rem; color: var(--muted); }
.calc-acid-head td {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
  padding-top: .6rem !important; border-bottom: 1px solid var(--rule);
}
.calc-live {
  font-family: var(--mono); font-size: .78rem; color: var(--accent-ink);
  margin: .35rem 0 0;
}

/* Picker on the left, recipe on the right — 2026-08-05, her observation that
   the page was a tall column with the right-hand half empty. One column again
   below 62rem, because side by side on a phone is two unreadable halves. */
.calc-two { display: block; }
@media (min-width: 62rem) {
  .calc-two {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
  }
  .calc-two > .calc-browse { margin-bottom: 0; }
  .calc-two > #calc-rows { margin-top: 0; }
}
/* The recipe table is the thing she is filling in; give it room to breathe. */
#calc-rows th:first-child { width: 45%; }

/* --- Calculator layout, third pass — 2026-08-05 --------------------------
   Three things she pointed at, all of them fair.

   1. The section numbers sat ON the border, outside the white box. A <legend>
      renders on the edge by definition; floating it puts it inside, which is
      where a heading belongs. The padding-top on the fieldset is what stops
      the float from colliding with the first field.
   2. Qualities and fatty acids now sit side by side in their own row across
      the full width, instead of one tall column under the picker.
   3. The result reads like her SoapCalc sheets: boxed, ruled, aligned. */

.calc fieldset { padding-top: .35rem; }
.calc legend {
  float: left; width: 100%; padding: 0 0 .55rem; margin: 0;
  border-bottom: 1px solid var(--hair);
}
.calc legend + * { clear: both; }

.calc-profile-two {
  display: grid; gap: 1.25rem; margin-top: 1.25rem;
  grid-template-columns: 1fr;
}
@media (min-width: 62rem) {
  .calc-profile-two { grid-template-columns: 1.15fr 1fr; }
}
.calc-profile-two table { margin: 0; }
.calc-profile-two thead th:first-child {
  font-size: .68rem; color: var(--accent-ink);
}

/* The result, laid out the way her SoapCalc printouts are: a boxed summary,
   then ruled tables that line up down the right-hand edge. */
.calc-result h3 { border-bottom: 1px solid var(--hair); }
.calc-result .calc-table {
  border: 1px solid var(--hair); background: var(--card);
}
.calc-result .calc-table th { background: var(--accent-wash); }
.calc-result .calc-table td { padding: .35rem .6rem; }
.calc-result-two {
  display: grid; gap: 1.25rem; grid-template-columns: 1fr; margin-top: .5rem;
}
@media (min-width: 62rem) {
  .calc-result-two { grid-template-columns: 1.3fr 1fr; align-items: start; }
}
.calc-result-two h3 { margin-top: 0; }

/* --- Calculator spacing, fourth pass — 2026-08-05 -------------------------
   Three things she noticed with the page open in front of her, and all three
   were about breathing room rather than function.

   1. The oil list stopped short and left a gap beside a much taller recipe
      table. Stretching the column and letting the <select> grow means the two
      halves end level, whatever the row count.
   2. Additives and notes were three lines deep, so writing a batch note meant
      scrolling inside a box. Seven.
   3. The rule under "1 · SOSA" sat right on top of the first radio button. */

@media (min-width: 62rem) {
  .calc-two { align-items: stretch; }
  .calc-two > .calc-browse { display: flex; flex-direction: column; }
  .calc-two > .calc-browse > select { flex: 1 1 auto; min-height: 20rem; }
}

.calc textarea { min-height: 9rem; }

.calc legend { padding-bottom: .6rem; }
.calc legend + * { clear: both; margin-top: .9rem !important; }
.calc fieldset { padding-top: .5rem; }

/* Room under every rule — 2026-08-05, "las opciones están muy pegadas a la
   línea… en todos los puntos dale espacio". Applies to the section legends,
   the result headings and the table heads alike, so the whole page breathes
   the same way instead of one box at a time. */
.calc legend + * { margin-top: 1.35rem !important; }
.calc fieldset { padding: .5rem 1.15rem 1.35rem; }
.calc-result h3 { padding-bottom: .5rem; margin-bottom: 1rem; }
.calc-table thead th { padding-bottom: .55rem; }
.calc-table tbody tr:first-child td { padding-top: .6rem; }
.calc-head { margin-bottom: 2rem; }

/* More still — she asked twice, which means once was not enough. The gap under
   a section rule is now larger than the gap between the fields below it, so the
   heading reads as a heading and not as the first row of the group. */
.calc legend + * { margin-top: 1.9rem !important; }
.calc-result h3 { margin-bottom: 1.3rem; }

/* --- Why the gap under the section rules never moved ----------------------
   She asked three times for space between "1 · SOSA" and the first field, and
   three times nothing happened. The cause was mine and it is a CSS rule worth
   remembering: a <legend> floated to sit inside the box is out of flow, so the
   field below it uses `clear` to get past — and **clearance replaces margin**.
   Every `margin-top` I added was being discarded by the browser, correctly.

   The space has to live on the float itself. A float's bottom margin extends
   its box, and clearance lands below that. */
.calc legend {
  margin-bottom: 1.5rem;
  padding-bottom: .55rem;
}
.calc legend + * { margin-top: 0 !important; }

/* And the boxed look she asked for: each field reads as its own cell rather
   than a line of text with an input after it. Colour is deliberately left
   alone — she said she would decide about that separately. */
.calc fieldset > label {
  display: block;
  background: var(--paper);
  border: 1px solid var(--hair);
  padding: .6rem .7rem;
  margin: 0 0 .6rem;
  font-size: .82rem;
  color: var(--muted);
}
.calc fieldset > label:last-of-type { margin-bottom: 0; }
.calc fieldset > label input[type="text"],
.calc fieldset > label select {
  border: 0; background: transparent; padding: .15rem 0 0;
  font-size: 1rem; color: var(--ink);
}
.calc fieldset > label input[type="radio"],
.calc fieldset > label input[type="checkbox"] { margin-right: .5rem; }
.calc-title-field { background: none !important; border: 0 !important; padding: 0 !important; }

/* The language switch becomes a submit button on the calculator so a finished
   recipe survives the change of language. It has to look identical to the link
   it replaces — a button that announced itself as a button would be a worse
   header for the sake of one private page. */
button.nav-lang {
  font: inherit; color: inherit; background: none; border: 1px solid var(--rule);
  cursor: pointer;
}

/* Bars beside the quality numbers — green inside the recommended range, red
   outside, the way SoapCalc draws them. Her ask, 2026-08-05.

   The width is a class, never a style attribute: the site runs
   `style-src 'self'` and an inline style would simply be dropped by the
   browser. Twenty steps is finer than the eye reads at this size. */
.calc-barcell { width: 34%; padding-right: 1rem !important; }
.calc-bar {
  display: block; height: .55rem; border-radius: 1px;
  background: var(--accent-ink); min-width: 2px;
}
.v-low .calc-bar, .v-high .calc-bar { background: #a33; }
.calc-qual tr { background: transparent; }
.calc-bar-0 { width: 0%; }
.calc-bar-5 { width: 5%; }
.calc-bar-10 { width: 10%; }
.calc-bar-15 { width: 15%; }
.calc-bar-20 { width: 20%; }
.calc-bar-25 { width: 25%; }
.calc-bar-30 { width: 30%; }
.calc-bar-35 { width: 35%; }
.calc-bar-40 { width: 40%; }
.calc-bar-45 { width: 45%; }
.calc-bar-50 { width: 50%; }
.calc-bar-55 { width: 55%; }
.calc-bar-60 { width: 60%; }
.calc-bar-65 { width: 65%; }
.calc-bar-70 { width: 70%; }
.calc-bar-75 { width: 75%; }
.calc-bar-80 { width: 80%; }
.calc-bar-85 { width: 85%; }
.calc-bar-90 { width: 90%; }
.calc-bar-95 { width: 95%; }
.calc-bar-100 { width: 100%; }

/* Search and "add to the recipe" on one line — 2026-08-05. They are the two
   halves of a single action and they were at opposite ends of a twelve-row
   list. The button keeps its own baseline so it lines up with the input rather
   than with the label above it. */
.calc-browse-top {
  display: flex; gap: .75rem; align-items: flex-end; flex-wrap: wrap;
}
.calc-browse-top label { flex: 1 1 12rem; margin: 0 !important; }
.calc-browse-top .btn { flex: 0 0 auto; white-space: nowrap; padding: .7rem 1.1rem; }
.calc-browse-foot { min-height: 1.1rem; }

/* Actual green and actual red — 2026-08-05. The bars were drawn in the site's
   ink colour, which is a very dark olive and reads as black: "no estoy viendo
   si está en verde o está en rojo". A bar whose whole job is to answer one
   question at a glance has to answer it.

   Both tones are dark enough to stay legible against the cream page and to
   survive being printed in greyscale, where the red still reads darker. */
.calc-bar        { background: #2f7d4f !important; }
.calc-bar-track  { background: var(--hair); display: block; width: 100%; border-radius: 1px; }
.v-low .calc-bar, .v-high .calc-bar { background: #b03030 !important; }
.calc-qual .v-ok   .calc-verdict { color: #2f7d4f; }
.calc-qual .v-low  .calc-verdict,
.calc-qual .v-high .calc-verdict { color: #b03030; }

/* The quality table carries a bar and a range and was being squeezed until
   "136 – 165" broke across two lines, while the fatty-acid list sat in a wide
   column holding eight short numbers. Her observation, 2026-08-05. */
@media (min-width: 62rem) {
  .calc-result-two { grid-template-columns: 1.75fr 1fr; }
}
.calc-qual td:nth-child(4) { white-space: nowrap; }
.calc-profile .calc-range  { white-space: nowrap; }

/* The line that tells her the recipe is being kept, and the only way to clear
   it. Quiet on purpose: it is a reassurance, not an action she needs to take. */
.calc-kept {
  display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
  font-size: .8rem; color: var(--muted);
  padding: .6rem .8rem; margin: 1rem 0 0;
  background: var(--card); border-left: 3px solid var(--accent-ink);
}
.calc-clear {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .08em;
  text-transform: uppercase; cursor: pointer;
  background: none; border: 1px solid var(--rule); color: var(--ink);
  padding: .4rem .7rem;
}
.calc-clear:hover { background: var(--ink); color: var(--paper); }

/* The same colour code as the PDF and as SoapCalc — 2026-08-05. Her reasoning
   is the one that matters: "hay menos probabilidades de error cuando uno está
   tomando pesos". Page and sheet must not disagree about which colour the lye
   is, or the code stops helping and starts confusing. */
.calc-big.b-oils  { background: #fffadb; }
.calc-big.b-lye   { background: #fce4e4; }
.calc-big.b-water { background: #e1f2fa; }
.calc-big.b-frag  { background: #ece5f9; }
.calc-big.b-total { background: #f0f0f0; }

/* --- Pastel blocks on the page, matching the sheet — 2026-08-05 -----------
   Her words: "algo bien sutil pero más tierno, algo más como yo 🌸". Each
   block gets the same wash it has in the PDF, so the page and the printout
   are recognisably the same document. Soft enough that black text sits on it
   comfortably and a home printer does not turn it to mud. */
.calc-result-two > div:first-child .calc-table { background: #eff7f0; }   /* bar quality */
.calc-result-two > div:last-child  .calc-table { background: #edf4f9; }   /* fatty acids */
.calc-result .calc-numbers { background: #f3f8fc; }                        /* the weights */
.calc-result h3 + .calc-table tbody tr:nth-child(even) { background: #f5fbf5; }
.calc-free { background: #fefaea !important; border-left-color: #e8dfa8 !important; }
.calc-result .calc-table th { background: rgba(255,255,255,.55); }

/* The whole result reads as one sheet rather than four loose tables. */
.calc-result {
  background: #fcfdfc; padding: 1.5rem 1.5rem 2rem;
  border: 1px solid var(--hair);
}

/* The fatty acids are one flat blue — her call, 2026-08-05: "ahí no necesitamos
   diferenciar uno del otro, este es más bien informativo". Striping earns its
   keep on the oils, where she reads across to a weight; here it would be
   pattern for its own sake. */
.calc-result h3 + .calc-table tbody tr:nth-child(even) { background: transparent; }
/* The stripe was #f5fbf5 against a #f8f9f6 card — two colours three points
   apart, which is no stripe at all. White and a green that can actually be
   seen, matching the PDF.

   The Total had no colour of its own, so it inherited the card's and landed
   the same shade as the last oil above it. It is not another oil: it is the
   answer to whether the recipe is whole, and it now reads as one — its own
   tone, a rule above it, and the figures in bold. */
.calc-oilrows tbody tr:nth-child(odd)  { background: #ffffff; }
.calc-oilrows tbody tr:nth-child(even) { background: #e7f3e7; }
.calc-oilrows tfoot .calc-total td {
  background: #dfe6df;
  border-top: 2px solid var(--accent-ink);
  font-weight: 600;
  padding-top: .55rem; padding-bottom: .55rem;
}
.calc-result-two > div:last-child .calc-table,
.calc-result-two > div:last-child .calc-table tbody tr { background: #edf4f9; }

/* The recipe-name field was set in Fraunces, the display serif, and it was the
   one thing on the page that looked like it came from somewhere else — she
   spotted it against the nav's Montserrat capitals without being told what
   either face was. Montserrat, like the rest of the calculator.

   Nothing else moves: she said the page and the PDF are right as they are. */
.calc-title-field input {
  font-family: var(--serif) !important;
  font-size: 1.05rem !important;
  letter-spacing: .01em;
}

/* And the same name where it reappears as the result's heading. It is the same
   words twice on one page; two faces for it was the whole thing she noticed. */
.calc-result h2 {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: .01em;
}

/* The page title as well — her call, 2026-08-05. The calculator is her private
   workshop tool, not a shop page: it should read like the PDF it produces, not
   like the storefront. Fraunces stays everywhere else on the site. */
.calc-head h1 {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: -.01em;
}

/* The remembered recipe now names itself and dates itself. */
.calc-kept-what { font-family: var(--mono); font-size: .8rem; color: var(--ink); font-weight: 500; }
.calc-kept-note { font-size: .75rem; }
.calc-kept { align-items: baseline; }

/* The oil list as tappable rows — 2026-08-05. iOS renders a sized <select> as
   a one-line wheel, so on her phone the twelve-row list she was meant to
   scroll did not exist. Buttons behave the same everywhere, and a tap is one
   gesture instead of two. Kept a finger-sized target: she uses this standing
   at a bench. */
/* The <select> stays visible until the script has actually built the list that
   replaces it, and hides itself from JavaScript. A stylesheet cannot know
   whether the script arrived. */
.calc-hidden { display: none !important; }
.calc-oillist {
  list-style: none; margin: .5rem 0 0; padding: 0;
  max-height: 20rem; overflow-y: auto;
  border: 1px solid var(--rule); background: var(--card);
  -webkit-overflow-scrolling: touch;
}
.calc-oillist li { margin: 0; }
.calc-oilpick {
  display: block; width: 100%; text-align: left;
  font-family: var(--mono); font-size: .85rem;
  padding: .6rem .7rem; border: 0; border-bottom: 1px solid var(--hair);
  background: transparent; color: var(--ink); cursor: pointer;
}
.calc-oilpick:hover { background: var(--accent-wash); }
.calc-oilpick.is-on { background: var(--ink); color: var(--paper); }
@media (max-width: 62rem) {
  .calc-oillist { max-height: 14rem; }
  .calc-oilpick { padding: .75rem .8rem; font-size: .9rem; }
}

/* The line that stands in for the profile table before there is anything to
   put in it. */
.calc-hint {
  font-size: .82rem; color: var(--muted); line-height: 1.5;
  padding: .9rem 1rem; margin: 1.25rem 0 0;
  background: var(--card); border-left: 3px solid var(--hair);
}

/* Column widths in the entry table — 2026-08-05. The per-cent column had room
   for a number that never exceeds 100, while the grams column was so narrow
   that "1.000,0 g" broke and left the "g" on its own line. Her reasoning is
   the right one: the percentage is bounded, the weight grows with the batch,
   so the space belongs to the weight. */
#calc-rows th:first-child,
#calc-rows td:first-child  { width: 46%; }
#calc-rows th:nth-child(2),
#calc-rows td:nth-child(2) { width: 18%; }
#calc-rows th:nth-child(3),
#calc-rows td:nth-child(3) { width: 36%; white-space: nowrap; }

/* Same reasoning on the result table below. */
.calc-oilrows td:nth-child(3),
.calc-oilrows th:nth-child(3) { white-space: nowrap; }
.calc-total td { white-space: nowrap; }

/* --- The recipe book ------------------------------------------------------
   Built 2026-08-05. It borrows the calculator's furniture on purpose: it is
   the same tool seen from the other end, and a second visual language for it
   would be a second thing to learn. */
.calc-bars { display: block; margin: 1rem 0 0; max-width: 22rem; }
.calc-bars input { max-width: 7rem; }
.calc-mine { border-left-color: var(--accent-ink); }
.calc-mine a { font-family: var(--mono); font-size: .8rem; letter-spacing: .06em; }
.rb-list input { max-width: 6rem; text-align: right; }
.rb-actions { white-space: nowrap; text-align: right; }
.rb-actions .btn { padding: .4rem .8rem; font-size: .65rem; }
.rb-hide {
  background: none; border: 0; color: var(--muted); cursor: pointer;
  font-family: var(--mono); font-size: .65rem; letter-spacing: .08em;
  text-transform: uppercase; padding: .4rem .5rem;
}
.rb-hide:hover { color: #b03030; }
.rb-numbers { margin-top: 1.25rem; }
.rb-archived { margin-top: 3rem; opacity: .8; }
.rb-archived h3 {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.rb-edit input { max-width: 5.5rem; text-align: right; }
.rb-edit { white-space: nowrap; }
.rb-edit .calc-inci { margin-left: .25rem; }

/* The three number columns in the recipe list are centred, header and field
   together — 2026-08-05. Right-aligning the heading over a boxed input that
   is narrower than its column left them visibly out of line. */
.calc-table th.mid, .calc-table td.mid { text-align: center; }
.rb-edit input, .rb-list td.mid input {
  text-align: center; margin-inline: auto; display: inline-block;
}
.rb-edit .calc-inci { display: inline; }

/* Column widths for the recipe list — 2026-08-05. Without them the columns
   shared the leftover space unevenly, so a right-aligned heading sat well to
   the right of the narrow box beneath it, and the "g" after the batch field
   was pushed onto its own line. Fixed widths, centred heading and field, and
   the unit kept on the same line as the number it belongs to. */
.rb-list th:nth-child(1), .rb-list td:nth-child(1) { width: 34%; text-align: left; }
.rb-list th:nth-child(2), .rb-list td:nth-child(2) { width: 17%; }
.rb-list th:nth-child(3), .rb-list td:nth-child(3) { width: 15%; }
.rb-list th:nth-child(4), .rb-list td:nth-child(4) { width: 15%; }
.rb-list th:nth-child(5), .rb-list td:nth-child(5) { width: 19%; }

.rb-list td.mid, .rb-list th.mid { text-align: center; vertical-align: middle; }
.rb-list td.mid input { width: 4.5rem; max-width: none; text-align: center; }
.rb-edit { white-space: nowrap; }
.rb-edit input { width: 4.5rem !important; }
.rb-edit .calc-inci { display: inline; margin-left: .3rem; font-style: normal; }

@media (max-width: 52rem) {
  .rb-list th, .rb-list td { width: auto !important; }
  /* Excluded from the card layout further down: there each box fills its own
     column, and forcing 3.5rem inside a 7rem column left dead space that
     pushed the "g" away from its number. Kept for anything that is still a
     real table at this width. */
  .rb-list td.mid:not([data-label]) input,
  .rb-edit:not([data-label]) input { width: 3.5rem !important; }
}

/* Days curing. Six weeks is her cure, so past 42 days the count turns green
   and gets a tick — the answer to "is that batch ready?" without arithmetic. */
.rb-age { font-family: var(--mono); }
.rb-age.is-cured { color: #2f7d4f; font-weight: 500; }

/* A refusal, repeated beside the buttons that caused it. On a phone the top of
   the page is four screens away, and a message she cannot see is a message
   that did not happen. */
.notice-here { border-left: 3px solid #9d2f2f; padding: .6rem .8rem; margin: .8rem 0; }

/* The compact picker: choose a bar, set how many, add, repeat. Sized for a
   thumb — the − and + are the buttons that get pressed most at a stall, so
   they are large enough to hit without looking. */
.till-picker { margin-bottom: 1rem; }
.till-pick-row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: stretch; }
.till-pick-select {
  flex: 1 1 100%; font-size: 1rem; padding: .65rem .5rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
}
.till-stepper { display: flex; flex: 0 0 auto; }
.till-step {
  width: 3rem; font-size: 1.4rem; line-height: 1;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); cursor: pointer;
}
.till-step:active { background: var(--rule); }
.till-count {
  width: 3.5rem; text-align: center; font-family: var(--mono); font-size: 1.15rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-left: 0; border-right: 0; border-radius: 0;
}
.till-add { flex: 1 1 6rem; }

.till-chosen { list-style: none; margin: .9rem 0 0; padding: 0; }
.till-chosen-row { display: flex; align-items: center; gap: .6rem;
  padding: .5rem 0; border-bottom: 1px solid var(--hair); }
.till-chosen-name { flex: 1 1 auto; }
.till-chosen-money { font-family: var(--mono); }
.till-chosen-empty { padding: .6rem 0; color: var(--muted); font-size: .9rem; }
.till-drop {
  flex: 0 0 auto; width: 2rem; height: 2rem; line-height: 1;
  font-size: 1.1rem; color: var(--muted);
  background: none; border: 1px solid var(--rule); cursor: pointer;
}
.till-drop:hover, .till-drop:focus { color: var(--ink); }
.till-chosen-total { margin: .7rem 0 0; font-weight: 600; }

/* The way out of a finished sale. Alone on the screen on purpose: nothing
   below it to suggest the sale is still in progress. */
.till-again { margin: 1.4rem 0 0; }

/* The recipe list on a phone.
   Five columns with fixed widths is a good table on a laptop and a wreck on a
   phone: Diana's screenshot on 2026-08-07 showed "JABON PARA BEBE O PIEL
   SENSIBLE" broken across five lines in a column 34% wide. Below 640px the
   table stops being a table — each recipe becomes a small card, and every
   field carries the label the hidden header used to give it. */
@media (max-width: 640px) {
  .rb-list, .rb-list thead, .rb-list tbody,
  .rb-list tr, .rb-list th, .rb-list td { display: block; width: auto; }

  .rb-list thead { position: absolute; left: -9999px; }

  .rb-list tr {
    border: 1px solid var(--rule);
    padding: .85rem 1rem;
    margin-bottom: .8rem;
    background: var(--card);
  }
  .rb-list td { border: 0; padding: .3rem 0; }

  /* The name gets the whole width, which is all it ever needed. */
  .rb-list td:nth-child(1) {
    width: auto; padding-bottom: .6rem; margin-bottom: .4rem;
    border-bottom: 1px solid var(--hair);
  }
  .rb-list td:nth-child(1) strong { font-size: 1.05rem; }

  /* Label, box, unit — three fixed columns so every box on the card lines up
     with the one above it. A flex row did not: only the batch size has a "g"
     after it, so its input sat further left than the other two and the whole
     card looked crooked. And 5.5rem clipped "1200" on the last digit, which
     Diana spotted straight away. */
  .rb-list td[data-label] {
    display: grid;
    grid-template-columns: 1fr 7rem;
    align-items: center;
    /* No gap between the box and its unit — the "g" belongs to the number, not
       to the edge of the card. The breathing room goes on the label instead. */
    column-gap: 0;
    text-align: left;
  }
  /* Not upper-cased, on purpose. The unit is part of the label now, and
     text-transform would turn "· g" into "· G" — Diana asked for the grams in
     lower case, and a capital G is a different unit to anybody who reads
     labels for a living. */
  .rb-list td[data-label]::before {
    content: attr(data-label);
    font-size: .78rem; letter-spacing: .02em;
    color: var(--muted); line-height: 1.3;
    padding-right: .7rem;
  }
  /* All three boxes identical. Earlier rules in this file cap .rb-list inputs
     at 6rem and force .rb-edit ones to a fixed width, which made the three
     rows of one card disagree with each other by a few pixels. */
  .rb-list td[data-label] input {
    width: 100% !important; max-width: none !important; box-sizing: border-box;
    text-align: center; padding-left: .3rem; padding-right: .3rem;
  }
  /* Hidden here: it is already in the label, and printing it twice looked
     like a stray letter beside the box. */
  .rb-list td[data-label] .calc-inci { display: none; }

  .rb-actions { text-align: left; padding-top: .7rem; white-space: normal; }
  .rb-actions .btn { display: inline-block; margin-right: .5rem; }
}

/* The password on her private pages. Narrow and quiet on purpose: there is one
   thing to do on this screen and nothing to explore. */
.gate { max-width: 26rem; padding-block: clamp(2.5rem, 8vw, 5rem) 4rem; }
.gate h1 { font-family: var(--serif); font-weight: 600; letter-spacing: -.01em;
  margin: .3rem 0 1.2rem; }
.gate h2 { font-size: 1rem; margin: 0 0 .5rem; }
.gate-why, .gate-note { font-size: .875rem; color: var(--muted); line-height: 1.6; }
.gate-note { margin-top: 1rem; }
.gate-form { margin: 1.2rem 0 0; }
.gate-field { display: block; margin-bottom: .9rem; }
.gate-field span { display: block; font-size: .8rem; color: var(--muted); margin-bottom: .3rem; }
.gate-field input {
  width: 100%; box-sizing: border-box; font-size: 1.05rem; padding: .7rem .8rem;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--rule); border-radius: 0;
}
.gate-field input:focus { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
.gate-form .cta { width: 100%; }
.gate-error { border-left: 3px solid #9d2f2f; padding: .6rem .8rem; }
