/* style — controls left, gallery right.
   One section of the old style.css. The pieces load in filename order
   and that order is the cascade, so do not rearrange them. */
/* ---------- layout: controls left, gallery right ---------- */
/* No overflow on #layout: any overflow value other than visible makes it a
   scroll container, which silently kills position:sticky for everything
   inside (tabs, gallery pane). If the panel is narrower than the two panes'
   floors, the body scrolls horizontally; below 664px they stack anyway. */
#layout {
  display: flex; gap: 8px; align-items: flex-start;
  /* The sign-in gate is absolutely positioned against this row, so the row has
     to be the containing block — otherwise the gate resolves against the page
     and swallows the header, taking the profile chip (and the only way to sign
     in) with it. z-index:0 makes this a stacking context as well, which pins
     everything inside it — the gate included — below the header and the
     settings drawer.

     TEN, NOT ZERO — and zero is why the model list could not be reached.

     Simon: "when on a small screen I cant access the ai models towards bottom
     of the dropdown list in the create space... at the moment bottom of list
     is overlapped by the section with daily spend in it."

     #spendbar is a sibling of this row inside #app, pinned to the foot at
     z-index 9, and it is NOT inside this stacking context. This row was, at
     zero. So every layer inside here was capped at zero however high its own
     z-index went, and the model list — sitting at --z-menu, 300, which reads
     like it settles the question — painted underneath a strip at 9. On a
     short window the list is tall enough to reach the foot, and its last few
     rows went behind the spend strip: visible enough to know they are there,
     impossible to click.

     Ten clears the spend strip and nothing else. The rail is 100, the drawer
     and the header 200, the settings modal 900, so the gate this context
     exists to pin is still pinned by all of them; the only thing that changes
     is that a menu in the make column is no longer under a status bar.

     Raising the list's own z-index cannot fix this and neither can lowering
     the strip's — the first is capped by this line and the second would let
     the column scroll over the strip. The context is the thing. */
  position: relative; z-index: 10;
}
/* Left generation panel: fixed width, stretched to the full panel height. The
   gallery fills the remaining width. When the panel is too narrow the two
   stack (media query) and #main returns to its natural height. */
#main {
  width: 380px; flex: 0 0 380px; display: flex; flex-direction: column;
  min-height: calc(100vh - var(--hdr-h, 50px));   /* full panel height below the fixed header */
}
/* Luma Modify needs elbow room: the work panel doubles and the gallery
   drops to roughly half its usual share (still fully usable). */
body.luma-wide #main {
  width: min(760px, 64vw); flex: 0 0 min(760px, 64vw);
}
#gallery-resizer { display: none; }
body.resizing { cursor: col-resize; }
body.resizing #gallery { pointer-events: none; }
#gallery-pane {
  flex: 1 1 auto; min-width: 250px; position: sticky; top: 40px;
  /* SQUARE, NOT A BOX. The seam between the gallery and the generation space
     is a single line down the left edge, not a rounded rectangle drawn all
     the way round the gallery — a border on three more sides than the one
     that is actually a boundary between two things reads as a mistake, and
     a radius on a pane that is flush with the top and right of the window
     has no corner to round. More outer padding too, so the grid does not
     start right at the line. */
  border-radius: 0; border: none; border-left: 1px solid var(--border);
  padding: 0 10px 0 14px; --gal-w: 96px;
  max-height: calc(100vh - 56px); display: flex; flex-direction: column;
}
/* EACH CARD ONLY IN ITS OWN VIEW. Simon: "When we are in the elements
   section, we shouldn't show generic images and videos generating there."
   A character on its way to Elements carries data-elements (49b-job-card);
   the strip carries the view it is in (47-gallery-load). */
#gallery-pending.mode-elements .pending-card:not([data-elements]) { display: none; }
/* BUT A CHARACTER BEING MADE SHOWS EVERYWHERE. Simon: "When you click to
   generate a character in the character library, it doesn't show a loading
   panel, which is what we need to be able to know a character is
   generating." The rule that hid character cards outside the Elements view
   was never asked for -- only the other direction was -- and it meant a
   character started from anywhere but the @ view generated invisibly. */
