/* ============================================================================
   THE BOUTIQUE CASCADE — products.html

   The house world, unchanged: ivory paper and the one emerald dark surface,
   antique gold accents, House Display for headings, Optima for prose, Allura
   for the single accent word. Nothing here introduces a colour, a face or a
   component the rest of the site does not already own. It composes them
   differently, which is the whole job.

   The structure is a diminuendo. The page opens at its largest and steps down
   in three movements, and scale is the only wayfinding — no section numerals,
   no eyebrow labels, no chrome announcing where you are:

     I   Bridal & Statement   5 pieces    plate 460px   heading largest
     II  Rings & Bangles      4 pieces    plate 300px   emerald ground
     III Pendants            16 pieces    plate 250px   4 x 4, quiet
     IV  Handicraft           6 pieces    plate 300px   emerald ground, 3 x 2

   That gradient is not arbitrary: it is the range's own gradient. The bridal
   work is the statement end, the pendants are the everyday end, and the page
   descends through the collection the way the price and occasion do.

   IV is a coda, not a fourth step of the descent, and it is the one place the
   scale goes back up. The house sells three categories — silver, gold and
   handicraft — and the first three movements are all jewellery. Handicraft is a
   different discipline, so it gets the emerald ground and a plate a size larger
   than the pendant field rather than continuing down, which would have filed the
   third category as a quieter footnote to the second. The diminuendo is I-III;
   IV answers it. Read the two dark grounds as bookends around the pendant field.

   Motion grades with it. Each movement sets --reveal-travel, --reveal-dur and
   --reveal-stagger, so the entry animation gets shorter, faster and tighter as
   the pieces get smaller. It is the same .reveal / .is-visible system every
   other page uses, only parameterised — see css/animations.css.

   ON RESOLUTION: the product photographs are 400x500 originals and that is all
   there is. An earlier pass capped every plate at ~260px to keep them pixel-crisp
   on a 2x screen, and the result was a jewellery page you could not see the
   jewellery on — the wrong trade for a surface whose whole job is letting someone
   fall for a piece. The plates are now sized for presence, and
   tools/optimize-images.py renders 640px and 920px candidates with a LANCZOS
   enlargement plus a mild unsharp pass. That adds no detail — nothing can — but it
   renders far better than handing the browser a 400px file and letting it stretch.
   If real high-resolution photography ever arrives, drop the UPSCALE_OK entry for
   these keys and the pipeline goes back to honest downscaling with no other change.
   ========================================================================= */

/* -------------------------------------------------------------- movements */
.movement{position:relative;}

/* The grade. Every value a movement needs to feel a step quieter than the one
   above it lives here, so the descent is legible in one place. */
.movement--i{
  --plate:460px;
  --plate-name:clamp(1.3rem,2.1vw,1.75rem);
  --reveal-travel:36px; --reveal-dur:1.15s; --reveal-stagger:.11s; --reveal-wipe:1.2s;
}
.movement--ii{
  --plate:300px;
  --plate-name:clamp(1.1rem,1.6vw,1.32rem);
  --reveal-travel:24px; --reveal-dur:.85s; --reveal-stagger:.07s; --reveal-wipe:.9s;
}
.movement--iii{
  --plate:250px;
  --plate-name:clamp(1rem,1.35vw,1.15rem);
  --reveal-travel:12px; --reveal-dur:.55s; --reveal-stagger:.035s; --reveal-wipe:.6s;
}
/* The coda, and the one movement that does NOT continue the diminuendo — it steps
   back UP to 300px after the 250px pendant field. That is deliberate. I-III descend
   through the jewellery the way its price and occasion do; handicraft is a different
   discipline, not a smaller grade of the same one, so continuing the descent would
   have said "more of the above, quieter" about the house's third product category.
   The emerald ground and the step back up are what mark it as a change of subject
   rather than a further step down. Read the descent as I-III, with IV answering it. */
.movement--iv{
  --plate:300px;
  --plate-name:clamp(1.1rem,1.6vw,1.32rem);
  --reveal-travel:20px; --reveal-dur:.8s; --reveal-stagger:.06s; --reveal-wipe:.85s;
}

