﻿/* mouldshop-category-grid.css — skin for the DYNAMIC Mouldshop Category Grid (TASK-0021a, ruling 6).
   Loaded via MouldshopHead. Tokens from mouldshop.css.

   THE COLUMN LADDER IS FIXED (6 / 4 / 3 since 2026-09-27; ruling 6 set 6 / 3 / 2) and is not an authoring choice — ruling 6. The
   `Columns` item field was removed from ItemType_Swift_CategoryGrid_Custom in this packet, so
   there is no `--ms-catgrid-cols` any more. Which project breakpoint (invariant 6:
   749 / 989 / 990 / 1200) drives which step:

     >= 992px     6 columns   base rule, no media query
     768-991px    4 columns   @media (max-width: 991.98px)
     < 768px      3 columns   @media (max-width: 767.98px)
     (2026-09-27, operator: moved onto the product slider's steps; was 6 / 3 / 2 at 990 / 750.
      The 749 / 989 / 990 / 1200 sentence above describes the old ladder.)

   KNOWING DEVIATION FROM THE REFERENCE, recorded per invariant 1. The live reference is NOT
   6/3/2: its top row is `grid grid-1 grid-lg-6` (1 column on mobile) and its bottom row is
   `grid grid-2 grid-lg-6` (2 columns), with no middle tier and the two rows disagreeing with
   each other. Ruling 6 states 6/3/2 explicitly, so ruling 6 wins. Do not "correct" this toward
   the reference.

   THE HOVER STATE IS THE REFERENCE'S, READ FROM ITS OWN STYLESHEET (invariant 10) —
   /Files/Templates/Designs/Swift/Assets/css/mouldshop2.css, the `.bon-svg` block:
     .bon-svg a                    transition: transform .35s, box-shadow .35s
     .bon-svg a > div              background-color: rgba(108,178,222,0), transition .35s
     .bon-svg a:hover > div        background-color: #6cb2de
     .bon-svg a:hover              box-shadow: rgba(50,50,93,.25) 0 30px 60px -12px,
                                              rgba(0,0,0,.3) 0 18px 36px -18px
     .bon-svg .bon-svg-lightblue   fill: #c4e0f2   -> opacity 0 on hover
     .bon-svg .bon-svg-darkblue    fill: #0a2979   -> fill #ffffff on hover
     .bon-svg a h5                 font-size: 1rem; padding: 0 15px 25px
   Values copied, selectors rewritten onto our own class names. Nothing here is invented.

   .bon-svg-lightblue / .bon-svg-darkblue are classes carried INSIDE the operator's icon files.
   They are reachable only because the SVG is inlined by ReadFile — this block is the working
   demonstration that a CSS rule reaches an inner path, which a sprite <use> could not provide. */

/* NO SIDE PADDING AND NO SIDE MARGIN, AT ANY WIDTH — the row fills its container and the
   six cells divide it (operator, 2026-09-04: "icons are not gaps between, and no side
   padding or margins", against the reference, whose cells butt together and span the width).

   `width: 100%` IS THE LOAD-BEARING LINE, and removing the padding without it did nothing
   visible. `.ms-catgrid` is a FLEX ITEM of `.ms-block-col`, so its width is content-sized:
   it measured 915 inside a 1288 container with `margin: 0 auto` splitting the remaining 373
   into two 186px bands. Taking the padding off just made it 40px narrower — the band stayed,
   as margin instead of padding. A block-level element would have filled the container on its
   own; a flex item does not, and that is the whole reason this looked unfixable from the
   padding side.

   The block padding stays: it is what separates the row from the slider above and whatever
   follows. Longhands rather than `2.5rem 0`, so a later edit to one cannot reintroduce the
   other. */
.ms-catgrid {
  /* TASK-0078 / DEC-0054: no max-width and no inline centring on a component root.
     This used to cap at var(--ms-page-width), which DUPLICATED the container ladder
     inside a container that is already on it -- measured 1288 inside 1288 at 1440.
     The row's container owns the width now; this element fills it. */
  width: 100%;
  padding-block: 2.5rem;
  padding-inline: 0;
}

.ms-catgrid-heading {
  font-family: var(--ms-font-head, "Montserrat", sans-serif);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.4vw, 1.875rem);
  color: var(--ms-ink);
  text-align: center;
  margin: 0 0 2rem;
}

