/* ==========================================================================
   Alan Holland — Wood Turner   |   shared stylesheet, V01
   Linked by every page. Edit here to restyle site-wide.

   Two values are fixed by the brief and everything else is derived from them:
     background  16% brightness gray  -> 0.16 * 255 = 41  -> #292929
     text        92% brightness white -> 0.92 * 255 = 235 -> #ebebeb
   ========================================================================== */

/* Karla carries the whole site for now. The italic axis is included because
   the editable prose fields support *emphasis*, which .prose em renders as
   italic — without it the browser would synthesise a slanted Karla. */
@import url('https://fonts.googleapis.com/css2?family=Karla:ital,wght@0,300..700;1,300..700&display=swap');

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

[hidden] { display: none !important; }

:root {
  /* ---- surfaces ---- */
  --bg:            #292929;   /* 16% */
  --surface:       #303030;
  --surface-hi:    #383838;
  --plinth:        #232323;   /* recessed wells: image mats, code, inputs */
  --line:          #3f3d39;
  --line-soft:     #363433;

  /* ---- type ---- */
  --text:          #ebebeb;   /* 92% */
  --muted:         #9c968c;
  --faint:         #6f6b65;

  /* ---- accent: oil-finished ash under a warm bulb ---- */
  --accent:        #cfa15c;
  --accent-hi:     #e2b978;
  --accent-dim:    rgba(207, 161, 92, 0.14);
  --walnut:        #8c5a2b;

  /* ---- state ---- */
  --available:     #93b070;
  --sold:          #928b82;
  --danger:        #d4695c;

  /* ---- metrics ---- */
  --wrap:          960px;
  --gut:           1.5rem;
  --rule:          1px solid var(--line);

  /* The page title and the header wordmark share one size, so a page title
     can never out-shout the name at the top of the site. Overridden once at
     the narrow breakpoint, which keeps the two locked together there too. */
  --title:         1.3rem;

  /* ---- type scale ---- */
  /* ---- type ----
     Both roles point at Karla for now. The two tokens are kept separate so a
     display face can be reintroduced by changing one line rather than hunting
     down every heading. */
  --display:       'Karla', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --body:          'Karla', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

/* Shared content column. Everything on the site lives inside one of these. */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--gut);
}

main { flex: 1 0 auto; }

img { max-width: 100%; display: block; }

a { color: var(--accent); text-decoration: none; transition: color .18s ease; }
a:hover { color: var(--accent-hi); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ==========================================================================
   Typography
   ========================================================================== */

/* A quiet scale. With the page title pinned to the wordmark's size there is
   little room left below it, so the hierarchy is carried by weight, colour
   and spacing rather than by large jumps in size. */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.015em;
}

h1 { font-size: var(--title); letter-spacing: -0.018em; }
h2 { font-size: 1.1rem; }
h3 { font-size: 1rem; }
h4 { font-size: 0.94rem; }

p + p { margin-top: 1em; }

