443 lines
18 KiB
CSS
443 lines
18 KiB
CSS
/* Chimera design system — corrections layer.
|
|
Loaded last on purpose: several component stylesheets are linked after
|
|
stitch.css, so token-level fixes have to sit downstream of all of them.
|
|
Tokens still live in stitch.css; this file only resolves conflicts. */
|
|
/* ============================================================================
|
|
Design-system corrections — 2026-08-23
|
|
One authoritative block. stitch.css owns the tokens; base.css owns the reset.
|
|
Ordered: type scale, elevation, surfaces, controls, forms, edges.
|
|
========================================================================== */
|
|
|
|
/* --- Type: one scale, sentence case above the label sizes ----------------- */
|
|
#chimera-route-title{
|
|
font-family:var(--display);
|
|
font-size:var(--fs-display);
|
|
font-weight:640;
|
|
letter-spacing:-.03em;
|
|
line-height:1.04;
|
|
text-transform:none;
|
|
text-wrap:balance;
|
|
}
|
|
.stat-num{font-family:var(--display);font-size:var(--fs-xl);font-weight:600;line-height:1;font-variant-numeric:tabular-nums}
|
|
.stat-lbl{font-size:var(--fs-xs)}
|
|
.page-header h1{font-family:var(--display);font-size:var(--fs-xl);font-weight:620;letter-spacing:-.025em;text-transform:none}
|
|
h2{font-size:var(--fs-lg)}
|
|
h3,h4{font-size:var(--fs-md)}
|
|
|
|
.game-card .s-tag{font-size:var(--fs-micro)}
|
|
#chimera-route-breadcrumb{font-size:var(--fs-micro);letter-spacing:.14em}
|
|
|
|
/* --- Elevation: three levels, nothing invented per component -------------- */
|
|
.card,.stat-card,.table-wrap,.chimera-home-panel{box-shadow:var(--shadow-panel)}
|
|
.modal,.modal-shell,.import-wizard,#toast-container .toast,.chimera-command-palette{box-shadow:var(--shadow-float)}
|
|
.btn,.chimera-toolbar-button,.chimera-route-link,.fbtn,.badge,.pill{box-shadow:var(--shadow-flat)}
|
|
|
|
/* --- Surfaces: inherit the radius scale, stop hand-rolling values --------- */
|
|
.card,.stat-card,.table-wrap,.backup-card,.progress-area,.chimera-home-panel,.catalog-health-strip{border-radius:var(--radius)}
|
|
.btn,.fbtn,input,select,textarea,.filter-select,.search-input,
|
|
.chimera-domain-button,.modal-workspace-links button{border-radius:var(--radius-sm)}
|
|
.modal,.modal-shell,.import-wizard{border-radius:var(--radius-lg)}
|
|
.badge,.pill,.s-tag,.bk-badge{border-radius:999px}
|
|
|
|
/* --- Controls: one filled button per view --------------------------------- */
|
|
/* Teal was acting as a second primary. It becomes an outlined action and the
|
|
accent fill stays the single call to action. */
|
|
.btn-teal{
|
|
background:transparent;
|
|
color:var(--teal);
|
|
border-color:rgba(34,199,232,.42);
|
|
font-weight:600;
|
|
}
|
|
.btn-teal:hover:not(:disabled){
|
|
background:rgba(34,199,232,.1);
|
|
border-color:var(--teal);
|
|
color:var(--teal);
|
|
}
|
|
.btn-accent{background:var(--accent-d);border-color:var(--accent-d);color:#fff}
|
|
.btn-accent:hover:not(:disabled){background:var(--accent);border-color:var(--accent);color:#1a1220}
|
|
.btn{min-height:36px;font-size:var(--fs-xs);display:inline-flex;align-items:center;justify-content:center;gap:7px}
|
|
|
|
/* --- Forms: label above field, one control height, full card width -------- */
|
|
.card .form-row,.card .form-grid{width:100%}
|
|
.card label{flex-direction:column;align-items:flex-start;gap:6px;font-size:var(--fs-xs)}
|
|
.card label.toggle-label,.card .toggle-row{flex-direction:row;align-items:center}
|
|
input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),
|
|
select,textarea{min-height:40px;font-size:var(--fs-xs)}
|
|
|
|
/* The accent picker was a raw native colour input sitting outside the system. */
|
|
.color-swatch{
|
|
position:relative;width:40px;height:40px;flex:none;overflow:hidden;
|
|
border:1px solid var(--border);border-radius:var(--radius-sm);
|
|
background:var(--accent-d);
|
|
}
|
|
.color-picker-input{
|
|
position:absolute;inset:-4px;width:calc(100% + 8px);height:calc(100% + 8px);
|
|
padding:0;border:0;background:transparent;cursor:pointer;opacity:0;
|
|
}
|
|
.color-hex-label{
|
|
width:11ch;font-family:var(--mono);font-size:var(--fs-xs);
|
|
text-transform:uppercase;text-align:center;
|
|
}
|
|
|
|
/* --- Edges: error placement, toast clearance, orphaned rails -------------- */
|
|
/* The header banner and the toast reported the same failure twice. The banner
|
|
keeps it (state persists); the toast is suppressed for route-load errors. */
|
|
.chimera-route-availability{
|
|
max-width:52ch;padding:7px 11px;line-height:1.45;
|
|
background:rgba(228,173,85,.1);border:1px solid rgba(228,173,85,.3);
|
|
color:var(--yellow);white-space:normal;
|
|
}
|
|
|
|
/* Toast stack clears the floating import button instead of landing under it. */
|
|
#toast-container{bottom:calc(var(--statusbar-h) + 74px)}
|
|
|
|
/* The catalog health rail was floating in the grid with no container, and its
|
|
text values were rendered at the numeral display size. */
|
|
.catalog-health-strip{
|
|
background:var(--surface2);
|
|
border:1px solid var(--border-soft);
|
|
padding:6px 4px;
|
|
}
|
|
#game-catalog-health{font-size:var(--fs-xs)}
|
|
#game-catalog-health strong,#game-catalog-health b{
|
|
font-family:var(--display);font-size:var(--fs-lg);font-weight:600;
|
|
line-height:1.15;font-variant-numeric:tabular-nums;
|
|
}
|
|
|
|
|
|
/* Compact settings rows: a class, not 17 copies of the same inline style. */
|
|
.setting-label{
|
|
flex:none;
|
|
white-space:nowrap;
|
|
font-size:var(--fs-xs);
|
|
color:var(--text-dim);
|
|
}
|
|
.form-row>.setting-label,.color-picker-row>.setting-label{align-self:center}
|
|
|
|
/* Last two off-scale sizes: a button inheriting the UA default, and a glyph. */
|
|
#sidebar-donate{font-size:var(--fs-xs)}
|
|
.chimera-command-search-icon,.game-library-search>span[aria-hidden]{font-size:var(--fs-md);line-height:1}
|
|
|
|
/* ============================================================================
|
|
Library: poster-first cards
|
|
The grid is the artwork. Actions live on the art and only when reached;
|
|
the card itself opens the detail modal, so it carries no button stack.
|
|
========================================================================== */
|
|
|
|
#games-grid{
|
|
grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
|
|
gap:20px 18px;
|
|
align-items:start;
|
|
}
|
|
|
|
.game-card{
|
|
display:flex;flex-direction:column;gap:0;
|
|
background:0 0;border:0;box-shadow:none;padding:0;
|
|
overflow:visible;
|
|
}
|
|
|
|
/* --- the poster ---------------------------------------------------------- */
|
|
.game-card-art{
|
|
position:relative;
|
|
aspect-ratio:var(--game-cover-ratio, 2 / 3);
|
|
overflow:hidden;
|
|
border-radius:var(--radius);
|
|
background:var(--surface2);
|
|
border:1px solid var(--border-soft);
|
|
box-shadow:0 12px 28px -14px rgba(0,0,0,.75);
|
|
transition:box-shadow .24s cubic-bezier(.2,.8,.2,1),border-color .24s;
|
|
}
|
|
.game-card:hover .game-card-art,
|
|
.game-card:focus-visible .game-card-art{
|
|
border-color:rgba(var(--accent-rgb),.5);
|
|
box-shadow:0 22px 46px -16px rgba(0,0,0,.85),0 0 0 1px rgba(var(--accent-rgb),.16);
|
|
}
|
|
.game-card-cover{width:100%;height:100%;object-fit:cover;display:block}
|
|
|
|
/* Coverless titles get a mark derived from their own title ID, so a grid with
|
|
no artwork still reads as a shelf of distinct things. */
|
|
.game-card-cover-fallback.is-visible{
|
|
position:absolute;inset:0;aspect-ratio:auto;
|
|
padding:14px;gap:4px;
|
|
background:
|
|
radial-gradient(circle at 74% 16%, hsl(var(--card-hue,265) 70% 46% / .42), transparent 46%),
|
|
radial-gradient(circle at 18% 88%, hsl(calc(var(--card-hue,265) + 46) 68% 40% / .34), transparent 50%),
|
|
linear-gradient(158deg,#1a1422,#0a0b11);
|
|
}
|
|
.game-card-cover-fallback.is-visible::before{
|
|
top:14%;right:-22%;width:86%;
|
|
border-color:hsl(var(--card-hue,265) 80% 78% / .22);
|
|
}
|
|
.game-card-cover-fallback strong{font-size:var(--fs-md);max-width:13ch;line-height:1.12}
|
|
.game-card-cover-fallback span{color:hsl(var(--card-hue,265) 85% 80% / .9)}
|
|
|
|
/* --- state marker: a corner chip, not a banner across the poster ---------- */
|
|
.game-card-sync{
|
|
position:absolute;top:10px;left:10px;right:auto;
|
|
display:inline-flex;align-items:center;gap:6px;
|
|
max-width:calc(100% - 20px);
|
|
padding:4px 9px;
|
|
font-size:var(--fs-micro);font-weight:600;letter-spacing:.02em;
|
|
color:var(--text);background:rgba(10,8,14,.72);
|
|
border:1px solid var(--border-soft);border-radius:999px;
|
|
backdrop-filter:blur(10px);
|
|
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
}
|
|
.game-card-sync.pending::before{
|
|
content:"";flex:none;width:6px;height:6px;border-radius:50%;
|
|
background:var(--yellow);box-shadow:0 0 0 3px rgba(228,173,85,.18);
|
|
}
|
|
|
|
/* --- actions: on the art, on reach --------------------------------------- */
|
|
.game-card-actions{
|
|
position:absolute;left:0;right:0;bottom:0;
|
|
display:flex;gap:6px;padding:10px;
|
|
background:linear-gradient(transparent,rgba(8,6,12,.82) 42%);
|
|
opacity:0;transform:translateY(6px);
|
|
z-index:4;
|
|
transition:opacity .2s,transform .2s cubic-bezier(.2,.8,.2,1);
|
|
}
|
|
.game-card:hover .game-card-actions,
|
|
.game-card:focus-within .game-card-actions{opacity:1;transform:none}
|
|
.game-card-actions .btn{flex:1;min-height:32px;padding-inline:8px;font-size:var(--fs-micro)}
|
|
|
|
/* Pointer-less and reduced-motion contexts never get hover, so pin them open. */
|
|
@media (hover:none){
|
|
.game-card-actions{position:static;opacity:1;transform:none;background:0 0;padding:8px 0 0}
|
|
}
|
|
@media (prefers-reduced-motion:reduce){
|
|
.game-card-art,.game-card-actions{transition:none}
|
|
}
|
|
|
|
/* --- caption ------------------------------------------------------------- */
|
|
.game-card-body{
|
|
display:flex;flex-direction:column;gap:3px;
|
|
padding:10px 2px 0;background:0 0;border:0;
|
|
}
|
|
.game-card-heading strong{
|
|
display:block;font-size:var(--fs-xs);font-weight:600;line-height:1.3;color:var(--text);
|
|
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
}
|
|
.game-card-meta{
|
|
font-family:var(--mono);font-size:var(--fs-micro);color:var(--text-muted);
|
|
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
}
|
|
.game-card .game-card-status{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
|
|
|
|
/* The sync chip was a full-width purple banner in uppercase mono across the
|
|
poster. It is a state marker, not a headline. */
|
|
.game-card-sync,
|
|
.game-card-sync.pending{
|
|
color:var(--text);
|
|
background:rgba(10,8,14,.72);
|
|
border-color:var(--border-soft);
|
|
font:600 var(--fs-micro)/1.2 var(--ui);
|
|
letter-spacing:.01em;
|
|
text-transform:none;
|
|
right:auto;
|
|
}
|
|
|
|
/* A coverless poster carries the title as its artwork, so the caption below it
|
|
would only repeat itself. Show the caption title when real cover art exists. */
|
|
/* Two art treatments already print the title onto the poster: the generated
|
|
fallback, and the "case" used when only a square icon exists. In both the
|
|
caption below would just repeat it. */
|
|
.game-card:has(.game-card-cover-fallback.is-visible) .game-card-heading,
|
|
.game-card:has(.game-card-cover-caption) .game-card-heading{display:none}
|
|
.game-card:has(.game-card-cover-fallback.is-visible) .game-card-body,
|
|
.game-card:has(.game-card-cover-caption) .game-card-body{padding-top:8px}
|
|
|
|
/* ============================================================================
|
|
Title detail: hero header
|
|
The dialog opened on a fifteen-row key/value table with an empty banner bar.
|
|
The header now carries the artwork, the name and the identity chips.
|
|
========================================================================== */
|
|
|
|
#modal-header{border-bottom:0}
|
|
#modal-banner{
|
|
filter:blur(18px) saturate(1.15);
|
|
transform:scale(1.25);
|
|
opacity:.55;
|
|
}
|
|
/* No artwork: a ground derived from the title's own ID, same seed as the card. */
|
|
#modal-header::before{
|
|
content:"";position:absolute;inset:0;
|
|
background:
|
|
radial-gradient(circle at 78% 8%, hsl(var(--modal-hue,265) 72% 48% / .40), transparent 52%),
|
|
radial-gradient(circle at 12% 96%, hsl(calc(var(--modal-hue,265) + 46) 66% 42% / .30), transparent 55%),
|
|
linear-gradient(160deg,#181322,#0b0910);
|
|
}
|
|
#modal-banner.has-art{opacity:.78}
|
|
/* Scrim so the title always holds against whatever artwork loads. */
|
|
#modal-header::after{
|
|
content:"";position:absolute;inset:0;pointer-events:none;
|
|
background:linear-gradient(180deg,rgba(10,8,14,.30),rgba(18,14,23,.86) 72%,var(--surface));
|
|
}
|
|
#modal-header-content{position:relative;z-index:2}
|
|
|
|
#modal-icon{
|
|
width:84px!important;height:84px!important;
|
|
border-radius:var(--radius-sm)!important;
|
|
border:1px solid rgba(255,255,255,.14);
|
|
box-shadow:0 16px 34px -12px rgba(0,0,0,.9)!important;
|
|
}
|
|
#modal-header h2#modal-title{text-wrap:balance}
|
|
#modal-subtitle{
|
|
margin-top:6px;
|
|
font-family:var(--mono);
|
|
font-size:var(--fs-micro);
|
|
color:var(--text-dim);
|
|
letter-spacing:.04em;
|
|
}
|
|
#modal-close{
|
|
position:relative;z-index:3;
|
|
width:36px;height:36px;display:grid;place-items:center;
|
|
border-radius:50%;
|
|
background:rgba(10,8,14,.55);border:1px solid var(--border-soft);
|
|
color:var(--text);backdrop-filter:blur(10px);
|
|
}
|
|
#modal-close:hover{background:rgba(10,8,14,.8);border-color:var(--accent)}
|
|
|
|
/* Workspace links read as destinations, not as a second toolbar. */
|
|
.modal-workspace-links{margin-top:12px;gap:6px}
|
|
.modal-workspace-links button{
|
|
background:rgba(10,8,14,.5)!important;
|
|
border-color:var(--border-soft)!important;
|
|
backdrop-filter:blur(10px);
|
|
}
|
|
|
|
/* --- metric tiles --------------------------------------------------------- */
|
|
.modal-metrics{
|
|
display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
|
|
gap:1px;background:var(--border-soft);
|
|
border:1px solid var(--border-soft);border-radius:var(--radius);
|
|
overflow:hidden;margin-bottom:18px;
|
|
}
|
|
.modal-metric{
|
|
display:flex;flex-direction:column;gap:5px;
|
|
padding:14px 16px;background:var(--surface2);
|
|
}
|
|
.modal-metric span{
|
|
font-family:var(--mono);font-size:var(--fs-micro);
|
|
letter-spacing:.11em;text-transform:uppercase;color:var(--text-muted);
|
|
}
|
|
.modal-metric strong{
|
|
font-family:var(--display);font-size:var(--fs-md);font-weight:600;
|
|
color:var(--text);font-variant-numeric:tabular-nums;line-height:1.15;
|
|
}
|
|
.modal-metric.is-ok strong{color:var(--green)}
|
|
.modal-metric.is-warn strong{color:var(--yellow)}
|
|
.modal-metric.is-idle strong{color:var(--text-muted)}
|
|
|
|
/* --- tab strip: fade the overflow instead of showing a raw scrollbar ------- */
|
|
.modal-tabs{
|
|
position:relative;
|
|
scrollbar-width:none;
|
|
-webkit-mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 44px),transparent);
|
|
mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 44px),transparent);
|
|
}
|
|
.modal-tabs::-webkit-scrollbar{height:0}
|
|
|
|
/* --- metadata table: let absent values recede ----------------------------- */
|
|
.detail-table th{
|
|
font-family:var(--ui);font-size:var(--fs-xs);font-weight:500;
|
|
letter-spacing:0;text-transform:none;color:var(--text-muted);
|
|
}
|
|
.detail-table td{font-size:var(--fs-xs)}
|
|
.detail-table td{color:var(--text)}
|
|
|
|
/* ============================================================================
|
|
Title detail: subsystem rail
|
|
Thirteen subsystems sat in a horizontal tab strip that overflowed and had to
|
|
be scrolled to be read. The same buttons become a vertical rail — every
|
|
destination scannable at once, no horizontal scroll, and room for the summary
|
|
to breathe. Structure is untouched: #modal already has exactly four children,
|
|
so this is a grid re-layout, not a rebuild.
|
|
Below 760px the original horizontal strip is kept; a rail costs too much width.
|
|
========================================================================== */
|
|
@media (min-width:760px){
|
|
#modal{
|
|
display:grid;
|
|
grid-template-columns:206px minmax(0,1fr);
|
|
grid-template-rows:auto minmax(0,1fr) auto;
|
|
}
|
|
#modal-header{grid-column:1/-1;grid-row:1}
|
|
#modal-body{grid-column:2;grid-row:2;min-height:0;overflow-y:auto}
|
|
#modal-footer{grid-column:1/-1;grid-row:3}
|
|
|
|
.modal-tabs{
|
|
grid-column:1;grid-row:2;
|
|
display:flex;flex-direction:column;align-items:stretch;
|
|
gap:2px;
|
|
min-height:0;
|
|
padding:14px 10px 18px;
|
|
overflow-x:hidden;overflow-y:auto;
|
|
border-bottom:0;
|
|
border-right:1px solid var(--border-soft);
|
|
background:rgba(10,8,14,.28);
|
|
/* cancel the horizontal overflow fade */
|
|
-webkit-mask-image:none;mask-image:none;
|
|
scrollbar-width:thin;
|
|
}
|
|
.modal-tabs::before{
|
|
content:"Subsystems";
|
|
padding:0 11px 9px;
|
|
font-family:var(--mono);font-size:var(--fs-micro);
|
|
letter-spacing:.14em;text-transform:uppercase;color:var(--text-muted);
|
|
}
|
|
.modal-tab{
|
|
flex:0 0 auto;
|
|
justify-content:flex-start;
|
|
min-height:34px;
|
|
margin-bottom:0;
|
|
padding:7px 11px;
|
|
text-align:left;
|
|
border-bottom:0;
|
|
border-left:2px solid transparent;
|
|
border-radius:var(--radius-sm);
|
|
font-size:var(--fs-xs);
|
|
}
|
|
.modal-tab:hover{background:rgba(255,255,255,.045)}
|
|
.modal-tab.active{
|
|
color:var(--text);
|
|
background:rgba(var(--accent-rgb),.16);
|
|
border-bottom-color:transparent;
|
|
border-left-color:var(--accent);
|
|
}
|
|
/* the count badge trails the label instead of crowding it */
|
|
.modal-tab .tab-badge{margin-left:auto}
|
|
}
|
|
|
|
/* ============================================================================
|
|
Utilities
|
|
index.html carried 275 inline style attributes. Inline styles beat every
|
|
stylesheet rule without !important, which is why the type scale could not
|
|
reach the settings labels no matter what the CSS said. These six recurring
|
|
declarations are now classes; the remaining ~220 are per-site layout and
|
|
dynamic values (progress widths) that need judgement, not a find-replace.
|
|
========================================================================== */
|
|
.u-flex-1{flex:1}
|
|
.u-push{margin-left:auto}
|
|
.u-row-8{align-items:center;gap:8px}
|
|
.u-fs-micro{font-size:var(--fs-micro)}
|
|
.u-field-md{min-width:150px}
|
|
.u-field-sm{width:140px}
|
|
|
|
/* <small> otherwise inherits the browser's 0.83em and compounds below the floor. */
|
|
small,.hint,.toggle-hint{font-size:var(--fs-xs)}
|
|
/* An unclassed <button> otherwise takes the UA default (13.33px) and sits off
|
|
the scale. Any class rule still wins, so styled buttons are unaffected. */
|
|
button{font-size:inherit}
|
|
|
|
|
|
/* Spacing utilities on the 4/8 scale the art direction specifies. These replace
|
|
56 ad-hoc inline margins that sat between steps (5, 6, 10, 14, 18px). */
|
|
.u-mt-4{margin-top:4px}
|
|
.u-mt-8{margin-top:8px}
|
|
.u-mt-12{margin-top:12px}
|
|
.u-mt-16{margin-top:16px}
|
|
.u-mb-4{margin-bottom:4px}
|
|
.u-mb-8{margin-bottom:8px}
|
|
.u-mb-12{margin-bottom:12px}
|
|
.u-mb-16{margin-bottom:16px}
|