.footer-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  align-items: stretch;
  /* v07zn — Hugo: footer panels were empty on first paint then
     stretched up half a second later when data arrived. Pin a
     min-height that matches the loaded size (4 activity rows ×
     56 px + header + padding ≈ 300 px) so the row stays the
     same height regardless of fetch state. */
  min-height: 300px;
}

/* v07z9 — Footer back to dynamic height. align-items: stretch on
   the grid already makes all three cards the same height as the
   tallest sibling. With limit=4 activity rows + 3-note stack at
   original sizes, all three cards naturally settle at around
   300 px — no fixed pixel value needed. */

/* Equal-height: every footer card stretches to the tallest sibling.
   Inner content scrolls if it exceeds that shared height. */
.footer-row > .sortable-panel,
.footer-row > .activity-card,
.footer-row > .media-card,
.footer-row > .notes-card {
  height: 100%;
  min-height: 0;
}
.footer-row .sortable-panel > .activity-card,
.footer-row .sortable-panel > .media-card,
.footer-row .sortable-panel > .notes-card {
  height: 100%;
  min-height: 0;
}
.activity-card,
.media-card,
.notes-card {
  overflow: hidden;
}
.footer-row :is(.activity-card, .media-card, .notes-card)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}
/* v07z4 — Today's Notes design: quote-style list. Each note item is
   a mini quote block, all stacked, scrollable inside the card. The
   spacing + glyph keep the "designed" feel Hugo liked vs the bare
   activity-row format. */
