/* R2 Console — clean file-manager UI. Tokens in tokens.css. */
@import url("tokens.css");

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }

/* The hidden attribute must always win over component display rules. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading);
  color: var(--text);
  /* The reference paints a three-stop vertical gradient in dark and a flat
     fill in light; --bg-gradient is only defined in the dark themes, so the
     fallback covers light without a second rule. */
  background: var(--bg-gradient, var(--bg));
  background-attachment: fixed;
  transition: background var(--transition), color var(--transition);
}

/* Headings use the display face with the measured tracking. */
h1, h2, h3, .brand { font-family: var(--font-display); letter-spacing: var(--heading-tracking); }

/* Their signature: gradient-filled display type on the wordmark. */
.brand-mark {
  background-image: var(--heading-gradient);
}

/* Frosted surfaces — nav blurs at 30px, cards at 20px on the reference. */
.topbar, .rail { backdrop-filter: var(--blur-surface); }
.card-file, .list-row, dialog.modal { backdrop-filter: var(--blur-card); }

/* Cards carry a soft accent glow instead of a hard shadow. */
.card-file:hover { box-shadow: 0 0 0 1px var(--border-strong), 0 8px 30px -12px var(--glow-purple); }

::-webkit-scrollbar { width: var(--scrollbar-width); height: var(--scrollbar-width); }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

button { font: inherit; }
input, select, textarea {
  width: 100%;
  padding: 14px 16px;            /* measured on the reference input */
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--input-bg, var(--surface));
  color: var(--text);
  font: inherit;
  transition: border-color var(--transition), box-shadow var(--transition);
}
/* The dropdown list of a native <select> is painted by the browser, not by the
   page, and it does not inherit the element's background/colour. Against the
   dark theme that produced a white row and dark-on-dark text. Two things fix
   it together: color-scheme tells the browser which palette to paint the popup
   with, and an explicit <option> colour covers the engines that ignore it.
   --input-bg is translucent, so options need an OPAQUE colour or they render
   over whatever is behind the popup. */
select {
  /* color-scheme is inherited and :root already declares light/dark per theme,
     so the popup follows the page's theme, not the OS. Do NOT redeclare it. */
  -webkit-appearance: none;
  appearance: none;
  /* Room for the chevron drawn below. */
  padding-right: 38px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 20px) center, calc(100% - 14px) center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  cursor: pointer;
}
select option {
  background-color: var(--select-popup-bg);
  color: var(--text);
}
select option:checked,
select option:hover {
  background-color: var(--primary-soft);
  color: var(--text);
}

/* A list-box select renders its options inline; the chevron and the reserved
   right padding belong only to the collapsed control. */
select[multiple], select[size]:not([size="1"]) {
  background-image: none;
  padding-right: 16px;
}

input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}
label { display: block; margin-bottom: 6px; color: var(--muted); font-size: var(--text-xs); font-weight: 500; }
code { font-family: var(--font-mono); font-size: 0.92em; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 28px;            /* measured: 48px tall, 0 28px inline */
  min-height: 48px;
  border: 1px solid transparent;
  border-radius: var(--radius);  /* reference CTA is 14px, inputs are 12px */
  background: var(--primary);
  color: var(--primary-ink);
  font-weight: 600;
  cursor: pointer; white-space: nowrap;
  transition: background var(--transition), border-color var(--transition), filter var(--transition);
}
.btn:hover:not(:disabled) { background: var(--primary-hover); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost { background: var(--surface); border-color: var(--border); color: var(--text); font-weight: 500; }
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); }
.btn-danger { background: none; border-color: var(--danger); color: var(--danger); font-weight: 500; }
.btn-danger:hover:not(:disabled) { background: var(--danger-soft); }
.btn-sm { padding: 8px 16px; min-height: 34px; font-size: var(--text-xs); }
.button-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  pointer-events: none;
}
.btn-sm .button-icon { width: 14px; height: 14px; flex-basis: 14px; }
.btn-icon {
  width: 38px; height: 38px; padding: 0;
  border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border); color: var(--muted);
  display: inline-grid; place-items: center;
  cursor: pointer;
}
.btn-icon:hover { background: var(--surface-2); color: var(--text); }

/* ---------- Sign-in gate ---------- */
body.is-gated .shell { visibility: hidden; pointer-events: none; }

/* One round trip decides which surface is right, and guessing flashes the
   wrong one in both directions: the file manager at a signed-out visitor, or
   the sign-in screen at someone already signed in. Show neither meanwhile. */
