/* ============================================================
   ZEYAA — stylesheet
   Palette and typography are derived from the ZEYÀ logo:
   blush circle, rosewood serif lettering, warm ivory ground.
   Owners: do not edit this file — edit site-content.js instead.
   ============================================================ */

/* ---------- Fonts (self-hosted variable fonts) ---------- */

@font-face {
  font-family: "Fraunces";
  src: url("../fonts/fraunces-var.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

/* The real italic cut, not a browser-slanted roman. Deliberately not
   preloaded: nothing above the fold is italic, so an eager fetch would only
   compete with the two hero-critical preloads in index.html. */
@font-face {
  font-family: "Fraunces";
  src: url("../fonts/fraunces-italic-var.woff2") format("woff2");
  font-weight: 300 700;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Manrope";
  src: url("../fonts/manrope-var.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */

:root {
  --ivory: #FBF7F4;
  --paper: #FFFDFB;
  --blush: #F5E3DA;      /* the logo's circle colour — now the page background */
  --band: #EAD3C6;       /* a shade deeper, for the alternate section bands */
  --blush-deep: #EFD8CC;
  --rosewood: #7A4038;
  --rosewood-deep: #5E2F29;
  --rosewood-faint: rgba(122, 64, 56, 0.28);
  --ink: #1F1715;
  --ink-soft: #6B5A53;
  --ivory-on-ink: #F3EAE4;
  --ivory-on-ink-soft: rgba(243, 234, 228, 0.66);
  --line: rgba(31, 23, 21, 0.14);
  /* 0.55, not 0.32: this is the border of .btn-ghost, which has no fill and
     no underline, so the border IS the whole "this is a button" signal. At
     0.32 it resolved to 1.98:1 against the blush — under the 3:1 that WCAG
     asks of a control's boundary, and a faint smudge on a phone outdoors. */
  --line-strong: rgba(31, 23, 21, 0.55);
  --line-on-ink: rgba(243, 234, 228, 0.18);

  --serif: "Fraunces", Georgia, "Times New Roman", serif;
  --sans: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  /* Slower and further out of the gate than --ease-out. Used only where
     something arrives on screen (scroll reveals, the hero), so the motion
     reads as settling rather than sliding. */
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);

  --container: 1200px;
  --gutter: clamp(20px, 5vw, 48px);
  --radius: 2px;
  --nav-h: 68px;

  /* Height of the announcement bar. Zero unless she has turned one on, in
     which case the publish job overrides this and main.js corrects it to the
     measured height once the text has wrapped. Everything that has to clear
     the fixed header adds it, so the offset is right either way. */
  --announce-h: 0px;
}

/* ---------- Reset & base ---------- */

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

/* The browser's own [hidden] rule is only `display: none`, so ANY class here
   that sets a display — .btn, .text-link, .lightbox — silently beats it and
   the element renders anyway. That has already shipped twice: an invisible
   lightbox that swallowed every click on the site, and a WhatsApp button
   that appeared with no number behind it. Marked !important so it cannot be
   lost to a more specific rule again. */
[hidden] {
  display: none !important;
}

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

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  font: 400 16.5px/1.7 var(--sans);
  color: var(--ink);
  background: var(--blush);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
}

ul,
ol {
  list-style: none;
}

::selection {
  background: var(--blush-deep);
  color: var(--rosewood-deep);
}

:focus-visible {
  outline: 2px solid var(--rosewood);
  outline-offset: 3px;
  border-radius: 1px;
}

section {
  scroll-margin-top: calc(var(--nav-h) + var(--announce-h) + 12px);
}

/* ---------- Primitives ---------- */

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(96px, 12vw, 176px);
}

/* Deeper ground for alternate sections. Used on the shell of any .section
   that should sit a shade darker than the page. */
.section.band {
  background: var(--band);
  border-block: 1px solid var(--line);
}

.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 200;
  padding: 12px 20px;
  background: var(--ink);
  color: var(--ivory-on-ink);
  font: 600 13px/1 var(--sans);
  letter-spacing: 0.08em;
  text-decoration: none;
  border-radius: var(--radius);
}

.skip-link:focus {
  top: 16px;
}

/* <main> is a programmatic focus target for the skip link only —
   no visible ring needed. */