.notes-empty {
  font-size: var(--fs-12);
  font-style: italic;
  color: color-mix(in srgb, var(--ink-cream-6) 55%, transparent);
  text-align: center;
  padding: 18px 14px;
}
.notes-today-scroll {
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-right: 2px;
}
.notes-today-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }
.notes-today-item {
  padding-bottom: 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--card-border) 10%, transparent);
}
.notes-today-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}
.notes-today-item .notes-quote {
  margin: 0;
  font-style: italic;
  font-size: var(--fs-13);
  line-height: 1.45;
  color: var(--ink-on-dark);
  text-wrap: pretty;
}
.notes-today-item .notes-glyph {
  font-family: var(--font-display);
  font-style: normal;
  font-size: var(--fs-22);
  color: var(--amber);
  margin-right: 3px;
  line-height: 0;
  vertical-align: -0.15em;
}
.notes-today-item .notes-meta {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 5px;
  font-size: var(--fs-10-5);
  color: var(--ink-on-dark-muted);
}
.notes-today-item .notes-range {
  font-family: var(--font-mono);
  font-size: var(--fs-10);
  letter-spacing: var(--track-02);
}
.activity-list {
  overflow-y: auto;
  /* v07z3 — Hugo: scroll internally without a visible scrollbar.
     The footer cards are tight enough that even a 6px scrollbar
     made the layout feel busy. Drag with wheel/trackpad like a
     mobile-style list. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  flex: 1 1 auto;
  min-height: 0;
}
.activity-list::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* Dark footer cards: re-tint internal text/borders */
.activity-card,
.media-card,
.notes-card { color: var(--ink-on-dark); }

.activity-card .rc-title,
.media-card .rc-title,
.notes-card .rc-title { color: var(--ink-on-dark-muted); }

.activity-card .rc-iconbtn,
.media-card .rc-iconbtn,
.notes-card .rc-iconbtn {
  background: color-mix(in srgb, var(--mix-light) 6%, transparent);
  border-color: var(--card-border-dark);
  color: var(--ink-on-dark);
}
.activity-card .rc-iconbtn:hover,
.media-card .rc-iconbtn:hover,
.notes-card .rc-iconbtn:hover { background: color-mix(in srgb, var(--mix-light) 12%, transparent); }

.activity-card { display: flex; flex-direction: column; gap: 10px; }
.activity-list { display: flex; flex-direction: column; gap: 10px; }
.activity-row {
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: 10px;
  /* v07za — Center the icon vertically with the WHOLE text block
     and horizontally within its 32 px column. align-items: center
     handles vertical alignment; justify-items: center centers the
     icon column's content (the 28 px circle inside the 32 px
     cell). */
  align-items: center;
  justify-items: start;
}
.activity-icon {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 1px solid;
  display: grid; place-items: center;
  justify-self: center;
}
/* v07zp — Skeleton ghost rows while /api/logs is in flight on a
   hard refresh. Same height as real rows so the card does not
   stretch up when data lands. Soft pulse so it reads as
   "loading" not "broken". */
.activity-card .activity-row--ghost {
  cursor: default;
  pointer-events: none;
}
.activity-icon--ghost {
  background: color-mix(in srgb, var(--mix-light) 6%, transparent) !important;
  border-color: color-mix(in srgb, var(--white) 8%, transparent) !important;
}
.activity-text--ghost {
  height: 12px;
  width: 78%;
  border-radius: 4px;
  background: color-mix(in srgb, var(--mix-light) 8%, transparent);
  animation: ghost-pulse var(--dur-pulse) ease-in-out infinite;
}
.activity-meta--ghost {
  height: 9px;
  width: 42%;
  margin-top: 8px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--mix-light) 5%, transparent);
  animation: ghost-pulse var(--dur-pulse) ease-in-out infinite;
}
@keyframes ghost-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}
.activity-text {
  font-size: var(--fs-13);
  color: var(--ink-on-dark);
  line-height: 1.35;
  /* v07zc — Hugo: long note bodies were making the rows grow,
     pushing the whole footer row taller than its siblings (which
     then made the WHOLE row shorter on Railway where notes were
     shorter). Clamp body to 2 lines so every row is identical
     height regardless of content. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.activity-frame { color: var(--ink-on-dark-muted); font-style: italic; }
.activity-meta {
  font-size: var(--fs-11);
  color: var(--ink-on-dark-muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* v07zc/v07ze — Hugo: rows must be a fixed 56 px (otherwise the
   panel resizes when a long note clamps to 2 lines). But the row's
   INNER content should center vertically regardless of whether the
   body is 1 line or 2 lines — without reserving 2-lines of empty
   space below a short body. Solution: lock row height + use
   align-content: center so the grid centers its row tracks. No
   min-height on the text element. */
.activity-card .activity-row {
  height: 56px;
  align-content: center;
  align-items: center;
}

.media-card { display: flex; flex-direction: column; gap: 10px; }
.thumb-row {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.media-thumb {
  aspect-ratio: var(--project-aspect, 21 / 9);
  border-radius: var(--r-sm);
  border: 1px solid color-mix(in srgb, var(--card-border) 35%, transparent);
  box-shadow: inset 0 1px 0 var(--media-thumb-inset-hi);
  cursor: pointer;
  transition: transform var(--dur-2) ease;
}
.media-thumb:hover { transform: translateY(-2px); }

.dropzone {
  border: 1.5px dashed color-mix(in srgb, var(--card-border) 40%, transparent);
  border-radius: var(--r-sm);
  padding: 14px 12px;
  text-align: center;
  background: color-mix(in srgb, var(--mix-light) 4%, transparent);
  display: flex; flex-direction: column;
  align-items: center; gap: 4px;
  cursor: pointer;
  transition: background var(--dur-2) ease;
}
.dropzone:hover { background: color-mix(in srgb, var(--mix-light) 8%, transparent); }
.dz-icon { color: var(--ink-on-dark-muted); }
.dz-text { font-size: var(--fs-13); color: var(--ink-on-dark); font-weight: var(--fw-med); }
.dz-sub { font-size: var(--fs-11); color: var(--ink-on-dark-muted); }

.notes-card { display: flex; flex-direction: column; gap: 8px; }
/* v07zb — Hugo: panel shrunk when clicking + to compose because the
   form's natural content height was less than the 3-note stack, and
   align-items: stretch on the footer row matches to the tallest
   sibling — so when notes shrunk, the whole row shrunk. Fix: give
   the compose form + empty state the same min-height as the stack,
   AND make the compose body grow to fill the card. The stack itself
   already uses flex: 1 1 auto; min-height: 0; so it naturally fills
   whatever the parent card height is. */
.notes-compose {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  min-height: 0;
}
.notes-compose-body {
  flex: 1 1 auto;
  min-height: 110px;
  resize: none;
  width: 100%;
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-13);
  line-height: 1.5;
  color: var(--ink-on-dark);
  background: color-mix(in srgb, var(--black) 18%, transparent);
  border: 1px solid var(--card-border-dark);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  outline: none;
  box-sizing: border-box;
}
.notes-compose-body:focus {
  border-color: color-mix(in srgb, var(--leaf) 70%, transparent);
  box-shadow: 0 0 0 var(--ring-w) color-mix(in srgb, var(--leaf) 15%, transparent);
}
.notes-compose-range {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-12);
  color: var(--ink-on-dark);
  background: color-mix(in srgb, var(--black) 18%, transparent);
  border: 1px solid var(--card-border-dark);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  outline: none;
  box-sizing: border-box;
}
.notes-compose-range:focus {
  border-color: color-mix(in srgb, var(--leaf) 70%, transparent);
  box-shadow: 0 0 0 var(--ring-w) color-mix(in srgb, var(--leaf) 15%, transparent);
}
.notes-compose-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.notes-compose-cancel,
.notes-compose-save {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-12);
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-04);
  padding: 7px 14px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-1) ease, filter var(--dur-1) ease;
}
.notes-compose-cancel {
  background: color-mix(in srgb, var(--mix-light) 6%, transparent);
  border: 1px solid var(--card-border-dark);
  color: var(--ink-on-dark-muted);
}
.notes-compose-cancel:hover { background: color-mix(in srgb, var(--mix-light) 12%, transparent); color: var(--ink-on-dark); }
.notes-compose-save {
  background: linear-gradient(180deg, var(--olive-6) 0%, var(--olive-4) 100%);
  border: 1px solid color-mix(in srgb, var(--surface-forest-6) 85%, transparent);
  color: var(--ink-cream);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 40%, transparent);
}
.notes-compose-save:hover { filter: brightness(1.06); }
/* Keep the dashboard empty-state pinned to the same height the
   stack would occupy so the row doesn't change height when notes
   appear. SCOPED to .notes-card so it doesn't leak into the shot
   modal's NotesPanel .notes-empty (which has different layout
   needs and was visually overlapping with the form below). */
