/* ═══════════════════════════════════════════════════════════════════════
   GBX Circle · THE WALL · one component, every property, every page
   00_canonical is the source. Never edit a copy. `npm run kit` syncs.
   Written 2026-09-22, ruled by Patrick.

   WHY THIS EXISTS. Measured on all six live sites on 2026-09-22, the logo
   wall was four different components wearing one name:

     BILS   a.pw-cell grid, 12 logos, opacity .50, static, hover
     CGTI   .track marquee, 54 logos, opacity .62, scrolling, NO hover
     DDIF   .pw-cell grid, 24 logos, opacity 1.0, static, hover
     MEVO   .wall-tile in a track, 62 logos, opacity .62, scrolling, hover
     AIO    .aio-wall 6 col, 12 logos, opacity 1.0, static, NO hover
     SCLI   bare .wall divs, 36 logos, opacity .62, scrolling, hover

   Three resting opacities, three of six scrolling, two with no hover at all,
   and not one line of it in the kit. Seven inline copies, so there was no
   component to change once. BILS index alone carried four hover rules for the
   same state, targeting .85 and .92, one of them !important. Whichever won,
   won by accident.

   THE CONTRACT. Two shapes, one vocabulary.

     <section class="gbx-wall">
       <div class="gbx-wall-head"> eyebrow, note </div>
       <div class="gbx-wall-grid">
         <a class="gbx-wall-cell" href="…"><img class="gbx-wall-logo" …></a>
       </div>
     </section>

     …or, for a long roll, the rail:

       <div class="gbx-wall-rail" data-gbx-wall-loop>
         <div class="gbx-wall-track"> …cells… </div>
       </div>

   A cell is an <a> when the logo links out and a <div> when it does not.
   Both get the same resting state, the same hover and the same reveal, which
   is the whole point: who-attends and members rendered bare images with no
   hover while index linked every one.

   THE MOTION. Three things, and they are the same on every property.
     rest    white knockout at .5, so a wall reads as one material
     hover   to .92 with the cell ground lifting, 2px rise, gold hairline
     arrive  staggered fade and rise as the wall enters view
   The rail adds a seamless leftward roll that pauses under the pointer.

   REDUCED MOTION is not a lesser version. Everything still arrives, still
   hovers and still reads. Only travel is removed.
   ═══════════════════════════════════════════════════════════════════════ */

/* The knobs live on :root, not on .gbx-wall. Measured on cgti.ai 2026-09-22:
   a rail that is not wrapped in a .gbx-wall section had no --wall-rest and no
   --wall-roll, so `opacity:var(--wall-rest)` computed to the initial 1 and
   `animation:gbx-wall-roll var(--wall-roll) …` was invalid as a whole and
   resolved to none. The logos sat at full opacity and the roll never started.
   BILS looked right only because its section happens to carry the class.
   A component that needs a wrapper to work is a component with a trap in it.
   Every part now stands on its own, and .gbx-wall is for overriding, not for
   switching on. */
:root{
  --wall-rest:.5;
  --wall-hover:.92;
  --wall-lift:2px;
  --wall-in:.72s;
  --wall-roll:64s;
}
.gbx-wall{ padding:var(--sp7, 144px) 0 0; }