/* ------------------------------------------------------------------ heads
   Heading, count, and a gold rule that draws itself as the movement arrives.
   No kicker: the heading carries its own weight, and the scale of the type is
   already telling the reader how deep into the cascade they are. */
.movement__head{display:grid;gap:.9rem;margin-bottom:clamp(1.8rem,4vw,3.2rem);}
.movement__top{display:flex;align-items:baseline;justify-content:space-between;gap:2rem;flex-wrap:wrap;}
.movement__head h2{font-size:var(--mv-h2,var(--fs-h2));font-weight:500;line-height:.98;max-width:18ch;}
.movement--i  .movement__head{--mv-h2:clamp(2.1rem,5vw,4.4rem);}
.movement--ii .movement__head{--mv-h2:clamp(1.8rem,3.8vw,3.2rem);}
.movement--iii .movement__head{--mv-h2:clamp(1.5rem,2.8vw,2.3rem);}
.movement__count{font-family:var(--font-mono);font-size:.7rem;letter-spacing:.22em;
  text-transform:uppercase;color:var(--ink-3);white-space:nowrap;}
.movement__lede{font-family:var(--font-body);color:var(--ink-2);max-width:54ch;}
/* transform, not width: composited, and it keeps the rule out of layout. */
/* Driven from the head's own .is-visible rather than carrying .reveal itself:
   .reveal writes transform:translateY, which would overwrite this scaleX. */
.movement__rule{height:1px;background:var(--oxblood);transform:scaleX(0);transform-origin:left;
  transition:transform 1.2s var(--ease);}
.movement__head.is-visible .movement__rule{transform:scaleX(1);}
@media (prefers-reduced-motion:reduce){.movement__rule{transform:none;}}

/* Dark movements inherit the house .dark treatment, with their own text colours.
   Keyed to .movement.dark rather than to --ii by name: there are two dark movements
   now (rings, and the handicraft coda), and the treatment belongs to the ground, not
   to a position in the sequence. --ii keeps only its scale grade above. */
.movement.dark .movement__head h2{color:var(--cream);}
.movement.dark .movement__count{color:rgb(var(--cream-rgb) / .58);}
.movement.dark .movement__lede{color:rgb(var(--cream-rgb) / .78);}
.movement.dark .movement__rule{background:var(--gold);}

/* ----------------------------------------------------------------- plates
   A piece and its label. The photograph sits on the paper panel with room
   around it; the panel is what changes size across the cascade, not the
   photograph's share of it. */
.cascade{display:grid;gap:clamp(1.4rem,3vw,2.6rem) clamp(1.2rem,2.4vw,2rem);}
.cascade--two{grid-template-columns:repeat(2,1fr);}
.cascade--four{grid-template-columns:repeat(4,1fr);}
/* Four rows of four, not two rows of eight. Eight across rendered each pendant at
   about 112px, which is a thumbnail of a small object — unreadable as jewellery on
   a page whose whole job is letting someone fall for a piece. Four, eight and two
   all divide sixteen, so the last row is never a part-row at any breakpoint. */
.cascade--dense{grid-template-columns:repeat(4,1fr);gap:clamp(1.2rem,2.4vw,2rem) clamp(1rem,1.8vw,1.5rem);}
/* Three, for the six handicraft pieces: two clean rows here and three at the 860px
   two-column step. Six divides by three and by two, so as with the pendant grid above
   there is never a part-row at any breakpoint. */
.cascade--three{grid-template-columns:repeat(3,1fr);}

.plate{display:flex;flex-direction:column;align-items:center;text-align:center;}
/* The hairline is doing real work, not decoration. These photographs come from
   several different shoots — studio white, black velvet, teal cloth, a bust —
   and without an edge they read as loose rectangles dropped on the paper. A
   single hairline turns each one into a mounted plate, which is what makes a
   mixed set look catalogued rather than collected. */
