/*
  Products we source — the category rail.

  ── What this has to look like ───────────────────────────────────────────────

  Eleven products, moving slowly, on paper. Read once and understood: a buyer finds
  the thing they sell and stops worrying about whether we make it.

  It was eleven thin-stroke drawings, one per card, on the argument that a drawn
  object reads as a technical drawing rather than as an app icon. It is photography
  now, and the reason is the one thing a drawing cannot do however well it is drawn:
  this company makes physical objects, and a row of line marks reads as a row of
  software features. A photograph of a chair is a chair.

  So the card is two parts and nothing else — a picture, then a name and three
  examples on a panel under it. No text over the image, no gradient scrim, no play
  affordance, no badge in a corner, no lift on hover. What keeps this from becoming a
  product carousel is the same restraint it always was: nothing here is clickable,
  because there is nothing behind a category to go to.

  ── Full bleed, and why ──────────────────────────────────────────────────────

  Only the closing line sits in `.shell` with the rest of the page. The rail does not:
  it runs to both edges of the window, because a rail that stops at the shell has two
  hard ends, and two hard ends is a row of eleven rather than a continuous one. Cards
  enter at one edge and leave at the other, and the light fades over those edges are
  what say the set does not begin or end on screen.

  There is nothing above the rail at all — no heading, no standfirst — so the section
  opens on the photographs. See the note in content/categories.js for why.

  ── Desktop-first ────────────────────────────────────────────────────────────

  Like every other stylesheet here: the wide layout is the base and the narrow
  ones are overrides at the bottom.
*/

/* ═══ THE SECTION ═════════════════════════════════════════════════════════ */

/*
  ── The surfaces ────────────────────────────────────────────────────────────

  The paper's first page now that this block opens the page under the hero — see the
  `--paper-*` note in tokens.css — and the grey of the two tones, with the cards
  standing on it in near-white.

  That is the way round it is for the section below as much as for this one. A black
  hero ending in a near-white band is most of the cut the paper exists to avoid, so
  the first page under it is the darker tone; and Why Library of Trade, directly
  after, is the lighter tone with grey panels in it. The same two values, inverted
  between two adjacent sections, which is what makes the pair read as a rhythm rather
  than as one long light band. Photographs on grey, panels on paper.

  So the card lifts rather than insets, and it lifts to the next section's ground:
  `--paper-white`, fourteen levels off this one, with the separation between a card and
  the section carried by the value step rather than by a hairline. There were two tile
  tones in the family for this — a lighter one for grey grounds, a darker one for
  near-white — and once the two sections inverted, both of them were a third value doing
  a job the two section tones already do between them. The family is two surfaces now,
  and the inversion is literally the same pair swapped.

  The fades come along for free. They are `linear-gradient(..., var(--bg), transparent)`
  and they were black because `--bg` was; they are now the paper, because it is.

  ── The vertical rhythm ─────────────────────────────────────────────────────

  Roomier than the strip below it, and deliberately: this section is a row of
  photographs rather than a row of labels, and a picture needs air around it in a way
  a 22px drawing does not. The cards clear the closing line by about 36px, and the
  section's own padding is the page's standard less a little.

  Its top padding is doing one more job than the others: it is the whole of the
  distance between the hero and the first photograph, since there is no heading in
  between. Less would read as a row of cards attached to the bottom of the hero.

  It is still not a full screen, and it must not become one. The whole argument for
  where this sits — directly under the hero, above everything that explains the
  company — is that it answers one question on the way past.
*/
.cats {
  /*
    Tight, and tighter than any other section on the page. This one has no heading and
    no lead — a reader arrives from the hero directly onto the cards — so the padding is
    not separating a head from a body, it is only keeping the top card off the hero's
    edge. It was 62–96px, which on a section whose content starts immediately read as a
    band of empty paper above the rail rather than as breathing room.

    The bottom is doing slightly more work than the top, because the foot line sits
    below the rail inside this padding and needs to belong to the rail rather than to
    the section under it. Hence the asymmetry: the gap under the cards is the foot's
    `margin-top` plus the line plus this, and the gap over them is only this.
  */
  padding-block: clamp(34px, 3.2vw, 46px) clamp(38px, 3.6vw, 52px);
  /*
    The card's width, on the section rather than on the card, because the two narrow
    layouts override it and the card's rule should not be restated to do that.
  */
  --cats-card: clamp(206px, 15.4vw, 268px);
  --bg: var(--paper-grey);
  --bg-raised: var(--paper-white);
  --line: var(--paper-line);
  --line-strong: var(--paper-line-strong);
  --fg-sub: var(--paper-sub);
  /*
    Soft and sub collapse to one grey here. On ink they are 55% and 72% white and the
    difference is legible; on paper the equivalent pair is #7C7C77 and #686864, and
    the lighter of the two takes the example lists under 4.5:1 on the card they sit
    on. One muted grey for every secondary word in the section, which is also what
    the brief asked for.
  */
  --fg-soft: var(--paper-sub);
}

