
/* CHOSEN: A + B combined (DECISIONS.md).
   B alone was four thin name-lists — structured but sparse. A alone was one flat
   A–Z grid — dense but characterless, 150 companies with no sense of what part of
   the park you were in. Combined: the four zones give the page its spine and its
   colour changes, and A's card grid fills each zone so it still reads as a
   directory. Filters cut ACROSS the zones and collapse any band they empty.

   Only the cross-zone filtering behaviour is page-specific; everything visible
   comes from the shared system (.zband .zintro .zlist .ecard .chip .filters
   .empty .ndiv .icowell, and data-lines for the drawn strokes). */

/* a band the active filter has emptied collapses rather than showing a hole */
.zband[hidden]{display:none}
/* the count chip reflects what the filter is actually showing */
.zcount{font-family:var(--f-mono);font-variant-numeric:tabular-nums;
  font-size:12.5px;color:var(--ink-56)}

/* ---- The directory's two views: cards (A · logo tiles) and list (B · logo rows)
   One set of rows, two layouts. The view toggle stamps data-view on #zones and
   the CSS does the rest, so search and the zone chips never care which view is
   showing and 200+ stands are never rendered twice.

   The bug this fixes: .ecard and the zone grid had no rules of their own, so the
   cards stacked flush and each one's stand line sat directly above the NEXT
   card's name. Every row now carries its own chrome, and the stand sits inside
   the row it belongs to.

   Markup per row: .logo (img, or .logo.mono with initials), then .body holding
   .stand (one <b> per stand code, no "Stand" word) and .nm. */
.ecard[hidden]{display:none}
.ecard .nm a{color:inherit;text-decoration:none}
/* the whole row is the link: the name's anchor is stretched over it */
.ecard .nm a::after{content:"";position:absolute;inset:0;border-radius:inherit}
.ecard .nm a:focus-visible{outline:none}
.ecard:has(.nm a:focus-visible){outline:2px solid var(--cream);outline-offset:3px}
.ecard .stand{font-family:var(--f-mono);font-weight:700;font-size:12.5px;letter-spacing:.02em;
  color:var(--accent);font-variant-numeric:tabular-nums}
.ecard .stand b{font-weight:inherit}
.ecard .logo{display:grid;place-items:center;background:var(--logo-well);overflow:hidden}
/* a minmax(0,1fr) track is what gives the image a height to fit: under an auto
   track max-height:100% resolved to nothing and tall logos spilled past the
   padding and were cut off */
.ecard .logo:has(img){grid-template:minmax(0,1fr)/minmax(0,1fr)}
.ecard .logo img{width:100%;height:100%;object-fit:contain}
.ecard .logo.mono{background:var(--navy-700)}
.ecard .logo.mono span{display:grid;place-items:center;font-family:var(--f-display);font-weight:800;
  letter-spacing:-.02em;color:var(--cream)}

/* ---- Zone heading: one compact row, the same in both views ----
   Icon centred on a two-line title (zone number over name), the stand count
   on the right, a hairline under it all. The stands run full width beneath
   rather than sharing the row with a tall intro column, so switching views
   never moves the heading. */
#zones .zband{grid-template-columns:minmax(0,1fr);gap:18px}
.zhead{display:flex;align-items:center;gap:16px;padding-bottom:18px;border-bottom:1px solid var(--ink-hairline)}
.zhead .icowell{width:48px;height:48px;border-radius:14px}
.zhead .zt{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:6px}
.zhead h2{margin:0;font-size:clamp(28px,3.4vw,40px);line-height:1;letter-spacing:-.04em;color:var(--cream)}
.zhead .zcount{white-space:nowrap}
.zhead .zcount b{color:var(--cream);font-size:18px;margin-right:2px}
.zintro > p{margin-top:14px;max-width:60ch}
#zones[data-view="list"] .zintro > p{display:none}

/* zones follow each other as one directory, not four pages */
#zones .zgap{min-height:clamp(72px,10vh,120px)}
#zones .band{padding-block:var(--pad-subband)}