#gallery-pending {
/* Simon: "there needs to be a horizontal scrollbar on the generation row
   when the results exceed the visible squares, for image and video."
   This used to be a grid, same as the historical #gallery below it — so
   once more results were in flight than fit on one line, it just grew a
   second row (and a third, ...), pushing the real gallery grid further
   down the pane instead of staying put. It's a flex row now: one line,
   fixed-size tiles (see #gallery-pending .gal-item in 22-width-ruler.css
   -- they must NOT shrink or this becomes the old squeeze-to-fit look
   instead of the requested scrollbar). Image and video pending cards
   already share this one element (see pendWrap in
   js/panel/46-gallery-cards.js and 49b-job-card.js) — no separate fix
   needed per media type. Overflow direction is set just below, not
   here — see the history in the comment on that rule. */
  display: flex; flex-wrap: wrap; overflow-x: hidden; overflow-y: visible;
  gap: 7px; align-content: start; padding-bottom: 2px;
}
/* THE SAME ROW HID WHAT WAS GENERATING — FIRST IN AE, NOW ON DESKTOP TOO.

   Round one, Simon: "in the after effects app, generations that are
   happening are appearing in a row separate to the main gallery, but
   this causes issues as you can't see what's generating. It should be
   within the main gallery view when in the After Effects app." That
   shipped as a CEP-only override (html:not(.desktop) #gallery-pending)
   on the theory that the desktop app's own window is normally wide
   enough for the single-line scrolling row to show most of what's in
   flight — only AE's much narrower docked panel actually hid tiles off
   the right edge.

   Round two, Simon again, this time plainly about the desktop app: "The
   way items are previewed in gallery as they're generating... is no
   longer working correctly. They're currently loading above the gallery
   in a separate section... They should just be loading straight into
   the gallery section as they did before." Same complaint, same cause —
   a narrow window, a busy queue, or just more than one row's worth of
   pending tiles all reproduce the exact "cut off behind a scrollbar"
   problem AE had, and the CEP-only scoping meant the desktop app kept
   shipping the old behavior this was supposed to have fixed everywhere.

   So the wrap-instead-of-scroll rule above is no longer conditional on
   .desktop — #gallery-pending already sits inside #gallery-scroll, the
   SAME scrolling pane the finished #gallery grid scrolls in (22-width-
   ruler.css), so every pending tile now stays visible in the ordinary
   vertical scroll a person is already using, right above the finished
   results, in both the CEP/AE panel and the desktop app alike. */
#gallery-pending:not(:empty) { margin-bottom: 7px; }
.pending-card { cursor: default; }
/* The maker's mark while it is still being made. Above the blur and the ring,
   and it stays put on hover — a pending card has no info strip to make room
   for, so the fade that applies to a finished tile would be hiding the one
   thing this card is being given. */
.pending-card .gmodel { z-index: 4; }
.gal-item.pending-card:hover .gmodel { opacity: 1; }

/* 16:9 generating placeholder — blurry out-of-focus backdrop + big progress ring */
.pend-thumb {
  position: relative; width: 100%; aspect-ratio: 16/9; overflow: hidden;
  background: #1a1a1f;
}
/* Interim base frame shown on a running pipeline card — full strength, so you
   can judge the image while the upscale runs; progress shrinks to a subtle
   corner badge instead of sitting on top of the art. */
.pend-preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; z-index: 1;
  -webkit-user-drag: none;
}
/* WHAT THE INTERIM FRAME IS. Over the picture rather than beside it, because
   the thing being labelled is the picture -- and top-right, which is the one
   corner of a pending card nothing else uses (the progress badge is
   bottom-left, the state line bottom-right). */
.pend-proxy {
  position: absolute; top: 5px; right: 5px; z-index: 3;
  padding: 2px 6px; border-radius: 3px;
  background: var(--scrim-light, rgba(0, 0, 0, .62));
  color: #fff; font-size: 8px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  pointer-events: none;
}
.pend-thumb.has-preview .pend-blur { display: none; }
.pend-thumb.has-preview .pend-center,
.pend-thumb.has-preview .pend-state { z-index: 2; pointer-events: none; }
/* AND THE RING STAYS IN THE MIDDLE OF IT.

   Simon: "Using COVERT gen, and just the low-res proxy is loaded. Make sure
   that the loading wheel stays in the middle of the video when the high-res
   is being generated."

   It used to shrink to a 32px badge in the bottom-left corner the moment a
   preview arrived, on the reasoning that the picture is the thing being
   judged and progress should get out of its way. That reasoning holds for a
   card whose interim frame IS the answer, and fails for this one: what is
   showing is a low-res proxy of a picture that has not finished, and a
   corner badge reads as a finished tile with a decoration on it. The
   difference between "here it is" and "here is roughly what it will be" has
   to be the loudest thing on the card, and in the middle is where the eye
   already goes.

   Smaller than the no-preview ring and with a dark track, so it reads over
   art rather than over the flat blur it was designed against. */