body.auth-pending .shell,
body.auth-pending .gate {
  visibility: hidden;
  pointer-events: none;
  /* Dead-man's switch. renderSession() normally drops .auth-pending within one
     round trip, but if main.js never loads there is nothing to drop it - and a
     permanent spinner over a blank page is worse than the flash this prevents.
     Pure CSS, so it still fires when no script runs at all. */
  animation: auth-reveal 0s linear 8s forwards;
}
@keyframes auth-reveal { to { visibility: visible; pointer-events: auto; } }
body.auth-pending::after {
  content: "";
  position: fixed;
  top: 50%; left: 50%;
  width: 28px; height: 28px;
  margin: -14px 0 0 -14px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: auth-spin 700ms linear infinite, auth-give-up 0s linear 8s forwards;
  z-index: 50;
}
@keyframes auth-spin { to { transform: rotate(360deg); } }
@keyframes auth-give-up { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  body.auth-pending::after { animation-duration: 2.4s, 0s; }
}
.gate {
  position: fixed; inset: 0; z-index: 40;
  display: grid; place-items: center;
  padding: var(--space-6);
  background: var(--bg);
}
.gate[hidden] { display: none; }
.gate-card {
  width: min(400px, 100%);
  padding: var(--space-8);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  display: grid; gap: var(--space-4);
}
.gate-brand { display: flex; align-items: center; gap: 10px; justify-content: center; }
.gate-brand strong { font-size: var(--text-lg); letter-spacing: -0.01em; }
.gate-card h1 { margin: 0; font-size: var(--text-xl); text-align: center; letter-spacing: -0.02em; }
.gate-hint { margin: 0; text-align: center; color: var(--muted); font-size: var(--text-sm); }
.gate-card a.btn { text-decoration: none; }
.gate-msg { margin: 0; min-height: 1.2em; text-align: center; color: var(--danger); font-size: var(--text-xs); }
.code-input {
  font-family: var(--font-mono);
  font-size: var(--text-lg);
  letter-spacing: 0.35em;
  text-align: center;
}

/* ---------- Shell ---------- */
.shell { display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
  display: flex; align-items: center; gap: var(--space-4);
  padding: 0 var(--space-6);
  height: 64px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: var(--text-base); letter-spacing: -0.01em; }
.brand-mark {
  width: 30px; height: 30px; border-radius: 8px;
  background: var(--primary); color: var(--primary-ink);
  display: grid; place-items: center;
  font-size: 15px; font-weight: 800;
}
.topbar .search {
  flex: 1 1 320px; max-width: 520px;
  position: relative;
}
.topbar .search input {
  padding-left: 36px;
  border-radius: 999px;
  background: var(--surface-2);
  border-color: transparent;
}
.topbar .search input:focus { background: var(--surface); border-color: var(--border-strong); }
.search-ico {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--faint); pointer-events: none;
}
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }

.avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--primary-soft); color: var(--primary);
  border: none;
  display: grid; place-items: center;
  font-weight: 700; font-size: var(--text-sm);
  cursor: pointer;
}

/* ---------- Body ---------- */
.body { display: flex; flex: 1; min-height: 0; }

.rail {
  width: 220px; flex: none;
  padding: var(--space-4) var(--space-3);
  display: flex; flex-direction: column; gap: 2px;
  border-right: 1px solid var(--border);
  background: var(--surface);
}
.rail-label {
  padding: var(--space-3) var(--space-3) var(--space-1);
  font-size: var(--text-xs); font-weight: 600;
  color: var(--faint); text-transform: uppercase; letter-spacing: 0.06em;
}
.rail-item {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  border: none; border-radius: var(--radius-sm);
  background: none; color: var(--muted);
  text-align: left; font-weight: 500;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.rail-item:hover { background: var(--surface-2); color: var(--text); }
.rail-item.is-active { background: var(--primary-soft); color: var(--primary); font-weight: 600; }
.rail-item svg { flex: none; }
.rail-foot { margin-top: auto; display: grid; gap: 2px; }

.main { flex: 1; min-width: 0; padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); position: relative; }

