site: hero demo explains each scene in plain words (headline, outcome, by hand); pre-flight becomes a checklist of what check verified, with fixes and the green re-check; scenes hold their final frame
This commit is contained in:
parent
f106ec4423
commit
f0a3923a4f
3 changed files with 158 additions and 64 deletions
|
|
@ -77,8 +77,12 @@
|
||||||
<button type="button" role="tab" id="sc-t4" aria-controls="sc-p4" aria-selected="false" tabindex="-1">Icons</button>
|
<button type="button" role="tab" id="sc-t4" aria-controls="sc-p4" aria-selected="false" tabindex="-1">Icons</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sc-stage">
|
<div class="sc-stage">
|
||||||
<!-- Every line below is real CLI output from the bundled demos (timestamps and absolute paths trimmed). -->
|
<!-- Terminal lines, counts and check results are real CLI output from the bundled demos (timestamps and absolute paths trimmed, long lines shortened with …). -->
|
||||||
<section class="sc sc-shots" id="sc-p1" role="tabpanel" aria-labelledby="sc-t1" data-cwd="examples/demo-game" tabindex="0">
|
<section class="sc sc-shots" id="sc-p1" role="tabpanel" aria-labelledby="sc-t1" data-cwd="examples/demo-game" tabindex="0">
|
||||||
|
<div class="sc-head">
|
||||||
|
<p class="sc-h">Store screenshots, every size, every language</p>
|
||||||
|
<p class="sc-out">One command rendered <b>200 store-ready images</b>: 6 device sizes plus the Play feature graphic, 4 languages, an A/B variant. Then checked them against both stores' specs: <b>0 errors</b>.</p>
|
||||||
|
</div>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound shots render --variants</span></p>
|
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound shots render --variants</span></p>
|
||||||
<div class="tick" aria-hidden="true">
|
<div class="tick" aria-hidden="true">
|
||||||
<p data-s="22"><b>[shots]</b> store/renders/en/ios69/01.png</p>
|
<p data-s="22"><b>[shots]</b> store/renders/en/ios69/01.png</p>
|
||||||
|
|
@ -115,29 +119,48 @@
|
||||||
<li data-s="45"><img src="img/hero/s-ar-4.webp" width="120" height="261" alt="" loading="lazy" decoding="async"><span>AR</span></li>
|
<li data-s="45"><img src="img/hero/s-ar-4.webp" width="120" height="261" alt="" loading="lazy" decoding="async"><span>AR</span></li>
|
||||||
<li data-s="45"><img src="img/hero/s-tr-4.webp" width="120" height="261" alt="" loading="lazy" decoding="async"><span>TR</span></li>
|
<li data-s="45"><img src="img/hero/s-tr-4.webp" width="120" height="261" alt="" loading="lazy" decoding="async"><span>TR</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="sc-foot" data-s="400"><b class="big"><span data-count="200">200</span> store images</b> <span class="sep">·</span> 4 languages <span class="sep">·</span> 6 sizes + feature graphic <span class="sep">·</span> A/B variant</p>
|
<p class="sc-foot" data-s="300"><b class="big"><span data-count="200">200</span> images</b> <span class="sep">·</span> iPhone 6.9" and 6.5", iPad 13", Play phone, 7" and 10" tablet, feature graphic</p>
|
||||||
|
<p class="sc-hand"><span>By hand</span> 200 exports from a design tool, each dropped into the right slot in two consoles.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="sc sc-pf" id="sc-p2" role="tabpanel" aria-labelledby="sc-t2" data-cwd="examples/demo-app" tabindex="0" hidden>
|
<section class="sc sc-pf" id="sc-p2" role="tabpanel" aria-labelledby="sc-t2" data-cwd="examples/demo-app" tabindex="0" hidden>
|
||||||
<p class="sc-pill" aria-hidden="true"><span class="pill-bad">3 problems</span><span class="pill-good">all good</span></p>
|
<div class="sc-head">
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound check</span></p>
|
<p class="sc-h">Catches what gets you rejected, before you upload</p>
|
||||||
<p class="t-out" data-s><b>[check]</b> config ok: storebound.config.ts</p>
|
<p class="sc-out">Checked a sign-in app against 7 store requirements, offline. <b>3 would have stopped it</b>, each named with its fix. Fixed, re-checked: <b>all 7 pass</b>.</p>
|
||||||
<p class="t-out" data-s><b>[check]</b> data safety: 764 CSV rows</p>
|
</div>
|
||||||
<p class="t-bad t-wrap" data-s>- play listing: store/listing.md is missing · storebound init creates it</p>
|
<div class="pf-strip">
|
||||||
<p class="t-bad t-wrap" data-s>- appstore listing: store/appstore.md is missing · storebound init creates it</p>
|
<div class="tick tick-pf" aria-hidden="true">
|
||||||
<p class="t-bad t-wrap" data-s>- App Review needs a demo account (sign-in required) · add reviewDemoAccount to .storebound/credentials.json · review-access skill</p>
|
<p class="t-cmd" data-s="type" data-o="1"><i>$</i> <span>storebound check</span></p>
|
||||||
<p class="t-err" data-s="600" data-pill="bad">error: 3 problems</p>
|
<p class="t-err" data-s="2000" data-o="10" data-pill="bad">error: 3 problems</p>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound init</span></p>
|
<p class="t-cmd" data-s="type" data-o="11"><i>$</i> <span>storebound init</span></p>
|
||||||
<p class="t-out" data-s><b>[init]</b> create store/listing.md</p>
|
<p class="t-out" data-s="200" data-o="12"><b>[init]</b> create store/listing.md</p>
|
||||||
<p class="t-out" data-s><b>[init]</b> create store/appstore.md</p>
|
<p class="t-out" data-s="500" data-o="13"><b>[init]</b> create store/appstore.md</p>
|
||||||
<p class="t-note" data-s="350"># owner adds the App Review demo account (.storebound/)</p>
|
<p class="t-note" data-s="1100" data-o="14"># you add the App Review demo account to .storebound/ (never chat, never git)</p>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound check</span></p>
|
<p class="t-cmd" data-s="type" data-o="15"><i>$</i> <span>storebound check</span></p>
|
||||||
<p class="t-ok" data-s><b>[check]</b> play listing: 2 languages</p>
|
<p class="t-ok" data-s="260" data-o="16" data-flip="pf-1"><b>[check]</b> play listing: 2 languages</p>
|
||||||
<p class="t-ok" data-s><b>[check]</b> appstore listing: 2 locales</p>
|
<p class="t-ok" data-s="260" data-o="17" data-flip="pf-2"><b>[check]</b> appstore listing: 2 locales</p>
|
||||||
<p class="t-ok t-strong" data-s data-pill="good"><b>[check]</b> all good</p>
|
<p class="t-ok t-strong" data-s="200" data-o="18" data-flip="pf-6" data-pill="good"><b>[check]</b> all good</p>
|
||||||
|
</div>
|
||||||
|
<p class="sc-pill" aria-hidden="true"><span class="pill-bad">3 to fix</span><span class="pill-good">7 of 7 pass</span></p>
|
||||||
|
</div>
|
||||||
|
<ol class="pf-list" aria-label="What storebound check verified">
|
||||||
|
<li class="pf-row" id="pf-1" data-bad data-s="260" data-o="2"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">Play listing<small>title 30 · short 80 · full 4000</small></span><span class="pf-det"><span class="d-bad">store/listing.md is missing · fix: <code>storebound init</code></span><span class="d-ok">2 languages, every field within limits <em>fixed by init</em></span></span></li>
|
||||||
|
<li class="pf-row" id="pf-2" data-bad data-s="260" data-o="3"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">App Store listing<small>name · subtitle · keywords 100</small></span><span class="pf-det"><span class="d-bad">store/appstore.md is missing · fix: <code>storebound init</code></span><span class="d-ok">2 locales, every field within limits <em>fixed by init</em></span></span></li>
|
||||||
|
<li class="pf-row" data-s="140" data-o="4"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">Screenshot captions<small>every scene, every language</small></span><span class="pf-det"><span class="d-ok">2 languages × 4 scenes, none missing</span></span></li>
|
||||||
|
<li class="pf-row" data-s="140" data-o="5"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">Play Data safety<small>the form Play requires</small></span><span class="pf-det"><span class="d-ok">764 answers built from the config</span></span></li>
|
||||||
|
<li class="pf-row" data-s="140" data-o="6"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">Account deletion<small>Play web page · Apple 5.1.1(v)</small></span><span class="pf-det"><span class="d-ok">web page and in-app path both set</span></span></li>
|
||||||
|
<li class="pf-row" id="pf-6" data-bad data-s="260" data-o="7"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">App Review sign-in<small>Apple 2.1 · Play App access</small></span><span class="pf-det"><span class="d-bad">sign-in required, no demo account · fix: add it to <code>.storebound/</code></span><span class="d-ok">demo account set, kept only in .storebound/ <em>fixed by you</em></span></span></li>
|
||||||
|
<li class="pf-row" data-s="140" data-o="8"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">Secrets stay out of git<small>store keys, demo account</small></span><span class="pf-det"><span class="d-ok">.storebound/ is in .gitignore</span></span></li>
|
||||||
|
<li class="pf-row pf-na" data-s="140" data-o="9"><span class="pf-ic" aria-hidden="true"></span><span class="pf-what">In-app products, subscriptions<small>price texts, terms URL</small></span><span class="pf-det"><span class="d-ok">none in this app, skipped</span></span></li>
|
||||||
|
</ol>
|
||||||
|
<p class="sc-hand"><span>Without it</span> a missing demo account comes back as a Guideline 2.1 rejection.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="sc sc-forms" id="sc-p3" role="tabpanel" aria-labelledby="sc-t3" data-cwd="examples/demo-app" tabindex="0" hidden>
|
<section class="sc sc-forms" id="sc-p3" role="tabpanel" aria-labelledby="sc-t3" data-cwd="examples/demo-app" tabindex="0" hidden>
|
||||||
|
<div class="sc-head">
|
||||||
|
<p class="sc-h">Store forms answered from your config</p>
|
||||||
|
<p class="sc-out">Play's Data safety form: <b>764 answers</b> derived from 12 lines of config, as the CSV Play imports. What only you can know, like encryption, <b>it asks you</b>. It never guesses.</p>
|
||||||
|
</div>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound play data-safety --dry</span></p>
|
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound play data-safety --dry</span></p>
|
||||||
<p class="t-out t-wrap" data-s="200"><b>[play]</b> data safety: 4 data types declared (PSL_EMAIL, PSL_USER_ACCOUNT, PSL_USER_GENERATED_CONTENT, PSL_CRASH_LOGS)</p>
|
<p class="t-out t-wrap" data-s="200"><b>[play]</b> data safety: 4 data types declared (PSL_EMAIL, PSL_USER_ACCOUNT, PSL_USER_GENERATED_CONTENT, PSL_CRASH_LOGS)</p>
|
||||||
<div class="tick tick-csv" aria-hidden="true">
|
<div class="tick tick-csv" aria-hidden="true">
|
||||||
|
|
@ -176,13 +199,17 @@
|
||||||
<p data-s="24"><em>TRUE</em> <span class="lbl">Data usage and handling (PSL_USER_GENERATED_CONTENT) / Collected</span></p>
|
<p data-s="24"><em>TRUE</em> <span class="lbl">Data usage and handling (PSL_USER_GENERATED_CONTENT) / Collected</span></p>
|
||||||
<p data-s="24"><em>TRUE</em> <span class="lbl">Data usage and handling (PSL_USER_GENERATED_CONTENT) / Required</span></p>
|
<p data-s="24"><em>TRUE</em> <span class="lbl">Data usage and handling (PSL_USER_GENERATED_CONTENT) / Required</span></p>
|
||||||
</div>
|
</div>
|
||||||
<p class="sc-foot" data-s="500"><b class="big"><span data-count="764">764</span> Data safety answers</b> <span class="sep">·</span> from 12 lines of config</p>
|
<p class="sc-foot" data-s="500"><b class="big"><span data-count="764">764</span> answers</b> <span class="sep">·</span> from 12 lines of config</p>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound apple encryption --dry</span></p>
|
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound apple encryption --dry</span></p>
|
||||||
<p class="t-err t-wrap" data-s="200">error: apple.usesNonExemptEncryption is not set in storebound.config.ts. Decide it with the export-compliance skill (false only if the app uses no encryption beyond the OS); storebound never guesses it.</p>
|
<p class="t-err t-wrap" data-s="300">error: apple.usesNonExemptEncryption is not set in storebound.config.ts. Decide it with the export-compliance skill (…); storebound never guesses it.</p>
|
||||||
<p class="ask" data-s><span>Ask the owner</span> Compliance answers are never guessed.</p>
|
<p class="sc-hand"><span>By hand</span> Play's form, question by question, for every data type you collect.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="sc sc-icons" id="sc-p4" role="tabpanel" aria-labelledby="sc-t4" data-cwd="examples/demo-game" tabindex="0" hidden>
|
<section class="sc sc-icons" id="sc-p4" role="tabpanel" aria-labelledby="sc-t4" data-cwd="examples/demo-game" tabindex="0" hidden>
|
||||||
|
<div class="sc-head">
|
||||||
|
<p class="sc-h">Every icon both stores and the web need</p>
|
||||||
|
<p class="sc-out">One 1024 px artwork became <b>21 icons</b>: App Store, Google Play, the Android launcher in 5 densities (round and legacy), Capacitor layers, web and favicons.</p>
|
||||||
|
</div>
|
||||||
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound icons</span></p>
|
<p class="t-cmd" data-s="type"><i>$</i> <span>storebound icons</span></p>
|
||||||
<div class="ic-row">
|
<div class="ic-row">
|
||||||
<figure class="ic-src" data-s="300"><img src="img/hero/i-src.webp" width="76" height="76" alt="The one artwork: a drop over a rainbow puddle" decoding="async"><figcaption>icon.png</figcaption></figure>
|
<figure class="ic-src" data-s="300"><img src="img/hero/i-src.webp" width="76" height="76" alt="The one artwork: a drop over a rainbow puddle" decoding="async"><figcaption>icon.png</figcaption></figure>
|
||||||
|
|
@ -222,7 +249,8 @@
|
||||||
<p data-s="20"><b>[icons]</b> store/icons/android/res/mipmap-xxxhdpi/ic_launcher.png <span class="kb">8.8 KB</span></p>
|
<p data-s="20"><b>[icons]</b> store/icons/android/res/mipmap-xxxhdpi/ic_launcher.png <span class="kb">8.8 KB</span></p>
|
||||||
<p data-s="20"><b>[icons]</b> icons done (4s)</p>
|
<p data-s="20"><b>[icons]</b> icons done (4s)</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="sc-foot" data-s="300"><b class="big"><span data-count="21">21</span> icons from one artwork</b> <span class="sep">·</span> App Store, Play, web, Capacitor, Android</p>
|
<p class="sc-foot" data-s="300"><b class="big"><span data-count="21">21</span> icons from one artwork</b> <span class="sep">·</span> in 4 s</p>
|
||||||
|
<p class="sc-hand"><span>By hand</span> 21 exports at exact sizes, each into the right folder.</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
49
site/main.js
49
site/main.js
|
|
@ -58,33 +58,44 @@
|
||||||
}
|
}
|
||||||
const reduce = matchMedia('(prefers-reduced-motion: reduce)');
|
const reduce = matchMedia('(prefers-reduced-motion: reduce)');
|
||||||
|
|
||||||
// Hero demo: four scenes of real CLI output. Auto-cycles while the hero is on screen and the page is visible,
|
// Hero demo: four scenes of real CLI output, each under a plain-language headline. Auto-cycles while the hero is on
|
||||||
// pauses on hover; a picked tab stops the cycle. Reduced motion: no animation, the tabs show each final frame.
|
// screen and the page is visible, pauses on hover; a picked tab stops the cycle. Every scene holds its final frame
|
||||||
|
// for HOLD_MS. Reduced motion: no animation, the tabs show each final frame.
|
||||||
|
// Steps: [data-s] elements appear in order (data-o overrides document order); data-s="type" types a command,
|
||||||
|
// a number is the pause after it in ms. data-flip="id ..." marks checklist rows fixed, data-pill sets the pill.
|
||||||
const demo = document.querySelector('[data-demo]');
|
const demo = document.querySelector('[data-demo]');
|
||||||
if (demo) {
|
if (demo) {
|
||||||
const tabs = [...demo.querySelectorAll('[role="tab"]')];
|
const tabs = [...demo.querySelectorAll('[role="tab"]')];
|
||||||
const panels = tabs.map(t => document.getElementById(t.getAttribute('aria-controls')));
|
const panels = tabs.map(t => document.getElementById(t.getAttribute('aria-controls')));
|
||||||
const cwd = demo.querySelector('[data-cwd]');
|
const cwd = demo.querySelector('[data-cwd]');
|
||||||
const SCENE_MS = [5200, 5600, 5800, 4800];
|
const HOLD_MS = 3400, START_MS = 250, CHAR_MS = 30, AFTER_TYPE_MS = 220;
|
||||||
const motion = !reduce.matches;
|
const motion = !reduce.matches;
|
||||||
let cur = 0, auto = motion, visible = false, hovered = false, run = 0;
|
let cur = 0, auto = motion, visible = false, hovered = false, run = 0;
|
||||||
const shown = () => visible && !document.hidden;
|
const shown = () => visible && !document.hidden;
|
||||||
const live = () => shown() && !hovered;
|
const live = () => shown() && !hovered;
|
||||||
const sync = () => demo.classList.toggle('is-held', !live());
|
const sync = () => demo.classList.toggle('is-held', !live());
|
||||||
const STOP = {};
|
const STOP = {};
|
||||||
// a wait that only counts down while the demo is on screen (hold: and not hovered); throws when a newer run started
|
// a wait that only counts down while the demo is on screen (and, while cycling, not hovered); throws when a newer run started
|
||||||
const wait = (ms, token, hold) => new Promise((done, fail) => {
|
const wait = (ms, token) => new Promise((done, fail) => {
|
||||||
let left = ms;
|
let left = ms;
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
const step = Math.min(left, 40);
|
const step = Math.min(left, 40);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (token !== run) return fail(STOP);
|
if (token !== run) return fail(STOP);
|
||||||
if (hold ? live() : shown()) left -= step;
|
if (auto ? live() : shown()) left -= step;
|
||||||
if (left <= 0) done(); else tick();
|
if (left <= 0) done(); else tick();
|
||||||
}, step);
|
}, step);
|
||||||
};
|
};
|
||||||
if (token !== run) fail(STOP); else if (ms <= 0) done(); else tick();
|
if (token !== run) fail(STOP); else if (ms <= 0) done(); else tick();
|
||||||
});
|
});
|
||||||
|
const steps = p => {
|
||||||
|
const els = [...p.querySelectorAll('[data-s]')];
|
||||||
|
return els.map((el, i) => ({ el, i, o: el.dataset.o ? Number(el.dataset.o) : i })).sort((a, b) => a.o - b.o || a.i - b.i).map(x => x.el);
|
||||||
|
};
|
||||||
|
const stepMs = el => el.dataset.s === 'type'
|
||||||
|
? el.querySelector('span').textContent.length * CHAR_MS + AFTER_TYPE_MS
|
||||||
|
: el.dataset.s === '' ? 22 : Number(el.dataset.s);
|
||||||
|
const sceneMs = panels.map(p => START_MS + steps(p).reduce((n, el) => n + stepMs(el), 0) + HOLD_MS);
|
||||||
const countUp = el => {
|
const countUp = el => {
|
||||||
const to = Number(el.dataset.count), t0 = performance.now(), dur = 700;
|
const to = Number(el.dataset.count), t0 = performance.now(), dur = 700;
|
||||||
const f = now => {
|
const f = now => {
|
||||||
|
|
@ -103,39 +114,37 @@
|
||||||
panels[k].hidden = !on;
|
panels[k].hidden = !on;
|
||||||
});
|
});
|
||||||
cwd.textContent = panels[i].dataset.cwd;
|
cwd.textContent = panels[i].dataset.cwd;
|
||||||
demo.style.setProperty('--sc-ms', SCENE_MS[i] + 'ms');
|
demo.style.setProperty('--sc-ms', sceneMs[i] + 'ms');
|
||||||
if (focus) tabs[i].focus();
|
if (focus) tabs[i].focus();
|
||||||
};
|
};
|
||||||
// plays one scene from empty to its final frame; returns how long it took (live time)
|
// plays one scene from empty to its final frame
|
||||||
const play = async (i, token) => {
|
const play = async (i, token) => {
|
||||||
const p = panels[i];
|
const p = panels[i];
|
||||||
const items = [...p.querySelectorAll('[data-s]')];
|
const items = steps(p);
|
||||||
for (const el of items) el.classList.remove('on', 'typing');
|
for (const el of items) el.classList.remove('on', 'typing');
|
||||||
|
for (const el of p.querySelectorAll('.fixed')) el.classList.remove('fixed');
|
||||||
for (const c of p.querySelectorAll('[data-count]')) c.textContent = '0';
|
for (const c of p.querySelectorAll('[data-count]')) c.textContent = '0';
|
||||||
delete p.dataset.pill;
|
delete p.dataset.pill;
|
||||||
p.classList.add('is-anim');
|
p.classList.add('is-anim');
|
||||||
demo.classList.add('is-ready');
|
demo.classList.add('is-ready');
|
||||||
let spent = 200;
|
await wait(START_MS, token);
|
||||||
await wait(200, token);
|
|
||||||
for (const el of items) {
|
for (const el of items) {
|
||||||
const s = el.dataset.s;
|
if (el.dataset.s === 'type') {
|
||||||
if (s === 'type') {
|
|
||||||
const n = el.querySelector('span').textContent.length;
|
const n = el.querySelector('span').textContent.length;
|
||||||
el.style.setProperty('--n', '0');
|
el.style.setProperty('--n', '0');
|
||||||
el.classList.add('typing', 'on');
|
el.classList.add('typing', 'on');
|
||||||
for (let k = 1; k <= n; k++) { el.style.setProperty('--n', String(k)); const ms = 12 + Math.random() * 14; spent += ms; await wait(ms, token); }
|
for (let k = 1; k <= n; k++) { el.style.setProperty('--n', String(k)); await wait(CHAR_MS, token); }
|
||||||
spent += 180; await wait(180, token);
|
await wait(AFTER_TYPE_MS, token);
|
||||||
el.classList.remove('typing');
|
el.classList.remove('typing');
|
||||||
} else {
|
} else {
|
||||||
el.classList.add('on');
|
el.classList.add('on');
|
||||||
if (el.dataset.pill) p.dataset.pill = el.dataset.pill;
|
if (el.dataset.pill) p.dataset.pill = el.dataset.pill;
|
||||||
|
for (const id of (el.dataset.flip ?? '').split(' ').filter(Boolean)) document.getElementById(id)?.classList.add('fixed');
|
||||||
const c = el.matches('[data-count]') ? el : el.querySelector('[data-count]');
|
const c = el.matches('[data-count]') ? el : el.querySelector('[data-count]');
|
||||||
if (c) countUp(c);
|
if (c) countUp(c);
|
||||||
const ms = s === '' ? 22 : Number(s);
|
await wait(stepMs(el), token);
|
||||||
spent += ms; await wait(ms, token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spent;
|
|
||||||
};
|
};
|
||||||
const restartBar = () => { demo.classList.remove('is-auto'); void demo.offsetWidth; if (auto) demo.classList.add('is-auto'); };
|
const restartBar = () => { demo.classList.remove('is-auto'); void demo.offsetWidth; if (auto) demo.classList.add('is-auto'); };
|
||||||
const cycle = async () => {
|
const cycle = async () => {
|
||||||
|
|
@ -144,8 +153,8 @@
|
||||||
for (;;) {
|
for (;;) {
|
||||||
select(cur, false);
|
select(cur, false);
|
||||||
restartBar();
|
restartBar();
|
||||||
const spent = await play(cur, token);
|
await play(cur, token);
|
||||||
await wait(SCENE_MS[cur] - spent, token, true);
|
await wait(HOLD_MS, token);
|
||||||
cur = (cur + 1) % tabs.length;
|
cur = (cur + 1) % tabs.length;
|
||||||
}
|
}
|
||||||
} catch (e) { if (e !== STOP) throw e; }
|
} catch (e) { if (e !== STOP) throw e; }
|
||||||
|
|
|
||||||
101
site/style.css
101
site/style.css
|
|
@ -285,8 +285,9 @@ h1 .hl { color: var(--accent); }
|
||||||
.install-note { margin-top: 10px; font-size: 13.5px; color: var(--muted); }
|
.install-note { margin-top: 10px; font-size: 13.5px; color: var(--muted); }
|
||||||
.install-note a { color: inherit; }
|
.install-note a { color: inherit; }
|
||||||
|
|
||||||
/* hero demo: four real CLI scenes in a dark window (same in both themes). Without JS every scene shows its final
|
/* hero demo: four real CLI scenes in a dark window (same in both themes). Each scene opens with a plain-language
|
||||||
frame; main.js adds .is-anim to play them and cycles the tabs while the hero is on screen. */
|
headline and outcome, then the proof. Without JS (or with reduced motion) every scene shows its final frame;
|
||||||
|
main.js adds .is-anim to play them and cycles the tabs while the hero is on screen. */
|
||||||
.demo { margin: 0; min-width: 0; position: relative; }
|
.demo { margin: 0; min-width: 0; position: relative; }
|
||||||
.demo::before { content: ""; position: absolute; inset: 8% -4% -6% 6%; z-index: -1; border-radius: 40px; background: color-mix(in srgb, var(--accent) 28%, transparent); filter: blur(60px); opacity: .55; }
|
.demo::before { content: ""; position: absolute; inset: 8% -4% -6% 6%; z-index: -1; border-radius: 40px; background: color-mix(in srgb, var(--accent) 28%, transparent); filter: blur(60px); opacity: .55; }
|
||||||
.demo-win { background: #0b1222; color: #e2e8f0; border: 1px solid rgb(148 163 184 / .18); border-radius: 14px; overflow: hidden;
|
.demo-win { background: #0b1222; color: #e2e8f0; border: 1px solid rgb(148 163 184 / .18); border-radius: 14px; overflow: hidden;
|
||||||
|
|
@ -303,45 +304,59 @@ h1 .hl { color: var(--accent); }
|
||||||
padding: 11px 10px 12px; cursor: pointer; border-radius: 8px 8px 0 0; white-space: nowrap; transition: color .15s ease, background-color .15s ease; }
|
padding: 11px 10px 12px; cursor: pointer; border-radius: 8px 8px 0 0; white-space: nowrap; transition: color .15s ease, background-color .15s ease; }
|
||||||
.sc-tabs button:hover { color: #e2e8f0; }
|
.sc-tabs button:hover { color: #e2e8f0; }
|
||||||
.sc-tabs button[aria-selected="true"] { color: #fff; background: rgb(255 255 255 / .05); }
|
.sc-tabs button[aria-selected="true"] { color: #fff; background: rgb(255 255 255 / .05); }
|
||||||
|
/* progress: the selected tab's underline fills over the scene's time (play + hold) */
|
||||||
|
.sc-tabs button::before { content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px; border-radius: 2px; background: rgb(129 140 248 / .22); opacity: 0; }
|
||||||
.sc-tabs button::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px; border-radius: 2px; background: #818cf8; transform: scaleX(0); transform-origin: left; }
|
.sc-tabs button::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px; border-radius: 2px; background: #818cf8; transform: scaleX(0); transform-origin: left; }
|
||||||
.sc-tabs button[aria-selected="true"]::after { transform: none; }
|
.sc-tabs button[aria-selected="true"]::after { transform: none; }
|
||||||
|
.demo.is-auto .sc-tabs button::before { opacity: 1; }
|
||||||
.sc-tabs button:focus-visible { outline: 2px solid #a5b4fc; outline-offset: -3px; }
|
.sc-tabs button:focus-visible { outline: 2px solid #a5b4fc; outline-offset: -3px; }
|
||||||
.demo.is-auto .sc-tabs button[aria-selected="true"]::after { animation: sc-bar var(--sc-ms, 6s) linear forwards; }
|
.demo.is-auto .sc-tabs button[aria-selected="true"]::after { animation: sc-bar var(--sc-ms, 6s) linear forwards; }
|
||||||
.demo.is-auto.is-held .sc-tabs button[aria-selected="true"]::after { animation-play-state: paused; }
|
.demo.is-auto.is-held .sc-tabs button[aria-selected="true"]::after { animation-play-state: paused; }
|
||||||
@keyframes sc-bar { from { transform: scaleX(0); } to { transform: none; } }
|
@keyframes sc-bar { from { transform: scaleX(0); } to { transform: none; } }
|
||||||
|
|
||||||
.sc-stage { position: relative; height: 404px; }
|
.sc-stage { position: relative; height: 548px; }
|
||||||
.sc { position: absolute; inset: 0; padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 0; overflow: hidden;
|
.sc { position: absolute; inset: 0; padding: 14px 16px 14px; display: flex; flex-direction: column; gap: 0; overflow: hidden;
|
||||||
font: 12px/1.65 var(--mono); color: #cbd5e1; outline: none; }
|
font: 12px/1.65 var(--mono); color: #cbd5e1; outline: none; }
|
||||||
.sc[hidden] { display: none; }
|
.sc[hidden] { display: none; }
|
||||||
|
.sc > * { flex-shrink: 0; }
|
||||||
.sc:focus-visible { box-shadow: inset 0 0 0 2px #a5b4fc; }
|
.sc:focus-visible { box-shadow: inset 0 0 0 2px #a5b4fc; }
|
||||||
.sc p { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
.sc p { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||||
.sc .t-wrap { white-space: normal; overflow-wrap: anywhere; }
|
.sc .t-wrap { white-space: normal; overflow-wrap: anywhere; }
|
||||||
|
|
||||||
|
/* the plain-language part: what storebound just did for you */
|
||||||
|
.sc-head { flex: none; margin: -14px -16px 12px; padding: 13px 16px 12px; background: linear-gradient(rgb(129 140 248 / .10), rgb(129 140 248 / .04)); border-bottom: 1px solid rgb(129 140 248 / .18); }
|
||||||
|
.sc .sc-head p { white-space: normal; overflow: visible; }
|
||||||
|
.sc-h { font: 700 16.5px/1.3 var(--font); letter-spacing: -.012em; color: #fff; text-wrap: balance; }
|
||||||
|
.sc-out { margin-top: 5px; font: 13.5px/1.5 var(--font); color: #aab6ca; text-wrap: pretty; }
|
||||||
|
.sc-out b { color: #fff; font-weight: 650; }
|
||||||
|
.sc-hand { margin-top: auto; padding-top: 10px; font: 12.5px/1.45 var(--font); color: #8391a7; white-space: normal !important; }
|
||||||
|
.sc-hand span { display: inline-block; font: 700 10.5px/1 var(--font); letter-spacing: .06em; text-transform: uppercase; color: #cbd5e1; background: rgb(148 163 184 / .14); padding: 4px 7px; border-radius: 5px; margin-right: 7px; vertical-align: 1px; }
|
||||||
|
|
||||||
.t-cmd { color: #f8fafc; margin-top: 8px; }
|
.t-cmd { color: #f8fafc; margin-top: 8px; }
|
||||||
.t-cmd:first-child, .sc-pill + .t-cmd { margin-top: 0; }
|
.t-cmd:first-child, .sc-head + .t-cmd { margin-top: 0; }
|
||||||
.t-cmd i { font-style: normal; color: #818cf8; }
|
.t-cmd i { font-style: normal; color: #818cf8; }
|
||||||
.t-cmd span { display: inline-block; vertical-align: bottom; overflow: hidden; white-space: nowrap; }
|
.t-cmd span { display: inline-block; vertical-align: bottom; overflow: hidden; white-space: nowrap; }
|
||||||
.t-out, .t-ok, .t-bad, .t-err, .t-note { padding-left: 2ch; }
|
.t-out, .t-ok, .t-bad, .t-err, .t-note { padding-left: 2ch; }
|
||||||
.t-out { color: #94a3b8; }
|
.t-out { color: #94a3b8; }
|
||||||
.sc b { font-weight: 400; color: #64748b; }
|
.sc b { font-weight: 400; color: #64748b; }
|
||||||
|
.sc .sc-out b { font-weight: 650; color: #fff; }
|
||||||
.t-ok { color: #86efac; }
|
.t-ok { color: #86efac; }
|
||||||
.t-ok b { color: #4ade80; }
|
.t-ok b { color: #4ade80; }
|
||||||
.t-strong { font-weight: 700; }
|
.t-strong { font-weight: 700; }
|
||||||
.t-bad { color: #fca5a5; }
|
.t-bad { color: #fca5a5; }
|
||||||
.t-err { color: #f87171; font-weight: 600; }
|
.t-err { color: #f87171; font-weight: 600; }
|
||||||
.t-note { color: #64748b; font-style: italic; }
|
.t-note { color: #7c8aa3; font-style: italic; }
|
||||||
.sc-foot { margin-top: 10px; font: 13px/1.4 var(--font); color: #94a3b8; }
|
.sc-foot { margin-top: 10px; font: 13px/1.4 var(--font); color: #94a3b8; white-space: normal !important; }
|
||||||
.sc-foot .big { font-weight: 700; color: #fff; font-size: 15px; letter-spacing: -.01em; }
|
.sc-foot .big { font-weight: 700; color: #fff; font-size: 15px; letter-spacing: -.01em; }
|
||||||
.sc-foot .big span { font-variant-numeric: tabular-nums; color: #a5b4fc; }
|
.sc-foot .big span { font-variant-numeric: tabular-nums; color: #a5b4fc; }
|
||||||
.sc-foot .sep { color: #475569; margin: 0 2px; }
|
.sc-foot .sep { color: #475569; margin: 0 2px; }
|
||||||
.sc-foot code { background: rgb(148 163 184 / .14); color: #e2e8f0; padding: .05em .35em; border-radius: 4px; font-size: 12px; }
|
|
||||||
|
|
||||||
/* streaming log: newest line at the bottom, older lines scroll off the top */
|
/* streaming log: newest line at the bottom, older lines scroll off the top */
|
||||||
.tick { display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden; height: calc(3 * 1.65em); margin: 2px 0 10px; padding-left: 2ch; color: #64748b; flex: none;
|
.tick { display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden; height: calc(2 * 1.65em); margin: 2px 0 10px; padding-left: 2ch; color: #64748b; flex: none;
|
||||||
-webkit-mask-image: linear-gradient(transparent, #000 45%); mask-image: linear-gradient(transparent, #000 45%); }
|
-webkit-mask-image: linear-gradient(transparent, #000 60%); mask-image: linear-gradient(transparent, #000 60%); }
|
||||||
.tick p { flex: none; padding-left: 0; }
|
.tick p { flex: none; padding-left: 0; }
|
||||||
.tick b { color: #475569; }
|
.tick b { color: #475569; }
|
||||||
.tick-csv { height: calc(7 * 1.6em); font-size: 11px; line-height: 1.6; margin: 6px 0 4px; padding: 6px 10px; border-radius: 8px; background: rgb(148 163 184 / .06); -webkit-mask-image: linear-gradient(transparent, #000 30%); mask-image: linear-gradient(transparent, #000 30%); }
|
.tick-csv { height: calc(9 * 1.6em); font-size: 11px; line-height: 1.6; margin: 6px 0 4px; padding: 6px 10px; border-radius: 8px; background: rgb(148 163 184 / .06); -webkit-mask-image: linear-gradient(transparent, #000 30%); mask-image: linear-gradient(transparent, #000 30%); }
|
||||||
.tick-csv p { color: #64748b; }
|
.tick-csv p { color: #64748b; }
|
||||||
.tick-csv em { font-style: normal; color: #4ade80; font-weight: 700; }
|
.tick-csv em { font-style: normal; color: #4ade80; font-weight: 700; }
|
||||||
.tick-csv .lbl { color: #94a3b8; }
|
.tick-csv .lbl { color: #94a3b8; }
|
||||||
|
|
@ -351,23 +366,45 @@ h1 .hl { color: var(--accent); }
|
||||||
.tick-ic .kb { color: #64748b; }
|
.tick-ic .kb { color: #64748b; }
|
||||||
|
|
||||||
/* screenshots */
|
/* screenshots */
|
||||||
.shot-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 7px; }
|
.shot-grid { list-style: none; margin: 0; padding: 0; max-width: 480px; display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 7px; }
|
||||||
.shot-grid li { position: relative; aspect-ratio: 1320 / 2868; border-radius: 6px; overflow: hidden; background: #1e293b; box-shadow: 0 0 0 1px rgb(255 255 255 / .06); }
|
.shot-grid li { position: relative; aspect-ratio: 1320 / 2868; border-radius: 6px; overflow: hidden; background: #1e293b; box-shadow: 0 0 0 1px rgb(255 255 255 / .06); }
|
||||||
.shot-grid img { width: 100%; height: 100%; object-fit: cover; }
|
.shot-grid img { width: 100%; height: 100%; object-fit: cover; }
|
||||||
.shot-grid li span { position: absolute; left: 3px; bottom: 3px; font: 700 8.5px/1 var(--font); letter-spacing: .04em; color: #0f172a; background: rgb(255 255 255 / .88); padding: 2px 3px; border-radius: 3px; }
|
.shot-grid li span { position: absolute; left: 3px; bottom: 3px; font: 700 8.5px/1 var(--font); letter-spacing: .04em; color: #0f172a; background: rgb(255 255 255 / .88); padding: 2px 3px; border-radius: 3px; }
|
||||||
|
|
||||||
/* pre-flight */
|
/* pre-flight: a checklist of what `storebound check` verified; the terminal strip above it shows the commands */
|
||||||
.sc-pill { position: absolute; top: 12px; right: 14px; font: 700 11px/1 var(--font); letter-spacing: .02em; overflow: visible; }
|
.pf-strip { position: relative; flex: none; display: flex; align-items: flex-end; gap: 10px; padding: 7px 10px 6px; margin-bottom: 8px; border-radius: 8px; background: rgb(0 0 0 / .28); box-shadow: inset 0 0 0 1px rgb(148 163 184 / .1); }
|
||||||
.sc-pill span { display: none; padding: 5px 9px; border-radius: 999px; }
|
.tick-pf { flex: 1; min-width: 0; margin: 0; padding-left: 0; height: calc(2 * 1.65em); }
|
||||||
|
.tick-pf .t-cmd { margin-top: 0; }
|
||||||
|
.tick-pf .t-out, .tick-pf .t-ok, .tick-pf .t-err, .tick-pf .t-note { padding-left: 2ch; }
|
||||||
|
.sc-pill { flex: none; font: 700 11px/1 var(--font); letter-spacing: .02em; overflow: visible; align-self: center; }
|
||||||
|
.sc-pill span { display: none; padding: 5px 9px; border-radius: 999px; white-space: nowrap; }
|
||||||
.sc.is-anim .sc-pill span { animation: pop .3s cubic-bezier(.2, .9, .3, 1.3); }
|
.sc.is-anim .sc-pill span { animation: pop .3s cubic-bezier(.2, .9, .3, 1.3); }
|
||||||
.pill-bad { color: #fecaca; background: rgb(239 68 68 / .2); box-shadow: inset 0 0 0 1px rgb(239 68 68 / .45); }
|
.pill-bad { color: #fecaca; background: rgb(239 68 68 / .2); box-shadow: inset 0 0 0 1px rgb(239 68 68 / .45); }
|
||||||
.pill-good { color: #bbf7d0; background: rgb(34 197 94 / .2); box-shadow: inset 0 0 0 1px rgb(34 197 94 / .5); }
|
.pill-good { color: #bbf7d0; background: rgb(34 197 94 / .2); box-shadow: inset 0 0 0 1px rgb(34 197 94 / .5); }
|
||||||
.sc:not(.is-anim) .pill-good, .sc[data-pill="bad"] .pill-bad, .sc[data-pill="good"] .pill-good { display: inline-block; }
|
.sc:not(.is-anim) .pill-good, .sc[data-pill="bad"] .pill-bad, .sc[data-pill="good"] .pill-good { display: inline-block; }
|
||||||
@keyframes pop { from { transform: scale(.6); opacity: 0; } }
|
@keyframes pop { from { transform: scale(.6); opacity: 0; } }
|
||||||
|
|
||||||
/* store forms */
|
.pf-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; font: 12.5px/1.3 var(--font); }
|
||||||
.ask { margin-top: 10px; font: 13px/1.4 var(--font); color: #fde68a; white-space: normal !important; }
|
.pf-row { display: grid; grid-template-columns: 18px minmax(0, 16em) minmax(0, 1fr); align-items: center; gap: 2px 10px; padding: 3px 8px; border-radius: 7px; background: rgb(148 163 184 / .045); transition: background-color .3s ease; }
|
||||||
.ask span { display: inline-block; font: 700 11px/1 var(--font); letter-spacing: .06em; text-transform: uppercase; color: #451a03; background: #fbbf24; padding: 5px 8px; border-radius: 6px; margin-right: 8px; vertical-align: 1px; }
|
.pf-ic { width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; font: 800 11px/1 var(--font); color: #052e16; background: #4ade80; }
|
||||||
|
.pf-ic::before { content: "✓"; }
|
||||||
|
.pf-what { color: #e2e8f0; font-weight: 600; min-width: 0; }
|
||||||
|
.pf-what small { display: block; font: 400 10px/1.3 var(--mono); color: #64748b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||||
|
.pf-det { color: #b4c0d3; min-width: 0; }
|
||||||
|
.pf-det code { white-space: nowrap; font-size: 11px; background: rgb(148 163 184 / .16); color: #f1f5f9; padding: .05em .35em; border-radius: 4px; }
|
||||||
|
.pf-det em { display: inline-block; margin-left: 4px; font: 600 10px/1 var(--font); font-style: normal; letter-spacing: .02em; color: #fcd34d; background: rgb(251 191 36 / .12); box-shadow: inset 0 0 0 1px rgb(251 191 36 / .3); padding: 3px 6px; border-radius: 999px; vertical-align: 1px; white-space: nowrap; }
|
||||||
|
.d-bad { display: none; }
|
||||||
|
.pf-na .pf-ic { background: #334155; color: #94a3b8; }
|
||||||
|
.pf-na .pf-ic::before { content: "–"; }
|
||||||
|
.pf-na .pf-what, .pf-na .pf-det { color: #64748b; }
|
||||||
|
/* while playing, a [data-bad] row shows the problem and its fix until the re-check flips it (.fixed) */
|
||||||
|
.sc.is-anim .pf-row[data-bad]:not(.fixed) { background: rgb(239 68 68 / .1); box-shadow: inset 2px 0 0 #f87171; }
|
||||||
|
.sc.is-anim .pf-row[data-bad]:not(.fixed) .pf-ic { background: #f87171; color: #450a0a; }
|
||||||
|
.sc.is-anim .pf-row[data-bad]:not(.fixed) .pf-ic::before { content: "✗"; }
|
||||||
|
.sc.is-anim .pf-row[data-bad]:not(.fixed) .d-bad { display: inline; color: #fecaca; }
|
||||||
|
.sc.is-anim .pf-row[data-bad]:not(.fixed) .d-ok { display: none; }
|
||||||
|
.sc.is-anim .pf-row.fixed { background: rgb(34 197 94 / .1); }
|
||||||
|
.sc.is-anim .pf-row.fixed .pf-ic { animation: pop .35s cubic-bezier(.2, .9, .3, 1.3); }
|
||||||
|
|
||||||
/* icons */
|
/* icons */
|
||||||
.ic-row { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
|
.ic-row { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
|
||||||
|
|
@ -394,9 +431,21 @@ h1 .hl { color: var(--accent); }
|
||||||
@keyframes caret { 50% { opacity: 0; } }
|
@keyframes caret { 50% { opacity: 0; } }
|
||||||
/* hide the first scene's lines until main.js starts it, so the final frame does not flash first */
|
/* hide the first scene's lines until main.js starts it, so the final frame does not flash first */
|
||||||
@media (prefers-reduced-motion: no-preference) { .js .demo:not(.is-ready) .sc [data-s] { opacity: 0; } }
|
@media (prefers-reduced-motion: no-preference) { .js .demo:not(.is-ready) .sc [data-s] { opacity: 0; } }
|
||||||
|
/* no JS: the four scenes stack, each in its final frame */
|
||||||
|
html:not(.js) .sc-tabs { display: none; }
|
||||||
|
html:not(.js) .sc-stage { height: auto; }
|
||||||
|
html:not(.js) .sc, html:not(.js) .sc[hidden] { position: relative; display: flex; }
|
||||||
|
html:not(.js) .sc + .sc { border-top: 1px solid rgb(148 163 184 / .18); }
|
||||||
|
html:not(.js) .sc-hand { margin-top: 10px; }
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.sc-stage { height: 412px; }
|
.sc-stage { height: 592px; }
|
||||||
.sc { padding: 12px 12px 14px; font-size: 11px; }
|
.sc { padding: 12px 12px 12px; font-size: 11px; }
|
||||||
|
.sc-head { margin: -12px -12px 10px; padding: 11px 12px 10px; }
|
||||||
|
.sc-h { font-size: 15.5px; }
|
||||||
|
.sc-out { font-size: 13px; }
|
||||||
|
.sc-hand { font-size: 12px; }
|
||||||
.sc-tabs { padding: 0 6px; }
|
.sc-tabs { padding: 0 6px; }
|
||||||
.sc-tabs button { font-size: 12.5px; padding: 10px 7px 11px; }
|
.sc-tabs button { font-size: 12.5px; padding: 10px 7px 11px; }
|
||||||
.shot-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
|
.shot-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
|
||||||
|
|
@ -404,13 +453,21 @@ h1 .hl { color: var(--accent); }
|
||||||
.sc-foot { font-size: 12px; }
|
.sc-foot { font-size: 12px; }
|
||||||
.sc-foot .big { font-size: 14px; display: block; }
|
.sc-foot .big { font-size: 14px; display: block; }
|
||||||
.sc-foot .big + .sep { display: none; }
|
.sc-foot .big + .sep { display: none; }
|
||||||
.tick-csv { font-size: 10px; height: calc(6 * 1.6em); }
|
.tick-csv { font-size: 10px; height: calc(9 * 1.6em); }
|
||||||
|
.tick-ic { height: calc(7 * 1.65em); }
|
||||||
|
.pf-strip { padding: 5px 8px 4px; }
|
||||||
|
.tick-pf { height: 1.65em; }
|
||||||
|
.pf-list { font-size: 12px; gap: 3px; }
|
||||||
|
.pf-row { grid-template-columns: 18px minmax(0, 1fr); padding: 4px 7px; gap: 0 8px; }
|
||||||
|
.pf-ic { width: 17px; height: 17px; grid-row: span 2; align-self: start; margin-top: 1px; }
|
||||||
|
.pf-what small { display: none; }
|
||||||
|
.pf-det { font-size: 11.5px; }
|
||||||
|
.pf-na { display: none !important; }
|
||||||
.ic-row { gap: 8px; }
|
.ic-row { gap: 8px; }
|
||||||
.ic-src img { width: 56px; height: 56px; border-radius: 13px; }
|
.ic-src img { width: 56px; height: 56px; border-radius: 13px; }
|
||||||
.ic-src figcaption { display: none; }
|
.ic-src figcaption { display: none; }
|
||||||
.ic-out { gap: 10px 4px; }
|
.ic-out { gap: 10px 4px; }
|
||||||
.ic-out small { font-size: 9px; }
|
.ic-out small { font-size: 9px; }
|
||||||
.ask { font-size: 12px; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.trust { list-style: none; padding: 22px 0 0; margin: 56px 0 0; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 8px 0; font-size: 14px; color: var(--ink-2); }
|
.trust { list-style: none; padding: 22px 0 0; margin: 56px 0 0; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 8px 0; font-size: 14px; color: var(--ink-2); }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue