/* Jewelry index cards and design leaf pages.
   Hand-editable: tools/build_jewelry.py writes the HTML, never this file.

   Product photographs are cut out with a transparent background and sit on
   BLACK. That is the whole reason the alpha was recovered -- on cream the metal
   and the page fight each other, and the abalone goes dull. */

/* ---------- the beach ground (shared by the index cards and the leaf hero) ----
   Lorayne picked background_4 on 2026-08-13. Jerry picked shadow treatment D --
   "cast + contact" -- and tuned it on 2026-08-16. Both decisions were made in
   mockups/background-test.html, which is the record; these values are a
   TRANSCRIPTION of its :root block, because that page is a hand-edited study and
   exports nothing. Retune it and these must be repasted. The same six values are
   also transcribed into tools/gen_design_review.GROUND_VARS for the review
   pages -- three copies now, so change all three or say which one is right.

   background_4.jpg is copied into img/ by tools/build_jewelry.py on every build.
   Do not hand-place it: a CSS background is invisible to tools/check_refs, which
   only scans src= attributes in HTML, so a missing ground would ship silently.

   ONLY put `beach` on a container holding a TRANSPARENT cut-out. The shadow is
   the piece's own silhouette flipped and projected; behind an opaque photograph
   it projects a rectangle. The worn hero therefore takes `beach` for the ground
   but emits no .shadow element. */
:root {
  --scale: 68;   /* piece height, % of stage     */
  --base:  21;   /* piece bottom, % up from base */
  --cast:  30;   /* shadow length, % of piece    */
  --lean: -29;   /* shadow lean, degrees         */
  --soft:  10;   /* shadow blur, px              */
  --dark:  25;   /* shadow opacity, %            */
}
.beach {
  background: url('../img/background_4.jpg') center / cover no-repeat;
}
/* Bottom-anchored, not centred: a cast shadow has to start where the piece meets
   the ground. Two classes deep so these outrank the generic `.design-card img`
   and `.design-hero .stage img` inset rules further down -- and `top: auto` is
   stated outright, because those rules set `top` and leaving it would fight
   `bottom` and stretch the piece. */
.beach .piece,
.beach .shadow {
  position: absolute;
  top: auto;
  left: 50%;
  bottom: calc(var(--base) * 1%);
  height: calc(var(--scale) * 1%);
  width: auto;
  object-fit: contain;
  display: block;
}
.beach .piece {
  transform: translateX(-50%);
  filter: drop-shadow(0 4px 4px rgba(40, 30, 18, .40));
}
.beach .shadow {
  transform-origin: bottom center;
  transform: translateX(-50%) scaleY(calc(var(--cast) / -100))
             skewX(calc(var(--lean) * 1deg));
  filter: brightness(0) blur(calc(var(--soft) * 1px));
  opacity: calc(var(--dark) / 100);
  pointer-events: none;
  /* after the flip the image's TOP is the far end -- fade it out with distance */
  -webkit-mask-image: linear-gradient(to bottom, transparent 4%, #000 72%);
          mask-image: linear-gradient(to bottom, transparent 4%, #000 72%);
}

/* ---------- index grid ---------- */
/* Jerry's call 2026-08-10: the index cards were too big. Smaller track plus a
   cap on the card itself -- the track alone would just make the last row's cards
   stretch to fill, which is how a "smaller" grid ends up looking identical. */
.design-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
  margin: 26px 0 44px;
  justify-content: start;
}
.design-card {
  display: block;
  max-width: 200px;
  text-decoration: none;
  color: inherit;
}
/* Ground was #0d0d0f until 2026-08-16; it is now the beach, set by `.beach`
   above. Kept as the fallback colour for the one case that emits no image. */
.design-card .shot {
  position: relative;
  aspect-ratio: 1;
  background-color: #0d0d0f;
  border-radius: 10px;
  overflow: hidden;
  transition: transform .18s ease;
}
.design-card:hover .shot { transform: translateY(-3px); }
/* Whole piece, scaled down -- see .options-row .ex-shot img for the reasoning.
   The index cards run tall cut-outs (Key, Winged Heart, Seahorse) that the old
   percentage sizing clipped at top and bottom. */
.design-card img {
  position: absolute;
  top: 7%;
  left: 7%;
  width: 86%;
  height: 86%;
  object-fit: contain;
  display: block;
}
.design-card h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin: 10px 0 2px;
}
.design-card .virtue {
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--brass);
}

/* ============================================================
   THE LEAF PAGE IS FIVE STACKED ROWS
   ============================================================
   1  nav
   2  hero            2 boxes: [name, virtue, lede] | [model image]
   3  images row      N from the catalog, capped at 3
   4  design options  2 boxes, 3 sub-boxes each, rule between
   5  choose your piece

   A grid, not a run of block elements. Each section owns one track, so no
   section can occupy another's space -- the hero overflowing onto the images
   row is not something to be tuned, it is something this makes impossible.
   Row heights are `auto`: every row is as tall as its own content. */
.leaf {
  display: grid;
  grid-template-rows: repeat(5, auto);
  align-content: start;
}
/* Spacing belongs to the container, so the rows cannot fight each other with
   competing margins -- and margin collapsing does not apply inside a grid. */
.leaf > * { margin-top: 0; margin-bottom: 0; min-width: 0; }
/* Tightened 2.25rem -> 1.1rem (Jerry 2026-08-17). The rows are already distinct
   -- the hero has a photograph, the option boxes have borders -- so the gap was
   doing separation work that the content does on its own, and it pushed Choose
   Your Piece below the fold on a laptop. The hero keeps a larger gap beneath it
   because it is the one row that reads as a header rather than a section. */
.leaf > * + * { margin-top: 1.1rem; }
.design-hero + * { margin-top: 1.6rem; }

/* ---------- leaf: prev / all / next ---------- */
/* `.leafnav` / `.pn` MOVED TO components.css on 2026-08-18. The banner detail
   pages want the same prev/next row, and item.html does not load this
   stylesheet -- duplicating the rules there would have been two copies to keep
   in step. Nothing else in this file references them. */

/* ---------- leaf: hero ---------- */
/* Jerry 2026-08-13, THIRD pass, and the previous two were the wrong idea rather
   than the wrong value. Instruction: "whatever you do on the home page to make
   the text and model image work should be replicated on the Design pages."

   The two heroes were built inside out from each other. The LANDING hero puts
   the height on the GRID (.lp-hero has min-height/max-height), makes the photo
   cell position:relative, and absolutely positions the photo at inset:0 with
   object-fit:contain -- so the row's height is fixed first and the photograph is
   fitted into whatever that leaves. The LEAF hero had no height on the grid at
   all and sized itself outward from the image, which is why capping the image
   twice changed nothing: there was never a box telling the row how tall to be.

   This block is now the landing hero's structure, member for member. Compare
   against .lp-hero / .lp-hero-visual / .hero-photo in components.css; if one
   changes, change the other. */
.design-hero {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: 34px;
  align-items: stretch;
  min-height: 300px;
}
/* The copy column centres itself in the row, as .lp-hero-copy does. Targeted by
   position because the generator emits a bare <div> here, not a class. */