.pend-thumb.has-preview .pend-center { inset: 0; }
.pend-thumb.has-preview .pend-ring { width: 38%; max-width: 64px; }
.pend-thumb.has-preview .pend-ring circle { stroke-width: 7; }
.pend-thumb.has-preview .pend-ring .pr-track { stroke: rgba(0, 0, 0, .45); }
.pend-thumb.has-preview .pend-pct {
  font-size: 15px; text-shadow: 0 1px 6px rgba(0, 0, 0, .8);
}
.pend-thumb.has-preview .pend-pct i { display: none; }
.pend-thumb.has-preview .pend-state {
  left: 0; right: 0; bottom: 5px; text-align: center;
  font-size: 8.5px; opacity: .95;
}

.pend-blur {
  position: absolute; inset: -30%;
  /* pink + blue only — no other hues in the mix */
  background:
    radial-gradient(38% 55% at 28% 32%, rgba(var(--accent-rgb),.55), transparent 70%),
    radial-gradient(42% 60% at 74% 66%, rgba(90,162,255,.45), transparent 72%),
    radial-gradient(46% 58% at 60% 22%, rgba(var(--accent-rgb),.35), transparent 70%),
    linear-gradient(130deg, #23232b, #14141a);
  filter: blur(24px) saturate(120%);
  transform: scale(1.15);
  /* rotate/drift the pink & blue blobs and pulse their saturation + luminance —
     no hue-rotate, so it never leaves the pink/blue palette. */
  animation: pendDrift 14s ease-in-out infinite alternate, pendGlow 8s ease-in-out infinite;
}
@keyframes pendDrift {
  0%   { transform: scale(1.14) translate(-4%, 2%) rotate(-8deg); }
  33%  { transform: scale(1.22) translate(4%, -3%) rotate(6deg); }
  66%  { transform: scale(1.18) translate(-2%, 4%) rotate(10deg); }
  100% { transform: scale(1.24) translate(3%, -2%) rotate(-4deg); }
}
/* saturation + luminance pulse only — hue stays put */
@keyframes pendGlow {
  0%   { filter: blur(24px) saturate(110%) brightness(96%); }
  50%  { filter: blur(24px) saturate(175%) brightness(120%); }
  100% { filter: blur(24px) saturate(110%) brightness(96%); }
}
.pend-center {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
}
.pend-ring { width: 54%; max-width: 84px; aspect-ratio: 1; display: block; transform: rotate(-90deg); }
.pend-ring circle { fill: none; stroke-width: 6; stroke-linecap: round; }
.pend-ring .pr-track { stroke: rgba(255,255,255,.16); }
.pend-ring .pr-spin  { stroke: rgba(255,255,255,.32); stroke-dasharray: 40 236;
  transform-origin: 50% 50%; animation: spin 1.15s linear infinite; }
.pend-ring .pr-fill  { stroke: var(--accent); transition: stroke-dashoffset .4s ease;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.55)); }
.pend-pct {
  position: absolute; font-weight: 800; font-size: 20px; color: #fff;
  text-shadow: 0 1px 6px var(--scrim-mid);
}
.pend-pct i { font-style: normal; font-size: 11px; font-weight: 700; opacity: .7; margin-left: 1px; }
.pend-state {
  position: absolute; bottom: 5px; left: 0; right: 0; text-align: center;
  font-size: 8.5px; letter-spacing: .4px; color: rgba(255,255,255,.85);
  text-shadow: 0 1px 4px var(--scrim-heavy);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 8px;
}
.pending-card .rm {
  position: absolute; top: 4px; right: 4px; width: 18px; height: 18px;
  border-radius: 50%; border: none; cursor: pointer; z-index: 3;
  background: var(--scrim-mid); color: #fff; font-size: 11px; line-height: 16px; text-align: center; padding: 0;
}
.pending-card .rm:hover { background: var(--accent); }
.pending-card.failed .pend-blur { background: linear-gradient(130deg, #3a1520, #1a1013); animation: none; }
.pending-card.failed .pr-fill { stroke: var(--red); }
.pending-card.failed .pr-spin { display: none; }
.pending-card.failed .pend-pct { color: var(--red); font-size: 16px; }
.gallery-head { margin: 8px 0 4px; }
/* Faint play badge over video thumbnails — marks them as videos at a glance.
   Fades away while the hover preview is playing so it never sits on motion. */
.gthumb .gplay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  pointer-events: none; z-index: 2; opacity: .55; transition: opacity .18s ease;
}
.gthumb .gplay svg {
  width: 26%; max-width: 44px; min-width: 22px; aspect-ratio: 1;
  color: rgba(255,255,255,.85);
  filter: drop-shadow(0 1px 6px rgba(0,0,0,.65));
}
.gal-item:hover .gplay { opacity: 0; }

/* WHERE IT IS, FOR TWO SECONDS.

   "Reveal in gallery" scrolls the tile into the middle of the view, which on
   a grid of ninety near-identical stills answers almost nothing on its own.
   The ring says which one, and then gets out of the way — a highlight that
   stayed would be one more piece of state to clear. */
/* ---- "THE PICTURE IS NOT READY, THE GENERATION IS FINE" ----

   A corner badge, not an overlay. The picture underneath is the thing being
   judged and covering it to explain that it is not ready yet would be its own
   kind of unhelpful — but a blurry or black tile with no explanation was
   being read as a bad generation, and people were re-running clips that were
   perfectly good. */
/* The upscale toggle when a clip HAS a 4K version: lit while the 4K is the
   one you are looking at, plain while the original is. One control, two
   directions, and its state readable without hovering it. */
.gact-upres.on { color: var(--accent); border-color: var(--accent); }
/* The roto view switch, lit while the luma matte is the one on screen — the
   matte is the default and the answer to "did it work", so lit means "you are
   looking at the one that tells you". */
.gact-roto.on { color: var(--accent); border-color: var(--accent); }

.gal-prep {
  position: absolute; left: 5px; top: 5px; z-index: 4;
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(10, 10, 12, .82); color: #d8d8de;
  border-radius: 3px; padding: 3px 6px; font-size: 8.5px; font-weight: 700;
  letter-spacing: .2px; pointer-events: auto;
}
.gal-prep-ring {
  width: 8px; height: 8px; border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .28); border-top-color: var(--accent);
  animation: galPrepSpin .8s linear infinite;
}
@keyframes galPrepSpin { to { transform: rotate(360deg); } }