/* ═══ THE TWO CONTROLS ═══════════════════════════════════════════════════ */

/*
  One at each end of the rail, at the edge of the window, level with the middle of
  the pictures.

  They spent a revision up in a head beside a heading, as a pair — and then the
  heading went, which took the thing they were beside with it. At the ends they are
  where what they do is: the left one moves the card at the left edge, the right one
  the card at the right, and there is nothing to connect across the width of the page.

  ── The vertical position ───────────────────────────────────────────────────

  The middle of the card, panel included. `top: 50%` of the rail is exactly that,
  because the rail is as tall as the viewport and the viewport is as tall as a card:
  no measurement, and nothing to keep in step with the panel's line count.

  It was half the picture's height for a revision, on the argument that a button
  reading over a photograph is better than a button reading over type. It is not: the
  pair sit at the ends of a row of cards, and a control an eighth of the way up from
  the bottom of what it controls looks misaligned with the thing it is beside — which
  is a plainer fault than a 48px square overlapping two words of a title. The fade
  behind each one is solid where the button stands, so what it actually overlaps is
  the section's ground either way.

  ── The horizontal position ─────────────────────────────────────────────────

  Just inside the window rather than out in the page's gutter, because the rail runs
  to the window edge and there is no gutter left to stand in. The fade behind each
  button is the section's own ground for its first third, so a card slides up to the
  button and out from under it instead of appearing to pass through a hole.
*/
.cats-nav {
  position: absolute;
  z-index: 2;
  top: 50%;
  transform: translateY(-50%);
}

.cats-nav[data-cats='prev'] {
  left: clamp(8px, 1vw, 18px);
}

.cats-nav--next {
  right: clamp(8px, 1vw, 18px);
}

/*
  48px square, near-square corner, one edge, an arrow, nothing else. The fill is the
  card's near-white rather than the section's grey: on a grey ground the two controls
  belong to the same set of objects as the eleven cards they move, and a button filled
  with the ground it stands on is a hole with an outline round it.

  Its border is `--paper-edge` and not the section hairline. Everything else bordered
  in this section is an area — a card, a picture — and a hairline is the right weight
  for an edge; this is a thing to press, and at `--paper-line` it read as a square
  somebody had forgotten to finish. The arrow takes `--fg` for the same reason: 1.6px
  of stroke inside 48px is not enough ink to be muted and still be seen.

  They ship `hidden` and the module reveals them — see the note in build.mjs.
*/
.cats-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 1px solid var(--paper-edge);
  border-radius: var(--r-xs);
  background: var(--bg-raised);
  color: var(--fg);
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}

/* Filled, and the arrow reverses out. The whole of the response, in 220ms. */
.cats-nav:hover {
  background: var(--paper-edge);
  color: var(--bg-raised);
}

.cats-nav svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* One arrow, flipped. Two paths for one shape is one more thing to keep in sync. */
.cats-nav--next svg {
  transform: scaleX(-1);
}

/* ═══ THE RAIL ═══════════════════════════════════════════════════════════ */

/*
  Out through the section's own gutter to both window edges. `position: relative`
  because the two fades hang off it.
*/
.cats-rail {
  position: relative;
  margin-inline: calc(var(--gutter) * -1);
}