/* ---------- Loading overlay ---------- */
.loading {
  position: absolute;
  inset: 0;
  z-index: 15;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-3);
  background: color-mix(in srgb, var(--bg) 62%, transparent);
  backdrop-filter: blur(2px);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: var(--text-sm);
}
.spinner {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 3px solid var(--border-strong);
  border-top-color: var(--primary);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Toolbar ---------- */
.page-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.page-head h1 { margin: 0; font-size: var(--text-xl); letter-spacing: -0.02em; }
.count-chip {
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  font-weight: 500; font-size: var(--text-xs);
}
.count-chip:empty { display: none; }
.page-head .spacer { flex: 1; }
.page-head select { width: auto; }

/* ---------- Path bar ---------- */
.crumbs {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  min-height: 38px;
  padding: 4px 10px 4px 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.crumbs button {
  border: none; background: none; color: var(--muted);
  padding: 4px 8px; border-radius: 999px;
  font: inherit; font-weight: 500;
  cursor: pointer;
}
.crumbs button:hover { background: var(--surface-2); color: var(--text); }
.crumbs button.is-here { color: var(--text); font-weight: 600; cursor: default; background: var(--primary-soft); }
.crumbs .sep { color: var(--faint); }
.crumb-back {
  display: inline-grid; place-items: center;
  width: 28px; height: 28px;
  padding: 0 !important;
  margin-right: 4px;
  border: 1px solid var(--border) !important;
  border-radius: 50% !important;
  background: var(--surface-2) !important;
}
.crumb-back:disabled { opacity: 0.35; cursor: default; }
.crumb-back:not(:disabled):hover { border-color: var(--border-strong) !important; }

.view-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.view-toggle button {
  border: none; background: var(--surface); color: var(--muted);
  width: 36px; height: 34px; display: grid; place-items: center; cursor: pointer;
}
.view-toggle button.is-active { background: var(--primary-soft); color: var(--primary); }

.selection-bar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 10px var(--space-4);
  background: var(--primary-soft);
  border-radius: var(--radius-sm);
  color: var(--primary); font-weight: 600;
}
.selection-bar[hidden] { display: none; }
.selection-bar .btn { margin-left: auto; }

/* ---------- File grid ---------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-3);
}
.card-file {
  position: relative;
  /* Measured on the reference card: rgba(255,255,255,0.03) fill, 20px radius,
     24px padding, hairline border, blur(20px). */
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-2);
  cursor: pointer;
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.card-file:hover { border-color: var(--border-strong); box-shadow: var(--shadow-1); }
.card-file[data-selected="1"] { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
/* ---------- File type icons ----------
   One hue per family (--ico-h, set by paintFileIcon on the icon box) and each
   theme supplies its own recipe for turning that hue into ink, wash and edge.

   TWO things were wrong with the first version of this.

   1. The recipe lived on :root. A custom property is resolved where it is
      DECLARED, not where it is used, so `--ico-ink: hsl(var(--ico-h) ...)` on
      :root froze at the root's own hue and inherited that one colour to every
      box. All fifteen families rendered the same blue. The hue set on the box
      never reached a pixel. The recipe therefore belongs in the rule that
      matches the box, which is where --ico-h actually is.

   2. HSL lightness is not perceived lightness. At a fixed L, yellow and teal
      read far brighter than indigo - measured across the fifteen families,
      the spread was 27 points of OKLCH L in the day theme, the brightest
      family 1.71x the lightness of the darkest. In a grid that is what reads
      as "these icons do not go together".

   So the lightness is pinned in OKLCH, where equal L means equally bright to
   a person, while chroma and hue are inherited from the family's own colour -
   each hue keeps as much saturation as it can carry, and none of them shouts.
   The HSL line stays as the fallback, guarded by @supports rather than by
   declaration order: an unsupported colour inside a custom property is not
   rejected at parse time, it poisons the property at use time.

   Per theme, and this is the part that has to differ: the day theme takes a
   heavier stroke and a pale wash, the night theme a lighter stroke, because a
   light stroke on a dark ground blooms and reads thicker than it measures. */
:root {
  --ico-h: 215;
  --ico-stroke: 1.7;
  --ico-ink-s: 62%;  --ico-ink-l: 40%;   --ico-ink-L: 0.500;
  --ico-wash-s: 78%; --ico-wash-l: 95%;  --ico-wash-L: 0.965;
  --ico-edge-s: 60%; --ico-edge-l: 87%;  --ico-edge-L: 0.895;
}
:root[data-theme="dark"] {
  --ico-stroke: 1.5;
  --ico-ink-s: 70%;  --ico-ink-l: 72%;   --ico-ink-L: 0.795;
  --ico-wash-s: 45%; --ico-wash-l: 15%;  --ico-wash-L: 0.268;
  --ico-edge-s: 40%; --ico-edge-l: 26%;  --ico-edge-L: 0.375;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ico-stroke: 1.5;
    --ico-ink-s: 70%;  --ico-ink-l: 72%;   --ico-ink-L: 0.795;
    --ico-wash-s: 45%; --ico-wash-l: 15%;  --ico-wash-L: 0.268;
    --ico-edge-s: 40%; --ico-edge-l: 26%;  --ico-edge-L: 0.375;
  }
}