.notes-card .notes-empty {
  flex: 1 1 auto;
  min-height: 220px;
  display: grid;
  place-items: center;
}
/* (18 Sep 2026) The empty card's ORNAMENT slot: the whole empty box (the card's children are positioned,
   .glass-dark > *), out of flow, so the centred line never moves. No paint of its own: the paper family
   paints it through a mask (skin-paper.css, --sk-orn-empty-*) only when a skin gives it a colour and
   an image; the shape's size and place are the mask's, and the box exists only while the skin paints it
   (the family gives it its content). */
.notes-card .notes-empty::before {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* v07z8 — ORIGINAL sizes restored. No shrinkage — 14 px italic body,
   28 px amber glyph, 11 px meta. Stack of 3 notes gets these full
   sizes; panel is now 380 px tall so they fit. */
.notes-quote {
  margin: 0;
  font-style: italic;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--ink-on-dark);
  position: relative;
  padding-left: 4px;
  text-wrap: pretty;
}
.notes-glyph {
  font-family: var(--font-display);
  font-style: normal;
  font-size: var(--fs-28);
  color: var(--amber);
  margin-right: 4px;
  line-height: 0;
  vertical-align: -0.1em;
}
.notes-meta {
  display: flex; justify-content: space-between;
  font-size: var(--fs-11); color: var(--ink-on-dark-muted);
  padding-top: 4px;
}
.notes-range { font-feature-settings: var(--tnum); }
/* Stack of up to 3 quote-style notes at FULL original size. */
.notes-stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow: hidden;
  flex: 1 1 auto;
  min-height: 0;
}
.notes-stack-item {
  padding-bottom: 11px;
  border-bottom: 1px solid color-mix(in srgb, var(--card-border) 10%, transparent);
}
.notes-stack-item:last-child {
  padding-bottom: 0;
  border-bottom: none;
}
/* v07zz279 — rows click-through to the Notes page */
.notes-stack-item--link { cursor: pointer; border-radius: var(--r-sm); margin: 0 -6px; padding-left: 6px; padding-right: 6px; transition: background var(--dur-1) ease; }
.notes-stack-item--link:hover { background: color-mix(in srgb, var(--ink-on-accent) 7%, transparent); }
.notes-stack-item--link:focus-visible { outline: var(--ring-w) solid color-mix(in srgb, var(--gold-5) 60%, transparent); outline-offset: var(--ring-offset); }
.notes-head-actions { display: flex; align-items: center; gap: 8px; }
.notes-viewall-btn {
  font: inherit; font-size: var(--fs-10); font-weight: var(--fw-bold); letter-spacing: var(--track-08);
  text-transform: uppercase; color: color-mix(in srgb, var(--ink-on-accent) 72%, transparent);
  background: color-mix(in srgb, var(--ink-on-accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--ink-on-accent) 18%, transparent);
  border-radius: var(--r-7); padding: 4px 9px; cursor: pointer; white-space: nowrap;
  transition: background var(--dur-1) ease, color var(--dur-1) ease;
}
.notes-viewall-btn:hover { background: color-mix(in srgb, var(--ink-on-accent) 16%, transparent); color: color-mix(in srgb, var(--ink-on-accent) 95%, transparent); }
.notes-stack-item .notes-quote {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pagination-dots {
  display: flex; justify-content: center; gap: 6px;
  padding-top: 6px;
}
.pdot {
  width: 6px; height: 6px; border-radius: 50%;
  background: color-mix(in srgb, var(--card-border) 25%, transparent);
  cursor: pointer;
  transition: background var(--dur-2) ease;
  border: none;
  padding: 0;
}
.pdot:hover { background: color-mix(in srgb, var(--card-border) 50%, transparent); }
.pdot.active { background: var(--leaf); }

/* The t06d solid-forest override was removed (Hugo update). Activity
   card now inherits the shared .glass-dark treatment used by
   Media Uploads + Notes — same translucent glassy backdrop. */

/* Hugo update — the global .activity-list rule in views.css (used by
   the full-page ActivityView in the slide-over) was leaking onto the
   dashboard's Recent Activity card and dropping a cream overlay on
   top of the dark glass. Reset bg/border/radius here so the inner
   list is transparent and the .glass-dark card shows through cleanly. */
.activity-card .activity-list {
  background: transparent;
  border: none;
  border-radius: 0;
  overflow: visible;
}
.activity-card .activity-row {
  /* Strip the views.css per-row dividers since the rows now sit on
     the dark glass directly — borders would look like seams. */
  border-bottom: 1px solid color-mix(in srgb, var(--card-border) 18%, transparent);
}
.activity-card .activity-row:last-child {
  border-bottom: none;
}
/* v07zb — Per-row click affordance inside the dashboard Recent
   Activity card. Each row deep-links to its underlying entity (shot
   / asset / review). Distinct from the parent card's openSlide
   click; stopPropagation in JS handles the routing. */
.activity-card .activity-row--click {
  cursor: pointer;
  margin: 0 -8px;
  padding: 6px 8px;
  border-radius: var(--r-sm);
  transition: background var(--dur-1) ease, transform var(--dur-1) ease;
}
.activity-card .activity-row--click:hover {
  background: color-mix(in srgb, var(--mix-light) 6%, transparent);
  transform: translateX(2px);
}
.activity-card .activity-row--click:focus-visible {
  outline: var(--ring-w) solid var(--leaf);
  outline-offset: -2px;
}

/* t07b — make the activity card clickable + show a "View all →"
   call-to-action so users discover it opens the slide-over. */
.activity-card--clickable { cursor: pointer; transition: transform var(--dur-2) ease, box-shadow var(--dur-2) ease; }
.activity-card--clickable:hover { transform: translateY(-1px); }
.activity-card--clickable:hover .activity-card-cta { opacity: 1; }
.activity-card-cta {
  font-size: var(--fs-10);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-10);
  color: var(--ink-on-dark-muted);
  text-transform: uppercase;
  opacity: 0.65;
  transition: opacity var(--dur-2) ease;
}