/* ---- Cards (A) ---- */
[data-zgrid]{display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(min(100%,220px),1fr))}
.ecard{position:relative;display:flex;flex-direction:column;min-width:0;overflow:hidden;
  border:1px solid var(--ink-hairline);border-radius:14px;background:var(--navy-900);
  transition:border-color var(--d-fast) var(--e-enter),transform var(--d-fast) var(--e-enter)}
.ecard:hover{border-color:var(--ink-rim);transform:translateY(-2px)}
/* the sponsor tag is a quiet label, not a pill: the sponsor's own full-width
   row already sets it apart */
.ecard .ztag{align-self:flex-start;justify-self:start;margin:0;padding:0;background:none;border-radius:0;
  color:var(--accent);font-family:var(--f-mono);font-size:11px;letter-spacing:.16em}
.ecard .logo{aspect-ratio:16/9;padding:20px 28px;border-bottom:1px solid var(--ink-hairline)}
.ecard .logo.mono span{width:64px;height:64px;border-radius:50%;box-shadow:inset 0 0 0 1px var(--ink-rim);font-size:22px}
.ecard .body{display:flex;flex-direction:column;gap:8px;flex-grow:1;padding:16px 18px 18px}
.ecard .stand b + b::before{content:" · "}
.ecard .nm{margin:0;font-size:18px;line-height:1.18;letter-spacing:-.02em;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

/* ---- List (B) ----
   Plain rows between hairline separators, no per-row chrome: less to read
   past at 200+. The logo keeps the sponsor card's recipe, a white well that
   runs separator to separator, so it reads as a window, not a floating chip. */
#zones[data-view="list"] [data-zgrid]{gap:0 40px;grid-template-columns:repeat(auto-fill,minmax(min(100%,520px),1fr));align-items:stretch}
#zones[data-view="list"] .ecard{flex-direction:row;align-items:stretch;gap:0;min-height:72px;
  border:0;border-bottom:1px solid var(--ink-hairline);border-radius:0;background:transparent}
#zones[data-view="list"] .ecard:hover{transform:none;border-color:var(--ink-hairline);background:rgba(246,241,230,.04)}
#zones[data-view="list"] .ecard .logo{flex:none;width:clamp(96px,22%,132px);aspect-ratio:auto;padding:10px 14px;border-radius:0;
  border-bottom:0} /* the row's own separator runs under the logo; a second one here sat 1px above it */
#zones[data-view="list"] .ecard .logo.mono span{width:40px;height:40px;font-size:15px}
#zones[data-view="list"] .ecard .body{display:grid;grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"tag tag" "nm st";align-items:baseline;align-content:center;gap:4px 16px;padding:12px 18px;flex:1 1 auto;min-width:0}
#zones[data-view="list"] .ecard .ztag{grid-area:tag}
#zones[data-view="list"] .ecard .nm{grid-area:nm;font-size:17px;line-height:1.2;display:block;overflow:visible;overflow-wrap:anywhere}
#zones[data-view="list"] .ecard .stand{grid-area:st;white-space:nowrap}
/* no rule above the first row (the heading's hairline is enough) and none
   below the last, so nothing runs past the logo strip's rounded ends */
#zones[data-view="list"] .zhead{border-bottom:0}
#zones[data-view="list"] .ecard[data-edge~="last"]{border-bottom:0}
/* the logo column reads as one strip per column: round its outer ends
   on the first and last row still showing IN EACH COLUMN. CSS cannot see
   columns, so the directory script stamps data-edge="first|last" after every
   filter, view switch and resize. */
#zones[data-view="list"] .ecard[data-edge~="first"] .logo{border-top-left-radius:12px;border-top-right-radius:12px}
#zones[data-view="list"] .ecard[data-edge~="last"] .logo{border-bottom-left-radius:12px;border-bottom-right-radius:12px}
/* ---- Main sponsor: always its own full-width row, in both views ----
   Sponsors sort first, so the row sits at the top of its zone, alone. */
[data-zgrid] > .ecard.sponsor{grid-column:1 / -1}
/* list view: the sponsor stands apart as its own group, then the rest */
#zones[data-view="list"] .ecard.sponsor{margin-bottom:20px}
#zones:not([data-view="list"]) .ecard.sponsor{flex-direction:row}
#zones:not([data-view="list"]) .ecard.sponsor .logo{flex:none;width:min(40%,360px);border-bottom:0;
  border-right:1px solid var(--ink-hairline)}
