:root {
  /* runtime knobs (theme.js injects #theme-vars overriding these) */
  --accent: #0a84ff;
  --accent-strong: #0066d6;
  --accent-soft: #4aa3ff;
  --accent-rgb: 10,132,255;
  --glass-alpha: .62;
  --wallpaper-image: none;
  --wallpaper-blur: 0px;
  --wallpaper-dim: .34;
  --r: 20px;
  --tile: 178px;
  --frame: clamp(12px, 1.6vw, 24px); /* gap so the app floats on the wallpaper */
  --mono: "JetBrains Mono", ui-monospace, monospace;
  /* legacy alias — older rules still reference --gold/--lilac */
  --gold: var(--accent);
  --gold-soft: var(--accent-strong);
  --lilac: var(--accent-soft);
  --bad: #ff6b5e;
  --good: #34c759;
  --on-accent: #fff;
  /* font presets via [data-font] */
  --body: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "IBM Plex Sans Thai", system-ui, sans-serif;
  --display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", "IBM Plex Sans Thai", sans-serif;
}
:root[data-font="grotesk"] { --body: "Space Grotesk", "IBM Plex Sans Thai", system-ui, sans-serif; --display: "Space Grotesk", "IBM Plex Sans Thai", sans-serif; }
:root[data-font="plex"] { --body: "IBM Plex Sans Thai", system-ui, "Segoe UI", sans-serif; --display: "IBM Plex Sans Thai", system-ui, sans-serif; }

/* ----- DARK palette (default) ----- */
:root, :root[data-mode="dark"] {
  color-scheme: dark; /* native <select> popups, scrollbars use dark theme */
  --bg: #0a0d14;
  --text: #f4f6fa;
  --muted: #b3bbc9;
  --faint: #7c8696;
  --line: rgba(255,255,255,.12);
  --line-soft: rgba(255,255,255,.07);
  --glass: rgba(26,30,40, var(--glass-alpha));
  --glass-2: rgba(255,255,255,.055);
  --glass-hi: rgba(255,255,255,.10);
  --glass-solid: rgba(20,24,33,.95); /* opaque glass for popovers/menus (readability) */
  --surface: var(--glass-2);
  --surface-2: var(--glass-hi);
  --bg-2: rgba(255,255,255,.04);
  --shadow: 0 26px 64px -30px rgba(0,0,0,.75);
  --wp-scrim: 4,6,12;
  --wp-fallback:
    radial-gradient(1100px 720px at 14% 8%, rgba(37,99,235,.42), transparent 60%),
    radial-gradient(1000px 760px at 86% 16%, rgba(124,58,237,.40), transparent 55%),
    radial-gradient(1200px 900px at 50% 108%, rgba(14,165,233,.34), transparent 60%),
    linear-gradient(160deg, #0c1224, #0a0f1c 70%);
}
/* ----- LIGHT palette ----- */
:root[data-mode="light"] {
  color-scheme: light;
  --bg: #e9edf5;
  --text: #1b1f27;
  --muted: #5a6273;
  --faint: #8a93a4;
  --line: rgba(15,23,42,.13);
  --line-soft: rgba(15,23,42,.07);
  --glass: rgba(255,255,255, var(--glass-alpha));
  --glass-2: rgba(15,23,42,.045);
  --glass-hi: rgba(15,23,42,.06);
  --glass-solid: rgba(255,255,255,.97); /* opaque glass for popovers/menus (readability) */
  --surface: var(--glass-2);
  --surface-2: var(--glass-hi);
  --bg-2: rgba(255,255,255,.5);
  --shadow: 0 20px 50px -26px rgba(30,41,90,.32);
  --wp-scrim: 255,255,255;
  --wp-fallback:
    radial-gradient(1100px 720px at 14% 8%, rgba(120,170,255,.55), transparent 60%),
    radial-gradient(1000px 760px at 86% 14%, rgba(206,178,255,.55), transparent 55%),
    radial-gradient(1200px 900px at 50% 108%, rgba(150,220,255,.5), transparent 60%),
    linear-gradient(160deg, #eef2fb, #e4eaf6 70%);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--text); font-family: var(--body); line-height: 1.55;
  -webkit-font-smoothing: antialiased; min-height: 100vh;
  padding: var(--frame); /* frame the floating app — wallpaper shows around it */
}
/* wallpaper layer (admin-uploaded image or the per-mode gradient fallback) */
body::before {
  content: ""; position: fixed; inset: -4%; z-index: -2;
  background: var(--wallpaper-image, none), var(--wp-fallback);
  background-size: cover; background-position: center; background-repeat: no-repeat;
  filter: blur(var(--wallpaper-blur)); transform: scale(1.04);
}
/* readability scrim over the wallpaper (mode-aware colour, intensity from theme) */
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: rgba(var(--wp-scrim), var(--wallpaper-dim));
}
button { font-family: inherit; cursor: pointer; color: inherit; }
input, select { font-family: inherit; }
::selection { background: rgba(var(--accent-rgb), .3); }

/* inline SVG icons inherit text colour and sit on the baseline */
.svgi { display: inline-block; vertical-align: -.18em; flex: none; }
button .svgi, a .svgi { vertical-align: middle; }

/* buttons */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:.45rem;
  background: var(--accent); color: var(--on-accent); border:none; text-decoration:none;
  border-radius:11px; padding:.6rem 1.1rem; font-weight:600; font-size:.9rem;
  box-shadow: 0 8px 18px -10px rgba(var(--accent-rgb),.65);
  transition:filter .15s, transform .1s, box-shadow .15s; }
.btn:hover { filter:brightness(1.08); }
.btn:active { transform: translateY(1px); }
.btn.full { width:100%; margin-top:1.4rem; }
.btn.ghost { background:var(--surface); color:var(--text); border:1px solid var(--line); box-shadow:none; }
.btn.ghost:hover { border-color:var(--accent); color:var(--accent); background:var(--surface-2); }
.btn.danger { background:transparent; color:var(--bad); border:1px solid rgba(255,107,94,.45); box-shadow:none; }
.btn.danger:hover { background:rgba(255,107,94,.12); }
.btn.sm { padding:.42rem .8rem; font-size:.82rem; border-radius:9px; }
.btn.sm > .svgi { margin-block:-3px; }
.x { background:none; border:none; color:var(--muted); font-size:1.4rem; line-height:1; display:grid; place-items:center; }
.x:hover { color:var(--text); }
.pill { font-family:var(--mono); font-size:.7rem; padding:.18rem .5rem; border-radius:6px; background:var(--surface-2); color:var(--muted); }
.err { color:var(--bad); font-size:.85rem; margin-top:1rem; min-height:1.1em; }
label.fld { display:block; font-size:.78rem; color:var(--muted); margin:.85rem 0 .35rem; }
input, select { width:100%; background:var(--surface); border:1px solid var(--line); color:var(--text);
  border-radius:11px; padding:.62rem .85rem; font-size:.93rem; transition:border-color .15s, box-shadow .15s; }