/* Small tracked-out label. Used for eyebrows, field names, statuses. */
.label {
  font-family: var(--body);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Sits just under the headings, and is told apart by colour and leading
   rather than by size — at this scale a larger lede would compete with the
   page title directly above it. */
.lede {
  font-size: 1rem;
  line-height: 1.7;
  color: #cfcac2;
  max-width: 60ch;
}

.prose { max-width: 66ch; }
.prose p { margin-bottom: 1em; }
.prose p:last-child { margin-bottom: 0; }
.prose em { font-style: italic; color: #d8d3cb; }
.prose strong { font-weight: 700; color: var(--text); }

/* ==========================================================================
   Growth-ring mark — the site's signature

   Concentric rings with an off-centre pith, the way end grain actually looks
   on a piece taken from one side of the trunk. Used as the header mark, the
   section divider, and the gallery's empty/loading state.
   ========================================================================== */

.ah-mark { width: 34px; height: 34px; color: var(--accent); flex: 0 0 auto; }
.ah-mark circle { vector-effect: non-scaling-stroke; }

/* ==========================================================================
   Divider
   ========================================================================== */

/* A plain hairline. Named .divider rather than .ring-rule because it no
   longer carries the mark: the two-line-with-a-glyph version read as fussy. */
.divider {
  height: 1px;
  background: var(--line);
  border: 0;
  margin: 3.25rem 0;
}

/* The header's copy sits flush. The space beneath it is padding on the
   header itself, so it stays inside the sticky box: as a margin it would
   collapse out through .wrap, ending the header's background at the rule and
   leaving the mobile nav to anchor too high. */
.divider-head { margin: 0; }

/* ==========================================================================
   Header
   ========================================================================== */

/* No border-bottom: the ring divider rendered inside the header is its
   bottom edge. The header still paints a solid background, so content
   scrolling past is covered even though the divider is inset to the content
   column rather than running the full width of the viewport. */
header.site-head {
  background: var(--bg);
  padding-bottom: 0.9rem;
  position: sticky;
  top: 0;
  z-index: 60;
}

.head-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 76px;
  padding-top: 0.75rem;
  padding-bottom: 0.5rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  color: var(--text);
}
.brand:hover { color: var(--text); }
.brand:hover .ah-mark { color: var(--accent-hi); }
.brand .ah-mark { transition: color .18s ease; }

.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-name {
  font-family: var(--display);
  font-size: var(--title);
  font-weight: 600;
  letter-spacing: -0.012em;
}
.brand-craft {
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--accent);
  margin-top: 0.28rem;
}

nav.site-nav { display: flex; align-items: center; gap: 1.9rem; }

.nav-link {
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0.35rem 0;
  border-bottom: 1px solid transparent;
}
.nav-link:hover { color: var(--text); }
.nav-link[aria-current="page"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  width: 42px;
  height: 38px;
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.nav-toggle:hover { border-color: var(--accent); color: var(--accent); }
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  display: block;
  width: 18px;
  height: 1.5px;
  background: currentColor;
  transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle span { position: relative; }
.nav-toggle span::before { content: ''; position: absolute; top: -6px; }
.nav-toggle span::after  { content: ''; position: absolute; top:  6px; }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after  { transform: translateY(-6px) rotate(-45deg); }

/* ==========================================================================
   Footer
   ========================================================================== */

footer.site-foot {
  flex: 0 0 auto;
  border-top: var(--rule);
  margin-top: 4rem;
  padding: 1.5rem 0;
  font-size: 0.78rem;
  color: var(--faint);
}

.foot-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.25rem;
  justify-content: space-between;
}

/* The admin chain — only rendered when a session cookie is present. */
.foot-admin { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
.foot-admin .sep { color: var(--line); }
.admin-flag {
  color: var(--accent);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 0.68rem;
}
.foot-admin button.linkish {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--faint);
  cursor: pointer;
  transition: color .18s ease;
}
.foot-admin button.linkish:hover { color: var(--accent); }

/* ==========================================================================
   Buttons and form controls
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--body);
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.62rem 1.2rem;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease, background .18s ease;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn-primary { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }
.btn-primary:hover { background: var(--accent); color: #1c1c1c; }
.btn-danger:hover { border-color: var(--danger); color: var(--danger); }
.btn-sm { padding: 0.4rem 0.8rem; font-size: 0.68rem; }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }

.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field > label { font-size: 0.68rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }

.input, .textarea, .select {
  width: 100%;
  background: var(--plinth);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: var(--body);
  font-size: 0.92rem;
  font-weight: 300;
  padding: 0.58rem 0.7rem;
  border-radius: 0;
  outline: none;
  transition: border-color .18s ease;
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--accent); }
.textarea { line-height: 1.6; resize: vertical; min-height: 7rem; }
.select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2rem;
}

/* ==========================================================================
   Status pill — available / sold
   ========================================================================== */

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.28rem 0.62rem;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.status::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.status-available { color: var(--available); }
.status-sold      { color: var(--sold); }
.status-sold::before { background: transparent; box-shadow: inset 0 0 0 1px currentColor; }

