screentinker/frontend/css/main.css
ScreenTinker f115cb454f style(switcher): widen dropdown, tighten rows, prevent org-line wrap
Visual polish to match the new device-count info in ce332ea. The
sidebar-constrained 188px dropdown was too narrow once a second
info chunk ('. N devices') joined the org name on the muted subtitle
line - long names like 'BRASA SALA\'s organization . 2 devices'
wrapped, doubling row height for half the dropdown.

Width: was left:0 + right:0 (= sidebar content width 188px). Now
left:0 + min-width:280px + max-width:360px. Detaches from the
sidebar (which is z-indexed) and extends into the main content area;
the max bound prevents indefinite sprawl on pathological org names.

Row height: padding 10px 12px -> 8px 12px; ws-org margin-top 2px -> 1px.
~58px per row -> ~46px. Less density-heavy at the platform_admin scale
(37 rows visible).

Menu padding: 4px 0 added on the panel so the first/last rows don't
sit flush against the panel border (fixes the 'first row clipped'
visual the tighter rows would otherwise still show).

Max-height: 320px -> 360px. Modest bump now that rows are shorter -
shows ~7 rows at once vs ~5 before.

.ws-org gains white-space:nowrap + overflow:hidden + text-overflow:ellipsis
so the org+count line truncates instead of wrapping. The 360px max-width
sets the truncation threshold.
2026-05-12 14:04:56 -05:00