.ms-catgrid-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  /* EVERY ROW AS TALL AS THE TALLEST (operator, 2026-09-24: the blue hover cell must be one
     height on all rows; a row whose names wrap to two lines was taller than the others). */
  grid-auto-rows: 1fr;
  gap: 0;
}

/* LADDER 6 / 4 / 3 on the product slider's steps (operator, 2026-09-27: "576-767 - keep 3,
   < 576 - also 3 - for testing"). Was 6 / 3 / 2 at 990 / 750. Every step divides the 12
   categories evenly, so no ragged last row. 576 is not a step here: 3 on both sides of it. */
@media (max-width: 991.98px) {
  .ms-catgrid-list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 767.98px) {
  .ms-catgrid-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.ms-catgrid-cell { min-width: 0; position: relative; }

/* ==========================================================================
   THE CORNER CROSSES — read off the reference, not invented.

   SOURCE, named per invariant 10:
     https://mouldshop-co-uk.dw10staging.dynamicweb-cms.com/
       /Files/Templates/Designs/Swift/Assets/css/mouldshop2.css   (the geometry)
       remikujawski/dynamicweb  js/mouldshop.js  `refactor()`     (which cells get one)

   WHAT THE REFERENCE DOES. Each cross is built from FOUR pseudo-elements across TWO
   adjacent cells, and neither cell draws a whole one:

     .openCross::after  { right: 6px;  bottom: -1px; border-bottom: 1px solid #444 }
     .openCross::before { right: -1px; bottom: 6px;  border-right:  1px solid #444 }
     .closeCross::before{ left: 6px;   top: -1px;    border-top:    1px solid #444 }
     .closeCross::after { left: -1px;  top: 6px;     border-left:   1px solid #444 }

   `openCross` sits on the cell ABOVE-LEFT of an intersection and draws the two arms that
   run up and to the left of it; `closeCross` sits on the cell BELOW-RIGHT and draws the two
   that run down and to the right. Together they make one `+` centred on the shared corner.
   Its JS then decides which cells carry which class, from the row count and the size of the
   last row.

   WHAT WE DO INSTEAD, AND WHY IT IS THE SAME PICTURE. One cell draws the whole cross, from
   its own two pseudo-elements, centred on its top-left corner. Every interior corner belongs
   to exactly one cell as its top-left, so one rule covers the grid with no double-drawing
   and nothing to co-ordinate between neighbours:

     a cross at cell (row i, col j)  <=>  i > 0 AND j > 0

   which is `refactor()`'s own `closeCross` condition. Its `openCross` condition exists to
   handle a ragged last row — a cell whose neighbour below is missing must not draw against
   empty space. Reading the two together, the union is exactly "every corner shared by four
   present cells", and the top-left form expresses that directly.

   NO JS. The reference recomputes on resize because it reads the column count at runtime;
   the count here is a fixed ladder (6 / 4 / 3; ruling 6 set 6/3/2), so the arithmetic is a media query and
   an `nth-child` per step — the same technique the card separators use, for the same reason
   (invariant 17: prefer a mechanism with no listener).

   Arms are 15px plus the 1px rule, so the cross measures 31px across, and `#444` is the
   reference's own value. Drawn on the CELL rather than inside the tile, so the hover fill
   cannot cover it. */
.ms-catgrid-cell::before,
.ms-catgrid-cell::after {
  content: none;
  position: absolute;
  background-color: var(--ms-catgrid-cross, var(--ms-rule));
  pointer-events: none;
}

/* the horizontal arm, centred on the cell's top-left corner */
.ms-catgrid-cell::before {
  left: -15px;
  top: 0;
  width: 31px;
  height: 1px;
}

/* the vertical arm, same corner */
.ms-catgrid-cell::after {
  left: 0;
  top: -15px;
  width: 1px;
  height: 31px;
}

/* >= 992: six columns. Not the first row (`n+7`), not the first column (`6n+1`). */
.ms-catgrid-cell:nth-child(n+7):not(:nth-child(6n+1))::before,
.ms-catgrid-cell:nth-child(n+7):not(:nth-child(6n+1))::after {
  content: "";
}

/* 768 - 991.98: four columns. Not the first row (`n+5`), not the first column (`4n+1`). */
@media (max-width: 991.98px) {
  .ms-catgrid-cell:nth-child(n+7):not(:nth-child(6n+1))::before,
  .ms-catgrid-cell:nth-child(n+7):not(:nth-child(6n+1))::after { content: none; }

  .ms-catgrid-cell:nth-child(n+5):not(:nth-child(4n+1))::before,
  .ms-catgrid-cell:nth-child(n+5):not(:nth-child(4n+1))::after { content: ""; }
}

/* < 768: three columns. Not the first row (`n+4`), not the first column (`3n+1`). */
@media (max-width: 767.98px) {
  .ms-catgrid-cell:nth-child(n+5):not(:nth-child(4n+1))::before,
  .ms-catgrid-cell:nth-child(n+5):not(:nth-child(4n+1))::after { content: none; }

  .ms-catgrid-cell:nth-child(n+4):not(:nth-child(3n+1))::before,
  .ms-catgrid-cell:nth-child(n+4):not(:nth-child(3n+1))::after { content: ""; }
}


.ms-catgrid-cellinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  text-align: center;
  height: 100%;
  background-color: rgba(108, 178, 222, 0);
  text-decoration: none;
  transition: transform .35s, box-shadow .35s, background-color .35s;
}
/* THE HOVER STATE IS THE SOURCE OF TRUTH (idle auto-highlight, operator 2026-09-25;
   docs/reference/HERO_ICON_GRID_IDLE_CYCLE.md Contract 1). Every hover rule in this file - this
   one and the three below (tint layer, icon body, name) - is now a selector list, values
   UNCHANGED:
     1. :hover, as it was;
     2. :focus-visible on the cell link - the same look for keyboard users;
     3. the IDLE state (Custom/mouldshop-icon-idle-cycle.js lights one cell at a time), gated
        behind [data-ms-idle-cycle]:not(:hover) on the GRID: the moment the pointer is over the
        grid the auto-highlight drops and only the real hover shows. The script does no styling. */
