/* ═══════════════════════════════════════════════════════════════════════════
   GALERIE — Brněnské Tlapky
   ═══════════════════════════════════════════════════════════════════════════ */
/* ── COLOURS ──────────────────────────────────────────────────────────────────
   None here. theme.css is loaded first and owns the lot:

     --accent       fills, borders, the logo glow
     --accent2      hover / active partner
     --on-accent    text sitting ON an --accent fill
     --accent-ink   --accent used AS TEXT on --bg (lightened for dark accents)
     --accent-rgb   the same colour as r,g,b — for rgba() tints
     --bg --surface --border --text --muted --nav-h --nav-logo

   Switch the site's colour in theme.css and the gallery follows. The split
   matters: on Flamik blue, Blood red and Violet, --accent as text is under
   4.5:1 and black on --accent is unreadable, so text uses --accent-ink and
   text on a fill uses --on-accent. Nothing below names a colour from the
   palette directly.                                                          */

/* Gallery-only tokens. Tints go through --accent-rgb rather than color-mix(),
   which is what theme.css provides that variable for. */
:root {
  --surface2: #241f1a;

  --accent-soft: rgba(var(--accent-rgb), .14);
  --accent-line: rgba(var(--accent-rgb), .45);

  /* --muted is tuned against --bg; on the lighter --surface panels it falls to
     3.7:1, so secondary text inside a card uses a lifted grey instead. Same
     idea as theme.css lightening --accent-ink for the dark accents. */
  --muted-card: #948b80;

  --ok: var(--accent-ink);
  --warn: #e8c024;
  --bad: #e85c4a;
  --gap: 6px;
  --r-lg: 16px;
  --r-sm: 8px;
  --font: 'JetBrainsMono Nerd Font', 'JetBrainsMono NF', 'JetBrains Mono', ui-monospace, monospace;
  color-scheme: dark;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--bg); }
