/* ===========================================================================
   band.css — leaf 03, the line-up                                (§6 leaf 03)

   One horizontal band spanning the card's inner width AND NO FURTHER, at a
   fixed 244px height, nine unequal panels butted edge to edge with no gaps.

   HARD RULE, written here because it is the rule people break:
     Big photographs big, small photographs typographic punctuation, nothing
     attempted in between. max-height:244px on EVERY band panel. 60 of the 79
     source images are exactly 260x260 and cannot be enlarged. Any request to
     "make the product images bigger" requires new photography, not a CSS
     change. Never drop a 260px tile into a sliver slot — use a type panel.

   The band is a real overflow-x:auto container: keyboard-, trackpad- and
   touch-operable, and fully functional with JS off. js/band.js scrubs its
   scrollLeft 1:1 against the section's raw progress and drives --grade; it
   adds no easing of its own — mechanical horizontal against damped vertical.

   No panel zooms on hover. There is no hover state on this band at all.
   =========================================================================== */

.band{
  --grade: 0;                    /* 0..1, set by js/band.js from section progress */
  position: relative;
  display: flex;
  flex-flow: row nowrap;
  align-items: stretch;
  gap: 0;                        /* butted, no gutters */
  width: 100%;
  max-width: 100%;               /* the card's inner width and no further */
  height: 244px;
  pointer-events: auto;          /* .card__inner is pointer-events:none */

  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  scroll-padding-inline-start: var(--card-pad);
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule-strong) transparent;

  /* tonal grade by scroll progress — replaces any neighbour-desaturate hover */
  filter:
    saturate(calc(0.58 + 0.42 * var(--grade)))
    contrast(calc(0.96 + 0.08 * var(--grade)));
  transition: filter 120ms linear;
}

.band::-webkit-scrollbar{ height: 4px }
.band::-webkit-scrollbar-track{ background: transparent }
.band::-webkit-scrollbar-thumb{ background: var(--rule-strong); border-radius: 2px }

/* --- every panel --------------------------------------------------------- */

.band .panel{
  position: relative;
  flex: 0 0 var(--w, 244px);
  width: var(--w, 244px);
  height: 244px;
  max-height: 244px;             /* the hard guard */
  margin: 0;
  overflow: hidden;
  scroll-snap-align: start;
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

/* --- photographic panels ------------------------------------------------- */

.band .panel[data-kind="photo"]{
  box-shadow: inset 0 0 0 1px var(--panel-edge);   /* 1px inner hairline */
}

.band .panel[data-kind="photo"] img{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* never scaled on hover, never scaled up past its source */
}

/* The line name sits bottom-left on a solid plate, not on the photograph:
   §7 decision 5 forbids text over a photograph anywhere on this site, and the
   leaf-03 brief wants the name at bottom-left inside the panel hairline. A
   small tipped-in label bar satisfies both and keeps AA contrast unconditional. */
.band .panel .name{
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 6px 10px 5px;
  background: var(--plate-foam);
  color: var(--stencil);
  border-top: 1px solid var(--panel-edge);
  border-right: 1px solid var(--panel-edge);
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: 11px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.024em;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- the label card (panel 1) -------------------------------------------- */

.band .panel[data-kind="label"]{
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4);
}

.band .panel[data-kind="label"] .label{
  margin: 0;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 32;
  font-weight: 500;
  font-size: var(--t-quote);
  letter-spacing: -0.008em;
  line-height: 1.02;
  text-wrap: balance;
}

.band .panel[data-kind="label"] .sub{
  margin: 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: var(--t-label);
  letter-spacing: 0;
  line-height: 1.42;
}

/* --- the two non-photographic type panels -------------------------------- */
/* These exist because 148px and 132px slots cannot hold a 260px tile without
   upscaling it. Type is the honest filler; a stretched photograph is not. */

.band .panel[data-kind="type"]{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-3);
}

.band .panel[data-kind="type"] .caps{
  writing-mode: vertical-rl;      /* rotated 90 degrees */
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: 11px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.024em;
  white-space: nowrap;
}

.band .panel[data-kind="type"] .callout{
  margin: auto 0 0;               /* the dimension sits at the panel's foot */
  width: 100%;
}

/* --- panel grounds -------------------------------------------------------- */
/* data-ground values come straight from the markup: "kraft-gold" is a family
   token, "plate-carbon" is a ground token. Both are mapped explicitly. */

.band .panel[data-ground="kraft-gold"]{
  background: var(--kraft-gold);
  color: var(--stencil);          /* kraft-gold is a non-text colour: dark ink */
}

.band .panel[data-ground="plate-carbon"]{
  background: var(--plate-carbon);
  color: var(--carbon-type);
}

/* The callout's own stylesheet grades for light plates. On the carbon type
   panel that ink fails, so it is re-inked here and only here. */