/*
  The fades, and the reason they are gradients over the cards rather than a filter
  on them: a blurred photograph is a photograph a reader tries to focus on. These
  only take the contrast out of the last couple of centimetres, so the card at each
  edge is still plainly a card with a picture and a name on it — partially visible,
  which is the whole signal that the row continues.

  Solid for its first third and ramping after that, rather than an even ramp from the
  window edge. The solid part is the band the control stands in: a photograph passing
  behind an opaque 48px square looks like a rendering fault if the ground either side
  of the square is a picture, and looks like a card arriving at a button if the ground
  is the section. So the fade is wide enough to hold the button and its inset, and
  opaque for that much of itself.
*/
.cats-rail::before,
.cats-rail::after {
  content: '';
  position: absolute;
  z-index: 1;
  top: 0;
  bottom: 0;
  width: clamp(72px, 8vw, 132px);
  pointer-events: none;
}

.cats-rail::before {
  left: 0;
  background: linear-gradient(to right, var(--bg) 0, var(--bg) 34%, transparent);
}

.cats-rail::after {
  right: 0;
  background: linear-gradient(to left, var(--bg) 0, var(--bg) 34%, transparent);
}

/*
  The scroll container — and with the script blocked, that is literally what it is:
  an ordinary horizontal scroller a reader can drag, flick or shift-wheel through.
  `js/categories.js` sets `data-rail="live"` on the rail once it is driving the
  track itself, and only then does this become a clipped window with a transform
  inside it. The two states are one property apart on purpose: the fallback is the
  same eleven cards in the same order, minus the movement.
*/
.cats-viewport {
  overflow-x: auto;
  overflow-y: hidden;
}

.cats-rail[data-rail='live'] .cats-viewport {
  overflow: hidden;
  /*
    Vertical gestures stay the page's, horizontal ones become the rail's. Without
    this a swipe down that starts on a card is swallowed by the drag handler and
    the page stops scrolling — which is a worse bug than no drag at all.
  */
  touch-action: pan-y;
  user-select: none;
  cursor: grab;
}

.cats-rail[data-rail='live'] .cats-viewport.is-held {
  cursor: grabbing;
}

.cats-track {
  display: flex;
  gap: 14px;
  list-style: none;
}

.cats-rail[data-rail='live'] .cats-track {
  will-change: transform;
}

/*
  The second set of cards is scaffolding for the loop — see the note in build.mjs.
  Until the module is live there is no loop to scaffold, so the duplicate is not in
  the page at all: a reader scrolling the fallback by hand would otherwise reach
  Promotional Products and then meet Fashion & Apparel again.
*/
.cats-rail:not([data-rail='live']) .cats-card[aria-hidden] {
  display: none;
}

/*
  ── The still rail ──

  A moving rail has no first card: every card is on its way somewhere, the one at the
  left edge is on its way out, and the fade over it is what says so. A rail that is
  not moving does have a first card, it is Fashion & Apparel, and two parts of the
  treatment above are wrong for it. Flush against the window edge it looks unaligned
  with the closing line under it, and under a fade it looks half-deleted before the
  reader has done anything.

  So the still rail is not the moving one held in place — it is a different object.
  The window narrows from the full bleed to the column, so the cards begin exactly
  where the heading does and are clipped at the far edge of the text column rather
  than at the edge of the screen; and the fades go, because nothing is on its way out.

  Two readers get this: anyone whose browser did not run the module, and anyone who
  has asked their system not to animate. Neither is a degraded case. Both get eleven
  cards, aligned, at full contrast, scrollable.
*/
.cats-rail:not([data-rail='live']) .cats-viewport {
  margin-inline: var(--gutter);
}

.cats-rail:not([data-rail='live'])::before,
.cats-rail:not([data-rail='live'])::after {
  width: 0;
}

/* ═══ THE CARDS ══════════════════════════════════════════════════════════ */