/* Slide-over panel — opens from the right with the full ActivityView.
   Hugo update: GLASS panel (frosted backdrop blur) with rounded
   top-left and bottom-left corners so the cutout reads cleanly
   against the page underneath. The backdrop sits below the panel
   and is darker only AROUND the panel; the panel itself uses a
   cream glass tint with backdrop-filter for the frosted look. */
.activity-slide-backdrop {
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--shade-6) 45%, transparent);
  backdrop-filter: blur(var(--blur-scrim-2));
  -webkit-backdrop-filter: blur(var(--blur-scrim-2));
  z-index: var(--z-overlay);
  animation: activitySlideFade var(--dur-3) ease-out;
}
@keyframes activitySlideFade { from { opacity: 0; } to { opacity: 1; } }
.activity-slide-panel {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: min(720px, 92vw);
  /* TRUE glass panel — very transparent cream + heavy backdrop blur
     so the page behind shows through frosted. */
  background: color-mix(in srgb, var(--cream-2) 32%, transparent);
  backdrop-filter: blur(28px) saturate(1.25);
  -webkit-backdrop-filter: blur(28px) saturate(1.25);
  border-left: 1px solid color-mix(in srgb, var(--card-border) 45%, transparent);
  /* Rounded LEFT corners only. Right edge butts up against the
     viewport so its rounding would be invisible anyway. */
  border-radius: 18px 0 0 18px;
  box-shadow:
    -20px 0 60px rgba(0, 0, 0, 0.40),
    inset 1px 0 0 color-mix(in srgb, var(--cream) 55%, transparent);
  /* Panel itself does NOT scroll — overflow hidden so its rounded
     corners stay visible. Flex column lays out the close button +
     ActivityView; the ActivityView's internal list scrolls. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  animation: activitySlideIn var(--dur-5) var(--ease-pop);
}
/* The inner ActivityView fills the rest of the panel and is
   transparent (the panel layer carries the glass tint). */
.activity-slide-panel .activity-view {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: transparent !important;
  border: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  margin: 0;
}
.activity-slide-panel .activity-view .activity-head {
  flex: 0 0 auto;
}
/* The inner list takes the remaining height and scrolls — bottom
   of the panel stays visible as a fixed edge. */
.activity-slide-panel .activity-view .activity-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  margin-bottom: 18px;
  background: transparent;
  border: none;
}

