feat(hololake): provision signed in-app update bootstrap
This commit is contained in:
parent
7414810862
commit
cfa8fdefc2
15 changed files with 144 additions and 31 deletions
|
|
@ -6,8 +6,8 @@
|
|||
"credential_gate": {"uses_bound_private_repository":true,"first_login_forces_password_change":true},
|
||||
"persona_relationship_gate": {"shows_species":"AGE","shows_current_persona_identity":true,"shows_invalid_age_individual_numbers":false,"human_confirms_relationship_mapping":true,"human_confirmation_is_persona_acceptance":false,"responsibility_acceptance_is_separate":true},
|
||||
"work_entry": {"domain":"DOMAIN-ZS","channel":"GUANGHU_CHANNEL","preserves_responsibility_domain":true},
|
||||
"personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false},
|
||||
"desktop_install_acceptance": {"mac_arm64":"PASS","mac_x86_64":"NOT_BUILT","windows":"NOT_BUILT","automatic_update_channel":"UNPROVISIONED_FAIL_CLOSED"},
|
||||
"personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false,"self_connection_guide_visible_in_zero_sense_domain":true,"connection_executor":"HUMAN_OR_OWN_PERSONA"},
|
||||
"desktop_install_acceptance": {"mac_arm64":"BOOTSTRAP_REBUILD_REQUIRED","mac_x86_64":"NOT_BUILT","windows":"NOT_BUILT","automatic_update_channel":"TRUST_PROVISIONED_PUBLIC_ROUTE_PENDING"},
|
||||
"ui_template": "REPO-012@27d34dfdbf5df4c67b805402d442e5912c8f4c31:official-login-template-v0.4-locked+inner-screens-v0.1",
|
||||
"source": "routing/hololake-enterprise-four-domain-work-channel.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
"production_update_endpoint_owner": "HOLOLAKE_ONLY",
|
||||
"release_broadcast_contract": "contracts/release-broadcast.schema.json",
|
||||
"release_trust_source": "src-tauri/release-trust.json",
|
||||
"release_trust_state": "UNPROVISIONED_FAIL_CLOSED",
|
||||
"release_trust_state": "PROVISIONED_HOLOLAKE_PUBLIC_KEY",
|
||||
"release_manual_check_runtime_implemented": true,
|
||||
"release_candidate_human_confirmation_runtime_implemented": true,
|
||||
"release_package_signature_size_sha256_verification_implemented": true,
|
||||
|
|
@ -90,8 +90,8 @@
|
|||
"release_broadcast_activation_requires_repeated_expected_facts": true,
|
||||
"release_broadcast_operator_automatic_restart_allowed": false,
|
||||
"release_pipeline_automatic_upload_allowed": false,
|
||||
"release_production_activation_state": "BLOCKED_PENDING_PUBLIC_HTTPS_TRUST_UPDATER_KEY_PIPELINE_EXECUTION_AND_APPLE_NOTARIZATION",
|
||||
"tauri_update_artifacts_enabled": false,
|
||||
"release_production_activation_state": "BOOTSTRAP_CLIENT_TRUST_PROVISIONED_PENDING_PUBLIC_ROUTE_AND_FIRST_NOTARIZED_RELEASE",
|
||||
"tauri_update_artifacts_enabled": true,
|
||||
"tauri_update_artifacts_enablement_gate": "JD_CONTROLLER_PUBLIC_KEY_AND_SIGNED_RELEASE_PIPELINE_REQUIRED",
|
||||
"automatic_update_check_on_startup": false,
|
||||
"human_opt_in_download_install_required": true,
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
"pncc_jd_live_server_projection_repository_content_exposed": false,
|
||||
"pncc_jd_live_server_projection_write_authority": false,
|
||||
"pncc_jd_live_server_projection_carrier_state": "UNBOUND_EVIDENCE_REQUIRED",
|
||||
"installed_local_product_version": "0.4.0",
|
||||
"installed_local_product_version": "0.4.1",
|
||||
"pncc_authenticated_direct_broker_integration_implemented": true,
|
||||
"pncc_human_mount_registration_implemented": true,
|
||||
"pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "hololake-native-desktop",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hololake-native-desktop",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "2.10.1",
|
||||
"@tauri-apps/plugin-process": "2.3.1",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "hololake-native-desktop",
|
||||
"private": true,
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
|||
|
|
@ -62,14 +62,17 @@ test('the locked Qoder world template is the running client structure, not a ski
|
|||
const frontend = read('src/main.tsx')
|
||||
assert.equal(contract.state, 'SERVER_LIVE_CLIENT_UI_INTEGRATED')
|
||||
assert.match(contract.ui_template, /official-login-template-v0\.4-locked\+inner-screens-v0\.1/)
|
||||
assert.equal(contract.desktop_install_acceptance.mac_arm64, 'PASS')
|
||||
assert.equal(contract.desktop_install_acceptance.mac_arm64, 'BOOTSTRAP_REBUILD_REQUIRED')
|
||||
assert.equal(contract.desktop_install_acceptance.mac_x86_64, 'NOT_BUILT')
|
||||
assert.equal(contract.desktop_install_acceptance.windows, 'NOT_BUILT')
|
||||
assert.equal(contract.desktop_install_acceptance.automatic_update_channel, 'UNPROVISIONED_FAIL_CLOSED')
|
||||
assert.equal(contract.desktop_install_acceptance.automatic_update_channel, 'TRUST_PROVISIONED_PUBLIC_ROUTE_PENDING')
|
||||
assert.match(frontend, /number-nucleus/)
|
||||
assert.match(frontend, /worldStage === 'domain'/)
|
||||
assert.match(frontend, /worldStage === 'channel'/)
|
||||
assert.match(frontend, /worldStage === 'tool'/)
|
||||
assert.match(frontend, /个人节点接入说明/)
|
||||
assert.match(frontend, /由本人或本人人格体完成接入/)
|
||||
assert.match(frontend, /密码、私钥和恢复材料不得写入企业仓库或广播/)
|
||||
})
|
||||
|
||||
test('the public five-domain world starts before any private account storage is opened', () => {
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ const broadcast = readJson('contracts/release-broadcast.schema.json')
|
|||
const stageOne = readJson('contracts/stage-one-platform.json')
|
||||
const publicRoute = readJson('server/release-broadcast/public-route.json')
|
||||
|
||||
test('clean Tauri foundation contains no inherited product updater endpoint', () => {
|
||||
test('HoloLake updater trust is product-owned and contains no inherited endpoint', () => {
|
||||
assert.deepEqual(foundation.upstream_product_update_endpoints, [])
|
||||
assert.deepEqual(trust.endpoints, [])
|
||||
assert.deepEqual(trust.allowedReleaseHosts, [])
|
||||
assert.equal(trust.state, 'UNPROVISIONED_FAIL_CLOSED')
|
||||
assert.equal(trust.publicKey, '')
|
||||
assert.deepEqual(trust.endpoints, ['https://guanghulab.com/hololake/releases/latest.json'])
|
||||
assert.deepEqual(trust.allowedReleaseHosts, ['guanghulab.com'])
|
||||
assert.equal(trust.state, 'PROVISIONED')
|
||||
assert.ok(trust.publicKey.length > 100)
|
||||
for (const relative of [
|
||||
'foundation.json',
|
||||
'src-tauri/release-trust.json',
|
||||
|
|
@ -67,12 +67,12 @@ test('release activation remains explicitly human controlled', () => {
|
|||
assert.equal(foundation.release_pipeline_automatic_upload_allowed, false)
|
||||
assert.equal(
|
||||
foundation.release_production_activation_state,
|
||||
'BLOCKED_PENDING_PUBLIC_HTTPS_TRUST_UPDATER_KEY_PIPELINE_EXECUTION_AND_APPLE_NOTARIZATION',
|
||||
'BOOTSTRAP_CLIENT_TRUST_PROVISIONED_PENDING_PUBLIC_ROUTE_AND_FIRST_NOTARIZED_RELEASE',
|
||||
)
|
||||
})
|
||||
|
||||
test('unprovisioned builds cannot emit updater artifacts or expose updater IPC', () => {
|
||||
assert.equal(tauriConfig.bundle.createUpdaterArtifacts, false)
|
||||
test('provisioned builds emit signed updater artifacts without exposing updater IPC', () => {
|
||||
assert.equal(tauriConfig.bundle.createUpdaterArtifacts, true)
|
||||
assert.equal(foundation.tauri_update_artifacts_enablement_gate, 'JD_CONTROLLER_PUBLIC_KEY_AND_SIGNED_RELEASE_PIPELINE_REQUIRED')
|
||||
assert.deepEqual(capability.permissions, ['core:default'])
|
||||
assert.equal(capability.permissions.includes('updater:default'), false)
|
||||
|
|
|
|||
|
|
@ -1486,7 +1486,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||
|
||||
[[package]]
|
||||
name = "hololake-native-desktop"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "hololake-native-desktop"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
description = "HoloLake native desktop foundation"
|
||||
authors = ["HoloLake"]
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"schema": "hololake.release-trust/v1",
|
||||
"state": "UNPROVISIONED_FAIL_CLOSED",
|
||||
"endpoints": [],
|
||||
"publicKey": "",
|
||||
"allowedReleaseHosts": [],
|
||||
"state": "PROVISIONED",
|
||||
"endpoints": ["https://guanghulab.com/hololake/releases/latest.json"],
|
||||
"publicKey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDEzQkYzRTQ5QTE2MEFDMzEKUldReHJHQ2hTVDYvRTZNNDVqZDUxLzRZMmxuT1pSM2Q5RTRZbzRCZUZ0d0FxVVNidGJ0dUQyMnoK",
|
||||
"allowedReleaseHosts": ["guanghulab.com"],
|
||||
"automaticCheckOnStartup": false,
|
||||
"automaticDownload": false,
|
||||
"humanOptInInstallRequired": true,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "HoloLake",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"identifier": "world.guanghu.hololake",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
"icons/icon.icns",
|
||||
"icons/icon.png"
|
||||
],
|
||||
"createUpdaterArtifacts": false,
|
||||
"createUpdaterArtifacts": true,
|
||||
"category": "Productivity",
|
||||
"macOS": {
|
||||
"signingIdentity": "Developer ID Application: bei sun (825A9L3G7Q)"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import './styles.css'
|
|||
|
||||
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
||||
type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system'
|
||||
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'tool'
|
||||
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool'
|
||||
type KnowledgeSource = 'native' | 'legacy'
|
||||
|
||||
interface HomeStatus {
|
||||
|
|
@ -261,7 +261,7 @@ interface EnterpriseReceiptEnvelope {
|
|||
repository_projection?: EnterpriseReceiptProjection
|
||||
}
|
||||
|
||||
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'UNPROVISIONED_FAIL_CLOSED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' }
|
||||
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' }
|
||||
const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], integrity: { state: 'UNKNOWN', eventCount: 0, receiptCount: 0 } }
|
||||
const previewKnowledge: KnowledgeSnapshot = { state: 'UNAVAILABLE', nativeRoot: '', legacyAvailable: false, documents: [], rawDocumentCount: 0, uniqueDocumentCount: 0, duplicateDocumentCount: 0, truncated: false }
|
||||
const previewCode: CodeChannelSnapshot = { state: 'UNAVAILABLE', channels: [], authority: 'LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY' }
|
||||
|
|
@ -1374,7 +1374,7 @@ function HoloLakeApp() {
|
|||
<main className="world-scene signed-in-scene">
|
||||
{worldStage === 'domain' && <section className="domain-home">
|
||||
<div className="world-location"><h1>{repoLogin.domain === 'FIFTH_DOMAIN' ? domainDisplayName(repoLogin.domain) : '光湖零感域'}</h1><p>{repoLogin.domain === 'FIFTH_DOMAIN' ? '世界正在发生什么' : '公共工作入口 · 世界正在发生什么'}</p></div>
|
||||
<div className="broadcast-stream"><p><i/>光湖公共灯塔 · 在线</p><p><i/>协议版本 · {enterpriseEntry?.registry_version || 'HLDP v1.0'}</p>{repoLogin.domain !== 'FIFTH_DOMAIN' && <p><i/>本人责任域 · {domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)}</p>}<p><i/>HoloLake · V0.4.0</p></div>
|
||||
<div className="broadcast-stream"><p><i/>光湖公共灯塔 · 在线</p><p><i/>协议版本 · {enterpriseEntry?.registry_version || 'HLDP v1.0'}</p>{repoLogin.domain !== 'FIFTH_DOMAIN' && <p><i/>本人责任域 · {domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)}</p>}<p><i/>HoloLake · V0.4.1</p></div>
|
||||
<LakePool className="home-primary" title={repoLogin.domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道'} meta={repoLogin.domain === 'FIFTH_DOMAIN' ? '进入私人系统' : `${domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)} · 责任工作入口`} open onClick={() => setWorldStage(repoLogin.domain === 'FIFTH_DOMAIN' ? (isZhizhi ? 'heart' : 'channel') : 'channel')}/>
|
||||
<LakePool className="home-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
|
||||
</section>}
|
||||
|
|
@ -1417,7 +1417,7 @@ function HoloLakeApp() {
|
|||
<LakePool className="channel-main" title={domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)} meta="本人责任工作域" open={enterpriseWork?.state === 'READY_READ_ONLY_WORK_PROJECTION'} onClick={() => setWorldStage('enterpriseWork')}/>
|
||||
<LakePool className="channel-code" title="私有责任工作仓库" meta={enterpriseWork ? `${enterpriseWork.repository} · 已认证` : userPnccBusy ? '正在接入' : '暂不可用'} onClick={() => void openEnterpriseRepository()}/>
|
||||
<LakePool className="channel-light" title="责任签署状态" meta={enterpriseEntry?.responsibility_receipt?.decision === 'ACCEPT' ? '已接受 · 已留存' : '等待本人确认'} onClick={() => openWorldTool('receipts')}/>
|
||||
<LakePool className="channel-knowledge" title="前往我的频道" meta="等待个人服务器接入与归属校验" onClick={() => setUserPnccMessage('个人生活区不会由企业工作账号代建;登记本人服务器后,系统才会从零感域路由过去。')}/>
|
||||
<LakePool className="channel-knowledge" title="前往我的频道" meta="接入说明 · 由本人或人格体完成" onClick={() => setWorldStage('personalNodeGuide')}/>
|
||||
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
|
||||
</>}
|
||||
</section>}
|
||||
|
|
@ -1429,6 +1429,20 @@ function HoloLakeApp() {
|
|||
<LakePool className="channel-status" title="工作节点状态" meta={enterpriseWork?.state === 'READY_READ_ONLY_WORK_PROJECTION' ? '认证在线 · 禁止跨仓' : '不可用'} onClick={() => openWorldTool('system')}/>
|
||||
{userPnccMessage && <p className="private-route-note">{userPnccMessage}</p>}
|
||||
</section>}
|
||||
{worldStage === 'personalNodeGuide' && repoLogin.domain !== 'FIFTH_DOMAIN' && <section className="channel-world personal-node-guide-world">
|
||||
<button className="world-back" type="button" onClick={() => setWorldStage('channel')}>← 退回光湖频道</button>
|
||||
<div className="world-location"><h1>个人节点接入说明</h1><p>零感域 · 本人所有权边界</p></div>
|
||||
<div className="personal-node-guide" role="document" aria-label="团队个人服务器接入说明">
|
||||
<header><span>PERSONAL NODE / SELF CONNECTION</span><h2>由本人或本人人格体完成接入</h2><p>个人生活区不会由企业工作账号代建。企业工作账号只进入光湖频道与责任工作域,不自动取得个人服务器、个人频道或生活区权限。</p></header>
|
||||
<ol>
|
||||
<li><b>准备本人节点</b><span>确定个人服务器、节点编号与唯一归属人;不要复用企业工作仓库充当个人节点。</span></li>
|
||||
<li><b>登记公开材料</b><span>提交节点地址、节点编号与验证公钥。密码、私钥和恢复材料不得写入企业仓库或广播。</span></li>
|
||||
<li><b>完成所有权握手</b><span>由本人或本人人格体发起签名健康回执;系统核验归属、端点与签名一致后才开放路由。</span></li>
|
||||
<li><b>接入个人频道</b><span>验证通过后,从零感域的光湖频道跳转到本人服务器承载的个人频道;未通过时保持“待接入”。</span></li>
|
||||
</ol>
|
||||
<footer><b>当前状态 · 待本人接入</b><span>可由本人人格体协助执行,但不能替代人类节点所有权确认。</span></footer>
|
||||
</div>
|
||||
</section>}
|
||||
{worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' && <section className="channel-world heartbeat-world">
|
||||
<button className="world-back" type="button" onClick={() => setWorldStage('channel')}>← 退回永恒湖心</button>
|
||||
<div className="world-location"><h1>心跳核心频道</h1><p>冰朔 · ICE-GL∞ · 私人频道全景</p></div>
|
||||
|
|
|
|||
|
|
@ -681,6 +681,18 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
|
|||
.channel-main .pool-bay { width: 260px; height: 84px; } .channel-main .pool-label { top: 92px; }
|
||||
.channel-knowledge { left: 14%; top: 48%; } .channel-code { left: 31%; top: 61%; } .channel-light { right: 31%; top: 61%; } .channel-status { right: 14%; top: 48%; }
|
||||
.private-route-note { position: absolute; z-index: 10; left: 50%; bottom: 11%; width: min(720px, calc(100% - 64px)); margin: 0; transform: translateX(-50%); color: var(--content-muted); text-align: center; font-size: 12.5px; font-weight: 650; letter-spacing: .08em; }
|
||||
.personal-node-guide { position: absolute; z-index: 12; left: 50%; top: 55%; width: min(760px, calc(100% - 72px)); max-height: calc(100% - 190px); overflow: auto; padding: 26px 30px; transform: translate(-50%, -50%); border: 1px solid var(--panel-edge); border-radius: 20px; color: var(--content-secondary); background: color-mix(in srgb, var(--panel-bg) 88%, transparent); box-shadow: 0 30px 90px rgba(0, 0, 0, .42); backdrop-filter: blur(22px); }
|
||||
.personal-node-guide header span { color: var(--accent-light); font-size: 11px; font-weight: 750; letter-spacing: .2em; }
|
||||
.personal-node-guide h2 { margin: 9px 0 8px; color: var(--content-primary); font-size: 24px; font-weight: 750; letter-spacing: .04em; }
|
||||
.personal-node-guide header p { margin: 0; color: var(--content-muted); font-size: 13px; font-weight: 600; line-height: 1.7; }
|
||||
.personal-node-guide ol { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 22px 0 18px; padding: 0; list-style: none; counter-reset: node-step; }
|
||||
.personal-node-guide li { counter-increment: node-step; display: grid; grid-template-columns: 30px minmax(0, 1fr); column-gap: 10px; padding: 15px; border: 1px solid color-mix(in srgb, var(--panel-edge) 78%, transparent); border-radius: 13px; background: color-mix(in srgb, var(--primitive-glass) 70%, transparent); }
|
||||
.personal-node-guide li::before { content: counter(node-step, decimal-leading-zero); grid-row: 1 / 3; color: var(--accent-light); font-size: 12px; font-weight: 800; letter-spacing: .08em; }
|
||||
.personal-node-guide li b { color: var(--content-primary); font-size: 14px; font-weight: 750; }
|
||||
.personal-node-guide li span { margin-top: 6px; color: var(--content-muted); font-size: 12.5px; font-weight: 600; line-height: 1.65; }
|
||||
.personal-node-guide footer { display: flex; justify-content: space-between; gap: 22px; padding-top: 16px; border-top: 1px solid var(--panel-edge); font-size: 12.5px; }
|
||||
.personal-node-guide footer b { color: var(--accent-light); font-weight: 750; white-space: nowrap; }
|
||||
.personal-node-guide footer span { color: var(--content-muted); text-align: right; }
|
||||
.bottle-heart { position: absolute; z-index: 12; left: 50%; top: 48%; display: grid; justify-items: center; gap: 9px; width: 300px; padding: 0; transform: translate(-50%, -50%); border: 0; background: transparent; color: inherit; }
|
||||
.bottle-heart i { width: 90px; height: 90px; border-radius: 46% 46% 52% 52%; background: radial-gradient(circle at 42% 32%, #fffdf5, color-mix(in srgb, var(--primitive-warm-glow) 74%, #f2bdc8) 37%, color-mix(in srgb, var(--primitive-warm-glow) 22%, transparent) 68%, transparent 74%); filter: drop-shadow(0 0 34px color-mix(in srgb, var(--primitive-warm-glow) 62%, transparent)); }
|
||||
.bottle-heart b { color: var(--content-primary); font-size: 22px; font-weight: 750; letter-spacing: .08em; }
|
||||
|
|
@ -715,6 +727,7 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
|
|||
.world-pool { width: 150px; } .pool-bay { width: 145px; }
|
||||
.channel-knowledge { left: 4%; } .channel-code { left: 25%; } .channel-light { right: 25%; } .channel-status { right: 4%; }
|
||||
.broadcast-stream { left: 5%; }
|
||||
.personal-node-guide ol { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-height: 720px) {
|
||||
.official-hero { top: 14px; } .official-hero h1 { font-size: 24px; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue