/* ============================================================
   Stonaria design system — v1 "stone & ink"
   ------------------------------------------------------------
   One stylesheet for both faces of the app. The storefront is
   quiet, generous and typographic; the Studio reuses the same
   tokens at higher density.

   Rules of the road (borrowed from Workrize, which learned them
   the hard way):
     * Colours ONLY from the variables below. No hex in a rule.
     * When deleting a rule, delete EVERY line of it. A stray
       closing brace makes CSS error recovery swallow the NEXT
       rule whole, and the loss is silent.
     * Anything that grows a popover inside main > * needs its
       own position/z-index — see the stacking note near .pop.
   ============================================================ */

/* ============================================================
   THE TYPEFACES, SELF-HOSTED — 6 August 2026
   ------------------------------------------------------------
   Both faces used to load from fonts.googleapis.com, which cost
   two things on every single page view: a render-blocking round
   trip to a third party before any text could be drawn, and the
   visitor's IP address handed to Google, which European
   regulators have treated as a data-protection matter. The audit
   listed it as P5 on 1 August and it stayed open because the
   files have to be fetched by a human.

   `font-display:swap` is deliberate. The alternative is that
   text stays INVISIBLE while a font loads — on a slow connection
   that is a blank page holding a paying visitor, which is a
   worse failure than a moment of the fallback face.

   SUBSET: `latin` only, and that is MEASURED rather than assumed.
   The whole catalogue — every name, write-up, origin and country
   — contains exactly three non-ASCII characters: u-diaeresis
   (U+00FC), dotless i (U+0131, in "Elazıg") and the em dash
   (U+2014). All three sit inside the latin subset; Google places
   the dotless i there, not in latin-ext.

   The latin-ext files were downloaded and then NOT used, because
   they cost about 140 KB per visitor to cover characters nothing
   here contains. An earlier note in this very comment claimed
   Elazıg needed them, which was wrong and was corrected by
   counting the characters rather than reasoning about them —
   the same lesson as the hero scrim, where the mean looked like
   the right metric until somebody measured the real library.

   If a stone with a Polish, Czech or Hungarian name is ever
   added, this becomes untrue. Count again before adding files:
   the check is a few lines over Stone.name.
   ============================================================ */
@font-face{
  font-family:'Cormorant Garamond';font-style:normal;font-weight:400;
  font-display:swap;
  src:url("fonts/cormorant-garamond-v21-latin-regular.87d22046a89f.woff2") format('woff2')}
@font-face{
  font-family:'Cormorant Garamond';font-style:italic;font-weight:400;
  font-display:swap;
  src:url("fonts/cormorant-garamond-v21-latin-italic.aa6bdaf51a40.woff2") format('woff2')}
@font-face{
  font-family:'Cormorant Garamond';font-style:normal;font-weight:500;
  font-display:swap;
  src:url("fonts/cormorant-garamond-v21-latin-500.efeea72c7a42.woff2") format('woff2')}
@font-face{
  font-family:'Cormorant Garamond';font-style:normal;font-weight:600;
  font-display:swap;
  src:url("fonts/cormorant-garamond-v21-latin-600.bf1e09fb81d7.woff2") format('woff2')}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/inter-v20-latin-regular.da03732a27e1.woff2") format('woff2')}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;
  src:url("fonts/inter-v20-latin-500.51205681a1ba.woff2") format('woff2')}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/inter-v20-latin-600.2ede57db1a3a.woff2") format('woff2')}

:root{
  /* Surfaces — warm limestone, not grey */
  --bg:#F7F5F1;
  --surface:#FFFFFF;
  --alt:#EFEBE4;
  --sand:#E4DED3;

  /* Ink */
  --ink:#16150F;
  --ink-2:#3A382F;
  --muted:#78746A;
  --inverse:#FAF8F4;

  /* Lines */
  --line:#E2DCD1;
  --line-soft:#EDE8DF;
  --line-strong:#CFC7B8;

  /* Accent — antique bronze. Deliberately restrained: the accent
     appears on state and action, never as decoration. */
  --accent:#8C6A3F;

  /* Gold, and the light that goes with it, for ANYTHING sitting on a dark
     ground: the footer, the curtain's hairline, the stage overlays.
     These deliberately do NOT flip with the theme. --accent and --inverse
     both invert under .theme-noir, which is correct for a page whose
     surface inverts — and wrong for the footer, which is ink in BOTH
     moods. That mismatch is what made the footer wordmark render
     black-on-black on every noir page. */
  --gold:#B9A47E;
  --on-dark:#FAF8F4;
  /* The wash behind anything that takes the whole screen — today the mobile
     drawer. Like --gold and --on-dark it does NOT flip: a scrim is darkness,
     and darkness does not become light because the page underneath is ivory. */
  --scrim:rgba(13,11,9,.58);
  --accent-deep:#6B4F2C;
  --accent-soft:rgba(140,106,63,.10);
  --accent-text:#6B4F2C;

  /* States */
  --ok-bg:#E7EFE4; --ok-ink:#3D6330;
  --warn-bg:#F8EFDC; --warn-ink:#8A6416;
  --err-bg:#F6E7E3; --err-ink:#9C3D2A;
  --info-bg:#E7EBEF; --info-ink:#3B5060;

  /* Studio chrome */
  --side-bg:#16150F;
  --side-ink:#B3AEA2;
  --side-line:rgba(250,248,244,.09);
  --topbar-bg:rgba(247,245,241,.86);

  /* Form */
  --radius:2px;            /* near-square: luxury reads as restraint */
  --radius-lg:4px;
  --ring:0 0 0 3px rgba(140,106,63,.22);
  --sh-1:0 1px 2px rgba(22,21,15,.04), 0 8px 28px rgba(22,21,15,.05);
  --sh-2:0 2px 8px rgba(22,21,15,.07), 0 28px 56px rgba(22,21,15,.10);

  --sidebar-w:236px;
  --shell:1320px;          /* storefront prose + grid width */
  --shell-wide:1800px;     /* Studio data tables need the screen */

  --font-d:'Cormorant Garamond','Didot','Times New Roman',serif;
  --font-b:'Inter','Helvetica Neue',system-ui,-apple-system,sans-serif;
  --ease:cubic-bezier(.22,.68,.28,1);
}

*{box-sizing:border-box}
html,body{margin:0}
body{
  font-family:var(--font-b);
  font-size:15px;line-height:1.6;
  color:var(--ink);background:var(--bg);
  -webkit-font-smoothing:antialiased;
}
::selection{background:var(--accent-soft);color:var(--ink)}

/* ---------- Type ---------- */
h1,h2,h3,h4,.display,.brand{
  font-family:var(--font-d);font-weight:400;
  letter-spacing:.004em;line-height:1.12;margin:0 0 .5em;
}
h1{font-size:44px}
h2{font-size:30px}
h3{font-size:22px}
.display{font-size:clamp(38px,6vw,74px);line-height:1.04}
.eyebrow{
  font-family:var(--font-b);font-size:11px;font-weight:500;
  letter-spacing:.19em;text-transform:uppercase;color:var(--muted);
}
.lede{font-size:18px;line-height:1.65;color:var(--ink-2);max-width:62ch}
.muted{color:var(--muted)}
.small{font-size:13px}
.price{font-variant-numeric:tabular-nums}
a{color:var(--accent-text);text-decoration:none;transition:color .18s var(--ease)}
a:hover{color:var(--accent-deep);text-decoration:underline;text-underline-offset:3px}

/* ---------- Layout ---------- */
.shell{max-width:var(--shell);margin:0 auto;padding:0 28px}
.shell-wide{max-width:var(--shell-wide);margin:0 auto;padding:0 28px}
.doc{max-width:74ch}          /* prose cap — never let copy run full width */
.stack>*+*{margin-top:18px}
.row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.row.between{justify-content:space-between}
.spacer{flex:1}
section.band{padding:76px 0;border-top:1px solid var(--line-soft)}
section.band.tight{padding:44px 0}

/* ---------- Storefront header ---------- */
.site-head{
  position:sticky;top:0;z-index:30;
  background:var(--topbar-bg);
  backdrop-filter:saturate(150%) blur(12px);
  -webkit-backdrop-filter:saturate(150%) blur(12px);
  border-bottom:1px solid var(--line);
}
.site-head .inner{
  display:flex;align-items:center;gap:26px;
  max-width:var(--shell);margin:0 auto;padding:16px 28px;
}
.site-head .brand{
  font-size:25px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink);margin:0;
}
.site-head .brand:hover{text-decoration:none;color:var(--accent-deep)}
/* `.nav-wrap` holds both navs and the spacer between them. On desktop it is
 * transparent to layout — it simply takes the row the two navs used to share,
 * so the header is unchanged. Below 820px it becomes the mobile panel. */
.nav-wrap{display:flex;flex:1;align-items:center;gap:26px}
.nav{display:flex;gap:22px;align-items:center}
.nav a{
  font-size:12px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-2);
  /* Nav items are flex children. Without this they SHRINK rather than
   * overflow, and a two-word link breaks across two lines inside its own
   * box — which is what the live header did on a phone. */
  white-space:nowrap;flex:0 0 auto;
}
.nav-toggle,.nav-scrim{display:none}
.nav a:hover,.nav a.active{color:var(--accent-deep);text-decoration:none}
.nav a.active{border-bottom:1px solid var(--accent)}
.basket-count{
  display:inline-block;min-width:18px;padding:0 5px;margin-left:6px;
  background:var(--ink);color:var(--inverse);
  font-size:10px;line-height:18px;text-align:center;border-radius:99px;
}

/* ---------- Buttons ---------- */
.btn{
  display:inline-block;border:1px solid var(--ink);background:var(--ink);
  color:var(--inverse);padding:11px 22px;border-radius:var(--radius);
  font-family:var(--font-b);font-size:12px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;cursor:pointer;
  transition:background .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease);
}
.btn:hover{background:var(--accent-deep);border-color:var(--accent-deep);
  color:var(--inverse);text-decoration:none}