main:focus {
  outline: none;
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  font: 700 12px/1 var(--sans);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--rosewood);
}

.diamond {
  width: 6px;
  height: 6px;
  flex: none;
  background: currentColor;
  transform: rotate(45deg);
}

.section-head {
  max-width: 920px;
  margin-bottom: clamp(48px, 7vw, 88px);
}

/* opsz 144 pins Fraunces to its display cut. Left to itself the optical-size
   axis tracks the rendered size, so the low end of every clamp() would soften
   into a text face and the headings would lose their contrast on a phone. */
.section-head h2 {
  font: 380 clamp(40px, 6vw, 84px)/1.05 var(--serif);
  font-variation-settings: "opsz" 144;
  letter-spacing: -0.018em;
  margin-top: 18px;
}

.section-head .lede {
  margin-top: 20px;
  font-size: clamp(17px, 1.5vw, 18.5px);
  line-height: 1.65;
  color: var(--ink-soft);
  max-width: 52ch;
}

/* Buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 50px;
  padding: 14px 28px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: 700 13px/1 var(--sans);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
  transition:
    background-color 150ms ease,
    border-color 150ms ease,
    color 150ms ease,
    transform 160ms var(--ease-out);
}

.btn:active {
  transform: scale(0.97);
}

.btn-primary {
  background: var(--rosewood);
  color: #FDF9F6;
}

.btn-ghost {
  border-color: var(--line-strong);
  color: var(--ink);
  background: transparent;
}

.btn-sm {
  min-height: 44px;
  padding: 11px 22px;
}

@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover {
    background: var(--rosewood-deep);
  }

  .btn-ghost:hover {
    border-color: var(--ink);
  }
}

/* A quiet alternative to .btn, for places where a second solid button would
   shout. The rule is drawn by ::after rather than text-decoration so it can
   be wiped in from the left. It is never used inside running prose — always
   as a standalone line — so colour and position carry the affordance while
   the rule is retracted, and :focus-visible draws it for keyboard users. */
.text-link {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  color: var(--rosewood-deep);
  font-weight: 600;
  text-decoration: none;
  padding-bottom: 3px;
  transition: color 150ms ease;
}

.text-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 320ms var(--ease-out);
}

.text-link .arrow {
  display: inline-block;
  transition: transform 220ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .text-link:hover {
    color: var(--rosewood);
  }

  .text-link:hover::after {
    transform: scaleX(1);
  }

  .text-link:hover .arrow {
    transform: translateX(4px);
  }
}

.text-link:focus-visible::after {
  transform: scaleX(1);
}

/* Scroll reveal — only when JS is running (html.js) */

@media (prefers-reduced-motion: no-preference) {
  html.js [data-reveal] {
    opacity: 0;
    transform: translateY(18px);
    transition:
      opacity 860ms var(--ease-reveal),
      transform 860ms var(--ease-reveal);
    transition-delay: calc(var(--d, 0) * 110ms);
  }

  html.js [data-reveal].is-visible {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Announcement bar ----------
   Optional, and off by default. Sits above the fixed header as an ink bar,
   bookending the ink footer. It is allowed to wrap: main.js measures it and
   feeds the height back into --announce-h, so two lines push the header down
   rather than hiding behind it. */

.announce {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 101;
  background: var(--ink);
  color: var(--ivory-on-ink);
  text-align: center;
}

.announce p {
  padding-block: 11px;
  font: 500 13px/1.45 var(--sans);
  letter-spacing: 0.04em;
}

@media (max-width: 600px) {
  .announce p {
    padding-block: 9px;
    font-size: 12px;
  }
}

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

.site-header {
  position: fixed;
  inset: var(--announce-h) 0 auto 0;
  z-index: 100;
  height: var(--nav-h);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition:
    background-color 250ms ease,
    border-color 250ms ease;
}

.site-header.is-scrolled,
.site-header.menu-open {
  background: var(--blush);
  border-bottom-color: var(--line);
}

.nav-bar {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  min-height: 44px;
}

.brand-logo {
  width: 44px;
  height: 44px;
  border-radius: 50%;      /* clips the logo's square black background */
  object-fit: cover;
  border: 1px solid var(--line);
}

/* Set beside the mark, not instead of it: the circle is the logo, this is
   the name. Sized to sit on the mark's optical centre at a 68px nav. */
.brand-word {
  font: 500 15px/1 var(--serif);
  letter-spacing: 0.28em;
  text-indent: 0.28em;     /* optically recenters tracked text */
  color: var(--rosewood);
  text-transform: uppercase;
}

.brand.logo-missing .brand-logo {
  display: none;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 36px);
}

.nav-links a:not(.btn) {
  font: 600 13.5px/1 var(--sans);
  letter-spacing: 0.01em;
  text-decoration: none;
  color: var(--ink);
  padding: 14px 2px;
  border-bottom: 1px solid transparent;
  transition: color 150ms ease, border-color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .nav-links a:not(.btn):hover {
    color: var(--rosewood);
    border-bottom-color: var(--rosewood-faint);
  }
}

/* Hamburger */

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  width: 48px;
  height: 48px;
  padding: 12px;
  background: none;
  border: 0;
  cursor: pointer;
  touch-action: manipulation;
}