/* ==========================================================================
   Home
   ========================================================================== */

/* Vertical padding is set with longhands throughout this sheet, never the
   shorthand. Anything paired with .wrap that writes `padding: 3rem 0` has the
   same specificity as .wrap's own `padding: 0 var(--gut)` and, coming later,
   silently resets the horizontal gutter to zero — which pushes the title and
   lede out to the edge while the sections under them stay inset. */
.hero { padding-top: 3.5rem; padding-bottom: 1rem; }

/* One column until home.js finds something currently showing, then an even
   split with the venue list beside the intro, separated by a vertical rule.
   The rule is its own 1px grid column rather than a border, so it can run the
   full height of the taller side while both columns still start at the top. */
.hero-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
}
.hero-split.is-split {
  grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr);
  gap: 2rem;
}

/* Hidden while the hero is a single column — a divider with nothing on the
   far side of it is just a stray line. */
.hero-split > .divider-v { display: none; }
.hero-split.is-split > .divider-v {
  display: block;
  align-self: stretch;   /* overrides align-items:start, which would
                            collapse it to nothing */
  width: 1px;
  height: auto;
  margin: 0;
}

/* In the narrow column the caps that shape the full-width intro would fight
   the grid, so they only apply while the hero is a single column. */
.hero-split.is-split .hero-text h1,
.hero-split.is-split .hero-text .lede { max-width: none; }

/* Nudges the venue heading onto the same optical line as the much larger
   page title beside it. */
.hero-showing { padding-top: 0.15rem; }
/* The cap is in ch, so it tracks the title size rather than a pixel guess.
   At the old 2.8rem headline 16ch was a sensible two-line measure; at the
   current size it would break the line every few words. */
.hero h1 { max-width: 30ch; }
.hero h1 em {
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.hero .lede { margin-top: 1.6rem; }
.hero-actions { margin-top: 2.25rem; display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* Three-up teaser of recent pieces on the home page. */
.home-recent { margin-top: 1rem; }
.home-recent-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

/* ==========================================================================
   Home: currently showing
   ========================================================================== */

/* A list of rows rather than cards: this is a signpost to the exhibition
   page, not a second copy of it, so it stays deliberately plain. */
.showing-row {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.85rem 0;
  border-bottom: var(--rule);
  color: var(--text);
}
.showing-row:first-child { border-top: var(--rule); }
.showing-row:hover { color: var(--accent); }

.showing-name { font-weight: 500; }
.showing-where {
  flex: 1 1 auto;
  font-size: 0.86rem;
  color: var(--faint);
}
.showing-row .arr { color: var(--faint); transition: transform .18s ease; }
.showing-row:hover .arr { color: var(--accent); transform: translateX(3px); }

/* ==========================================================================
   Work gallery
   ========================================================================== */

.page-head { padding-top: 2.25rem; }
.page-head h1 { margin-bottom: 0.75rem; }

.gallery-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 2rem 0 1.75rem;
  padding-bottom: 1rem;
  border-bottom: var(--rule);
}
.filters { display: flex; gap: 0.4rem; }
.filter {
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  font-family: var(--body);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.4rem 0.75rem;
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease;
}
.filter:hover { color: var(--text); }
.filter[aria-pressed="true"] { color: var(--accent); border-color: var(--line); }
.count { font-size: 0.72rem; color: var(--faint); letter-spacing: 0.1em; }

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}
.grid-3 { grid-template-columns: repeat(3, 1fr); }

.piece-card { display: block; color: var(--text); }
.piece-card:hover { color: var(--text); }

/* Square, because every uploaded photograph is square. object-fit: cover
   still guards the case of an odd file slipping in — it crops rather than
   distorting, and the grid keeps its rhythm.

   display:block is load-bearing, not tidiness. The card is assembled from
   <span>s so the whole thing can sit inside a single <a>, and aspect-ratio
   is ignored on a non-replaced inline box — without this the frame has no
   ratio at all and collapses onto the image's own proportions. */