1386 lines
30 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Layout */
body {
display: flex;
overflow: hidden;
}
.sidebar {
width: var(--sidebar-width);
height: 100vh;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
flex-shrink: 0;
position: fixed;
left: 0;
top: 0;
z-index: 100;
}
.sidebar-header {
padding: 20px 16px;
border-bottom: 1px solid var(--border);
}
.logo {
display: flex;
align-items: center;
gap: 10px;
color: var(--accent);
font-weight: 700;
font-size: 16px;
}
/* Workspace switcher (Phase 3 MVP). Sits in sidebar-header below the logo.
Three render modes via JS: dropdown (>1 ws), static text (1 ws),
muted empty state (0 ws). */
.workspace-switcher { position: relative; margin-top: 12px; }
.workspace-switcher-button {
display: flex; align-items: center; justify-content: space-between;
width: 100%; padding: 8px 10px;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); color: var(--text-primary);
font-size: 13px; cursor: pointer; transition: all var(--transition);
}
.workspace-switcher-button:hover { border-color: var(--accent); }
.workspace-switcher-static {
display: block; padding: 4px 2px;
color: var(--text-primary); font-size: 13px; font-weight: 500;
}
.workspace-switcher-static::before {
content: 'Workspace';
display: block;
font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
color: var(--text-muted); margin-bottom: 2px;
}
.workspace-switcher-empty {
display: block; padding: 8px 10px;
color: var(--text-muted); font-size: 12px; font-style: italic;
}
.workspace-switcher-button .chev {
flex-shrink: 0; margin-left: 8px; color: var(--text-muted);
transition: transform var(--transition);
}
.workspace-switcher.open .chev { transform: rotate(180deg); }
.workspace-switcher-menu {
display: none;
/* Width: detach from the narrow sidebar-header (188px content width). The
sidebar is z-indexed and the dropdown is free to extend beyond the
sidebar into the main content area. min/max bounds keep it readable
for normal-length names without sprawling on extreme cases. */
position: absolute; top: calc(100% + 4px); left: 0;
min-width: 280px; max-width: 360px;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: 0 4px 12px rgba(0,0,0,0.3);
max-height: 360px; padding: 4px 0; overflow-y: auto; z-index: 100;
}
.workspace-switcher.open .workspace-switcher-menu { display: block; }
.workspace-switcher-item {
display: flex; align-items: center; gap: 8px;
padding: 8px 12px; cursor: pointer;
border-bottom: 1px solid var(--border);
color: var(--text-primary); font-size: 13px;
}
.workspace-switcher-item:last-child { border-bottom: none; }
.workspace-switcher-item:hover { background: var(--bg-input); }
.workspace-switcher-item.current { font-weight: 600; }
.workspace-switcher-item .check {
flex-shrink: 0; color: var(--accent); width: 14px;
}
.workspace-switcher-item .ws-meta { flex: 1; min-width: 0; }
.workspace-switcher-item .ws-name {
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.workspace-switcher-item .ws-org {
font-size: 11px; color: var(--text-muted); margin-top: 1px;
/* nowrap + ellipsis: long "Org Name . N devices" lines truncate cleanly
instead of wrapping onto a second line that doubles row height. */
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.workspace-switcher-pencil {
flex-shrink: 0; visibility: hidden;
background: none; border: none; padding: 4px;
color: var(--text-muted); cursor: pointer;
border-radius: 4px; transition: all var(--transition);
}
.workspace-switcher-item:hover .workspace-switcher-pencil { visibility: visible; }
.workspace-switcher-pencil:hover { color: var(--accent); background: var(--bg-input); }
.nav-links {
flex: 1;
padding: 12px 8px;
}
.nav-link {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: var(--radius);
color: var(--text-secondary);
transition: all var(--transition);
margin-bottom: 2px;
}
.nav-link:hover {
background: var(--bg-card);
color: var(--text-primary);
}
.nav-link.active {
background: var(--accent);
color: white;
}
.sidebar-footer {
padding: 16px;
border-top: 1px solid var(--border);
}
.connection-status {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-muted);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.status-dot.online { background: var(--success); box-shadow: 0 0 6px var(--success); }
.status-dot.offline { background: var(--danger); }
.status-dot.provisioning { background: var(--warning); animation: pulse 2s infinite; }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.content {
margin-left: var(--sidebar-width);
flex: 1;
height: 100vh;
overflow-y: auto;
padding: 24px 32px;
}
/* Page Header */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.page-header h1 {
font-size: 24px;
font-weight: 600;
}
.page-header .subtitle {
color: var(--text-secondary);
font-size: 13px;
margin-top: 2px;
}
/* Buttons */
.btn {
padding: 8px 16px;
border-radius: var(--radius);
font-weight: 500;
font-size: 13px;
transition: all var(--transition);
display: inline-flex;
align-items: center;
gap: 6px;
}
.btn-primary {
background: var(--accent);
color: white;
}
.btn-primary:hover {
background: var(--accent-hover);
}
.btn-secondary {
background: var(--bg-card);
border: 1px solid var(--border);
color: var(--text-primary);
}
.btn-secondary:hover {
background: var(--bg-card-hover);
}
.btn-danger {
background: var(--danger-dim);
color: #fca5a5;
}
.btn-danger:hover {
background: var(--danger);
color: white;
}
.btn-sm {
padding: 5px 10px;
font-size: 12px;
}
.btn-icon {
padding: 6px;
border-radius: var(--radius);
color: var(--text-secondary);
transition: all var(--transition);
}
.btn-icon:hover {
background: var(--bg-card);
color: var(--text-primary);
}
/* Device Grid */
.device-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.device-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
transition: all var(--transition);
cursor: pointer;
}
.device-card:hover {
border-color: var(--accent);
box-shadow: var(--shadow);
transform: translateY(-2px);
}
.device-card-preview {
aspect-ratio: 16/9;
background: var(--bg-primary);
position: relative;
overflow: hidden;
}
.device-card-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.device-card-preview .no-preview {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--text-muted);
gap: 8px;
}
.device-card-preview .no-preview svg {
opacity: 0.3;
}
.device-card-status {
position: absolute;
top: 10px;
right: 10px;
display: flex;
align-items: center;
gap: 6px;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(4px);
padding: 4px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 500;
}
.device-card-select {
position: absolute;
top: 8px;
left: 8px;
z-index: 5;
background: rgba(0,0,0,0.6);
border-radius: 4px;
padding: 3px 5px;
display: flex;
align-items: center;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s;
}
.device-card:hover .device-card-select,
.device-card.selected .device-card-select { opacity: 1; }
.device-card-select input { cursor: pointer; margin: 0; }
.device-card.selected { outline: 2px solid var(--primary, #3B82F6); outline-offset: -2px; }
/* Wall editor — free-form pan/zoom canvas */
.wall-viewport {
position: relative;
overflow: hidden;
cursor: grab;
user-select: none;
background:
linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px) 0 0 / 40px 40px,
linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px) 0 0 / 40px 40px,
var(--bg-primary);
}
.wall-viewport.panning { cursor: grabbing; }
/* Inner canvas: a 0×0 anchor whose CSS transform supplies pan + zoom.
All rect children are absolutely positioned in canvas-data coordinates
and inherit the parent transform. transform-origin is the canvas's
top-left so pan offsets map cleanly to data → screen pixels. */
.wall-canvas {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
transform-origin: 0 0;
/* Disable transition so panning doesn't lag behind the cursor */
}
.wall-zoom-readout {
position: absolute;
bottom: 8px;
right: 12px;
background: rgba(0,0,0,0.65);
color: #fff;
padding: 3px 8px;
border-radius: 12px;
font-size: 11px;
pointer-events: none;
font-variant-numeric: tabular-nums;
}
.wall-screen {
position: absolute;
background: rgba(59,130,246,0.08);
border: 2px solid var(--primary, #3B82F6);
border-radius: 4px;
box-sizing: border-box;
cursor: move;
user-select: none;
touch-action: none;
overflow: hidden;
}
.wall-screen-overlap {
position: absolute;
background: rgba(96,165,250,0.35);
pointer-events: none;
display: none;
z-index: 1;
}
.wall-screen-label {
position: absolute;
top: 4px;
left: 6px;
right: 24px;
pointer-events: none;
z-index: 2;
}
.wall-screen-name {
font-size: 12px;
font-weight: 600;
color: var(--text-primary, #fff);
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wall-screen-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: 10px;
color: var(--text-muted);
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.wall-screen-remove {
position: absolute;
top: 4px;
right: 4px;
z-index: 3;
width: 20px;
height: 20px;
background: rgba(0,0,0,0.6);
color: #fff;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0;
}
.wall-screen-remove:hover { background: var(--danger, #ef4444); }
.wall-player {
position: absolute;
background: rgba(96,165,250,0.18);
border: 2px dashed #60a5fa;
border-radius: 4px;
box-sizing: border-box;
cursor: move;
user-select: none;
touch-action: none;
z-index: 5;
box-shadow: 0 0 0 9999px transparent; /* keeps stacking explicit */
}
.wall-player-label {
position: absolute;
top: 4px;
left: 6px;
pointer-events: none;
color: #dbeafe;
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
font-size: 11px;
letter-spacing: 1px;
}
/* Selected rect highlight (works for both screens and the player) */
.wall-screen.selected,
.wall-player.selected {
outline: 3px solid #facc15;
outline-offset: 1px;
z-index: 6;
}
/* Fine-position panel inputs */
.wall-pos-grid {
display: grid;
grid-template-columns: auto 1fr auto 1fr;
gap: 6px 8px;
align-items: center;
font-size: 12px;
}
.wall-pos-grid label {
font-size: 11px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.wall-pos-grid input {
width: 100%;
padding: 4px 6px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary, #fff);
font: inherit;
font-variant-numeric: tabular-nums;
}
.wall-pos-grid input:focus { outline: 1px solid var(--primary); outline-offset: 0; border-color: var(--primary); }
/* Eight resize handles, used by both screens and the player */
.wall-handle {
position: absolute;
width: 10px;
height: 10px;
background: #fff;
border: 1px solid #1d4ed8;
border-radius: 2px;
z-index: 4;
}
.wall-player .wall-handle { border-color: #60a5fa; }
.wall-handle-nw { top: -5px; left: -5px; cursor: nw-resize; }
.wall-handle-n { top: -5px; left: 50%; transform: translateX(-50%); cursor: n-resize; }
.wall-handle-ne { top: -5px; right: -5px; cursor: ne-resize; }
.wall-handle-e { top: 50%; right: -5px; transform: translateY(-50%); cursor: e-resize; }
.wall-handle-se { bottom: -5px; right: -5px; cursor: se-resize; }
.wall-handle-s { bottom: -5px; left: 50%; transform: translateX(-50%); cursor: s-resize; }
.wall-handle-sw { bottom: -5px; left: -5px; cursor: sw-resize; }
.wall-handle-w { top: 50%; left: -5px; transform: translateY(-50%); cursor: w-resize; }
/* Wall editor — legacy cells (kept for migration; new editor uses wall-canvas) */
.wall-cell {
position: relative;
background: var(--bg-card);
border: 2px dashed var(--border);
border-radius: var(--radius);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 11px;
color: var(--text-secondary);
user-select: none;
}
.wall-cell.occupied {
background: rgba(59,130,246,0.15);
border: 2px solid var(--primary, #3B82F6);
cursor: grab;
}
.wall-cell.occupied:active { cursor: grabbing; }
.wall-cell.drag-over {
border-color: var(--success, #10b981);
box-shadow: 0 0 0 2px rgba(16,185,129,0.25) inset;
}
.wall-cell-name { font-weight: 500; padding: 0 6px; text-align: center; }
.wall-cell-pos {
position: absolute;
bottom: 4px;
font-size: 9px;
color: var(--text-muted);
letter-spacing: 0.5px;
}
.wall-cell-remove {
position: absolute;
top: 4px; right: 4px;
background: rgba(0,0,0,0.6);
border: none;
color: #fff;
border-radius: 50%;
width: 20px; height: 20px;
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0;
}
.wall-cell-remove:hover { background: var(--danger, #ef4444); }
.wall-card .wall-card-preview {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(139,92,246,0.15), rgba(59,130,246,0.1));
}
.wall-card-grid {
display: grid;
gap: 4px;
width: 65%;
aspect-ratio: 16/9;
padding: 8px;
}
.wall-card-cell {
background: rgba(255,255,255,0.05);
border: 1px solid rgba(139,92,246,0.3);
border-radius: 2px;
}
.wall-card-cell.filled {
background: rgba(139,92,246,0.5);
border-color: rgba(139,92,246,0.9);
}
.device-card-progress {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 6px 10px 8px;
background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0));
color: #fff;
font-size: 11px;
pointer-events: none;
}
.device-card-progress-label {
display: flex;
justify-content: space-between;
gap: 8px;
margin-bottom: 4px;
font-weight: 500;
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.device-card-progress-label .dcp-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.device-card-progress-label .dcp-time {
font-variant-numeric: tabular-nums;
opacity: 0.85;
}
.device-card-progress-track {
height: 3px;
background: rgba(255,255,255,0.2);
border-radius: 2px;
overflow: hidden;
}
.device-card-progress-fill {
height: 100%;
width: 0%;
background: var(--primary, #3B82F6);
transition: width 0.9s linear;
}
.device-card-progress-fill.indeterminate {
background: linear-gradient(90deg, transparent, var(--primary, #3B82F6), transparent);
background-size: 50% 100%;
animation: dcp-indeterminate 1.4s linear infinite;
}
@keyframes dcp-indeterminate {
0% { background-position: -50% 0; }
100% { background-position: 150% 0; }
}
.device-card-body {
padding: 14px 16px;
}
.device-card-name {
font-weight: 600;
font-size: 15px;
margin-bottom: 6px;
}
.device-card-meta {
display: flex;
align-items: center;
gap: 12px;
color: var(--text-secondary);
font-size: 12px;
}
.device-card-meta .meta-item {
display: flex;
align-items: center;
gap: 4px;
}
/* Device Detail */
.device-detail {
max-width: 1200px;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--text-secondary);
margin-bottom: 16px;
font-size: 13px;
transition: color var(--transition);
}
.back-link:hover { color: var(--text-primary); }
.device-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 20px;
}
.device-header-left {
display: flex;
align-items: center;
gap: 16px;
}
.device-header-left h1 {
font-size: 22px;
}
.device-status-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.device-status-badge.online { background: var(--success-dim); color: var(--success); }
.device-status-badge.offline { background: var(--danger-dim); color: #fca5a5; }
.device-status-badge.provisioning { background: var(--warning-dim); color: var(--warning); }
.tabs {
display: flex;
gap: 0;
border-bottom: 1px solid var(--border);
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
color: var(--text-secondary);
font-size: 13px;
font-weight: 500;
border-bottom: 2px solid transparent;
transition: all var(--transition);
cursor: pointer;
}
.tab:hover { color: var(--text-primary); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-content { display: none; }
.tab-content.active { display: block; }
/* Screenshot Preview */
.screenshot-container {
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
aspect-ratio: 16/9;
position: relative;
margin-bottom: 20px;
}
.screenshot-container img {
width: 100%;
height: 100%;
object-fit: contain;
}
.screenshot-container .no-screenshot {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--text-muted);
gap: 8px;
}
/* Remote Control */
.remote-container {
display: flex;
gap: 20px;
}
.remote-screen {
flex: 1;
background: #000;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
position: relative;
cursor: crosshair;
}
.remote-screen canvas {
width: 100%;
display: block;
}
.remote-controls {
width: 120px;
display: flex;
flex-direction: column;
gap: 8px;
}
.remote-controls .btn {
width: 100%;
justify-content: center;
}
/* Playlist */
.playlist-container {
display: flex;
flex-direction: column;
gap: 8px;
}
.playlist-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: all var(--transition);
}
.playlist-item:hover {
border-color: var(--border-light);
}
.playlist-item-thumb {
width: 80px;
height: 45px;
border-radius: 4px;
object-fit: cover;
background: var(--bg-primary);
flex-shrink: 0;
}
.playlist-item-info {
flex: 1;
min-width: 0;
}
.playlist-item-name {
font-weight: 500;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playlist-item-meta {
font-size: 11px;
color: var(--text-secondary);
}
.playlist-item-actions {
display: flex;
gap: 4px;
}
/* Info Grid */
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.info-card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px;
}
.info-card-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
margin-bottom: 6px;
}
.info-card-value {
font-size: 20px;
font-weight: 600;
}
.info-card-value.small {
font-size: 14px;
}
/* Progress bar */
.progress-bar {
height: 6px;
background: var(--bg-primary);
border-radius: 3px;
overflow: hidden;
margin-top: 8px;
}
.progress-bar-fill {
height: 100%;
border-radius: 3px;
transition: width var(--transition);
}
.progress-bar-fill.success { background: var(--success); }
.progress-bar-fill.warning { background: var(--warning); }
.progress-bar-fill.danger { background: var(--danger); }
/* Content Library */
.content-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.content-item {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
transition: all var(--transition);
}
.content-item:hover {
border-color: var(--border-light);
}
.content-item-preview {
aspect-ratio: 16/9;
background: var(--bg-primary);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.content-item-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.content-item-preview .video-icon {
color: var(--text-muted);
}
.content-item-body {
padding: 10px 12px;
}
.content-item-name {
font-size: 12px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.content-item-size {
font-size: 11px;
color: var(--text-muted);
}
.content-item-actions {
display: flex;
justify-content: flex-end;
padding: 0 12px 10px;
gap: 4px;
}
/* Upload Area */
.upload-area {
border: 2px dashed var(--border);
border-radius: var(--radius-lg);
padding: 48px 24px;
text-align: center;
cursor: pointer;
transition: all var(--transition);
margin-bottom: 24px;
}
.upload-area:hover,
.upload-area.dragover {
border-color: var(--accent);
background: rgba(59, 130, 246, 0.05);
}
.upload-area svg {
margin: 0 auto 12px;
color: var(--text-muted);
}
.upload-area p {
color: var(--text-secondary);
font-size: 14px;
}
.upload-area .upload-hint {
font-size: 12px;
color: var(--text-muted);
margin-top: 4px;
}
/* Modal */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
width: 440px;
max-width: 90vw;
box-shadow: var(--shadow);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--border);
}
.modal-header h3 {
font-size: 16px;
font-weight: 600;
}
.modal-body {
padding: 20px;
}
.modal-description {
color: var(--text-secondary);
font-size: 13px;
margin-bottom: 16px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px 20px;
border-top: 1px solid var(--border);
}
/* Form Elements */
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
margin-bottom: 6px;
}
.input {
width: 100%;
padding: 8px 12px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text-primary);
transition: border-color var(--transition);
}
.input:focus {
outline: none;
border-color: var(--accent);
}
.pairing-input {
width: 100%;
padding: 12px 16px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text-primary);
font-size: 28px;
font-weight: 700;
text-align: center;
letter-spacing: 8px;
font-family: 'Courier New', monospace;
}
.pairing-input:focus {
outline: none;
border-color: var(--accent);
}
/* Toast */
.toast-container {
position: fixed;
bottom: 24px;
right: 24px;
display: flex;
flex-direction: column;
gap: 8px;
z-index: 2000;
}
.toast {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 12px 16px;
min-width: 280px;
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 10px;
animation: slideIn 0.3s ease;
}
.toast.success { border-left: 3px solid var(--success); }
.toast.error { border-left: 3px solid var(--danger); }
.toast.info { border-left: 3px solid var(--accent); }
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* Empty State */
.empty-state {
text-align: center;
padding: 60px 24px;
color: var(--text-muted);
}
.empty-state svg {
margin: 0 auto 16px;
opacity: 0.3;
}
.empty-state h3 {
color: var(--text-secondary);
margin-bottom: 8px;
}
.empty-state p {
font-size: 13px;
max-width: 360px;
margin: 0 auto;
}
/* Settings */
.settings-section {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 20px;
}
.settings-section h3 {
font-size: 16px;
margin-bottom: 16px;
}
/* Assign Modal */
.assign-content-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 12px;
max-height: 400px;
overflow-y: auto;
}
.assign-content-item {
background: var(--bg-input);
border: 2px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
cursor: pointer;
transition: all var(--transition);
}
.assign-content-item:hover {
border-color: var(--accent);
}
.assign-content-item.selected {
border-color: var(--accent);
box-shadow: 0 0 0 1px var(--accent);
}
.assign-content-item img {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
}
.assign-content-item-name {
padding: 6px 8px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--border-light);
}
/* Upload progress */
.upload-progress {
margin-top: 16px;
}
.upload-progress-bar {
height: 4px;
background: var(--bg-primary);
border-radius: 2px;
overflow: hidden;
}
.upload-progress-fill {
height: 100%;
background: var(--accent);
border-radius: 2px;
transition: width 0.3s ease;
}
/* Help tooltips */
.help-tip {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--bg-card);
border: 1px solid var(--border);
color: var(--text-muted);
font-size: 11px;
font-weight: 600;
cursor: help;
position: relative;
margin-left: 6px;
flex-shrink: 0;
}
.help-tip:hover::after {
content: attr(data-tip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 8px 12px;
font-size: 12px;
font-weight: 400;
color: var(--text-secondary);
white-space: normal;
width: 250px;
z-index: 1000;
box-shadow: var(--shadow);
margin-bottom: 6px;
line-height: 1.4;
}
/* Table wrapper: enables horizontal scroll when table min-width exceeds viewport */
.table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Mobile hamburger toggle */
.mobile-menu-btn {
display: none;
position: fixed;
top: 12px;
left: 12px;
z-index: 200;
width: 44px;
height: 44px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius);
align-items: center;
justify-content: center;
color: var(--text-primary);
cursor: pointer;
}
/* Responsive */
@media (max-width: 768px) {
.mobile-menu-btn { display: flex; }
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s ease;
z-index: 150;
}
.sidebar.open {
transform: translateX(0);
}
.sidebar-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 140;
}
.sidebar-backdrop.open { display: block; }
.nav-link { min-height: 44px; padding: 10px 14px; }
.content { margin-left: 0; padding: 16px; padding-top: 68px; }
.page-header { flex-direction: column; gap: 12px; align-items: flex-start; }
.device-grid { grid-template-columns: 1fr; }
.content-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.info-grid { grid-template-columns: 1fr; }
.remote-container { flex-direction: column; }
.remote-controls { width: 100%; flex-direction: row; flex-wrap: wrap; }
.modal { width: 95vw; max-height: 90vh; overflow-y: auto; }
.tabs {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
mask-image: linear-gradient(to right, black calc(100% - 24px), transparent 100%);
-webkit-mask-image: linear-gradient(to right, black calc(100% - 24px), transparent 100%);
}
.tab { white-space: nowrap; flex-shrink: 0; }
.playlist-item { flex-wrap: wrap; }
/* Dashboard stats stack to single column */
.dash-stats-row { flex-direction: column; }
.dash-stats-row .info-card { flex: none; }
/* Content-library 3-up toolbar stacks vertically */
.content-toolbar { flex-direction: column; }
.content-toolbar > div[style*="width:320px"] { width: auto !important; }
/* Schedule controls: allow wrap and widen select to full row */
.schedule-controls { gap: 8px; }
.schedule-controls > select { flex: 1 1 100%; }
.schedule-controls > button,
.schedule-controls > span { flex: 0 1 auto; }
/* Tap targets: minimum 44px height for interactive elements */
.btn { min-height: 44px; padding: 10px 16px; }
.btn-sm { min-height: 36px; padding: 8px 12px; }
.btn-icon { min-width: 40px; min-height: 40px; }
/* Form inputs: 16px font to prevent iOS focus zoom; 44px tap target */
.input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="url"],
input[type="search"],
input[type="tel"],
select,
textarea {
font-size: 16px;
min-height: 44px;
}
.pairing-input { font-size: 24px; letter-spacing: 6px; }
/* Modals: adjust padding at 95vw so content doesn't touch edges */
.modal-header,
.modal-footer { padding: 14px 16px; }
.modal-body { padding: 16px; }
/* Toast container: full-width bar instead of 280px fixed to right */
.toast-container {
left: 12px;
right: 12px;
bottom: 12px;
}
.toast { min-width: 0; width: 100%; }
}
@media (max-width: 480px) {
.content-grid { grid-template-columns: 1fr; }
.assign-content-grid { grid-template-columns: 1fr 1fr; }
}