/* Declared on the box, where --ico-h is, so each family resolves its own.
   --ico-c (default 1) is how much colour the family is allowed to carry; only
   the generic fallback damps it, so an unknown file reads as neutral rather
   than as a confident blue competing with `document`. */
.card-file .thumb, .list-row .mini-ico {
  --ico-ink:  hsl(var(--ico-h) calc(var(--ico-ink-s)  * var(--ico-c, 1)) var(--ico-ink-l));
  --ico-wash: hsl(var(--ico-h) calc(var(--ico-wash-s) * var(--ico-c, 1)) var(--ico-wash-l));
  --ico-edge: hsl(var(--ico-h) calc(var(--ico-edge-s) * var(--ico-c, 1)) var(--ico-edge-l));
}
@supports (color: oklch(from red l c h)) {
  .card-file .thumb, .list-row .mini-ico {
    --ico-ink:  oklch(from hsl(var(--ico-h) var(--ico-ink-s)  var(--ico-ink-l))  var(--ico-ink-L)  calc(c * var(--ico-c, 1)) h);
    --ico-wash: oklch(from hsl(var(--ico-h) var(--ico-wash-s) var(--ico-wash-l)) var(--ico-wash-L) calc(c * var(--ico-c, 1)) h);
    --ico-edge: oklch(from hsl(var(--ico-h) var(--ico-edge-s) var(--ico-edge-l)) var(--ico-edge-L) calc(c * var(--ico-c, 1)) h);
  }
}

