/* ==========================================================================
   MORE CREATIONS — site.css
   --------------------------------------------------------------------------
   A photography-first jewellery site. Two rules drive everything here:

     1. THE PHOTOGRAPHS ARE THE DESIGN. They get the largest areas, the widest
        margins and the least competition. Nothing decorative is placed over
        an image except a caption that earns its contrast.
     2. ONE ACCENT, ONE NEUTRAL. Gold is the only accent and it is only ever
        hairlines, small caps and eyebrow rules. The moment gold becomes a
        button fill it starts competing with the stones.

   Colour, type scale and rhythm all live in the tokens below; nothing further
   down hard-codes a colour.
   ========================================================================== */

/* ============================================================== tokens */

:root {
  /* ink — warm near-blacks, so photographs sit in a gallery rather than a void */
  --ink-900: #0b0a09;
  --ink-800: #121110;
  --ink-700: #1a1815;
  --ink-600: #242119;
  --ink-500: #34302a;
  --ink-400: #4d4841;

  /* paper — the light side, used for exactly one inverted section */
  --paper-100: #f6f3ee;
  --paper-200: #e9e4db;
  --paper-300: #d5cec2;

  /* gold — sampled from the More Creations mandala */
  --gold-100: #f7ecc6;
  --gold-200: #ecd79a;
  --gold-300: #d9b45c;
  --gold-400: #c9a227;
  --gold-500: #a3801c;

  /* text */
  --fg: #f2ede5;
  --fg-soft: #c8c0b4;
  --fg-mute: #8f887d;
  --fg-ink: #14120f;
  --fg-ink-soft: #57504400;

  --line: rgba(201, 162, 39, 0.20);
  --line-soft: rgba(242, 237, 229, 0.10);

  --rose: #c2304a;          /* used once: the ruby accent on the eyebrow rules */

  --font-display: 'Cormorant Garamond', 'Times New Roman', serif;
  --font-sans: 'Jost', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Luxury here is mostly rhythm, not decoration: generous vertical air, a
     wider gutter, and a slower ease. One fluid scale drives every section so
     nothing can drift out of step. */
  --gap: clamp(5rem, 10vw, 11rem);
  --gut: clamp(1.4rem, 6vw, 7rem);
  --measure: 58ch;

  /* Two easings. `--ease` for anything the user directly manipulates (hover,
     focus) and `--ease-slow` for entrances, which should feel unhurried. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-slow: cubic-bezier(0.16, 0.84, 0.28, 1);
}

/* ============================================================== reset */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink-900);
  color: var(--fg);
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: clamp(1rem, 0.5vw + 0.89rem, 1.0625rem);
  line-height: 1.78;
  /* Wider tracking on body copy is the single cheapest luxury signal there is:
     it reads as space bought on purpose rather than type set to fill. */
  letter-spacing: 0.012em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; }

button { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--gold-300);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--gold-400); color: var(--ink-900); }

/* =========================================================== typography */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: 1.04;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}

/* Larger and looser than before: display type at this size wants the air
   around it, and the tighter tracking only works with the extra leading. */
h1 { font-size: clamp(2.9rem, 7.6vw, 6.6rem); }
h2 { font-size: clamp(2.2rem, 4.8vw, 4.3rem); }
h3 { font-size: clamp(1.4rem, 2vw, 2rem); }
h4 { font-size: 1.1rem; letter-spacing: 0.02em; }

h1 em, h2 em, h3 em {
  font-style: italic;
  color: var(--gold-200);
}

p { margin: 0 0 1.2em; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 0 0 1.8rem;
  font-family: var(--font-sans);
  font-size: 0.68rem;
  font-weight: 400;
  /* The signature of the whole page: small caps opened out until they read as a
     label rather than a word. Capped at 0.30em and kept on one line — past that
     a mid-length label wraps and the wrap looks like a mistake. */
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: var(--gold-300);
  white-space: nowrap;
}

/* Long labels give up their tracking before they are allowed to wrap. */
@media (max-width: 620px) {
  .eyebrow { letter-spacing: 0.2em; white-space: normal; }
}

.eyebrow .rule {
  display: block;
  width: clamp(1.6rem, 4vw, 3.2rem);
  height: 1px;
  background: currentColor;
  opacity: 0.7;
  flex: none;
}

/* ============================================================== buttons */

.btn {
  --btn-fg: var(--fg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 1rem 2.1rem;
  border: 1px solid transparent;
  border-radius: 0;
  background: none;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.45s var(--ease), color 0.45s var(--ease),
              border-color 0.45s var(--ease), transform 0.45s var(--ease);
}

.btn--solid {
  background: var(--gold-300);
  color: var(--ink-900);
  border-color: var(--gold-300);
}
.btn--solid:hover { background: var(--gold-200); border-color: var(--gold-200); }

.btn--line {
  border-color: var(--line);
  color: var(--fg);
}
.btn--line:hover {
  border-color: var(--gold-300);
  color: var(--gold-200);
}
.btn--line:hover { background: rgba(201, 162, 39, 0.07); }

.btn--full { width: 100%; }

/* A gold keyline under a section's heading, drawn from the centre out. Applied
   to the h2 rather than the head wrapper so it measures the type, not the
   column, and only on the centred section heads. */
.section-keyline {
  position: relative;
  padding-bottom: clamp(1.6rem, 3vw, 2.4rem);
}
.section-keyline::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  translate: -50% 0;
  width: clamp(3rem, 9vw, 6.5rem);
  height: 1px;
  background: linear-gradient(to right, transparent, var(--gold-400), transparent);
}

/* The keyline supplies the space between the heading and the copy that follows
   it, so the heading itself needs almost none. The per-section head rules set
   their own margin, and they are declared further down the file, so they win on
   source order — which is why this is not written as a standalone override.
   The 0.5rem below is applied in each of those rules instead. */

