Files
ark-stronghold-protocol/public/css/theme.css
T
sganggs 221e6b8dd2 0.2.5-dev: community round 30 and follow-ups
- Stock: fixed trait gifts (缪尔赛思 / 变形者集群 变形同构体, 耶拉 不融冰, 蜜蜡 / 卡涅利安 迅捷作战粮, 诗怀雅 盟约之币) still occupy stock but are granted past the cap; an operator card already shown in the shop or a reward can be taken after the pool runs out and overdraws (GitHub #498, the owner's decision); item buys and item rewards keep the stock check. 洛洛 / 焰尾 sub-pools use the measured weights (PR #497 by @2321Robin; the occupancy idea of PR #496 by @2321Robin re-implemented differently).
- Boss pairs are drawn at random once per match and fixed to battle start; the Hidden Core reuses them; a leaver does not split a full pair (the owner's decision).
- 阿戈尔 in 联防 no longer re-applies the opening devour HP loss or a false revive; Touch's team medic stays for the match after its holder leaves (owner-approved, official lifetime unverified).
- Auto-skill ranges: 司霆惊蛰 S2, 斩业星熊 S3 against air, 予愿安洁莉娜 S3; 自在's post-rebirth invulnerability is also untargetable (#510); Horn / Clementia / catapult / 忍冬 first hit (PR #502 by @Xinglan233); 不融冰 / tear gas trigger on real damage.
- Bond layer-up sound in round start / draft / prep / battle (#482): one channel, a new cue replaces the old, no extra cooldown; a prep update that adds layers and activates a bond plays one cue.
- Client: multi-swing fix (#506), refresh single-flight (PR #507 by @robll-v1), LP tower keeps the settled value with a −N tick (PR #504 by @robll-v1), export with only 潜能 / 练度 changes (PR #503 by @Xinglan233), spectator rosters list DIY members (PR #505 by @Xinglan233), banned members beside bond tooltips (PR #494 by @YuriRestia), recovery action above the away overlay (PR #511 by @RainoutOvO), the /dev/sim-check.html consistency page (PR #492 by @Cloudnyco), Korean (PR #490 by @D-Roote) and English Victoria wording (PR #499 by @YuriRestia).
- golden: 45 of 287 scenarios move against 0.2.4 (roster 17, bonds 3, fields 6, matches 17, standins 2), each attributed in docs/history/0.2.5.md §30.9 / §30.16.
2026-10-12 14:07:08 +08:00

394 lines
15 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.
/* ==========================================================================================
theme.css — design tokens, fonts, root scale, base reset, shared backdrops & utilities.
Look: research 06 §12 (near-black green-grey, mint accents, gold prices, bracket frames).
Root scale: 1rem = 100 design px at 1920×1080 and scales with min(vw/1920, vh/1080), so the
whole UI is authored in design pixels ÷ 100 (e.g. 24px → .24rem). The root never drops below
40px, which keeps it clear of CJK browsers' minimum-font-size clamping. 1px hairlines stay px.
========================================================================================== */
:root {
color-scheme: dark;
/* surfaces */
--bg-0: #0c0f0e;
--bg-1: #141816;
--bg-2: #1e2421;
--bg-3: #252c29;
--bg-4: #2d3632;
--bg-glass: rgba(16, 20, 18, .82);
--line: #2f3a35;
--line-2: #3e4b45;
--line-3: #58675f;
/* accents (official hex values where research marks [DATA]) */
--mint-500: #4ed8af;
--mint-400: #59f4ca;
--mint-600: #35d8b4;
--mint-glow: #17f9b7;
--mint-700: #2a9e7f;
--mint-800: #145443;
--mint-900: #0d2c24;
--mint-a10: rgba(78, 216, 175, .10);
--mint-a20: rgba(78, 216, 175, .20);
--mint-a35: rgba(78, 216, 175, .35);
--gold: #ffc600;
--gold-2: #f2bd3e;
--gold-a20: rgba(255, 198, 0, .2);
--amber: #f6a329;
--amber-a20: rgba(246, 163, 41, .2);
--orange: #e85a1a;
--orange-hi: #ff7a33;
--orange-a20: rgba(232, 90, 26, .22);
--red: #e73118;
--red-abyss: #ff0024;
--red-premium: #ff5454;
--red-a20: rgba(231, 49, 24, .22);
--ice: #9fd4ff;
--ice-a20: rgba(159, 212, 255, .2);
--ice-900: #14283a;
/* text */
--text-hi: #f2f2f2;
--text-md: #c3cbc7;
--text-lo: #8a948f;
--text-dim: #5d6863;
--text-on-mint: #06110d;
/* difficulty (shared/constants.js DIFFICULTY_COLORS) */
--diff-funny: #f6a329;
--diff-normal: #e85a1a;
--diff-hard: #e73118;
--diff-abyss: #ff0024;
/* chess tier accents (I–VI), sampled from the official tier chips (img_chess_level_1..6) */
--tier-1: #a8a8a8;
--tier-2: #f2f2f2;
--tier-3: #34e6b2;
--tier-4: #34b8d8;
--tier-5: #f8bf00;
--tier-6: #f88000;
/* shop level tag colours (shopLevelDisplayDataDict) */
--lv-1: #434343;
--lv-2: #626654;
--lv-3: #445760;
--lv-4: #615b74;
--lv-5: #6c5e41;
--lv-6: #5d341e;
/* type */
/* the Han / Hangul faces follow <html lang> (ui/lang.js htmlLang; the :lang() rules below): Simplified Chinese by default */
--cjk-faces: 'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans CJK SC';
--font-cjk: var(--cjk-faces), sans-serif;
--font-num: 'Bender', 'Rajdhani', 'Oxanium', var(--cjk-faces), sans-serif;
--font-display: 'Novecento', 'Novecento Wide', 'Oxanium', 'Rajdhani', var(--cjk-faces), sans-serif;
--font-mono: 'Bender', 'Oxanium', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
/* motion */
--ease-out: cubic-bezier(.2, .8, .2, 1);
--ease-in-out: cubic-bezier(.6, 0, .3, 1);
--t-fast: 150ms;
--t-med: 200ms;
--t-slow: 250ms;
/* layers */
--z-screen: 1;
--z-banner: 40;
--z-conn: 75;
--z-modal: 80;
--z-toast: 90;
--z-tooltip: 95;
--z-rotate: 100;
/* bracket corners (see .brackets) */
--bk-c: var(--mint-500);
--bk-l: .14rem;
--bk-w: 2px;
}
/* Language packs (docs/I18N.md): the same characters take their own glyph forms per language — Japanese kanji,
Traditional Chinese, a Korean face for Hangul (each stack falls back to the Simplified Chinese faces). */
:root:lang(ja) { --cjk-faces: 'Noto Sans JP', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic UI', 'Yu Gothic', 'Meiryo', 'Noto Sans CJK JP', 'Noto Sans SC', 'Microsoft YaHei'; }
:root:lang(ko) { --cjk-faces: 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans CJK KR', 'Noto Sans SC', 'Microsoft YaHei'; }
:root:lang(zh-TW) { --cjk-faces: 'Noto Sans TC', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', 'Noto Sans SC', 'Microsoft YaHei'; }
/* ---- root scale ----------------------------------------------------------------------------- */
html {
font-size: clamp(40px, min(calc(100vw / 19.2), calc(100vh / 10.8)), 240px);
/* mobile browsers: 100vh is the height with the toolbars hidden — the small viewport (svh; Safari 15.4+, Chrome 108+,
Firefox 101+) is what is really visible while they show; older browsers keep the line above */
font-size: clamp(40px, min(calc(100vw / 19.2), calc(100svh / 10.8)), 240px);
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
height: 100%;
background: var(--bg-0);
}
/* ---- text root: 设置 →「文字大小」 ---------------------------------------------------------------
1rem above is the LAYOUT root, and five modules convert it to pixels with getComputedStyle:
ui/fieldHost.js hudPadding / hudBands (the prep camera's HUD clearance), ui/gameLogic/panel.js
panelSlots (which side the detail card docks), ui/underframe.js (the tile labels' pixel budget) and
ui/fallbackField.js (the DOM board) — raising it would move the board, not just the text.
--t is the TEXT root: it appears only in font-size declarations, so a player can enlarge the
interface text without touching any layout. The field itself is sized from host.clientWidth /
clientHeight (render/app.js), which no font-size can reach.
小 = 1rem reproduces the design's own sizes exactly, on every device. The other steps keep the
desktop gentle and lift the phone: 1rem there is clamped at 40 px by the rule above, which is what
made the .18rem body text 7.2 CSS px. */
:root { --t: 1rem; }
:root[data-text="md"] { --t: max(1rem, 64px); }
:root[data-text="lg"] { --t: max(calc(1rem * 1.15), 76px); }
:root[data-text="xl"] { --t: max(calc(1rem * 1.3), 88px); }
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
height: 100%;
overflow: hidden;
background: var(--bg-0);
color: var(--text-hi);
font-family: var(--font-cjk);
font-size: calc(.18 * var(--t));
line-height: 1.5;
font-weight: 400;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
input, textarea, [contenteditable], .selectable { -webkit-user-select: text; user-select: text; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
img { max-width: 100%; }
h1, h2, h3, h4, p { margin: 0; }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--mint-400); outline-offset: 2px; }
::selection { background: var(--mint-500); color: var(--text-on-mint); }
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line-2); }
::-webkit-scrollbar-thumb:hover { background: var(--mint-700); }
.sr-only {
position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* ---- typography helpers ---------------------------------------------------------------------- */
.num { font-family: var(--font-num); font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.display { font-family: var(--font-display); text-transform: uppercase; letter-spacing: .12em; }
.micro {
font-family: var(--font-display);
font-size: calc(.11 * var(--t));
line-height: 1.2;
letter-spacing: .18em;
text-transform: uppercase;
color: var(--text-dim);
white-space: nowrap;
}
.micro--mint { color: var(--mint-700); }
.micro--gold { color: #9c7c16; }
.micro--hi { color: var(--text-lo); }
.t-mint { color: var(--mint-500); }
.t-gold { color: var(--gold); }
.t-amber { color: var(--amber); }
.t-orange { color: var(--orange-hi); }
.t-red { color: var(--red-premium); }
.t-ice { color: var(--ice); }
.t-lo { color: var(--text-lo); }
.t-dim { color: var(--text-dim); }
/* ---- bracket corners: 4 L-shaped corners drawn with gradients on ::after ---------------------- */
.brackets { position: relative; }
.brackets::after {
content: '';
position: absolute;
inset: -1px;
pointer-events: none;
background:
linear-gradient(var(--bk-c), var(--bk-c)) top left / var(--bk-l) var(--bk-w) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) top left / var(--bk-w) var(--bk-l) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) top right / var(--bk-l) var(--bk-w) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) top right / var(--bk-w) var(--bk-l) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) bottom left / var(--bk-l) var(--bk-w) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) bottom left / var(--bk-w) var(--bk-l) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) bottom right / var(--bk-l) var(--bk-w) no-repeat,
linear-gradient(var(--bk-c), var(--bk-c)) bottom right / var(--bk-w) var(--bk-l) no-repeat;
transition: opacity var(--t-med) var(--ease-out);
}
/* ---- texture overlays -------------------------------------------------------------------------- */
.fx-scan { position: relative; }
.fx-scan::before {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(0deg, rgba(255, 255, 255, .028) 0 1px, transparent 1px 3px);
mix-blend-mode: screen;
z-index: 0;
}
.fx-noise {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 .8 0 0 0 0 .9 0 0 0 0 .85 0 0 0 .55 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/></svg>");
}
/* ---- global app backdrop (behind every screen) ------------------------------------------------- */
.app-root {
position: fixed;
inset: 0;
overflow: hidden;
isolation: isolate;
}
.app-bg {
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
background:
radial-gradient(120% 80% at 50% 110%, rgba(23, 249, 183, .10), transparent 55%),
radial-gradient(80% 60% at 50% 40%, rgba(40, 60, 52, .35), transparent 70%),
linear-gradient(180deg, #0b0e0d 0%, #0e1311 55%, #0a0d0c 100%);
}
.app-bg::before {
/* faint survey grid */
content: '';
position: absolute;
inset: 0;
background:
linear-gradient(rgba(78, 216, 175, .045) 1px, transparent 1px) 0 0 / .8rem .8rem,
linear-gradient(90deg, rgba(78, 216, 175, .045) 1px, transparent 1px) 0 0 / .8rem .8rem;
-webkit-mask-image: radial-gradient(90% 80% at 50% 50%, #000 30%, transparent 85%);
mask-image: radial-gradient(90% 80% at 50% 50%, #000 30%, transparent 85%);
}
.app-bg::after {
/* grain + scanlines + vignette */
content: '';
position: absolute;
inset: 0;
background:
repeating-linear-gradient(0deg, rgba(255, 255, 255, .02) 0 1px, transparent 1px 3px),
radial-gradient(130% 100% at 50% 50%, transparent 55%, rgba(0, 0, 0, .65) 100%),
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 .75 0 0 0 0 .85 0 0 0 0 .8 0 0 0 .6 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='.35'/></svg>");
opacity: .9;
}
/* ---- screens ------------------------------------------------------------------------------------ */
.screen {
position: absolute;
inset: 0;
z-index: var(--z-screen);
display: flex;
flex-direction: column;
overflow: hidden;
animation: screen-in var(--t-slow) var(--ease-out) backwards;
}
/* Opacity only: a transform here would become the containing block of fixed overlays. */
@keyframes screen-in { from { opacity: 0; } }
.screen__scroll {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
}
/* crosshair "+" marks used as decoration */
.cross {
position: absolute;
width: .22rem;
height: .22rem;
pointer-events: none;
background:
linear-gradient(var(--mint-700), var(--mint-700)) center / 100% 1px no-repeat,
linear-gradient(var(--mint-700), var(--mint-700)) center / 1px 100% no-repeat;
opacity: .7;
}
/* ---- boot splash (index.html, removed by main.js) ------------------------------------------- */
.boot {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: var(--bg-0);
z-index: 200;
transition: opacity 250ms ease;
}
.boot.is-done { opacity: 0; pointer-events: none; }
.boot__inner { display: grid; justify-items: center; gap: .18rem; }
.boot__hex {
width: .64rem;
height: .64rem;
border: 2px solid var(--mint-500);
clip-path: polygon(25% 3%, 75% 3%, 100% 50%, 75% 97%, 25% 97%, 0 50%);
animation: boot-spin 1.4s linear infinite;
background: linear-gradient(135deg, transparent 45%, rgba(78, 216, 175, .35) 50%, transparent 55%);
}
.boot__text { font-family: var(--font-display); font-size: .13rem; letter-spacing: .4em; color: var(--mint-700); }
.boot__err { max-width: 6rem; text-align: center; color: var(--text-lo); font-size: .16rem; }
@keyframes boot-spin { to { transform: rotate(360deg); } }
/* ---- rotate-device hint (portrait phones/tablets) ------------------------------------------- */
.rotate-hint {
position: fixed;
inset: 0;
z-index: var(--z-rotate);
display: none;
place-items: center;
align-content: center;
gap: 18px;
padding: 24px;
text-align: center;
background: var(--bg-0);
color: var(--text-md);
font-size: 16px;
}
.rotate-hint__phone {
width: 54px;
height: 90px;
border: 3px solid var(--mint-500);
border-radius: 9px;
position: relative;
animation: rotate-phone 2.4s var(--ease-in-out) infinite;
box-shadow: 0 0 24px rgba(23, 249, 183, .25);
}
.rotate-hint__phone::after {
content: '';
position: absolute;
left: 50%;
bottom: 6px;
width: 14px;
height: 3px;
margin-left: -7px;
background: var(--mint-500);
border-radius: 2px;
}
.rotate-hint__title { font-size: 20px; font-weight: 700; color: var(--text-hi); }
.rotate-hint__micro { font-family: var(--font-display); font-size: 11px; letter-spacing: .3em; color: var(--mint-700); }
@keyframes rotate-phone {
0%, 20% { transform: rotate(0deg); }
50%, 75% { transform: rotate(-90deg); }
100% { transform: rotate(0deg); }
}
/* only on a device that can be turned (js/ui/device.js sp-rotatable: a touch screen whose screen is not landscape) —
a narrow desktop window or a split-view iPad keeps the game */
@media (orientation: portrait) and (max-width: 1024px) {
.sp-rotatable .rotate-hint { display: grid; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
transition-duration: 1ms !important;
}
}