.piece-card-frame {
  display: block;
  position: relative;
  background: var(--plinth);
  border: 1px solid var(--line-soft);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  transition: border-color .22s ease;
}
.piece-card:hover .piece-card-frame { border-color: var(--accent); }
.piece-card-frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform .5s cubic-bezier(.2,.7,.3,1), opacity .3s ease;
}
.piece-card:hover .piece-card-frame img { transform: scale(1.035); }

/* Sold pieces read one notch quieter in the grid without being hidden. */
.piece-card.is-sold .piece-card-frame img { opacity: 0.72; }
.piece-card.is-sold:hover .piece-card-frame img { opacity: 1; }

.piece-card-tag {
  position: absolute;
  top: 0.65rem; left: 0.65rem;
  background: rgba(20, 20, 20, 0.82);
  backdrop-filter: blur(3px);
}

/* Stand-in for a piece that has no photograph yet. */
.no-img {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
  min-height: 120px;
  background:
    repeating-linear-gradient(
      100deg,
      rgba(255,255,255,0.012) 0 7px,
      transparent 7px 15px
    ),
    var(--plinth);
}
.no-img .ah-mark { width: 40%; max-width: 88px; height: auto; color: var(--line); }

/* Also blockified for the reason above: vertical padding and margins have no
   layout effect on an inline box, and the title and meta have to stack. */
.piece-card-body { display: block; padding-top: 0.85rem; }
.piece-card-title {
  display: block;
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.piece-card:hover .piece-card-title { color: var(--accent); }
.piece-card-meta { display: block; font-size: 0.78rem; color: var(--faint); margin-top: 0.2rem; }

/* Empty / loading state for any gallery-ish list. */
.state {
  padding: 4rem 1rem;
  text-align: center;
  color: var(--faint);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
.state .ah-mark { width: 48px; height: 48px; color: var(--line); }
/* A state block dropped into a grid should span it, not take one cell. */
.grid > .state { grid-column: 1 / -1; }
.state.is-loading .ah-mark { animation: ah-turn 2.6s linear infinite; }
@keyframes ah-turn { to { transform: rotate(360deg); } }

/* ==========================================================================
   Single piece page
   ========================================================================== */

.piece-layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 2.5rem;
  align-items: start;
  padding-top: 2.5rem;
}

/* Square to match the source images. Left at 4:3 this would letterbox every
   photograph with permanent bars either side. object-fit stays 'contain' so
   a non-square file is shown whole rather than cropped — the main image is
   the one place a crop would actually lose something. */
.piece-stage {
  background: var(--plinth);
  border: 1px solid var(--line-soft);
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: zoom-in;
}
.piece-stage img { width: 100%; height: 100%; object-fit: contain; }

.piece-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.75rem;
}
.piece-thumb {
  width: 72px; height: 72px;
  padding: 0;
  background: var(--plinth);
  border: 1px solid var(--line-soft);
  cursor: pointer;
  overflow: hidden;
  transition: border-color .18s ease, opacity .18s ease;
  opacity: 0.7;
}
.piece-thumb img { width: 100%; height: 100%; object-fit: cover; }
.piece-thumb:hover { opacity: 1; }
.piece-thumb[aria-current="true"] { border-color: var(--accent); opacity: 1; }

.piece-info h1 { margin-bottom: 0.4rem; }
.piece-sub { color: var(--faint); font-size: 0.86rem; margin-bottom: 1.25rem; }
.piece-status-row { margin-bottom: 1.5rem; }

.spec-list { border-top: var(--rule); margin: 1.5rem 0; }
.spec-row {
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  padding: 0.6rem 0;
  border-bottom: var(--rule);
  font-size: 0.88rem;
}
.spec-row dt { color: var(--muted); font-size: 0.68rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; padding-top: 0.2rem; }
.spec-row dd { text-align: right; }