/* ── head ─────────────────────────────────────────────────────────── */
.gbx-wall-head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--sp5, 55px); margin-bottom:var(--sp6, 89px);
  padding:0 var(--sp5, 55px);
}
.gbx-wall-kick{
  font-family:var(--sans); font-size:9px; font-weight:300;
  letter-spacing:.22em; text-transform:uppercase; color:var(--gold, #ba9933);
  margin:0;
}
.gbx-wall-note{
  font-family:var(--hv, var(--sans)); font-size:15px; font-weight:300;
  line-height:1.76; letter-spacing:.01618em; max-width:320px; margin:0;
  color:rgba(243,246,249,0.52); text-align:left;
}

/* ── the grid ─────────────────────────────────────────────────────── */
.gbx-wall-grid{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
  gap:var(--sp5, 55px) var(--sp6, 89px);
  padding:var(--sp4, 34px) var(--sp5, 55px) 0;
}

/* ── tiles · the squared variant ──────────────────────────────────────
   BILS, AIO and SCLI set their wall as a six column field of squares with a
   hairline between them. Kept as a variant rather than lost, because it is
   the masterbrand's densest wall and it reads as a room.

   The hairline belongs to the CELL, never to the grid. A grid that paints its
   own background paints every cell a short row leaves empty, and a wall is
   the one component whose count changes every time a partner signs. That
   fault shipped three times this week on other grids before this rule was
   written down. Twelve logos over six columns is clean today and will not be
   clean at thirteen. */
.gbx-wall-grid[data-gbx-wall-tiles]{
  display:grid; grid-template-columns:repeat(6,1fr);
  gap:1px; background:transparent; padding:0 var(--sp5, 55px);
  justify-content:stretch;
}
.gbx-wall-grid[data-gbx-wall-tiles] .gbx-wall-cell{
  aspect-ratio:1/1; padding:var(--sp3, 21px);
  background:var(--stone, #1F2828);
  border:0; box-shadow:0 0 0 .5px rgba(255,255,255,0.04);
}
.gbx-wall-grid[data-gbx-wall-tiles] .gbx-wall-cell:hover,
.gbx-wall-grid[data-gbx-wall-tiles] .gbx-wall-cell:focus-visible{
  background:var(--mid, #222929);
  box-shadow:0 0 0 .5px var(--g2, rgba(186,153,51,0.236));
}
.gbx-wall-grid[data-gbx-wall-tiles] .gbx-wall-logo{ max-width:100%; }
@media (max-width:900px){
  .gbx-wall-grid[data-gbx-wall-tiles]{ grid-template-columns:repeat(3,1fr); padding:0 24px; }
}
@media (max-width:480px){
  .gbx-wall-grid[data-gbx-wall-tiles]{ grid-template-columns:repeat(2,1fr); }
}

/* ── the cell · link or not, it behaves the same ──────────────────── */
.gbx-wall-cell{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  /* wrap is here so an optional .gbx-wall-org stacks under the mark rather
     than beside it. A cell with only a logo never wraps, so nothing changes
     for the other 148 cells in the estate. */
  padding:14px 18px; text-decoration:none; background:transparent;
  border:.5px solid transparent; border-radius:2px;
  transition:background .3s var(--ease, ease), border-color .3s var(--ease, ease),
             transform .45s var(--ease-enter, ease);
}
.gbx-wall-cell:hover,
.gbx-wall-cell:focus-visible{
  background:var(--mid, #222929);
  border-color:var(--g2, rgba(186,153,51,0.236));
  transform:translateY(calc(var(--wall-lift) * -1));
}
.gbx-wall-cell:focus-visible{ outline:2px solid var(--gold, #ba9933); outline-offset:3px; }

/* The mark itself. brightness(0) invert(1) knocks every logo to one white,
   which is the only reason a wall of 62 different brands reads as one
   surface. A logo that must keep its colour carries data-gbx-wall-colour. */
.gbx-wall-logo{
  display:block; width:100%; max-width:140px; height:auto;
  object-fit:contain; min-width:0;
  opacity:var(--wall-rest);
  filter:brightness(0) invert(1);
  transition:opacity .5s var(--ease, ease), filter .5s var(--ease, ease);
}
.gbx-wall-cell:hover .gbx-wall-logo,
.gbx-wall-cell:focus-visible .gbx-wall-logo{ opacity:var(--wall-hover); }
.gbx-wall-logo[data-gbx-wall-colour]{ filter:none; }
.gbx-wall-logo svg{ width:100%; height:32px; }

/* An optional name UNDER the mark, and under is the whole point. The cell is
   a flex row, so a label placed beside a logo that sizes to its container
   collapsed the logo to zero height. Measured on ddif/index 2026-09-23: six
   cells, imgH 0, nothing visible but the caption. flex-basis 100% on the label
   forces the wrap, which stacks them in every browser and leaves a cell with
   no label untouched, since one item never wraps. */
.gbx-wall-org{
  flex:0 0 100%; text-align:center; margin-top:8px;
  font-family:var(--serif); font-size:13px; font-weight:300;
  letter-spacing:.18em; text-transform:uppercase;
  color:rgba(243,246,249,0.52); transition:color .3s var(--ease, ease);
}
.gbx-wall-cell:hover .gbx-wall-org{ color:var(--gold, #ba9933); }

/* ── arrive · staggered, and visible at rest if the script never runs ──
   A wall parked at opacity 0 waiting on an observer is a blank section in a
   screenshot and to a reader with JS off. So the cells are visible by
   default, and only a cell the script has ARMED starts hidden. */
.gbx-wall[data-gbx-wall-armed] .gbx-wall-cell{
  opacity:0; transform:translateY(14px);
  transition:opacity var(--wall-in) var(--ease-enter, ease) var(--wall-d, 0ms),
             transform var(--wall-in) var(--ease-enter, ease) var(--wall-d, 0ms),
             background .3s var(--ease, ease), border-color .3s var(--ease, ease);
}
.gbx-wall[data-gbx-wall-armed] .gbx-wall-cell.gbx-wall-in{
  opacity:1; transform:none;
}

/* ── the rail · a long roll with no seam ──────────────────────────────
   The track holds the cells twice and travels exactly -50%, so the second
   copy lands where the first began. Never de-duplicate it. */
.gbx-wall-rail{
  overflow:hidden; position:relative;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
}
.gbx-wall-track{
  display:flex; align-items:center; gap:var(--sp6, 89px);
  width:max-content; will-change:transform;
  animation:gbx-wall-roll var(--wall-roll) linear infinite;
}
.gbx-wall-rail:hover .gbx-wall-track,
.gbx-wall-rail:focus-within .gbx-wall-track{ animation-play-state:paused; }
.gbx-wall-track .gbx-wall-cell{ flex:0 0 auto; }
.gbx-wall-track .gbx-wall-logo{ max-width:120px; }
@keyframes gbx-wall-roll{ from{ transform:translateX(0) } to{ transform:translateX(-50%) } }

/* ── narrow ───────────────────────────────────────────────────────── */
@media (max-width:900px){
  .gbx-wall{ padding-top:var(--sp6, 89px); }
  .gbx-wall-head{ flex-direction:column; align-items:flex-start; gap:16px;
                  margin-bottom:var(--sp5, 55px); padding:0 24px; }
  .gbx-wall-note{ max-width:100%; }
  .gbx-wall-grid{ gap:24px 34px; padding:var(--sp4, 34px) 24px 0; }
  .gbx-wall-logo{ max-width:104px; }
  .gbx-wall-org{ display:none; }
}
@media (max-width:480px){
  .gbx-wall-grid{ gap:20px 24px; }
  .gbx-wall-logo{ max-width:88px; }
}

/* ── reduced motion · everything still works, nothing travels ─────── */
@media (prefers-reduced-motion:reduce){
  .gbx-wall-track{ animation:none; }
  .gbx-wall-cell{ transition:background .3s, border-color .3s; transform:none; }
  .gbx-wall-cell:hover, .gbx-wall-cell:focus-visible{ transform:none; }
  .gbx-wall[data-gbx-wall-armed] .gbx-wall-cell{
    opacity:1; transform:none; transition:opacity .2s;
  }
}