/*
  A picture with a panel under it, inside one rounded box.

  `overflow: hidden` is what makes the radius work: the corner is set once on the
  card, and the picture takes the top two and the panel the bottom two without either
  of them being told a radius of its own. Nothing is rounded in the image files — see
  the note on `PLACEHOLDER` in content/categories.js.

  `flex: 0 0 auto` so a card is the width it is asked to be: the track is a flex row
  and eleven shrinkable items in it would simply divide the window between them.

  ── The width ───────────────────────────────────────────────────────────────

  About six cards across a large desktop: 15.4vw is 222px at 1440 and hits the 268
  ceiling just past 1740, which holds the count at six and a fraction from a laptop
  to a studio display rather than letting a card grow until five fill the screen. The
  fraction is the point — a rail that ends on a whole card at the window edge looks
  like a row that happens to fit.

  ── The corner ──────────────────────────────────────────────────────────────

  8px, which is between the two radius tokens and written out rather than named. It was
  `--r-sm` at 10, on the argument that the section below should not carry a different
  corner — and that section is now six near-square panels in a modular grid, deliberately
  cornered at 3px, so there is nothing left to match. What is left is what this card is:
  a photograph with a panel under it, and the brief's 6–8px is the corner that reads as a
  printed card rather than as a rounded button.
*/
.cats-card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  width: var(--cats-card);
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-raised);
  transition: border-color var(--dur-fast) var(--ease);
}

/*
  The whole of the hover: the hairline comes up. No transform, no shadow, no scale,
  and no zoom on the photograph. It is not a control — there is nothing behind a
  category to go to — so the response has to read as "you are looking at this one"
  and not as "press this".
*/
.cats-card:hover {
  border-color: var(--line-strong);
}

/*
  Square, and the picture fills it.

  The source files are 4:5, which is what photos.py crops a portrait to, and a square
  window on them takes a tenth off the top and the bottom. That is deliberate rather
  than incidental: square puts the picture at about 60% of the card's height, which is
  the proportion that keeps the panel under it from reading as a caption on a poster.

  `object-position` sits a little above centre because the placeholder's subject does
  — the chair is in the upper two thirds of the frame and the floor is in the lower
  third, and a centred crop spends the card's best inch on floorboards. It is one
  declaration to revisit when the real photography lands.
*/
.cats-figure {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}

.cats-figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  /*
    A picture inside a draggable rail. Without this the browser's own image drag wins
    the gesture: the pointer picks the photograph up, the rail stays where it is, and
    the reader is left holding a ghost of a chair. js/categories.js cancels `dragstart`
    as well, because Firefox does not read this property.
  */
  -webkit-user-drag: none;
  user-select: none;
}

/*
  The panel. Padding on all four sides rather than only where the type is, because
  the picture's bottom edge is the panel's top edge and type set hard against it
  would read as a caption printed on the photograph.

  `margin-top: auto` on nothing here: every card is the same height because every
  picture is square and every panel holds the same three lines. A title that wraps to
  two lines makes its own card taller than its neighbours — which is why the eleven
  titles are checked against this width rather than left to chance.
*/
.cats-panel {
  padding: clamp(18px, 1.5vw, 22px);
}

.cats-card-title {
  font-family: var(--font-display);
  font-size: var(--t-lead);
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.011em;
  color: var(--fg);
}

/*
  The examples, as a list rather than as a sentence — three separate things, and a
  screen reader should say so and then stop — and one example a line, with no
  separators.

  They were set inline with middle dots between them, which is the treatment the
  copy was written for and which fails on two thirds of the cards: three examples
  and two of them two words long do not fit the panel, so most of them wrapped —
  and a separator cannot be attached to either side of a line break without being
  orphaned by it. On the following item it opens the second line with a dot standing
  on its own; on the preceding item it closes the first line with one. Dropping the
  dots and letting the examples run on is worse still: "Custom merch Branded gifts
  Accessories" is three things or two, depending on the reader.

  Stacked, all three fit at every width, every card has the same three lines, and
  there is no punctuation left to strand.
*/
.cats-tags {
  margin-top: 10px;
  list-style: none;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg-soft);
}

/* ═══ THE CLOSING LINE ═══════════════════════════════════════════════════ */

/*
  The reader whose product is not on the rail, answered in one line. It is the
  reason a closed list of eleven does not read as a limit — and the only thing
  saying so, now that the twelfth card, Other Consumer Goods, has gone with the
  drawings. So it sits directly under the cards rather than at the end of the
  section.
*/
.cats-foot {
  margin-top: clamp(26px, 2.6vw, 38px);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--fg-soft);
}