.buy-box {
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 1.15rem;
  margin-top: 1.5rem;
}
.buy-box .label { display: block; margin-bottom: 0.85rem; }
.buy-links { display: flex; flex-direction: column; gap: 0.5rem; }
.buy-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  border: 1px solid var(--line);
  padding: 0.6rem 0.85rem;
  color: var(--text);
  font-size: 0.88rem;
  transition: border-color .18s ease, color .18s ease;
}
.buy-link:hover { border-color: var(--accent); color: var(--accent); }
.buy-link .arr { color: var(--faint); }
.buy-link:hover .arr { color: var(--accent); }
.buy-note { font-size: 0.82rem; color: var(--faint); }

/* Share — sits under the buy box but reads a step below it, so the buy links
   keep the emphasis. */
.share-row { margin-top: 0.6rem; }

.btn-quiet {
  width: 100%;
  background: none;
  border-color: var(--line-soft);
  color: var(--muted);
}
.btn-quiet:hover { border-color: var(--accent); color: var(--accent); }
.btn-quiet.is-copied {
  border-color: var(--available);
  color: var(--available);
}

/* The copied address, shown briefly. Doubles as the fallback when a browser
   refuses the copy: it can still be selected by hand. */
.share-status {
  display: block;
  margin-top: 0.45rem;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--faint);
  word-break: break-all;
  user-select: all;
}
.share-status:empty { margin-top: 0; }

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 2.5rem;
}
.back-link:hover { color: var(--accent); }

/* ---- lightbox ---- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(14, 14, 14, 0.94);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 4rem;
  cursor: zoom-out;
}
.lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; }
.lightbox-close, .lightbox-prev, .lightbox-next {
  position: absolute;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  width: 44px; height: 44px;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color .18s ease, color .18s ease;
}
.lightbox-close:hover, .lightbox-prev:hover, .lightbox-next:hover { border-color: var(--accent); color: var(--accent); }
.lightbox-close { top: 1.25rem; right: 1.25rem; }
.lightbox-prev  { left: 1.25rem;  top: 50%; transform: translateY(-50%); }
.lightbox-next  { right: 1.25rem; top: 50%; transform: translateY(-50%); }
.lightbox-caption {
  position: absolute;
  bottom: 1.25rem; left: 50%;
  transform: translateX(-50%);
  font-size: 0.76rem;
  color: var(--faint);
  letter-spacing: 0.1em;
}

/* ==========================================================================
   Contact
   ========================================================================== */

.contact-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 3rem;
  padding-top: 2.5rem;
}
.contact-lines { border-top: var(--rule); }
.contact-line {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.9rem 0;
  border-bottom: var(--rule);
}
.contact-line .label { font-size: 0.64rem; }
.contact-line .val { font-size: 1rem; }

/* Inside a contact row the chips sit tighter to their label than they do on
   an exhibition card, where they follow a paragraph. */
.contact-line .chip-row { margin-top: 0.4rem; }

/* ==========================================================================
   Inline page editing (admin only)
   ========================================================================== */

body.editing [data-ah-field] {
  outline: 1px dashed var(--accent);
  outline-offset: 6px;
  cursor: text;
  position: relative;
}
body.editing [data-ah-field]:hover { outline-style: solid; }

.edit-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 150;
  background: var(--surface);
  border-top: 1px solid var(--accent);
  padding: 0.75rem 0;
}
.edit-bar-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  justify-content: space-between;
}
.edit-bar-msg { font-size: 0.8rem; color: var(--muted); }
.edit-bar-actions { display: flex; gap: 0.5rem; }
body.editing { padding-bottom: 4.5rem; }

.ah-editing-input {
  width: 100%;
  background: var(--plinth);
  border: 1px solid var(--accent);
  color: var(--text);
  font: inherit;
  padding: 0.5rem 0.65rem;
  line-height: inherit;
  resize: vertical;
}

/* ==========================================================================
   About: bio picture
   ========================================================================== */

/* Title, lede and body all sit in the left column so the picture starts level
   with the heading. An even split gives the picture half the content column;
   the gap means each side is a shade under half in absolute terms. */
.bio-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
  padding-top: 2.25rem;   /* matches .page-head, so About opens level with
                             every other page */
}