input:focus, select:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3.5px rgba(var(--accent-rgb),.2); }
/* native dropdown list — keep options readable (was light-on-light) */
option { background:#161a23; color:#f4f6fa; }
:root[data-mode="light"] option { background:#fff; color:#1b1f27; }

/* ---------- LOGIN ---------- */
.login-wrap { min-height:100vh; display:grid; place-items:center; padding:1.5rem; }
.login-card { width:min(400px,100%); background:var(--glass);
  backdrop-filter:blur(34px) saturate(180%); -webkit-backdrop-filter:blur(34px) saturate(180%);
  border:1px solid var(--line); border-radius:26px; padding:2.6rem 2rem; box-shadow:var(--shadow); text-align:center; }
.login-card img.logo { width:118px; max-width:55%; margin:0 auto .4rem; display:block; }
.login-card p.sub { color:var(--muted); font-size:.9rem; margin:.2rem 0 1.2rem; }
.login-card label.fld, .login-card input { text-align:left; }
.hint { color:var(--faint); font-size:.76rem; font-family:var(--mono); margin-top:1.3rem; line-height:1.7; text-align:left; }

/* password show/hide eye + login secondary action */
.pw-wrap { position:relative; }
.pw-wrap input { padding-right:2.6rem; }
.pw-eye { position:absolute; right:.45rem; top:50%; transform:translateY(-50%); background:none; border:none;
  color:var(--faint); font-size:1rem; line-height:1; padding:.25rem .35rem; border-radius:6px; }
.pw-eye:hover { color:var(--gold); }
/* "or" divider + Google button slot on the login card */
.or-sep { display:flex; align-items:center; gap:.7rem; margin:1.15rem 0 .95rem; color:var(--faint); font-size:.76rem; }
.or-sep::before, .or-sep::after { content:''; flex:1; height:1px; background:var(--line); }
.gbtn { display:flex; justify-content:center; min-height:46px; }
/* our own Google button — matches the card instead of Google's white iframe */
.gsign { display:flex; align-items:center; justify-content:center; gap:.6rem; width:100%;
  background:var(--surface); border:1px solid var(--line); color:var(--text);
  border-radius:999px; padding:.72rem 1rem; font-size:.92rem; font-family:inherit; cursor:pointer;
  transition:border-color .15s, background .15s, transform .08s; }
.gsign:hover { border-color:var(--accent); background:var(--surface-2); }
.gsign:active { transform:scale(.985); }
.gsign:disabled { opacity:.55; cursor:default; }
.gsign.busy { opacity:.7; pointer-events:none; }
.gsign .glogo { display:flex; background:#fff; border-radius:50%; padding:3px; flex:0 0 auto; }
.login-alt { margin-top:1.2rem; font-size:.84rem; color:var(--muted); text-align:center; }
.login-alt a { color:var(--gold); cursor:pointer; text-decoration:none; }
.login-alt a:hover { text-decoration:underline; }

/* admin: per-user project assignment */
.passign { display:flex; flex-direction:column; gap:.45rem; min-width:min(440px,82vw); }
.passign .prow { display:flex; align-items:center; gap:.7rem; border:1px solid var(--line); border-radius:9px; padding:.5rem .7rem; transition:opacity .15s; }
.passign .prow.off { opacity:.5; }
.passign .prow .pck { width:auto; }
.passign .prow .pn { flex:1; font-size:.92rem; }
.passign .prow .prole { width:auto; }

/* ---------- SHELL ---------- */
.shell { display:grid; grid-template-columns:252px minmax(0,1fr);
  height: calc(100vh - 2 * var(--frame));
  border-radius: 26px; overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow); }
.sidebar { background:var(--glass); backdrop-filter:blur(30px) saturate(180%); -webkit-backdrop-filter:blur(30px) saturate(180%);
  border-right:1px solid var(--line); display:flex; flex-direction:column; padding:1.3rem 1rem; }
.sidebar .logo { width:var(--logo-size, 100px); max-width:78%; margin:.4rem auto 1.4rem; display:block; opacity:.97; }
.navitem { display:flex; align-items:center; gap:.55rem; padding:.58rem .7rem; border-radius:12px; color:var(--muted); font-size:.92rem; border:1px solid transparent; cursor:pointer; margin-top:.4rem; transition:background .14s, color .14s; }
.navitem:hover { background:var(--glass-hi); color:var(--text); }
.navitem .svgi { color:var(--faint); }
.side-label { font-size:.7rem; letter-spacing:.12em; text-transform:uppercase; color:var(--faint); font-family:var(--mono); padding:.5rem .5rem .4rem; }
.proj { display:flex; align-items:center; gap:.55rem; padding:.58rem .7rem; border-radius:12px; color:var(--muted); font-size:.92rem; border:1px solid transparent; cursor:pointer; transition:background .14s, color .14s; }
.proj:hover { background:var(--glass-hi); color:var(--text); }
.proj.active { background:rgba(var(--accent-rgb),.16); border-color:rgba(var(--accent-rgb),.4); color:var(--text); }
/* A long project name used to push the role badge past the 252px sidebar —
   the name is the only part allowed to shrink, and it ellipsises instead. */
.proj .pic, .proj .role { flex:0 0 auto; }
.proj .pnm { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.proj .role { margin-left:auto; font-size:.62rem; font-family:var(--mono); color:var(--faint); }
.proj .empty { color:var(--faint); font-size:.85rem; }
.side-foot { margin-top:auto; border-top:1px solid var(--line); padding-top:.8rem; }

.main { display:flex; flex-direction:column; overflow:hidden; }

/* topbar: search + view toggle + size + user (right) */
.topbar { display:flex; align-items:center; gap:.8rem; padding:.8rem 1.3rem; border-bottom:1px solid var(--line);
  background:var(--glass); backdrop-filter:blur(24px) saturate(180%); -webkit-backdrop-filter:blur(24px) saturate(180%); z-index:5; }
.search { flex:1; position:relative; max-width:580px; display:flex; align-items:center; }
.search input { padding-left:2.4rem; background:var(--surface); border-radius:999px; }
.search-ic { position:absolute; left:.85rem; top:50%; transform:translateY(-50%); color:var(--faint); display:grid; place-items:center; pointer-events:none; }
.spacer { flex:1; }
.segmented { display:flex; border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--surface); }
.segmented button { background:transparent; border:none; padding:.45rem .7rem; color:var(--muted); display:grid; place-items:center; transition:background .14s, color .14s; }
.segmented button.active { background:var(--surface-2); color:var(--accent); }
/* click column header to sort */
th.sortable { cursor:pointer; user-select:none; white-space:nowrap; }
th.sortable:hover { color:var(--gold); }
/* compact selects inside admin modal tables (was stretching full-width = cluttered) */
.modal-body table select { width:auto; padding:.38rem .5rem; font-size:.84rem; }
.add-member .field-row > div { flex:0 1 220px; }
.sizer { display:flex; align-items:center; gap:.4rem; }
.sizer input[type=range] { width:90px; accent-color:var(--gold); }
.bell { position:relative; background:var(--surface); border:1px solid var(--line); border-radius:12px; width:38px; height:38px; color:var(--muted); display:grid; place-items:center; transition:border-color .14s, color .14s; }
.bell:hover { border-color:var(--accent); color:var(--accent); }
.bell-badge { position:absolute; top:-6px; right:-6px; min-width:18px; height:18px; padding:0 4px; border-radius:9px; background:var(--bad); color:#fff; font-size:.66rem; font-family:var(--mono); display:grid; place-items:center; line-height:1; }
.bell-dd { position:absolute; right:0; top:46px; width:min(340px,86vw); background:var(--glass-solid); backdrop-filter:blur(30px) saturate(180%); -webkit-backdrop-filter:blur(30px) saturate(180%); border:1px solid var(--line); border-radius:16px; box-shadow:var(--shadow); z-index:62; overflow:hidden; text-align:left; cursor:default; }
.bell-hd { padding:.65rem .85rem; font-size:.76rem; color:var(--faint); font-family:var(--mono); border-bottom:1px solid var(--line); }
.bell-list { max-height:60vh; overflow:auto; }
.bell-empty { padding:1.4rem; text-align:center; color:var(--faint); font-size:.86rem; }
.bell-item { padding:.65rem .85rem; border-bottom:1px solid var(--line-soft); }
.bell-item:last-child { border-bottom:none; }
.bell-who { font-size:.84rem; margin-bottom:.5rem; color:var(--text); }
.bell-acts { display:flex; gap:.45rem; }
.bell-acts .btn { flex:1; }
.usermenu { position:relative; }
.avatar { width:38px; height:38px; border-radius:50%; background:linear-gradient(135deg,var(--accent),var(--accent-soft)); display:grid; place-items:center; color:var(--on-accent); font-weight:700; font-size:.95rem; cursor:pointer; border:1px solid rgba(255,255,255,.18); box-shadow:0 6px 16px -8px rgba(var(--accent-rgb),.6); }
.dropdown { position:absolute; right:0; top:46px; background:var(--glass-solid); backdrop-filter:blur(30px) saturate(180%); -webkit-backdrop-filter:blur(30px) saturate(180%); border:1px solid var(--line); border-radius:16px; padding:.5rem; min-width:210px; box-shadow:var(--shadow); z-index:60; }
.dropdown .who { padding:.4rem .6rem .6rem; border-bottom:1px solid var(--line); margin-bottom:.4rem; }
.dropdown .who .nm { font-weight:600; font-size:.92rem; }
.dropdown .who .rl { font-size:.72rem; color:var(--faint); font-family:var(--mono); }
.dropdown button { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; background:none; border:none; padding:.5rem .6rem; border-radius:8px; color:var(--text); font-size:.88rem; }
.dropdown button:hover { background:var(--surface-2); }

/* subbar: back + breadcrumb + actions */
.subbar { display:flex; align-items:center; gap:.6rem; padding:.7rem 1.3rem; border-bottom:1px solid var(--line-soft); flex-wrap:wrap;
  background:var(--glass); backdrop-filter:blur(18px) saturate(170%); -webkit-backdrop-filter:blur(18px) saturate(170%); }
.iconbtn { width:34px; height:34px; border-radius:11px; border:1px solid var(--line); background:var(--surface); color:var(--muted); display:grid; place-items:center; font-size:1rem; transition:border-color .14s, color .14s, background .14s; }
.iconbtn:hover:not(:disabled) { border-color:var(--accent); color:var(--accent); }
.iconbtn:disabled { opacity:.35; cursor:default; }
.crumbs { display:flex; align-items:center; gap:.3rem; font-size:.92rem; flex-wrap:wrap; }
.crumbs a { color:var(--muted); cursor:pointer; text-decoration:none; padding:.2rem .55rem; border-radius:8px; border:1.5px solid rgba(var(--accent-rgb),.22); transition:background .12s, border-color .12s, color .12s, box-shadow .12s; }
.crumbs a:hover { color:var(--accent); border-color:rgba(var(--accent-rgb),.5); }
.crumbs a.dropinto { background:rgba(var(--accent-rgb),.22); color:var(--accent); border-color:var(--accent); box-shadow:0 0 0 3px rgba(var(--accent-rgb),.32); }
.crumbs .sep { color:var(--faint); }
.crumbs .cur { color:var(--text); font-weight:500; cursor:pointer; padding:.2rem .4rem; border-radius:8px; }
.crumbs .cur:hover { background:var(--surface-2); }

.content { padding:1.3rem; overflow:auto; flex:1; position:relative; }
/* Drop affordance. The dashed frame is an `outline` painted on the scroll
   container's own border box, so (unlike the old absolutely-positioned ::after,
   which was anchored to the TOP of the scrollable content) it stays put while
   the list scrolls under the cursor mid-drag. The hint chip is `position:sticky`
   so it likewise tracks the viewport instead of scrolling away. */
.content.drag { outline:2px dashed var(--accent); outline-offset:-12px; border-radius:18px; background:rgba(var(--accent-rgb),.06); }
.content.drag::before { content:"วางไฟล์/โฟลเดอร์ที่นี่เพื่ออัปโหลด"; position:sticky; top:42%; z-index:6; display:block; width:max-content; max-width:88%; margin:0 auto; padding:.55rem 1.15rem; border-radius:999px; background:var(--accent); color:#fff; font-size:1rem; box-shadow:0 6px 22px rgba(0,0,0,.18); pointer-events:none; }

/* grid */
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--tile),1fr)); gap:1rem; }
.tile { background:var(--glass); backdrop-filter:blur(18px) saturate(160%); -webkit-backdrop-filter:blur(18px) saturate(160%);
  border:1px solid var(--line); border-radius:var(--r); overflow:hidden; cursor:pointer; box-shadow:var(--shadow);
  transition:border-color .18s, transform .14s, box-shadow .18s; }
.tile:hover { border-color:rgba(var(--accent-rgb),.55); transform:translateY(-3px); }
.tile.dropinto { border-color:var(--accent); box-shadow:0 0 0 2px rgba(var(--accent-rgb),.4) inset; }
.tile.dragging { opacity:.4; }
.list tr.dragging td { opacity:.45; }
.crumbs a.dropinto { background:rgba(var(--accent-rgb),.18); color:var(--accent); border-radius:5px; box-shadow:0 0 0 2px rgba(var(--accent-rgb),.45); }