/* ================================================================ header */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 3rem);
  padding: 1.15rem var(--gut);
  transition: background 0.5s var(--ease), backdrop-filter 0.5s var(--ease),
              border-color 0.5s var(--ease), padding 0.5s var(--ease);
  border-bottom: 1px solid transparent;
}

/* The nav sits directly over the hero photograph, so before it sticks it is
   given a soft top-down scrim of its own rather than a hard bar. */
.nav::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(11, 10, 9, 0.85), rgba(11, 10, 9, 0));
  opacity: 1;
  transition: opacity 0.5s var(--ease);
  pointer-events: none;
  z-index: -1;
}

.nav.is-stuck {
  background: rgba(11, 10, 9, 0.86);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom-color: var(--line-soft);
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}
.nav.is-stuck::before { opacity: 0; }

/* The nav lockup is the supplied wordmark rebuilt in type: "M" + mandala as the
   O + "RE", with CREATIONS beneath. Setting it rather than scaling down
   wordmark.png keeps it crisp at nav size and lets the mandala rotate on hover,
   which a single flattened image could not do. */
.nav__brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  margin-right: auto;
  line-height: 1;
}

.nav__row {
  display: flex;
  align-items: center;
  /* No gap: the mandala is the O, so it must sit inside the word rather than
     beside it. */
}

.nav__word {
  font-family: var(--font-display);
  font-size: 1.72rem;
  font-weight: 300;
  letter-spacing: 0.13em;
}
/* trailing letter-spacing would otherwise push the mandala off-centre */
.nav__row .nav__word:first-child { margin-right: -0.13em; }

.nav__mark {
  width: 1.56rem;
  height: 1.56rem;
  flex: none;
  margin: 0 0.06em;
  transition: transform 0.8s var(--ease);
}
.nav__brand:hover .nav__mark { transform: rotate(120deg); }

/* CREATIONS sits on the second line, justified to the width of the MORE above
   it — which is how the supplied wordmark is set, and the main reason this
   lockup is type rather than a scaled-down PNG. Each letter is its own span so
   space-between can do the justification; scaling the text would stretch the
   glyphs. The brand is announced once, by the link's aria-label, so this line
   is hidden from assistive tech to avoid reading "M O R E C R E A T I O N S". */
.nav__sub {
  display: flex;
  justify-content: space-between;
  width: 100%;
  font-family: var(--font-sans);
  font-size: 0.78rem;
  font-weight: 300;
  letter-spacing: 0;
  line-height: 1;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-top: 0.3rem;
}

.nav__links {
  display: flex;
  column-gap: clamp(1.1rem, 2.4vw, 2.3rem);
  font-size: 0.72rem;
  letter-spacing: 0.17em;
  text-transform: uppercase;
}
.nav__links a {
  position: relative;
  padding: 0.3rem 0;
  color: var(--fg-soft);
  transition: color 0.3s var(--ease);
}
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--gold-300);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.45s var(--ease);
}
.nav__links a:hover { color: var(--gold-200); }
.nav__links a:hover::after { transform: scaleX(1); }

.nav__burger {
  display: none;
  width: 2.4rem; height: 2.4rem;
  background: none; border: 0; cursor: pointer;
  padding: 0.6rem 0.35rem;
  flex-direction: column;
  justify-content: center;
  gap: 0.34rem;
}
.nav__burger span {
  display: block;
  height: 1px;
  background: var(--fg);
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease);
}
.nav__burger[aria-expanded="true"] span:first-child { transform: translateY(0.35rem) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:last-child { transform: translateY(-0.35rem) rotate(-45deg); }

.drawer {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.4rem;
  padding: 0 var(--gut);
  background: rgba(11, 10, 9, 0.97);
  backdrop-filter: blur(10px);
  transform: translateY(-101%);
  transition: transform 0.6s var(--ease);
}
.drawer.is-open { transform: translateY(0); }
.drawer a {
  font-family: var(--font-display);
  font-size: clamp(2rem, 8vw, 3rem);
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.drawer .btn { margin-top: 2rem; align-self: flex-start; }

/* ================================================================== hero */

/* Side by side on desktop: the copy owns the left column and the photograph
   owns the right, whole. This is the honest arrangement for a jewellery site —
   cropping a collar to make room for a headline is a bad trade, and an earlier
   full-bleed version needed a heavy scrim over the stones to stay readable. */
.hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  align-items: center;
  gap: clamp(1.5rem, 4vw, 4.5rem);
  min-height: 100svh;
  padding: clamp(7rem, 12vh, 9.5rem) var(--gut) clamp(3rem, 7vh, 5rem);
  isolation: isolate;
}

/* A soft pool of warm light behind the photograph, so the image sits inside the
   dark page instead of being cut out of it. */
.hero::after {
  content: '';
  position: absolute;
  z-index: -1;
  top: 50%;
  right: calc(var(--gut) * -0.5);
  width: 62vw;
  height: 72vh;
  translate: 0 -50%;
  background: radial-gradient(closest-side, rgba(201, 162, 39, 0.11), rgba(11, 10, 9, 0) 78%);
  pointer-events: none;
}

.hero__media {
  margin: 0;
  position: relative;
}

.hero__media img {
  width: 100%;
  height: auto;
  border: 1px solid var(--line-soft);
  animation: heroReveal 1.5s var(--ease) both;
}

/* Fade only, deliberately no transform. The interactive layer measures this
   image's rendered box to size its canvas, and a scale() animation left the
   measurement 2.5% too large if it happened to be taken mid-animation — which
   it was, every time, because the layer starts on window load. The result was a
   canvas 8px taller than the photograph and a subtly mis-scaled layer. Opacity
   cannot corrupt a measurement. */
@keyframes heroReveal {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.hero__copy { max-width: 40rem; }

.hero__title { margin: 0 0 1.7rem; }
.hero__title .line { display: block; overflow: hidden; }
.hero__title .line > span {
  display: block;
  animation: riseIn 1.15s var(--ease) both;
}
.hero__title .line:last-child > span { animation-delay: 0.13s; }

@keyframes riseIn {
  from { transform: translateY(105%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.hero__lede {
  font-size: 1.06rem;
  color: var(--fg-soft);
  max-width: 42ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 2.3rem;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem clamp(1.4rem, 3vw, 2.8rem);
  margin: 2.9rem 0 0;
  padding-top: 1.8rem;
  border-top: 1px solid var(--line-soft);
  max-width: 40rem;
}
.hero__meta dt {
  font-family: var(--font-display);
  font-size: 1.24rem;
  color: var(--gold-200);
  margin-bottom: 0.15rem;
}
.hero__meta dd {
  margin: 0;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--fg-mute);
  max-width: 19ch;
  line-height: 1.5;
}

/* A gold keyline set inside the photograph, the way a jeweller frames a stone
   in the window. It must not sit on the image edge, so it is inset a fixed
   distance rather than outlined, and it never receives pointer events. */
.hero__media {
  position: relative;
}
.hero__media::after {
  content: '';
  position: absolute;
  inset: clamp(0.6rem, 1.4vw, 1.15rem);
  border: 1px solid rgba(217, 180, 92, 0.34);
  pointer-events: none;
  /* above the interactive layer's canvas, or the frame would be painted over */
  z-index: 2;
}

/* ---- the interactive photograph layer --------------------------------
   The canvas is created by assets/js/interactive-photo.js and positioned over
   the <img> it replaces. Its BOX is deliberately not set here: the script sizes
   it in explicit pixels measured from the image, because a stylesheet
   `inset: 0` / `100%` would win on the bottom and right edges and stretch the
   canvas away from the photograph. Only the parts CSS must own live here. */
.iphoto__canvas {
  position: absolute;
  display: block;
  z-index: 1;
}

.hero__credit {
  margin-top: 1.1rem;
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

/* ============================================================= statement */

.statement {
  position: relative;
  display: grid;
  place-items: center;
  text-align: center;
  padding: var(--gap) var(--gut);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
}

.statement__mark {
  position: absolute;
  width: min(46rem, 92vw);
  opacity: 0.055;
  pointer-events: none;
  animation: slowSpin 120s linear infinite;
}
@keyframes slowSpin { to { transform: rotate(360deg); } }

.statement__text {
  position: relative;
  margin: 0;
  max-width: 30ch;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.2vw, 2.6rem);
  font-style: italic;
  line-height: 1.24;
  color: var(--fg);
}
.statement__text em { color: var(--gold-200); font-style: italic; }

/* ================================================================== work */

.work { padding: var(--gap) 0 var(--gap); }

.work__head {
  padding: 0 var(--gut);
  max-width: 56rem;
  margin: 0 auto var(--gap);
  text-align: center;
}
.work__head .eyebrow { justify-content: center; }
.work__head h2 { margin: 0 0 0.5rem; }
.work__note { margin-inline: auto; color: var(--fg-soft); }

/* ---- the piece's stamped details ---------------------------------------
   Metal, weights and certification, set as a definition list rather than prose
   because a buyer checks these against a certificate, and a fact is easier to
   find in a list than in a paragraph. Two columns on wide screens. */
.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: 0 clamp(2rem, 5vw, 5rem);
  max-width: 68rem;
  margin: var(--gap) auto 0;
  padding: 0 var(--gut);
}
.specs > div {
  display: grid;
  grid-template-columns: minmax(8rem, 0.6fr) 1fr;
  gap: 0.4rem 1.4rem;
  align-items: baseline;
  padding: 1.05rem 0;
  border-top: 1px solid var(--line-soft);
}
.specs dt {
  font-size: 0.63rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold-300);
}
.specs dd {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.65;
  color: var(--fg-soft);
}

/* narrower than the two-column grid: stack the label above the value */
@media (max-width: 520px) {
  .specs > div { grid-template-columns: 1fr; gap: 0.25rem; }
}

/* Now a route to the enquiry form rather than a closing statement, so it reads
   as a sentence again rather than as an all-caps notice. The link inside it is
   the only interactive thing under the photographs. */
.work__foot {
  margin: calc(var(--gap) * 0.7) auto 0;
  padding: 0 var(--gut);
  max-width: 44ch;
  text-align: center;
  font-size: 0.84rem;
  letter-spacing: 0.04em;
  line-height: 1.8;
  color: var(--fg-mute);
}

/* Inline text link. Gold underline rather than a full underline, so it reads as
   an invitation next to body copy instead of as emphasis. */
.link {
  color: var(--gold-200);
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.link:hover { color: var(--gold-100); border-color: var(--gold-300); }
/* ---- the gallery grid ---------------------------------------------------
   An editorial grid rather than a uniform board of thumbnails. The lead frame
   is full-bleed. The three details below it are placed explicitly rather than
   left to auto-flow: a tall frame spanning two rows on the left, with a square
   and a landscape stacked beside it. Auto-placement put the last two on rows of
   their own and left a hole under the third, which reads as a mistake rather
   than as space. Every source photograph is landscape, so the tall frame takes
   its crop from the centre and the grid keeps a clean outer edge. */

/* ---- one piece per block -----------------------------------------------
   Each product renders its own heading, gallery and specification list. The
   blocks are separated by a hairline and a lot of air, because two pieces shown
   close together read as one collection rather than as two separate commissions. */
.piece + .piece {
  margin-top: calc(var(--gap) * 1.15);
  padding-top: calc(var(--gap) * 0.9);
  border-top: 1px solid var(--line-soft);
}

.piece__head {
  max-width: 46rem;
  margin: 0 auto calc(var(--gap) * 0.55);
  padding: 0 var(--gut);
  text-align: center;
}

/* an h3, because the section already owns the h2 — but sized as a section
   heading rather than a card title, or the two pieces read as sub-sections */
.piece__name {
  font-size: clamp(1.9rem, 3.6vw, 3.1rem);
  line-height: 1.06;
  margin: 0 0 1.3rem;
}
.piece__name em { font-style: italic; color: var(--gold-200); }

.piece__lede {
  margin-inline: auto;
  max-width: 52ch;
  color: var(--fg-soft);
}

/* the specs sit under their own piece, so the spacing the work section used to
   supply above them is now the block's own */
.piece .specs { margin-top: calc(var(--gap) * 0.55); }

.gallery {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(1rem, 2.4vw, 2.2rem);
  padding: 0 var(--gut);
}

.shot {
  position: relative;
  grid-column: span 6;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  text-align: left;
  overflow: hidden;
  display: block;
}

/* the lead frame runs the full width of the page and breaks the gutter */
.shot--lead {
  grid-column: 1 / -1;
  margin-inline: calc(var(--gut) * -1);
}

/* ---- detail grid: one tall frame beside two stacked --------------------
   The tall frame on the left spans both rows and the two right-hand frames take
   a row each, so the rows split the height the tall frame occupies and the
   bottom edges cannot step.

   object-fit then crops each to whatever shape its cell turns out to be. An
   earlier note here claimed that was always fine "because every one of these
   photographs is the same square frame of the same piece, centred". The squares
   are the same, but the PIECE is not centred in them, and that assumption is
   what produced the over-cropped right-hand pair the client objected to: a 4:3
   frame shows only the middle 75% of a square, which for the cascade clipped the
   pendant's tail. See the per-piece object-position rules below. */
.info-grid {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 7fr 5fr;
  grid-template-rows: 1fr 1fr;
  gap: clamp(1rem, 2.4vw, 2.2rem);
}

/* The tiles are direct children of ONE grid: the tall frame spans both rows on
   the left, and the two detail frames take a row each on the right. That is what
   makes the tiling exact — the rows split the height the tall frame occupies, so
   the bottom edges cannot disagree.

   NB: these must out-specify `.shot { grid-column: span 6 }`, which is why they
   are written `.info-grid > .shot--x` rather than `.shot--x`. And `right-a` and
   `right-b` are two SLOTS, so each needs its own rule — mapping both to one
   class put them in the same cell and they overlapped exactly. */
.info-grid > .shot--tall { grid-column: 1; grid-row: 1 / span 2; }
.info-grid > .shot--right-a { grid-column: 2; grid-row: 1; }
.info-grid > .shot--right-b { grid-column: 2; grid-row: 2; }

/* stretch to fill the cell rather than holding a fixed aspect ratio, or the two
   rows will not split the height evenly */
/* The RIGHT-hand pair is what sets the row heights, so this is where the pair's
   proportions are actually decided. Both `height: 100%` and a missing
   aspect-ratio let these stretch to 514x583 each, which forced the tall frame
   beside them to 1200px and stretched its photograph into a portrait crop.

   Giving them a 4:3 ratio pins the rows to 386px each, so the tall frame lands
   at 719x806 — a mild portrait crop of the square clasp photograph, with none of
   the empty neck that a 1200px frame was showing. */
.info-grid > .shot--right-a .shot__frame,
.info-grid > .shot--right-b .shot__frame { height: auto; }
.info-grid > .shot--right-a img,
.info-grid > .shot--right-b img { height: auto; aspect-ratio: 4 / 3; }

/* ---- where the crop lands, per piece -----------------------------------
   A 4:3 frame inside a square photograph shows only the middle 75% of the
   height. Where that 75% should sit is a property of the photograph, not of the
   layout, so it is set per piece.

   The cascade's squares put the pendant LOW: the sapphires run from about 0.38
   to 0.93 of the frame, so a centre crop (0.125..0.875) cut the bottom of the
   drop off and framed the middle of the pendant — the "too zoomed in" the client
   reported. object-position 50% 100% moves the window down to 0.25..1.00, which
   shows the whole piece with the cascade completing inside the frame.

   This must NOT be global. The ruby squares put the necklace HIGH — its stones
   sit at roughly 0.05..0.54 — and the same rule would crop the clasp off the top
   of both frames. Measured with tools/crop-debug.mjs. */
/* The cascade's LEAD frame is 16:9, which from a square shows a band only ~0.61
   of the height tall — and this piece is tall and narrow, so the band cannot hold
   the chain, the neckline and the drop together. The client's call was to spend
   it on the drop. At 90% the window is 0.350..0.961 against stones measured at
   0.370..0.930, so the cascade runs from its first sapphire to its final stone
   and only the chain above is trimmed. See README for the alternatives.

   Each re-shot lead has needed this retuned, because the piece sits at a
   different height in every frame: 80% -> 88% -> 90%. If the photograph is
   replaced again, re-measure rather than reusing this number. */
.piece--cascade .shot--lead img { object-position: 50% 90%; }

.piece--cascade .info-grid > .shot--right-a img,
.piece--cascade .info-grid > .shot--right-b img { object-position: 50% 100%; }

.shot__frame {
  position: relative;
  overflow: hidden;
  background: var(--ink-800);
}

.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.15s var(--ease), filter 0.7s var(--ease);
}

.shot--lead img { aspect-ratio: 16 / 9; }
/* Square. This frame holds the clasp view, whose photograph is square, and a
   square frame is also noticeably shorter than the 4:5 it had — which left a
   band of empty mock neck below the necklace in every crop.

   An earlier note here claimed 4:5 was the only ratio that would tile, and that
   was wrong: it assumed the right-hand frames needed their own aspect ratio.
   They do not — they are `object-fit: cover` and stretch to fill their cells.
   Only this frame's ratio is constrained, and by nothing but taste. */
.shot--tall img { aspect-ratio: 1 / 1; }

/* This frame holds the clasp view, and that photograph is mostly empty mock neck
   below the necklace.

   NB: the `object-position` below is currently INERT, and an earlier note here
   claimed otherwise. The clasp photograph is square and this frame is square
   (719x719), so the drawn image is exactly 719x719 with zero overflow and there
   is no spare space on either axis to position within. What actually centres the
   clasp is the photograph, not this rule. It is kept because it is harmless and
   starts working the moment the frame stops being exactly square — and because a
   clasp shot for the cascade is expected, which may not be square.

   `height: auto` is load-bearing. `.shot img` sets `height: 100%`, and a
   percentage height alongside an aspect-ratio is over-constrained: the height
   wins and the ratio is ignored. That is what kept this frame 1200px tall even
   after the ratio changed to 1:1 — the tile never got shorter, so the grid never
   shrank. Here the aspect ratio has to be the thing that decides. */
.info-grid > .shot--tall img {
  object-position: 50% 40%;
  height: auto;
}

.shot:hover img { transform: scale(1.045); }
.shot:focus-visible { outline-color: var(--gold-300); }

/* a hover veil with the "view" affordance */
.shot__veil {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(11, 10, 9, 0.42);
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.shot:hover .shot__veil,
.shot:focus-visible .shot__veil { opacity: 1; }

.shot__veil span {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.7rem 1.3rem;
  border: 1px solid rgba(242, 237, 229, 0.5);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  backdrop-filter: blur(3px);
}

.shot__cap {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  padding: 1rem 0.15rem 0;
}
.shot__num {
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  color: var(--gold-300);
  flex: none;
}
.shot__title {
  font-family: var(--font-display);
  font-size: 1.22rem;
  line-height: 1.2;
}
.shot__sub {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--fg-mute);
}

/* ============================================================ specialty */

/* The section that says what the business actually is. It sits between the
   photographs and the process, on the light-adjacent panel, so it reads as a
   statement of fact rather than more sales copy. */
.specialty {
  padding: var(--gap) var(--gut);
  background: var(--ink-800);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}

.specialty__head { max-width: 52rem; margin-bottom: var(--gap); }
.specialty__head h2 { margin: 0.4rem 0 0.5rem; }
.specialty__intro { color: var(--fg-soft); max-width: 56ch; }

.specialty__cols {
  display: grid;
  /* Exactly four cards, so this is pinned to a deliberate 2x2 rather than fitted
     with auto-fit. auto-fit gave FOUR columns at 1440px (281px cards, too narrow
     for four lines of copy), THREE at 1100px (which orphaned the fourth card on a
     second row with two-thirds of the width empty), and two only below 900px.
     A fixed 2x2 is balanced at every desktop width; the media query below drops
     it to one column on a phone. */
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.6rem, 3.5vw, 3rem);
}

.spec { padding-top: 1.5rem; border-top: 1px solid var(--line); }
.spec h3 {
  font-size: 1.5rem;
  margin-bottom: 0.7rem;
  color: var(--gold-200);
}
.spec p { font-size: 0.9rem; line-height: 1.7; color: var(--fg-soft); }

/* =============================================================== about */

/* The company's own description, set as it was written rather than broken into
   slogans. Two asymmetric columns: the prose carries the weight on the left,
   and the three "more" commitments sit beside it as a list, because they are a
   list and setting them as prose would flatten the parallel. */
.about {
  padding: var(--gap) var(--gut);
  border-top: 1px solid var(--line-soft);
}

.about__head { max-width: 52rem; margin-bottom: var(--gap); }
.about__head .eyebrow { justify-content: flex-start; }
.about__head h2 { margin: 0.4rem 0 0.5rem; }

.about__cols {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}

.about__lede p {
  font-size: clamp(1.05rem, 1.15vw, 1.22rem);
  line-height: 1.94;
  color: var(--fg-soft);
  max-width: 54ch;
}
.about__lede p + p { margin-top: 1.4rem; }

.about__more {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.about__more li {
  position: relative;
  padding: 1.15rem 0 1.15rem 1.5rem;
  border-top: 1px solid var(--line-soft);
}
.about__more li:last-child { border-bottom: 1px solid var(--line-soft); }
/* a gold diamond, echoing the tick markers used in #commission */
.about__more li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 1.75rem;
  width: 0.38rem;
  height: 0.38rem;
  background: var(--gold-400);
  rotate: 45deg;
}
.about__more b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 400;
  color: var(--gold-200);
  margin-bottom: 0.15rem;
}
.about__more span {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--fg-mute);
}

@media (max-width: 900px) {
  .about__cols { grid-template-columns: 1fr; }
  /* Two cards side by side stop working once each is under ~20rem. */
  .specialty__cols { grid-template-columns: 1fr; }
}

/* ================================================================= craft */

.craft {
  padding: var(--gap) var(--gut);
  background: var(--ink-800);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}

.craft__head { max-width: 46rem; margin-bottom: var(--gap); }
.craft__head h2 { margin-top: 0.4rem; }

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: clamp(1.8rem, 3.5vw, 3.2rem);
  counter-reset: step;
}

.step { position: relative; padding-top: 1.6rem; border-top: 1px solid var(--line); }

.step__n {
  display: block;
  font-family: var(--font-display);
  font-size: 0.86rem;
  letter-spacing: 0.2em;
  color: var(--gold-300);
  margin-bottom: 0.7rem;
}

.step h4 {
  font-size: 1.5rem;
  margin-bottom: 0.55rem;
}

.step p {
  font-size: 0.9rem;
  color: var(--fg-soft);
  line-height: 1.68;
}

/* Each column heading in the specialty section gets a short gold keyline above
   it. A single 1px gold rule, left-aligned, does the work that a border and a
   heading and more whitespace would otherwise need. */
.spec { position: relative; }
.spec::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 2.6rem;
  height: 1px;
  background: var(--gold-400);
}


/* ================================================================= visit */

.visit {
  position: relative;
  display: flex;
  justify-content: center;
  text-align: center;
  padding: calc(var(--gap) * 1.15) var(--gut);
  background: var(--ink-800);
  border-top: 1px solid var(--line-soft);
  overflow: hidden;
}

.visit__mark {
  position: absolute;
  width: min(34rem, 76vw);
  opacity: 0.05;
  pointer-events: none;
  top: 50%; left: 50%;
  translate: -50% -50%;
  animation: slowSpin 140s linear infinite reverse;
}

.visit__inner {
  position: relative;
  width: 100%;
  max-width: 44rem;
  text-align: center;
}

.visit h2 { position: relative; margin-bottom: 1.5rem; }
.visit p {
  position: relative;
  margin-inline: auto;
  max-width: 46ch;
  color: var(--fg-soft);
}

/* ---- the enquiry form ---------------------------------------------------
   The last thing on the page is the thing you want people to do, so it is a
   form in the flow rather than a button that opens one. It shares field names
   and validation with the modal version, so the two cannot drift apart. */

.enquiry {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1.3rem;
  margin-top: 3rem;
  padding: clamp(1.6rem, 3.4vw, 2.8rem);
  text-align: left;
  /* No fill and no blur: a filled panel reads as a form on a website. A bare
     gold hairline around generous padding reads as an order card. */
  border: 1px solid var(--line);
}

/* inner keyline, set just inside the border, as on a box lid */
.enquiry::before {
  content: '';
  position: absolute;
  inset: 5px;
  border: 1px solid rgba(201, 162, 39, 0.12);
  pointer-events: none;
}

.enquiry__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: 1.3rem;
}

.enquiry label {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  font-size: 0.63rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.enquiry input,
.enquiry textarea {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.01em;
  color: var(--fg);
  background: var(--ink-900);
  border: 1px solid var(--line-soft);
  padding: 0.75rem 0.9rem;
  resize: vertical;
  transition: border-color 0.3s var(--ease);
}
/* a seeded specification is one line, so this should not open as a wall of box */
.enquiry textarea { min-height: 4.6rem; max-height: 14rem; }
.enquiry input::placeholder,
.enquiry textarea::placeholder { color: #6b655c; }
.enquiry input:focus,
.enquiry textarea:focus {
  outline: none;
  border-color: var(--gold-400);
}
.enquiry input.is-bad,
.enquiry textarea.is-bad { border-color: var(--rose); }

.enquiry__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.9rem 1.6rem;
  margin-top: 0.4rem;
}

/* The submit sits inside a gold-ruled card, so it is an outline rather than a
   gold fill: two gold rectangles stacked would fight each other. */
.enquiry__submit {
  border-color: var(--gold-400);
  color: var(--gold-200);
}
.enquiry__submit:hover {
  background: var(--gold-300);
  border-color: var(--gold-300);
  color: var(--ink-900);
}

.enquiry__alt {
  font-size: 0.8rem;
  color: var(--fg-mute);
}
.enquiry__alt a {
  color: var(--fg-soft);
  border-bottom: 1px solid var(--line);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.enquiry__alt a:hover { color: var(--gold-200); border-color: var(--gold-300); }

.enquiry__status {
  margin: 0;
  min-height: 1.3em;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--gold-200);
}
.enquiry__status.is-bad { color: #ff9d9d; }
.enquiry__status a {
  color: var(--gold-100);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  transition: border-color 0.3s var(--ease);
}
.enquiry__status a:hover { border-color: var(--gold-200); }

/* ---- contact block ------------------------------------------------------
   The address and number again, in the section people actually land on, so they
   are reachable without scrolling to the footer. */
.visit__contact {
  position: relative;
  list-style: none;
  margin: 2.2rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.7rem clamp(1.2rem, 3vw, 2.4rem);
  font-size: 0.84rem;
  color: var(--fg-mute);
}
.visit__contact li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  text-align: left;
}
.visit__contact svg { flex: none; color: var(--gold-400); }
.visit__contact a { color: var(--fg-soft); transition: color 0.3s var(--ease); }
.visit__contact a:hover { color: var(--gold-200); }

/* ---- the map -------------------------------------------------------------
   A Google embed is a bright rectangle in the middle of a very dark page. It is
   toned down with a light desaturation and opacity — NOT the invert/hue-rotate
   trick, which turns the whole map into a negative. That trick was tried and
   rejected for two reasons: it inverts Google's own logo and attribution text,
   and Google's branding guidelines require their marks be shown unmodified. It
   also just looked wrong.

   Full colour and full opacity return on hover, which doubles as the
   affordance that the map is interactive. The tint is a filter, not an overlay,
   so the map stays usable — drag, zoom and click all still work. */
.visit__map {
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  max-width: 40rem;
  position: relative;
}
.visit__map iframe {
  display: block;
  width: 100%;
  height: clamp(15rem, 34vw, 23.75rem);
  border: 1px solid var(--line);
  opacity: 0.72;
  filter: grayscale(0.35) contrast(0.95);
  transition: opacity 0.6s var(--ease), filter 0.6s var(--ease), border-color 0.6s var(--ease);
}
.visit__map:hover iframe,
.visit__map:focus-within iframe {
  opacity: 1;
  filter: none;
  border-color: var(--gold-400);
}
.visit__map figcaption {
  margin-top: 0.75rem;
  text-align: center;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.visit__map figcaption a { color: var(--fg-mute); transition: color 0.3s var(--ease); }
.visit__map figcaption a:hover { color: var(--gold-200); }

/* Motion preferences: no tint transition, just the two end states. */
@media (prefers-reduced-motion: reduce) {
  .visit__map iframe { transition: none; }
}

/* ================================================================ footer */

.foot {
  padding: clamp(2.6rem, 5vw, 4rem) var(--gut) 1.6rem;
  border-top: 1px solid var(--line-soft);
}

.foot__top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr);
  gap: clamp(1.8rem, 4vw, 4rem);
  align-items: start;
}

.foot__wordmark { width: min(15rem, 46vw); opacity: 0.85; }

.foot__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: 1.8rem;
}
.foot__cols h4 {
  font-family: var(--font-sans);
  font-size: 0.66rem;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-300);
  margin-bottom: 0.8rem;
}
.foot__cols p {
  font-size: 0.82rem;
  line-height: 1.75;
  color: var(--fg-mute);
  max-width: none;
}
.foot__cols a { transition: color 0.3s var(--ease); }
.foot__cols a:hover { color: var(--gold-200); }

.foot__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.6rem 1.5rem;
  margin-top: 3rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line-soft);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--fg-mute);
}
.foot__fine { opacity: 0.75; }

/* ============================================================== lightbox */

.lb {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  /* `grid-template-rows: 1fr` + `margin: auto` on the frame, NOT
     `place-items: center`. Centring a grid item in a row that cannot grow past
     the viewport makes the overflow escape at BOTH ends, and the top half cannot
     be scrolled to — the content is simply unreachable. `margin: auto` centres
     the frame while there is room and degrades to top-aligned when there is not,
     so every pixel stays reachable. */
  grid-template-rows: 1fr;
  padding: clamp(0.75rem, 2.5vw, 2.4rem);
  overflow-y: auto;
  /* Reaching the end of the lightbox must not start scrolling the page behind. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.lb[hidden] { display: none; }

.lb__scrim {
  position: absolute;
  inset: 0;
  background: rgba(6, 6, 5, 0.94);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  cursor: zoom-out;
}
.lb.is-open .lb__scrim { opacity: 1; }

.lb__frame {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  max-width: min(96rem, 100%);
  /* Centres the frame inside the 1fr row while it fits; when it does not, the
     auto margins collapse and the frame sits at the top where every part of it
     can be scrolled to. */
  margin: auto;
  min-height: 0;
  opacity: 0;
  transform: scale(0.985);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.lb.is-open .lb__frame { opacity: 1; transform: scale(1); }

.lb__img {
  max-width: 100%;
  /* The frame also has to hold the caption, the count and the dialog padding —
     about 120px measured at desktop. Sizing the photograph as a fraction of the
     VIEWPORT cannot know that, and at 74svh the whole frame came to 944px, which
     does not fit a 900px laptop screen: the caption needed a scroll to read.

     A percentage max-height against the flex frame does not work either — the
     frame has no resolved height of its own, so the percentage simply does not
     apply and the image is limited by width instead (measured: it stayed 770px
     tall however the viewport changed).

     So: subtract the chrome explicitly. 5rem covers the caption, count and gap
     with room to spare at every breakpoint, and `svh` keeps mobile browser
     toolbars out of the calculation. On a short screen the lightbox scrolls,
     which is safe. */
  max-height: min(74svh, calc(100svh - 5rem));
  width: auto;
  object-fit: contain;
  border: 1px solid var(--line-soft);
  background: var(--ink-800);
}

.lb__caption {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 1.5rem;
  width: 100%;
  max-width: 60rem;
  text-align: left;
}
.lb__caption h3 {
  grid-column: 1;
  font-size: clamp(1.15rem, 1.7vw, 1.5rem);
}
.lb__caption p {
  grid-column: 1;
  margin: 0.35rem 0 0;
  font-size: 0.84rem;
  line-height: 1.62;
  color: var(--fg-soft);
  max-width: 68ch;
}
.lb__count {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  color: var(--fg-mute);
  white-space: nowrap;
}

.lb__x {
  position: absolute;
  top: -0.4rem;
  right: 0;
  width: 2.6rem; height: 2.6rem;
  border: 0; background: none;
  font-size: 1.7rem;
  line-height: 1;
  color: var(--fg-soft);
  cursor: pointer;
  transition: color 0.3s var(--ease), transform 0.3s var(--ease);
}
.lb__x:hover { color: var(--gold-200); transform: rotate(90deg); }

.lb__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: 3rem; height: 3rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-soft);
  background: rgba(11, 10, 9, 0.6);
  color: var(--fg-soft);
  cursor: pointer;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.lb__nav:hover {
  color: var(--gold-200);
  border-color: var(--line);
  background: rgba(11, 10, 9, 0.9);
}
.lb__nav--prev { left: -3.9rem; }
.lb__nav--next { right: -3.9rem; }

/* ================================================================= modal */

.modal {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: grid;
  place-items: center;
  padding: 1.2rem;
}
.modal[hidden] { display: none; }

.modal__scrim {
  position: absolute;
  inset: 0;
  background: rgba(6, 6, 5, 0.9);
  backdrop-filter: blur(5px);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
}
.modal.is-open .modal__scrim { opacity: 1; }