.btn:focus-visible{outline:none;box-shadow:var(--ring)}
.btn.secondary{background:transparent;color:var(--ink);border-color:var(--line-strong)}
.btn.secondary:hover{background:var(--alt);color:var(--ink);border-color:var(--ink)}
.btn.quiet{background:transparent;border-color:transparent;color:var(--accent-text);
  padding-left:0;padding-right:0}
.btn.quiet:hover{background:transparent;text-decoration:underline}
.btn.danger{background:var(--err-ink);border-color:var(--err-ink)}
.btn.small{padding:7px 14px;font-size:11px}
.btn.block{display:block;width:100%;text-align:center}
.btn[disabled],.btn.is-disabled{opacity:.45;pointer-events:none}

/* ---------- Cards & panels ---------- */
.card{
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:22px;box-shadow:var(--sh-1);
}
.card h2,.card h3{margin-top:0}
.card-head{display:flex;align-items:baseline;gap:12px;
  justify-content:space-between;margin-bottom:14px}
.panel{background:var(--alt);border-radius:var(--radius-lg);padding:22px}

/* ---------- Panels that must line up across a row ----------
 * Three cards side by side looked wrong because their HEADINGS wrapped to a
 * different number of lines (three, two, two) — so every paragraph began at
 * a different height. The cards were the same height; the rows inside them
 * were not.
 *
 * SUBGRID ties the inner rows to the outer grid, so heading and body align
 * across all three columns at every width, with no fixed heights to go stale
 * the next time the copy changes. `min-height` on the heading would have
 * worked today and broken at the next breakpoint.
 *
 * Anything that is not a two-part card is left alone, hence the opt-in
 * `.grid.aligned` rather than styling every .panel everywhere. */
.grid.aligned{grid-template-rows:auto auto}
.grid.aligned .panel{
  display:grid;grid-row:span 2;grid-template-rows:subgrid;gap:10px;
  align-content:start;
}
.grid.aligned .panel h3{
  font-size:19px;line-height:1.32;margin:0;align-self:start;
}
.grid.aligned .panel p{margin:0}
/* No subgrid (older Safari): fall back to equal heights and a heading that
 * simply starts at the top. Slightly uneven, never broken. */
@supports not (grid-template-rows: subgrid){
  .grid.aligned .panel{display:block}
  .grid.aligned .panel h3{margin:0 0 10px}
}
.empty{
  padding:44px 22px;text-align:center;color:var(--muted);
  border:1px dashed var(--line-strong);border-radius:var(--radius-lg);
}

/* ---------- Product grid ---------- */
.grid{display:grid;gap:30px}
.grid.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
.piece{display:block;color:inherit}
.piece:hover{text-decoration:none}
.piece .frame{
  position:relative;aspect-ratio:4/5;overflow:hidden;
  background:var(--alt);border-radius:var(--radius);
}
/* <picture> is a wrapper for format negotiation and nothing else. Taking it
 * out of the box tree means every existing rule that targets the <img> keeps
 * working, including the absolutely-positioned shots in the Light Room and
 * on the stage. Without this, adding <picture> is a layout change. */
picture{display:contents}
.piece .frame img{width:100%;height:100%;object-fit:cover;
  transition:transform .7s var(--ease)}
.piece:hover .frame img{transform:scale(1.035)}
.piece .frame .placeholder{
  display:flex;align-items:center;justify-content:center;height:100%;
  color:var(--muted);font-family:var(--font-d);font-size:30px;
}
.piece .name{font-family:var(--font-d);font-size:19px;margin:14px 0 2px}
.piece .meta{font-size:13px;color:var(--muted)}
.piece .price{margin-top:6px;font-size:14px}

/* Product page */
.pdp{display:grid;grid-template-columns:1.25fr 1fr;gap:56px;align-items:start}
.pdp .gallery{display:grid;gap:12px}
.pdp .gallery img{width:100%;border-radius:var(--radius);background:var(--alt)}
.pdp .buy{position:sticky;top:110px}
.spec{border-top:1px solid var(--line);padding:12px 0;display:flex;gap:16px}
.spec dt{flex:0 0 130px;color:var(--muted);font-size:13px;margin:0}
.spec dd{margin:0;font-size:14px}

/* ---------- Badges & chips ---------- */
.badge{
  display:inline-block;padding:3px 9px;border-radius:99px;
  font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  background:var(--alt);color:var(--ink-2);
}
.badge.ok{background:var(--ok-bg);color:var(--ok-ink)}
.badge.warn{background:var(--warn-bg);color:var(--warn-ink)}
.badge.err{background:var(--err-bg);color:var(--err-ink)}
.badge.info{background:var(--info-bg);color:var(--info-ink)}
.badge.accent{background:var(--accent-soft);color:var(--accent-text)}
.chip{
  display:inline-flex;align-items:center;gap:7px;
  padding:6px 12px;border:1px solid var(--line);border-radius:99px;
  background:var(--surface);font-size:12px;color:var(--ink-2);
}
.chip.is-on{border-color:var(--ink);background:var(--ink);color:var(--inverse)}
/* Chips used as LINKS — the enquiry page's "what would help" row. They must
 * not wear the underline every other anchor gets. */
a.chip{text-decoration:none;transition:border-color .2s var(--ease)}
a.chip:hover{text-decoration:none;border-color:var(--line-strong)}
a.chip.is-on:hover{border-color:var(--ink)}

/* Hover tip. Native title= is slow and cannot put one item per line. */
.has-tip{position:relative}
.has-tip .tip{
  position:absolute;left:50%;top:calc(100% + 8px);transform:translateX(-50%);
  min-width:150px;max-width:280px;padding:9px 12px;
  background:var(--ink);color:var(--inverse);
  font-size:12px;line-height:1.5;border-radius:var(--radius);
  box-shadow:var(--sh-2);opacity:0;visibility:hidden;
  transition:opacity .16s var(--ease);z-index:50;white-space:pre-line;
}
.has-tip:hover .tip,.has-tip:focus-within .tip{opacity:1;visibility:visible}

/* ---------- Forms ---------- */
label{display:block;font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);margin-bottom:6px}
input:not([type=checkbox]):not([type=radio]),select,textarea{
  width:100%;max-width:520px;padding:11px 13px;
  border:1px solid var(--line-strong);border-radius:var(--radius);
  background:var(--surface);color:var(--ink);
  font-family:var(--font-b);font-size:15px;
}
input:focus,select:focus,textarea:focus{
  outline:none;border-color:var(--accent);box-shadow:var(--ring)}
textarea{min-height:110px;resize:vertical}
.field+.field{margin-top:16px}
.field .hint{font-size:12px;color:var(--muted);margin-top:5px;text-transform:none;
  letter-spacing:0}
.field .err{font-size:12px;color:var(--err-ink);margin-top:5px}
/* The enquiry form's honeypot. Moved off-screen rather than hidden with
 * `display:none` or `visibility:hidden`: a form-filling bot skips both of
 * those, and this field only works if it looks ordinary to a parser and is
 * invisible to a person. Do not "tidy" this into .sr-only — that class is
 * for things a screen reader SHOULD read, and this is the opposite. */
.trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
fieldset{border:0;padding:0;margin:0}

/* ---------- Messages ---------- */
.messages{margin:20px 0;display:grid;gap:10px}
.msg{padding:12px 16px;border-radius:var(--radius);font-size:14px;
  border:1px solid transparent}
.msg.success{background:var(--ok-bg);color:var(--ok-ink)}
.msg.error{background:var(--err-bg);color:var(--err-ink)}
.msg.warning{background:var(--warn-bg);color:var(--warn-ink)}
.msg.info,.msg.debug{background:var(--info-bg);color:var(--info-ink)}

/* ---------- Tables (Studio) ---------- */
.table-wrap{overflow-x:auto;border:1px solid var(--line);
  border-radius:var(--radius-lg);background:var(--surface)}
table.data{width:100%;border-collapse:collapse;font-size:14px}
table.data th{
  text-align:left;padding:11px 14px;background:var(--alt);
  font-size:11px;font-weight:600;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-2);white-space:nowrap;border-bottom:1px solid var(--line);
}
table.data td{padding:11px 14px;border-bottom:1px solid var(--line-soft);
  vertical-align:middle}
table.data tr:last-child td{border-bottom:0}
table.data tbody tr:hover{background:var(--alt)}
table.data td.num{text-align:right;font-variant-numeric:tabular-nums}

/* ---------- Filtering and bulk selection (Studio) ----------
   Added 11 August 2026. Both are built by studio/static/studio/studio.js
   and both degrade to nothing: no script, no filter box, and the bulk bar
   simply stays visible with its buttons enabled.

   `tr[hidden]` needs saying OUT LOUD. The `hidden` attribute is honoured by
   the browser default sheet as `display:none`, but any rule that sets a
   display value on a row — and a table full of them is exactly where such
   rules get written — beats it, because both are the same specificity and
   ours comes later. The filter then hides nothing at all while the count
   insists it has. */
table.data tbody tr[hidden]{display:none}

.filter{display:flex;align-items:center;gap:10px;margin:0 0 12px}
.filter-input{
  /* 16px is not a style choice. Below it, iOS Safari zooms the viewport on
     focus and does not zoom back out — the fault that made /enquire/
     unusable on a phone. It applies to every text field on this site,
     Studio included. */
  font-family:var(--font-b);font-size:16px;
  padding:9px 13px;min-width:min(320px,100%);
  border:1px solid var(--line);border-radius:999px;
  background:var(--surface);color:var(--ink);
  transition:border-color .18s var(--ease), box-shadow .18s var(--ease)}