.card-file .thumb {
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  background: var(--ico-wash);
  box-shadow: inset 0 0 0 1px var(--ico-edge);
  display: grid; place-items: center;
  overflow: hidden;
  color: var(--ico-ink);
}
.card-file .thumb svg { width: 40%; max-width: 46px; height: auto; stroke-width: var(--ico-stroke); }
.card-file .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* An image preview fills the box, so the tinted frame behind it is noise. */
.card-file .thumb:has(img) { background: var(--surface-2); box-shadow: none; }
.card-file .thumb.folder svg { width: 34px; height: 34px; }
.card-file .name {
  font-weight: 500; font-size: var(--text-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card-file .meta { color: var(--faint); font-size: var(--text-xs); display: flex; justify-content: space-between; gap: 8px; }
.card-file .check {
  position: absolute; top: 10px; left: 10px;
  width: 20px; height: 20px;
  accent-color: var(--primary);
  opacity: 0;
  transition: opacity var(--transition);
}
/* The checkbox is invisible until hover, so a keyboard user tabbed onto a
   control they could not see. Reveal it on focus too. */
.card-file:hover .check,
.card-file:focus-within .check,
.card-file .check:focus-visible,
.card-file[data-selected="1"] .check { opacity: 1; }

/* `display: none` removed these from the tab order AND the accessibility tree,
   so every file action (View, Share, Rename, Move, Delete) was unreachable
   without a mouse: keyboard users could never focus them, and touch devices
   have no hover to reveal them. Fading keeps them focusable, and
   :focus-within brings them up when a keyboard lands inside the card. */
.card-file .actions {
  position: absolute; top: 8px; right: 8px;
  display: flex; gap: 4px;
  opacity: 0;
  transition: opacity var(--transition);
}
.card-file:hover .actions,
.card-file:focus-within .actions,
.card-file[data-selected="1"] .actions { opacity: 1; }

/* Touch devices have no hover state at all, so these must simply stay put. */
@media (hover: none) {
  .card-file .check,
  .card-file .actions { opacity: 1; }
}

.card-file:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.card-file .actions .btn-icon { width: 30px; height: 30px; box-shadow: var(--shadow-1); }

/* ---------- File list ---------- */
.list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.list-row {
  display: grid;
  grid-template-columns: 32px 40px minmax(0, 1fr) 110px 170px 180px;
  align-items: center; gap: var(--space-3);
  padding: 10px var(--space-4);
  border-bottom: 1px solid var(--border);
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--surface-2); }
.list-row[data-selected="1"] { background: var(--primary-soft); }
.list-row.head {
  color: var(--faint); font-size: var(--text-xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--surface);
}
.list-row.head:hover { background: var(--surface); }
.list-row .mini-ico {
  width: 32px; height: 32px; border-radius: 8px;
  background: var(--ico-wash); color: var(--ico-ink);
  box-shadow: inset 0 0 0 1px var(--ico-edge);
  display: grid; place-items: center;
  overflow: hidden;
  flex: none;
}
/* 18px against the card's 46px: the same stroke in viewBox units is optically
   thinner once the whole glyph is that small, so it gets a little back. */
.list-row .mini-ico svg { width: 18px; height: 18px; stroke-width: calc(var(--ico-stroke) + 0.2); }
.list-row .mini-ico img { width: 100%; height: 100%; object-fit: cover; }
.list-row .mini-ico:has(img) { background: var(--surface-2); box-shadow: none; }
.list-row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.list-row .dim { color: var(--faint); font-size: var(--text-xs); }
.list-row .row-actions { display: flex; gap: 4px; justify-content: flex-end; }

.empty {
  padding: var(--space-8);
  text-align: center;
  color: var(--muted);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}
.empty[hidden] { display: none; }
.center-row { display: flex; justify-content: center; }

/* ---------- Drag overlay ---------- */
.drop-overlay {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  background: rgb(37 99 235 / 0.12);
  backdrop-filter: blur(3px);
  pointer-events: none;
}
.drop-overlay[hidden] { display: none; }
.drop-overlay .drop-card {
  padding: var(--space-8) var(--space-12);
  background: var(--surface);
  border: 2px dashed var(--primary);
  border-radius: var(--radius-lg);
  text-align: center;
  box-shadow: var(--shadow-2);
}
.drop-card strong { display: block; font-size: var(--text-lg); margin-bottom: 4px; }
.drop-card span { color: var(--muted); font-size: var(--text-sm); }

/* ---------- Upload queue (bottom-right panel) ---------- */
.queue-panel {
  position: fixed; right: var(--space-6); bottom: var(--space-6);
  width: min(380px, calc(100vw - 48px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  z-index: 50;
  overflow: hidden;
}
.queue-panel[hidden] { display: none; }
.queue-panel header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px var(--space-4);
  border-bottom: 1px solid var(--border);
  font-weight: 600;
}
.queue-panel ul { list-style: none; margin: 0; padding: var(--space-2); max-height: 300px; overflow-y: auto; display: grid; gap: 6px; }
.queue-item { padding: 8px 10px; border-radius: var(--radius-sm); background: var(--surface-2); }
.queue-item .q-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.queue-item .q-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; font-size: var(--text-xs); }
.queue-item .q-status { color: var(--faint); font-size: var(--text-xs); white-space: nowrap; }
.queue-item[data-state="done"] .q-status { color: var(--success); }
.queue-item[data-state="error"] .q-status { color: var(--danger); }
/* A cancellation is not a failure: inert, not alarming. */
.queue-item[data-state="cancelled"] { opacity: 0.6; }
.queue-item[data-state="cancelled"] .q-status { color: var(--faint); }

/* Auto-dismiss after everything settles - see scheduleQueueAutoHide(). */
.queue-panel.is-dismissing {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 240ms ease, transform 240ms ease;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .queue-panel.is-dismissing { transform: none; }
}
.queue-item .q-error { color: var(--danger); font-size: var(--text-xs); line-height: 1.4; }
/* A shared filename is a caution, not a failure: the upload worked. */
.queue-item[data-warn="1"] .q-error { color: var(--accent-gold); }
.queue-item[data-warn="1"] .q-status { color: var(--accent-gold); }
.queue-item .q-error[hidden] { display: none; }
progress { width: 100%; height: 5px; border: 0; border-radius: 999px; background: var(--border); accent-color: var(--primary); }
progress::-webkit-progress-bar { background: var(--border); border-radius: 999px; }
progress::-webkit-progress-value { background: var(--primary); border-radius: 999px; }

/* ---------- Dialogs ---------- */
dialog.modal {
  width: min(520px, 92vw);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 20px 60px -20px var(--glow-purple), var(--shadow-2);
  color: var(--text);
  box-shadow: var(--shadow-2);
}
/* A dialog is capped at the viewport by the UA (max-height: calc(100% - 34px))
   but nothing was scrollable inside it, so any dialog taller than the window
   simply hid its own footer - the customer could see the content and not the
   button that dismisses it. Applies to all twelve dialogs. */
dialog.modal { max-height: calc(100dvh - 2 * var(--space-6)); overflow-y: auto; overscroll-behavior: contain; }
dialog.modal::backdrop { background: rgb(16 24 40 / 0.5); }
dialog.modal form, dialog.modal .modal-body { padding: var(--space-6); display: grid; gap: var(--space-4); }
dialog.modal h2 { margin: 0; font-size: var(--text-lg); letter-spacing: -0.01em; }
dialog.modal.modal-wide { width: min(820px, 94vw); }
.modal-section {
  margin: 0 0 var(--space-3);
  font-size: var(--text-xs); font-weight: 600;
  color: var(--faint); text-transform: uppercase; letter-spacing: 0.06em;
}
dialog.modal section { padding-top: var(--space-3); border-top: 1px solid var(--border); }
dialog.modal section:first-of-type { padding-top: 0; border-top: 0; }
.modal-hint { margin: 0; color: var(--muted); font-size: var(--text-sm); }
.modal-actions {
  display: flex; gap: var(--space-2); justify-content: flex-end;
  /* Without wrapping, a three-button footer pushes Close off the right edge
     of a phone and the customer cannot dismiss the dialog. */
  flex-wrap: wrap;
}
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.field-grid.three-col { grid-template-columns: 1fr 150px auto; align-items: end; }
.settings-actions { justify-content: flex-start; margin-top: var(--space-3); flex-wrap: wrap; }
@media (max-width: 560px) { .field-grid { grid-template-columns: 1fr; } }

.folder-picker {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.folder-picker-head {
  min-height: 48px;
  padding: 7px 10px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.folder-picker-head .btn-icon { width: 32px; height: 32px; flex: none; }
.folder-picker-head .btn-icon:disabled { opacity: 0.35; cursor: default; }
.folder-picker-crumbs {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: thin;
}
.folder-picker-crumbs button {
  padding: 5px 7px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.folder-picker-crumbs button:hover { background: var(--surface); color: var(--text); }
.folder-picker-crumbs button[aria-current="page"] { color: var(--text); cursor: default; }
.folder-picker-crumbs .sep { color: var(--faint); }
.folder-picker-list {
  min-height: 170px;
  max-height: 300px;
  padding: 6px;
  overflow-y: auto;
}
.folder-picker-row {
  width: 100%;
  padding: 10px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  text-align: left;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}
.folder-picker-row:hover, .folder-picker-row:focus-visible { background: var(--primary-soft); }
.folder-picker-row svg { color: var(--primary); flex: none; }
.folder-picker-row .chevron { margin-left: auto; color: var(--faint); }
.folder-picker-state {
  min-height: 170px;
  padding: var(--space-6);
  display: grid;
  place-items: center;
  color: var(--muted);
  text-align: center;
}
.folder-picker-state:empty { display: none; }
.move-destination {
  margin: 0;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: var(--text-xs);
}

.session-chip {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--primary-soft);
  color: var(--primary);
  font-weight: 600;
}
.session-chip small { display: block; color: var(--muted); font-weight: 500; }

.storage-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.storage-row .s-name { font-weight: 600; }
.storage-row .s-bucket { color: var(--faint); font-size: var(--text-xs); font-family: var(--font-mono); }
.storage-row .s-badge {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 2px 8px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted);
}
.storage-row .s-badge.edge { background: var(--primary-soft); color: var(--primary); }
.storage-row .s-actions { margin-left: auto; display: flex; gap: 4px; }
.storage-row .s-actions .btn-icon { width: 30px; height: 30px; }
.storage-row input { padding: 6px 10px; font-size: var(--text-sm); }

/* ---------- Managers, Trash, jobs, sessions and access ---------- */
.manager-list { display: grid; gap: var(--space-2); max-height: min(52vh, 520px); overflow: auto; }
.manager-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-3);
  align-items: center; padding: 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface);
}
/* A row with a selection box needs a third column, or the checkbox takes
   column one and pushes the action buttons onto an implicit second row. */