/* ─── Activity drawer — opaque rows + tabs, brighter header text,
   internal glass reflections to enhance the frosted feel. ─────── */

/* Rows: opaque cream tile (was 0.45 cream inside a transparent panel,
   which read as washed-out grey through the glass). */
.activity-slide-panel .activity-row {
  background: color-mix(in srgb, var(--cream-21) 96%, transparent);
  border: 1px solid color-mix(in srgb, var(--card-border) 45%, transparent);
  border-radius: var(--r-panel);
  margin-bottom: 6px;
  padding: 12px 16px;
}
.activity-slide-panel .activity-row:hover {
  background: var(--cream-11);
}
.activity-slide-panel .activity-row--click {
  cursor: pointer;
  transition: background var(--dur-1) ease, transform var(--dur-1) ease;
}
.activity-slide-panel .activity-row--click:hover {
  transform: translateX(-2px);
  border-color: color-mix(in srgb, var(--tan-deep) 55%, transparent);
}

/* Filter tab pill row: opaque cream pill. */
.activity-slide-panel .activity-filter-row {
  background: color-mix(in srgb, var(--cream-21) 92%, transparent);
  border: 1px solid color-mix(in srgb, var(--card-border) 45%, transparent);
}
.activity-slide-panel .activity-filter.is-active {
  background: var(--ivory-3);
  color: var(--ink);
  border-color: color-mix(in srgb, var(--tan-deep) 55%, transparent);
}

/* Header text — brighter ink so it reads cleanly on the glass. */
.activity-slide-panel .activity-eyebrow {
  color: var(--ink-muted);
  font-weight: var(--fw-heavy);
  letter-spacing: var(--track-18);
}
.activity-slide-panel .activity-title {
  color: var(--ink);
  font-weight: var(--fw-med);
}
.activity-slide-panel .activity-sub {
  color: var(--ink-slide-sub);
  font-weight: var(--fw-med);
}

/* Glass interior reflections — two subtle linear-gradient overlays
   simulating soft light bouncing inside a frosted pane. */
.activity-slide-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 18px 0 0 18px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--ivory-3) 18%, transparent) 0%, color-mix(in srgb, var(--ivory-3) 0%, transparent) 30%),
    linear-gradient(90deg, color-mix(in srgb, var(--ivory-3) 10%, transparent) 0%, color-mix(in srgb, var(--ivory-3) 0%, transparent) 35%);
  pointer-events: none;
  z-index: 0;
}
.activity-slide-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 18px 0 0 18px;
  background:
    radial-gradient(120% 60% at 20% 0%, color-mix(in srgb, var(--ivory-3) 16%, transparent) 0%, color-mix(in srgb, var(--ivory-3) 0%, transparent) 60%),
    radial-gradient(80% 40% at 90% 100%, color-mix(in srgb, var(--hi-3) 10%, transparent) 0%, color-mix(in srgb, var(--hi-3) 0%, transparent) 60%);
  pointer-events: none;
  z-index: 0;
}
/* Make the panel content sit above the reflections. */
.activity-slide-panel > * {
  position: relative;
  z-index: 1;
}
@keyframes activitySlideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }
.activity-slide-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--card-border);
  color: var(--ink-muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  z-index: 5;
}
.activity-slide-close:hover { background: color-mix(in srgb, var(--tan-deep) 10%, transparent); color: var(--ink); }

/* Pre-v01 Recent Activity card uses the base .activity-frame +
   .activity-text + .activity-meta styles from earlier in this file
   (lines ~72-82) — no shot-ID accent, no opacity locks. The v01l
   "polish" overrides have been removed so the card matches the
   original muted forest-glass look. */

/* ─── v06a — Media Uploads functional bits ─────────────────────────── */

/* Highlight the dropzone while a file is hovering over it. */
.dropzone.is-dragover {
  background: color-mix(in srgb, var(--leaf) 16%, transparent);
  border-color: color-mix(in srgb, var(--leaf) 85%, transparent);
}

/* Real (uploaded) thumbnails replace the gradient placeholders.
   `.media-thumb` already defines the box (size/radius); we only need
   to make the image fill it and add a video-glyph fallback for
   .mp4/.mov uploads (we don't generate a still preview client-side). */
.media-thumb--real {
  overflow: hidden;
  position: relative;
  background: rgba(0, 0, 0, 0.25);
}
.media-thumb--real img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.media-thumb-vid {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  font-size: var(--fs-18);
  color: color-mix(in srgb, var(--ink-hi) 78%, transparent);
  background: linear-gradient(155deg, color-mix(in srgb, color-mix(in srgb, var(--olive-11) 60%, var(--mix-dark)) 60%, transparent), color-mix(in srgb, color-mix(in srgb, var(--olive-5) 39%, var(--mix-dark)) 85%, transparent));
}