.nav-toggle span {
  display: block;
  width: 100%;
  height: 1.5px;
  background: var(--ink);
  transition: transform 250ms var(--ease-out), opacity 200ms ease;
}

.menu-open .nav-toggle span:nth-child(1) {
  transform: translateY(8.5px) rotate(45deg);
}

.menu-open .nav-toggle span:nth-child(2) {
  transform: translateY(-8.5px) rotate(-45deg);
}

/* Mobile menu overlay — editorial layout:
   nav links · featured photo · "Message on Instagram" · contact line. */

.mobile-menu {
  position: fixed;
  inset: calc(var(--nav-h) + var(--announce-h)) 0 0 0;
  z-index: 90;
  background: var(--blush);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: clamp(18px, 5vw, 34px) var(--gutter) calc(28px + env(safe-area-inset-bottom));
  visibility: hidden;
  opacity: 0;
  transition: opacity 250ms ease, visibility 0s linear 250ms;
}

.mobile-menu.is-open {
  visibility: visible;
  opacity: 1;
  transition: opacity 250ms ease;
}

.menu-inner {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 4.5vw, 28px);
}

.menu-links {
  display: flex;
  flex-direction: column;
}

.menu-links li {
  border-bottom: 1px solid var(--line);
}

.menu-links a {
  display: block;
  padding: clamp(13px, 3vw, 17px) 0;
  font: 380 clamp(29px, 8.5vw, 44px)/1.1 var(--serif);
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .menu-links a:hover {
    color: var(--rosewood);
  }
}

.menu-feature {
  margin: 0;
}

.menu-feature .media {
  height: clamp(230px, 40vh, 400px);
}

.menu-cta {
  padding-bottom: clamp(16px, 4vw, 22px);
  border-bottom: 1px solid var(--line-strong);
  text-align: center;
}

.menu-order {
  display: inline-block;
  padding: 6px 4px;
  font: 400 clamp(21px, 6vw, 27px)/1.2 var(--serif);
  color: var(--ink);
  text-decoration: none;
  transition: color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .menu-order:hover {
    color: var(--rosewood);
  }
}

.menu-contact {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 14px;
  margin-top: auto;
  padding-top: 6px;
  font: 700 12px/1.6 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: center;
}

.menu-contact a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rosewood-faint);
  padding-bottom: 2px;
  transition: border-color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .menu-contact a:hover {
    border-bottom-color: var(--rosewood);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .mobile-menu .menu-links li,
  .mobile-menu .menu-feature,
  .mobile-menu .menu-cta,
  .mobile-menu .menu-contact {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out);
  }

  .mobile-menu.is-open .menu-links li,
  .mobile-menu.is-open .menu-feature,
  .mobile-menu.is-open .menu-cta,
  .mobile-menu.is-open .menu-contact {
    opacity: 1;
    transform: none;
    transition-delay: calc(var(--d, 0) * 45ms + 60ms);
  }
}

body.menu-locked {
  overflow: hidden;
}

@media (max-width: 899px) {
  .nav-links {
    display: none;
  }

  .nav-toggle {
    display: flex;
  }
}

@media (min-width: 900px) {
  .mobile-menu {
    display: none;
  }
}