html.gv-lock { overflow: hidden; }
body {
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
a { color: inherit; }
img { display: block; max-width: 100%; }
button, input, select { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
h1, h2 { font-family: var(--font); font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.gmuted { color: var(--muted); }
[hidden] { display: none !important; }

/* ── Nav: [O galerii] … [logo] … [Galerie][Upload][profil] ───────────────── */
.gnav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center; justify-content: space-between;
  /* Scales smoothly rather than stepping down at a breakpoint. Zooming in
     shrinks the layout viewport, so an earlier `padding: .5rem below 560px`
     rule made the logo jump to the edge the moment the zoom crossed that
     line. clamp() keeps a visible gutter at every zoom level. */
  padding: 0 clamp(.85rem, 2.5vw, 2rem);
  background: rgba(14, 12, 10, .88);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}
.gnav-side { display: flex; align-items: center; gap: .25rem; }
.gnav-side a:not(.gnav-logo):not(.gnav-profile) {
  text-decoration: none; color: var(--muted); white-space: nowrap;
  font-size: .9rem; font-weight: 500;
  padding: .45rem .75rem; border-radius: 6px; border: 1px solid transparent;
  letter-spacing: .3px;
  transition: color .2s ease, background .2s ease, border-color .2s ease;
}
.gnav-side a:not(.gnav-logo):not(.gnav-profile):hover, .gnav-side a.active {
  color: var(--text); background: var(--surface); border-color: var(--border);
}
/* The logo is shown at its own proportions — fixed to the bar's height, width
   follows the artwork. No square box, no cropping, and the same size on every
   screen size.
   The hover glow is not here: the element also carries theme.css's `nav-logo`
   class, which owns it (and swaps in the six-colour shadow stack under the
   rainbow theme). Setting `filter` here would fight it. */
.gnav-logo {
  flex: none; margin-right: .5rem;
  display: inline-flex; align-items: center;
  text-decoration: none;
}
.gnav-logo img { height: var(--nav-logo); width: auto; display: block; }
.gnav-profile {
  width: 38px; height: 38px; margin-left: .35rem; padding: 0; overflow: hidden;
  display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  cursor: pointer; transition: border-color .2s ease;
}
.gnav-profile img { width: 100%; height: 100%; object-fit: cover; }
.gnav-profile:hover { border-color: var(--accent); }

/* ── Page frame ─────────────────────────────────────────────────────────── */
/* The caps grow with the screen instead of stopping at a laptop width — on a
   wide monitor the old 1100px left two thirds of the window empty. The gutter
   grows too, so the content never runs into the edge. */
.gwrap {
  width: 100%; max-width: 1400px; margin: 0 auto; flex: 1;
  padding: calc(var(--nav-h) + 2rem) clamp(1.25rem, 3vw, 3rem) 4rem;
}
.gwrap-wide { max-width: 1900px; }
/* The footer lives in /site.css now — the same one on every page. */

.gsec { display: flex; gap: .25rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.gsec a {
  text-decoration: none; font-size: .8rem; color: var(--muted);
  padding: .35rem .7rem; border-radius: 999px; border: 1px solid var(--border);
}
.gsec a:hover { color: var(--text); }
.gsec a.active { color: var(--text); background: var(--accent-soft); border-color: var(--accent-line); }

.gpage-head { margin-bottom: 1.25rem; }
.gpage-head h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 800; }
.gpage-head p { margin-top: .35rem; }
.gpage-head a { color: var(--accent-ink); }
.gat { color: var(--accent-ink); text-decoration: none; }
.gat:hover { text-decoration: underline; }
.ghead-row { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: .9rem; }
.ghead-row h2 { font-size: 1.3rem; }
.ghead-row a { color: var(--muted); font-size: .8rem; }
.ghead-links { display: flex; gap: .9rem; }

.gbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
  padding: .5rem .9rem; border-radius: var(--r-sm);
  background: var(--surface2); border: 1px solid var(--border);
  font-size: .8rem; font-weight: 500; text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: border-color .15s, background .15s;
}
.gbtn:hover { border-color: var(--accent); }
.gbtn:disabled { opacity: .45; cursor: not-allowed; }
.gbtn-main { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 700; padding: .65rem 1.2rem; }
.gbtn-main:hover:not(:disabled) { background: var(--accent2); }

.gempty {
  text-align: center; padding: 3.5rem 1rem; color: var(--muted);
  border: 1px dashed var(--border); border-radius: var(--r-lg);
  display: grid; gap: 1rem; justify-items: center;
}
.gnote {
  margin-bottom: 1rem; padding: .7rem 1rem; border-radius: var(--r-sm);
  background: rgba(232, 192, 36, .1); border: 1px solid rgba(232, 192, 36, .35); color: var(--warn); font-size: .82rem;
}

/* ── Home ───────────────────────────────────────────────────────────────── */
.ghero { margin: 1rem 0 2rem; }
.ghero h1 { font-size: clamp(3rem, 11vw, 7.5rem); font-weight: 800; letter-spacing: -.04em; line-height: .9; }
.ghero p { margin-top: .9rem; color: var(--muted); max-width: 72ch; }

.gtiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 3rem; }
.gtiles-3 { grid-template-columns: repeat(3, 1fr); }
.gtile {
  position: relative; aspect-ratio: 1; overflow: hidden;
  display: flex; flex-direction: column; justify-content: flex-end; gap: .15rem;
  padding: 1rem; text-align: left; text-decoration: none; cursor: pointer;
  border-radius: var(--r-lg); border: 1px solid var(--border);
  background: var(--surface);
  isolation: isolate;
}
.gtile::before {
  content: ""; position: absolute; inset: 0; z-index: -2;
  background: var(--cover, none) center / cover no-repeat;
  transition: transform .5s ease;
}
.gtile::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to top, rgba(14, 12, 10, .93) 0%, rgba(14, 12, 10, .35) 55%, rgba(14, 12, 10, .1) 100%);
}
.gtile:hover::before { transform: scale(1.05); }
.gtile:hover { border-color: var(--accent-line); }
.gtile-name { font-weight: 800; font-size: clamp(1.1rem, 2.1vw, 1.55rem); line-height: 1.05; }
.gtile-meta { font-size: .75rem; color: var(--muted-card); }
.gtile-up { border-style: dashed; background: transparent; }
.gtile-up::after { background: none; }
.gtile-plus {
  position: absolute; top: .6rem; right: .9rem;
  font-size: 3.5rem; font-weight: 600; line-height: 1; color: var(--accent-ink);
}