/* ── Upload modal — sits on the shared .modal-backdrop / .glass card.
   Custom layout for the file-row list, progress bars, and action row. */
.upload-modal-card {
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  padding: 22px 22px 18px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.upload-modal-head { display: flex; flex-direction: row; align-items: flex-start; justify-content: space-between; gap: 12px; }
.upload-modal-head-titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* v07zz288 — bulk vs individual segmented toggle */
.upload-mode-toggle { display: inline-flex; flex: 0 0 auto; gap: 2px; padding: 3px; border-radius: var(--r-panel); background: color-mix(in srgb, var(--shade-14) 6%, transparent); border: 1px solid color-mix(in srgb, var(--card-border) 35%, transparent); }
.upload-mode-tab { font: inherit; font-size: var(--fs-12); font-weight: var(--fw-semi); color: var(--ink-muted); padding: 5px 11px; border: 0; border-radius: var(--r-7); background: transparent; cursor: pointer; white-space: nowrap; transition: background var(--dur-1) ease, color var(--dur-1) ease; }
.upload-mode-tab:hover:not(.is-active) { color: var(--ink); }
.upload-mode-tab.is-active { background: var(--card-bg-solid); color: var(--ink); box-shadow: 0 1px 3px color-mix(in srgb, var(--shadow-ink) 14%, transparent); }
.upload-mode-tab:disabled { opacity: 0.5; cursor: default; }
/* bulk-mode file list (just names + per-file status; the control set above drives them) */
.upload-bulk-files { display: flex; flex-direction: column; gap: 4px; margin-top: 2px; max-height: 200px; overflow-y: auto; padding: 8px 10px; border-radius: var(--r-panel); background: color-mix(in srgb, var(--shade-14) 4%, transparent); border: 1px solid color-mix(in srgb, var(--card-border) 30%, transparent); }
.upload-bulk-file { display: flex; align-items: center; gap: 10px; font-size: var(--fs-12-5); color: var(--ink); }
.upload-bulk-file-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-bulk-file-size { flex: 0 0 auto; font-size: var(--fs-11); color: var(--ink-muted); }
.upload-bulk-file-stat { flex: 0 0 auto; font-size: var(--fs-11); font-weight: var(--fw-bold); color: var(--ink-muted); }
.upload-bulk-file-stat.is-ok { color: color-mix(in srgb, var(--olive-7) 90%, var(--mix-dark)); }
.upload-bulk-file-stat.is-err { color: var(--red-14); }
.upload-bulk-file.is-done .upload-bulk-file-name { color: var(--ink-muted); }
.upload-modal-eyebrow {
  font-size: var(--fs-10);
  letter-spacing: var(--track-18);
  font-weight: var(--fw-bold);
  color: var(--ink-muted);
  text-transform: uppercase;
}
.upload-modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-22);
  letter-spacing: var(--track-01);
  color: var(--ink);
}
.upload-modal-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--card-border) 30%, transparent) transparent;
  padding-right: 4px;
}
.upload-modal-body::-webkit-scrollbar { width: 6px; }
.upload-modal-body::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--card-border) 30%, transparent); border-radius: 3px; }
.upload-modal-warn {
  padding: 10px 12px;
  background: color-mix(in srgb, var(--danger-soft-2) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger-soft-2) 40%, transparent);
  border-radius: var(--r-sm);
  font-size: var(--fs-12);
  color: var(--danger-ink);
}
.upload-modal-foot {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
}
.upload-modal-cancel,
.upload-modal-submit {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-12);
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-04);
  padding: 8px 16px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-1) ease, filter var(--dur-1) ease;
}
.upload-modal-cancel {
  background: color-mix(in srgb, var(--cream) 55%, transparent);
  border: 1px solid var(--card-border);
  color: var(--ink);
}
.upload-modal-cancel:hover:not(:disabled) { background: color-mix(in srgb, var(--cream) 85%, transparent); }
.upload-modal-submit {
  background: linear-gradient(180deg, var(--olive-6) 0%, var(--olive-4) 100%);
  border: 1px solid color-mix(in srgb, var(--surface-forest-6) 85%, transparent);
  color: var(--ink-cream);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 40%, transparent),
    0 2px 6px color-mix(in srgb, var(--surface-forest) 25%, transparent);
}
.upload-modal-submit:hover:not(:disabled) { filter: brightness(1.06); }
.upload-modal-submit:disabled { opacity: 0.5; cursor: not-allowed; }
.upload-modal-cancel:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── File row inside the modal */
.upload-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  background: color-mix(in srgb, var(--cream-2) 55%, transparent);
  border: 1px solid var(--card-border);
  border-radius: var(--r-panel);
}
.upload-row.is-done {
  background: color-mix(in srgb, var(--ok) 10%, transparent);
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
}
.upload-row.is-error {
  background: color-mix(in srgb, var(--danger-soft-2) 10%, transparent);
  border-color: color-mix(in srgb, var(--danger-soft-2) 45%, transparent);
}
.upload-row-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
}
.upload-row-name {
  font-size: var(--fs-13);
  font-weight: var(--fw-med);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}