#zones:not([data-view="list"]) .ecard.sponsor .body{justify-content:center;gap:10px;padding:24px 28px}
#zones:not([data-view="list"]) .ecard.sponsor .nm{font-size:clamp(22px,2.4vw,30px)}
@media (max-width:640px){
  #zones:not([data-view="list"]) .ecard.sponsor{flex-direction:column}
  #zones:not([data-view="list"]) .ecard.sponsor .logo{width:auto;border-right:0;border-bottom:1px solid var(--ink-hairline)}
}
/* a jump target clears the sticky nav */
.ecard[id]{scroll-margin-top:96px}

@media (max-width:640px){
  [data-zgrid]{grid-template-columns:repeat(auto-fill,minmax(min(100%,150px),1fr))}
}
/* the zone chips' colour dot: the class was used but never defined, which
   left an empty gap before every zone name */
.chip .swatch{width:9px;height:9px;border-radius:var(--r-pill);background:var(--accent);flex:none}
/* results bar: what is showing on the left, how it is shown on the right */
.resbar{max-width:var(--maxw);margin:0 auto;display:flex;align-items:center;justify-content:space-between;
  gap:var(--gap-sm);flex-wrap:wrap;padding-top:var(--gap-sm);border-top:1px solid var(--ink-hairline)}
.resbar .count{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:700;color:var(--ink-56)}
/* ---- Search and filters stay reachable ----
   The controls pin under the fixed nav once the page scrolls past them, on
   phones and desktops alike. The frosted ground only shows while stuck
   (data-stuck, set by the script), so at rest the block sits on the page
   exactly as before. Desktop puts search and chips on one row to keep the
   stuck bar short; phones scroll the chips sideways instead of wrapping. */
.ctl-sticky{position:sticky;top:var(--nav-h,80px);z-index:30;display:grid;gap:12px;padding-block:12px}
/* solid, and reaching up behind the nav: the nav's own ground is a fade, so
   without this the stands scroll visibly through both bars. No fade-in: on a
   reload the browser restores the scroll position with the bar already
   pinned, and a fade let the stands show through it for a moment. */
.ctl-sticky::before{content:"";position:absolute;inset:calc(var(--nav-h,80px) * -1) calc(50% - 50vw) 0;z-index:-1;pointer-events:none;
  background:var(--navy-950);
  border-bottom:1px solid var(--ink-hairline);opacity:0}
.ctl-sticky[data-stuck]::before{opacity:1}
.ctl-sticky > *{margin:0 !important;max-width:none}
.ctl-sticky .filters{padding:0}
.ctl-sticky .resbar{padding-top:10px}
@media (min-width:1024px){
  .ctl-sticky{grid-template-columns:minmax(320px,480px) minmax(0,1fr);align-items:center;column-gap:var(--gap-md)}
  .ctl-sticky .resbar{grid-column:1 / -1}
}
@media (max-width:640px){
  .ctl-sticky{top:var(--nav-h,72px);gap:10px}
  .ctl-sticky .filters{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    margin-inline:calc(var(--gut) * -1) !important;padding-inline:var(--gut)}
  .ctl-sticky .filters::-webkit-scrollbar{display:none}
  .ctl-sticky .filters .chip{flex:none}
  .ctl-sticky .resbar{padding-top:8px}
}

/* the zones live inside the controls' band (a sticky element only stays
   pinned within its parent), so their own bands must not add the page
   gutter a second time */
.band.ctl > #zones > .band,.band.ctl > #zones > .zgap{padding-inline:0}

/* the toggle: two chips joined into one control */
.viewtoggle{display:inline-flex;gap:2px;padding:3px;border-radius:var(--r-pill);
  box-shadow:inset 0 0 0 1.5px var(--ink-hairline)}
.viewtoggle .chip{box-shadow:none;padding:6px 12px}
.viewtoggle .chip[aria-pressed="true"]{background:var(--cream);color:var(--ink-on-light)}