.ms-catgrid-cellinner:hover,
.ms-catgrid-cellinner:focus-visible,
[data-ms-idle-cycle]:not(:hover) .ms-catgrid-cell.is-idle-active .ms-catgrid-cellinner {
  background-color: #6cb2de;
  color: #fff;
  box-shadow: rgba(50, 50, 93, .25) 0 30px 60px -12px, rgba(0, 0, 0, .3) 0 18px 36px -18px;
}

/* Icon box. THE ICON FILLS THE CELL, and there is no fixed pixel size anywhere in the chain.
   Measured on the live reference at 1440: the icon box and its SVG both render 214.656px
   square, which is exactly the cell width (1288 container / 6 columns, gap 0).

   RETRACTION, AND IT IS ABOUT WHICH NUMBER GETS READ. This block used to claim "128px matches
   the reference's per-paragraph inline style on the 6-column rows". The inline style IS there:

       <div class="icon-auto text-inherit opacity-100" style="height: 128px; width: 128px;">

   and it is OVERRIDDEN. The reference's own stylesheet carries

       .bon-svg a > div > div > div { width: 100% !important; height: 100% !important }

   read off `CSS.getMatchedStylesForNode` on the live page, which is why the computed value is
   214.656 and not 128. 128px is vendor Swift's paragraph field value; Mouldshop overrides it on
   purpose. The declaration was read and the rendered result was not.

   `Q-0040` then measured the same page and reported 228px, which is the SVG's `viewBox` and its
   `width`/`height` ATTRIBUTES, not a rendered size. Three numbers have been in this record for
   one icon - 128, 228, 214.656 - and only the last came off `getBoundingClientRect`.

   `aspect-ratio` rather than a second length: the reference gets its square from the artwork's
   own 1:1 viewBox in an auto-height parent. Stating the ratio keeps the box square if an icon
   file ever ships a non-square viewBox, and keeps the empty-cell box below square too. */
