/* MeshBay Web Client — Phase 9.6 */ /* ── Theme variables ──────────────────────────────────────────────────────── */ :root, .theme-light { --bg-base: #f8fafc; --bg-surface: #ffffff; --bg-raised: #f1f5f9; --text: #0f172a; --text-secondary: #475569; --text-dim: #94a3b8; --border: #e2e8f0; --border-focus: #0ea5e9; --accent: #0ea5e9; --accent-hover: #0284c7; --accent-text: #ffffff; --error: #ef4444; --error-bg: #fef2f2; --success: #22c55e; --nav-bg: #0f172a; --nav-text: #e2e8f0; /* One step down from sky-400 towards sky-500: the wordmark sat brighter than the picture beside it, which made the two read as different materials. */ --nav-brand: #23b1f0; --sidebar-bg: #f1f5f9; --sidebar-hover: #e2e8f0; --shadow: 0 1px 3px rgba(0, 0, 0, 0.08); --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.1); } .theme-dark { --bg-base: #0f172a; --bg-surface: #1e293b; --bg-raised: #334155; --text: #f1f5f9; --text-secondary: #94a3b8; --text-dim: #64748b; --border: #334155; --error-bg: #450a0a; --nav-bg: #020617; --sidebar-bg: #1e293b; --sidebar-hover: #334155; --shadow: 0 1px 3px rgba(0, 0, 0, 0.3); --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.3); } /* ── Reset ────────────────────────────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg-base); color: var(--text); line-height: 1.6; min-height: 100vh; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } .search-synced { font-size: 0.72em; color: var(--text-dim); margin-top: 3px; } /* ── Icons ────────────────────────────────────────────────────────────────── */ /* Sized in em and stroked in currentColor, so an icon inherits the size and colour of whatever it sits next to and needs no rule of its own. Only the places that want a fixed size say so below. */ .icon { width: 1em; height: 1em; vertical-align: -0.14em; flex-shrink: 0; } .icon.flip { transform: rotate(180deg); } /* ── Navigation ───────────────────────────────────────────────────────────── */ .nav { background: var(--nav-bg); color: var(--nav-text); padding: 0 16px; height: 52px; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 100; } .nav-left, .nav-right { display: flex; align-items: center; gap: 12px; } .nav-brand { color: var(--nav-brand); font-weight: 700; /* 22px measured in the application. 1.15em was lettering sized for plain text and read as a caption beside the picture; 1.45em overshot. */ font-size: 1.375em; text-decoration: none; } .nav-brand:hover { text-decoration: none; } /* The M is a picture standing in for a letter, so it is sized in `em` and follows the wordmark rather than being pinned to a pixel height. Sized by measuring the running application, twice. At 1.35em of the old font size it rendered 25px against a 29px line box — *smaller* than the letters beside it, so the wordmark read as unchanged. At 1.9em it was 35px against 29px, which overshot the other way. The pair is now set together: the picture leads, the lettering follows a little behind it. */ .nav-brand { display: inline-flex; align-items: center; } .nav-brand-m { /* **In pixels, not `em` — and that is the point.** Written in `em` this was a fraction of the lettering's size, so every adjustment to the text moved the picture too and the pair could never be settled: making the words bigger made the M bigger by the same stroke, and the ratio never changed. Two independent things need two independent numbers. 30px against 22px of lettering. */ height: 30px; width: auto; /* Positive, deliberately. The glyph this replaces carried its own side bearing and an image has none, so without this the picture sits flush against the "e". */ margin-right: 4px; } .nav-hamburger { display: none; background: none; border: none; color: var(--nav-text); font-size: 1.4em; cursor: pointer; padding: 4px 8px; line-height: 1; } .nav-user { color: var(--text-dim); font-size: 0.9em; } /* ── User Menu ──────────────────────────────────────────────────────────── */ .user-menu-wrap { position: relative; } .user-menu-trigger { display: flex; align-items: center; gap: 8px; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.12); color: var(--nav-text); padding: 4px 10px 4px 4px; border-radius: 20px; cursor: pointer; font-size: 0.85em; transition: background 0.12s; } .user-menu-trigger:hover { background: rgba(255, 255, 255, 0.15); } .user-menu-avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--accent); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.8em; flex-shrink: 0; } .user-menu-avatar.lg { width: 36px; height: 36px; font-size: 0.95em; } .user-menu-name { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .user-menu-caret { font-size: 0.7em; color: rgba(255,255,255,0.5); } .user-menu-dropdown { position: absolute; right: 0; top: calc(100% + 6px); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-lg); min-width: 220px; z-index: 120; /* The language submenu expands to ten entries inside this dropdown, which overruns a short viewport. Clip horizontally as before, scroll vertically rather than hiding the last languages where nothing can reach them. */ max-height: calc(100vh - 72px); overflow-x: hidden; overflow-y: auto; padding: 4px 0; } .user-menu-header { display: flex; align-items: center; gap: 10px; padding: 12px 14px 8px; } .user-menu-uname { font-weight: 600; font-size: 0.9em; color: var(--text); } .user-menu-role { font-size: 0.75em; color: var(--text-dim); text-transform: capitalize; } .user-menu-divider { height: 1px; background: var(--border); margin: 4px 0; } .user-menu-section-label { padding: 6px 14px 2px; font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); font-weight: 600; } .user-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 14px; background: none; border: none; border-radius: 0; color: var(--text); font-size: 0.85em; cursor: pointer; text-align: left; } .user-menu-item:hover { background: var(--bg-raised); } .user-menu-logout { color: var(--error); } .umi-icon { width: 17px; height: 17px; flex-shrink: 0; color: var(--text-secondary); } .umi-arrow { margin-left: auto; width: 13px; height: 13px; color: var(--text-dim); } .umi-check { margin-left: auto; width: 14px; height: 14px; color: var(--accent); } .user-menu-caret { width: 13px; height: 13px; color: var(--nav-text); opacity: 0.8; } .user-menu-sub { padding-left: 32px; } .nav-btn { background: rgba(255, 255, 255, 0.1); color: var(--nav-text); border: none; padding: 6px 14px; border-radius: 6px; font-size: 0.85em; cursor: pointer; text-decoration: none; } .nav-btn:hover { background: rgba(255, 255, 255, 0.18); text-decoration: none; } /* ── Layout ───────────────────────────────────────────────────────────────── */ .layout { display: flex; min-height: calc(100vh - 52px); } /* ── Sidebar ──────────────────────────────────────────────────────────────── */ .sidebar { width: 240px; background: var(--sidebar-bg); border-right: 1px solid var(--border); padding: 16px 0; flex-shrink: 0; overflow-y: auto; } .sidebar-section { padding: 0 12px; margin-bottom: 24px; } .sidebar-heading { font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); padding: 4px 8px; margin-bottom: 4px; font-weight: 600; } .sidebar-item { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border-radius: 6px; color: var(--text); text-decoration: none; font-size: 0.9em; transition: background 0.12s; } .sidebar-item:hover { background: var(--sidebar-hover); text-decoration: none; } .sidebar-item.active { background: var(--accent); color: var(--accent-text); } .sidebar-empty { padding: 8px 12px; color: var(--text-dim); font-size: 0.85em; font-style: italic; } /* ── Main content ─────────────────────────────────────────────────────────── */ .main { flex: 1; padding: 24px 32px; max-width: 960px; min-width: 0; } .main h2 { font-size: 1.25em; margin-bottom: 16px; } .page-message { color: var(--text-secondary); margin-bottom: 24px; } /* ── Page center (login / register) ───────────────────────────────────────── */ .page-center { display: flex; align-items: center; justify-content: center; min-height: calc(100vh - 52px); padding: 24px; } /* A centring page already reserves the whole viewport below the header, so the main column's own padding is added on top of it and the page overflows by exactly that much — 48px, and a scrollbar on sign-in at every window size. Measured, not deduced: `.page-center` ended at 795 and `main` at 819. The page brings its own 24px, so dropping main's here changes nothing visible and removes the duplicated arithmetic rather than adding more. */ .main:has(> .page-center) { padding: 0; } /* ── Cards ────────────────────────────────────────────────────────────────── */ .card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 24px; box-shadow: var(--shadow); } .login-card { width: 100%; max-width: 380px; } .login-card h2 { text-align: center; margin-bottom: 20px; } .login-card form { display: flex; flex-direction: column; gap: 12px; } .login-footer { text-align: center; margin-top: 16px; font-size: 0.85em; color: var(--text-dim); } /* ── Form elements ────────────────────────────────────────────────────────── */ input[type="text"], input[type="password"], input[type="email"] { padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-base); color: var(--text); font-size: 0.95em; width: 100%; transition: border-color 0.15s; } input:focus { outline: none; border-color: var(--border-focus); } button { padding: 10px 20px; background: var(--accent); color: var(--accent-text); border: none; border-radius: 6px; font-size: 0.95em; cursor: pointer; transition: background 0.15s; } button:hover { background: var(--accent-hover); } button:disabled { opacity: 0.5; cursor: not-allowed; } /* ── Alerts ───────────────────────────────────────────────────────────────── */ .error-msg { background: var(--error-bg); color: var(--error); border: 1px solid var(--error); border-radius: 6px; padding: 8px 12px; font-size: 0.85em; } .success-msg { background: #16a34a20; color: var(--success); border: 1px solid var(--success); border-radius: 6px; padding: 8px 12px; font-size: 0.85em; } .settings-hint { font-size: 0.85em; color: var(--text-dim); margin-bottom: 8px; } /* ── Group cards (9.7 prep) ───────────────────────────────────────────────── */ .group-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; } .group-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; cursor: pointer; transition: border-color 0.12s, box-shadow 0.12s; text-decoration: none; color: var(--text); display: block; } .group-card:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); text-decoration: none; } .group-card h3 { font-size: 1em; margin-bottom: 4px; } .badge { display: inline-block; background: var(--bg-raised); color: var(--text-secondary); padding: 2px 8px; border-radius: 12px; font-size: 0.75em; } .badge-ok { background: #16a34a20; color: var(--success); } .badge-err { background: var(--error-bg); color: var(--error); } /* ── Group page header ───────────────────────────────────────────────────── */ .group-header { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; } .group-header h2 { margin-bottom: 0; } /* ── Group tabs ──────────────────────────────────────────────────────────── */ .group-tabs { display: flex; gap: 0; margin-bottom: 16px; border-bottom: 2px solid var(--border); } .group-tab { background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -2px; padding: 10px 18px; color: var(--text-secondary); cursor: pointer; border-radius: 0; transition: color 0.12s, border-color 0.12s; display: flex; align-items: center; justify-content: center; } .group-tab:hover { color: var(--text); background: none; } .group-tab.active { color: var(--accent); border-bottom-color: var(--accent); } .tab-icon { width: 22px; height: 22px; } .group-mute-btn { background: none; border: none; cursor: pointer; color: var(--text-dim); padding: 4px; border-radius: 4px; display: flex; align-items: center; margin-left: auto; } .group-mute-btn:hover { color: var(--text); } .group-mute-btn .icon { width: 20px; height: 20px; } /* ── Chat panel ──────────────────────────────────────────────────────────── */ .chat-panel { position: relative; display: flex; flex-direction: column; /* Overwritten from JS with the space actually left below the group header, which varies with the description and the buttons. These two are the value used for the first paint and the fallback if that never runs. `dvh` rather than `vh` so a phone's collapsing URL bar does not add a scrollbar. */ height: calc(100dvh - 260px); min-height: 240px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; } .chat-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 4px; } .chat-empty { text-align: center; color: var(--text-dim); font-style: italic; padding: 40px 16px; } .chat-msg { display: flex; flex-direction: column; align-items: flex-start; max-width: 75%; } .chat-msg-own { align-self: flex-end; align-items: flex-end; } .chat-sender { font-size: 0.72em; color: var(--text-dim); margin-bottom: 2px; padding-left: 10px; font-weight: 600; } .chat-bubble { background: var(--bg-raised); padding: 8px 12px; border-radius: 12px 12px 12px 4px; font-size: 0.9em; line-height: 1.45; word-break: break-word; display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap; } .chat-bubble-own { background: var(--accent); color: var(--accent-text); border-radius: 12px 12px 4px 12px; } .chat-text { white-space: pre-wrap; } .chat-time { font-size: 0.65em; color: var(--text-dim); white-space: nowrap; flex-shrink: 0; } .chat-bubble-own .chat-time { color: rgba(255, 255, 255, 0.6); } .chat-input-row { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--border); background: var(--bg-base); } .chat-input { flex: 1; resize: none; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); font-size: 0.9em; font-family: inherit; line-height: 1.4; min-height: 38px; max-height: 100px; } .chat-input:focus { outline: none; border-color: var(--border-focus); } .chat-send { align-self: flex-end; padding: 8px 16px; font-size: 0.85em; border-radius: 8px; white-space: nowrap; } /* ── File toolbar ────────────────────────────────────────────────────────── */ /* One bar, three groups: what you can add on the left, where you are in the middle, what you can do with what is here on the right. It used to be four controls of three different heights sitting in a row. */ .file-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; padding: 8px; background: var(--bg-raised); border: 1px solid var(--border); border-radius: 8px; flex-wrap: wrap; } .toolbar-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; } .toolbar-group.right { margin-left: auto; } .tb-btn { display: inline-flex; align-items: center; gap: 6px; /* the icon and the word are not the same word */ height: 32px; padding: 0 11px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); color: var(--text); font-size: 0.83em; white-space: nowrap; cursor: pointer; transition: border-color 0.12s, color 0.12s, background 0.12s; } .tb-btn:hover { border-color: var(--accent); color: var(--accent); } .tb-btn:disabled { opacity: 0.45; cursor: not-allowed; } .tb-btn:disabled:hover { border-color: var(--border); color: var(--text); } .tb-btn .icon { width: 15px; height: 15px; } .tb-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); } .tb-btn.primary:hover { background: var(--accent-hover); color: var(--accent-text); } .tb-btn.active { background: color-mix(in srgb, var(--accent) 12%, var(--bg-surface)); border-color: var(--accent); color: var(--accent); } /* The filter is a field, not a button, and should not pretend otherwise. */ .tb-search { display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); color: var(--text-dim); /* A flex item defaults to min-width:auto and refuses to shrink below its content — which is how a 150px field pushed the toolbar off a phone. */ min-width: 0; } .tb-search:focus-within { border-color: var(--border-focus); } .tb-search .icon { width: 14px; height: 14px; flex-shrink: 0; } .tb-search input { border: none; background: none; outline: none; padding: 0; width: 150px; min-width: 0; font-size: 0.83em; color: var(--text); } .breadcrumbs { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; min-width: 0; font-size: 0.85em; min-width: 0; flex-wrap: wrap; } .crumb { display: inline-flex; align-items: center; cursor: pointer; color: var(--accent); padding: 2px 4px; border-radius: 4px; } .crumb:hover { background: var(--bg-raised); text-decoration: none; } .crumb-sep { color: var(--text-dim); } /* ── File table ──────────────────────────────────────────────────────────── */ .file-table { width: 100%; border-collapse: collapse; font-size: 0.9em; } .file-table th { text-align: left; padding: 8px 12px; border-bottom: 2px solid var(--border); color: var(--text-secondary); font-size: 0.8em; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; user-select: none; } .file-table th.sortable { cursor: pointer; } .file-table th.sortable:hover { color: var(--text); } .file-row td { padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; } .file-row:hover { background: var(--bg-raised); } .dir-row { cursor: pointer; } .dir-row td { font-weight: 500; } .file-name { word-break: break-word; min-width: 0; } .file-size { white-space: nowrap; color: var(--text-secondary); } .file-type { color: var(--text-dim); } .file-date { white-space: nowrap; color: var(--text-dim); } .file-empty { text-align: center; padding: 24px 12px !important; color: var(--text-dim); font-style: italic; } /* ── Spinner ─────────────────────────────────────────────────────────────── */ .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border); border-top: 2px solid var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; vertical-align: middle; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* ── File action menu ───────────────────────────────────────────────────── */ .file-actions-cell { position: relative; width: 40px; } .file-menu-btn { background: none; border: 1px solid var(--border); color: var(--text-secondary); width: 30px; height: 30px; border-radius: 6px; font-size: 1.1em; padding: 0; display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1; } .file-menu-btn:hover { background: var(--bg-raised); border-color: var(--accent); color: var(--accent); } .file-menu { position: absolute; right: 0; top: 100%; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow-lg); z-index: 30; min-width: 130px; padding: 4px 0; overflow: hidden; } .file-menu button { display: block; width: 100%; padding: 8px 14px; background: none; border: none; color: var(--text); font-size: 0.85em; text-align: left; cursor: pointer; border-radius: 0; } .file-menu button:hover { background: var(--bg-raised); } /* ── File link (clickable name) ─────────────────────────────────────────── */ .file-link { color: var(--accent); cursor: pointer; text-decoration: none; } .file-link:hover { text-decoration: underline; } /* ── File preview overlay ───────────────────────────────────────────────── */ .preview-image-wrap { display: flex; align-items: center; justify-content: center; max-width: 90vw; max-height: calc(100vh - 100px); padding: 40px 20px 20px; } .preview-image { max-width: 100%; max-height: calc(100vh - 120px); object-fit: contain; border-radius: 4px; } .preview-text-wrap { width: min(90vw, 900px); max-height: calc(100vh - 100px); overflow: auto; padding: 60px 20px 20px; } .preview-text { background: var(--bg-surface); color: var(--text); padding: 20px; border-radius: 8px; font-size: 0.85em; line-height: 1.6; white-space: pre-wrap; word-break: break-word; overflow-x: auto; margin: 0; font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; tab-size: 4; } /* ── Chat attachment ────────────────────────────────────────────────────── */ .chat-attach { display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; font-size: 1.2em; cursor: pointer; border-radius: 8px; flex-shrink: 0; color: var(--text-secondary); } .chat-attach:hover { background: var(--bg-raised); color: var(--accent); } .chat-attachment { display: flex; flex-direction: column; gap: 2px; } .chat-att-img, .chat-att-file { font-weight: 500; font-size: 0.88em; } .chat-att-thumb { max-width: 200px; max-height: 200px; border-radius: 6px; object-fit: contain; display: block; cursor: pointer; } .chat-att-size { font-size: 0.75em; color: var(--text-dim); } .chat-bubble-own .chat-att-size { color: rgba(255, 255, 255, 0.6); } .file-menu button { display: flex; align-items: center; gap: 8px; } .file-menu button.danger { color: var(--error); } .file-menu button.danger:hover { background: var(--error-bg); } .fmi { font-size: 0.9em; width: 16px; text-align: center; flex-shrink: 0; } /* ── Download button + progress ──────────────────────────────────────────── */ .dl-btn { background: none; border: 1px solid var(--border); color: var(--accent); width: 30px; height: 30px; border-radius: 6px; font-size: 0.9em; padding: 0; display: flex; align-items: center; justify-content: center; cursor: pointer; } .dl-btn:hover { background: var(--bg-raised); border-color: var(--accent); } .dl-btn:disabled { opacity: 0.3; cursor: not-allowed; } .dl-progress { flex: 1; height: 6px; background: var(--bg-raised); border-radius: 3px; overflow: hidden; } .dl-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.2s; } /* ── Settings page ───────────────────────────────────────────────────────── */ .settings-section { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px 20px; margin-bottom: 16px; } .settings-heading { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); font-weight: 600; margin-bottom: 12px; } .settings-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 8px 0; } .settings-row + .settings-row { border-top: 1px solid var(--border); } .settings-label { font-size: 0.9em; color: var(--text); } .settings-value { font-size: 0.9em; color: var(--text-secondary); } .settings-select { padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-base); color: var(--text); font-size: 0.9em; cursor: pointer; min-width: 180px; } .settings-select:focus { outline: none; border-color: var(--border-focus); } /* ── Video player overlay ─────────────────────────────────────────────────── */ .video-overlay { position: fixed; inset: 0; z-index: 200; background: rgba(0, 0, 0, 0.92); display: flex; flex-direction: column; align-items: center; justify-content: center; } .video-top-bar { position: absolute; top: 0; left: 0; right: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 20px; z-index: 210; } .video-title { color: #e2e8f0; font-size: 0.9em; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; } .video-close { background: rgba(255, 255, 255, 0.12); border: none; color: #e2e8f0; width: 36px; height: 36px; border-radius: 50%; font-size: 1.2em; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .video-close:hover { background: rgba(255, 255, 255, 0.25); } .video-close.dl-active { background: rgba(34, 197, 94, 0.25); pointer-events: none; } .video-close.cast-active { background: rgba(59, 130, 246, 0.35); } .video-close.cast-active:hover { background: rgba(59, 130, 246, 0.5); } .cast-url-input { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 4px; color: #e2e8f0; font-size: 0.75em; font-family: inherit; padding: 4px 8px; min-width: 0; flex: 1; max-width: 420px; cursor: text; outline: none; } .cast-url-input:focus { border-color: rgba(59, 130, 246, 0.6); } .cast-wrapper { display: inline-block; } .cast-picker { position: absolute; top: calc(100% + 4px); right: 0; z-index: 100; background: rgba(28, 28, 32, 0.97); border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 8px; padding: 4px 0; min-width: 230px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); } .cast-picker-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 14px; background: none; border: none; color: #e2e8f0; font-size: 0.82em; cursor: pointer; text-align: left; font-family: inherit; } .cast-picker-item:hover { background: rgba(255, 255, 255, 0.09); } .cast-picker-item svg { width: 16px; height: 16px; flex-shrink: 0; } .cast-picker-scanning { color: #94a3b8; cursor: default; } .cast-picker-scanning:hover { background: none; } .cast-picker-empty { color: #64748b; cursor: default; font-style: italic; } .cast-picker-empty:hover { background: none; } .cast-picker-sep { height: 1px; margin: 4px 10px; background: rgba(255,255,255,0.1); } .cast-status-label { color: #94a3b8; font-size: 0.78em; display: flex; align-items: center; max-width: 420px; overflow: hidden; white-space: nowrap; } .video-container { width: 100%; max-width: min(90vw, 1280px); max-height: calc(100vh - 120px); display: flex; align-items: center; justify-content: center; } .video-container video { width: 100%; max-height: calc(100vh - 120px); border-radius: 4px; outline: none; } /* Where the film was picked up again. Sits over the picture rather than pushing it down, and clears itself once the viewer is watching. */ .video-container { position: relative; } .video-resumed { position: absolute; left: 12px; top: 12px; display: flex; gap: 10px; align-items: center; padding: 6px 12px; border-radius: 999px; background: rgba(15, 23, 42, 0.82); color: #e2e8f0; font-size: 0.85rem; pointer-events: auto; animation: video-resumed-fade 6s forwards; } @keyframes video-resumed-fade { 0%, 70% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } } .video-resumed .linklike { background: none; border: 0; padding: 0; color: #7dd3fc; cursor: pointer; font: inherit; text-decoration: underline; } .video-loading { text-align: center; color: #94a3b8; } .video-loading-label { margin-bottom: 16px; font-size: 0.95em; } .video-progress-bar { width: min(400px, 80vw); height: 6px; background: rgba(255, 255, 255, 0.1); border-radius: 3px; overflow: hidden; margin-bottom: 8px; } .video-progress-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.15s; } .video-progress-text { font-size: 0.8em; color: #64748b; } .video-error { color: var(--error); font-size: 0.9em; text-align: center; max-width: 400px; } .play-btn { background: none; border: 1px solid var(--border); color: var(--success); width: 30px; height: 30px; border-radius: 6px; font-size: 0.85em; padding: 0; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; margin-right: 4px; } .play-btn:hover { background: var(--bg-raised); border-color: var(--success); } .play-btn:disabled { opacity: 0.3; cursor: not-allowed; } /* ── Admin panel ─────────────────────────────────────────────────────────── */ .admin-tabs { display: flex; gap: 4px; margin-bottom: 20px; border-bottom: 2px solid var(--border); flex-wrap: wrap; } .admin-tab { padding: 8px 16px; border: none; background: none; color: var(--text-secondary); font-size: 0.9em; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: color 0.15s, border-color 0.15s; } .admin-tab:hover { color: var(--text); } .admin-tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; } .admin-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-bottom: 20px; } .stat-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 20px; text-align: center; } .stat-card .stat-value { font-size: 2rem; font-weight: 700; color: var(--accent); } .stat-card .stat-label { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); margin-top: 4px; } .admin-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; } .admin-search { flex: 1; min-width: 180px; max-width: 300px; padding: 7px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-base); color: var(--text); font-size: 0.9em; } .admin-search:focus { outline: none; border-color: var(--border-focus); } .admin-select { padding: 7px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-base); color: var(--text); font-size: 0.85em; cursor: pointer; } .admin-table { width: 100%; border-collapse: collapse; font-size: 0.88em; } .admin-table th { text-align: left; padding: 8px 10px; border-bottom: 2px solid var(--border); color: var(--text-secondary); font-size: 0.8em; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; } .admin-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: middle; } .admin-table tr:hover { background: var(--bg-raised); } .admin-table .admin-empty { text-align: center; padding: 24px; color: var(--text-dim); font-style: italic; } /* Two things this cell has to do: hold a row of buttons, and stay as tall as the rest of its row even when it is empty — the group owner cannot be removed, and neither can your own account in the admin table. `display: flex` did the second one and broke the first. **A flex