/* ---------- Media frames & photo placeholders ---------- */

/* Deliberately borderless. A hairline around every photo turns the page into
   a contact sheet of framed thumbnails; the photograph should meet the page
   directly. The only framed state left is .img-missing, which is a stand-in
   and *should* read as an empty slot. */
.media {
  position: relative;
  overflow: hidden;
  background: var(--paper);
}

/* Absolute, so a card can stack a second photo on top of the cover for the
   hover crossfade. With .media positioned and every use giving it a definite
   height (aspect-ratio or explicit), this is identical to width/height 100%
   everywhere only one image is present. */
.media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ratio-45 {
  aspect-ratio: 4 / 5;
}

.ratio-23 {
  aspect-ratio: 2 / 3;
}

.ratio-11 {
  aspect-ratio: 1 / 1;
}

/* Styled stand-in shown until a real photo exists at the path.
   `data-ph` on the .media element supplies the label text. */

.media.img-missing {
  background: linear-gradient(165deg, var(--paper) 0%, var(--blush-deep) 100%);
}

.media.img-missing img {
  display: none;
}

.media.img-missing::before {
  content: "";
  position: absolute;
  inset: 10px;
  border: 1px solid var(--rosewood-faint);
  pointer-events: none;
}

.media.img-missing::after {
  content: attr(data-ph);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px;
  font: 700 11px/1.9 var(--sans);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--rosewood-deep);
}

/* ---------- Hero ---------- */

.hero {
  min-height: 92svh;
  display: flex;
  align-items: center;
  padding-top: calc(var(--nav-h) + var(--announce-h) + clamp(28px, 5vw, 64px));
  padding-bottom: clamp(64px, 8vw, 112px);
}

/* The headline runs the full width of the container and the photograph is
   placed beneath its right half, rising to meet it. Two columns, four rows:
   the type stack owns column 1, the photo owns column 2 from the lede down. */
.hero-grid {
  display: grid;
  /* The right column is narrow on purpose: it lets the photograph keep its
     full 2:3 frame — nothing cropped off the model — while still standing
     tall enough to reach up alongside the headline. */
  grid-template-columns: 1.3fr 0.7fr;
  column-gap: clamp(32px, 4vw, 72px);
  align-items: start;
  flex: 1;                 /* .hero is a flex row; without this the grid
                              shrinks to its content and never reaches the
                              right edge, so the photo can't bleed */
}

.hero .eyebrow,
.hero h1 {
  grid-column: 1 / -1;
}

/* The photo spans both of these rows, so its height is shared between them.
   Sending the lede down and the buttons up closes the gap that would
   otherwise open between the two, and sets the pair against the middle of
   the photograph instead of stranding them at the top and bottom. */
.hero .lede {
  grid-column: 1;
  align-self: end;
}

.hero-ctas {
  grid-column: 1;
  align-self: start;
}

.hero h1 {
  position: relative;
  z-index: 1;              /* the descenders sit over the top of the photo */
  font: 380 clamp(52px, 8.5vw, 124px)/1.02 var(--serif);
  font-variation-settings: "opsz" 144;
  letter-spacing: -0.02em;
  margin-top: 26px;
  max-width: 15ch;
}

.hero .lede {
  margin-top: 30px;
  font-size: clamp(17px, 1.6vw, 19px);
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 44ch;
}

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px clamp(20px, 3vw, 34px);
  margin-top: 40px;
}

.hero-media {
  grid-column: 2;
  grid-row: 3 / span 2;
  /* Pulls the photo up under the headline so the two interlock rather than
     sitting in tidy parallel columns. */
  margin-top: clamp(-72px, -5vw, -24px);
}

@media (min-width: 900px) {
  /* Let the photograph run off the right edge of the viewport. */
  .hero .container {
    max-width: none;
    padding-inline-end: 0;
    margin-inline: 0;
    padding-inline-start: max(var(--gutter), calc((100vw - var(--container)) / 2 + var(--gutter)));
  }
}

@media (prefers-reduced-motion: no-preference) {
  html.js .hero [data-hero] {
    opacity: 0;
    transform: translateY(14px);
    animation: hero-in 860ms var(--ease-reveal) forwards;
    animation-delay: calc(var(--d, 0) * 110ms + 80ms);
  }

  @keyframes hero-in {
    to {
      opacity: 1;
      transform: none;
    }
  }
}