.manager-row.has-pick { grid-template-columns: auto minmax(0, 1fr) auto; }
.manager-row strong, .manager-row small { display: block; overflow-wrap: anywhere; }
.manager-row small { margin-top: 3px; color: var(--muted); }
.manager-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }
.manager-actions select { width: auto; min-width: 110px; }

/* ---------- Rich preview ---------- */
dialog.preview-modal { width: min(1100px, 96vw); height: min(820px, 92vh); }
.preview-modal { padding: var(--space-4) !important; }
.preview-head { display: flex; justify-content: space-between; gap: var(--space-4); align-items: start; }
.preview-stage {
  height: calc(100% - 130px); min-height: 320px; margin: var(--space-4) 0;
  display: grid; place-items: center; overflow: auto; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface-2);
}
.preview-stage img, .preview-stage video { max-width: 100%; max-height: 100%; }
.preview-stage audio { width: min(640px, 90%); }
.preview-stage iframe { width: 100%; height: 100%; border: 0; background: white; }
.preview-stage pre { width: 100%; height: 100%; margin: 0; padding: var(--space-4); overflow: auto; white-space: pre-wrap; }
.preview-nav { display: flex; justify-content: space-between; }

/* ---------- Public managed share ---------- */
.share-shell { min-height: 100vh; display: grid; place-items: center; padding: var(--space-6); }
.share-card {
  width: min(480px, 100%); display: grid; gap: var(--space-4); padding: clamp(24px, 6vw, 48px);
  border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
  box-shadow: var(--shadow-2); text-align: center;
}
.share-card h1 { margin: 0; font-size: var(--text-xl); overflow-wrap: anywhere; }
.share-card form { display: grid; gap: var(--space-3); text-align: left; }
.share-card .eyebrow { margin: 0; color: var(--primary); text-transform: uppercase; letter-spacing: .08em; font-size: var(--text-xs); font-weight: 700; }
.share-primary { width: 100%; justify-content: center; }