/* ── Toolbar ───────────────────────────────────────────────────────────── */
.gtool { display: grid; gap: .6rem; margin-bottom: 1rem; }
.gtool-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.gsearch, .gtool select, .gupload input[type=text], .gupload select, .gshare input {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: .55rem .75rem; font-size: .82rem; min-width: 0;
}
.gsearch { flex: 1 1 280px; }
.gsearch:focus, .gtool select:focus, .gupload input:focus, .gupload select:focus { border-color: var(--accent); outline: none; }
.gtool-view { justify-content: space-between; }
.gmode { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.gmode button { background: transparent; border: 0; padding: .4rem .75rem; font-size: .78rem; color: var(--muted); cursor: pointer; }
.gmode button + button { border-left: 1px solid var(--border); }
.gmode button.on { background: var(--accent-soft); color: var(--text); }
.gsize { display: inline-flex; align-items: center; gap: .5rem; font-size: .78rem; color: var(--muted); flex: 1 1 200px; max-width: 360px; }
.gsize input { flex: 1; accent-color: var(--accent); }
.gsize output { min-width: 4.5em; text-align: right; font-variant-numeric: tabular-nums; }
.gcount { font-size: .78rem; color: var(--muted); }

.gchips { display: flex; flex-wrap: wrap; gap: .35rem; }
.gchip {
  font-size: .74rem; text-decoration: none; color: var(--muted-card);
  padding: .2rem .55rem; border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
}
.gchip small { opacity: .6; }
.gchip:hover { color: var(--text); border-color: var(--accent-line); }
.gchip.on { color: var(--text); background: var(--accent-soft); border-color: var(--accent-line); }

/* ── Photo grid ────────────────────────────────────────────────────────────
   mode-h: justified rows — every item grows in proportion to its aspect ratio,
           so a whole row scales evenly and nothing gets cropped.
   mode-w: equal-width columns (masonry). --size drives both.              */
.ggrid { --size: 240px; }
.ggrid .gi {
  position: relative; display: block; overflow: hidden;
  border-radius: 4px; background: var(--surface);
  aspect-ratio: var(--r);
}
.ggrid .gi img { width: 100%; height: 100%; object-fit: cover; transition: transform .3s ease, opacity .2s; }
.ggrid .gi:hover img { transform: scale(1.03); }
.ggrid .gi:focus-visible { outline-offset: -2px; }

.ggrid.mode-h { display: flex; flex-wrap: wrap; gap: var(--gap); }
.ggrid.mode-h .gi { flex: var(--r) 1 calc(var(--r) * var(--size)); max-height: calc(var(--size) * 1.6); }
.ggrid.mode-h .gfill { flex: 1000000 1 0; height: 0; }

.ggrid.mode-w { columns: var(--size); column-gap: var(--gap); }
.ggrid.mode-w .gi { margin-bottom: var(--gap); break-inside: avoid; }
.ggrid.mode-w .gfill { display: none; }

.gpager { display: flex; justify-content: center; align-items: center; gap: 1rem; margin-top: 1.5rem; font-size: .82rem; color: var(--muted); }
.gpager a { color: var(--text); text-decoration: none; padding: .45rem .8rem; border: 1px solid var(--border); border-radius: var(--r-sm); }
.gpager a:hover { border-color: var(--accent); }

/* ── Events ────────────────────────────────────────────────────────────── */
.gevents { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
.gev {
  display: flex; flex-direction: column; overflow: hidden; text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface);
  transition: border-color .15s;
}
.gev:hover { border-color: var(--accent-line); }
.gev img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.gev-body { display: grid; gap: .1rem; padding: .85rem 1rem 1rem; }
.gev-name { font-weight: 700; font-size: 1.15rem; line-height: 1.2; }
.gev-meta { font-size: .75rem; color: var(--muted); }
.gev-count { font-size: .78rem; color: var(--accent-ink); margin-top: .3rem; }
.gev-empty { opacity: .55; }
.gev-empty .gev-count { color: var(--muted); }

.gev-head { display: grid; grid-template-columns: minmax(0, 360px) 1fr; gap: 1.5rem; align-items: end; margin-bottom: 1.5rem; }
.gev-banner { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--r-lg); border: 1px solid var(--border); }
.gev-head h1 { font-size: clamp(1.8rem, 4vw, 2.8rem); font-weight: 800; }
.gev-head p { margin-top: .35rem; font-size: .82rem; }
.gev-head a { color: var(--accent-ink); }

