/* exhibit.css -- variant A's portfolio: the salon wall's layout, the pinned
 * category bar, and the gallery chrome that has to agree with the wall colour.
 *
 * Owner's call (2026-10-21): one dense wall, not one work per bay. Works of
 * every shape are packed edge to edge in equal-width columns (a salon hang);
 * gallery.js + masonry.js compute each work's place and width, this file
 * only sets the knobs they read. How a canvas, its light and its shadow are
 * drawn is the shared exhibition-board module (shared/canvas-frame.css +
 * .js, classes cf-wall / cf / cf-print; rules: tools/frame-lab/BRIEF.md
 * section 10). Loaded after ring.css so these rules win at equal specificity.
 */

/* The wall. Solid, not a gradient: the overlay scrolls itself, so a gradient
   background would stay put while the works move past it. --wall is the one
   wall colour the board module (--cf-wall defaults to it) and the bar share. */
.gallery { --wall: var(--a-bg); background: var(--wall); }
/* A classic (space-taking) scrollbar that appears only when the wall is
   taller than the screen would narrow the wall, which can make it short
   enough to lose the scrollbar again -- a relayout loop. Reserve its room. */
.gallery { scrollbar-gutter: stable; }

/* ---------- bar: back / categories / book, pinned while the wall scrolls ---------- */
/* Solid down to its last 14px (the labels must stay readable over a moving
   wall), then a short fade so works slide under it rather than being cut. */
.g-bar {
  --g-gut: clamp(18px, 4vw, 48px);   /* = the bar's side padding (ring.css .g-bar) */
  /* minmax(0, auto): the categories take their full width when there is room
     and scroll sideways in the middle when there isn't (7+ rooms at 900px),
     instead of pushing the book button off the bar. */
  display: grid; grid-template-columns: 1fr minmax(0, auto) 1fr; align-items: center;
  column-gap: 1rem; row-gap: 4px;
  background: linear-gradient(180deg, var(--wall) 0%, var(--wall) calc(100% - 14px), color-mix(in srgb, var(--wall) 0%, transparent) 100%);
}
.g-back { justify-self: start; }
/* The way home wears the homepage nav's lockup -- pebble mark, "Bevan
   Photography · Sydney" -- in the same face, weight and size (ring.css
   .brand / .brand-mark / .brand-loc), so the gallery reads as the same site,
   also when a shared #work link skips the homepage. */
.g-back { gap: 0.5em; letter-spacing: -0.01em; line-height: 1.15; }
.g-back .brand-text { text-align: left; }
.g-book { justify-self: end; }
/* With the middle column free to shrink, the two outer 1fr columns fall to
   their min-content -- which for Chinese is one character. Hold the book
   button to one line (the lockup's text already is, ring.css .brand-text)
   so the categories give way, not it. At every width: a zoomed window can
   be 899.x px wide, between the 899px and 900px rules. */
.g-book { white-space: nowrap; }

/* Categories as gallery wayfinding, not chips: the rooms of the show are
   lettered on the wall along one hairline rail, the way a museum signs its
   floors. No fill, no box -- the room you are in carries a dark 2px bar
   laid on the rail and is set heavier, in full ink; the count rides as a
   small plate number. The hover line is the homepage nav's (ring.css
   .nav-link::after), so one gesture means "you can go here" across the
   site; the 2px bar means "you are here". */
.g-filter {
  --cat-px: clamp(10px, 1vw, 14px);   /* side padding of a label: gives way first when rooms are added */
  --cat-py: 8px;             /* 20px line + 2 x 8px = 36px on a mouse; 44px on touch, below */
  display: flex; justify-self: center; min-width: 0; max-width: 100%;
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  padding: 4px; margin-block: -4px;        /* room for the focus ring inside the scroller's clip */
  scroll-padding-inline: 2rem;             /* a chosen label slides in with its neighbour peeking (gallery.js) */
}
.g-filter::-webkit-scrollbar { display: none; }
.g-filter[hidden] { display: none; }
.g-cat {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit; color: var(--a-ink-2);
  position: relative; display: inline-flex; align-items: baseline; flex: none;
  padding: var(--cat-py) var(--cat-px); border-radius: 4px;
  font-size: 0.92rem; line-height: 20px; letter-spacing: 0.08em; white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.2s ease;
}
/* The rule. A border, not a background: it survives forced-colors mode. */
.g-cat::after {
  content: ""; position: absolute; left: var(--cat-px); right: var(--cat-px); bottom: calc(var(--cat-py) - 6px);
  border-top: 1px solid currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.35s var(--ease-out-soft);
}
/* The rail: one hairline under the whole row, drawn by every label so it
   has no gaps (labels sit edge to edge) and scrolls with them. It runs from
   under the first name to under the last count; the bars above sit on it. */