/* ---------- Consent download mode ---------- */
.consent { display: grid; gap: var(--space-3); text-align: left; }
.consent-heading {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .06em;
}
/* Scrolls inside its own box: the terms must never push the accept control
   below the fold, which is where people stop reading and start leaving. */
.consent-terms {
  max-height: 210px;
  overflow-y: auto;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.6;
}
.consent-terms p { margin: 0 0 var(--space-2); }
.consent-terms ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; }
.consent-accept {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text); font-size: var(--text-sm); font-weight: 500;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.consent-accept:hover { border-color: var(--border-strong); }
.consent-accept:has(:checked) { border-color: var(--primary); background: var(--primary-soft); }
/* The generic input rule stretches every input to 100%; a checkbox must not. */
.consent-accept input {
  width: 18px; height: 18px; flex: 0 0 18px;
  margin: 0; padding: 0;
  accent-color: var(--primary);
  cursor: pointer;
}
.consent-accept span { line-height: 1.4; }

/* The download-mode selector reads as a sentence, so it gets the full row. */
.field-wide { grid-column: 1 / -1; }

/* ---------- Bulk selection bar ---------- */
.bulk-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  flex-wrap: wrap;
  padding: 10px var(--space-3);
  margin-bottom: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.bulk-bar[hidden] { display: none; }
.bulk-all { display: flex; align-items: center; gap: 8px; margin: 0; cursor: pointer;
  color: var(--text); font-size: var(--text-xs); font-weight: 500; }
/* The generic input rule stretches every input to 100%; a checkbox must not. */
.bulk-all input, .manager-row .row-pick {
  width: 16px; height: 16px; flex: 0 0 16px; margin: 0; padding: 0;
  accent-color: var(--primary); cursor: pointer;
}
.bulk-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.manager-row .row-pick { margin-right: 10px; }
.manager-row.is-picked { background: var(--primary-soft); }
/* An armed destructive button says so before it acts. */
.btn.is-armed { background: var(--danger); border-color: var(--danger); color: #fff; }

/* ---------- Toasts ---------- */
.toasts {
  position: fixed; right: var(--space-6); top: 76px;
  display: grid; gap: var(--space-2); z-index: 70;
  max-width: min(360px, 88vw);
}
.toast {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  font-size: var(--text-sm);
  animation: toast-in 160ms ease;
}
.toast[data-kind="success"] { border-left-color: var(--success); }
.toast[data-kind="error"] { border-left-color: var(--danger); }
.toast[data-kind="info"] { border-left-color: var(--primary); }
.toast .toast-action { margin-left: var(--space-3); }
@keyframes toast-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; } }