@media (max-width: 899px) {
  .hero {
    min-height: 0;
  }

  .hero-grid {
    grid-template-columns: 1fr;
  }

  .hero .eyebrow,
  .hero h1,
  .hero .lede,
  .hero-ctas,
  .hero-media {
    grid-column: 1;
  }

  .hero-media {
    grid-row: auto;
    margin-top: clamp(36px, 8vw, 56px);
  }

  .hero h1 {
    max-width: none;
  }
}

/* ---------- Brand promise strip ---------- */

.promise {
  background: var(--band);
  border-block: 1px solid var(--line);
  padding: 26px 0;
}

/* No separator glyphs between the three claims — the gap does that work.
   It has to be wide enough to read as separation on its own. */
.promise ul {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: clamp(32px, 6vw, 88px);
  row-gap: 14px;
}

.promise li {
  font: 700 11.5px/1.6 var(--sans);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--rosewood-deep);
  text-align: center;
}

@media (max-width: 639px) {
  .promise ul {
    flex-direction: column;
  }
}

/* ---------- Collection ---------- */

/* Twelve columns, in a five-piece cycle: two large (7 + 5, the second
   dropped) then three at 4 each. Ten pieces is exactly two cycles, and the
   cycle repeats cleanly at any count, so she can add an eleventh without the
   layout falling apart. The point is that no two rows look alike. */
.product-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(56px, 6vw, 88px) clamp(28px, 3vw, 48px);
}

.card {
  grid-column: span 4;
}

.card:nth-child(5n+1) {
  grid-column: span 7;
}

.card:nth-child(5n+2) {
  grid-column: span 5;
  margin-top: clamp(40px, 5vw, 72px);
}

.card:nth-child(5n+1) h3,
.card:nth-child(5n+2) h3 {
  font-size: clamp(26px, 2.2vw, 32px);
}

/* The rhythm is a desktop luxury. Narrower than this and it just becomes
   uneven column widths, so drop to a plain stack. */
@media (max-width: 1023px) {
  .product-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .card,
  .card:nth-child(5n+1),
  .card:nth-child(5n+2) {
    grid-column: auto;
    margin-top: 0;
  }

  .card:nth-child(5n+1) h3,
  .card:nth-child(5n+2) h3 {
    font-size: clamp(21px, 1.7vw, 25px);
  }
}

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

.card {
  display: flex;
  flex-direction: column;
}

.card img {
  transform: scale(1.001);
  transition: transform 600ms var(--ease-out);
}

/* The second photo, stacked over the cover and faded in on hover. It
   replaces the row of thumbnails that used to sit under every card: the
   full set is one tap away in the lightbox either way, and a phone (which
   never hovers) now downloads one photo per card instead of seven. */
.card .media-hover {
  opacity: 0;
  transition: opacity 450ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .card:hover img {
    transform: scale(1.03);
  }

  .card:hover .media-hover {
    opacity: 1;
  }
}

/* Says there are more photos without drawing a row of boxes to prove it.
   The element is its own scrim — it spans the foot of the photo and fades
   out upwards — because these photos run from near-black to pale pink and
   plain text sat on some of them invisibly.
   aria-hidden: the lightbox announces its own count to screen readers. */
.card-count {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 40px 16px 14px;
  font: 700 10px/1 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-on-ink);
  background: linear-gradient(to top, rgba(20, 15, 14, 0.5), rgba(20, 15, 14, 0));
  pointer-events: none;
}

/* ---------- Sold pieces ----------
   A one-of-a-kind piece that has sold stays on the site: it still shows the
   range of the work, and it saves her deleting photos she may want back. */

.sold-flag {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 2;
  padding: 6px 12px;
  font: 700 10px/1 var(--sans);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--rosewood-deep);
  background: var(--paper);
  border: 1px solid var(--line-strong);
}