.gal-item.gal-found { animation: galFound 2.2s ease-out; }
.gal-item.gal-found::after {
  content: ''; position: absolute; inset: -3px; border-radius: 3px;
  border: 2px solid var(--accent); pointer-events: none;
}
@keyframes galFound {
  0%, 55% { box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .45); }
  100%    { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
}
/* Small corner tag on media captured from the AE timeline (reference-slot
   capture buttons + the Add-AE-frame tile) — marks it as source material
   brought in from After Effects rather than a generation. */
.gthumb .ae-badge {
  position: absolute; top: 6px; left: 6px; z-index: 3;
  padding: 2px 6px; border-radius: 3px;
  font-size: 9px; font-weight: 700; letter-spacing: .4px; line-height: 1.2;
  color: rgba(255,255,255,.92); background: rgba(18,18,22,.72);
  border: 1px solid rgba(255,255,255,.22);
  pointer-events: none;
}
/* Characters mode: the toggle just highlights — the type filters stay
   visible and clickable as the way back to the normal gallery. */
#btn-gallery-elements.active, #btn-gallery-3d.active { color: var(--accent); background: var(--accent-soft); }
/* 3D model tile without a rendered preview: centred cube glyph */
.m3d-ph {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); background: #1b1b1f;
}
.m3d-ph svg { width: 34px; height: 34px; opacity: .55; }