/* ---------- Status dot ---------- */
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); display: inline-block; }
.status-dot[data-state="on"] { background: var(--success); }
.status-dot[data-state="err"] { background: var(--danger); }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .body { flex-direction: column; }
  .rail {
    display: flex; width: 100%; min-height: 48px; padding: 6px var(--space-3);
    border-right: 0; border-bottom: 1px solid var(--border); overflow-x: auto;
  }
  .rail-label, .rail-foot { display: none; }
  #bucket-nav { display: flex !important; gap: 4px !important; }
  .rail-item { white-space: nowrap; }
  .topbar { padding: 0 var(--space-4); gap: var(--space-2); }
  .main { padding: var(--space-4); }
  .page-head { flex-wrap: wrap; }
  .page-head .spacer { flex-basis: 20%; }
  .list-row { grid-template-columns: 32px 32px minmax(0, 1fr) 80px 72px; }
  .list-row .hide-sm { display: none; }
}
@media (max-width: 560px) {
  .field-grid.three-col { grid-template-columns: 1fr; }
  .topbar .brand span:last-child { display: none; }
  .topbar .search { order: 3; flex-basis: 100%; max-width: none; }
  .topbar { height: auto; min-height: 64px; flex-wrap: wrap; padding-block: 8px; }
  .manager-row { grid-template-columns: 1fr; }
  .manager-actions { justify-content: flex-start; }
  .preview-head { display: grid; }
  dialog.preview-modal { height: 94vh; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---------- Share link dialog ----------
   The link is the point of this dialog, so it gets the visual weight and the
   only primary button. Settings are collapsed because most shares never need
   them - showing four controls before the link existed made the common case
   feel like filling in a form. */
.modal-share form { gap: var(--space-4); }

.share-head { display: grid; gap: 2px; }
.share-head h2 { margin: 0; }
.share-head .modal-hint {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted);
  word-break: break-all;
}

.share-hero {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
}

.share-url {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  text-overflow: ellipsis;
}
.share-url:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

.share-copy { flex: none; min-width: 92px; justify-content: center; }
.share-copy[data-copied] { background: var(--success); color: #fff; }

/* Loading and error states are on the container so the input can stay a plain
   readonly field that screen readers announce normally. */
.share-hero[data-state="loading"] .share-url {
  color: var(--muted);
  background:
    linear-gradient(90deg, transparent, rgb(255 255 255 / 0.06), transparent)
    0 0 / 200% 100% no-repeat var(--surface);
  animation: share-shimmer 1.1s linear infinite;
}
.share-hero[data-state="error"] .share-url { border-color: var(--danger); }

@keyframes share-shimmer {
  from { background-position: -100% 0; }
  to { background-position: 100% 0; }
}

.share-summary {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.5;
}

.share-options { border-top: 1px solid var(--border); padding-top: var(--space-3); }
.share-options > summary {
  cursor: pointer;
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
}
.share-options > summary::-webkit-details-marker { display: none; }
.share-options > summary::before {
  content: "";
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--transition);
}
.share-options[open] > summary::before { transform: rotate(90deg); }
.share-options > summary:hover { color: var(--text); }
.share-options > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Scoped to [open]: setting `display` on a child of a CLOSED <details>
   overrides the browser's own hiding, so the settings stayed on screen
   at full height and the dialog was never actually simplified. */
/* Hide it explicitly rather than trusting the UA to do it. Engines differ on
   how <details> conceals its content (display:none on children vs
   ::details-content { content-visibility }), and this one left the body laid
   out at full height - so the dialog was never actually simplified. */
.share-options:not([open]) .share-options-body { display: none; }
.share-options[open] .share-options-body {
  display: grid; gap: var(--space-3); padding-top: var(--space-3);
}

.share-actions { justify-content: flex-end; }

@media (max-width: 560px) {
  .share-hero { flex-direction: column; }
  .share-copy { width: 100%; }
}

/* ---------- Layout utilities ----------
   These replace inline style attributes. The Worker serves style-src 'self',
   which blocks style="" outright, so every one of those declarations was
   being dropped by the browser and the layout silently never applied. */
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.stack-2 { display: grid; gap: 2px; }
.stack-6 { display: grid; gap: 6px; }
.w-auto { width: auto; }
.actions-start { justify-content: flex-start; }
.check-row { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-2); }

/* Two-step confirm. Previously set via btn.style.background, which the
   Worker's style-src 'self' CSP blocked outright - so the armed state was
   invisible and customers pressed once, saw nothing, and moved on. */
.btn-icon.is-danger { color: var(--danger); }
.btn-icon.is-armed {
  background: var(--danger-soft);
  color: var(--danger);
  border-color: var(--danger);
}

/* Held for one frame while the theme attribute changes — see applyTheme().
   Without it, elements that already existed keep the previous theme's
   transitioned colour instead of adopting the new token. */
:root.theme-switching *,
:root.theme-switching *::before,
:root.theme-switching *::after {
  transition: none !important;
}