.card.is-sold .price {
  color: var(--ink-soft);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.card.is-sold .price .usd {
  text-decoration: inherit;
}

/* Name and price on one baseline, joined by a rule that takes up whatever
   space is left — a caption line, not a product tile. */
.card-heading {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: clamp(20px, 2.4vw, 28px);
}

.card h3 {
  flex: 0 1 auto;
  font: 440 clamp(21px, 1.7vw, 25px)/1.2 var(--serif);
  letter-spacing: -0.005em;
}

.card-rule {
  flex: 1 1 auto;
  min-width: 24px;
  align-self: center;
  border-bottom: 1px solid var(--line);
}

.card .price {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

.card .price .usd {
  color: var(--ink-soft);
  font-weight: 500;
}

.card .desc {
  margin-top: 10px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink-soft);
  max-width: 46ch;
  flex: 1;
}

.card-cta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: clamp(16px, 2vw, 22px);
}

.card .dm {
  font-size: 13.5px;
  color: var(--ink-soft);
}

.content-error {
  padding: 28px;
  border: 1px solid var(--line-strong);
  background: var(--paper);
  font-size: 15px;
  max-width: 560px;
}

/* Sitewide alert injected when site-content.js fails to parse —
   the owner must see it wherever they're looking, not just in
   the Collection section. */
.content-error-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 300;
  padding: 12px 20px max(12px, env(safe-area-inset-bottom));
  background: var(--rosewood-deep);
  color: #FDF9F6;
  font: 600 13px/1.5 var(--sans);
  text-align: center;
}

.noscript-note {
  padding: 28px;
  border: 1px solid var(--line);
  background: var(--paper);
  max-width: 560px;
}

/* ---------- Craft ---------- */

.craft {
  background: var(--band);
  border-block: 1px solid var(--line);
}

.craft-grid {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: start;
}

/* On the deeper craft band, lift the placeholder toward paper/blush
   so the frame still reads against the surrounding tone. */
.craft .media.img-missing {
  background: linear-gradient(165deg, var(--paper) 0%, var(--blush) 100%);
}

.craft-copy p + p {
  margin-top: 1.2em;
}

.craft-copy p {
  color: #4E3B33;
  max-width: 58ch;
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 4vw, 48px);
  margin-top: clamp(48px, 6vw, 80px);
}

.stat {
  border-top: 1px solid var(--rosewood-faint);
  padding-top: clamp(20px, 3vw, 32px);
}

.stat .num {
  display: block;
  font: 380 clamp(56px, 7vw, 96px)/1 var(--serif);
  font-variation-settings: "opsz" 144;
  color: var(--rosewood);
  letter-spacing: -0.02em;
}

.stat p {
  margin-top: 12px;
  font: 600 12px/1.7 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #4E3B33;
  max-width: 24ch;
}

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

  .stats {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}

/* ---------- Detail close-ups ---------- */

/* Unequal columns and three different crops, so the row reads as a spread
   rather than three matching tiles. The published files are all square; the
   aspect ratios below re-crop them in the browser, so nothing is re-rendered
   and nothing is upscaled. Written as 3n+… so the pattern restarts cleanly
   if she adds a fourth close-up. */
.details-grid {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr 1fr;
  gap: clamp(20px, 3vw, 40px);
  align-items: start;
}

.details-grid figure:nth-child(3n+1) .media.ratio-11 {
  aspect-ratio: 4 / 5;
}

.details-grid figure:nth-child(3n+3) .media.ratio-11 {
  aspect-ratio: 3 / 4;
}

.details-grid figure:nth-child(3n+2) {
  transform: translateY(clamp(28px, 4vw, 48px));
}

.details-grid figcaption {
  display: block;
  margin-top: 16px;
  font: italic 400 15px/1.5 var(--serif);
  color: var(--ink-soft);
}

@media (max-width: 767px) {
  .details-grid {
    display: flex;
    gap: 16px;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-bottom: 8px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
  }

  .details-grid figure {
    flex: 0 0 82%;
    scroll-snap-align: start;
  }

  .details-grid figure:nth-child(3n+2) {
    transform: none;
  }
}

/* ---------- How to order ---------- */

.steps {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(32px, 4vw, 56px);
  counter-reset: step;
}

.step {
  position: relative;
}

/* A large, pale numeral carries the sequence on its own — there used to be a
   hairline rule threaded behind these, with each number painting a patch of
   background over it to punch a hole. At this size the rule is redundant. */