.filter-input:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring)}
.filter-input::placeholder{color:var(--ink-3,var(--ink-2))}
.filter-count{font-size:12px;color:var(--ink-2);
  letter-spacing:.06em;font-variant-numeric:tabular-nums}
.filter-empty td{color:var(--ink-2);font-style:italic;text-align:center;
  padding:22px 14px}

/* The bar that carries the bulk buttons. It is ALWAYS in the flow, never a
   floating overlay that appears on first tick: an element that arrives
   under the cursor is an element that gets clicked by accident, and these
   buttons publish things to a public website. */
.bulk-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:10px 0 14px}
.bulk-count{font-size:13px;color:var(--ink-2);
  font-variant-numeric:tabular-nums;min-width:96px}
.bulk-bar.is-live .bulk-count{color:var(--ink);font-weight:500}
.bulk-bar button[disabled]{opacity:.45;cursor:not-allowed}
table.data td.pick, table.data th.pick{width:34px;padding-right:0}
table.data input[type=checkbox]{width:16px;height:16px;accent-color:var(--accent);
  cursor:pointer}

.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:16px}
.stat{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:18px}
.stat .n{font-family:var(--font-d);font-size:34px;line-height:1}
.stat .k{font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted);margin-top:6px}

/* Timeline */
.timeline{list-style:none;margin:0;padding:0;border-left:1px solid var(--line)}
.timeline li{position:relative;padding:0 0 18px 22px}
.timeline li::before{
  content:"";position:absolute;left:-4px;top:6px;width:7px;height:7px;
  border-radius:99px;background:var(--accent);
}
.timeline .when{font-size:12px;color:var(--muted)}

/* ---------- Studio shell ---------- */
.app{display:flex;min-height:100vh}
.sidebar{
  width:var(--sidebar-w);flex-shrink:0;background:var(--side-bg);
  padding:22px 12px;display:flex;flex-direction:column;gap:2px;
  position:sticky;top:0;height:100vh;overflow-y:auto;
}
.sidebar .brand{
  display:block;padding:2px 10px 20px;color:var(--inverse);
  font-size:20px;letter-spacing:.17em;text-transform:uppercase;
}
.sidebar .brand:hover{color:var(--accent);text-decoration:none}
.side-label{
  font-size:10px;font-weight:600;letter-spacing:.17em;text-transform:uppercase;
  color:#6E6A5E;padding:16px 10px 5px;
}
.side-link{
  display:block;padding:8px 12px;border-radius:var(--radius);
  font-size:14px;color:var(--side-ink);position:relative;
  transition:color .2s var(--ease),background .2s var(--ease);
}
.side-link:hover{color:var(--inverse);background:rgba(250,248,244,.06);
  text-decoration:none}
.side-link.active{color:var(--inverse);background:rgba(140,106,63,.22)}
.side-link.active::before{
  content:"";position:absolute;left:-1px;top:24%;bottom:24%;width:2px;
  background:var(--accent);
}
.side-foot{margin-top:auto;padding:14px 10px 4px;
  border-top:1px solid var(--side-line);font-size:13px}
.side-foot .who{display:block;color:#8C887C;font-size:12px;
  word-break:break-all;margin-bottom:10px}
.maincol{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{
  position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:12px;
  padding:12px 26px;background:var(--topbar-bg);
  backdrop-filter:saturate(150%) blur(12px);
  -webkit-backdrop-filter:saturate(150%) blur(12px);
  border-bottom:1px solid var(--line);
}
main.content{flex:1;padding:26px;max-width:var(--shell-wide);width:100%}
main.content>*+*{margin-top:22px}
.page-head{display:flex;align-items:flex-end;gap:16px;
  justify-content:space-between;flex-wrap:wrap}
.page-head h1{margin:0;font-size:32px}

/* STACKING NOTE: main.content children are their own stacking contexts once
   any transform/animation is applied to them. A popover inside one is then
   trapped under later siblings whatever its z-index. Any element that grows
   a popover directly inside main.content must set position:relative and a
   z-index below the topbar (20) — Workrize lost an afternoon to this. */
.pop{position:relative;z-index:15}
.pop-menu{
  position:absolute;top:calc(100% + 6px);left:0;min-width:220px;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius-lg);box-shadow:var(--sh-2);
  padding:8px;z-index:40;
}

/* ---------- Storefront footer ----------
 * Centred and given room. The previous version was three columns of headed
 * links — filing, not design, and it read as a shop. Nothing here is grouped
 * under a heading; there are few enough links that grouping them was only
 * ever adding furniture. */
.site-foot{
  margin-top:120px;padding:96px 28px 40px;
  background:var(--foot-bg,var(--ink));color:var(--side-ink);
  text-align:center;
}
/* Gold, not --inverse. The footer sits on ink in BOTH themes, but
 * --inverse flips to near-black under .theme-noir — so the wordmark was
 * black on a #0D0B09 ground on every noir page, which is most of the public
 * site. --gold does not flip, so this reads on either mood. */
.foot-mark{
  font-family:var(--font-d);font-weight:400;
  font-size:clamp(26px,4.4vw,46px);letter-spacing:.44em;
  text-transform:uppercase;color:var(--gold);
  text-indent:.44em;      /* tracking pushes the block off-centre without this */
  line-height:1.1;
}
.foot-line{
  font-family:var(--font-d);font-size:17px;font-style:italic;
  color:#9A9284;margin:18px auto 0;max-width:42ch;
}
/* Email and telephone on one line. The spacing that used to sit on
 * `.foot-mail` moves here, or it would be applied once per link and push the
 * two apart vertically the moment there were two of them. */
.foot-contact{
  display:flex;justify-content:center;align-items:baseline;
  flex-wrap:wrap;gap:0 14px;margin:30px 0 0;
}
.foot-sep{color:var(--gold);opacity:.5}
.foot-mail{
  display:inline-block;
  font-size:12px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--gold);border-bottom:1px solid transparent;padding-bottom:3px;
}
.foot-mail:hover{color:var(--on-dark);border-bottom-color:var(--gold);
  text-decoration:none}