.band .panel[data-ground="plate-carbon"] .callout .num{ color: var(--carbon-soft) }
.band .panel[data-ground="plate-carbon"] .callout .lead,
.band .panel[data-ground="plate-carbon"] .callout .tick{ stroke: var(--rule-dark) }

/* --- focus -------------------------------------------------------------- */
/* Panels that are links are real tab stops, which is how arrow keys reach the
   band's scroll. The ring is inset so it is never clipped by overflow:hidden. */
.band .panel:focus-visible{ outline-offset: -4px }

/* --- narrow cards -------------------------------------------------------- */
/* The band does not reflow: it is a band. It simply scrolls sooner, which is
   the behaviour a physical line-up would have. Panel widths are literal px
   because they are derived from source-image widths, not from the viewport. */
@media (max-width: 560px){
  .band{ scroll-padding-inline-start: var(--s-4) }
  .band .panel[data-kind="label"]{ padding: var(--s-3) }
}

/* --- no JS / reduced motion ---------------------------------------------- */
/* Nothing is lost: the band is a native scroller and the grade rests at its
   end state. */
html:not(.js) .band{ --grade: 1; transition: none }

@media (prefers-reduced-motion: reduce){
  .band{
    --grade: 1;
    transition: none;
    scroll-behavior: auto;
  }
}


/* ===========================================================================
   band--lineup — leaf 03 as a flat sliding strip of captioned plates.

   Modelled on the reference recording: large plates with the name, the range and
   the forms set underneath, generous air between them, a label panel to open on
   and an index panel to close on. No perspective, no rotation, no hover zoom —
   the horizontal is mechanical (js/band.js scrubs scrollLeft 1:1 against the
   section's raw progress) against the damped vertical.

   The 244px guard above still governs the FLAT band. Here the plate is the
   subject, so it is allowed to 1.3x its source and no further: most tiles are
   260x260, and past that they visibly soften. Bigger plates need new
   photography, not a bigger number.
   ======================================================================== */

.band--lineup{
  height: min(100%, 62svh);
  align-items: center;
  gap: clamp(1.25rem, 3.2cqw, 3.25rem);
  padding-inline: var(--card-pad);
  scroll-snap-type: x proximity;
}

/* The opening label and the closing index panel read as type, not as plates. */
.band--lineup .lineup__intro,
.band--lineup .lineup__end{
  flex: 0 0 auto;
  width: min(clamp(200px, 24cqw, 320px), 38svh);
  scroll-snap-align: start;
}
.band--lineup .lineup__intro .label{ margin: 0 }
.band--lineup .lineup__intro .sub{ margin-top: var(--s-2) }
.band--lineup .lineup__end .body{ margin: 0; max-width: 26ch }
.band--lineup .lineup__all{
  display: inline-block;
  margin-top: var(--s-4);
  font-family: var(--f-label);
  font-variation-settings: "wdth" 84, "wght" 600;
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-plate);
  border-bottom: 1px solid var(--on-rule);
  padding-bottom: 2px;
}

/* --- one line, one card -------------------------------------------------- */