.upload-row-size {
  font-size: var(--fs-11);
  color: var(--ink-muted);
  font-feature-settings: var(--tnum);
  flex-shrink: 0;
}
.upload-row-fields {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);   /* minmax(0, 1fr) lets the
                                                    right cell actually shrink
                                                    when its <select> has
                                                    long option text */
  gap: 8px;
}
.upload-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;        /* same reason — allow flex/grid children to shrink */
}
.upload-field-label {
  font-size: var(--fs-10);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-10);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.upload-field-select {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-12-5);
  color: var(--ink);
  background: color-mix(in srgb, var(--cream) 65%, transparent);
  border: 1px solid var(--card-border);
  border-radius: var(--r-xs);
  padding: 6px 8px;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-muted) 50%),
                    linear-gradient(135deg, var(--ink-muted) 50%, transparent 50%);
  background-position: calc(100% - 14px) center, calc(100% - 9px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 24px;
  /* v06d — native <select> sizes itself to the longest option by default,
     which pushed the Shot picker off the right edge of the modal when
     option labels like "SH0010 · Missouri Farm Morning" were long.
     Forcing the element to fill its grid cell (combined with minmax(0,1fr)
     on the parent) clips it back inside the card; the dropdown popup
     still expands to the option text when opened. */
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.upload-field-select:focus {
  border-color: color-mix(in srgb, var(--leaf) 70%, transparent);
  box-shadow: 0 0 0 var(--ring-w) color-mix(in srgb, var(--leaf) 15%, transparent);
}
.upload-field-select:disabled { opacity: 0.6; cursor: not-allowed; }

/* Progress bar */
.upload-row-progress {
  position: relative;
  height: 8px;
  background: color-mix(in srgb, var(--ink-muted) 18%, transparent);
  border-radius: var(--r-round);
  overflow: hidden;
}
.upload-row-progress-bar {
  height: 100%;
  background: linear-gradient(90deg, var(--leaf-bright) 0%, var(--olive-4) 100%);
  border-radius: var(--r-round);
  transition: width var(--dur-0) ease;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 45%, transparent);
}
.upload-row-progress-text {
  position: absolute;
  top: -16px;
  right: 0;
  font-size: var(--fs-10);
  font-feature-settings: var(--tnum);
  color: var(--ink-muted);
  font-weight: var(--fw-semi);
}

/* Final-status line under the row */
.upload-row-status {
  font-size: var(--fs-11-5);
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-02);
}
.upload-row-status--ok  { color: var(--olive-3); }
.upload-row-status--err { color: var(--danger-ink); }

/* ─── v06b — type-aware upload row fields ──────────────────────────── */

/* Field-grid swaps from "Type + Shot" to "Type + Description" to
   "Type + Ref-of" depending on the kind selector — see UploadRow.jsx.
   The grid columns are unchanged but a couple of inputs need styling
   parity with the existing .upload-field-select. */
.upload-field-input {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-12-5);
  color: var(--ink);
  background: color-mix(in srgb, var(--cream) 65%, transparent);
  border: 1px solid var(--card-border);
  border-radius: var(--r-xs);
  padding: 6px 8px;
  outline: none;
}
.upload-field-input:focus {
  border-color: color-mix(in srgb, var(--leaf) 70%, transparent);
  box-shadow: 0 0 0 var(--ring-w) color-mix(in srgb, var(--leaf) 15%, transparent);
}
.upload-field-input:disabled { opacity: 0.6; cursor: not-allowed; }

/* Reference picker — two side-by-side selects (category + asset) under
   a single "Reference of" label. */
.upload-ref-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: 6px;
}
.upload-ref-row .upload-field-select {
  /* Selects in this nested row carry the same dropdown caret styling
     they have at top level — width-wise we just want them to share
     the row evenly. */
  min-width: 0;
}

/* Edit-only episode selector sits on its own row underneath the
   type/description pair, since it's not part of the main pairing. */
.upload-row .upload-field--episode {
  margin-top: 6px;
}