/* ── Fursuiters ────────────────────────────────────────────────────────── */
.gsuiters { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
/* A fursuiter card: picture on top (the fursona's badge / owner's picture when
   they have an account, otherwise their newest photo), @name underneath. */
.gsu {
  display: flex; flex-direction: column; overflow: hidden;
  border-radius: var(--r-lg); border: 1px solid var(--border); background: var(--surface); text-decoration: none;
  transition: border-color .2s ease, transform .2s ease;
}
.gsu-pic { position: relative; display: block; aspect-ratio: 1; background: var(--bg); overflow: hidden; }
.gsu-pic img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .3s ease; }
.gsu-nopic { display: grid; place-items: center; width: 100%; height: 100%; font-size: 2.4rem; opacity: .35; }
.gsu-badge .gsu-pic::after {           /* a registered fursona: small accent ring */
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  box-shadow: inset 0 0 0 3px rgba(var(--accent-rgb), .55);
}
.gsu-meta { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; padding: .55rem .7rem 0; }
.gsu-name { font-weight: 700; font-size: .86rem; color: var(--accent-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.gsu-count { font-size: .7rem; color: var(--muted); flex: none; }
.gsu-owner { padding: .1rem .7rem .55rem; font-size: .7rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gsu-meta:last-child { padding-bottom: .6rem; }
.gsu:hover { border-color: var(--accent-line); transform: translateY(-2px); }
.gsu:hover .gsu-pic img { transform: scale(1.05); }
/* the fursuiter's own page */
.gsu-head { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; }
.gsu-head-pic { width: 112px; height: 112px; border-radius: 22px; object-fit: cover; border: 2px solid var(--accent); box-shadow: 0 0 0 5px rgba(var(--accent-rgb), .14); }
.gsu-head-text { flex: 1; min-width: 200px; }
.gsu-owner-line { margin-top: .25rem !important; font-size: .95rem; }
.gsu-owner-line a, .gsu-owner-line strong { color: var(--accent-ink); font-weight: 700; }
.gsu-more { margin-left: auto; }
.gtree { text-decoration: none; margin-left: .3rem; }
h1.gat { color: var(--accent-ink); }

/* ── Viewer ────────────────────────────────────────────────────────────── */
.gviewer {
  position: fixed; inset: 0; z-index: 200;
  display: none; grid-template-columns: 1fr 360px;
  /* Definite, not auto: the stage below asks for 100% of this row, and a row
     sized by its own content cannot answer that. Without it the photo falls
     back to its natural size and overflows the stage instead of fitting. */
  grid-template-rows: 100%;
  background: #080706;
}
.gviewer.open { display: grid; }
.gv-stage {
  position: relative; display: grid;
  /* Explicit 100%/100% tracks, not place-items:center. The pan layer has to
     stretch to a DEFINITE box: an auto track would be sized by the image,
     the image's own max-height:100% would then resolve against it, and that
     circle makes the browser drop the constraint — the photo renders at full
     pixel size and overflows instead of fitting. */
  grid-template-columns: 100%; grid-template-rows: 100%;
  padding: 1.25rem; min-height: 0; overflow: hidden;
  /* The stage handles its own pinch and drag, so the browser must not also
     scroll or zoom the page from gestures that start here. */
  touch-action: none;
}
/* Pan/zoom layer. It fills the stage, so the image's max-width/height are
   measured against the stage, and transform-origin 50% 50% is the stage
   centre — which is what the zoom-at-a-point maths assumes. */
.gv-pan {
  grid-area: 1 / 1; min-width: 0; min-height: 0;
  /* Definite tracks again, for the same reason as the stage: the images are
     grid items here, so their max-height:100% is measured against this row.
     An auto row would be sized by the image it is meant to be constraining. */
  display: grid; grid-template-columns: 100%; grid-template-rows: 100%;
  place-items: center;
  transform: translate3d(var(--zx, 0px), var(--zy, 0px), 0) scale(var(--zs, 1));
  transform-origin: 50% 50%;
}
/* Only while a gesture is not in progress — animating during a drag would lag
   a frame behind the finger. */
.gv-pan.ease { transition: transform .18s ease-out; }
@media (prefers-reduced-motion: reduce) { .gv-pan.ease { transition: none; } }
.gviewer.zoomed .gv-pan { cursor: grab; }
.gviewer.panning .gv-pan { cursor: grabbing; }
.gv-stage img {
  grid-area: 1 / 1; max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain; border-radius: 4px;
  /* The photo is a thing you drag to pan, not a thing you drag out of the page.
     draggable="false" handles Chrome and Firefox; Safari needs this too. */
  -webkit-user-drag: none; user-select: none; -webkit-user-select: none;
}
.gv-lo { filter: blur(6px); transform: scale(1.01); }
.gv-img { opacity: 0; transition: opacity .2s; }
.gviewer.loaded .gv-img { opacity: 1; }
.gviewer.loaded .gv-lo { visibility: hidden; }
.gv-nav, .gv-close {
  position: absolute; z-index: 2;
  width: 44px; height: 44px; display: grid; place-items: center;
  background: rgba(26, 23, 20, .8); border: 1px solid var(--border); border-radius: 50%;
  color: var(--text); font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.gv-nav:hover, .gv-close:hover { border-color: var(--accent); }
/* ── Zoom controls ── bottom-left of the stage, clear of the nav arrows and
   the close button. Always visible, because a zoom nobody finds is no zoom. */
.gv-zoom {
  position: absolute; z-index: 3; left: 1rem; bottom: 1rem;
  display: flex; align-items: center; gap: .15rem;
  padding: .2rem; border-radius: 999px;
  background: rgba(26, 23, 20, .82); border: 1px solid var(--border);
  backdrop-filter: blur(8px);
}
.gv-zb {
  width: 30px; height: 30px; display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%;
  color: var(--text); font-size: 1rem; line-height: 1; cursor: pointer;
}
.gv-zb:hover:not(:disabled) { background: rgba(var(--accent-rgb), .18); }
.gv-zb:disabled { opacity: .35; cursor: default; }
.gv-zval {
  min-width: 4.2ch; text-align: center; font-size: .72rem;
  color: var(--muted-card); font-variant-numeric: tabular-nums; user-select: none;
}
/* Appears only once the display copy has run out of detail: either as the
   "fetching the original" note, or — for an original big enough to hurt — as
   the offer to load it, with the price on the label. */
.gv-zfull {
  font-size: .68rem; color: var(--accent-ink);
  padding: .25rem .6rem; margin-left: .15rem;
  background: rgba(var(--accent-rgb), .14); border: 1px solid var(--accent-line);
  border-radius: 999px; font-family: inherit; cursor: pointer; white-space: nowrap;
}
.gv-zfull:hover:not(:disabled) { background: rgba(var(--accent-rgb), .26); }
.gv-zfull:disabled { cursor: default; opacity: .75; }

.gv-prev { left: 1rem; top: 50%; transform: translateY(-50%); }
.gv-next { right: 1rem; top: 50%; transform: translateY(-50%); }
.gv-close { top: 1rem; right: 1rem; }
.gv-info {
  overflow-y: auto; padding: 1.5rem 1.25rem;
  background: var(--surface); border-left: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 1rem;
}
.gv-title { font-size: 1.35rem; font-weight: 700; word-break: break-word; }
.gv-pos { font-size: .72rem; color: var(--muted); margin-top: -.6rem; }
.gv-meta { display: grid; gap: .55rem; }
.gv-row { display: grid; grid-template-columns: 6.5rem 1fr; gap: .5rem; font-size: .8rem; }
.gv-row dt { color: var(--muted); }
.gv-row dd { word-break: break-word; }
.gv-row dd a:not(.gtag):not(.gat) { color: var(--text); }
.gtags { display: flex; flex-wrap: wrap; gap: .3rem; }
.gtag {
  font-size: .72rem; text-decoration: none; padding: .1rem .45rem;
  border-radius: 4px; background: var(--surface2); border: 1px solid var(--border);
}
.gtag:hover { border-color: var(--accent); }
.gshare { display: flex; gap: .4rem; }
.gshare input { flex: 1; font-size: .74rem; color: var(--muted); }
.gv-actions { display: flex; flex-wrap: wrap; gap: .4rem; }
.gv-handle { display: none; }
.gv-more summary { cursor: pointer; font-size: .8rem; color: var(--muted); margin-bottom: .6rem; }
.gv-more summary:hover { color: var(--text); }

/* share page: viewer sits in the page instead of floating over it */
.gphoto-page { padding: calc(var(--nav-h) + 1rem) 1.25rem 2rem; flex: 1; }
.gviewer-static { position: static; background: none; min-height: calc(100vh - var(--nav-h) - 6rem); }
.gviewer-static .gv-info { border: 1px solid var(--border); border-radius: var(--r-lg); }
.gviewer-static .gv-stage img { max-height: calc(100vh - var(--nav-h) - 4rem); }

/* ── Upload ────────────────────────────────────────────────────────────── */
.gupload { display: grid; gap: 1.25rem; }
.gdrop {
  display: grid; place-items: center; gap: .3rem; padding: 2.75rem 1rem; text-align: center; cursor: pointer;
  border: 2px dashed var(--border); border-radius: var(--r-lg); background: var(--surface);
  transition: border-color .15s, background .15s;
}
.gdrop input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.gdrop:hover, .gdrop.over { border-color: var(--accent); background: var(--accent-soft); }
.gdrop:focus-within { border-color: var(--accent); }
.gdrop-big { font-size: 1.4rem; font-weight: 700; }
.gshared { border: 1px solid var(--border); border-radius: var(--r-lg); padding: 1rem; display: grid; gap: .8rem; grid-template-columns: 1fr 1fr; }
.gshared legend { padding: 0 .4rem; font-size: .78rem; color: var(--muted); }
.gupload label { display: grid; gap: .3rem; font-size: .78rem; color: var(--muted); }
.gupload label small { font-size: .7rem; }
.gupload input[type=text], .gupload select { color: var(--text); width: 100%; }
.gshared .gcheck { display: flex; align-items: center; gap: .5rem; grid-column: 1 / -1; color: var(--text); }
.gcheck input { accent-color: var(--accent); width: 16px; height: 16px; }
.gqueue { display: grid; gap: .6rem; }
.gq {
  display: grid; grid-template-columns: 96px 1fr; gap: .9rem; padding: .75rem;
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface);
}
.gq-img { width: 96px; height: 96px; object-fit: cover; border-radius: var(--r-sm); background: var(--surface2); }
.gq-body { display: grid; gap: .5rem; min-width: 0; }
.gq-top { display: flex; align-items: center; gap: .6rem; font-size: .8rem; }
.gq-file { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.gq-x { background: none; border: 0; color: var(--muted); font-size: 1.3rem; line-height: 1; cursor: pointer; }
.gq-x:hover { color: var(--bad); }
.gq-fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.gq-bar { height: 4px; border-radius: 2px; background: var(--surface2); overflow: hidden; display: none; }
.gq-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width .2s; }
.gq.busy .gq-bar { display: block; }
.gq.busy .gq-fields, .gq.busy .gq-x { pointer-events: none; opacity: .5; }
.gq-msg { font-size: .76rem; color: var(--muted); }
.gq-msg:empty { display: none; }
.gq-msg a { color: var(--accent-ink); }
.gq.err { border-color: rgba(232, 92, 74, .45); }
.gq.err .gq-msg { color: var(--bad); }
.gq.ok { border-color: rgba(134, 232, 36, .4); }
.gstate { font-size: .7rem; padding: .15rem .5rem; border-radius: 999px; white-space: nowrap; }
.gstate.pending { background: rgba(232, 192, 36, .14); color: var(--warn); }
.gstate.approved { background: rgba(134, 232, 36, .14); color: var(--ok); }
.gup-actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

/* ── O Galerii ──────────────────────────────────────────────────────────────
   A reference page, so it is laid out like one: the topic sits in a narrow
   left column and its text in a wide one, with a single hairline marking each
   new topic. The query diagram up top is the one loud element.            */
/* A generous cap rather than the old 940px: the reading measure is held by
   .ga-body, which adds a column as the window grows instead of letting lines
   run the width of a monitor, so the page can be much wider than a single
   column of text would allow. Past this there is nothing left to fill. */
.gabout { max-width: min(1750px, 100%); }

.ga-hero { margin-bottom: 3rem; }
.ga-hero h1 {
  font-size: clamp(2.4rem, 7vw, 4rem); font-weight: 800;
  letter-spacing: -.04em; line-height: .95; margin-bottom: .9rem;
}
.ga-lead { max-width: 78ch; color: rgba(237, 232, 226, .82); margin-bottom: 2rem; }
.ga-counts { margin-top: 1.6rem; font-size: .82rem; color: var(--muted); }
.ga-counts strong { color: var(--text); font-weight: 700; }

/* the annotated query: tokens on one line, labels hanging under each */
/* Stays on --bg rather than --surface: theme.css tunes --accent-ink to clear
   4.5:1 against --bg, and the lighter surface eats that margin (Violet's
   #bd23ff drops to 4.1:1 on it). The border alone marks the panel out. */
.ga-query {
  /* Hugs the query rather than stretching: it is one short line of text, and a
     full-width box around it reads as an empty panel. The floor keeps it a
     panel rather than a label when the tokens stack on a narrow screen. */
  width: fit-content; min-width: min(100%, 46ch); max-width: 100%;
  padding: 1.4rem 1.5rem 1.1rem;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--bg);
  overflow-x: auto;
}
/* one column per token, so each label hangs under the word it explains */
.ga-query-line { display: flex; gap: 1.5rem; align-items: flex-start; }
.ga-col { display: flex; flex-direction: column; align-items: flex-start; gap: .45rem; min-width: 0; }
.ga-tok {
  font-size: clamp(1rem, 2.6vw, 1.35rem); font-weight: 700;
  text-decoration: none; color: inherit; white-space: nowrap;
  border-bottom: 2px solid currentColor; padding-bottom: .15rem;
}
/* The three tokens are told apart by brightness, not hue: the palette in
   theme.css covers lime, orange, two blues, yellow, red, pink and violet, so
   any fixed hue here would eventually land on top of --accent-ink (yellow
   beside Cheese yellow, salmon beside Blood red). Achromatic greys never
   collide, and dimming the excluded term happens to say what it means.
   The └─ labels underneath carry the meaning regardless. */
/* Scoped to .ga-tok, not the column: the column also holds the └─ label, and
   colouring (or gradient-filling) the whole column drags the label with it. */
.ga-tok-who  .ga-tok { color: var(--accent-ink); }   /* themed, most prominent */
.ga-tok-what .ga-tok { color: var(--text); }
.ga-tok-not  .ga-tok { color: #9c9690; }             /* 6.7:1 on --bg — dim but readable */
.ga-tok:hover { background: rgba(255, 255, 255, .07); }
.ga-note { font-size: .72rem; color: var(--muted-card); white-space: nowrap; }
.ga-query-run { margin-top: 1.1rem; font-size: .8rem; color: var(--muted-card); white-space: normal; }
.ga-query-run a { color: var(--accent-ink); }

/* topic | text */
.ga-sec {
  display: grid; grid-template-columns: clamp(150px, 12%, 230px) minmax(0, 1fr); gap: 2rem;
  padding: 1.8rem 0; border-top: 1px solid var(--border);
}
.ga-sec h2 { font-size: .95rem; font-weight: 700; color: var(--muted); padding-top: .15rem; }
.ga-body { max-width: 80ch; }
/* Past roughly 1250px there is more width than a line of text should use, so
   the body flows into columns instead of stretching. Reading order still runs
   top-to-bottom, then across. Blocks are kept whole so a paragraph is never
   split down the middle and the example list stays in one piece. */
@media (min-width: 1250px) {
  .ga-body { max-width: none; columns: 2; column-gap: 3.5rem; }
  .ga-body > * { break-inside: avoid; }
  .ga-body > p:first-child { margin-top: 0; }
}
@media (min-width: 1800px) {
  .ga-body { columns: 3; }
}
.ga-body p { margin-bottom: .85rem; color: rgba(237, 232, 226, .82); }
.ga-body p:last-child { margin-bottom: 0; }
.ga-body a { color: var(--accent-ink); }
.ga-body strong { color: var(--text); }
.ga-body code { color: var(--accent-ink); font-size: .92em; }

/* each example is the real search it describes */
.ga-syntax { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .1rem 1.25rem; margin: 1.1rem 0; }
.ga-syntax dt a {
  display: inline-block; padding: .2rem .5rem; border-radius: 6px;
  background: var(--surface2); border: 1px solid var(--border);
  color: var(--text); text-decoration: none; font-size: .82rem; white-space: nowrap;
}
.ga-syntax dt a:hover { border-color: var(--accent); color: var(--accent-ink); }
.ga-syntax dd { font-size: .82rem; color: var(--muted); padding: .3rem 0; }

/* the only action on the page, so the only accented block */
.ga-takedown {
  margin-top: 2.2rem; padding: 1.5rem;
  border: 1px solid var(--accent-line); border-radius: var(--r-lg);
  background: var(--accent-soft);
}
.ga-takedown h2 { font-size: 1.15rem; font-weight: 800; margin-bottom: .5rem; }
/* .gbtn is nowrap by design, but this one carries a full email address and
   pushed the page sideways on a 320px screen. Let this label alone wrap. */
.ga-takedown .gbtn { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.ga-takedown p { max-width: 75ch; margin-bottom: 1.1rem; color: rgba(237, 232, 226, .85); }

@media (max-width: 720px) {
  .ga-query { overflow-x: visible; }
  .ga-query-line { flex-direction: column; gap: 1rem; }
  .ga-sec { grid-template-columns: 1fr; gap: .7rem; }
  .ga-syntax { grid-template-columns: 1fr; gap: .15rem; }
  .ga-syntax dd { padding: 0 0 .7rem; }
}

/* ── Responsive ────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .gtiles, .gtiles-3 { grid-template-columns: repeat(2, 1fr); }
  .gev-head { grid-template-columns: 1fr; }
  .gq-fields, .gshared { grid-template-columns: 1fr; }

  /* the photo keeps the whole screen; details are a sheet you pull up */
  .gviewer { display: none; grid-template-columns: 1fr; grid-template-rows: 1fr; }
  .gviewer.open { display: block; }
  .gv-stage { position: absolute; inset: 0; padding: .5rem; }
  .gv-stage img { max-height: 100%; max-width: 100%; }
  .gv-info {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
    max-height: 82dvh; padding: 0 1rem 1.25rem;
    border-left: 0; border-top: 1px solid var(--border);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    transform: translateY(calc(100% - 3.4rem));
    transition: transform .28s ease;
    overscroll-behavior: contain;
  }
  .gviewer.info-open .gv-info { transform: translateY(0); }
  .gv-handle {
    position: sticky; top: 0; z-index: 1;
    display: flex; align-items: center; justify-content: space-between; gap: .75rem;
    width: 100%; height: 3.4rem; padding: 0;
    background: var(--surface); border: 0; color: var(--text);
    font: inherit; font-size: .82rem; text-align: left; cursor: pointer;
  }
  .gv-handle::before {
    content: ""; position: absolute; top: .45rem; left: 50%; transform: translateX(-50%);
    width: 40px; height: 4px; border-radius: 2px; background: var(--border);
  }
  .gv-handle span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .gv-handle i { font-style: normal; color: var(--muted); transition: transform .28s ease; }
  .gviewer.info-open .gv-handle i { transform: rotate(180deg); }
  .gviewer:not(.gviewer-static) .gv-title { display: none; }   /* the handle already shows it */
  .gviewer-static .gv-title { font-size: 1.15rem; }
  .gv-nav { top: auto; bottom: 4.6rem; transform: none; }
  .gv-prev { left: .6rem; }
  .gv-next { right: .6rem; }
  .gv-close { top: .6rem; right: .6rem; }
  /* The info sheet covers the bottom 3.4rem, so the zoom cluster rides at the
     same height as the arrows, between them. It only appears once the photo is
     actually zoomed: pinch is how you zoom on a phone, so until then the
     buttons would be clutter over the photo. +/- go for the same reason. */
  .gv-zoom { left: 50%; right: auto; bottom: 4.6rem; transform: translateX(-50%); max-width: calc(100% - 7rem); }
  .gviewer:not(.zoomed) .gv-zoom { display: none; }
  .gv-zoom .gv-zb[data-z="in"], .gv-zoom .gv-zb[data-z="out"] { display: none; }

  /* share page: normal stacked layout, no sheet */
  .gviewer-static { position: static; display: block; }
  .gviewer-static .gv-stage { position: static; padding: 0; }
  .gviewer-static .gv-stage img { max-height: 70vh; }
  .gviewer-static .gv-info {
    position: static; transform: none; max-height: none; padding: 1.25rem 1rem;
    border: 1px solid var(--border); border-radius: var(--r-lg); margin-top: 1rem;
  }
  .gviewer-static .gv-handle { display: none; }
}

@media (max-width: 560px) {
  /* The logo stays at full size here, so the space for the links is found by
     tightening everything around it instead. The nav's own gutter is not
     touched — it clamps, see .gnav. */
  .gnav-side { gap: .1rem; }
  .gnav-side a:not(.gnav-logo):not(.gnav-profile) { padding: .4rem .4rem; font-size: .74rem; letter-spacing: 0; }
  .gnav-logo { margin-right: .25rem; }
  .gnav-profile { width: 34px; height: 34px; margin-left: .15rem; }
  .gtile { padding: .75rem; }
  .gq { grid-template-columns: 64px 1fr; }
  .gq-img { width: 64px; height: 64px; }
  .gv-nav { width: 38px; height: 38px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
}

/* ── Tag index ─────────────────────────────────────────────────────────── */
.gmode a { padding: .4rem .75rem; font-size: .78rem; color: var(--muted); text-decoration: none; }
.gmode a + a { border-left: 1px solid var(--border); }
.gmode a.on { background: var(--accent-soft); color: var(--text); }
.gabc { margin-top: .2rem; }
.gtaglist { list-style: none; columns: 320px; column-gap: 1.5rem; }
.gtaglist li { break-inside: avoid; }
.gtaglist a {
  display: grid; grid-template-columns: minmax(0, 1fr) 90px 3.5rem; align-items: center; gap: .6rem;
  padding: .35rem .5rem; border-radius: 6px; text-decoration: none; font-size: .82rem;
}
.gtaglist a:hover { background: var(--surface); }
.gtl-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gtaglist a:hover .gtl-name { color: var(--accent-ink); }
.gtl-bar { height: 5px; border-radius: 3px; background: var(--surface2); overflow: hidden; }
.gtl-bar i { display: block; height: 100%; background: var(--accent); opacity: .65; }
.gtl-n { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) { .gtaglist a { grid-template-columns: minmax(0, 1fr) 56px 2.8rem; } }

/* per-page picker + "fetching the next page" hint */
.gper { display: inline-flex; align-items: center; gap: .4rem; font-size: .78rem; color: var(--muted); }
.gper select { padding: .35rem .5rem; }
.gviewer.loading-more .gv-next::after {
  content: ""; position: absolute; inset: -5px; border-radius: 50%;
  border: 2px solid transparent; border-top-color: var(--accent-ink);
  animation: gvspin .7s linear infinite;
}
@keyframes gvspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .gviewer.loading-more .gv-next::after { animation: none; } }

/* ═══════════════════════════════════════════════════════════════════════════
   RAINBOW THEME — the gallery's half
   Turned on the same way as the rest of the site: <html data-theme="rainbow">.

   theme.css already does the work; it just names the index's selectors
   (.hero h1 em, .section-label, .btn-primary, .card:hover …). These are the
   gallery's equivalents, using theme.css's own --accent-grad / --accent-grad-h
   so there is still exactly one place the flag colours are written down.
   Kept here rather than added to theme.css so the gallery's class names stay
   the gallery's business.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Gradient text ── background-clip:text paints the stripes through the
   glyphs; the -webkit- prefix is still required on Safari and all of iOS. */
[data-theme="rainbow"] h1.gat,
[data-theme="rainbow"] .gtile-plus,
[data-theme="rainbow"] .ga-tok-who .ga-tok {
  background-image: var(--accent-grad-h);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
  font-weight: 800;   /* gradient text can't carry a shadow, so add weight */
}
/* .ga-tok draws its own underline from currentColor, which is transparent
   once the fill is a gradient — give it back a real colour. */
[data-theme="rainbow"] .ga-tok-who .ga-tok { border-bottom-color: #ff8c00; }

/* ── Gradient fill ── white text with a shadow, so it reads over any stripe. */
/* The flag is darkened 55% under the label. White over the raw stripes is
   3.9:1 at best — #ffed00 is nearly as bright as the text — and this button
   carries an email address worth reading. At 55% the worst stripe reaches
   5.5:1 and the flag is still unmistakably the flag. */
[data-theme="rainbow"] .gbtn-main {
  background-image: linear-gradient(rgba(0,0,0,.55), rgba(0,0,0,.55)), var(--accent-grad);
  background-color: transparent;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .65);
}
[data-theme="rainbow"] .gbtn-main:hover:not(:disabled) {
  background-image: linear-gradient(rgba(0,0,0,.4), rgba(0,0,0,.4)), var(--accent-grad-h);
  color: #fff;
}

/* ── Gradient edge ── a gradient can't be a border-colour, so it's a padded
   gradient backdrop with the page colour masked back in on top. */
[data-theme="rainbow"] .ga-takedown {
  background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    var(--accent-grad-h) border-box;
  border: 2px solid transparent;
}

/* ── Gradient shadow ── isn't a thing either, so stack six offset colours. */
[data-theme="rainbow"] .gtile:hover,
[data-theme="rainbow"] .gev:hover,
[data-theme="rainbow"] .gsu:hover {
  border-color: #ff8c00;
  box-shadow:
    -6px 0 10px -6px #e40303,
    -3px 0 10px -6px #ff8c00,
     0   0 10px -6px #ffed00,
     3px 0 10px -6px #008026,
     6px 0 10px -6px #004cff,
     9px 0 10px -6px #732982;
}

/* Gradient text is a readability risk when more contrast is asked for —
   #ffed00 and #008026 sit very differently against the dark. Flatten it. */
@media (prefers-contrast: more) {
  [data-theme="rainbow"] h1.gat,
  [data-theme="rainbow"] .gtile-plus,
  [data-theme="rainbow"] .ga-tok-who .ga-tok {
    background-image: none;
    -webkit-text-fill-color: var(--accent-ink);
            color: var(--accent-ink);
  }
}