.modal__box {
  position: relative;
  width: min(30rem, 100%);
  max-height: calc(100svh - 2.4rem);
  overflow-y: auto;
  padding: clamp(1.6rem, 4vw, 2.6rem);
  background: var(--ink-800);
  border: 1px solid var(--line);
  text-align: center;
  opacity: 0;
  transform: translateY(0.9rem);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.modal.is-open .modal__box { opacity: 1; transform: translateY(0); }

.modal__mark { width: 2.6rem; margin: 0 auto 1.1rem; opacity: 0.9; }
.modal__box h3 { margin-bottom: 0.7rem; }
.modal__lede { font-size: 0.86rem; color: var(--fg-soft); margin-inline: auto; }

.modal__x {
  position: absolute;
  top: 0.5rem; right: 0.6rem;
  width: 2.4rem; height: 2.4rem;
  border: 0; background: none;
  font-size: 1.5rem; line-height: 1;
  color: var(--fg-mute);
  cursor: pointer;
  transition: color 0.3s var(--ease);
}
.modal__x:hover { color: var(--gold-200); }

.modal__form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-top: 1.6rem;
  text-align: left;
}
.modal__form label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.modal__form input,
.modal__form textarea {
  font-family: var(--font-sans);
  font-size: 0.92rem;
  font-weight: 300;
  letter-spacing: 0.01em;
  color: var(--fg);
  background: var(--ink-900);
  border: 1px solid var(--line-soft);
  padding: 0.7rem 0.85rem;
  resize: vertical;
  transition: border-color 0.3s var(--ease);
}
.modal__form input:focus,
.modal__form textarea:focus {
  outline: none;
  border-color: var(--gold-400);
}
.modal__form input.is-bad,
.modal__form textarea.is-bad { border-color: var(--rose); }

.modal__status {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--gold-200);
  text-align: center;
  min-height: 1.2em;
}
.modal__status.is-bad { color: #ff9d9d; }
/* The mailto fallback link, styled to match the page form's. Without this the
   fallback rendered as unstyled body-copy blue on a near-black panel. */
.modal__status a {
  color: var(--gold-100);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  transition: border-color 0.3s var(--ease);
}
.modal__status a:hover { border-color: var(--gold-200); }

/* The submit button is disabled while the request is in flight. Without this it
   still looks pressable and the form appears to have frozen. */
.enquiry__submit[disabled],
.modal__form button[type=submit][disabled] {
  opacity: 0.55;
  cursor: progress;
}

/* ============================================================== reveals */

.reveal {
  opacity: 0;
  transform: translateY(1.6rem);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Gallery tiles use their own class because they are created after ui.js has
   collected the `.reveal` elements — see revealTiles() in gallery.js. */
.shot {
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.shot--hidden { opacity: 0; transform: translateY(1.6rem); }

/* =========================================================== responsive */

@media (max-width: 1080px) {
  .lb__nav--prev { left: 0.4rem; }
  .lb__nav--next { right: 0.4rem; }
  .lb__nav { background: rgba(11, 10, 9, 0.82); }
}

@media (max-width: 1180px) {
  .nav__links { column-gap: clamp(0.7rem, 1.3vw, 1.1rem); font-size: 0.66rem; }
}

@media (max-width: 900px) {
  .nav__links, .nav__cta { display: none; }
  .nav__burger { display: flex; }
  /* CREATIONS stays: it is half the brand, and the lockup is only ~74px wide,
     so there is room for it next to the burger on any phone. */

  /* Stack: the headline reads first, the collar follows at full width. Putting
     the photograph behind the copy at this size would mean covering the stones
     again, which is the thing this layout exists to avoid. */
  .hero {
    grid-template-columns: 1fr;
    gap: 2.4rem;
    min-height: auto;
    padding-top: clamp(6rem, 16vh, 8rem);
  }
  .hero::after { display: none; }
  .hero__copy { max-width: none; }
  .hero__meta { gap: 1.2rem 2rem; }

  /* One column: the tall/detail pairing only makes sense with the width to show
     two frames side by side, so below this the pair becomes individually stacked
     frames in their natural landscape crop.

     These selectors must name `.shot--right-a` / `.shot--right-b`. An earlier
     version targeted a `.shot--right` wrapper class that the builder no longer
     creates, so the rules silently did nothing. */
  .shot { grid-column: 1 / -1; }
  .info-grid { grid-template-columns: 1fr; grid-template-rows: none; }
  .info-grid > .shot--tall,
  .info-grid > .shot--right-a,
  .info-grid > .shot--right-b { grid-column: 1; grid-row: auto; }
  .shot--tall img { aspect-ratio: 3 / 2; }
  .info-grid > .shot--right-a .shot__frame,
  .info-grid > .shot--right-b .shot__frame { height: auto; }
  .info-grid > .shot--right-a img,
  .info-grid > .shot--right-b img { height: auto; aspect-ratio: 3 / 2; }

  .foot__top { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .lb__frame { gap: 0.8rem; }
  .lb__img { max-height: 58svh; }
  .lb__nav { width: 2.5rem; height: 2.5rem; }
  .hero__meta dd { max-width: none; }
}

/* ============================================== motion / contrast prefs */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .hero__media img { animation: none; }
  .statement__mark, .visit__mark { animation: none; }
}

@media (prefers-contrast: more) {
  :root {
    --fg-soft: #e4ded4;
    --fg-mute: #b6afa3;
    --line: rgba(201, 162, 39, 0.45);
    --line-soft: rgba(242, 237, 229, 0.26);
  }
}

/* ======================================================= print fallback */

@media print {
  .nav, .drawer, .lb, .modal { display: none !important; }
  body { background: #fff; color: #000; }
  .hero { min-height: auto; display: block; }
  .hero__media { margin-top: 2rem; }
  .hero::after { display: none; }
}