.g-cat::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: calc(var(--cat-py) - 6px);
  border-top: 1px solid var(--a-line);
}
.g-cat:first-child::before { left: var(--cat-px); }
.g-cat:last-child::before { right: var(--cat-px); }
/* Forced colours paint every line in the text colour: the rail steps back
   to GrayText so the 2px bar and the hover line still stand out from it. */
@media (forced-colors: active) {
  .g-cat::before { border-top-color: GrayText; }
}
.g-cat:active { color: var(--a-ink); }
/* Hover only where there is a pointer that hovers: on touch a tap would
   leave the line stuck on the label just left. */
@media (hover: hover) {
  .g-cat:hover { color: var(--a-ink); }
  .g-cat:hover::after { transform: scaleX(1); }
  .g-cat:not([aria-pressed="true"]):hover .g-cat-n { color: var(--a-ink-2); }
}
/* You are here: full ink (count too), medium weight, a 2px bar -- so it
   reads at a glance and never by colour alone; where the face has no 500
   (Microsoft YaHei), the ink count and the bar still set it apart from a
   hovered neighbour. Weight changes the CJK glyphs only (their advance is
   fixed), so nothing beside it moves. */
.g-cat[aria-pressed="true"] { color: var(--a-ink); font-weight: 500; }
.g-cat[aria-pressed="true"]::after { border-top-width: 2px; transform: scaleX(1); }
.g-cat:focus-visible { outline: 2px solid var(--a-ink); outline-offset: 2px; }
/* The count: a plate number set high beside the name, tabular and never
   bold, so the label keeps its width whichever room is chosen. --a-ink-3
   is the lightest grey that still reads 4.8:1 on the wall. */
.g-cat-n {
  position: relative; top: -0.5em; margin-left: 0.3em;
  font-size: 0.72em; font-weight: 400; line-height: 1; letter-spacing: 0; color: var(--a-ink-3); font-variant-numeric: tabular-nums;
}
.g-cat[aria-pressed="true"] .g-cat-n { color: var(--a-ink); }

/* Below 1200px the place drops under the name (as the homepage nav does
   below 480px): beside it, the lockup crowds the categories at 900-1100px. */
@media (max-width: 1199px) {
  .g-back .brand-loc { display: block; margin-top: 1px; font-size: 0.8em; }
  .g-back .brand-sep { display: none; }
  .g-back .brand-mark { height: 2.3em; }
}
/* Not enough room for one line: categories drop to their own row, centred
   on the page axis like the wall title below them. If they ever outgrow
   the screen they start at the gutter instead -- the first name lines up
   with the back arrow -- and scroll sideways; the row runs edge to edge,
   so a label cut by the screen edge says "more this way". */
@media (max-width: 899px) {
  .g-bar { grid-template-columns: 1fr auto; }
  .g-filter {
    --cat-px: 12px;
    grid-row: 2; grid-column: 1 / -1; justify-self: stretch; max-width: none;
    margin-inline: calc(var(--g-gut) * -1); padding-inline: calc(var(--g-gut) - var(--cat-px));
    scroll-padding-inline: calc(var(--g-gut) + 2rem);
  }
  /* Auto margins, not justify-content: center -- they share out free space
     but drop to 0 on overflow, so the first label can't be pushed out of
     reach past the scroller's left edge. */
  .g-cat:first-child { margin-left: auto; }
  .g-cat:last-child { margin-right: auto; }
}
/* Short screens (phones on their side, 200% zoom): one row again -- it fits
   from ~740px -- with the categories scrolling sideways in the middle if
   they must, so the bar doesn't eat a third of the height. Not below 560px
   wide: a short narrow portrait screen (320x480) would squeeze the
   categories to a sliver between the two buttons; it keeps two rows. */
