:root {
  --gallery-accent: #d47b72;
  /* One rail row, and the one place its height is stated: the rail's closed form reads this rather than
     carrying a second copy of it in JavaScript. A row is its card's name and nothing else — the size and
     summary it used to carry are the Landing's now — so the row is as tall as one line of it. */
  --gallery-row-height: 36px;
  color-scheme: dark;
  color: var(--lab-text);
  background: var(--lab-bg);
  font-family: Inter, ui-sans-serif, system-ui, sans-serif;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { min-height: 100%; margin: 0; }
body { background: var(--lab-bg-deep); }
button, input, select { font: inherit; }
button, select { cursor: pointer; }
button, input, select { border: 1px solid var(--lab-border-strong); border-radius: 5px; color: var(--lab-text); background: var(--lab-bg-deep); }
button { padding: 7px 10px; }
input, select { min-width: 0; padding: 8px; }
button:hover, select:hover { border-color: var(--lab-muted); color: #edf0f4; }
:focus-visible { outline: 2px solid var(--lab-focus); outline-offset: 3px; }
.gallery-app { height: 100dvh; min-height: 640px; display: flex; flex-direction: column; }
.gallery-header { display: flex; align-items: center; justify-content: space-between; gap: 18px; min-height: 64px; padding: 10px 20px; border-bottom: 1px solid var(--lab-border); background: #242830; }
.gallery-brand { display: flex; align-items: center; gap: 11px; }
.gallery-mark { display: grid; place-items: center; width: 32px; height: 32px; border: 1px solid var(--gallery-accent); border-radius: 50%; color: var(--gallery-accent); font-weight: 800; }
.gallery-brand strong { display: block; color: #f0f2f4; font-size: 15px; }
.gallery-brand span { display: block; margin-top: 3px; color: var(--lab-muted); font-size: 11px; }
.gallery-nav { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.gallery-nav a { padding: 5px 7px; border: 1px solid transparent; border-radius: 4px; color: var(--lab-muted); font-size: 11px; text-decoration: none; }
.gallery-nav a:hover, .gallery-nav a:focus-visible { border-color: var(--lab-border-strong); color: var(--lab-text); background: var(--lab-panel); }
.gallery-workspace { flex: 1; min-height: 0; display: grid; grid-template-columns: 280px minmax(0, 1fr); }
.gallery-browser { min-height: 0; overflow-y: auto; padding: 20px 14px; border-right: 1px solid var(--lab-border); background: #242830; scrollbar-gutter: stable; }
.gallery-browser__heading { display: flex; align-items: center; justify-content: space-between; margin: 0 4px 18px; }
.gallery-browser h1 { margin: 0; font-size: 17px; color: #edf0f4; font-weight: 600; }
#library-count { color: var(--lab-muted); font-size: 11px; }
.gallery-search, .gallery-filter { display: grid; gap: 7px; margin: 0 4px 14px; color: var(--lab-muted); font-size: 11px; }
.gallery-search input, .gallery-filter select { width: 100%; font-size: 12px; }
/* The rail. Rows are absolutely placed at index × row height inside a scroller whose height comes from a
   sizer, so the scrollbar describes the whole list while only the rows the scroller reaches exist. */
.gallery-rail { display: grid; gap: 6px; margin: 20px 0; }
.gallery-rail__all { width: 100%; min-height: 34px; text-align: left; font-size: 12px; border-color: transparent; background: transparent; }
.gallery-rail__hint { margin: 0 4px; }
.gallery-rail__scroller { position: relative; height: clamp(180px, 34vh, 440px); overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-gutter: stable; }
.gallery-rail__sizer { width: 1px; }
.gallery-rail__rows { position: absolute; top: 0; left: 0; right: 0; height: 0; }
.gallery-rail__row { position: absolute; left: 0; right: 0; display: flex; align-items: center; gap: 4px; height: var(--gallery-row-height); padding: 3px 0; }
.gallery-rail__cell { display: flex; align-items: center; min-width: 0; }
.gallery-rail__cell--open { flex: 1; min-width: 0; }
.gallery-rail__cell--delete { flex: 0 0 auto; }
.gallery-rail__scroller button, .gallery-rail__all { min-height: 26px; text-align: left; font-size: 12px; border-color: transparent; background: transparent; }
.gallery-rail__scroller button { overflow: hidden; padding: 4px 8px; white-space: nowrap; text-overflow: ellipsis; }
.gallery-rail__scroller button.gallery-rail__delete { padding: 4px 5px; color: #e39a91; font-size: 10px; }
.gallery-rail__scroller button.gallery-rail__delete:hover { border-color: rgb(212 123 114 / 45%); color: #ffd4ce; background: rgb(212 123 114 / 10%); }
/* A row's button is pressed while the Camera is landed on its card. "Show all cards" is not a toggle — it is
   the way back to the whole Expanse — so it carries no pressed state, only the accent it has always worn. */
.gallery-rail__scroller button[aria-pressed="true"], .gallery-rail .gallery-rail__all { border-color: rgb(212 123 114 / 45%); color: var(--gallery-accent); background: rgb(212 123 114 / 9%); }
/* A row whose card is in the Window is one the Camera is currently over; a screen reader hears "current". */
.gallery-rail__row[aria-current="true"] button { border-color: rgb(212 123 114 / 22%); }
.gallery-rail__row[aria-current="true"]::before { position: absolute; left: 0; top: 7px; bottom: 7px; width: 2px; border-radius: 2px; background: var(--gallery-accent); content: ""; }
.gallery-rail__cell--open button { width: 100%; }
.gallery-rail__select { flex: 0 0 14px; width: 14px; height: 14px; margin: 0 0 0 5px; padding: 0; accent-color: var(--gallery-accent); }
/* What is on the Expanse, announced rather than drawn: it is read aloud, never seen. */
.gallery-announcement { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.gallery-set-bar { display: flex; flex-direction: column; align-items: stretch; gap: 10px; margin: 0 0 20px; padding: 12px; border: 1px solid var(--lab-border-strong); border-radius: 6px; background: var(--lab-bg-deep); font-size: 12px; }
.gallery-set-bar label { display: grid; gap: 6px; }
#set-bar-count { color: var(--gallery-accent); font-weight: 600; }
#add-to-set { color: var(--gallery-accent); border-color: rgb(212 123 114 / 45%); }
.gallery-set-bar__clear { background: transparent; }
.gallery-sets { padding: 18px 4px 0; border-top: 1px solid var(--lab-border); }
.gallery-sets h2 { margin: 0 0 14px; font-size: 12px; color: #edf0f4; font-weight: 600; }
.gallery-hint { margin: 0 0 12px; color: var(--lab-muted); font-size: 11px; line-height: 1.6; }
#sets-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 20px; }
.gallery-set { display: grid; gap: 9px; }
.gallery-set > strong { font-size: 12px; font-weight: 500; }
.gallery-set__members { display: flex; flex-wrap: wrap; gap: 5px; }
.gallery-set__empty { color: var(--lab-muted); font-size: 11px; }
.gallery-set__chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 7px; border: 1px solid var(--lab-border); border-radius: 5px; color: var(--lab-muted); background: var(--lab-bg-deep); font-size: 10px; }
.gallery-set__chip button { display: grid; place-items: center; min-width: 20px; min-height: 20px; padding: 0; border: 0; color: var(--lab-muted); background: transparent; }
.gallery-expanse { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.gallery-expanse__toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; padding: 18px 24px; border-bottom: 1px solid var(--lab-border); }
.gallery-expanse__heading h2 { margin: 0 0 5px; color: #edf0f4; font-size: 16px; font-weight: 600; }
#expanse-meta { color: var(--lab-muted); font-size: 11px; }
.gallery-expanse__controls { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; font-size: 11px; }
.gallery-expanse__controls label { display: flex; align-items: center; gap: 8px; }
.gallery-expanse__controls select { max-width: 150px; }
#altitude { width: 100px; padding: 0; accent-color: var(--gallery-accent); }
#altitude-value { min-width: 42px; font-variant-numeric: tabular-nums; }
/* Empty plane is what the Camera is flown by, so the stage is a grabbable surface: the pointer says so, and
   a touch that lands on it flies rather than scrolling the page past it. */
.gallery-stage { position: relative; flex: 1; min-height: 320px; overflow: hidden; cursor: grab; touch-action: none; background: radial-gradient(ellipse at center, #333944 0%, #252a32 56%, #20242b 100%); }
.gallery-stage--flying { cursor: grabbing; }
.gallery-stage::before { position: absolute; inset: 0; opacity: .15; background-image: linear-gradient(rgb(255 255 255 / 6%) 1px, transparent 1px), linear-gradient(90deg, rgb(255 255 255 / 6%) 1px, transparent 1px); background-size: 32px 32px; content: ""; pointer-events: none; }
/* Hovering names a card rather than magnifying it: a frame where the card is drawn, and its name beside it.
   It sits over the stage's canvas and never takes the pointer, so what is under it is still the card. */
.gallery-identity { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.gallery-identity__frame { position: absolute; border: 1px solid var(--gallery-accent); border-radius: 4px; box-shadow: 0 0 0 3px rgb(212 123 114 / 18%); }
.gallery-identity__name { position: absolute; max-width: calc(100% - 16px); padding: 4px 8px; border-radius: 5px; color: #f4f6f8; background: rgb(24 27 33 / 92%); font-size: 11px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A Landing. The card that fills the stage is the card on the Expanse — nothing here draws it — so this is
   only the chrome around it: its name, its authored dimensions, its summary and its faces. The layer takes
   no pointer, so the stage under it is still the card; the panel itself does, so its controls work. */
.gallery-landing { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.gallery-landing__panel { position: absolute; left: 20px; bottom: 20px; display: grid; gap: 8px; width: min(300px, calc(100% - 40px)); padding: 14px 16px; border: 1px solid var(--lab-border-strong); border-radius: 8px; background: rgb(24 27 33 / 92%); box-shadow: 0 10px 30px rgb(0 0 0 / 35%); pointer-events: auto; }
.gallery-landing__leave { justify-self: start; padding: 4px 8px; font-size: 11px; color: var(--lab-muted); background: transparent; }
.gallery-landing__leave kbd { padding: 1px 4px; border: 1px solid var(--lab-border-strong); border-radius: 3px; font: inherit; font-size: 10px; }
.gallery-landing__name { margin: 0; color: #f0f2f4; font-size: 15px; font-weight: 600; }
.gallery-landing__meta { margin: 0; color: var(--gallery-accent); font-size: 11px; font-variant-numeric: tabular-nums; }
.gallery-landing__summary { margin: 0; color: var(--lab-muted); font-size: 11px; line-height: 1.6; }
.gallery-landing__face { display: flex; align-items: center; gap: 8px; color: var(--lab-muted); font-size: 11px; }
.gallery-landing__face select { flex: 1; min-width: 0; font-size: 12px; }
/* The Card set shelf: one slot per set, pinned to the foot of the stage so it holds still on screen while
   the Expanse moves underneath. It never takes the pointer — the drop target is the Zone each slot is
   measured into, not the slot itself — so a press anywhere over it flies the plane under it as usual. It
   sits under the Landing's panel, which is the bottom-left corner's, and right of it. */
/* It stands clear of the foot of the stage on purpose: the last stretch of every edge is what a carried card
   flies the Camera by, and a set sitting in it would mean aiming at a slot and asking to fly at once. */
.gallery-shelf { position: absolute; z-index: 3; right: 16px; bottom: 84px; left: 336px; display: flex; align-items: flex-end; justify-content: flex-end; flex-wrap: wrap; gap: 8px; pointer-events: none; }
.gallery-shelf__title { padding-bottom: 6px; color: var(--lab-muted); font-size: 10px; letter-spacing: .02em; }
.gallery-shelf__sets { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.gallery-shelf__set { display: grid; gap: 3px; width: 132px; min-height: 52px; padding: 7px 9px; border: 1px solid var(--lab-border-strong); border-radius: 7px; background: rgb(24 27 33 / 92%); box-shadow: 0 8px 22px rgb(0 0 0 / 32%); }
.gallery-shelf__name { color: #f0f2f4; font-size: 11px; font-weight: 600; line-height: 1.3; }
.gallery-shelf__count { color: var(--gallery-accent); font-size: 10px; font-variant-numeric: tabular-nums; }
/* What the set holds, so its membership is readable without flying to find its members. One line, so the
   slot is the same rectangle whatever the set holds — which is the rectangle its Zone is measured into. */
.gallery-shelf__members { overflow: hidden; color: var(--lab-muted); font-size: 10px; line-height: 1.4; white-space: nowrap; text-overflow: ellipsis; }
.gallery-expanse__footer { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; padding: 12px 24px; border-top: 1px solid var(--lab-border); font-size: 11px; color: var(--lab-muted); }
.gallery-status { color: var(--gallery-accent); }
@media (max-width: 1000px) {
  .gallery-workspace { grid-template-columns: 240px minmax(0, 1fr); }
  .gallery-expanse__toolbar { padding: 16px; }
  .gallery-expanse__controls { gap: 10px; }
  .gallery-expanse__footer { padding: 12px 16px; }
}
@media (max-width: 700px) {
  .gallery-app { height: auto; min-height: 100dvh; }
  .gallery-header { flex-wrap: wrap; padding: 12px 16px; gap: 12px; }
  .gallery-workspace { display: flex; flex-direction: column; }
  .gallery-browser { max-height: 330px; padding: 16px; border-right: 0; border-bottom: 1px solid var(--lab-border); }
  .gallery-browser__heading { margin-bottom: 12px; }
  /* The rail keeps one column at every width: a row's place is its index times one constant height, and a
     second column would make that two. */
  .gallery-rail { margin: 14px 0; }
  .gallery-rail__scroller { height: clamp(140px, 24vh, 240px); }
  /* On a stage this narrow a landed card is as wide as the stage lets it be, so the panel spans the foot of
     it rather than standing beside it. */
  .gallery-landing__panel { left: 12px; right: 12px; bottom: 12px; width: auto; }
  /* Nothing stands beside the shelf at this width, so it keeps the whole foot of the stage. */
  .gallery-shelf { left: 12px; right: 12px; bottom: 84px; }
  .gallery-shelf__title { display: none; }
  .gallery-expanse { min-height: 560px; }
  .gallery-stage { min-height: 380px; }
}