/*
  An inline link, in the page's display face rather than the mono uppercase of
  `.link` — that one is a standalone action with a rule under it, and this one is
  the last three words of a sentence. Underlined with the same hairline everything
  else on this page is drawn with, and the arrow is `content` for the same reason
  it is everywhere else: it never reaches the accessible name.
*/
.cats-ask {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: border-color var(--dur-fast) var(--ease);
}

.cats-ask::after {
  content: '→';
  margin-left: 8px;
  transition: transform var(--dur-fast) var(--ease);
  display: inline-block;
}

.cats-ask:hover {
  border-color: var(--signal);
}

.cats-ask:hover::after,
.cats-ask:focus-visible::after {
  transform: translateX(3px);
}

/* ═══ NARROW ═════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  /*
    Wider cards on the narrower screen, deliberately. 15.4vw of a tablet is a 140px
    card, and a 140px photograph of a chair is a thumbnail. Three and a bit at 900px,
    which is enough of a fourth to show the row continues.

    Set on the variable rather than on the width, so the controls come down with it and
    stay level with the middle of the picture.
  */
  .cats {
    --cats-card: 262px;
  }
}

@media (max-width: 560px) {
  /*
    One and a quarter cards. The point of this rail is that a picture, a title and
    three examples can be read, and shrinking a card to fit more of them on a phone
    costs exactly that — so the card gets wider as the screen gets narrower, and the
    quarter of the second one is what says there are more.
  */
  .cats {
    --cats-card: 300px;
  }

  .cats-track {
    gap: 12px;
  }

  /*
    And the controls go. On a 390px screen a 300px card has 48px of button over each
    of its edges, which is a third of the photograph covered to save a gesture the
    platform already has — the rail is dragged on a phone, and dragging is better than
    either button. They are also the one part of this section a touch reader gains
    nothing from: there is no hover, so a control is a tap that does what a flick does.
  */
  .cats-nav {
    display: none;
  }

  /* The fade is a proportion of a much smaller window; and with no button to stand in
     it, there is nothing left for the solid third to do. */
  .cats-rail::before,
  .cats-rail::after {
    width: 26px;
    background: linear-gradient(to right, var(--bg), transparent);
  }

  .cats-rail::after {
    background: linear-gradient(to left, var(--bg), transparent);
  }
}

/* ═══ REDUCED MOTION ═════════════════════════════════════════════════════ */

/*
  The rail does not drift on its own — that is handled in the module, which never
  adds the drift to the offset. What is turned off here is the arrow on the closing link.
  Both controls still work, and pressing one still moves the rail, because reduced
  motion means "do not animate", not "do not respond".

  And since it is not moving, it is the still rail, and it takes the still rail's
  geometry: column-width window, no fades. The note above the
  `:not([data-rail='live'])` rules is the argument; these are the same two
  declarations for the reader who has the module but does not want it animating.
*/
@media (prefers-reduced-motion: reduce) {
  .cats-viewport {
    margin-inline: var(--gutter);
  }

  .cats-rail::before,
  .cats-rail::after {
    width: 0;
  }

  .cats-ask::after,
  .cats-ask:hover::after {
    transition: none;
    transform: none;
  }
}

/* ═══ PRINT ══════════════════════════════════════════════════════════════ */

/*
  All eleven on the paper, wrapped into rows. A printed rail is otherwise five
  cards and a clipped sixth, and the section's whole job is the completeness of the
  list. The duplicate set and the two controls go.
*/
@media print {
  .cats-rail {
    margin-inline: 0;
  }

  .cats-rail::before,
  .cats-rail::after,
  .cats-nav,
  .cats-card[aria-hidden] {
    display: none;
  }

  /* The rail's own margin is gone, so the still rail's inset would indent the cards
     past the paragraph above them. */
  .cats-viewport {
    overflow: visible;
    margin-inline: 0;
  }

  .cats-track {
    flex-wrap: wrap;
    gap: 18px;
    transform: none !important;
  }
}