.step .num {
  display: block;
  position: relative;
  font: 380 clamp(72px, 8vw, 120px)/0.85 var(--serif);
  font-variation-settings: "opsz" 144;
  letter-spacing: -0.02em;
  color: var(--rosewood-faint);
  margin-bottom: clamp(-10px, -0.8vw, -4px);
}

.step h3 {
  position: relative;
  font: 460 21px/1.3 var(--serif);
  margin-top: 0;
}

.step p {
  margin-top: 10px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 34ch;
}

.step .btn,
.step .text-link {
  margin-top: 20px;
}

@media (max-width: 767px) {
  .steps {
    grid-template-columns: 1fr;
    gap: 44px;
  }
}

/* ---------- FAQ ---------- */

.faq-list {
  max-width: 880px;
}

.faq-list details {
  border-top: 1px solid var(--line);
}

.faq-list details:last-child {
  border-bottom: 1px solid var(--line);
}

.faq-list summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 26px 4px;
  cursor: pointer;
  font: 420 clamp(19px, 2vw, 22px)/1.4 var(--serif);
  list-style: none;
  transition: color 150ms ease;
}

.faq-list summary::-webkit-details-marker {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .faq-list summary:hover {
    color: var(--rosewood-deep);
  }
}

.faq-icon {
  position: relative;
  flex: none;
  width: 14px;
  height: 14px;
  transition: transform 250ms var(--ease-out);
}

.faq-icon::before,
.faq-icon::after {
  content: "";
  position: absolute;
  background: var(--rosewood);
}

.faq-icon::before {
  left: 0;
  top: 50%;
  width: 100%;
  height: 1.5px;
  transform: translateY(-50%);
}

.faq-icon::after {
  top: 0;
  left: 50%;
  width: 1.5px;
  height: 100%;
  transform: translateX(-50%);
}

.faq-list details[open] .faq-icon {
  transform: rotate(45deg);
}

.faq-body {
  padding: 0 4px 26px;
  color: var(--ink-soft);
  line-height: 1.75;
  max-width: 60ch;
}

/* An answer is one <p> per paragraph, so leaving a blank line in the panel
   actually produces a paragraph break. */
.faq-body p + p {
  margin-top: 0.85em;
}

/* ---------- Testimonials ----------
   The whole section is omitted until she adds a quote, so these rules simply
   don't apply on a site that has none yet. */

.quotes {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(28px, 3.5vw, 48px);
}

.quotes li {
  border-top: 1px solid var(--line);
  padding-top: 26px;
}

.quotes blockquote p {
  font: italic 300 clamp(19px, 2vw, 23px)/1.55 var(--serif);
  color: var(--ink);
}

.quotes blockquote p + p {
  margin-top: 0.7em;
}

/* Attribution in italic serif rather than another tracked uppercase label —
   the page already has plenty of those. */
.quotes cite {
  display: block;
  margin-top: 18px;
  font: italic 400 14px/1.6 var(--serif);
  color: var(--ink-soft);
}

/* ---------- Closing CTA + footer (on ink) ---------- */

.closing {
  background: var(--ink);
  color: var(--ivory-on-ink);
}

.closing-inner {
  padding-block: clamp(96px, 13vw, 168px) 0;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.closing .eyebrow {
  color: #CE9484;
  justify-content: center;
}

.closing h2 {
  font: 380 clamp(48px, 7.5vw, 112px)/1.03 var(--serif);
  font-variation-settings: "opsz" 144;
  letter-spacing: -0.02em;
  margin-top: 20px;
}

.closing .lede {
  margin-top: 18px;
  color: var(--ivory-on-ink-soft);
  max-width: 46ch;
}

.closing-ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px clamp(24px, 3vw, 40px);
  margin-top: 44px;
}

.closing .btn-primary {
  background: var(--ivory);
  color: var(--ink);
}