/* Move-to folder picker */
.movepick { min-width:min(440px,84vw); }
.mp-crumbs { display:flex; flex-wrap:wrap; align-items:center; gap:.25rem; font-size:.9rem; padding:0 0 .6rem; border-bottom:1px solid var(--line); margin-bottom:.55rem; }
.mp-crumbs a { color:var(--muted); cursor:pointer; }
.mp-crumbs a:hover { color:var(--gold); }
.mp-crumbs .sep { color:var(--faint); }
.mp-list { max-height:46vh; overflow:auto; display:flex; flex-direction:column; gap:.25rem; }
.mp-row { display:flex; align-items:center; gap:.5rem; width:100%; text-align:left; background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:.55rem .7rem; color:var(--text); cursor:pointer; font-size:.92rem; }
.mp-row:hover { border-color:var(--gold); }
.mp-row .mp-nm { flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mp-row .mp-go { color:var(--faint); }
.mp-row.blocked { opacity:.4; cursor:not-allowed; }
.mp-row .mp-tag { flex:none; font-size:.68rem; color:var(--faint); border:1px solid var(--line); border-radius:999px; padding:.05rem .4rem; }
.mp-empty { color:var(--muted); padding:.9rem; text-align:center; font-size:.9rem; }
.btn:disabled { opacity:.45; cursor:not-allowed; }
.thumb { aspect-ratio:16/10; background:var(--surface-2); display:grid; place-items:center; position:relative; overflow:hidden; }
.thumb .ic-lg { color:var(--faint); display:grid; place-items:center; }
.thumb .th-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.thumb img { width:100%; height:100%; object-fit:cover; }
.badge { position:absolute; top:.4rem; right:.4rem; font-family:var(--mono); font-size:.58rem; background:rgba(0,0,0,.6); color:var(--gold); padding:.15rem .4rem; border-radius:5px; }
.tmeta { padding:.6rem .7rem .7rem; }
.tname { font-size:.85rem; word-break:break-word; line-height:1.3; }
.tsub { font-size:.7rem; color:var(--faint); font-family:var(--mono); margin-top:.25rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* list */
.list { width:100%; border-collapse:collapse; }
.list th { text-align:left; font-family:var(--mono); font-size:.7rem; text-transform:uppercase; color:var(--faint); padding:.5rem .7rem; border-bottom:1px solid var(--line); }
.list td { padding:.5rem .7rem; border-bottom:1px solid var(--line-soft); font-size:.9rem; }
.list tr { cursor:pointer; }
.list tbody tr:hover { background:var(--surface); }
.list .nm { display:flex; align-items:center; gap:.6rem; }
.list .nm .ic { color:var(--faint); display:inline-grid; place-items:center; }
.list .col-r { text-align:right; color:var(--faint); font-family:var(--mono); font-size:.78rem; white-space:nowrap; }
/* Uploader (admin-only column): a long display name must clip instead of
   stretching the table — the clip lives on the span because max-width on a td
   is only advisory in an auto-layout table. */
.list .col-who span { display:inline-block; max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; vertical-align:bottom; }
.list tr.dropinto td { background:rgba(var(--accent-rgb),.1); }
/* Ctrl held during an internal drag = copy. Green tells you before you let go
   that the original is staying put. */
.tile.dropcopy { border-color:var(--good,#42d692); box-shadow:0 0 0 2px rgba(66,214,146,.45) inset; }
.list tr.dropcopy td { background:rgba(66,214,146,.14); }

.empty-state { color:var(--faint); text-align:center; padding:4rem 1rem; }

/* context menu */
.ctx { position:fixed; background:var(--glass-solid); backdrop-filter:blur(28px) saturate(180%); -webkit-backdrop-filter:blur(28px) saturate(180%); border:1px solid var(--line); border-radius:14px; padding:.4rem; min-width:188px; box-shadow:var(--shadow); z-index:90; }
.ctx button { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; background:none; border:none; padding:.5rem .65rem; border-radius:9px; color:var(--text); font-size:.86rem; }
.ctx button:hover { background:var(--glass-hi); }
.ctx button.danger { color:var(--bad); }
.ctx .div { height:1px; background:var(--line); margin:.3rem 0; }

/* modal */
.overlay { position:fixed; inset:0; background:rgba(4,6,12,.5); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); display:grid; place-items:center; padding:1.5rem; z-index:70; }
.modal { background:var(--glass); backdrop-filter:blur(40px) saturate(190%); -webkit-backdrop-filter:blur(40px) saturate(190%); border:1px solid var(--line); border-radius:24px; width:min(960px,100%); max-height:90vh; display:flex; flex-direction:column; overflow:hidden; box-shadow:var(--shadow); }
.modal.sm { width:min(460px,100%); }
.modal.md { width:min(740px,100%); }
.modal-head { display:flex; align-items:center; gap:.8rem; padding:1rem 1.3rem; border-bottom:1px solid var(--line); }
.modal-head h3 { font-family:var(--display); font-weight:600; font-size:1.05rem; flex:1; word-break:break-word; }
.modal-body { padding:1.3rem; overflow:auto; }
.modal-foot { padding:1rem 1.3rem; border-top:1px solid var(--line); display:flex; gap:.6rem; justify-content:flex-end; flex-wrap:wrap; }
.noprev { text-align:center; color:var(--muted); padding:2.5rem 1.5rem; }
.noprev .ic { font-size:3rem; color:var(--faint); }
.noprev .note { font-size:.85rem; color:var(--faint); margin-top:.6rem; max-width:42ch; margin-inline:auto; }
.cap { text-align:center; color:var(--faint); font-size:.78rem; margin-top:.6rem; }

.field-row { display:flex; gap:.6rem; align-items:end; flex-wrap:wrap; }
.field-row > div { flex:1; min-width:120px; }
.copybar { display:flex; gap:.5rem; margin-top:.8rem; }
.copybar input { font-family:var(--mono); font-size:.82rem; }
/* share dialog: one row per permission (read / read-write / editor) */
.shrows { display:flex; flex-direction:column; gap:.55rem; }
.shrow { border:1px solid var(--line); border-radius:9px; padding:.55rem .7rem; }
.shrow .shlabel { font-size:.82rem; font-weight:500; margin-bottom:.4rem; }
.shrow .shline { display:flex; align-items:center; gap:.4rem; }
.shrow .shlink { flex:1; min-width:0; font-family:var(--mono); font-size:.74rem; }
.shrow .shnone { flex:1; color:var(--faint); font-size:.8rem; }

/* upload progress */
.uprog { position:fixed; bottom:1.3rem; right:1.3rem; width:min(360px,92vw); background:var(--glass); backdrop-filter:blur(30px) saturate(180%); -webkit-backdrop-filter:blur(30px) saturate(180%); border:1px solid var(--line); border-radius:18px; padding:.9rem 1rem; box-shadow:var(--shadow); z-index:85; }
.uprog-head { display:flex; align-items:center; justify-content:space-between; gap:.6rem; font-size:.86rem; color:var(--text); margin-bottom:.55rem; }
.uprog-head .ut { font-weight:500; }
.uprog-btns { display:flex; align-items:center; gap:.55rem; flex:none; }
.uprog .bar { height:7px; background:var(--surface-2); border-radius:999px; overflow:hidden; }
.uprog .bar i { display:block; height:100%; width:0; background:linear-gradient(90deg,var(--gold),var(--lilac)); transition:width .15s; }
.uprog .meta { font-size:.72rem; color:var(--faint); font-family:var(--mono); margin-top:.45rem; }
.uprog.mvprog.stacked { bottom:10.5rem; } /* sits above the upload panel when both run */
.uprog-x { background:none; border:none; color:var(--muted); cursor:pointer; display:grid; place-items:center; padding:0 1px; }
.uprog-x:hover { color:#e8716b; }
.uprog-exp { background:none; border:none; color:var(--muted); cursor:pointer; display:grid; place-items:center; padding:0 1px; }
.uprog-exp:hover { color:var(--gold); }
.uprog-list { display:none; margin-top:.6rem; max-height:230px; overflow:auto; flex-direction:column; gap:.1rem; border-top:1px solid var(--line); padding-top:.5rem; }
.uprog-list.open { display:flex; }
.uf { display:flex; align-items:center; gap:.5rem; font-size:.74rem; color:var(--muted); padding:.18rem 0; }
.uf-st { width:15px; height:15px; flex:none; display:grid; place-items:center; }
.uf-nm { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-family:var(--mono); }
.uf-ic { display:grid; place-items:center; } .uf-ic.ok { color:#5fd08a; } .uf-ic.bad { color:#e8716b; }
.uf-prog { width:15px; height:15px; transform:rotate(-90deg); display:block; }
.uf-prog-bg { fill:none; stroke:var(--line); stroke-width:2.5; }
.uf-prog-fg { fill:none; stroke:var(--gold); stroke-width:2.5; stroke-linecap:round; transition:stroke-dashoffset .15s linear; }

/* admin / modal tables */
table.tbl, .modal-body table { width:100%; border-collapse:collapse; font-size:.88rem; margin-top:.8rem; }
table.tbl th, table.tbl td, .modal-body th, .modal-body td { text-align:left; padding:.55rem .6rem; border-bottom:1px solid var(--line); vertical-align:top; }
table.tbl th, .modal-body th { font-family:var(--mono); font-size:.72rem; text-transform:uppercase; color:var(--muted); }
/* Motion tokens. One easing curve everywhere is what makes movement read as
   one system rather than a pile of separate effects. */
:root { --dur:.18s; --dur-2:.26s; --ease:cubic-bezier(.16,1,.3,1); }

/* ---- Preview cache: space map ------------------------------------------- */
/* Tiles are positioned in pixels by squarify() in admin.js, so this only
   handles looks; geometry lives in JS because it needs the real box size. */
.tm-top { display:flex; align-items:center; gap:.8rem; flex-wrap:wrap; margin:.9rem 0 .5rem; }
.tm-hint { font-size:.76rem; color:var(--faint); flex:1 1 140px; min-width:0; }
.tm-acts { display:flex; gap:.4rem; flex-wrap:wrap; }
.tmap { position:relative; width:100%; height:clamp(240px,34vh,400px); border-radius:14px;
  overflow:hidden; background:var(--bg-2); border:1px solid var(--line-soft); }
.tmt { position:absolute; border:0; border-radius:7px; padding:.3rem .45rem; overflow:hidden;
  display:flex; flex-direction:column; gap:.1rem; text-align:left; color:#fff; background:var(--tc);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.14);
  transition:transform var(--dur) var(--ease), filter var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease), opacity var(--dur-2) var(--ease);
  animation:tmtIn .34s var(--ease) both; }
@keyframes tmtIn { from { opacity:0; transform:scale(.9); } }
.tmt:hover { filter:brightness(1.13); transform:scale(1.015); z-index:2;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.35), 0 12px 24px -12px rgba(0,0,0,.85); }
.tmt.on { box-shadow:inset 0 0 0 2px #fff, 0 14px 28px -12px rgba(0,0,0,.9); z-index:3; }
.tmt.dim { opacity:.2; }
.tmt-n { font-size:.72rem; font-weight:600; line-height:1.15; word-break:break-all;
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
.tmt-b { font-family:var(--mono); font-size:.67rem; opacity:.85; }

.tmlegend { display:flex; flex-wrap:wrap; gap:.35rem; margin-top:.55rem; }
.tmlg { display:inline-flex; align-items:center; gap:.4rem; padding:.28rem .55rem; border-radius:999px;
  border:1px solid var(--line); background:transparent; color:var(--muted); font-size:.74rem;
  transition:border-color var(--dur) var(--ease), color var(--dur) var(--ease),
             background var(--dur) var(--ease), transform var(--dur) var(--ease); }
.tmlg:hover { color:var(--text); border-color:var(--accent); }
.tmlg:active { transform:scale(.97); }
.tmlg.on { border-color:var(--accent); color:var(--accent); background:rgba(var(--accent-rgb),.1); }
.tmlg i { width:9px; height:9px; border-radius:3px; flex:0 0 auto; }
.tmlg-n { max-width:190px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tmlg-b { font-family:var(--mono); font-size:.68rem; color:var(--faint); }

.tmdetail { margin-top:.7rem; border:1px solid var(--line); border-radius:14px;
  background:var(--bg-2); overflow:hidden; animation:qpop .22s var(--ease) both; }
.tmd-head { display:flex; align-items:center; gap:.8rem; padding:.6rem .75rem; }
.tmd-id { flex:1 1 auto; min-width:0; }
.tmd-name { font-weight:600; font-size:.88rem; overflow-wrap:anywhere; }
.tmd-where { font-size:.72rem; color:var(--faint); overflow-wrap:anywhere; }
.tmd-tot { flex:0 0 auto; text-align:right; line-height:1.25; }
.tmd-tot b { font-family:var(--mono); font-size:.85rem; }
.tmd-tot span { display:block; font-size:.7rem; color:var(--faint); }
.tmd-head .btn { flex:0 0 auto; }
.tmd-vars { border-top:1px solid var(--line-soft); max-height:270px; overflow-y:auto; }
.tmd-var { display:flex; align-items:center; gap:.7rem; padding:.4rem .75rem; border-top:1px solid var(--line-soft); }
.tmd-var:first-child { border-top:0; }
.tmd-vname { flex:1 1 auto; min-width:0; font-size:.8rem; overflow-wrap:anywhere; }
.tmd-vsize { font-family:var(--mono); font-size:.76rem; color:var(--muted); }
.tmd-vage { font-size:.72rem; color:var(--faint); }
.tmd-more { color:var(--faint); font-size:.76rem; }

/* Wide tables stay readable by scrolling inside their own box instead of being
   squeezed into a phone's width. */
.tscroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }

/* ---- motion -------------------------------------------------------------- */
/* Only transform/opacity are animated, so these run on the compositor and cost
   the server nothing. The global prefers-reduced-motion rule still wins. */
.apane > #adminPane { animation:paneIn .26s var(--ease) both; }
@keyframes paneIn { from { opacity:0; transform:translateY(10px); } }
.modal { animation:modalIn .3s var(--ease) both; }
@keyframes modalIn { from { opacity:0; transform:scale(.965) translateY(10px); } }
.overlay { animation:ctxFade .2s ease-out both; }
.apane-title { animation:paneIn .3s var(--ease) both; }


/* Admin holds tables, file lists and long paths, so it gets more room than the
   generic 960px modal. The overlay's own padding keeps it off the screen edge. */
.modal.admin-modal { width:min(1280px,100%); }
/* Admin modal: vertical nav on the left, section content on the right. Seven
   sections never fit one horizontal row, and each new one made the bar worse. */
.modal-body.admin-body { padding:0; overflow:hidden; flex:1; min-height:0; display:flex; }
.asplit { display:flex; width:100%; min-height:0; }
.aside { flex:0 0 210px; display:flex; flex-direction:column; gap:.1rem; padding:.75rem .5rem; border-right:1px solid var(--line); overflow-y:auto; }
.anav { display:flex; align-items:center; gap:.55rem; width:100%; padding:.4rem .5rem; border:0; border-radius:8px; background:transparent; color:var(--text); font-size:.85rem; line-height:1.25; text-align:left; transition:background .16s ease, color .16s ease; }
.anav:hover { background:rgba(var(--accent-rgb),.08); }
.anav.on { background:rgba(var(--accent-rgb),.16); color:var(--accent); font-weight:500; }
.anav-i { flex:0 0 auto; width:23px; height:23px; border-radius:6px; display:grid; place-items:center; color:#fff; }
.anav-t { min-width:0; }
.anav-sep { height:1px; margin:.42rem .55rem; background:var(--line); }
.apane { flex:1; min-width:0; padding:1.3rem 1.6rem 1.6rem; overflow-y:auto; }
.apane-title { font-family:var(--display); font-size:1.2rem; font-weight:600; margin:0 0 .95rem; }
/* Too narrow for two columns — nav becomes a wrapped row above the content. */
@media (max-width:620px) {
  .asplit { flex-direction:column; }
  .aside { flex:0 0 auto; flex-direction:row; flex-wrap:wrap; gap:.3rem; padding:.7rem; border-right:0; border-bottom:1px solid var(--line); }
  .anav { width:auto; font-size:.82rem; padding:.35rem .55rem; }
  .anav-sep { display:none; }
  .apane { padding:1rem; }
}

.tabs { display:flex; flex-wrap:wrap; gap:.3rem; margin-bottom:1rem; }
.tab { padding:.42rem .62rem; border-radius:9px; border:1px solid var(--line); background:transparent; color:var(--muted); font-size:.8rem; letter-spacing:-.01em; white-space:nowrap; flex:0 0 auto; transition:background var(--dur,.18s) ease, border-color var(--dur,.18s) ease, color var(--dur,.18s) ease; }
.tab:hover { color:var(--text); border-color:var(--accent); background:rgba(var(--accent-rgb),.06); }
/* Below ~560px seven tabs cannot fit one row at any sane size, so let them wrap
   rather than hide behind a horizontal scroll the user has to discover. */
@media (max-width:560px) { .tab { padding:.42rem .7rem; font-size:.82rem; } }
.tab.active { background:rgba(var(--accent-rgb),.14); border-color:var(--accent); color:var(--accent); }

.toast { position:fixed; bottom:1.3rem; left:50%; transform:translateX(-50%); background:var(--glass); backdrop-filter:blur(24px) saturate(180%); -webkit-backdrop-filter:blur(24px) saturate(180%); border:1px solid var(--line); color:var(--text); padding:.7rem 1.1rem; border-radius:12px; font-size:.88rem; z-index:95; box-shadow:var(--shadow); }
.toast.bad { border-color:rgba(255,107,94,.55); }

/* no text-select anywhere (except typing fields) — keeps marquee/drag clean */
body { user-select:none; -webkit-user-select:none; }
input, textarea { user-select:text; -webkit-user-select:text; }
/* selection (click + rubber-band marquee; no checkboxes) */
.tile.selected { border-color:var(--accent); box-shadow:0 0 0 2px rgba(var(--accent-rgb),.5) inset; }
.list tr.selected td { background:rgba(var(--accent-rgb),.14); }
.marquee { position:fixed; border:1px solid var(--accent); background:rgba(var(--accent-rgb),.16); border-radius:4px; z-index:60; pointer-events:none; }

/* ---------- LIGHTBOX (full-screen preview, Google Drive style) ---------- */
.lightbox { position:fixed; inset:0; background:rgba(8,9,13,.94); z-index:100; display:flex; flex-direction:column; }
.lb-top { display:flex; align-items:center; gap:1rem; padding:.8rem 1.2rem; }
.lb-x { background:none; border:none; color:var(--muted); display:grid; place-items:center; width:36px; height:36px; margin-left:-.5rem; border-radius:10px; transition:color .14s, background .14s; }
.lb-x:hover { color:var(--text); background:rgba(255,255,255,.08); }
.lb-title { font-family:var(--display); font-size:1rem; flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:var(--text); }
.lb-title .ds-view-pg { color:var(--faint); font-family:var(--mono); font-size:.8rem; }
.lb-actions { display:flex; gap:.25rem; }
.lb-act { width:36px; height:36px; display:grid; place-items:center; border:none; border-radius:10px; background:transparent; color:rgba(255,255,255,.72); text-decoration:none; transition:color .14s, background .14s; }
.lb-act:hover { color:#fff; background:rgba(255,255,255,.1); }
.lb-act:active { background:rgba(255,255,255,.16); }
.lb-act:focus-visible, .lb-x:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.lb-act.danger:hover { color:#ff6b6b; background:rgba(255,107,107,.12); }
.lb-stage { flex:1; display:grid; place-items:center; padding:.5rem 2rem 2rem; min-height:0; }
.lb-stage img { max-width:100%; max-height:85vh; object-fit:contain; border-radius:6px; }
.lb-stage video { max-width:100%; }
.lb-stage .prep, .lb-stage .noprev { color:var(--muted); }
.lb-nav { position:fixed; top:50%; transform:translateY(-50%); width:46px; height:46px; border-radius:50%; border:1px solid var(--line); background:rgba(30,36,48,.7); color:var(--text); font-size:1.6rem; line-height:1; display:grid; place-items:center; z-index:101; }
.lb-nav:hover { border-color:var(--gold); color:var(--gold); }
.lb-nav.prev { left:1.2rem; }
.lb-nav.next { right:1.2rem; }

/* share page fills viewport so the drop zone covers the whole area */
#share { min-height:100vh; display:flex; flex-direction:column; }

/* admin: project list. Six bordered boxes stacked at rest read as clutter and
   ate the whole modal, so a closed project is just a row (same density as the
   users table) and only the open one lifts into a card. */
.proj-card { background:transparent; border-bottom:1px solid var(--line-soft); }
.proj-card:last-child { border-bottom:none; }
.proj-card .ph { display:flex; align-items:center; gap:.55rem; flex-wrap:wrap;
  padding:.55rem .35rem; cursor:pointer; user-select:none; }
.proj-card .ph b { font-family:var(--display); font-size:.95rem; min-width:0; overflow-wrap:anywhere; }
/* drive pill is mono with no spaces — let it break instead of pushing the
   delete button out of the card on narrow screens */
.proj-card .ph .pill { min-width:0; overflow-wrap:anywhere; }
.proj-card .ph [data-delp] { flex:0 0 auto; }
/* delete stays one click away but stops shouting from every single row */
.proj-card.collapsed [data-delp] { border-color:transparent; color:var(--faint); }
.proj-card.collapsed .ph:hover [data-delp] { color:var(--bad); border-color:rgba(255,107,94,.45); }
.proj-card:not(.collapsed) { border:1px solid var(--line); border-radius:12px;
  background:var(--bg-2); padding:.15rem .9rem .9rem; margin:.5rem 0; }
.proj-card:not(.collapsed) .ph { border-bottom:1px solid var(--line); padding-bottom:.6rem; margin-bottom:.6rem; }
.proj-card.collapsed .pc-body { display:none; }
.pc-caret { color:var(--muted); font-size:.9rem; line-height:1; transition:transform .15s; display:inline-block; }
.proj-card .ph:hover .pc-caret { color:var(--accent); }
.proj-card:not(.collapsed) .pc-caret { transform:rotate(90deg); }
.pc-count { font-size:.72rem; color:var(--faint); font-family:var(--mono); }
.sec-label { font-family:var(--mono); font-size:.68rem; letter-spacing:.1em; text-transform:uppercase; color:var(--faint); margin:.5rem 0 .3rem; }

/* admin: Google sign-in settings + per-user email cell */
.gauth { background:var(--surface); border:1px solid var(--line); border-radius:12px; padding:.75rem .85rem; }
.gauth .fld { margin-top:.6rem; }
.chk { display:flex; align-items:center; gap:.5rem; font-size:.86rem; color:var(--text); cursor:pointer; }
.chk input[type=checkbox] { width:auto; flex:0 0 auto; margin:0; accent-color:var(--accent); }
.gauth-foot { display:flex; align-items:center; justify-content:space-between; gap:.7rem; flex-wrap:wrap; margin-top:.7rem; }
.gauth-hint { font-size:.74rem; color:var(--faint); flex:1; min-width:180px; }
/* the email column must be able to shrink — a default text input carries an
   implicit ~20ch min width that pushed the users table past the modal (740px) */
.uemail { font-size:.82rem; padding:.35rem .5rem; width:100%; min-width:0; }
.modal-body table td:has(.uemail) { min-width:120px; }
.glink { font-size:.7rem; color:var(--faint); margin-top:.25rem; }
.glink a { color:var(--gold); cursor:pointer; }
.glink a:hover { text-decoration:underline; }
.add-member { background:var(--surface); border:1px dashed var(--line); border-radius:9px; padding:.7rem; margin-top:.7rem; }
.add-member .al { font-size:.78rem; color:var(--muted); margin-bottom:.45rem; }

/* selection action bar */
.selbar { position:fixed; bottom:1.3rem; left:50%; transform:translateX(-50%); display:flex; align-items:center; gap:.7rem;
  background:var(--glass); backdrop-filter:blur(30px) saturate(180%); -webkit-backdrop-filter:blur(30px) saturate(180%); border:1px solid var(--accent); border-radius:999px; padding:.5rem .7rem .5rem 1.1rem;
  box-shadow:var(--shadow); z-index:88; max-width:min(760px, calc(100vw - 2.6rem)); }
.selbar span { font-size:.86rem; color:var(--text); }
/* The bar is a pill, so its label must stay on ONE line. Without this a long
   filename wraps into a tall column and the pill balloons over the file list
   as soon as the window is narrow enough to squeeze it. Buttons never shrink;
   the name gives up the space instead and gets an ellipsis (full name in title). */
.selbar .selcount { flex:0 1 auto; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.selbar .btn { flex:0 0 auto; }

/* proxy preparing spinner */
.prep { text-align:center; color:var(--muted); padding:2.5rem 1.5rem; }
.prep .note { font-size:.92rem; margin-top:1rem; }
.prep .note .sub { font-size:.78rem; color:var(--faint); }
.spinner { width:42px; height:42px; border:3px solid var(--surface-2); border-top-color:var(--gold); border-radius:50%; margin:0 auto; animation:spin 1s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }

/* folder browser (source add) */
.browse { border:1px solid var(--line); border-radius:10px; overflow:hidden; margin:.6rem 0; }
.browse .bhead { display:flex; align-items:center; gap:.5rem; padding:.5rem .7rem; background:var(--surface-2); font-family:var(--mono); font-size:.76rem; color:var(--muted); }
.browse .blist { max-height:240px; overflow:auto; }
.browse .brow { display:flex; align-items:center; gap:.5rem; padding:.5rem .7rem; border-top:1px solid var(--line-soft); cursor:pointer; font-size:.88rem; }
.browse .brow:hover { background:var(--surface); }

/* admin appearance / settings tab */
.set-grid { display:flex; flex-direction:column; gap:1.3rem; }
/* A text field 1000px wide is worse to read than one at a sane width — cap the
   controls without shrinking the labels or section headings around them. */
.set-sec input:not([type=range]):not([type=color]), .set-sec select, .set-sec textarea { max-width:560px; }
.preset-row { display:flex; gap:.5rem; flex-wrap:wrap; }
.accent-row { display:flex; align-items:center; gap:.7rem; flex-wrap:wrap; }
.accent-row input[type=color] { width:46px; height:34px; padding:2px; border:1px solid var(--line); border-radius:9px; background:var(--surface); cursor:pointer; }
.sw-row { display:flex; gap:.45rem; flex-wrap:wrap; }
.sw { width:24px; height:24px; border-radius:50%; border:1px solid rgba(255,255,255,.25); cursor:pointer; padding:0; transition:transform .1s; }
.sw:hover { transform:scale(1.15); }
input[type=range] { width:100%; accent-color:var(--gold); margin:.1rem 0 .2rem; }
.wall-prev { width:128px; height:78px; border-radius:12px; border:1px solid var(--line); background:var(--surface-2) center/cover no-repeat; }
/* logo preview sits inline with its file picker so both stay flush with the
   text fields above (the old two-column split left the pickers indented) */
.asset-row { display:flex; align-items:center; gap:.7rem; }
.asset-row .wall-prev { flex:0 0 auto; }
.asset-row input[type=file] { flex:1; min-width:0; }
.asset-prev { flex:0 0 auto; width:56px; height:56px; object-fit:contain; padding:6px;
  border:1px solid var(--line); border-radius:12px; background:var(--surface-2); }
/* per-file-type custom icon grid (รูปลักษณ์ tab) */
.ficon-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(84px,1fr)); gap:.75rem; }
.ficon-cell { display:flex; flex-direction:column; align-items:center; gap:.3rem; text-align:center; }
.ficon-btn { width:100%; aspect-ratio:1/1; display:grid; place-items:center; background:var(--surface-2); border:1px solid var(--line); border-radius:14px; cursor:pointer; color:var(--faint); transition:border-color .15s, transform .15s; }
.ficon-btn:hover { border-color:rgba(var(--accent-rgb),.55); transform:translateY(-2px); }
.ficon-prev { display:grid; place-items:center; }
.ficon-prev img { width:38px; height:38px; object-fit:contain; }
.ficon-lbl { font-size:.72rem; color:var(--text); }
.ficon-rst { font-size:.68rem; color:var(--faint); background:none; border:none; cursor:pointer; padding:.1rem .4rem; border-radius:6px; }
.ficon-rst:hover { color:var(--accent); background:rgba(var(--accent-rgb),.12); }
.set-foot { display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-top:1.4rem; padding-top:1rem; border-top:1px solid var(--line); flex-wrap:wrap; }

/* mobile projects bottom-sheet list */
.sheet-projs { display:flex; flex-direction:column; gap:.45rem; }
.sheet-proj { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; background:var(--surface); border:1px solid var(--line); border-radius:12px; padding:.72rem .85rem; color:var(--text); font-size:.95rem; }
.sheet-proj.active { border-color:var(--accent); color:var(--accent); background:rgba(var(--accent-rgb),.1); }
.sheet-proj .role { margin-left:auto; font-size:.66rem; font-family:var(--mono); color:var(--faint); flex:0 0 auto; }
.sheet-proj .svgi, .sheet-proj svg { flex:0 0 auto; }
.sheet-proj .pnm { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* HDD scan results (detect -> pick which to add) */
.scan-row { display:flex; align-items:center; gap:.6rem; padding:.5rem 0; border-bottom:1px solid var(--line-soft); }
.scan-row .scan-name { flex:1; min-width:0; }
.scan-row.off { opacity:.65; }

/* ============ RESPONSIVE ============ */
/* Mobile bottom tab bar (iOS-style) — hidden on desktop, shown <640px */
.tabbar { display:none; }
.tabbar button { background:none; border:none; color:var(--faint); display:flex; flex-direction:column;
  align-items:center; gap:3px; font-size:.62rem; font-weight:500; padding:.35rem .4rem; flex:1;
  border-radius:12px; transition:color .14s; }
.tabbar button.active { color:var(--accent); }
.tabbar button .svgi { width:23px; height:23px; }

/* ----- Tablet / iPad (641–1024px): slimmer sidebar, denser grid ----- */
@media (min-width:641px) and (max-width:1024px){
  .shell { grid-template-columns:210px minmax(0,1fr); }
  .sidebar { padding:1.1rem .8rem; }
  .grid { grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:.85rem; }
  .topbar, .subbar { padding-left:1rem; padding-right:1rem; }
  .content { padding:1rem; }
}

/* ----- Phone (<640px): off-canvas sidebar -> bottom tab bar ----- */
@media (max-width:640px){
  body { padding:0; } /* full-bleed on phones — no floating frame */
  .shell { grid-template-columns:minmax(0,1fr); height:100vh; border-radius:0; border:none; box-shadow:none; }
  .sidebar { display:none; }
  .tabbar { display:flex; position:fixed; left:0; right:0; bottom:0; z-index:45;
    justify-content:space-around; align-items:center;
    padding:.4rem .3rem calc(.4rem + env(safe-area-inset-bottom));
    background:var(--glass); backdrop-filter:blur(28px) saturate(180%); -webkit-backdrop-filter:blur(28px) saturate(180%);
    border-top:1px solid var(--line); }
  .topbar { padding:.55rem .8rem; gap:.5rem; padding-top:calc(.55rem + env(safe-area-inset-top)); }
  .topbar .segmented, .topbar .spacer { display:none; }
  .search { min-width:0; max-width:none; }
  .search input { padding-top:.5rem; padding-bottom:.5rem; }
  .subbar { padding:.55rem .8rem; }
  .content { padding:.85rem .85rem calc(80px + env(safe-area-inset-bottom)); }
  .grid { grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem; }
  /* full-screen-ish dialogs as bottom sheets */
  .overlay { align-items:flex-end; padding:0; }
  .modal { width:100%; max-width:100%; max-height:90vh; border-radius:22px 22px 0 0; animation:sheetUp .26s cubic-bezier(.16,1,.3,1); }
  .modal.sm { width:100%; }
  .modal-foot { padding-bottom:calc(1rem + env(safe-area-inset-bottom)); }
  .uprog { left:.7rem; right:.7rem; bottom:calc(78px + env(safe-area-inset-bottom)); width:auto; }
  .selbar { bottom:calc(80px + env(safe-area-inset-bottom)); width:max-content; max-width:92vw; }
  .toast { bottom:calc(82px + env(safe-area-inset-bottom)); }
  .lb-nav.prev { left:.4rem; } .lb-nav.next { right:.4rem; }
  .lb-stage { padding:.5rem .6rem 1rem; }
}
@keyframes sheetUp { from { transform:translateY(100%); } to { transform:translateY(0); } }

@media (prefers-reduced-motion: reduce){
  * { animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

/* ---- ค้นในบท : full-text search across the project's PDFs ----------------- */
/* Topbar trigger. Sits next to the filename filter and must read as a distinct
   action, not a second text field — hence the label. */
.deep-btn { display:inline-flex; align-items:center; gap:.4rem; white-space:nowrap; flex:0 0 auto;
  background:var(--surface); border:1px solid var(--line); border-radius:999px; padding:.45rem .85rem;
  color:var(--muted); font-size:.84rem; font-weight:500; transition:border-color .14s, color .14s, background .14s; }
.deep-btn:hover { border-color:var(--accent); color:var(--accent); background:var(--surface-2); }
.deep-btn .lbl { line-height:1; }
@media (max-width:760px) { .deep-btn .lbl { display:none; } .deep-btn { padding:.45rem; border-radius:12px; } }

.ds { display:flex; flex-direction:column; gap:.7rem; min-height:min(58vh,520px); }
.ds-bar { position:relative; display:flex; align-items:center; }
.ds-bar input { padding-left:2.5rem; background:var(--surface); border-radius:999px; font-size:.95rem; }
.ds-ic { position:absolute; left:.9rem; top:50%; transform:translateY(-50%); color:var(--faint);
  display:grid; place-items:center; pointer-events:none; }

.ds-status { font-size:.78rem; color:var(--faint); font-family:var(--mono); display:flex; align-items:center; gap:.45rem; min-height:1.2rem; }
.ds-status.busy { color:var(--gold); }
.ds-status.bad { color:var(--bad); }
.ds-status b { color:var(--text); }
.ds-spin { width:11px; height:11px; border:2px solid var(--line); border-top-color:var(--gold);
  border-radius:50%; animation:ds-rot .7s linear infinite; display:inline-block; }
@keyframes ds-rot { to { transform:rotate(360deg); } }

.ds-res { flex:1; overflow:auto; display:flex; flex-direction:column; gap:.45rem; margin:0 -.2rem; padding:0 .2rem; }
.ds-empty { padding:2.2rem 1rem; text-align:center; color:var(--faint); font-size:.9rem; }

.ds-item { display:block; width:100%; text-align:left; background:var(--surface); border:1px solid var(--line);
  border-radius:14px; padding:.6rem .75rem; cursor:pointer; transition:border-color .14s, background .14s; }
.ds-item:hover { border-color:var(--accent); background:var(--surface-2); }
.ds-item-hd { display:flex; align-items:center; justify-content:space-between; gap:.6rem; margin-bottom:.25rem; }
.ds-file { display:inline-flex; align-items:center; gap:.4rem; font-size:.86rem; font-weight:600; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ds-file svg { flex:0 0 auto; color:var(--muted); }
.ds-meta { flex:0 0 auto; font-size:.72rem; color:var(--faint); font-family:var(--mono); }
.ds-dir { font-size:.7rem; color:var(--faint); font-family:var(--mono); margin-bottom:.3rem;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ds-snip { font-size:.85rem; line-height:1.65; color:var(--muted);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.ds-snip mark { background:rgba(var(--accent-rgb),.22); color:var(--text); border-radius:4px; padding:0 .15em; font-weight:600; }

.ds-foot-hint { flex:1; font-size:.76rem; color:var(--faint); align-self:center; }

.ds-warn { color:var(--bad); }

/* ============ HDD CAPACITY (admin ▸ ไดรฟ์) ============ */
.dsum { font-size:.82rem; color:var(--muted); background:var(--bg-2); border:1px solid var(--line-soft);
  border-radius:12px; padding:.55rem .8rem; }
.dsum b { color:var(--text); font-weight:600; }
.dusage { min-width:180px; max-width:280px; }
.dbar { height:9px; border-radius:99px; background:rgba(128,138,155,.28); overflow:hidden;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.22); }
.dbar > i { display:block; height:100%; border-radius:99px;
  background:linear-gradient(90deg, var(--accent-soft), var(--accent));
  transition:width .45s cubic-bezier(.16,1,.3,1); }
.dusage.low .dbar > i { background:linear-gradient(90deg, #ff9d7d, var(--bad)); }
.dmeta { display:flex; gap:.6rem; justify-content:space-between; align-items:baseline;
  margin-top:.3rem; font-size:.74rem; color:var(--muted); white-space:nowrap; }
.dmeta b { color:var(--text); font-weight:600; }
.dusage.low .dmeta, .dusage.low .dmeta b { color:var(--bad); }
.dmeta .dpct { font-family:var(--mono); font-size:.67rem; color:var(--faint); }
.dnone { font-size:.76rem; color:var(--faint); }
.scan-item { border-bottom:1px solid var(--line-soft); padding-bottom:.45rem; }
.scan-item > .scan-row { border-bottom:none; padding-bottom:.15rem; }
.scan-item .dusage { max-width:340px; }
/* mount paths have no spaces, so they set an un-shrinkable min width unless we
   allow breaks anywhere — that is what used to push this list past the modal. */
.scan-row .pill { min-width:0; overflow-wrap:anywhere; }

/* Drive rows are cards, not a table: four un-shrinkable columns (long mount
   path + fixed-width capacity bar + nowrap buttons) never fit the 740px admin
   modal and overflowed sideways. Stacking them keeps every width fluid. */
.dlist { display:flex; flex-direction:column; gap:.55rem; margin-top:.8rem; }
.dcard { background:var(--bg-2); border:1px solid var(--line-soft); border-radius:14px; padding:.7rem .85rem; }
.dcard.off { opacity:.62; }
.dc-top { display:flex; align-items:flex-start; gap:.7rem; flex-wrap:wrap; }
.dc-id { flex:1 1 240px; min-width:0; }
.dc-name { font-size:.92rem; font-weight:600; overflow-wrap:anywhere; }
.dc-path { display:inline-block; max-width:100%; margin-top:.28rem; overflow-wrap:anywhere; line-height:1.5; }
.dc-act { display:flex; gap:.35rem; flex:0 0 auto; margin-left:auto; }
.dcard .dusage { min-width:0; max-width:none; margin-top:.6rem; }
@media (max-width:640px) {
  .dusage { min-width:110px; }
  .dmeta { flex-direction:column; gap:.1rem; align-items:flex-start; white-space:normal; }
  .dcard .dmeta { flex-direction:row; justify-content:space-between; align-items:baseline; }
}
.dshare { margin-top:.15rem; font-size:.68rem; color:var(--faint); }

/* ============ TOUCH ACTIONS + PHONE LAYOUT ============================== */
/* Phones have no right-click, so every per-file action used to be unreachable
   there. Three routes in now: a visible ⋯ button, long-press, and the
   selection bar. Menus become bottom sheets because a popup dropped at the
   fingertip lands under the finger (and off-screen near an edge). */

/* ----- the ⋯ affordance ----- */
.more-btn { background:var(--surface); border:1px solid var(--line); color:var(--muted);
  width:32px; height:32px; border-radius:50%; display:inline-grid; place-items:center;
  font-size:1.05rem; line-height:1; cursor:pointer; flex:0 0 auto;
  transition:opacity .14s, border-color .14s, color .14s, background .14s; }
.more-btn:hover { border-color:var(--accent); color:var(--accent); background:var(--surface-2); }
.tile { position:relative; }
.tile .more-btn { position:absolute; top:.4rem; right:.4rem; z-index:3;
  background:var(--glass-solid); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); }
/* the "no preview" badge shares the top-right corner with ⋯ — move it left */
.tile .thumb .badge { right:auto; left:.4rem; }
/* Sits next to the filename, not at the far edge, and stays visible — a
   hover-only control is invisible to anyone who doesn't already know it exists. */
.list th.col-more, .list td.col-more { width:1%; padding:.35rem .6rem; text-align:right; white-space:nowrap; }
.list .more-btn { width:30px; height:30px; font-size:1rem; }

/* ----- long-press feedback + no native callout/drag on touch ----- */
.tile.lp-hold { border-color:var(--accent); transform:scale(.97); }
.list tr.lp-hold td { background:rgba(var(--accent-rgb),.12); }
@media (hover:none) {
  .tile, .list tr, .more-btn { -webkit-touch-callout:none; -webkit-user-select:none; user-select:none; }
  .tile img, .thumb img, .list .ic { -webkit-user-drag:none; }
}

/* ----- action menu as a bottom sheet ----- */
.ctx .ci { flex:0 0 1.25em; display:inline-grid; place-items:center; }
.ctx-back { position:fixed; inset:0; z-index:90; display:flex; align-items:flex-end;
  background:rgba(0,0,0,.42); animation:ctxFade .18s ease-out; }
.ctx.sheet { position:static; width:100%; min-width:0; max-height:78vh; overflow-y:auto;
  border-radius:22px 22px 0 0; border-bottom:none;
  padding:.5rem .5rem calc(.6rem + env(safe-area-inset-bottom));
  animation:sheetUp .26s cubic-bezier(.16,1,.3,1); }
.ctx.sheet button { min-height:50px; padding:.8rem .9rem; font-size:1rem; gap:.85rem; border-radius:14px; }
.ctx.sheet button:active { background:var(--glass-hi); }
@keyframes ctxFade { from { opacity:0; } to { opacity:1; } }

/* ----- folder colour picker (a .ctx panel holding swatches, not a list) -----
   Selectors carry .pal so they outrank the generic `.ctx button` / `.ctx.sheet
   button` rules above, which would otherwise stretch every swatch to a
   full-width 50px row. */
.ctx.pal { min-width:0; padding:.55rem; }
.ctx.pal .pal-h { display:flex; align-items:center; justify-content:space-between; gap:1.2rem;
  font-size:.7rem; letter-spacing:.05em; text-transform:uppercase; color:var(--faint); padding:.15rem .3rem .55rem; }
.ctx.pal .pal-grid { display:grid; grid-template-columns:repeat(8,auto); gap:.34rem; justify-content:center; }
.ctx.pal .sw { width:24px; height:24px; min-height:0; padding:0; border-radius:50%;
  background:var(--sw,transparent); border:1px solid rgba(0,0,0,.22);
  transition:transform .12s ease-out, box-shadow .12s ease-out; }
.ctx.pal .sw:hover, .ctx.pal .sw:focus-visible { transform:scale(1.16); }
.ctx.pal .sw.on { box-shadow:0 0 0 2px var(--glass-solid), 0 0 0 4px var(--accent); }
/* "back to the default blue" is a text button in the header, not a 25th swatch,
   so the grid below stays an even 3 x 8. */
.ctx.pal .sw.none { width:auto; height:auto; min-height:0; border-radius:999px; background:none;
  border:1px solid var(--line); color:var(--muted); padding:.2rem .55rem;
  font-size:.68rem; letter-spacing:.03em; text-transform:none; white-space:nowrap; }
.ctx.pal .sw.none:hover { transform:none; background:var(--glass-hi); color:var(--text); }
.ctx.pal .sw.none.on { box-shadow:none; border-color:var(--accent); color:var(--accent); }
.ctx.pal.sheet { padding:.7rem .7rem calc(1rem + env(safe-area-inset-bottom)); }
.ctx.pal.sheet .pal-h { font-size:.8rem; padding:.3rem .3rem .7rem; }
.ctx.pal.sheet .pal-grid { gap:.55rem; }
.ctx.pal.sheet .sw { width:34px; height:34px; }
.ctx.pal.sheet .sw.none { width:auto; height:auto; font-size:.8rem; padding:.35rem .8rem; }

/* ----- secondary line under a filename (phones only) ----- */
.list .nmtxt { min-width:0; overflow-wrap:anywhere; }
.rowsub { display:none; font-size:.7rem; color:var(--faint); font-family:var(--mono); margin-top:.15rem; }

/* ----- search field must shrink, never overflow its pill ----- */
.search input { width:100%; min-width:0; text-overflow:ellipsis; }
.topbar .segmented, .topbar .bell, .topbar .usermenu, .topbar .deep-btn { flex:0 0 auto; }

/* ----- phone layout repairs ----- */
@media (max-width:640px){
  html, body { overflow-x:hidden; }
  .topbar { gap:.45rem; }
  /* iOS zooms the page when a focused input is under 16px */
  .search input { font-size:16px; }

  /* breadcrumb scrolls sideways instead of wrapping into 3 stacked lines,
     and the action buttons shed their labels to stay on the same row */
  .subbar { flex-wrap:nowrap; gap:.45rem; }
  .subbar .spacer { display:none; }
  .crumbs { flex-wrap:nowrap; overflow-x:auto; min-width:0; flex:1;
    scrollbar-width:none; -webkit-overflow-scrolling:touch; }
  .crumbs::-webkit-scrollbar { display:none; }
  .crumbs a, .crumbs .cur, .crumbs .sep { white-space:nowrap; flex:0 0 auto; }
  .subbar .btn, .subbar .iconbtn { flex:0 0 auto; }
  .subbar .btn .lbl { display:none; }

  /* four columns never fit 390px: keep the name, fold type/size/date into a
     sub-line, and give rows a finger-sized hit area */
  .list th.col-r, .list td.col-r { display:none; }
  .list .rowsub { display:block; }
  .list th { padding:.4rem .5rem; }
  .list td { padding:.62rem .5rem; }
  .list .nm { align-items:flex-start; gap:.55rem; min-width:0; }
  .list .nm .ic { margin-top:.15rem; }

  /* selection bar: full width, scrollable, never wider than the screen */
  .selbar { left:.6rem; right:.6rem; width:auto; max-width:none; transform:none;
    justify-content:flex-start; overflow-x:auto; scrollbar-width:none; padding-left:.9rem; }
  .selbar::-webkit-scrollbar { display:none; }
  /* the highlighted row already says WHICH file; drop the name so the
     buttons fit, and let them shrink to their icons */
  .selbar .selcount { display:none; }
  .selbar .btn { flex:0 0 auto; }
  .selbar .btn .lbl { display:none; }
  .selbar { gap:.45rem; padding:.5rem .6rem; justify-content:space-between; }
}

/* Admin tables on phones: username + display name + a role dropdown + three
   buttons cannot share 390px, and the table used to run straight off the modal.
   Each row becomes a stacked block instead, with the actions wrapping under. */
@media (max-width:640px) {
  .modal-body table, .modal-body table tbody, .modal-body table tr, .modal-body table td { display:block; }
  .modal-body table thead { display:none; }
  .modal-body table tr { border-bottom:1px solid var(--line); padding:.5rem 0; }
  .modal-body table tr:last-child { border-bottom:none; }
  .modal-body table td { width:auto; border-bottom:none; padding:.1rem;
    text-align:left !important; white-space:normal !important; }
  .modal-body table td:first-child { font-weight:600; }
  .modal-body table td:nth-child(2) { color:var(--muted); font-size:.82rem; }
  .modal-body table select { width:100%; margin:.35rem 0 .1rem; }
  .modal-body table td .btn { margin:.25rem .3rem 0 0; }
}

/* Signed-in member viewing a share link: they keep their real project rights,
   so the badge says who they are instead of what the link allows. */
.pill.member { border-color:rgba(var(--accent-rgb),.55); color:var(--accent);
  background:rgba(var(--accent-rgb),.12); }

/* ============================================================
   Resolution picker inside the Plyr control bar (quality.js)
   ============================================================ */
.qctl { position:relative; display:flex; align-items:center; }
.qbtn.plyr__control { display:inline-flex; align-items:center; gap:.28rem;
  font:600 .74rem/1 var(--mono); letter-spacing:.02em;
  padding:.34rem .5rem .34rem .6rem; border-radius:999px;
  background:rgba(255,255,255,.14); color:#fff;
  border:1px solid rgba(255,255,255,.22);
  backdrop-filter:blur(10px) saturate(160%); -webkit-backdrop-filter:blur(10px) saturate(160%);
  transition:background .2s, border-color .2s; }
.qbtn.plyr__control:hover, .qbtn.plyr__control[aria-expanded="true"] {
  background:rgba(var(--accent-rgb),.85); border-color:rgba(255,255,255,.4); color:#fff; }
.qbtn .qlab { display:inline-flex; align-items:center; gap:.32rem; }
.qbtn .svgi { transition:transform .2s; opacity:.75; }
.qbtn[aria-expanded="true"] .svgi { transform:rotate(180deg); }

/* Fixed to the VIEWPORT (quality.js sets left/top/max-height): on a phone the
   player is short and hugs the right edge, so a box anchored to the button ran
   off the top and off the left. Scrolls inside when the screen is too short. */
/* Above .lightbox (100) and .lb-nav (101): the popup is re-parented to <body>,
   so in the drive's lightbox a lower value put it behind the black backdrop —
   it rendered, you just couldn't see it. */
.qmenu { position:fixed; z-index:120; overflow-y:auto; -webkit-overflow-scrolling:touch;
  width:min(292px, calc(100vw - 16px)); text-align:left;
  background:var(--glass-solid);
  backdrop-filter:blur(30px) saturate(185%); -webkit-backdrop-filter:blur(30px) saturate(185%);
  border:1px solid var(--line); border-radius:16px; padding:.45rem;
  box-shadow:0 26px 60px -24px rgba(0,0,0,.75), inset 0 1px 0 rgba(255,255,255,.14);
  animation:qpop .16s cubic-bezier(.16,1,.3,1); }
@keyframes qpop { from { opacity:0; transform:translateY(6px) scale(.97); } }
.qmenu-h { font-size:.68rem; letter-spacing:.09em; text-transform:uppercase;
  color:var(--faint); padding:.4rem .6rem .35rem; }
.qmenu-f { font-size:.7rem; color:var(--faint); padding:.45rem .6rem .25rem;
  border-top:1px solid var(--line-soft); margin-top:.3rem; }
/* Touch targets: the same rows need to be thumb-sized on a phone. */
@media (max-width:640px), (pointer:coarse) {
  .qrow { padding:.62rem .6rem; }
  .qname { font-size:.95rem; }
  .qsub { font-size:.76rem; }
  .qtag { font-size:.7rem; padding:.2rem .5rem; }
  .qbtn.plyr__control { padding:.4rem .5rem .4rem .62rem; font-size:.78rem; }
}
.qrow { display:flex; align-items:center; gap:.5rem; width:100%;
  background:none; border:none; color:var(--text); cursor:pointer;
  padding:.44rem .55rem; border-radius:11px; text-align:left;
  font-family:var(--body); transition:background .15s; }
.qrow:hover { background:var(--glass-hi); }
.qrow.on { background:rgba(var(--accent-rgb),.14); }
.qtick { width:15px; flex:none; display:grid; place-items:center; color:var(--accent); }
.qtxt { display:flex; flex-direction:column; gap:.1rem; min-width:0; flex:1 1 auto; }
.qname { font-size:.87rem; font-weight:600; }
/* one line per row, always — a wrapped hint made the rows different heights */
.qsub { font-size:.7rem; color:var(--faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.qtag { flex:none; font-family:var(--mono); font-size:.66rem; color:var(--muted);
  background:var(--surface-2); border-radius:999px; padding:.16rem .45rem;
  display:inline-flex; align-items:center; gap:.3rem; }
.qtag.busy { color:var(--accent); background:rgba(var(--accent-rgb),.14); }
.qtag.bad { color:var(--bad); background:rgba(255,107,94,.14); }
.qspin { width:10px; height:10px; flex:none; border-radius:50%;
  border:2px solid currentColor; border-top-color:transparent;
  animation:qspin .7s linear infinite; }
@keyframes qspin { to { transform:rotate(360deg); } }
.qbtn.busy .qspin { width:9px; height:9px; }


/* ============================================================
   Admin › พรีวิว (Proxy) — the media cache browser
   ============================================================ */
.mc-warn { color:var(--bad); }
.mc-note { font-size:.78rem; color:var(--faint); margin:.4rem 0 .2rem; }
.mc-bar { display:flex; align-items:center; gap:.8rem; flex-wrap:wrap;
  justify-content:space-between; margin:.7rem 0; }
.mc-check { display:flex; align-items:center; gap:.5rem; font-size:.87rem; cursor:pointer; }
.mc-check input { width:auto; }
.mc-check em { color:var(--faint); font-style:normal; font-size:.8rem; }
.mc-cap { display:flex; align-items:center; gap:.45rem; }
.mc-cap .fld { font-size:.78rem; color:var(--muted); white-space:nowrap; }
.mc-cap input { width:88px; padding:.35rem .5rem; font-size:.85rem; }
.mc-sorts, .mc-acts { display:flex; align-items:center; gap:.4rem; flex-wrap:wrap;
  font-size:.8rem; color:var(--muted); }
.mc-sorts .btn.on { border-color:var(--accent); color:var(--accent); background:var(--surface-2); }
/* class rules beat the UA [hidden] rule, so restate it for the queue slot */
.mc-queue[hidden] { display:none; }
.mc-queue { display:flex; flex-direction:column; gap:.45rem;
  font-size:.84rem; color:var(--accent); background:rgba(var(--accent-rgb),.1);
  border:1px solid rgba(var(--accent-rgb),.3); border-radius:12px; padding:.55rem .75rem; margin:.5rem 0; }
.mcq-top { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
.mcq-top .btn { margin-left:auto; }
.mcq-enc { font-size:.72rem; padding:.1rem .45rem; border-radius:999px;
  background:rgba(var(--accent-rgb),.18); border:1px solid rgba(var(--accent-rgb),.35); }
.mcq-now, .mcq-row { display:flex; align-items:center; gap:.5rem; min-width:0; }
.mcq-now { padding:.15rem 0; }
.mcq-tag { flex:none; font-size:.7rem; font-weight:600; padding:.1rem .4rem; border-radius:6px;
  background:rgba(var(--accent-rgb),.2); }
.mcq-tag.is-proxy { background:rgba(168,85,247,.22); color:#c4a3ff; }
.mcq-tag.is-rend { background:rgba(56,189,248,.2); color:#7dd3fc; }
.mcq-name { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  color:var(--text); font-size:.8rem; }
.mcq-pct { flex:none; font-variant-numeric:tabular-nums; font-size:.78rem; }
.mcq-bar { flex:0 0 110px; height:5px; border-radius:999px; overflow:hidden;
  background:rgba(var(--accent-rgb),.2); }
.mcq-bar i { display:block; height:100%; background:currentColor; border-radius:999px;
  transition:width .4s ease; }
.mcq-wait { border-top:1px dashed rgba(var(--accent-rgb),.28); padding-top:.4rem; margin-top:.1rem;
  display:flex; flex-direction:column; gap:.22rem; }
.mcq-waithead { font-size:.75rem; opacity:.85; }
.mcq-no { flex:none; width:1.2rem; text-align:right; font-size:.72rem; opacity:.6;
  font-variant-numeric:tabular-nums; }
.mcq-more { font-size:.74rem; opacity:.7; padding-left:1.7rem; }
:root[data-mode="light"] .mcq-tag.is-proxy { background:rgba(126,34,206,.14); color:#6b21a8; }
:root[data-mode="light"] .mcq-tag.is-rend { background:rgba(2,132,199,.14); color:#075985; }
@media (max-width:640px) {
  .mcq-bar { flex-basis:60px; }
  .mcq-enc { display:none; }
}

.mc-list { display:flex; flex-direction:column; gap:.55rem; }
.mc-card { border:1px solid var(--line); border-radius:14px; background:var(--bg-2); overflow:hidden; }
.mc-card.orphan { border-color:rgba(255,107,94,.4); }
.mc-head { display:flex; align-items:center; gap:.8rem; padding:.6rem .75rem; }
.mc-id { flex:1 1 auto; min-width:0; }
.mc-name { font-weight:600; font-size:.92rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mc-where { font-size:.74rem; color:var(--faint); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mc-tot { flex:0 0 auto; text-align:right; line-height:1.25; }
.mc-tot b { font-family:var(--mono); font-size:.85rem; }
.mc-tot span { display:block; font-size:.7rem; color:var(--faint); }
.mc-head .btn { flex:0 0 auto; }
.mc-vars { border-top:1px solid var(--line-soft); }
.mc-var { display:flex; align-items:center; gap:.7rem; padding:.4rem .75rem;
  border-top:1px solid var(--line-soft); font-size:.83rem; }
.mc-var:first-child { border-top:none; }
.mc-vname { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mc-vsize, .mc-vage { flex:0 0 auto; font-family:var(--mono); font-size:.74rem; color:var(--muted); }
.mc-vage { color:var(--faint); min-width:56px; text-align:right; }

@media (max-width:640px) {
  .mc-bar { flex-direction:column; align-items:stretch; }
  .mc-cap { flex-wrap:wrap; }
  .mc-head { flex-wrap:wrap; }
  .mc-tot { text-align:left; }
  .mc-head .btn { width:100%; }
  .mc-var { flex-wrap:wrap; gap:.35rem .6rem; }
  .mc-vname { flex:1 0 100%; }
}
.mc-var.mc-more { color:var(--faint); font-size:.78rem; justify-content:center; }

/* ===== MACHINE VITALS (admin "สถานะเครื่อง") =====
   Each card carries one --k / --k-rgb pair; everything inside — sparkline, bar,
   glow, core ticks — derives from those two, so a card recolours in one place.
   The light palette re-picks every hue: the dark-mode cyan/green/violet sit at
   roughly 1.6-2.5:1 on a white card, which is unreadable for the small mono
   labels and the network readouts. */
.sv { display:flex; flex-direction:column; gap:.85rem; transition:opacity .3s;
  --sv-dl:#22d3ee; --sv-ul:#fb923c; --sv-warm:#f5a524; }
.sv.sv-stale { opacity:.45; } /* last known frame, server not answering */

.sv-card[data-k="cpu"] { --k:var(--accent); --k-rgb:var(--accent-rgb); }
.sv-card[data-k="ram"] { --k:#a78bfa; --k-rgb:167,139,250; }
.sv-card[data-k="gpu"] { --k:#34c759; --k-rgb:52,199,89; }
.sv-card[data-k="net"] { --k:var(--sv-dl); --k-rgb:34,211,238; }
.sv-card[data-k="disk"] { --k:#fbbf24; --k-rgb:251,191,36; }

:root[data-mode="light"] .sv { --sv-dl:#0e7490; --sv-ul:#c2410c; --sv-warm:#b45309; }
:root[data-mode="light"] .sv-card[data-k="ram"] { --k:#6d28d9; --k-rgb:109,40,217; }
:root[data-mode="light"] .sv-card[data-k="gpu"] { --k:#15803d; --k-rgb:21,128,61; }
:root[data-mode="light"] .sv-card[data-k="net"] { --k-rgb:14,116,144; }
:root[data-mode="light"] .sv-card[data-k="disk"] { --k:#b45309; --k-rgb:180,83,9; }

.sv-top { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap;
  background:var(--bg-2); border:1px solid var(--line-soft); border-radius:14px; padding:.6rem .85rem; }
.sv-id { display:flex; align-items:center; gap:.5rem; min-width:0; }
.sv-id b { font-family:var(--mono); font-size:.9rem; letter-spacing:.01em; }
.sv-id span:not(.sv-dot) { font-size:.74rem; color:var(--faint); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sv-dot { flex:0 0 auto; width:8px; height:8px; border-radius:50%; background:var(--good);
  box-shadow:0 0 0 0 rgba(52,199,89,.55); animation:svPulse 2s ease-out infinite; }
.sv-stale .sv-dot { background:var(--bad); animation:none; }
@keyframes svPulse { 70% { box-shadow:0 0 0 7px rgba(52,199,89,0); } 100% { box-shadow:0 0 0 0 rgba(52,199,89,0); } }
.sv-up { font-size:.78rem; color:var(--muted); white-space:nowrap; }
.sv-up b { color:var(--text); font-family:var(--mono); font-size:.82rem; }
.sv-tick { display:block; font-size:.66rem; color:var(--faint); font-family:var(--mono); text-align:right; }

.sv-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem; }

.sv-card { position:relative; overflow:hidden; border:1px solid var(--line); border-radius:16px;
  background:linear-gradient(180deg, rgba(var(--k-rgb),.07), transparent 62%), var(--bg-2);
  padding:.7rem .8rem .6rem; display:flex; flex-direction:column; gap:.45rem; }
.sv-card.sv-off { opacity:.5; }
.sv-card header { display:flex; align-items:baseline; gap:.5rem; min-width:0; }
.sv-k { font-family:var(--mono); font-size:.68rem; letter-spacing:.14em; color:var(--k); font-weight:600; }
.sv-card .sv-sub { flex:1; min-width:0; font-size:.7rem; color:var(--faint);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; }

.sv-read { display:flex; align-items:baseline; gap:.18rem; }
.sv-read b { font-family:var(--display); font-size:2.1rem; font-weight:600; line-height:1;
  font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
.sv-read i { font-style:normal; font-size:.9rem; color:var(--faint); }
.sv-temp { margin-left:auto; font-family:var(--mono); font-size:.74rem; color:var(--faint);
  border:1px solid var(--line-soft); border-radius:999px; padding:.1rem .45rem; }
.sv-temp:empty { display:none; }
.sv-temp.warm { color:var(--sv-warm); border-color:rgba(245,165,36,.4); }
.sv-temp.hot { color:var(--bad); border-color:rgba(255,107,94,.5); }

.sv-spk { display:block; width:100%; height:42px; overflow:visible; }
.sv-gridline { fill:none; stroke:var(--line-soft); stroke-width:1; stroke-dasharray:2 5; vector-effect:non-scaling-stroke; }
.sv-area { fill:rgba(var(--k-rgb),.16); stroke:none; }
.sv-line { fill:none; stroke:var(--k); stroke-width:1.6; vector-effect:non-scaling-stroke;
  stroke-linejoin:round; stroke-linecap:round; }
.sv-line2 { stroke:var(--sv-ul); stroke-dasharray:3 2.5; } /* upload, dashed so both directions read apart */

.sv-bar { height:5px; border-radius:99px; background:var(--surface-2); overflow:hidden; }
.sv-bar i { display:block; height:100%; border-radius:99px; background:var(--k); transition:width .45s ease-out; }

/* One tick per core — the cheapest way to show "one thread pinned" vs "all eight busy" */
.sv-cores { display:flex; align-items:flex-end; gap:2px; height:16px; }
.sv-cores i { flex:1; height:100%; display:flex; align-items:flex-end;
  background:var(--surface-2); border-radius:2px; overflow:hidden; }
.sv-cores i b { display:block; width:100%; background:var(--k); border-radius:2px; transition:height .45s ease-out; }

.sv-card footer { font-size:.7rem; color:var(--faint); font-family:var(--mono); line-height:1.5;
  border-top:1px solid var(--line-soft); padding-top:.4rem; margin-top:auto; }

.sv-net .sv-two { display:flex; gap:1.1rem; align-items:flex-end; }
.sv-two > span { display:flex; flex-direction:column; gap:.1rem; min-width:0; }
.sv-two em { font-style:normal; font-size:.66rem; font-family:var(--mono); letter-spacing:.06em; color:var(--faint); }
.sv-two b { font-family:var(--display); font-size:1.15rem; font-weight:600; line-height:1.1;
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.sv-dl b { color:var(--sv-dl); }
.sv-ul b { color:var(--sv-ul); }

.sv-note { font-size:.72rem; color:var(--faint); line-height:1.6; }

/* DISK — full width because it carries one row per physical disk, and because
   this is the card people come to the tab for: when the box feels slow it is
   almost always a saturated spindle, not the CPU. */
.sv-disk { grid-column:1 / -1; }
.dk-list { display:flex; flex-direction:column; gap:.55rem; }
.dk { display:flex; flex-direction:column; gap:.35rem; padding:.55rem .6rem;
  border:1px solid var(--line-soft); border-radius:12px; background:var(--surface-1); }
.dk + .dk { margin-top:0; }
.dk.heavy { border-color:rgba(245,165,36,.45); }
.dk.full { border-color:rgba(255,107,94,.55); background:linear-gradient(180deg, rgba(255,107,94,.06), transparent 70%), var(--surface-1); }

.dk-head { display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; min-width:0; }
.dk-name { font-size:.82rem; font-weight:600; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dk-tag { font-family:var(--mono); font-size:.6rem; letter-spacing:.08em; color:var(--k);
  border:1px solid rgba(var(--k-rgb),.4); border-radius:999px; padding:.05rem .35rem; }
.dk-dev { font-family:var(--mono); font-size:.65rem; color:var(--faint); }
.dk-state { margin-left:auto; font-family:var(--mono); font-size:.65rem; color:var(--faint);
  border:1px solid var(--line-soft); border-radius:999px; padding:.05rem .4rem; }
.dk.heavy .dk-state { color:var(--sv-warm); border-color:rgba(245,165,36,.45); }
.dk.full .dk-state { color:var(--bad); border-color:rgba(255,107,94,.55); }
.dk-util { display:flex; align-items:baseline; gap:.1rem; }
.dk-util b { font-family:var(--display); font-size:1.35rem; font-weight:600; line-height:1;
  font-variant-numeric:tabular-nums; }
.dk-util i { font-style:normal; font-size:.72rem; color:var(--faint); }
.dk.full .dk-util b { color:var(--bad); }

.dk .dk-bar { height:4px; }
.dk.full .dk-bar i { background:var(--bad); }
.dk .dk-spk { height:30px; }

.dk-rw { display:flex; gap:1rem; flex-wrap:wrap; }
.dk-rw > span { display:flex; flex-direction:column; gap:.05rem; min-width:0; }
.dk-rw em { font-style:normal; font-size:.62rem; font-family:var(--mono); letter-spacing:.05em; color:var(--faint); }
.dk-rw b { font-family:var(--mono); font-size:.78rem; font-variant-numeric:tabular-nums; white-space:nowrap; }
.dk-r b { color:var(--sv-dl); }
.dk-w b { color:var(--sv-ul); }

.dk-mounts { display:flex; flex-wrap:wrap; gap:.15rem .9rem; font-family:var(--mono);
  font-size:.66rem; color:var(--faint); }
.dk-mounts b { color:var(--muted); font-weight:600; }
.dk-nomount, .dk-none { font-size:.7rem; color:var(--faint); }

.dk-io { border-top:1px solid var(--line-soft); padding-top:.45rem; }
.dk-io > em { font-style:normal; font-family:var(--mono); font-size:.62rem;
  letter-spacing:.08em; color:var(--faint); }
.dk-io ul { list-style:none; margin:.3rem 0 0; padding:0; display:flex; flex-direction:column; gap:.18rem; }
.dk-io li { display:flex; align-items:baseline; gap:.45rem; font-size:.7rem; min-width:0; }
.dk-io li b { font-family:var(--mono); font-size:.72rem; }
.dk-io li span { font-family:var(--mono); font-size:.64rem; color:var(--faint); }
.dk-io li i { font-style:normal; margin-left:auto; font-family:var(--mono);
  font-size:.68rem; color:var(--muted); white-space:nowrap; }
.dk-io-note { color:var(--faint); font-size:.64rem !important; }
.dk-io-note::before { content:'ℹ '; }

@media (max-width:640px) {
  .sv-grid { grid-template-columns:1fr; }
  .sv-top { flex-direction:column; align-items:flex-start; gap:.35rem; }
  .sv-tick, .sv-up { text-align:left; }
  .dk-dev { display:none; } /* the model name already identifies the disk */
  .dk-io li i { margin-left:0; flex-basis:100%; }
}
/* ---- Transfers ------------------------------------------------------------
   Two surfaces on the same data: a compact live list inside the notification
   bell, and the full "การโหลด" admin tab. Bars animate their width only, so a
   two-second poll glides instead of stepping. */

/* bell section */
.bell-tfs { max-height:44vh; overflow:auto; }
.bell-tf { padding:.75rem .9rem .7rem; border-bottom:1px solid var(--line-soft); }
.bell-tf:last-child { border-bottom:none; }
.bell-tftop { display:flex; align-items:baseline; gap:.6rem; }
.bell-tfwho { flex:1 1 auto; min-width:0; font-size:.8rem; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bell-tfgeo, .tf-geo { flex:0 0 auto; font-size:.74rem; color:var(--faint); white-space:nowrap; }
.tf-geo { margin-left:auto; }
.bell-tffile { display:flex; align-items:center; gap:.4rem; font-size:.9rem; font-weight:600; color:var(--text); margin:.2rem 0 .55rem; overflow:hidden; white-space:nowrap; }
.bell-tffile .tf-kind { flex:0 0 auto; font-weight:400; }
.bell-tfname { min-width:0; overflow:hidden; text-overflow:ellipsis; }

/* progress line + the two numbers that matter, shared by bell and admin tab */
.bell-tfprog, .tf-prog { display:flex; align-items:center; gap:.6rem; }
.bell-tfprog .bell-tfbar, .tf-prog .tf-bar { flex:1 1 auto; }
.bell-tfprog b, .tf-prog b { flex:0 0 auto; min-width:3.4em; text-align:right; font-family:var(--mono); font-size:.8rem; color:var(--text); font-variant-numeric:tabular-nums; }
.bell-tfstats, .tf-stats { display:flex; align-items:baseline; justify-content:space-between; gap:.6rem; margin-top:.45rem; font-size:.82rem; color:var(--muted); }
.bell-tfspd, .tf-spd { font-family:var(--mono); font-size:1.12rem; font-weight:600; color:var(--accent); font-variant-numeric:tabular-nums; }
.bell-tf.stalled .bell-tfspd, .tf-row.stalled .tf-spd { color:var(--bad); font-family:inherit; font-size:.95rem; }
.bell-tfsub, .tf-sub { display:flex; justify-content:space-between; gap:.6rem; margin-top:.2rem; font-size:.72rem; color:var(--faint); }
.bell-tfsub span:last-child, .tf-sub > span:last-child { font-family:var(--mono); font-size:.68rem; }
.bell-tfsub span:first-child, .tf-sub > span:first-child { font-variant-numeric:tabular-nums; }
.bell-tfbar, .tf-bar { height:6px; border-radius:99px; background:var(--surface-2); overflow:hidden; }
.bell-tfbar i, .tf-bar i { display:block; height:100%; border-radius:99px;
  background:linear-gradient(90deg, rgba(var(--accent-rgb),.5), var(--accent)); transition:width .7s ease; }
.bell-tf.stalled .bell-tfbar i, .tf-row.stalled .tf-bar i { background:var(--bad); }
.bell-tfsum { padding:.55rem .9rem; font-size:.76rem; color:var(--faint); border-top:1px solid var(--line-soft); }
.bell-tfsum b { font-family:var(--mono); color:var(--accent); }
.bell-badge.live { background:var(--accent); }

/* admin tab */
.tf { display:flex; flex-direction:column; gap:.8rem; transition:opacity .3s; }
.tf.tf-stale { opacity:.45; }
.tf-hd { display:flex; align-items:center; gap:1.6rem; background:var(--bg-2); border:1px solid var(--line-soft);
  border-radius:14px; padding:.7rem .95rem; }
.tf-sum span { display:block; font-family:var(--mono); font-size:1.15rem; color:var(--text); line-height:1.2; }
.tf-sum small { font-size:.7rem; color:var(--faint); }
.tf-dot { margin-left:auto; width:8px; height:8px; border-radius:50%; background:var(--faint); }
.tf-dot.busy { background:var(--good); box-shadow:0 0 0 0 rgba(52,199,89,.55); animation:svPulse 2s ease-out infinite; }

.tf-live { display:flex; flex-direction:column; gap:.55rem; }
.tf-row { border:1px solid var(--line); border-radius:14px; padding:.7rem .85rem; background:var(--surface); }
.tf-row.stalled { border-color:rgba(255,107,94,.45); }
.tf-row-top { display:flex; align-items:center; gap:.45rem; flex-wrap:wrap; }
.tf-who { font-weight:600; font-size:.9rem; }
.tf-kind { font-family:var(--mono); font-size:.64rem; padding:.12rem .4rem; border-radius:5px;
  background:var(--surface-2); color:var(--muted); }
.tf-kind.dl { background:rgba(var(--accent-rgb),.16); color:var(--accent); }
.tf-kind.rs { background:rgba(52,199,89,.16); color:var(--good); }
.tf-file { font-size:.92rem; font-weight:600; color:var(--text); margin:.3rem 0 .55rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tf-proj { color:var(--faint); font-weight:400; }
/* a zip has no known total until it ends — show motion, not a fake percentage */
.tf-row.unknown .tf-bar i { background:repeating-linear-gradient(90deg,
  rgba(var(--accent-rgb),.55) 0 12px, rgba(var(--accent-rgb),.2) 12px 24px); background-size:24px 100%;
  animation:tfSlide 1s linear infinite; }
@keyframes tfSlide { to { background-position:24px 0; } }
.tf-idle { padding:1.5rem; text-align:center; color:var(--faint); font-size:.88rem;
  border:1px dashed var(--line); border-radius:14px; }

.tf-h4 { font-size:.78rem; color:var(--faint); font-family:var(--mono); margin:.5rem 0 -.2rem; }
.tf-hist { display:flex; flex-direction:column; max-height:34vh; overflow:auto; }
.tf-hrow { display:flex; align-items:center; gap:.55rem; padding:.42rem .1rem; border-bottom:1px solid var(--line-soft); font-size:.78rem; }
.tf-hrow:last-child { border-bottom:none; }
.tf-pill { flex:0 0 auto; font-family:var(--mono); font-size:.64rem; padding:.15rem .45rem; border-radius:5px; }
.tf-pill.ok { background:rgba(52,199,89,.16); color:var(--good); }
.tf-pill.warn { background:rgba(245,165,36,.18); color:#f5a524; }
.tf-pill.bad { background:rgba(255,107,94,.16); color:var(--bad); }
.tf-hfile { flex:1 1 40%; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tf-hwho { flex:0 1 22%; min-width:0; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tf-hnum { flex:0 0 auto; font-family:var(--mono); font-size:.7rem; color:var(--muted); }
.tf-hwhen { flex:0 0 auto; font-family:var(--mono); font-size:.68rem; color:var(--faint); }

.tf-al { display:flex; flex-direction:column; gap:.5rem; }
.tf-lab { font-size:.78rem; color:var(--muted); }
.tf-set { font-family:var(--mono); font-size:.68rem; color:var(--good); }
.tf-al input[type="password"] { width:100%; }
/* labels use .chk (shared) — the global `input { width:100% }` would otherwise
   stretch a checkbox across the row and squeeze its text into a column */
.tf-alrow { display:flex; align-items:center; gap:.5rem; font-size:.84rem; color:var(--text); }
.tf-gb { width:80px; flex:0 0 auto; }
.tf-albtns { display:flex; gap:.45rem; flex-wrap:wrap; margin-top:.2rem; }
.tf-foot { font-size:.7rem; color:var(--faint); border-top:1px solid var(--line-soft); padding-top:.6rem; }
.tf-foot code { font-family:var(--mono); background:var(--surface-2); padding:.1rem .3rem; border-radius:4px; }

:root[data-mode="light"] .tf-pill.warn { background:rgba(180,83,9,.14); color:#b45309; }

@media (prefers-reduced-motion: reduce) {
  .bell-tfbar i, .tf-bar i { transition:none; }
  .tf-row.unknown .tf-bar i, .tf-dot.busy { animation:none; }
}

@media (max-width: 720px) {
  .tf-hrow { flex-wrap:wrap; }
  .tf-hfile { flex-basis:100%; }
  .tf-hwho, .tf-hnum, .tf-hwhen { flex:0 0 auto; }
}

/* ---- late overrides ------------------------------------------------------ */
/* These have to sit at the very bottom: they override rules declared further
   down the file (.proj-card .ph, .dc-top, .anav), and with equal specificity
   the last declaration wins. Putting them earlier silently does nothing. */
.anav { transition:background var(--dur) var(--ease), color var(--dur) var(--ease),
                   transform var(--dur) var(--ease); }
.anav:active { transform:scale(.98); }

/* ---- narrow screens ------------------------------------------------------ */
@media (max-width:620px) {
  /* The project header wrapped into a jumble: name, tag, member count and the
     delete button all landed wherever they fit. Give each its own cell. */
  .proj-card .ph { display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:.3rem .5rem; }
  .proj-card .ph .pc-caret { grid-column:1; grid-row:1; }
  .proj-card .ph b { grid-column:2; grid-row:1; }
  .proj-card .ph [data-delp] { grid-column:3; grid-row:1; margin-left:0 !important; }
  .proj-card .ph .pill { grid-column:2 / -1; grid-row:2; justify-self:start; }
  .proj-card .ph .pc-count { grid-column:2 / -1; grid-row:3; justify-self:start; }

  .field-row { flex-direction:column; align-items:stretch; }
  .field-row > div { min-width:0; }
  .field-row .btn { width:100%; }

  .dc-top .dc-act { display:flex; gap:.4rem; width:100%; justify-content:flex-end; }
  .tscroll table { min-width:520px; }

  .tmap { height:clamp(220px,42vh,330px); }
  .tmd-head { flex-wrap:wrap; }
  .tmd-head .btn { width:100%; }
  .tmlg-n { max-width:130px; }
}

/* ---- phone view toggle --------------------------------------------------
   The topbar's segmented grid/list control has no room on a phone and is
   hidden there (see the phone block above), which left the list view with no
   way to be reached on mobile at all. The subbar carries a single flip button
   instead — phones only, so the desktop toolbar is unchanged. */
.subbar .viewtog { display:none; }
@media (max-width:640px){
  .subbar .viewtog { display:grid; }
}

/* Share pages: topbar logo doubles as the way back to the site front page. */
.topbar .homelink { display:flex; align-items:center; flex:0 0 auto; border-radius:10px; transition:opacity .14s, transform .14s; }
.topbar .homelink:hover { opacity:.78; transform:translateY(-1px); }
.topbar .homelink:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }

/* ---------- SVG glyphs that replaced emoji ---------- */
.dropdown button .svgi { color:var(--muted); flex:0 0 auto; }
.dropdown button:hover .svgi { color:var(--text); }
.ctx .ci .svgi { color:var(--muted); }
.ctx button:hover .ci .svgi { color:var(--text); }
.ctx button.danger .ci .svgi { color:var(--bad); }
.empty-state .es-ic { color:var(--faint); opacity:.7; margin-bottom:.5rem; }
.msg-ic { color:var(--muted); margin-bottom:.8rem; display:grid; place-items:center; }
.svgi.inl { vertical-align:-2px; }
.pw-eye { display:grid; place-items:center; }
.sh-thumb-x { display:grid; place-items:center; color:var(--faint); }
.mp-row .svgi { color:var(--muted); flex:0 0 auto; }