@media (min-width: 560px) and (max-width: 899px) and (max-height: 500px) {
  .g-bar { grid-template-columns: auto minmax(0, 1fr) auto; padding-top: max(env(safe-area-inset-top), 10px); }
  .g-filter { --cat-px: 10px; grid-row: auto; grid-column: auto; justify-self: center; min-width: 0; max-width: 100%; margin-inline: 0; scroll-padding-inline: 2rem; }   /* 10px: five rooms fit at 740px */
}
/* Wherever the row sits between back and book (one-line bar), a long list
   scrolls in place: it fades at both ends instead of cutting a name in
   half. The fade lies in the side padding, so with room to spare it only
   touches empty wall. (The phone row runs off the screen edge instead.)
   "not ... (max-width: 899px)" is the exact complement of the two-row rule,
   so a zoomed 899.x px window gets the fade too. */
@media not all and (max-width: 899px), (min-width: 560px) and (max-height: 500px) {
  .g-filter {
    padding-inline: 12px;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 10px, #000 calc(100% - 10px), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 10px, #000 calc(100% - 10px), transparent);
  }
}

/* Wall text: the exhibition title, then straight onto the wall. */
.gallery .g-head { margin-bottom: clamp(2rem, 5vw, 3.5rem); }

/* ---------- the salon wall ---------- */
.exhibit {
  --col-min: 260px;          /* narrowest column before the wall drops one */
  --cols-max: 4;
  position: relative; list-style: none; margin: 0 auto; padding: 0;
  max-width: 1680px;
  column-gap: clamp(12px, 1.6vw, 28px);   /* the gap between works, across and down; gallery.js reads it */
  transition: opacity 0.18s ease;         /* gallery.js FADE_MS */
}
/* Reduced motion (tokens.css keeps a 0.01ms transition on everything):
   visibility must flip at once, or the back button can't take focus as
   the gallery opens. */
@media (prefers-reduced-motion: reduce) {
  .gallery, .gallery * { transition-property: none !important; }
}

/* Changing category: only the wall (already the stacking context) dims,
   and can't be clicked while it is being rehung. */
.exhibit.is-switching { opacity: 0; pointer-events: none; }

/* Placed by gallery.js with left / top only: no transform, opacity,
   z-index or filter on a work, so the wall (.cf-wall) stays the one
   stacking context and every shadow sits under every canvas. */
.exhibit[data-hang="salon"] > .work {
  --cw: 300px;               /* canvas width, set per work by gallery.js */
  position: absolute; left: 0; top: 0; width: var(--cw); margin: 0;
}
.exhibit[data-hang="salon"] > .work[hidden] { display: none; }
.hang { position: relative; width: 100%; }
.canvas { position: relative; display: block; width: 100%; cursor: zoom-in; }
.canvas:focus-visible { outline: 2px solid var(--a-ink); outline-offset: 6px; }
/* No width/height attributes on the <img>: they would pin its height over
   aspect-ratio and crop the border. --cf-ar (set on .cf by gallery.js) may
   differ from the photo by a few per cent -- masonry.js levels the columns
   with it -- so cover crops evenly. */
.print { display: block; width: 100%; height: auto; aspect-ratio: var(--cf-ar, 1.5); object-fit: cover; }

@media (max-width: 639px) {
  .exhibit { --col-min: 128px; }      /* two columns even on a 320px phone */
}
@media (max-width: 479px) {
  .g-back { font-size: min(1rem, 4.4vw); }   /* = .brand on the homepage */
}
/* Touch targets: every phone and tablet, whichever way it is held. A label
   grows by its padding (20px line + 2 x 12px = 44px), so the name and its
   rule stay put and only the tappable box gets taller. */
@media (max-width: 639px), (pointer: coarse) {
  .g-filter { --cat-py: 12px; }
  .g-back, .g-book { min-height: 44px; }
}