.bio-text > h1 { margin-bottom: 0.75rem; }
.bio-text > .lede { margin-bottom: 1.5rem; }

.bio-stage {
  background: var(--plinth);
  border: 1px solid var(--line-soft);
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: zoom-in;
}
.bio-stage img { width: 100%; height: 100%; object-fit: cover; }

/* A grid rather than a flex row, so the strip always ends flush with the
   stage above it instead of leaving a ragged right edge. The column count is
   set inline by about.js from the number of pictures, capped so a long set
   doesn't shrink to postage stamps. */
.bio-thumbs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.6rem;
  margin-top: 0.6rem;
}
.bio-thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  padding: 0;
  background: var(--plinth);
  border: 1px solid var(--line-soft);
  cursor: pointer;
  overflow: hidden;
  opacity: 0.7;
  transition: border-color .18s ease, opacity .18s ease;
}
.bio-thumb img { width: 100%; height: 100%; object-fit: cover; }
.bio-thumb:hover { opacity: 1; }
.bio-thumb[aria-current="true"] { border-color: var(--accent); opacity: 1; }

/* The in-place picture manager, shown only while editing the page. */
.bio-editor {
  border: 1px solid var(--accent);
  background: var(--surface);
  padding: 1rem;
}

/* ==========================================================================
   Exhibition
   ========================================================================== */

.ex-group { margin-bottom: 3rem; }
.ex-group:last-child { margin-bottom: 0; }
.ex-group > h2 {
  padding-bottom: 0.75rem;
  border-bottom: var(--rule);
  margin-bottom: 1.5rem;
}

.ex-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem;
}

.ex-card {
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 1.25rem;
  /* Clears the sticky header when arrived at via an anchor from the home
     page, so the card is not tucked underneath it. */
  scroll-margin-top: 7rem;
}

/* Marks the card a home-page link pointed at, so it is obvious which of
   several venues was meant. */
.ex-card:target { border-color: var(--accent); }

/* Past venues stay legible but stop competing with somewhere you could
   actually go this week. */
.ex-past { opacity: 0.62; }
.ex-past:hover { opacity: 1; }

.ex-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.ex-name { margin: 0; }

.ex-tag {
  flex: 0 0 auto;
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.2rem 0.5rem;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.ex-current  { color: var(--available); }
.ex-upcoming { color: var(--accent); }

.ex-dates {
  font-size: 0.86rem;
  color: var(--accent);
  margin-bottom: 0.4rem;
}
.ex-address {
  font-size: 0.9rem;
  font-style: normal;      /* <address> italicises by default */
  color: var(--muted);
  line-height: 1.5;
  margin-bottom: 0.6rem;
}

/* Hours read as a labelled aside rather than another paragraph of prose. */
.ex-hours {
  font-size: 0.86rem;
  color: var(--muted);
  white-space: pre-line;   /* several typed lines keep their breaks */
  margin-bottom: 0.6rem;
}
.ex-hours-label {
  display: block;
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.15rem;
}
.ex-notes {
  font-size: 0.88rem;
  color: #cfcac2;
  margin-bottom: 0.75rem;
}

/* Contact routes as a wrapping row of chips. Only what the venue actually
   has is rendered, so a place with nothing but an address shows a single
   Maps chip rather than a row of empty slots.

   .chip-row / .chip are the generic names; .ex-links / .ex-link are the
   exhibition page's originals, kept so that page needs no change. */
.ex-links,
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.9rem;
}
.ex-link,
.chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  padding: 0.32rem 0.6rem;
  font-size: 0.76rem;
  color: var(--muted);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: border-color .18s ease, color .18s ease;
}
.ex-link:hover,
.chip:hover { border-color: var(--accent); color: var(--accent); }

/* The map chip leads, so it carries the accent from the start. */
.ex-link:first-child { color: var(--accent); border-color: var(--accent-dim); }
.ex-link:first-child:hover { border-color: var(--accent); }

/* The derived-values panel in the exhibition manager. */
.ex-preview .spec-row { font-size: 0.82rem; }

