Spaces:
Paused
Paused
Download static/style.css from Cnass/sprite: direct link, hf CLI and curl.
- Browser
- Download file 27.8 kB
-
https://huggingface.co/spaces/Cnass/sprite/resolve/main/static/style.css
- Command line
-
hf download hf://spaces/Cnass/sprite/static/style.css
-
curl -L -o style.css https://huggingface.co/spaces/Cnass/sprite/resolve/main/static/style.css
27.8 kB
| /* ========================================================================== | |
| Sprite Forge — Pixel Art Sprite Dataset Builder | |
| Design: dark editor workspace (Aseprite/annotation-tool register), | |
| phosphor-green confidence accent, monospace data + Inter UI chrome. | |
| ========================================================================== */ | |
| @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap'); | |
| :root { | |
| --canvas: #15171c; | |
| --panel: #1d2027; | |
| --panel-raised: #262a33; | |
| --panel-raised-2: #2e333e; | |
| --line: #363b47; | |
| --line-soft: #2a2e38; | |
| --ink: #e8e9ed; | |
| --ink-dim: #8b90a0; | |
| --ink-faint: #5a5f6e; | |
| --accent: #7fe08b; | |
| --accent-dim: #4d9e59; | |
| --accent-wash: rgba(127, 224, 139, 0.12); | |
| --warn: #f2b84b; | |
| --warn-wash: rgba(242, 184, 75, 0.14); | |
| --danger: #e8607a; | |
| --danger-wash: rgba(232, 96, 122, 0.14); | |
| --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; | |
| --font-ui: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| --radius-sm: 3px; | |
| --radius-md: 5px; | |
| --topbar-h: 52px; | |
| --statusbar-h: 44px; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| html, body { | |
| height: 100%; | |
| margin: 0; | |
| } | |
| body { | |
| background: var(--canvas); | |
| color: var(--ink); | |
| font-family: var(--font-ui); | |
| font-size: 13px; | |
| line-height: 1.5; | |
| overflow: hidden; | |
| } | |
| button { | |
| font-family: inherit; | |
| } | |
| ::selection { | |
| background: var(--accent-wash); | |
| color: var(--ink); | |
| } | |
| /* Scrollbars: thin, editor-style */ | |
| ::-webkit-scrollbar { width: 10px; height: 10px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--panel-raised-2); | |
| border-radius: 6px; | |
| border: 2px solid var(--panel); | |
| } | |
| ::-webkit-scrollbar-thumb:hover { background: var(--line); } | |
| /* ========================================================================== | |
| App shell | |
| ========================================================================== */ | |
| #app { | |
| display: grid; | |
| grid-template-rows: var(--topbar-h) 1fr var(--statusbar-h); | |
| height: 100vh; | |
| } | |
| /* ---- topbar ---- */ | |
| .topbar { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| padding: 0 16px; | |
| background: var(--panel); | |
| border-bottom: 1px solid var(--line); | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-family: var(--font-mono); | |
| font-weight: 700; | |
| font-size: 14px; | |
| letter-spacing: 0.02em; | |
| color: var(--ink); | |
| white-space: nowrap; | |
| } | |
| .brand-mark { | |
| display: inline-grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| grid-template-rows: repeat(4, 1fr); | |
| width: 18px; | |
| height: 18px; | |
| gap: 1px; | |
| } | |
| .brand-mark span { | |
| background: var(--line); | |
| } | |
| .brand-mark span:nth-child(2), | |
| .brand-mark span:nth-child(5), | |
| .brand-mark span:nth-child(6), | |
| .brand-mark span:nth-child(9), | |
| .brand-mark span:nth-child(11), | |
| .brand-mark span:nth-child(15) { | |
| background: var(--accent); | |
| } | |
| .topbar-actions { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .topbar-spacer { flex: 1; } | |
| .topbar-stat { | |
| font-family: var(--font-mono); | |
| font-size: 12px; | |
| color: var(--ink-dim); | |
| white-space: nowrap; | |
| } | |
| .topbar-stat b { | |
| color: var(--ink); | |
| font-weight: 600; | |
| } | |
| /* ---- buttons ---- */ | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 7px 12px; | |
| background: var(--panel-raised); | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-sm); | |
| color: var(--ink); | |
| font-size: 12.5px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: background-color 120ms ease, border-color 120ms ease; | |
| white-space: nowrap; | |
| } | |
| .btn:hover { background: var(--panel-raised-2); border-color: var(--ink-faint); } | |
| .btn:active { transform: translateY(1px); } | |
| .btn:disabled { opacity: 0.4; cursor: not-allowed; } | |
| .btn:disabled:hover { background: var(--panel-raised); border-color: var(--line); } | |
| .btn-primary { | |
| background: var(--accent); | |
| border-color: var(--accent); | |
| color: #0d1f10; | |
| font-weight: 600; | |
| } | |
| .btn-primary:hover { background: #93e79d; border-color: #93e79d; } | |
| .btn-danger { | |
| color: var(--danger); | |
| border-color: var(--danger); | |
| background: transparent; | |
| } | |
| .btn-danger:hover { background: var(--danger-wash); } | |
| .btn-ghost { | |
| background: transparent; | |
| border-color: transparent; | |
| color: var(--ink-dim); | |
| } | |
| .btn-ghost:hover { background: var(--panel-raised); color: var(--ink); } | |
| .btn-sm { padding: 4px 9px; font-size: 11.5px; } | |
| .btn-block { width: 100%; justify-content: center; } | |
| /* ========================================================================== | |
| Workspace: three-pane layout | |
| ========================================================================== */ | |
| .workspace { | |
| display: grid; | |
| grid-template-columns: 240px 1fr 320px; | |
| overflow: hidden; | |
| } | |
| .pane { | |
| overflow-y: auto; | |
| min-height: 0; | |
| } | |
| .pane-left { | |
| background: var(--panel); | |
| border-right: 1px solid var(--line); | |
| } | |
| .pane-center { | |
| background: var(--canvas); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| .pane-right { | |
| background: var(--panel); | |
| border-left: 1px solid var(--line); | |
| } | |
| .pane-section { | |
| padding: 14px; | |
| border-bottom: 1px solid var(--line-soft); | |
| } | |
| .pane-label { | |
| font-family: var(--font-mono); | |
| font-size: 10.5px; | |
| font-weight: 600; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| color: var(--ink-faint); | |
| margin: 0 0 10px 0; | |
| } | |
| /* ---- sheets list (left pane) ---- */ | |
| .sheet-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| cursor: pointer; | |
| color: var(--ink-dim); | |
| font-size: 12.5px; | |
| } | |
| .sheet-item:hover { background: var(--panel-raised); color: var(--ink); } | |
| .sheet-item.active { | |
| background: var(--accent-wash); | |
| color: var(--ink); | |
| box-shadow: inset 2px 0 0 var(--accent); | |
| } | |
| .sheet-item-name { | |
| flex: 1; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| font-family: var(--font-mono); | |
| } | |
| .sheet-item-count { | |
| font-family: var(--font-mono); | |
| font-size: 11px; | |
| color: var(--ink-faint); | |
| } | |
| .empty-hint { | |
| color: var(--ink-faint); | |
| font-size: 12px; | |
| padding: 6px 10px 2px; | |
| line-height: 1.6; | |
| } | |
| /* ---- upload dropzone ---- */ | |
| .dropzone { | |
| border: 1.5px dashed var(--line); | |
| border-radius: var(--radius-md); | |
| padding: 18px 10px; | |
| text-align: center; | |
| color: var(--ink-faint); | |
| font-size: 11.5px; | |
| cursor: pointer; | |
| transition: border-color 120ms ease, background-color 120ms ease; | |
| } | |
| .dropzone:hover, .dropzone.drag-over { | |
| border-color: var(--accent-dim); | |
| background: var(--accent-wash); | |
| color: var(--ink-dim); | |
| } | |
| .dropzone-icon { | |
| font-family: var(--font-mono); | |
| font-size: 18px; | |
| color: var(--ink-faint); | |
| margin-bottom: 6px; | |
| } | |
| .padding-row { | |
| display: flex; | |
| gap: 6px; | |
| margin-top: 10px; | |
| } | |
| .padding-chip { | |
| flex: 1; | |
| text-align: center; | |
| padding: 5px 0; | |
| font-family: var(--font-mono); | |
| font-size: 11px; | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-sm); | |
| color: var(--ink-dim); | |
| cursor: pointer; | |
| background: transparent; | |
| } | |
| .padding-chip:hover { border-color: var(--ink-faint); } | |
| .padding-chip.active { | |
| border-color: var(--accent); | |
| background: var(--accent-wash); | |
| color: var(--accent); | |
| } | |
| /* ========================================================================== | |
| Center canvas: sheet + bounding box overlay | |
| ========================================================================== */ | |
| .canvas-toolbar { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 8px 14px; | |
| border-bottom: 1px solid var(--line); | |
| background: var(--panel); | |
| } | |
| .canvas-toolbar-title { | |
| font-family: var(--font-mono); | |
| font-size: 12.5px; | |
| color: var(--ink); | |
| font-weight: 500; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| .canvas-toolbar-spacer { flex: 1; } | |
| .zoom-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| font-family: var(--font-mono); | |
| font-size: 11.5px; | |
| color: var(--ink-dim); | |
| } | |
| .canvas-scroll { | |
| flex: 1; | |
| overflow: auto; | |
| position: relative; | |
| /* checkerboard = the actual transparency convention every image editor | |
| uses, doubles as the canvas's textural identity */ | |
| background-color: var(--canvas); | |
| background-image: | |
| linear-gradient(45deg, #1b1e24 25%, transparent 25%), | |
| linear-gradient(-45deg, #1b1e24 25%, transparent 25%), | |
| linear-gradient(45deg, transparent 75%, #1b1e24 75%), | |
| linear-gradient(-45deg, transparent 75%, #1b1e24 75%); | |
| background-size: 20px 20px; | |
| background-position: 0 0, 0 10px, 10px -10px, -10px 0px; | |
| display: flex; | |
| align-items: flex-start; | |
| justify-content: flex-start; | |
| padding: 40px; | |
| } | |
| .sheet-stage { | |
| position: relative; | |
| flex-shrink: 0; | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| box-shadow: 0 0 0 1px var(--line); | |
| } | |
| .sheet-stage img { | |
| display: block; | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| } | |
| /* ---- bounding box overlay: the signature element ---- */ | |
| .bbox { | |
| position: absolute; | |
| border: 1px solid var(--bbox-color, var(--accent)); | |
| cursor: pointer; | |
| transition: box-shadow 100ms ease; | |
| } | |
| .bbox:hover { | |
| box-shadow: 0 0 0 1px var(--bbox-color, var(--accent)), 0 0 8px 0 var(--bbox-color, var(--accent)); | |
| z-index: 5; | |
| } | |
| @media (max-width: 880px) { | |
| /* Small sprites (a handful of source pixels, scaled down further to | |
| fit a phone screen) can end up as a 4-6px tappable box -- well under | |
| any usable touch target. An invisible ::before hit-area extends the | |
| actual tap region without changing the visible box size or shifting | |
| layout (position:absolute + inset avoids affecting sibling sprites' | |
| boxes, since each bbox is independently absolutely positioned). */ | |
| .bbox::before { | |
| content: ''; | |
| position: absolute; | |
| inset: -6px; | |
| } | |
| } | |
| .bbox-tag { | |
| position: absolute; | |
| top: -1px; | |
| left: -1px; | |
| transform: translateY(-100%); | |
| background: var(--bbox-color, var(--accent)); | |
| color: #0d1f10; | |
| font-family: var(--font-mono); | |
| font-size: 9px; | |
| font-weight: 700; | |
| line-height: 1; | |
| padding: 2px 3px; | |
| white-space: nowrap; | |
| pointer-events: none; | |
| } | |
| /* Reviewed sprites stay identifiable at a glance: accepted gets a solid | |
| filled tag + checkmark, rejected gets a dimmed dashed border. Relying on | |
| opacity alone was too subtle against small, already-dark sprites. */ | |
| .bbox.status-accepted { | |
| border-style: solid; | |
| box-shadow: 0 0 0 1px var(--accent); | |
| } | |
| .bbox.status-accepted .bbox-tag { | |
| background: var(--accent); | |
| color: #0d1f10; | |
| } | |
| .bbox.status-accepted .bbox-tag::after { | |
| content: ' ✓'; | |
| } | |
| .bbox.status-rejected { | |
| border-style: dashed; | |
| opacity: 0.4; | |
| } | |
| .bbox.status-rejected .bbox-tag { | |
| background: var(--panel-raised-2); | |
| color: var(--ink-faint); | |
| text-decoration: line-through; | |
| } | |
| .bbox.selected { | |
| border-color: var(--ink); | |
| animation: marching-ants 550ms linear infinite; | |
| z-index: 6; | |
| } | |
| .bbox.selected::after { | |
| content: ''; | |
| position: absolute; | |
| inset: -2px; | |
| border: 1px dashed var(--ink); | |
| animation: marching-ants 550ms linear infinite; | |
| } | |
| @keyframes marching-ants { | |
| to { background-position: 8px 0; } | |
| } | |
| .bbox.selected { | |
| background-image: repeating-linear-gradient( | |
| 90deg, var(--ink) 0 4px, transparent 4px 8px | |
| ); | |
| background-size: 8px 1px; | |
| background-repeat: repeat-x; | |
| background-position: 0 0; | |
| border: none; | |
| outline: 1px solid var(--ink); | |
| } | |
| .bbox.selected::after { display: none; } | |
| @media (prefers-reduced-motion: reduce) { | |
| .bbox.selected { animation: none; } | |
| } | |
| .canvas-empty { | |
| margin: auto; | |
| text-align: center; | |
| color: var(--ink-faint); | |
| font-size: 13px; | |
| max-width: 320px; | |
| } | |
| .canvas-empty-mark { | |
| font-family: var(--font-mono); | |
| font-size: 28px; | |
| color: var(--line); | |
| margin-bottom: 12px; | |
| } | |
| /* ========================================================================== | |
| Right pane: inspector | |
| ========================================================================== */ | |
| .inspector-empty { | |
| padding: 40px 20px; | |
| text-align: center; | |
| color: var(--ink-faint); | |
| font-size: 12.5px; | |
| line-height: 1.7; | |
| } | |
| .preview-frame { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background-color: var(--canvas); | |
| background-image: | |
| linear-gradient(45deg, #1b1e24 25%, transparent 25%), | |
| linear-gradient(-45deg, #1b1e24 25%, transparent 25%), | |
| linear-gradient(45deg, transparent 75%, #1b1e24 75%), | |
| linear-gradient(-45deg, transparent 75%, #1b1e24 75%); | |
| background-size: 16px 16px; | |
| background-position: 0 0, 0 8px, 8px -8px, -8px 0px; | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-md); | |
| height: 200px; | |
| overflow: hidden; | |
| } | |
| .preview-frame img { | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| max-width: 100%; | |
| max-height: 100%; | |
| } | |
| .sprite-id { | |
| font-family: var(--font-mono); | |
| font-size: 11px; | |
| color: var(--ink-faint); | |
| margin-top: 8px; | |
| word-break: break-all; | |
| } | |
| .dim-row { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: baseline; | |
| margin-top: 10px; | |
| font-family: var(--font-mono); | |
| } | |
| .dim-row .dims { font-size: 15px; font-weight: 600; color: var(--ink); } | |
| .dim-row .source { font-size: 10.5px; color: var(--ink-faint); } | |
| .confidence-banner { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-top: 12px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| font-size: 11.5px; | |
| border: 1px solid var(--line); | |
| } | |
| .confidence-banner.high { border-color: var(--accent-dim); background: var(--accent-wash); color: var(--accent); } | |
| .confidence-banner.mid { border-color: #b88a2c; background: var(--warn-wash); color: var(--warn); } | |
| .confidence-banner.low { border-color: #b8425a; background: var(--danger-wash); color: var(--danger); } | |
| .confidence-banner .conf-value { font-family: var(--font-mono); font-weight: 700; } | |
| .confidence-reasons { | |
| margin: 6px 0 0 0; | |
| padding-left: 16px; | |
| font-size: 11px; | |
| color: var(--ink-dim); | |
| } | |
| .score-grid { | |
| display: grid; | |
| grid-template-columns: 1fr auto; | |
| gap: 6px 10px; | |
| margin-top: 10px; | |
| font-size: 11.5px; | |
| } | |
| .score-grid .k { color: var(--ink-dim); } | |
| .score-grid .v { font-family: var(--font-mono); color: var(--ink); text-align: right; } | |
| .score-bar { | |
| grid-column: 1 / -1; | |
| height: 3px; | |
| background: var(--line-soft); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| margin: -3px 0 4px; | |
| } | |
| .score-bar-fill { height: 100%; background: var(--accent); } | |
| .field-group { margin-top: 14px; } | |
| .field-label { | |
| display: block; | |
| font-size: 11px; | |
| color: var(--ink-dim); | |
| margin-bottom: 5px; | |
| } | |
| .field-select, .field-input { | |
| width: 100%; | |
| padding: 7px 9px; | |
| background: var(--panel-raised); | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-sm); | |
| color: var(--ink); | |
| font-family: var(--font-ui); | |
| font-size: 12.5px; | |
| } | |
| .field-select:focus, .field-input:focus { | |
| outline: none; | |
| border-color: var(--accent-dim); | |
| } | |
| .tag-list { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 5px; | |
| margin-top: 6px; | |
| } | |
| .tag-chip { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 4px; | |
| padding: 3px 7px; | |
| background: var(--panel-raised-2); | |
| border-radius: var(--radius-sm); | |
| font-family: var(--font-mono); | |
| font-size: 10.5px; | |
| color: var(--ink-dim); | |
| } | |
| .tag-chip button { | |
| background: none; | |
| border: none; | |
| color: var(--ink-faint); | |
| cursor: pointer; | |
| font-size: 11px; | |
| padding: 0; | |
| line-height: 1; | |
| } | |
| .tag-chip button:hover { color: var(--danger); } | |
| .dup-banner { | |
| margin-top: 12px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| border: 1px solid #7a5230; | |
| background: var(--warn-wash); | |
| font-size: 11.5px; | |
| color: var(--warn); | |
| } | |
| .dup-banner b { font-family: var(--font-mono); } | |
| .bg-toggle { | |
| display: flex; | |
| gap: 6px; | |
| margin-top: 10px; | |
| } | |
| .bg-toggle .padding-chip { flex: 1; } | |
| .action-row { | |
| display: flex; | |
| gap: 8px; | |
| margin-top: 16px; | |
| } | |
| .action-row .btn { flex: 1; justify-content: center; } | |
| .edit-row { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 6px; | |
| margin-top: 10px; | |
| } | |
| .edit-row label { | |
| display: block; | |
| font-size: 9.5px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.04em; | |
| color: var(--ink-faint); | |
| margin-bottom: 3px; | |
| } | |
| .edit-row input { | |
| width: 100%; | |
| padding: 5px 6px; | |
| background: var(--panel-raised); | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-sm); | |
| color: var(--ink); | |
| font-family: var(--font-mono); | |
| font-size: 11.5px; | |
| } | |
| /* ========================================================================== | |
| Status bar | |
| ========================================================================== */ | |
| .statusbar { | |
| display: flex; | |
| align-items: center; | |
| gap: 18px; | |
| padding: 0 16px; | |
| background: var(--panel); | |
| border-top: 1px solid var(--line); | |
| font-family: var(--font-mono); | |
| font-size: 11.5px; | |
| } | |
| .stat { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| color: var(--ink-dim); | |
| } | |
| .stat-dot { | |
| width: 7px; | |
| height: 7px; | |
| border-radius: 1px; | |
| } | |
| .stat-dot.accepted { background: var(--accent); } | |
| .stat-dot.rejected { background: var(--danger); } | |
| .stat-dot.pending { background: var(--ink-faint); } | |
| .stat-dot.duplicates { background: var(--warn); } | |
| .stat b { color: var(--ink); font-weight: 600; } | |
| .statusbar-spacer { flex: 1; } | |
| .batch-controls { | |
| display: flex; | |
| gap: 6px; | |
| } | |
| /* ========================================================================== | |
| Toasts | |
| ========================================================================== */ | |
| #toast-stack { | |
| position: fixed; | |
| bottom: calc(var(--statusbar-h) + 14px); | |
| right: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| z-index: 100; | |
| } | |
| .toast { | |
| padding: 10px 14px; | |
| background: var(--panel-raised); | |
| border: 1px solid var(--line); | |
| border-left: 3px solid var(--accent); | |
| border-radius: var(--radius-sm); | |
| font-size: 12px; | |
| color: var(--ink); | |
| box-shadow: 0 4px 16px rgba(0,0,0,0.35); | |
| max-width: 320px; | |
| animation: toast-in 180ms ease; | |
| } | |
| .toast.error { border-left-color: var(--danger); } | |
| @keyframes toast-in { | |
| from { opacity: 0; transform: translateY(6px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* ========================================================================== | |
| Modal (merge/split) | |
| ========================================================================== */ | |
| .modal-backdrop { | |
| position: fixed; | |
| inset: 0; | |
| background: rgba(10, 11, 14, 0.7); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 200; | |
| } | |
| .modal { | |
| width: 420px; | |
| max-width: calc(100vw - 40px); | |
| background: var(--panel); | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-md); | |
| padding: 18px; | |
| box-shadow: 0 12px 40px rgba(0,0,0,0.5); | |
| } | |
| .modal-title { | |
| font-family: var(--font-mono); | |
| font-size: 13px; | |
| font-weight: 600; | |
| margin: 0 0 6px; | |
| } | |
| .modal-desc { | |
| font-size: 12px; | |
| color: var(--ink-dim); | |
| margin: 0 0 14px; | |
| line-height: 1.6; | |
| } | |
| .modal-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| gap: 8px; | |
| margin-top: 16px; | |
| } | |
| /* ========================================================================== | |
| Responsive: mobile layout below ~880px | |
| ========================================================================== | |
| Desktop uses a fixed 100vh grid where each pane scrolls independently | |
| (an editor-window model). That model breaks down on a phone: three | |
| panes stacked vertically inside a fixed viewport height leaves each | |
| one too short to use. Below the breakpoint we switch to natural page | |
| scroll instead -- the whole app becomes one tall scrollable column, | |
| sections stack in the order you actually use them (upload → canvas → | |
| inspector), and only the sprite-sheet canvas keeps its own internal | |
| scroll (it needs horizontal pan/zoom regardless of viewport). */ | |
| @media (max-width: 880px) { | |
| html, body { | |
| height: auto; | |
| } | |
| body { | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| /* bottom padding is set later in this block once the fixed | |
| statusbar's mobile height (two rows) is known */ | |
| } | |
| #app { | |
| height: auto; | |
| min-height: 100vh; | |
| display: block; | |
| } | |
| .topbar { | |
| height: auto; | |
| min-height: var(--topbar-h); | |
| flex-wrap: wrap; | |
| row-gap: 6px; | |
| padding: 8px 12px; | |
| position: sticky; | |
| top: 0; | |
| z-index: 20; | |
| } | |
| .topbar-stat { display: none; } | |
| .topbar-actions { gap: 6px; } | |
| .topbar-actions .btn { | |
| padding: 9px 12px; /* keep tap targets >= ~40px tall */ | |
| font-size: 12px; | |
| } | |
| .workspace { | |
| display: block; | |
| overflow: visible; | |
| } | |
| .pane { overflow: visible; } | |
| .pane-left { | |
| border-right: none; | |
| border-bottom: 1px solid var(--line); | |
| } | |
| .pane-left .pane-section { padding: 12px; } | |
| /* Sheets list: horizontal chip scroller instead of a tall vertical | |
| list, so it doesn't push the canvas below the fold on a phone. */ | |
| #sheet-list { | |
| display: flex; | |
| gap: 8px; | |
| overflow-x: auto; | |
| -webkit-overflow-scrolling: touch; | |
| padding-bottom: 4px; | |
| } | |
| #sheet-list .empty-hint { white-space: normal; min-width: 220px; } | |
| .sheet-item { | |
| flex: 0 0 auto; | |
| min-width: 120px; | |
| } | |
| .dropzone { padding: 14px 10px; } | |
| .pane-center { | |
| overflow: visible; | |
| } | |
| .canvas-toolbar { | |
| position: sticky; | |
| top: var(--topbar-h); | |
| z-index: 15; | |
| flex-wrap: wrap; | |
| row-gap: 6px; | |
| } | |
| .canvas-toolbar-title { font-size: 11.5px; } | |
| .zoom-controls .btn { padding: 8px 10px; min-height: 40px; } | |
| /* The canvas itself: allow real horizontal + vertical panning with a | |
| thumb, and let the browser's native pinch-zoom work on it (desktop | |
| disables that nowhere, but touch-action defaults can interfere with | |
| drag-scroll -- pan-x pan-y keeps both working together). */ | |
| .canvas-scroll { | |
| height: 62vh; | |
| min-height: 320px; | |
| max-height: 520px; | |
| padding: 16px; | |
| touch-action: pan-x pan-y pinch-zoom; | |
| } | |
| .canvas-empty { font-size: 12.5px; padding: 0 8px; } | |
| .pane-right { | |
| border-left: none; | |
| border-top: 1px solid var(--line); | |
| } | |
| .inspector-empty { padding: 24px 20px; } | |
| .preview-frame { height: 160px; } | |
| .score-grid { font-size: 11px; } | |
| .action-row .btn { padding: 11px 10px; } /* bigger tap target */ | |
| /* Baseline tap-target floor: anything clickable gets at least ~40px of | |
| height on touch, even where desktop uses a tighter, mouse-precision | |
| padding (padding-chips, sm buttons, tag remove buttons, etc). */ | |
| .btn, .padding-chip, .sheet-item, select, input[type="number"], input[type="text"] { | |
| min-height: 40px; | |
| } | |
| .btn-sm { min-height: 38px; } | |
| .tag-chip button { min-width: 24px; min-height: 24px; } | |
| .field-select, .field-input { padding: 10px 9px; } | |
| /* Status bar: at phone widths the counts + batch actions no longer | |
| fit on one row. Instead of overflowing off-screen, the bar becomes | |
| two stacked rows -- counts on top (always visible, compact), batch | |
| action buttons in a horizontally-scrollable strip below -- and | |
| stays pinned to the viewport bottom like on desktop, since it's the | |
| primary place review actions live. */ | |
| .statusbar { | |
| position: fixed; | |
| left: 0; right: 0; bottom: 0; | |
| height: auto; | |
| flex-wrap: wrap; | |
| row-gap: 8px; | |
| padding: 10px 12px; | |
| z-index: 25; | |
| } | |
| .statusbar .stat { flex: 0 0 auto; font-size: 11px; } | |
| .statusbar-spacer { display: none; flex-basis: 100%; height: 0; } | |
| .batch-controls { | |
| flex: 0 0 100%; | |
| overflow-x: auto; | |
| -webkit-overflow-scrolling: touch; | |
| padding-bottom: 2px; | |
| } | |
| .batch-controls .btn { | |
| flex: 0 0 auto; | |
| padding: 10px 13px; | |
| font-size: 11.5px; | |
| } | |
| body { padding-bottom: 92px; } /* clears the now-taller fixed statusbar */ | |
| .modal { width: calc(100vw - 32px); } | |
| .edit-row { grid-template-columns: repeat(2, 1fr); row-gap: 8px; } | |
| #toast-stack { | |
| left: 12px; | |
| right: 12px; | |
| bottom: 100px; | |
| } | |
| .toast { max-width: none; } | |
| } | |
| @media (max-width: 460px) { | |
| .brand { font-size: 12.5px; } | |
| .brand-mark { width: 15px; height: 15px; } | |
| .topbar-actions .btn span.btn-label-full { display: none; } | |
| } | |
| /* Focus visibility (kept intentional, not suppressed) */ | |
| a:focus-visible, button:focus-visible, [tabindex]:focus-visible, | |
| input:focus-visible, select:focus-visible { | |
| outline: 2px solid var(--accent); | |
| outline-offset: 2px; | |
| } | |
| /* ========================================================================== | |
| Auto-labelling notice | |
| ========================================================================== */ | |
| .auto-tag-banner { | |
| margin-top: 12px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| border: 1px solid #3c5470; | |
| background: rgba(96, 152, 232, 0.12); | |
| font-size: 11.5px; | |
| color: #8fb4e8; | |
| line-height: 1.55; | |
| } | |
| .auto-tag-banner b { font-family: var(--font-mono); } | |
| /* ========================================================================== | |
| Bulk assign panel | |
| ========================================================================== */ | |
| .field-hint { | |
| color: var(--ink-faint); | |
| font-weight: 400; | |
| } | |
| .checkbox-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| margin-top: 7px; | |
| font-size: 11.5px; | |
| color: var(--ink-dim); | |
| cursor: pointer; | |
| } | |
| .checkbox-row input { accent-color: var(--accent-dim); } | |
| .mono { font-family: var(--font-mono); } | |
| /* ========================================================================== | |
| Animation preview | |
| ========================================================================== */ | |
| .modal-wide { | |
| width: 560px; | |
| } | |
| .modal-scroll { | |
| max-height: calc(100vh - 60px); | |
| overflow-y: auto; | |
| } | |
| .anim-stage { | |
| position: relative; | |
| margin: 14px auto 4px; | |
| background: | |
| repeating-conic-gradient(#22252c 0% 25%, #1a1d23 0% 50%) 50% / 16px 16px; | |
| border: 1px solid var(--line); | |
| border-radius: var(--radius-sm); | |
| overflow: hidden; | |
| } | |
| .anim-frame { | |
| position: absolute; | |
| display: none; | |
| image-rendering: pixelated; | |
| } | |
| .anim-frame.visible { display: block; } | |
| .anim-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-top: 12px; | |
| flex-wrap: wrap; | |
| } | |
| .anim-counter { | |
| font-family: var(--font-mono); | |
| font-size: 12px; | |
| color: var(--ink-dim); | |
| min-width: 62px; | |
| text-align: center; | |
| } | |
| .anim-fps { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-left: auto; | |
| font-family: var(--font-mono); | |
| font-size: 11.5px; | |
| color: var(--ink-dim); | |
| } | |
| .anim-fps input { width: 96px; accent-color: var(--accent-dim); } | |
| /* ========================================================================== | |
| Dataset report | |
| ========================================================================== */ | |
| .report-warnings { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 7px; | |
| margin: 12px 0 4px; | |
| } | |
| .report-warning { | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| border: 1px solid #7a5230; | |
| background: var(--warn-wash); | |
| color: var(--warn); | |
| font-size: 11.5px; | |
| line-height: 1.55; | |
| } | |
| .report-ok { | |
| margin: 12px 0 4px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| border: 1px solid var(--accent-dim); | |
| background: var(--accent-wash); | |
| color: var(--accent); | |
| font-size: 11.5px; | |
| } | |
| .report-note { | |
| margin-top: 6px; | |
| padding: 8px 10px; | |
| border-radius: var(--radius-sm); | |
| background: var(--panel-raised); | |
| border: 1px solid var(--line-soft); | |
| color: var(--ink-dim); | |
| font-size: 11.5px; | |
| line-height: 1.6; | |
| } | |
| .dist-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| margin-top: 6px; | |
| } | |
| .dist-row { | |
| display: grid; | |
| grid-template-columns: 130px 1fr 78px; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 11.5px; | |
| } | |
| .dist-name { | |
| font-family: var(--font-mono); | |
| color: var(--ink); | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| .dist-bar { | |
| height: 7px; | |
| background: var(--panel-raised); | |
| border-radius: 99px; | |
| overflow: hidden; | |
| } | |
| .dist-bar-fill { | |
| display: block; | |
| height: 100%; | |
| background: var(--accent-dim); | |
| } | |
| .dist-count { | |
| font-family: var(--font-mono); | |
| color: var(--ink-dim); | |
| text-align: right; | |
| } | |
| @media (max-width: 880px) { | |
| .dist-row { grid-template-columns: 96px 1fr 66px; } | |
| .modal-wide { width: auto; } | |
| } | |