.foot-nav{
  display:flex;justify-content:center;flex-wrap:wrap;
  gap:0 34px;margin-top:52px;
}
.foot-nav a{
  font-size:11px;letter-spacing:.19em;text-transform:uppercase;
  color:#8C887C;padding:7px 0;
}
.foot-nav a:hover{color:var(--on-dark);text-decoration:none}
.site-foot .fine{
  display:flex;justify-content:center;align-items:center;gap:26px;
  max-width:var(--shell);margin:64px auto 0;padding:22px 0 0;
  border-top:1px solid var(--side-line);
  font-size:11px;letter-spacing:.1em;color:#6E6A5E;
}
.site-foot .fine a{color:#6E6A5E}
.site-foot .fine a:hover{color:var(--side-ink);text-decoration:none}

/* ---------- Responsive ---------- */
@media (max-width:1100px){
  .grid.g4{grid-template-columns:repeat(3,minmax(0,1fr))}
  .pdp{grid-template-columns:1fr;gap:32px}
  .pdp .buy{position:static}
}
@media (max-width:820px){
  h1{font-size:34px}
  .grid.g3,.grid.g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .site-head .inner{flex-wrap:wrap;gap:14px;padding:14px 20px}
  /* WITHOUT JAVASCRIPT this is the whole mobile header: the two navs stack
   * under the wordmark and every link is reachable. It is not pretty, and it
   * is not meant to be — it is the floor under the panel below, and the
   * reason the panel is allowed to be a script at all. */
  .nav-wrap{flex:1 1 100%;flex-wrap:wrap;gap:12px}
  .nav{gap:16px;width:100%;flex-wrap:wrap}
  .sidebar{position:fixed;left:-100%;z-index:60;transition:left .25s var(--ease)}
  .sidebar.open{left:0}
  .site-foot{padding:64px 20px 32px;margin-top:80px}
  .foot-nav{gap:0 22px;margin-top:38px}
  .shell,.shell-wide{padding:0 20px}
}
@media (max-width:560px){
  .grid.g2,.grid.g3,.grid.g4{grid-template-columns:1fr}
}

/* ============================================================
   THE MOBILE MENU
   ------------------------------------------------------------
   Below 820px the navigation collapses behind a single mark and
   opens as a full-height drawer.

   THE NO-JS FLOOR. Everything here hangs off `html.js`, so a
   visitor without JavaScript never sees the toggle and never
   sees a collapsed panel — they get the stacked header in the
   block above, with every link present and clickable. This is
   the same contract as the curtain and the reveals: motion and
   chrome may be scripted, navigation may not.

   WHY `backdrop-filter` COMES OFF HERE, AND WHY IT IS NOT
   COSMETIC. `backdrop-filter` makes an element a containing
   block for `position:fixed` DESCENDANTS. With it on, the
   drawer below is positioned against the 57px header box
   instead of the viewport, and a full-height panel silently
   becomes a header-height sliver. Removing it on mobile is what
   makes the drawer possible; it also stops a phone compositing
   a blur behind a bar it is already scrolling.

   The drawer uses the --side-* tokens — the footer and Studio
   chrome palette. Those deliberately do NOT flip with the
   theme, so the drawer is ink in both moods, which is correct:
   it is chrome, not a page surface.
   ============================================================ */
@media (max-width:820px){
  .site-head{backdrop-filter:none;-webkit-backdrop-filter:none}
  .site-head .inner{position:relative}

  .nav-toggle{
    display:inline-flex;align-items:center;justify-content:center;
    width:42px;height:42px;margin:-8px -10px -8px auto;
    padding:0;border:0;background:none;color:var(--ink);
    cursor:pointer;position:relative;z-index:4;
  }
  .nav-toggle-mark{display:block;width:22px;height:12px;position:relative}
  .nav-toggle-mark span{
    position:absolute;left:0;width:100%;height:1px;
    background:currentColor;
    transition:transform .28s var(--ease),opacity .28s var(--ease);
  }
  .nav-toggle-mark span:first-child{top:0}
  .nav-toggle-mark span:last-child{bottom:0}
  /* Open: the two rules cross. Two bars, not three — a cross needs
   * exactly two, and the third is only ever there to be hidden. */
  .nav-toggle[aria-expanded="true"]{color:var(--on-dark)}
  .nav-toggle[aria-expanded="true"] .nav-toggle-mark span:first-child{
    transform:translateY(5.5px) rotate(45deg)}
  .nav-toggle[aria-expanded="true"] .nav-toggle-mark span:last-child{
    transform:translateY(-5.5px) rotate(-45deg)}

  html.js .nav-wrap{
    position:fixed;top:0;right:0;bottom:0;left:auto;
    width:min(82vw,330px);
    flex:0 0 auto;flex-direction:column;flex-wrap:nowrap;
    align-items:stretch;justify-content:flex-start;gap:0;
    padding:88px 28px 40px;
    background:var(--side-bg);
    border-left:1px solid var(--side-line);
    transform:translateX(101%);
    transition:transform .34s var(--ease),visibility .34s;
    visibility:hidden;overflow-y:auto;z-index:3;
  }
  html.js .nav-wrap[data-open]{transform:none;visibility:visible}

  html.js .nav-wrap .nav{
    flex-direction:column;align-items:stretch;gap:0;width:100%}
  html.js .nav-wrap .nav a{
    display:block;padding:15px 0;
    font-size:14px;letter-spacing:.17em;color:var(--side-ink);
  }
  html.js .nav-wrap .nav a:hover,
  html.js .nav-wrap .nav a.active{color:var(--gold);border-bottom:0}
  /* The spacer earns its keep: on desktop it pushes the account nav right,
   * in the drawer it becomes the hairline between the two groups. */
  html.js .nav-wrap .spacer{
    flex:0 0 auto;height:1px;margin:20px 0;background:var(--side-line)}

  html.js .nav-scrim{
    display:block;position:fixed;inset:0;z-index:2;
    background:var(--scrim);
    opacity:0;visibility:hidden;transition:opacity .34s var(--ease),visibility .34s;
  }
  html.js .nav-scrim[data-open]{opacity:1;visibility:visible}
}
/* Scroll lock. On <html> rather than <body> so iOS honours it. */
html[data-nav-open],html[data-nav-open] body{overflow:hidden}

/* ============================================================
   THE PHONE, BEYOND THE HEADER
   ------------------------------------------------------------
   Three faults that are invisible on a desktop and unavoidable
   on a handset. None of them is a matter of taste.
   ============================================================ */
@media (max-width:820px){
  /* 1. THE ONE THAT ACTUALLY LOSES ENQUIRIES.
   *
   * iOS Safari zooms the whole viewport when a text field smaller than
   * 16px takes focus — and it does NOT zoom back out when the field is
   * blurred. Our fields are 15px. So tapping the first box on /enquire/
   * leaves the visitor on a page wider than their screen, scrolled
   * sideways, part way through the only form on the site that generates
   * business. One pixel of type is the whole difference. */
  input:not([type=checkbox]):not([type=radio]),select,textarea{font-size:16px}

  /* 2. TOUCH TARGETS.
   *
   * A finger is not a cursor. These are all comfortably clickable with a
   * mouse and all under the ~44px a thumb needs. The type does not grow —
   * only the box around it, so nothing about the design changes. */
  .btn{padding:14px 22px}
  .light-switch button{padding:12px 16px}
  .nav-toggle{width:44px;height:44px}

  /* 3. TIPS THAT HANG OFF THE SCREEN.
   *
   * `.tip` is centred on its trigger with translateX(-50%) and is up to
   * 280px wide. On a 390px screen a trigger near either edge puts half of
   * that outside the viewport, which on a touch device also means a
   * horizontal scrollbar on a page that should not have one. */
  .has-tip .tip{max-width:min(280px,calc(100vw - 32px))}
}

/* ============================================================
   EXPERIENCE LAYER — "Noir public, Ivory inside"
   ------------------------------------------------------------
   Two moods on one design system, switched by a class on <body>:
     .theme-ivory  (default) — shopping, checkout, account, private
     .theme-noir             — home, collections, journal, editorial
   Everything below flips VARIABLES, so components restyle
   themselves. Never hardcode a colour per theme — override the
   variable inside .theme-noir instead.

   Motion contract:
   * All entrance motion lives behind `html.js` (set by
     experience.js), so a no-JS visitor sees everything instantly.
   * EVERYTHING respects prefers-reduced-motion at the bottom of
     this file. Additions must join that block.
   ============================================================ */

/* ---------- Noir palette ---------- */
.theme-noir{
  --bg:#131110;
  --surface:#1C1916;
  --alt:#211D19;
  --sand:#2A251F;
  --ink:#F2EDE3;
  --ink-2:#D9D2C4;
  --muted:#9A9284;
  --inverse:#16150F;
  --line:#2E2922;
  --line-soft:#262019;
  --line-strong:#41392E;
  --accent:#B9A47E;
  --accent-deep:#D6C29A;
  --accent-soft:rgba(185,164,126,.12);
  --accent-text:#B9A47E;
  --ok-bg:#22301D; --ok-ink:#A9C797;
  --warn-bg:#332A14; --warn-ink:#D8BC7E;
  --err-bg:#361E17; --err-ink:#E0967F;
  --info-bg:#1E262D; --info-ink:#9FB6C7;
  --topbar-bg:rgba(19,17,16,.78);
  --foot-bg:#0D0B09;
  --sh-1:0 1px 2px rgba(0,0,0,.4), 0 10px 34px rgba(0,0,0,.45);
  --sh-2:0 2px 8px rgba(0,0,0,.5), 0 30px 60px rgba(0,0,0,.55);
}
.theme-noir ::selection{background:var(--accent);color:var(--inverse)}
.theme-noir .btn:hover{background:var(--accent);border-color:var(--accent);color:var(--inverse)}
.theme-noir .piece .frame{background:var(--alt)}
.theme-noir .site-head{border-bottom-color:rgba(242,237,227,.08)}

/* ---------- Type upgrade (both themes) ---------- */
.site-head .brand{letter-spacing:.24em;text-transform:uppercase;font-size:21px}
h1{font-weight:500}
.display{font-weight:500}

/* ---------- The curtain (arrival ritual, first page of a session) ----------
 * Duration lives in one place: --curtain-ms, set by experience.js. Every
 * animation below is a fraction of it, so changing the JS constant retimes
 * the whole sequence and nothing drifts out of step.
 *
 * CONTRACT CHANGE, July 2026. This was a full-bleed photograph of a random
 * stone cycling Night -> Day -> Museum, behind a wordmark that blurred in
 * from 6px. Every part of that is gone: the image, the scrim, the radial
 * readability plate, the stone caption, and the blur. A photographic
 * slideshow on arrival reads as a template effect, and it delayed the site
 * by four seconds to say nothing the page could not say better.
 *
 * What is left is ink, the name, and a hairline. It PARTS rather than
 * sliding away — a curtain that lifts in one piece is a garage door.
 *
 * Two leaves, each a hair over half the screen. The overlap is what stops a
 * 1px seam of live page showing down the middle on fractional device pixel
 * ratios. */
.curtain{
  --curtain-ms:1800ms;
  position:fixed;inset:0;z-index:100;
  display:flex;align-items:center;justify-content:center;
  cursor:pointer;
  overflow:hidden;
}
.curtain-leaf{
  position:absolute;top:0;bottom:0;width:calc(50% + 1px);
  background:#0D0B09;
}
.curtain-leaf[data-leaf="l"]{left:0}
.curtain-leaf[data-leaf="r"]{right:0}
/* Sits above the leaves so the name is never half-covered mid-part. */
.curtain-mark{
  position:relative;z-index:1;
  display:flex;flex-direction:column;align-items:center;gap:26px;
}
.curtain .word{
  font-family:var(--font-d);font-size:clamp(30px,6vw,64px);
  letter-spacing:.30em;text-transform:uppercase;color:#F2EDE3;
  padding-left:.30em; /* balance tracking */
  opacity:0;
}
.curtain .rule{height:1px;width:0;background:#B9A47E}
/* No blur, no bounce, no overshoot. The type settles; it does not perform. */
html.js .curtain .word{
  animation:curtainWord calc(var(--curtain-ms) * .62) var(--ease) forwards;
}
html.js .curtain .rule{
  animation:curtainRule calc(var(--curtain-ms) * .5) var(--ease) calc(var(--curtain-ms) * .28) forwards;
}
@keyframes curtainWord{
  0%{opacity:0;letter-spacing:.42em}
  100%{opacity:1;letter-spacing:.30em}
}
@keyframes curtainRule{to{width:min(280px,44vw)}}
/* The part. The leaves withdraw and the name goes with them — it belongs to
 * the curtain, not to the page underneath. */
html.js .curtain.is-done .curtain-leaf[data-leaf="l"]{
  animation:curtainPartL .8s var(--ease) forwards;
}
html.js .curtain.is-done .curtain-leaf[data-leaf="r"]{
  animation:curtainPartR .8s var(--ease) forwards;
}
html.js .curtain.is-done .curtain-mark{
  animation:curtainMarkOut .34s var(--ease) forwards;
}
@keyframes curtainPartL{to{transform:translateX(-100%)}}
@keyframes curtainPartR{to{transform:translateX(100%)}}
@keyframes curtainMarkOut{to{opacity:0}}
/* No JS: the curtain must never trap the page. */
html:not(.js) .curtain{display:none}

/* ---------- Living hero ---------- */
.hero-stage{
  position:relative;overflow:hidden;
  min-height:88vh;display:flex;align-items:center;
  background:#131110;color:#F2EDE3;
}
.hero-stage .layer{position:absolute;inset:-12%;pointer-events:none}
.hero-stage .layer.l1{
  background:
    radial-gradient(1100px 700px at 78% 18%, rgba(185,164,126,.16), transparent 60%),
    radial-gradient(900px 900px at 12% 88%, rgba(120,98,66,.12), transparent 55%);
}
.hero-stage .layer.l2{
  background:
    radial-gradient(700px 500px at 30% 30%, rgba(242,237,227,.05), transparent 60%);
}
html.js .hero-stage .layer.l1{animation:heroDrift 26s ease-in-out infinite alternate}
html.js .hero-stage .layer.l2{animation:heroDrift 34s ease-in-out infinite alternate-reverse}
@keyframes heroDrift{
  from{transform:scale(1) translate(0,0)}
  to{transform:scale(1.08) translate(2.5%,-2%)}
}
.hero-stage .inner{position:relative;max-width:var(--shell);margin:0 auto;
  padding:120px 28px;width:100%}
.hero-stage .eyebrow{color:#B9A47E}
.hero-stage .display{color:#F2EDE3}
.hero-stage .lede{color:#CFC8B9}
.hl{display:block;overflow:hidden}
.hl span{display:inline-block}
html.js .hl span{transform:translateY(110%);animation:hlUp .9s var(--ease) forwards}
html.js .hl:nth-child(2) span{animation-delay:.15s}
html.js .hl:nth-child(3) span{animation-delay:.3s}
@keyframes hlUp{to{transform:translateY(0)}}

/* ---------- Scroll reveal ---------- */
html.js [data-reveal]{
  opacity:0;transform:translateY(26px);
  transition:opacity .8s var(--ease),transform .8s var(--ease);
}
html.js [data-reveal].is-in{opacity:1;transform:none}
html.js [data-reveal] .frame img,
html.js [data-reveal] .frame .placeholder{
  clip-path:inset(8% 8% 8% 8%);transition:clip-path 1s var(--ease) .1s;
}
html.js [data-reveal].is-in .frame img,
html.js [data-reveal].is-in .frame .placeholder{clip-path:inset(0 0 0 0)}
/* Stagger siblings */
html.js .grid>[data-reveal]:nth-child(2){transition-delay:.08s}
html.js .grid>[data-reveal]:nth-child(3){transition-delay:.16s}
html.js .grid>[data-reveal]:nth-child(4){transition-delay:.24s}

/* ---------- Cursor tilt & magnetic buttons ---------- */
html.js .piece .frame{transition:transform .35s var(--ease);will-change:transform}
html.js .btn{transition:background .2s var(--ease),color .2s var(--ease),
  border-color .2s var(--ease),transform .25s var(--ease)}

/* ---------- Product theatre (ivory white-cube) ---------- */
.theatre{display:grid;grid-template-columns:1.35fr 1fr;gap:64px;align-items:start}
.theatre .stage{display:grid;gap:16px}
.theatre .stage figure{margin:0;position:relative;cursor:zoom-in}
.theatre .stage img{width:100%;border-radius:var(--radius);display:block}
.theatre .stage .zoom-hint{
  position:absolute;right:14px;bottom:14px;
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  background:rgba(255,255,255,.82);color:var(--ink);
  padding:6px 10px;border-radius:2px;opacity:0;
  transition:opacity .25s var(--ease);
}
.theatre .stage figure:hover .zoom-hint{opacity:1}
.theatre .rail{position:sticky;top:110px}
.theatre .rail .price-line{
  font-family:var(--font-d);font-size:30px;margin:14px 0 4px;
}
.plate{
  border-top:1px solid var(--ink);margin-top:34px;padding-top:8px;
}
.plate .spec{border-top:0;border-bottom:1px solid var(--line-soft)}
.plate .spec dt{letter-spacing:.1em;text-transform:uppercase;font-size:11px}

/* Zoom lightbox */
.lightbox{
  position:fixed;inset:0;z-index:90;background:rgba(12,11,9,.94);
  display:flex;align-items:center;justify-content:center;cursor:zoom-out;
}
.lightbox img{
  max-width:none;width:160%;transition:transform .1s linear;
  will-change:transform;
}
.lightbox .close-hint{
  position:absolute;top:20px;right:26px;color:#CFC8B9;
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;
}

/* ---------- Page crossfade (modern browsers, pure CSS) ---------- */
@view-transition{navigation:auto}
::view-transition-old(root){animation:vtFade .28s ease both}
::view-transition-new(root){animation:vtFade .28s ease reverse both}
@keyframes vtFade{from{opacity:1}to{opacity:0}}

/* ---------- Reduced motion: everything above stands down ---------- */
@media (prefers-reduced-motion: reduce){
  html.js .curtain{display:none}
  html.js .hero-stage .layer.l1,
  html.js .hero-stage .layer.l2{animation:none}
  html.js .hl span{animation:none;transform:none}
  html.js [data-reveal]{opacity:1;transform:none;transition:none}
  html.js .lr-shot{transition:none}
  html.js [data-reveal] .frame img,
  html.js [data-reveal] .frame .placeholder{clip-path:none;transition:none}
  html.js .piece .frame{transition:none}
  /* The drawer still opens and closes — it just arrives, rather than slides. */
  html.js .nav-wrap,html.js .nav-scrim,
  .nav-toggle-mark span{transition:none}
  ::view-transition-old(root),::view-transition-new(root){animation:none}
}
@media (max-width:820px){
  .theatre{grid-template-columns:1fr;gap:32px}
  .theatre .rail{position:static}
  .hero-stage{min-height:72vh}
  /* 120px of padding is a desktop measurement. On a 390px screen it is a
   * third of the viewport spent on nothing, which is what pushed the
   * headline below the fold on a phone. */
  .hero-stage .inner{padding:64px 20px}
}

/* ============================================================
   THE LIGHTING SWITCH
   ------------------------------------------------------------
   Museum / Day / Night. Three curated shots of the same stone,
   crossfading in place while the surrounding room shifts mood.

   All three images are in the DOM and stacked; only opacity
   changes, so switching never reflows and never re-downloads.
   The room tint comes from `data-lighting` on the section, which
   is why the shift feels like light rather than a filter.
   ============================================================ */

.lightbox-stage{position:relative;border-radius:var(--radius);overflow:hidden;
  background:var(--alt);aspect-ratio:4/5}
.lightbox-stage .shot{
  position:absolute;inset:0;opacity:0;
  transition:opacity .9s var(--ease);
}
.lightbox-stage .shot img{width:100%;height:100%;object-fit:cover;display:block}
.lightbox-stage .shot.is-lit{opacity:1}
/* No JS: show the first shot and let the rest sit behind it. */
html:not(.js) .lightbox-stage .shot:first-child{opacity:1}

/* The room responds to the light. Tints only — the image is untouched. */
/* The room re-lights around the stone. It has to do that by overriding the
 * THEME VARIABLES, not by setting a literal colour on the container.
 *
 * PAID FOR, 4 August 2026. These three rules used to set only
 * `background:#F6F3EC;color:#16150F` and leave the variables alone. A stone
 * page is theme-noir, so inside a DAY room --bg was still #131110 and
 * --ink-2 still #D9D2C4 — colours chosen for a dark page, being drawn on a
 * light one. Two things broke and neither of them failed:
 *
 *   * the write-up (.lede, color:var(--ink-2)) rendered pale beige on pale
 *     cream, unreadable, on the one screen where the words are the product;
 *   * the selected lighting button took background:currentColor — dark,
 *     inherited from the room — and its label took color:var(--bg), ALSO
 *     dark because the theme variable was stale. The word "Day" disappeared
 *     into its own pill.
 *
 * The tell that this had happened once already is the patch that used to sit
 * here, `.lit-room[data-lighting="DAY"] .muted{color:#78746A}` — a single
 * descendant repaired by hand instead of the cause, which is why the next
 * descendant to go wrong went wrong silently. It is deleted: --muted is
 * correct now. If you find yourself adding a rule of that shape, this block
 * is wrong again. Fix the variables, never the descendant. */
.lit-room{transition:background .9s var(--ease),color .9s var(--ease)}
.lit-room[data-lighting="MUSEUM"]{
  --bg:#131110; --surface:#1C1916; --alt:#211D19; --sand:#2A251F;
  --ink:#F2EDE3; --ink-2:#D9D2C4; --muted:#9A9284; --inverse:#16150F;
  --line:#2E2922; --line-soft:#262019; --line-strong:#41392E;
  background:var(--bg);color:var(--ink);
}
.lit-room[data-lighting="DAY"]{
  --bg:#F6F3EC; --surface:#FFFFFF; --alt:#EFEBE4; --sand:#E4DED3;
  --ink:#16150F; --ink-2:#3A382F; --muted:#78746A; --inverse:#FAF8F4;
  --line:#E2DCD1; --line-soft:#EDE8DF; --line-strong:#CFC7B8;
  background:var(--bg);color:var(--ink);
}
.lit-room[data-lighting="NIGHT"]{
  --bg:#0E1116; --surface:#151A21; --alt:#1A2029; --sand:#222933;
  --ink:#DDE3EA; --ink-2:#C2CAD4; --muted:#8F97A3; --inverse:#0E1116;
  --line:#232A33; --line-soft:#1C222A; --line-strong:#39424E;
  background:var(--bg);color:var(--ink);
}

.light-switch{display:inline-flex;border:1px solid currentColor;border-radius:99px;
  padding:3px;gap:2px;opacity:.85}
.light-switch button{
  appearance:none;background:transparent;border:0;cursor:pointer;
  color:inherit;font-family:var(--font-b);font-size:10px;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;padding:8px 15px;
  border-radius:99px;transition:background .3s var(--ease),color .3s var(--ease);
}
.light-switch button[aria-pressed="true"]{
  background:var(--ink);
}
.light-switch button[aria-pressed="true"] span{
  color:var(--inverse);mix-blend-mode:normal;
}
.light-switch button:focus-visible{outline:none;box-shadow:var(--ring)}
.light-caption{font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  opacity:.65;margin-top:12px}

/* ---------- The stage bar (controls BELOW the image, 31 July 2026) --------
 * Replaces four symbols floating over the bottom-right corner of the stone.
 * Two problems with that: it mixed a STATE control (which light) with an
 * ACTION (full screen) in one undifferentiated row, and icon-only with a
 * hover tooltip is unlabelled on every touch device.
 *
 * Here they are separated, labelled, and they never cover the stone — which
 * matters most on the one screen where somebody is judging a material. */
.stage-bar{
  display:flex;align-items:center;justify-content:space-between;
  gap:16px;margin-top:14px;flex-wrap:wrap;
}
.seg{
  display:inline-flex;border:1px solid currentColor;border-radius:99px;
  padding:3px;gap:2px;opacity:.9;
}
.seg button{
  appearance:none;background:transparent;border:0;cursor:pointer;color:inherit;
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--font-b);font-size:10px;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;padding:9px 16px;
  border-radius:99px;transition:background .3s var(--ease),color .3s var(--ease);
}
.seg button svg{width:14px;height:14px;flex:none}
/* The selected light. NOT `background:currentColor` with a `var(--bg)`
 * label — that pairing quietly assumes the INHERITED colour and the theme
 * VARIABLES agree about which way round the page is, and inside a .lit-room
 * they do not. --ink and --inverse are a contrasting pair by definition, so
 * this cannot resolve to dark-on-dark however the room is lit. */
.seg button[aria-pressed="true"]{background:var(--ink)}
.seg button[aria-pressed="true"] span,
.seg button[aria-pressed="true"] svg{color:var(--inverse)}
.seg button:focus-visible{outline:none;box-shadow:var(--ring)}
.seg-action{
  display:inline-flex;align-items:center;gap:9px;color:inherit;
  font-size:10px;font-weight:500;letter-spacing:.18em;text-transform:uppercase;
  padding:9px 16px;border:1px solid currentColor;border-radius:99px;opacity:.75;
  transition:opacity .3s var(--ease);
}
.seg-action:hover{opacity:1;text-decoration:none}
.seg-action svg{width:14px;height:14px;flex:none}
@media (max-width:720px){
  /* Stacked, and the light control goes full width — it is the one that
   * gets used, and a thumb needs the room. */
  .stage-bar{flex-direction:column;align-items:stretch}
  .seg{justify-content:space-between}
  .seg button{flex:1;justify-content:center;padding:11px 8px;letter-spacing:.12em}
  .seg-action{justify-content:center}
}

@media (prefers-reduced-motion: reduce){
  .lightbox-stage .shot,.lit-room{transition:none}
}

/* ============================================================
   WALLPAPER MODE — the whole slab, nothing else
   ------------------------------------------------------------
   A full-bleed viewer for looking at ONE stone properly. The
   critical difference from the lightbox: `object-fit:contain`,
   not cover. Cover fills the screen but CROPS, which for a slab
   means hiding the veining at the edges — the part a buyer is
   actually judging. Contain shows the entire image, letterboxed
   against black.

   Chrome fades out after a few still seconds and returns on the
   first movement, so the stone is alone on the screen but the
   controls are never lost.
   ============================================================ */

.wallpaper{
  position:fixed;inset:0;z-index:120;
  background:#0A0908;
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;
}
.wallpaper .plate-img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:contain;            /* WHOLE image — never crop a slab */
  opacity:0;transition:opacity 1s var(--ease);
}
.wallpaper .plate-img.is-lit{opacity:1}

/* Fill variant, for someone who wants it edge-to-edge and accepts the crop. */
.wallpaper.is-filling .plate-img{object-fit:cover}

.wallpaper .chrome{
  position:absolute;inset:0;pointer-events:none;
  opacity:1;transition:opacity .6s var(--ease);
}
.wallpaper.is-idle .chrome{opacity:0}
.wallpaper .chrome > *{pointer-events:auto}

.wallpaper .wp-head{
  position:absolute;top:0;left:0;right:0;
  display:flex;align-items:flex-start;justify-content:space-between;
  gap:20px;padding:26px 30px;
  background:linear-gradient(to bottom,rgba(10,9,8,.72),transparent);
  color:#F2EDE3;
}
.wallpaper .wp-title{font-family:var(--font-d);font-size:26px;line-height:1.15}
.wallpaper .wp-sub{
  font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:#B9A47E;margin-top:6px;
}
.wallpaper .wp-foot{
  position:absolute;bottom:0;left:0;right:0;
  display:flex;align-items:center;justify-content:space-between;
  gap:20px;padding:24px 30px 30px;
  background:linear-gradient(to top,rgba(10,9,8,.78),transparent);
  color:#F2EDE3;
}
.wallpaper .wp-hint{font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:#8F897D}
.wallpaper .stage-controls{position:static;max-width:none}
.wallpaper .sym-group{border-color:rgba(247,244,238,.28)}
.wallpaper .sym{color:#F2EDE3}
.wallpaper .sym[aria-pressed="true"]{color:#0A0908;background:#E8DFCB}

.wp-close,.wp-fill{
  appearance:none;border:1px solid rgba(242,237,227,.35);
  background:rgba(10,9,8,.4);color:#F2EDE3;cursor:pointer;
  font-family:var(--font-b);font-size:10px;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;
  padding:9px 15px;border-radius:99px;
  transition:background .2s var(--ease),border-color .2s var(--ease);
}
.wp-close:hover,.wp-fill:hover{background:rgba(242,237,227,.14);
  border-color:#F2EDE3}
.wp-close:focus-visible,.wp-fill:focus-visible{outline:none;box-shadow:var(--ring)}

/* The trigger on a stone page. */
.wp-open{
  display:inline-flex;align-items:center;gap:9px;
  margin-top:16px;
}

@media (max-width:700px){
  .wallpaper .wp-title{font-size:19px}
  .wallpaper .wp-head,.wallpaper .wp-foot{padding:18px 18px 22px}
  .wallpaper .wp-foot{flex-direction:column;align-items:flex-start;gap:12px}
  .wallpaper .wp-hint{display:none}
}
@media (prefers-reduced-motion: reduce){
  .wallpaper .plate-img,.wallpaper .chrome{transition:none}
  .wallpaper.is-idle .chrome{opacity:1}   /* never hide controls */
}

/* ---------- Wallpaper filmstrip & stone navigation ---------- */
.wp-strip{
  display:flex;gap:8px;align-items:center;
  overflow-x:auto;overflow-y:hidden;
  padding:4px 2px 6px;margin:0;
  scrollbar-width:thin;scroll-behavior:smooth;
  max-width:100%;
}
.wp-strip::-webkit-scrollbar{height:5px}
.wp-strip::-webkit-scrollbar-thumb{background:rgba(242,237,227,.28);border-radius:99px}
.wp-strip::-webkit-scrollbar-track{background:transparent}

.wp-chip{
  flex:0 0 auto;position:relative;
  width:52px;height:66px;padding:0;
  border:1px solid rgba(242,237,227,.22);border-radius:2px;
  background:#1A1714;cursor:pointer;overflow:hidden;
  transition:border-color .25s var(--ease),transform .25s var(--ease),
             opacity .25s var(--ease);
  opacity:.55;
}
.wp-chip img{width:100%;height:100%;object-fit:cover;display:block}
.wp-chip:hover{opacity:1;border-color:rgba(242,237,227,.6);transform:translateY(-2px)}
.wp-chip[aria-current="true"]{
  opacity:1;border-color:#B9A47E;box-shadow:0 0 0 1px #B9A47E;
}
.wp-chip:focus-visible{outline:none;box-shadow:var(--ring)}

.wp-nav{
  position:absolute;top:50%;transform:translateY(-50%);
  width:52px;height:52px;border-radius:99px;
  border:1px solid rgba(242,237,227,.3);
  background:rgba(10,9,8,.42);color:#F2EDE3;
  cursor:pointer;font-size:19px;line-height:1;
  display:flex;align-items:center;justify-content:center;
  transition:background .2s var(--ease),border-color .2s var(--ease);
}
.wp-nav:hover{background:rgba(242,237,227,.16);border-color:#F2EDE3}
.wp-nav:focus-visible{outline:none;box-shadow:var(--ring)}
.wp-nav.prev{left:22px}
.wp-nav.next{right:22px}

.wp-count{font-size:11px;letter-spacing:.14em;color:#8F897D;white-space:nowrap}
.wallpaper .wp-title a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
.wallpaper .wp-title a:hover{border-bottom-color:#B9A47E;text-decoration:none}

.wallpaper .wp-foot{flex-direction:column;align-items:stretch;gap:14px}
.wallpaper .wp-controls{
  display:flex;align-items:center;justify-content:space-between;
  gap:20px;flex-wrap:wrap;
}

@media (max-width:700px){
  .wp-nav{width:42px;height:42px;font-size:16px}
  .wp-nav.prev{left:8px}
  .wp-nav.next{right:8px}
  .wp-chip{width:42px;height:54px}
}
@media (prefers-reduced-motion: reduce){
  .wp-chip,.wp-nav{transition:none}
  .wp-strip{scroll-behavior:auto}
}

/* ============================================================
   ON-IMAGE OVERLAY — name in the corner, controls as symbols
   ------------------------------------------------------------
   The lighting controls and the full-screen trigger used to sit
   BELOW the image on flat black, which read as browser chrome
   rather than as part of the piece. They now float over the
   stone itself: name typeset top-left, symbols bottom-right.

   Legibility over an unknown photograph is the whole problem.
   Two devices solve it without tinting the stone:
     * a scrim — a soft gradient confined to the top and bottom
       eighths, so the middle of the slab is never veiled;
     * a blurred glass pill behind the symbols, which lifts them
       off both pale marble and black granite.
   ============================================================ */

.lightbox-stage{position:relative}

.stage-veil{
  position:absolute;inset:0;pointer-events:none;border-radius:var(--radius);
  background:
    linear-gradient(to bottom, rgba(10,9,8,.55) 0%, rgba(10,9,8,0) 22%),
    linear-gradient(to top,    rgba(10,9,8,.62) 0%, rgba(10,9,8,0) 26%);
}

/* ---- The name, top-left ---- */
.stage-name{
  position:absolute;top:0;left:0;right:0;
  padding:22px 26px;pointer-events:none;
  color:#F7F4EE;
}
.stage-name .n{
  font-family:var(--font-d);font-weight:400;
  font-size:clamp(21px,2.4vw,32px);line-height:1.1;
  letter-spacing:.005em;
  text-shadow:0 1px 24px rgba(10,9,8,.55);
}
.stage-name .s{
  margin-top:7px;
  font-size:10px;font-weight:500;letter-spacing:.24em;text-transform:uppercase;
  color:#D8CBB0;
  text-shadow:0 1px 16px rgba(10,9,8,.7);
}

/* ---- The symbols, bottom-right ---- */
/* ONE pill, not two.
 *
 * The lighting symbols and the full-screen symbol used to sit in separate
 * glass pills with a gap, hard against the corner. Two competing objects in
 * one corner read as an accident, and the second clipped on narrow screens.
 * They are now a single control, divided by a hairline, inset from the edge. */
.stage-controls{
  position:absolute;bottom:22px;right:22px;
  display:inline-flex;align-items:center;gap:0;
  padding:4px;border-radius:99px;max-width:calc(100% - 44px);
  background:rgba(14,12,10,.46);
  border:1px solid rgba(247,244,238,.18);
  backdrop-filter:blur(12px) saturate(140%);
  -webkit-backdrop-filter:blur(12px) saturate(140%);
}
.sym-group{
  display:inline-flex;align-items:center;gap:2px;
  padding:0;background:transparent;border:0;
}
.sym{
  appearance:none;border:0;background:transparent;cursor:pointer;
  width:36px;height:36px;border-radius:99px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  color:#EDE7DC;opacity:.62;
  transition:opacity .25s var(--ease),background .25s var(--ease),
             color .25s var(--ease);
}
.sym svg{width:18px;height:18px;display:block}
.sym:hover{opacity:1;background:rgba(247,244,238,.12)}
.sym[aria-pressed="true"]{
  opacity:1;color:#0E0C0A;background:#E8DFCB;
}
.sym:focus-visible{outline:none;box-shadow:var(--ring)}

/* The expand control sits in its own pill, slightly apart. */
/* Divided from the lighting symbols by a hairline: same object, two jobs. */
.sym-solo{
  display:inline-flex;padding:0 0 0 5px;margin-left:5px;
  background:transparent;border:0;
  border-left:1px solid rgba(247,244,238,.20);
}

/* Screen-reader text: the symbols still announce themselves. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* Hover label, reusing the existing tip pattern but placed above. */
.sym .tip{
  position:absolute;bottom:calc(100% + 10px);left:50%;transform:translateX(-50%);
  padding:5px 9px;border-radius:2px;
  background:rgba(14,12,10,.92);color:#EDE7DC;
  font-size:9px;letter-spacing:.16em;text-transform:uppercase;
  white-space:nowrap;opacity:0;visibility:hidden;
  transition:opacity .18s var(--ease);
}
.sym{position:relative}
.sym:hover .tip,.sym:focus-visible .tip{opacity:1;visibility:visible}

@media (max-width:640px){
  .stage-name{padding:16px 18px}
  .stage-controls{bottom:14px;right:14px;max-width:calc(100% - 28px)}
  .sym{width:32px;height:32px}
  .sym svg{width:16px;height:16px}
}
@media (prefers-reduced-motion: reduce){
  .sym,.sym .tip{transition:none}
}

/* ---------- The Light Room ----------
 * Dark surround on purpose: the room is unlit so the stone is the only lit
 * thing in it. Do not add a background tint here — it would sit behind
 * every slab and falsify all three at once, which is the one failure this
 * screen exists to prevent. */
.light-room{max-width:1240px;margin:0 auto;padding:56px 24px 96px}
.lr-head{max-width:56ch;margin:0 auto 44px;text-align:center}
.lr-title{font-family:var(--font-d);font-size:clamp(28px,4vw,46px);letter-spacing:.02em;margin:0}
.lr-lede{color:var(--muted);margin:14px 0 0;line-height:1.7}
.lr-empty{text-align:center;color:var(--muted);padding:40px 0}

.lr-wall{display:grid;gap:18px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.lr-slab{margin:0}
.lr-frame{position:relative;aspect-ratio:3/4;overflow:hidden;background:#0D0B09}
/* All three shots are stacked and only one is opaque, so switching the
 * light is a class change and not a network request. The cost is three
 * card renditions per slab, which the worker has already warmed. */
.lr-shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0}
.lr-shot[data-current]{opacity:1}
html.js .lr-shot{transition:opacity .5s var(--ease)}

.lr-slab figcaption{display:flex;flex-direction:column;gap:3px;padding:12px 2px 0}
.lr-name{font-family:var(--font-d);font-size:15px;letter-spacing:.03em;text-decoration:none}
.lr-sub{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.lr-note{font-size:11px;color:var(--muted);opacity:.75}
.lr-drop{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  text-decoration:none;margin-top:4px;align-self:flex-start}
.lr-drop:hover{color:var(--ink)}

.lr-switch{display:flex;justify-content:center;margin:30px auto 0;width:fit-content;
  border:1px solid var(--line-strong);overflow:hidden}
.lr-light{padding:9px 20px;font-size:11px;letter-spacing:.2em;text-transform:uppercase;
  text-decoration:none;color:var(--muted)}
.lr-light + .lr-light{border-left:1px solid var(--line-strong)}
.lr-light.is-on{background:var(--ink);color:var(--bg)}
.lr-light:focus-visible{outline:none;box-shadow:var(--ring)}

.lr-out{text-align:center;margin-top:26px}

.lr-picker{margin-top:64px;border-top:1px solid var(--line);padding-top:28px}
.lr-picker-title{font-family:var(--font-d);font-size:15px;font-weight:500;letter-spacing:.03em;
  display:flex;align-items:baseline;gap:12px;margin:0 0 16px}
.lr-slots{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.lr-tray{list-style:none;margin:0;padding:0;display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
.lr-tray a{display:block;text-decoration:none;color:inherit}
.lr-tray img{width:100%;aspect-ratio:3/4;object-fit:cover;display:block}
.lr-tray span{display:block;font-size:11px;letter-spacing:.04em;margin-top:6px;color:var(--muted)}
.lr-tray a:hover span{color:var(--ink)}

/* ---------- Homepage hero: a real stone, not a gradient ----------
 * The catalogue IS the product, so the first thing above the fold is a slab.
 * Scrim is confined to the top and bottom so the stone's own colour is never
 * falsified in the middle — same rule as the stage overlay and the curtain. */
.hero-stone{position:relative;overflow:hidden}
.hero-slab{position:absolute;inset:0}
.hero-slab img{width:100%;height:100%;object-fit:cover;display:block}
.hero-scrim{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(13,11,9,.82) 0%,rgba(13,11,9,.30) 34%,
    rgba(13,11,9,.30) 62%,rgba(13,11,9,.88) 100%)}

/* THE SCRIM IS SIZED TO THE STONE.
 *
 * The hero is a real slab picked at random on every visit, and the headline
 * over it is white. About half the library is pale — Bianco Lasa, the
 * Calacattas — so on those visits the page was white type on a white stone.
 *
 * One fixed scrim cannot serve both ends of the library. Heavy enough for a
 * Calacatta turns Sahara Noir into a black sheet, which is precisely the
 * fault the mobile scrim was turned DOWN to fix. So the site measures the
 * photograph (renditions.measure_luminance) and picks one of two gradients.
 *
 * The dark case is the rule the rest of the site follows: tint the top and
 * bottom, leave the middle alone, because a tint through the middle
 * falsifies the stone's colour and the colour is the product.
 *
 * THE LIGHT CASE DELIBERATELY BREAKS THAT RULE, and it is worth being honest
 * about why rather than pretending one rule covers both. On a stone PAGE the
 * visitor is judging the stone and fidelity wins. On the homepage the slab is
 * a mood and the sentence is the point — an unreadable headline is a worse
 * outcome than a slab shown a shade deeper than life. A visitor who wants
 * the true colour is one click away, on a page built for exactly that.
 *
 * Unmeasured images take the LIGHT treatment. See StoneImage.tone. */
.hero-stone[data-tone="light"] .hero-scrim{
  background:linear-gradient(180deg,rgba(13,11,9,.86) 0%,rgba(13,11,9,.62) 38%,
    rgba(13,11,9,.60) 60%,rgba(13,11,9,.92) 100%)}
/* A last line of defence, and only on the pale case: a soft contact shadow
 * so a bright vein passing directly behind a letter cannot swallow it. Not
 * applied to dark stones, where it would only muddy the type. */
.hero-stone[data-tone="light"] .display,
.hero-stone[data-tone="light"] .lede{
  text-shadow:0 1px 24px rgba(13,11,9,.55)}
.hero-stone .inner{position:relative;z-index:2}
.hero-credit{position:absolute;right:28px;bottom:22px;z-index:2;
  font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(242,237,227,.62);text-decoration:none}
.hero-credit:hover{color:rgba(242,237,227,.95);text-decoration:none}

/* THE PHONE HERO.
 *
 * The scrim's stops are a DESKTOP measurement: 82% black at the top, easing
 * to 30% by a third of the way down. That was drawn against a 57px header
 * and a hero roughly as wide as it is tall. On a phone the header is taller,
 * the hero is a tall portrait, and the same 82% stop covers the whole of the
 * first screen — so the top of the homepage reads as a dead black band with
 * a stone somewhere below it. The stone that ships as the hero, Sahara Noir,
 * is nearly black itself, which is why it disappeared completely.
 *
 * The fix is to lift the top stop and reach the clear midsection sooner. The
 * rule the stops still obey is the one from the stage overlay: the MIDDLE is
 * never tinted, because a tint there falsifies the stone's own colour, and
 * the colour is the product.
 *
 * The bottom stop stays heavy — it is what the buttons sit on. */
@media (max-width:820px){
  .hero-scrim{background:linear-gradient(180deg,
    rgba(13,11,9,.58) 0%,rgba(13,11,9,.16) 26%,
    rgba(13,11,9,.24) 58%,rgba(13,11,9,.90) 100%)}
  /* The pale case on a phone, where the headline covers most of the slab and
   * there is nowhere for the eye to go if it fails. Heavier than desktop for
   * that reason. */
  .hero-stone[data-tone="light"] .hero-scrim{background:linear-gradient(180deg,
    rgba(13,11,9,.80) 0%,rgba(13,11,9,.58) 26%,
    rgba(13,11,9,.62) 58%,rgba(13,11,9,.93) 100%)}
  /* Two calls to action, two different widths, ragged down the left. On a
   * phone they become one column at a single width. */
  .hero-stage .row{display:flex;flex-direction:column;align-items:stretch;gap:12px}
  .hero-stage .row .btn{width:100%;text-align:center}
}

/* The three doors at the foot of the homepage. */
.panel.door{display:block;color:inherit;text-decoration:none;
  border:1px solid transparent;transition:border-color .2s var(--ease)}
.panel.door:hover{text-decoration:none;border-color:var(--line-strong)}
.panel.door h3{margin:0 0 6px}
.panel.door p{margin:0;color:var(--muted)}

/* ---------- Library facets ----------
 * Built for 513 stones. Facets are plain links so the library works with
 * scripts off, and a value with nothing behind it is never rendered — a
 * "Blue (0)" tells a visitor the collection is thin. */
.facets{display:grid;gap:26px;margin-top:34px;padding-top:24px;
  border-top:1px solid var(--line);
  grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.facet ul{list-style:none;margin:10px 0 0;padding:0;display:flex;
  flex-wrap:wrap;gap:7px}
.facet a{display:inline-flex;align-items:baseline;gap:6px;
  padding:5px 11px;border:1px solid var(--line-strong);
  border-radius:var(--radius);font-size:12px;letter-spacing:.04em;
  color:var(--ink-2);text-decoration:none}
.facet a:hover{border-color:var(--ink);color:var(--ink);text-decoration:none}
.facet a.is-on{background:var(--ink);border-color:var(--ink);color:var(--inverse)}
.facet-n{font-size:10px;opacity:.6;font-variant-numeric:tabular-nums}

/* "Show me something else" — discovery beats search at 513. */
.surprise{display:grid;gap:22px;align-items:center;margin-top:30px;
  grid-template-columns:minmax(0,220px) minmax(0,1fr);
  color:inherit;text-decoration:none}
.surprise:hover{text-decoration:none}
.surprise-frame{aspect-ratio:3/4;overflow:hidden;border-radius:var(--radius);
  background:var(--alt)}
.surprise-frame img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .7s var(--ease)}
.surprise:hover .surprise-frame img{transform:scale(1.035)}
.surprise-say h2{margin:6px 0 4px}

/* ============================================================
   THE FAST DOORS — the floating call / mail / WhatsApp stack
   ------------------------------------------------------------
   Rebuilt 11 August 2026. It was one WhatsApp-green pill from
   5 August; base.html carries the argument for why that went and
   what replaced it.

   THE COLOURS ARE THE FOOTER'S, NOT A LOGO'S. --side-bg and
   --gold, the pair used by the footer, the curtain hairline and
   the stage overlays. Like those, they do NOT flip with the
   theme: these discs are ink in both moods, because a control
   that inverts halfway down a scroll reads as a rendering fault.
   The cost is knowingly paid — WhatsApp green is the most
   recognisable object on the internet and this gives it up. What
   is bought is a site that still looks like itself.

   z-index 25 is chosen, not arbitrary. ABOVE page content and
   BELOW .site-head (30), so when the mobile drawer opens — the
   drawer lives inside the header — the stack is covered by it
   rather than floating over an open menu.

   Well below the curtain (100), the wallpaper viewer (120) and
   the lightbox (90): a full-screen view of a slab is the one
   moment on this site when nothing may sit on top of the stone.
   ============================================================ */
.hail{
  position:fixed;right:22px;bottom:22px;z-index:25;
  display:flex;flex-direction:column;align-items:flex-end;gap:10px}

/* The email button is a chooser. The wrapper exists only to give the
   panel something to position against — see base.html for why a plain
   mailto was not enough on its own. */
.hail-item{position:relative;display:flex;justify-content:flex-end}

.hail-btn{
  display:inline-flex;align-items:center;gap:9px;
  color:var(--gold);text-decoration:none}
.hail-btn:hover{color:var(--gold);text-decoration:none}

.hail-disc{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  /* 48px. Below ~44 is a mis-tap, and a mis-tap on the top one of
     these places a phone call. */
  width:48px;height:48px;border-radius:50%;
  background:var(--side-bg);
  border:1px solid rgba(185,164,126,.42);
  /* Two shadows, both faint: a tight one so the disc has an edge
     against a pale stone, and a wide soft one so it lifts. The
     old button used these at nearly double the opacity, which is
     most of what made it shout. */
  box-shadow:0 1px 3px rgba(22,21,15,.12), 0 8px 22px rgba(22,21,15,.14);
  transition:background .2s var(--ease), border-color .2s var(--ease),
             color .2s var(--ease), transform .2s var(--ease)}
.hail-disc svg{display:block}

.hail-btn:hover .hail-disc{
  background:var(--gold);color:var(--side-bg);
  border-color:var(--gold);transform:translateY(-1px)}
.hail-btn:focus-visible{outline:none}
.hail-btn:focus-visible .hail-disc{box-shadow:var(--ring)}

/* ---- the email chooser panel ----
   Sits ABOVE the button rather than beside it: the stack is already at the
   right edge, and a panel opening leftward on a phone runs off the screen
   or lands under the thumb that just tapped.

   It is not a floating overlay in the z-index sense — it inherits .hail
   stacking (25), so it stays under the header and the mobile drawer like
   everything else in this corner. */
.hail-menu{
  position:absolute;bottom:calc(100% + 10px);right:0;
  min-width:212px;padding:8px;
  background:var(--side-bg);
  border:1px solid rgba(185,164,126,.42);border-radius:14px;
  box-shadow:0 2px 6px rgba(22,21,15,.18), 0 16px 40px rgba(22,21,15,.28)}
.hail-menu[hidden]{display:none}

.hail-menu-head{
  margin:2px 10px 8px;font-family:var(--font-b);font-size:11px;
  letter-spacing:.12em;text-transform:uppercase;color:rgba(185,164,126,.72)}

.hail-menu-item{
  display:block;width:100%;text-align:left;
  padding:9px 10px;border:0;border-radius:9px;
  background:none;color:var(--on-dark);
  font-family:var(--font-b);font-size:14px;line-height:1.3;
  text-decoration:none;cursor:pointer;
  transition:background .15s var(--ease), color .15s var(--ease)}
.hail-menu-item:hover,
.hail-menu-item:focus-visible{
  background:rgba(185,164,126,.14);color:var(--gold);
  text-decoration:none;outline:none}

.hail-menu-address{
  margin:6px 10px 2px;font-family:var(--font-b);font-size:12px;
  color:rgba(250,248,244,.55);
  /* Selectable on purpose: the fallback for a browser that refuses the
     clipboard is a person reading it off the screen. */
  user-select:text}

/* The name of the channel, to the LEFT of the disc. Hidden at rest —
   at rest this stack should be three quiet marks — and revealed on
   hover, which is when somebody is deciding which one to press. */
.hail-label{
  font-family:var(--font-b);font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;white-space:nowrap;
  padding:6px 11px;border-radius:999px;
  background:var(--side-bg);color:var(--gold);
  border:1px solid rgba(185,164,126,.30);
  opacity:0;transform:translateX(8px);pointer-events:none;
  transition:opacity .2s var(--ease), transform .2s var(--ease)}

/* HOVER-CAPABLE POINTERS ONLY. On a touch screen there is no hover
   state to reveal anything with — the first tap IS the press — so
   without this query the labels would either never appear or, worse,
   appear stuck after a tap and sit over the page. Each button still
   carries an .sr-only name, so nothing is lost for a screen reader. */
@media (hover:hover){
  .hail-btn:hover .hail-label,
  .hail-btn:focus-visible .hail-label{opacity:1;transform:none}
}
@media (hover:none){
  .hail-label{display:none}
}

@media (max-width:820px){
  /* Tucked in tighter, and one size smaller. Three discs down the
     right edge of a 390px screen is a column across somebody's
     photograph of a slab; this keeps it to a margin. */
  .hail{right:16px;bottom:16px;gap:9px}
  .hail-disc{width:44px;height:44px}
}

@media (prefers-reduced-motion: reduce){
  .hail-disc, .hail-btn:hover .hail-disc, .hail-label{
    transition:none;transform:none}
}