.plate__frame{
  width:100%;max-width:var(--plate);aspect-ratio:4/5;
  background:var(--paper-3);position:relative;overflow:hidden;
  display:grid;place-items:center;
  outline:1px solid var(--line);outline-offset:0;
}
.movement.dark .plate__frame{outline-color:rgb(var(--cream-rgb) / .16);}
.overture .plate__frame{outline-color:rgb(var(--gold-rgb) / .38);}
.plate__frame picture{display:block;width:100%;height:100%;}
.plate__frame img{width:100%;height:100%;object-fit:cover;
  transition:transform 1.2s var(--ease);}
.plate:hover .plate__frame img{transform:scale(1.04);}
.plate__cap{padding-top:.95rem;display:grid;gap:.3rem;}
.plate__name{font-family:var(--font-display);font-weight:500;font-size:var(--plate-name);
  line-height:1.14;color:var(--ink);letter-spacing:-.005em;transition:color .35s;}
.plate:hover .plate__name{color:var(--oxblood-deep);}
.plate__meta{font-family:var(--font-mono);font-size:.6rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-3);}
.movement.dark .plate__frame{background:rgb(var(--cream-rgb) / .07);}
.movement.dark .plate__name{color:var(--cream);}
.movement.dark .plate:hover .plate__name{color:var(--gold);}
.movement.dark .plate__meta{color:rgb(var(--cream-rgb) / .6);}
.movement--iii .plate__cap{padding-top:.7rem;}
.movement--iii .plate__meta{font-size:.55rem;letter-spacing:.16em;}

/* --------------------------------------------------------------- overture
   The first movement opens on one piece given a whole emerald panel to itself.
   It reads as the largest thing on the page without the photograph being any
   bigger than its four siblings — the panel, the measure and the type do it. */
.overture{
  background:var(--emerald);color:var(--cream);
  display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);
  align-items:center;gap:clamp(1.8rem,5vw,5rem);
  padding:clamp(1.8rem,4vw,3.4rem);margin-bottom:clamp(2rem,4.5vw,3.6rem);
}
.overture .plate__frame{max-width:min(420px,100%);margin-inline:auto;background:rgb(var(--cream-rgb) / .07);}
.overture__say h3{font-family:var(--font-display);font-weight:500;
  font-size:clamp(1.7rem,3.4vw,3rem);line-height:1;color:var(--cream);max-width:14ch;}
.overture__say h3 em{font-family:var(--font-display-italic);font-style:normal;color:var(--gold);}
.overture__meta{font-family:var(--font-mono);font-size:.66rem;letter-spacing:.22em;
  text-transform:uppercase;color:var(--gold);margin-bottom:1rem;display:block;}
.overture__note{font-family:var(--font-body);color:rgb(var(--cream-rgb) / .8);max-width:44ch;margin-top:1.1rem;}
@media (max-width:860px){
  .overture{grid-template-columns:1fr;text-align:left;}
  .overture .plate__frame{margin-inline:0;}
}

/* ------------------------------------------------------------------ narrow
   The cascade must keep descending on a phone, so the column counts step down
   in step with each other rather than all collapsing to two. */
@media (max-width:1180px){
  .cascade--dense{grid-template-columns:repeat(4,1fr);}
}
@media (max-width:860px){
  .cascade--four{grid-template-columns:repeat(2,1fr);}
  .cascade--dense{grid-template-columns:repeat(2,1fr);}
  .cascade--three{grid-template-columns:repeat(2,1fr);}
}
@media (max-width:560px){
  /* The statement work goes one-up on a phone. At two columns it rendered the
     same 164px as the rings movement below it, which flattened the descent
     exactly where it should be steepest — and scale is this page's only
     wayfinding, so losing it on the majority device loses the structure. */
  .cascade--two{grid-template-columns:1fr;}
  .cascade--four{grid-template-columns:repeat(2,1fr);}
  .cascade--dense{grid-template-columns:repeat(2,1fr);}
  .cascade--three{grid-template-columns:repeat(2,1fr);}
  /* on a phone the plate cap is the only thing separating two pieces, so the
     smallest movement needs its label back at a readable size, and its plate
     lifted enough to stay a photograph rather than an icon */
  .movement--iii{--plate-name:1rem;}
}