.closing .text-link {
  color: var(--ivory-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .closing .text-link:hover {
    color: #FFFFFF;
  }
}

.closing .btn-ghost {
  border-color: rgba(243, 234, 228, 0.38);
  color: var(--ivory-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .closing .btn-primary:hover {
    background: var(--blush);
  }

  .closing .btn-ghost:hover {
    border-color: var(--ivory-on-ink);
  }
}

.closing :focus-visible {
  outline-color: var(--ivory-on-ink);
}

.closing-contact {
  margin-top: 32px;
  font: 600 13px/2 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ivory-on-ink-soft);
}

.closing-contact a {
  color: var(--ivory-on-ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(243, 234, 228, 0.35);
  padding-bottom: 2px;
  transition: border-color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .closing-contact a:hover {
    border-bottom-color: var(--ivory-on-ink);
  }
}

.site-footer {
  width: 100%;
  margin-top: clamp(64px, 9vw, 110px);
  border-top: 1px solid var(--line-on-ink);
  padding: 30px 0 max(30px, env(safe-area-inset-bottom));
}

.footer-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px 32px;
  font-size: 13px;
  color: var(--ivory-on-ink-soft);
}

.footer-brand {
  font: 500 15px/1 var(--serif);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ivory-on-ink);
  text-decoration: none;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
}

.footer-nav a {
  text-decoration: none;
  padding: 8px 0;
  transition: color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .footer-nav a:hover {
    color: var(--ivory-on-ink);
  }
}

@media (max-width: 639px) {
  .footer-row {
    flex-direction: column;
    text-align: center;
  }
}

/* ---------- Lightbox (click a photo to view it larger) ---------- */

.media.zoomable {
  cursor: zoom-in;
}

.media.zoomable:focus-visible {
  outline: 2px solid var(--rosewood);
  outline-offset: 3px;
}

body.lb-open {
  overflow: hidden;
}

/* Until JS opens it, the overlay must be fully removed from the page —
   not just transparent. Without this, `display:flex` overrides the
   browser's [hidden]{display:none}, leaving an invisible full-screen
   layer that silently swallows every click (menu + buttons included). */
.lightbox[hidden] {
  display: none;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 56px);
  background: rgba(20, 15, 14, 0.9);
  opacity: 0;
  transition: opacity 260ms var(--ease-out);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.lightbox.is-open {
  opacity: 1;
}

.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transform: scale(0.98);
  transition: transform 260ms var(--ease-out);
}

.lightbox.is-open .lightbox-img {
  transform: scale(1);
}

.lightbox-close {
  position: absolute;
  top: clamp(10px, 2.5vw, 22px);
  right: clamp(10px, 2.5vw, 22px);
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: 300 30px/1 var(--sans);
  color: var(--ivory-on-ink);
  background: rgba(31, 23, 21, 0.55);
  border: 1px solid var(--line-on-ink);
  border-radius: 50%;
  cursor: pointer;
  transition: background 200ms ease, transform 200ms ease;
}

.lightbox-close:hover {
  background: rgba(31, 23, 21, 0.85);
  transform: scale(1.05);
}

.lightbox-close:focus-visible {
  outline: 2px solid var(--ivory-on-ink);
  outline-offset: 2px;
}

/* Prev / next arrows + counter — shown only for multi-photo galleries. */
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 4px;
  font: 300 34px/1 var(--sans);
  color: var(--ivory-on-ink);
  background: rgba(31, 23, 21, 0.55);
  border: 1px solid var(--line-on-ink);
  border-radius: 50%;
  cursor: pointer;
  transition: background 200ms ease, opacity 200ms ease;
}

.lightbox-prev {
  left: clamp(10px, 2.5vw, 22px);
}

.lightbox-next {
  right: clamp(10px, 2.5vw, 22px);
}

.lightbox-nav:hover {
  background: rgba(31, 23, 21, 0.85);
}

.lightbox-nav:focus-visible {
  outline: 2px solid var(--ivory-on-ink);
  outline-offset: 2px;
}

.lightbox-nav:disabled {
  opacity: 0.32;
  cursor: default;
}

.lightbox-counter {
  position: absolute;
  bottom: clamp(10px, 2.5vw, 22px);
  left: 50%;
  transform: translateX(-50%);
  padding: 5px 13px;
  font: 600 12px/1 var(--sans);
  letter-spacing: 0.12em;
  color: var(--ivory-on-ink);
  background: rgba(31, 23, 21, 0.55);
  border: 1px solid var(--line-on-ink);
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* ---------- Reduced motion: kill everything that moves ---------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