.band--lineup .line-card{
  flex: 0 0 auto;
  display: grid;
  grid-template-rows: auto auto auto auto;
  gap: 0;
  width: min(clamp(210px, 45cqw, 500px), 50svh);
  scroll-snap-align: start;
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.band--lineup .line-card__plate{
  display: block;
  width: 100%;
  height: min(clamp(230px, 30cqw, 420px), 45svh);
  overflow: hidden;
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px var(--panel-edge);
}
.band--lineup .line-card__plate img{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.band--lineup .line-card__name{
  font-family: var(--f-display);
  font-variation-settings: "opsz" 32;
  font-weight: 500;
  font-size: clamp(1rem, 1.7cqw, 1.5rem);
  letter-spacing: -0.008em;
  line-height: 1.1;
  color: var(--on-plate);
  margin-top: clamp(0.5rem, 1.1cqw, 0.9rem);
}
.band--lineup .line-card__range,
.band--lineup .line-card__forms{
  font-family: var(--f-label);
  font-size: var(--t-label);
  color: var(--on-soft);
  line-height: 1.35;
}
.band--lineup .line-card__range{ font-variation-settings: "wdth" 88; margin-top: 3px }
.band--lineup .line-card__forms{ font-variation-settings: "wdth" 76 }
.band--lineup .line-card__go{
  margin-top: clamp(0.4rem, 0.9cqw, 0.7rem);
  font-family: var(--f-label);
  font-variation-settings: "wdth" 84, "wght" 600;
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-plate);
}
/* The only state on the strip: the arrow leads, the hairline draws. */
.band--lineup .line-card__go span{
  display: inline-block;
  transition: translate 320ms var(--e-roll);
}
.band--lineup .line-card:hover .line-card__go span,
.band--lineup .line-card:focus-visible .line-card__go span{ translate: 4px 0 }
.band--lineup .line-card:focus-visible .line-card__plate{ outline: 2px solid var(--focus); outline-offset: 3px }

@media (prefers-reduced-motion: reduce){
  .band--lineup .line-card__go span{ transition: none }
}

/* ===========================================================================
   The box slide — leaf 03 (js/boxslide.js).

   Its own leaf, so unlike the earlier attempts this owns its grid cell outright
   and cannot squeeze anything: .leaf__body holds exactly ONE child here, the
   same shape body-03 has with the band.

   The canvas is absolutely positioned inside .boxslide, so the cell's size is
   fixed by the type in it and adding or removing the renderer never reflows.
   ======================================================================== */
.boxslide{
  position: relative;
  display: grid;
  /* THE LINE IS THE HEAD OF THE SLIDE, so it sits where a head sits: hard into
     the top-left corner of the cell.

     Centred, it was neither — a 11ch column floating at mid-height with air
     above and below it and the carton beside it, so the eye had no entry point
     and the two objects read as equals. Anchoring it top-left restores the
     order the leaf actually argues in (claim, then the object that answers it),
     and it frees the whole lower-right of the cell for the carton, which is
     where js/boxslide.js's BIAS_X / BIAS_Y now put it. The composition is a
     diagonal instead of two things side by side. */
  align-content: start;
  justify-items: start;
  /* REAL vertical space, and it has to be stated. .leaf__body is a grid whose
     row is sized to its content, so a cell holding only a heading and one line
     of type collapses to ~53px and the canvas inside it collapses with it. The
     camera move needs a frame to happen in. Capped in vh so the leaf still fits
     a 684px laptop window with the heading above it. */
  /* 66vh, not more. 78 overflowed the card and put an internal scrollbar down
     the right of the slide — the leaf body has the heading above this and the
     card's own padding around it, so the canvas cannot have the whole viewport.
     The box is made larger by MARGIN 0.94 in js/boxslide.js instead, which
     costs nothing in layout. */
  /* 64vh. 72 overflowed the card and put an internal scrollbar down the slide —
     the heading column and the card's own padding both sit inside this. The box
     is made large by MARGIN 0.82 in js/boxslide.js, which costs no layout. */
  min-height: clamp(350px, 64vh, 700px);
  width: 100%;
}

/* THE STACK STANDS IN A LIT SPACE, not on a sheet of paper.

   The leaf's plate is one flat kraft cream edge to edge, and against it three
   kraft boxes had nothing to be seen against — same hue, same value, no
   suggestion of a room. Two gradients fix it and neither is decoration: a warm
   pool up and behind where the carton stands, so the light in the render has
   somewhere to have come from, and a cool floor wash along the bottom for the
   contact shadow to sit in. Both are far below the contrast of anything else on
   the leaf; you should not be able to point at them, only miss them. */
.boxslide::before{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* BOTH REACH ZERO BEFORE THE CELL EDGE, and that is not a detail. The first
     pass sized them generously and clipped: a gradient still at strength where
     its box ends draws a hard rectangle, and the slide gained a visible warm
     panel with four corners sitting on the plate. The warm pool is an ellipse
     small enough to close inside the cell (centre 58%/38%, radii 40%/36%, so it
     spans 18-98% across and 2-74% down), and the floor wash is a LINEAR gradient
     that returns to transparent at 100% rather than a radial cut off at the
     bottom. */
  background:
    radial-gradient(40% 36% at 58% 38%, rgba(224,162,76,0.17), rgba(224,162,76,0) 100%),
    linear-gradient(to bottom, rgba(27,29,33,0) 48%, rgba(27,29,33,0.075) 84%, rgba(27,29,33,0) 100%);
}

/* The type sits ABOVE the canvas and fades as the camera goes into the box —
   js/boxslide.js writes --boxslide-type. It is the leaf's accessible heading,
   so it is never display:none and never aria-hidden. */
.boxslide > .label,
.boxslide > .sub{
  position: relative;
  z-index: 1;
  opacity: var(--boxslide-type, 1);
}

.boxslide__gl{
  /* A SQUARISH WINDOW, centred — not the full width of the cell.

     The leaf body is ~1046px wide against ~457px tall, which is 2.29:1, and the
     carton is very nearly square. Framed to fit inside a frame that wide it
     covers about 18% of it and reads as a small object adrift in a letterbox.
     Constraining the canvas to 6:5 and centring it means the fit solve has a
     frame the same shape as the thing in it, so the box fills the slide instead
     of floating in it. Height-driven, so it still tracks the cell. */
  /* Fills the CELL. It used to be a centred 5:4 window, which was right while
     the box had to compete with copy for the same middle; now the copy has its
     own column on the left and the carton is meant to own the rest, so the
     canvas takes the whole cell and js/boxslide.js biases the box within it. */
  position: absolute;
  inset: 0;
  /* 1, above the ::before atmosphere and below the copy. */
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
}

/* No JS, no WebGL, or reduced motion: the leaf is just its heading, which is
   what the markup already is. Nothing to hide and no gap to leave. */

/* The box slide's line uses .statement, the site's display voice — the same
   class leaf 02's "We make it. We cut it." uses — rather than .label, which is
   the small Archivo tag "Our lines" wears. It is a sentence, not a tag.
   Constrained here because .statement is otherwise sized for a two-column grid
   cell and this one sits over a canvas. */
.boxslide > .statement{
  /* ONE CLAUSE PER LINE, and that is the whole measure.

     11ch was sized for the old centred column, and at --t-statement it broke
     five words across five ragged lines ("We make / products / you can trust /
     to protect / what matters") — a display face set at a width it cannot
     stand up in. .clause is display:block, so the two lines are already
     guaranteed; the measure only has to be wide enough that neither of them
     wraps a second time. 25ch fits the longer clause with a word to spare and
     still stops short of the carton, which now starts around 55% across. */
  max-width: 25ch;
  margin: 0;
  position: relative;
  /* 2 — over the canvas, which is now 1 so it can sit over the atmosphere. */
  z-index: 2;
  opacity: var(--boxslide-type, 1);
}

/* The copy drifts horizontally against the carton as the leaf scrolls —
   js/boxslide.js writes --boxslide-shift in em, and the box slides the other
   way via the camera's view offset. Opposing directions are what makes it read
   as two layers at different depths instead of one picture panning.
   transform only, so it never costs a layout. */
.boxslide > .statement{
  transform: translate3d(var(--boxslide-shift, 0em), 0, 0);
  will-change: transform, opacity;
}

/* ===========================================================================
   Liquid-glass plates.

   The line-up photographs read as slabs of glass with the picture behind them,
   rather than as images stamped on paper. Four things together, and it needs
   all four — any one alone just looks like a drop shadow:

     1  a SOFT EDGE. The corners round, and the image's own opacity still
        feathers (the mask below), so the picture dissolves into the glass
        instead of stopping at it.
     2  a lit RIM. Bright along the top-left where a thick edge would catch the
        light, dim along the bottom-right. This is what gives it thickness.
     3  a SPECULAR sweep across the upper face — the highlight a curved surface
        throws, not a flat one.
     4  a lift SHADOW, tight and close, so the slab sits above the card ground.

   Everything is a pseudo-element over the image, so nothing here can disturb
   ribbon.js's transforms on the card itself.
   ======================================================================== */
.band--lineup .line-card__plate{
  position: relative;
  /* Smooth, not pill. Big enough to read as a radius, small enough that the
     photograph is still a rectangle. */
  border-radius: 16px;
  /* NO isolation:isolate here, and no stacking context of any kind.
     js/ribbon.js lays these cards out on a preserve-3d arc; a stacking context
     on a descendant flattens that subtree and drops it out of the depth sort,
     so the cards paint in DOM order instead of by distance and the carousel
     collapses into a jumble of overlapping plates. Cost me the carousel once. */
  box-shadow:
    /* contact — tight and dark, holds it to the ground */
    0 1px 2px rgba(27, 29, 33, 0.07),
    /* lift — wide and soft, well below full strength so the card stays calm */
    0 14px 32px -18px rgba(27, 29, 33, 0.32);
}

/* The glass itself: rim, specular, and the inner hairline that used to be a
   flat inset border. Above the picture, below nothing, and inert to pointers. */
.band--lineup .line-card__plate::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  background:
    /* specular sweep across the top face */
    linear-gradient(148deg,
      rgba(255, 255, 255, 0.34) 0%,
      rgba(255, 255, 255, 0.10) 18%,
      rgba(255, 255, 255, 0) 42%),
    /* faint return light off the bottom edge */
    linear-gradient(328deg,
      rgba(255, 255, 255, 0.16) 0%,
      rgba(255, 255, 255, 0) 26%);
  box-shadow:
    /* the lit top-left rim, and the shaded bottom-right one */
    inset 0 1px 0 rgba(255, 255, 255, 0.62),
    inset 1px 0 0 rgba(255, 255, 255, 0.34),
    inset 0 -1px 0 rgba(27, 29, 33, 0.10),
    inset -1px 0 0 rgba(27, 29, 33, 0.06),
    /* the ranked hairline the flat plate used to carry, kept */
    inset 0 0 0 1px var(--panel-edge);
}

/* The feather has to follow the new radius, or the mask would cut square
   corners back out of a rounded plate. */
.band--lineup .line-card__plate img{
  border-radius: inherit;
}