/* Description input expands to fill its grid cell (overrides the
   default select sizing). */
.upload-field--desc .upload-field-input { width: 100%; }

/* ─── v06c — Edit destination info pill ─────────────────────────────
   Edits ask no questions; the right column just shows an info chip
   telling the user where the file will land + that the version
   number is picked automatically. Styled to feel "passive" — visible
   info, not an actionable input. */
.upload-field--info .upload-info-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: color-mix(in srgb, var(--leaf) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--leaf) 35%, transparent);
  border-radius: var(--r-round);
  font-size: var(--fs-12);
  font-weight: var(--fw-semi);
  color: var(--olive-3);
  letter-spacing: var(--track-01);
  width: fit-content;
}
.upload-field--info .upload-info-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--leaf) 20%, transparent);
  color: color-mix(in srgb, var(--leaf-bright) 50%, var(--mix-dark));
  font-size: var(--fs-11);
  font-weight: var(--fw-bold);
}

/* v06e — clickable edit-thumb deep-link to Review. Edit uploads carry
   a video_reviews row id, so the thumb is a one-click shortcut to
   open that review. Other thumbs (frames, references) stay passive. */
.media-thumb--clickable {
  cursor: pointer;
  transition: transform var(--dur-2) ease, box-shadow var(--dur-2) ease;
}
.media-thumb--clickable:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.30);
}
.media-thumb--clickable:focus-visible {
  outline: var(--ring-w) solid var(--leaf);
  outline-offset: var(--ring-offset);
}
.media-thumb--clickable:hover .media-thumb-vid {
  filter: brightness(1.15);
}

/* ─── v06g — Asset-bound reference picker ──────────────────────────
   Sits BELOW the Type / Reference-of row when the reference category
   is character / animal / location / prop. Two-column grid like the
   row above so the controls line up visually. The optional
   "descriptor" field tucks under the asset picker once an asset is
   selected. */
.upload-ref-asset-row {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 8px;
  margin-top: 6px;
}
.upload-ref-asset-row .upload-field--ref-asset {
  /* Make the picker span the right column when present, but the
     descriptor (when shown) is a separate full-row item below it. */
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 8px;
  align-items: start;
}
.upload-ref-asset-row .upload-field--ref-asset > .upload-field-label {
  /* Label sits in the left column of the asset-picker row. */
  grid-column: 1;
}
.upload-ref-asset-row .upload-field--ref-asset > .upload-field-select {
  grid-column: 2;
}
.upload-ref-asset-row .upload-field--ref-desc {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 8px;
  align-items: center;
}
.upload-ref-asset-row .upload-field--ref-desc > .upload-field-label {
  grid-column: 1;
}
.upload-ref-asset-row .upload-field--ref-desc > .upload-field-input {
  grid-column: 2;
}
.upload-field-hint {
  font-weight: 400;
  font-size: var(--fs-9);
  color: var(--ink-soft);
  margin-left: 4px;
  letter-spacing: var(--track-05);
  text-transform: lowercase;
}

/* ─── Inline new-asset create form ──────────────────────────────────
   Replaces the descriptor row when "+ Create new …" is selected in
   the asset picker. Name input + Create / Cancel buttons stack
   horizontally; an error line drops underneath on failure. */
.upload-new-asset {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--leaf) 8%, transparent);
  border: 1px dashed color-mix(in srgb, var(--leaf) 45%, transparent);
  border-radius: var(--r-sm);
}
.upload-new-asset-name {
  flex: 1 1 220px;
  min-width: 0;
}
.upload-new-asset-create,
.upload-new-asset-cancel {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-11-5);
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-04);
  padding: 6px 12px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-1) ease, filter var(--dur-1) ease;
}
.upload-new-asset-create {
  background: linear-gradient(180deg, var(--olive-6) 0%, var(--olive-4) 100%);
  border: 1px solid color-mix(in srgb, var(--surface-forest-6) 85%, transparent);
  color: var(--ink-cream);
}
.upload-new-asset-create:hover:not(:disabled) { filter: brightness(1.06); }
.upload-new-asset-create:disabled { opacity: 0.55; cursor: not-allowed; }
.upload-new-asset-cancel {
  background: color-mix(in srgb, var(--cream) 55%, transparent);
  border: 1px solid var(--card-border);
  color: var(--ink-muted);
}
.upload-new-asset-cancel:hover:not(:disabled) { background: color-mix(in srgb, var(--cream) 85%, transparent); }
.upload-new-asset-err {
  flex-basis: 100%;
  margin-top: 4px;
  font-size: var(--fs-11);
  font-weight: var(--fw-semi);
  color: var(--danger-ink);
}