.design-hero > :first-child {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
/* background-COLOR, not the `background` shorthand. The shorthand resets
   background-image to none, and at two classes deep it outranks `.beach` -- so
   writing it that way silently wiped the ground off every leaf hero while
   leaving the rule looking correct. Same reason on .stage--worn below. */
/* The HEIGHT CAP BELONGS HERE, on the image box -- not on .design-hero.
   It was on the row, which has no `overflow`, so a copy column taller than
   --hero-model-h (a long lede on a narrow window) overflowed the row's box and
   painted straight over the images row beneath it. A max-height caps the box,
   it does not clip the content. The five sections are independent block rows and
   each must size to its own content; only the picture needs bounding. */
.design-hero .stage {
  position: relative;
  background-color: #0d0d0f;
  border-radius: 12px;
  overflow: hidden;
  /* A DEFINITE height, not max-height, and NO align-self here.
     The worn stage's only child is position:absolute inset:0, so the box has no
     intrinsic height of its own -- it was being held open by the grid's
     align-items:stretch. `align-self: center` cancels that stretch, the box
     collapses to zero, and the model image disappears entirely. That is the
     same failure recorded against this file on 2026-08-11, repeated on
     2026-08-17. Anything that stops this box being stretched must replace the
     height, not merely cap it. */
  height: var(--hero-model-h);
}
/* The square belongs to the FALLBACK hero only -- if a design has no Model
   no worn photograph. Scoped off the worn shot so it cannot force a square on a
   portrait, and centred in the row rather than stretched, since a stretched
   aspect-ratio:1 box would grow its width to match the row height. */
.design-hero .stage:not(.stage--worn) {
  aspect-ratio: 1;
  height: auto;
  max-width: var(--hero-model-h);
  margin-inline: auto;
  align-self: center;
}
/* Whole piece, scaled down -- see .options-row .ex-shot img. This is the
   fallback hero for the designs with no worn shot, so a clip here would cut the
   one photograph those pages open on.
   :not(.stage--worn) is LOAD-BEARING, not tidiness. A worn shot sets
   aspect-ratio:auto and is sized by its own rule below, which declares no
   `position` -- so an unscoped `position:absolute` here still reached it, took
   its only child out of flow, collapsed the container to zero height and made
   every worn hero disappear off the top of the page. Broke exactly that way on
   2026-08-11; caught by Jerry, not by the build. */
.design-hero .stage:not(.stage--worn) img {
  position: absolute;
  top: 6%;
  left: 6%;
  width: 88%;
  height: 88%;
  object-fit: contain;
  display: block;
}
/* A worn (aka Model) shot is a photograph, not a cut-out, so it fills the tile 
   instead of floating inside it with black margins. Anchored high: these are 
   portraits and centring them crops the face. */
/* NEVER crop a worn shot (Jerry 2026-08-10). This was `object-fit: cover`, which
   filled the square tile by slicing the top and bottom off a portrait -- losing
   the face, and sometimes the piece itself. `contain` shows the whole frame; the
   tile gives up its square so there is no letterboxing to look at. */
/* The worn shot fills its column and is fitted inside it -- exactly what
   .lp-hero-visual does on the landing page. No height of its own: it stretches to
   the row, and the row's height is set on .design-hero above. */
/* NO GROUND ON A WORN SHOT (Jerry, 2026-08-16: "The Model images should not have
   any extra background"). A model photograph already carries its own setting, so
   anything behind it either never shows or competes with it in the letterbox
   margins. A ground exists for cut-outs, which have no setting of their own.
   This briefly carried the beach earlier the same day; that was the mistake. */
.design-hero .stage--worn {
  background: transparent;
}
/* Absolute + inset:0 + contain, identical to .lp-hero-visual .hero-photo.

   The 2026-08-11 warning this replaces said absolute positioning here collapsed
   every worn hero to zero height. That was TRUE THEN and is not now, and the
   difference is the whole point of this rewrite: back then nothing gave the row
   a height, so taking the image out of flow left literally nothing to hold the
   container open. .design-hero now carries min-height:300px, so the row stands up
   on its own and the photograph is fitted into it -- which is precisely why the
   landing hero has always been able to do this safely.

   If min-height is ever removed from .design-hero, this collapses again. */
.design-hero .stage--worn img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 12px;
  display: block;
}
.design-hero h1 { margin: 0 0 6px; }
.design-hero .virtue {
  font-size: .8rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--brass);
  margin-bottom: 14px;
}
/* REMOVED 2026-08-18 (Jerry saw a dark line across every beach tile).
   `.piece { border-top: 1px solid rgba(0,0,0,.12); padding: 16px 0 }` and
   `.piece h3` were left over from the PIECE ROW, which was deleted from the leaf
   page on 2026-08-17. Nothing emits that row any more -- but beach_tile() emits
   the cut-out as <img class="piece">, so the orphaned rule kept painting its
   border along the top edge of all 69 tiles, and its 16px padding was inflating
   the element past the height the shadow geometry was tuned against.

   The beach cut-out is styled by `.beach .piece` near the top of this file.
   A deleted section's CSS is not inert: it collides with whatever later reuses
   the class name. */