/* ==========================================================================
   Admin: work manager
   ========================================================================== */

.admin-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: var(--rule);
  margin-bottom: 1.5rem;
}

/* Heading and save state share a baseline, so the note reads as an aside to
   the title rather than a second line of page furniture. */
.admin-title {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.admin-title .count { color: var(--accent); }

.admin-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* Tabs between the two admin screens. */
.admin-tabs {
  display: flex;
  gap: 1.5rem;
  margin: -0.5rem 0 1.75rem;
}
.admin-tabs a {
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding-bottom: 0.4rem;
  border-bottom: 1px solid transparent;
}
.admin-tabs a:hover { color: var(--text); }
.admin-tabs a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.admin-layout {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 2rem;
  align-items: start;
  padding-bottom: 3rem;
}

.work-list { border: 1px solid var(--line); background: var(--surface); }

/* The row is a container, not a control: the title is a button inside it so
   the row itself is free to handle dragging without fighting click semantics. */
.work-list-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--line-soft);
  color: var(--text);
  font-size: 0.86rem;
  background: none;
  transition: background .16s ease, opacity .16s ease;
}
.work-list-item:last-child { border-bottom: none; }
.work-list-item:hover { background: var(--surface-hi); }
.work-list-item[aria-current="true"] { background: var(--accent-dim); color: var(--accent); }

.work-list-pick {
  flex: 1 1 auto;
  min-width: 0;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.work-list-item .dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }
.work-list-item .dot.available { background: var(--available); }
.work-list-item .dot.sold { background: transparent; box-shadow: inset 0 0 0 1px var(--sold); }

/* The grip is the only part with touch-action:none, so a finger dragged
   anywhere else on the list still scrolls the page. On a mouse the whole row
   is draggable, which is what the pointer type check in manage.js decides. */
.work-list-item .grip {
  flex: 0 0 auto;
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1;
  letter-spacing: 0.08em;
  padding: 0.2rem 0.1rem;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.work-list-item:hover .grip { color: var(--muted); }

/* While a drag is live the row travels with the pointer's insertion point,
   so it reads as a gap being carried rather than a copy being dropped. */
.work-list-item.dragging {
  opacity: 0.55;
  background: var(--accent-dim);
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}
.work-list.is-dragging { cursor: grabbing; }
.work-list.is-dragging .grip { cursor: grabbing; }
.work-list.is-dragging .work-list-item:hover { background: none; }
.work-list.is-dragging .work-list-item.dragging:hover { background: var(--accent-dim); }
.work-list.is-dragging .work-list-item[aria-current="true"] { background: var(--accent-dim); }

/* Suppresses text selection across the page mid-drag. */
body.is-reordering { user-select: none; -webkit-user-select: none; }

.list-hint {
  font-size: 0.72rem;
  color: var(--faint);
  line-height: 1.5;
  margin-top: 0.6rem;
}

/* The line under the short-name field: mode on the first row, the resulting
   address on its own row beneath it. */
.slug-note {
  font-size: 0.7rem;
  color: var(--faint);
  line-height: 1.5;
}
.slug-note .slug-addr {
  display: block;
  margin-top: 0.15rem;
  color: var(--faint);
  word-break: break-all;
}
.slug-note .slug-addr strong { color: var(--accent); font-weight: 500; }

/* A button that reads as a link. Shared by the slug note and the footer. */
button.linkish {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  transition: color .18s ease;
}
button.linkish:hover { color: var(--accent); }

.editor { border: 1px solid var(--line); background: var(--surface); padding: 1.35rem; }
.editor-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.editor-grid .span-2 { grid-column: 1 / -1; }
.editor h2 { margin-bottom: 1.1rem; }
.editor-section { margin-top: 1.5rem; padding-top: 1.25rem; border-top: var(--rule); }
.editor-section > .label { display: block; margin-bottom: 0.85rem; }

.img-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
.img-row .thumb {
  width: 52px; height: 52px;
  background: var(--plinth);
  border: 1px solid var(--line);
  object-fit: cover;
  flex: 0 0 auto;
}
.img-row .input { font-size: 0.8rem; }

.link-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; }
.link-row .input:first-child { max-width: 180px; }