.ms-catgrid-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 1;
  max-width: 100%;
}
.ms-catgrid-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* The two-tone contract. These class names come from inside the operator's icon files. */
.ms-catgrid-icon .bon-svg-lightblue,
.ms-catgrid-icon .bon-svg-darkblue {
  transition: fill .35s, opacity .35s;
}
.ms-catgrid-icon .bon-svg-lightblue { fill: #c4e0f2; }
.ms-catgrid-icon .bon-svg-darkblue  { fill: #0a2979; }

.ms-catgrid-cellinner:hover .ms-catgrid-icon .bon-svg-lightblue,
.ms-catgrid-cellinner:focus-visible .ms-catgrid-icon .bon-svg-lightblue,
[data-ms-idle-cycle]:not(:hover) .ms-catgrid-cell.is-idle-active .ms-catgrid-cellinner .ms-catgrid-icon .bon-svg-lightblue { opacity: 0; }
.ms-catgrid-cellinner:hover .ms-catgrid-icon .bon-svg-darkblue,
.ms-catgrid-cellinner:focus-visible .ms-catgrid-icon .bon-svg-darkblue,
[data-ms-idle-cycle]:not(:hover) .ms-catgrid-cell.is-idle-active .ms-catgrid-cellinner .ms-catgrid-icon .bon-svg-darkblue  { fill: #ffffff; }

/* A cell whose group has no icon keeps the box, so the label baseline stays on the grid and
   the row does not jump. Degrading must not shift the layout. */
.ms-catgrid-icon:empty { display: inline-block; }

/* THE NAME WRAPS, AND IT HAS TO EARN IT RATHER THAN BE TOLD WHERE TO BREAK.
   The reference's titles are hand-typed in Feature paragraphs, so a human broke "Mould /
   Cooling" by pressing return. Ours come from the CATALOGUE TREE - a product group name is one
   string with no line breaks in it - so the break can only come from the layout.

   `display: block` is the load-bearing line. As an inline element in a flex column the name
   shrank to its own text (measured: 90.2px inside a 214.7px cell), so it only wrapped when a
   name was longer than the whole cell. As a block it fills the cell and wraps at the same
   point the reference's `h5` does.

   `text-wrap: balance` is what makes the two lines even rather than one long line and one
   orphan word. A single-word name - "Bikes", "Sale" - stays on one line, because nothing in
   CSS can break a word that has no space in it. If a specific title ever has to break at a
   chosen word, that needs an authored display-name field on the group, not a style. */
.ms-catgrid-name {
  display: block;
  width: 100%;

  /* 12ch, AND IT IS MEASURED, NOT PICKED. `display: block` alone changed nothing visible: at
     the full 214.7px cell every category name on this box still fits on one line, so there was
     nothing for `balance` to balance. Swept at 1440: `none` and 14ch leave "Mould Cooling" on
     one line; 12ch is the widest value at which EVERY two-word name wraps and every one-word
     name does not. 10ch and 9ch do the same but crowd the longer ones for no gain.
     Single words - "Bikes", "Sale", "Components" - stay on one line at any width, because CSS
     cannot break a word with no space in it. */
  max-width: 12ch;
  margin-inline: auto;
  text-wrap: balance;
  font-family: var(--ms-font-head, "Montserrat", sans-serif);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.25;
  color: var(--ms-ink);
  padding: 0 15px 25px;
  transition: color .25s ease-in-out;
}
.ms-catgrid-cellinner:hover .ms-catgrid-name,
.ms-catgrid-cellinner:focus-visible .ms-catgrid-name,
[data-ms-idle-cycle]:not(:hover) .ms-catgrid-cell.is-idle-active .ms-catgrid-cellinner .ms-catgrid-name { color: #fff; }

/* MOBILE / TOUCH (guide §4 "why mobile is separate"): no hover to yield to, so the idle state is
   an INSTANT swap - the same four changes, no transition, no fade. Separate class
   (is-idle-active-mobile) and a faster cycle (1800 / 0), both set by the script. */
@media (max-width: 767.98px), (hover: none) and (pointer: coarse) {
  .ms-catgrid-cell.is-idle-active-mobile .ms-catgrid-cellinner {
    background-color: #6cb2de;
    color: #fff;
    box-shadow: rgba(50, 50, 93, .25) 0 30px 60px -12px, rgba(0, 0, 0, .3) 0 18px 36px -18px;
    transition: none;
  }
  .ms-catgrid-cell.is-idle-active-mobile .ms-catgrid-icon .bon-svg-lightblue { opacity: 0; transition: none; }
  .ms-catgrid-cell.is-idle-active-mobile .ms-catgrid-icon .bon-svg-darkblue  { fill: #ffffff; transition: none; }
  .ms-catgrid-cell.is-idle-active-mobile .ms-catgrid-name { color: #fff; transition: none; }
}