@media (max-width: 620px) {
  .gallery .big { grid-template-columns: 1fr; }
}
/* Same fix as .gallery .tile img, and the one Lorayne actually reported
   (2026-08-11, naming the Lotus metal shots): the whole piece must show, at a
   smaller size, never cut. Her per-metal option shots are portrait, so pinning
   width to 92% pushed their height past this square box and clipped the top and
   bottom -- on every design page carrying an options row, not just Lotus. */
   The :not() exclusions are LOAD-BEARING, not tidiness. This selector is two
   classes plus an element, which outranks `.beach .piece` / `.beach .shadow` --
   so without them it wins, re-centres the piece, and the cast shadow lands on
   top of its own piece instead of behind and below it. Same class of silent
   override as the `background` shorthand two rules up. */
.options-row .ex-shot img:not(.piece):not(.shadow) {
  position: absolute;
  top: 4%;
  left: 4%;
  width: 92%;
  height: 92%;
  object-fit: contain;
  display: block;
}
.m-silver  { background: radial-gradient(circle at 34% 28%, #FBFCFD, #C3C8CD 58%, #8E9399); }
.m-vermeil { background: radial-gradient(circle at 34% 28%, #FBE9B4, #D9B24C 58%, #A8801F); }
.m-brass   { background: radial-gradient(circle at 34% 28%, #F5E3A6, #CBA52A 58%, #8A6A1E); }
.m-copper  { background: radial-gradient(circle at 34% 28%, #F6CBA2, #C97D49 58%, #8E4E27); }
.m-bronze  { background: radial-gradient(circle at 34% 28%, #E7C79C, #A97B45 58%, #6E4C22); }
.m-plated  { background: radial-gradient(circle at 34% 28%, #FBEFC6, #DCC069 58%, #A78F31); }
/* Silver plating is a DIFFERENT finish from the gold plating above. Until
   2026-08-15 both shared .m-plated and four live pages said gold where the piece
   is silver. Kept a touch softer and warmer than .m-silver: it is a plating over
   brass, not solid sterling. */
.m-silver-plated { background: radial-gradient(circle at 34% 28%, #FDFDFD, #C8CAC7 58%, #94958E); }
.m-white-bronze  { background: radial-gradient(circle at 34% 28%, #FBF7EC, #C0B8A4 58%, #837B69); }
/* "Artisan metal" is a LINE, not one metal -- brass, copper or bronze. The
   swatch sweeps all three rather than picking one and misrepresenting it. */
.m-artisan { background: conic-gradient(from 210deg, #CBA52A, #C97D49, #A97B45, #CBA52A); }

/* ---------- leaf: the meaning card ---------- */
/* Symbolism, materials and the piece table, carried over from the Style D
   prototype. One card so the editorial reads as a unit rather than as three
   loose bands competing with the photographs. */
.lede {
  font-size: 1.05rem;
  line-height: 1.6;
  margin: 14px 0 0;
}
/* These stones were read off the photographs by us, not supplied by Lorayne.
   The badge says so on the page itself, not only in the notes block. */
.tbc {
  font-size: .58rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--warn, #A9834F);
  border: 1px solid currentColor;
  border-radius: 3px;
  padding: 1px 5px;
  margin-left: 8px;
  vertical-align: middle;
}
/* Wording we drafted for a design Lorayne has not written words for yet. It
   carries the same badge as the read-off-a-photograph stones because it is the
   same kind of claim: ours, plausible, and awaiting her yes. Anything wearing
   this badge is provisional and MUST NOT ship as her voice -- either she
   approves it or it comes off before the real launch. */
.tbc--gen { text-transform: lowercase; letter-spacing: .06em; }
.lede--gen { font-style: italic; }
.design-hero .virtue .tbc--gen { margin-left: 10px; }
.d-silver   { background: linear-gradient(145deg, #F4F5F6, #BDC2C7 60%, #8E9399); }
.d-vermeil  { background: linear-gradient(145deg, #F7E2A8, #D5AC48 55%, #A8801F); }
.d-abalone  { background: conic-gradient(from 210deg, #1E6E63, #39B3A0, #2F7FB8, #6A4E9C, #1E6E63); }
.d-mop      { background: conic-gradient(from 30deg, #FDF6EC, #E8D9E6, #DCE9F0, #F5EAD8, #FDF6EC); }
.d-topaz    { background: radial-gradient(circle at 34% 28%, #BFE9FA, #3FA9D4 55%, #16688F); }
.d-amethyst { background: radial-gradient(circle at 34% 28%, #D9C2F0, #8B5FBF 55%, #4E2E77); }
.d-citrine  { background: radial-gradient(circle at 34% 28%, #FBEBB6, #E0A93C 55%, #9C6A12); }
.d-peridot  { background: radial-gradient(circle at 34% 28%, #E2F0B0, #9BC24A 55%, #5B7A1E); }
.d-moonstone{ background: radial-gradient(circle at 34% 28%, #FFFFFF, #DCE6EC 55%, #AEBCC6); }
.d-pearl    { background: radial-gradient(circle at 34% 28%, #FFFDF8, #EFE6DA 55%, #C9BCAB); }
.d-druzy    { background: radial-gradient(circle at 30% 26%, #FFFFFF, #C9C3D6 50%, #7E7691); }
.d-generic  { background: linear-gradient(145deg, #EDE6DA, #C3B7A5 60%, #94897A); }

.grid { width: 100%; border-collapse: collapse; font-size: .88rem; margin: 0; }
.grid th, .grid td {
  text-align: left;
  padding: 9px 10px 9px 0;
  border-bottom: 1px solid rgba(0, 0, 0, .12);
}
.grid th {
  font-size: .68rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  opacity: .7;
}
.grid .num { text-align: right; white-space: nowrap; padding-right: 0; }
.grid .n { opacity: .55; }

/* Jerry 2026-08-13: the leaf hero now behaves like the LANDING hero -- copy and
   model stay on ONE row at every width and never stack. This block used to drop
   to `grid-template-columns: 1fr` with `order: -1` on the stage, which pushed the
   design name and lede below the photograph on a merely-narrow laptop window.
   Narrow viewports shrink the TYPE and the GAP instead; the model keeps its
   --hero-model-h height, exactly as the landing band does. Mirrors the tiers in
   components.css under .lp-hero-copy -- if one set moves, move the other. */
@media (max-width: 860px) {
  .design-hero { gap: 22px; }
  .design-hero h1 { font-size: clamp(1.15rem, 3.2vw, 1.6rem); }
  .design-hero .virtue { font-size: .68rem; letter-spacing: .12em; }
  .design-hero .lede { font-size: .82rem; line-height: 1.45; }
}
@media (max-width: 700px) {
  .design-hero { gap: 16px; }
  .design-hero h1 { font-size: clamp(1rem, 4vw, 1.3rem); }
  .design-hero .virtue { font-size: .6rem; letter-spacing: .1em; }
  .design-hero .lede { font-size: .74rem; line-height: 1.4; }
}
/* Below this the bar cannot hold two collections side by side, so it stacks and
   the vertical rule becomes a horizontal one. */
@media (max-width: 860px) {
  .lc-bar { gap: 14px; }
  .lc-rule { width: 100%; height: 1px; min-height: 0; }
  .lc-cell--stones { grid-auto-flow: row; }
}
/* ============================================================
   IMAGES ROW  --  N images per design, as the catalog orders them
   ============================================================ */
.images-row { margin: 0; }
.ir-grid {
  display: grid;
  /* minmax(0,220px), never auto-fit/1fr: a fractional track stretches a lone
     tile to the full row width, which made a single image read as a hero. */
  grid-template-columns: repeat(auto-fill, minmax(0, 220px));
  gap: 1rem;
  justify-content: center;
}
.ir-cell { margin: 0; }
/* position:relative and a HEIGHT are both mandatory here, and their absence is
   why this row rendered at the top of the page instead of in its own track.
   beach_tile() emits its two images as position:absolute -- without a
   positioned ancestor they anchored to whatever was positioned further up the
   tree, and with no height the tile itself collapsed to nothing. Same shape as
   .design-card .shot, which gets it right. */
.ir-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius, 3px);
  overflow: hidden;
}

/* ============================================================
   DESIGN OPTIONS  --  nested boxes, all the way down
   ============================================================
   .design-options
     .do-grid                 flex, WRAPS
       .do-box                one per line: Premier, Artisan
         .do-cell--icon       the logo, its own box
         .do-cell--metals     one box holding...
           .do-item           ...a box per metal
         .do-cell--stones     one box holding...
           .do-item           ...a box per stone

   Flex with wrap rather than two fixed columns and a breakpoint: the two line
   boxes sit side by side while they FIT, and drop to their own rows when they
   do not. flex-basis is what decides that, so the layout answers to its own
   content instead of to a width someone guessed. No media query.

   Each line box carries its own border, so the two are visibly separate whether
   they are side by side or stacked -- which also removes the shared divider that
   had to flip from a vertical rule to a horizontal one. */
.design-options { margin: 0; }
.do-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: stretch;
}
.do-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Left-aligned so the 5px to the first cell -- the logo, or the leading stone
     on a design with no Premier line -- stays 5px. Centring would make it vary
     with content. */
  justify-content: flex-start;
  gap: 14px;
  /* grow | shrink | basis. The basis is the width below which this box stops
     sharing a line; two of them plus the gap must exceed the row for the pair
     to split, which is exactly the behaviour asked for. */
  flex: 1 1 340px;
  min-width: 0;
  padding: 12px 12px 12px 5px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius, 3px);
}

/* --- the three sub-boxes --- */
.do-cell { min-width: 0; }
.do-cell--icon img { display: block; width: 76px; height: auto; }
.do-cell h3 {
  margin: 0 0 .4rem;
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft, #6b6257);
}

/* --- a box per metal, a box per stone --- */
.do-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .55rem;
}
.do-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25rem;
  /* Each item is a narrow box of its own so a long name wraps inside it rather
     than widening the row. Stacking the label under the swatch is what keeps
     these boxes narrow -- side by side, "Sterling silver" would set the width
     of everything. */
  width: 4.4rem;
}
/* Metal and stone NAMES in black, per the spec: the swatch carries the colour,
   so tinting the label too made the darker metals hard to read. */
.do-item b {
  font-weight: 500;
  color: #000;
  font-size: 10pt;
  line-height: 1.15;
  text-align: center;
}
/* The swatches. Stones AND metals are transparent cut-outs from her own charts,
   so they layer on whatever the box background is. `.do-disc` is the CSS
   gradient still used by the three finishes she has not photographed (white
   bronze, silver plated, gold plated) and `.do-dot` is the same fallback for a
   stone we hold no photograph of. */
.do-disc,
.do-dot {
  width: 26px; height: 26px; border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, .18);
  flex: 0 0 auto;
}
.do-stone,
.do-metal {
  width: 26px; height: 26px; border-radius: 50%;
  object-fit: cover; flex: 0 0 auto;
}

/* ============================================================
   CHOOSE YOUR PIECE  --  every SKU, named in full
   Replaces one-image-per-SKU, which put 103 of 118 boxes on the
   placeholder. No prices: commerce is deferred, this is a naming aid.
   ============================================================ */
.choose-piece { margin: 0; }
.cp-help { margin: .25rem 0 .75rem; color: var(--ink-soft, #6b6257); }
.cp-select {
  width: 100%;
  max-width: 34rem;
  padding: .6rem .75rem;
  font: inherit;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius, 3px);
}