.drop-zone {
  border: 1px dashed var(--line);
  padding: 1.1rem;
  text-align: center;
  color: var(--faint);
  font-size: 0.82rem;
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}
.drop-zone:hover, .drop-zone.over { border-color: var(--accent); color: var(--accent); }

.toast {
  position: fixed;
  right: 1.25rem; bottom: 1.25rem;
  z-index: 300;
  background: var(--surface);
  border: 1px solid var(--accent);
  color: var(--text);
  padding: 0.7rem 1rem;
  font-size: 0.84rem;
  box-shadow: 0 6px 24px rgba(0,0,0,.45);
}
.toast.err { border-color: var(--danger); color: var(--danger); }

/* ==========================================================================
   Login
   ========================================================================== */

.login-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 4rem;
  padding-bottom: 2rem;
  gap: 1.75rem;
}
.login-wrap .ah-mark { width: 64px; height: 64px; }
.login-box {
  width: 100%;
  max-width: 340px;
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.login-err {
  font-size: 0.78rem;
  color: var(--danger);
  display: none;
}
.login-err.on { display: block; }

/* ==========================================================================
   Responsive — the 960px column collapses in three steps
   ========================================================================== */

@media (max-width: 860px) {
  .piece-layout { grid-template-columns: 1fr; gap: 1.75rem; }
  .bio-layout { grid-template-columns: 1fr; gap: 1.75rem; }
  /* Stacked, a half-width square becomes a full-width one — far too
     dominant on a tablet, so the picture column is capped. */
  .bio-media { max-width: 460px; }
  .contact-grid { grid-template-columns: 1fr; gap: 2rem; }
  .admin-layout { grid-template-columns: 1fr; }
  /* The venue list drops below the intro rather than beside it, and the
     rule turns back into an ordinary horizontal one between them. */
  .hero-split.is-split { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .hero-split.is-split > .divider-v {
    width: auto;
    height: 1px;
    align-self: auto;
  }
  .hero-showing { padding-top: 0; }
}

@media (max-width: 720px) {
  :root { --gut: 1.15rem; }

  .ex-list { grid-template-columns: 1fr; }

  .nav-toggle { display: inline-flex; }

  nav.site-nav {
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--bg);
    border-bottom: var(--rule);
    padding: 0.5rem var(--gut) 1rem;
    display: none;
  }
  nav.site-nav.open { display: flex; }
  .nav-link {
    padding: 0.8rem 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .nav-link[aria-current="page"] { border-bottom-color: var(--accent); }

  /* No position:relative here on purpose. The sticky header is itself a
     positioned element, so the dropdown anchors to that instead and opens
     below the ring divider rather than on top of it. */
  .head-inner { min-height: 64px; }

  .grid, .grid-3 { grid-template-columns: repeat(2, 1fr); gap: 1.1rem; }

  .hero { padding-top: 2.25rem; padding-bottom: 0.5rem; }

  .foot-inner { flex-direction: column; align-items: flex-start; }

  .lightbox { padding: 3.5rem 1rem; }
  .lightbox-prev { left: 0.5rem; }
  .lightbox-next { right: 0.5rem; }

  .editor-grid { grid-template-columns: 1fr; }
  .link-row { flex-wrap: wrap; }
  .link-row .input:first-child { max-width: none; }
}

@media (max-width: 460px) {
  :root { --title: 1.15rem; }

  .showing-row { flex-wrap: wrap; gap: 0.15rem 1rem; }
  .showing-where { flex-basis: 100%; }

  .grid, .grid-3 { grid-template-columns: 1fr; }
  .brand .ah-mark { width: 28px; height: 28px; }
  .piece-thumb { width: 58px; height: 58px; }
  .spec-row { flex-direction: column; gap: 0.15rem; }
  .spec-row dd { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
