diff --git a/src/components/home/LiteBand.astro b/src/components/home/LiteBand.astro
index c8547f0..0a89e80 100644
--- a/src/components/home/LiteBand.astro
+++ b/src/components/home/LiteBand.astro
@@ -94,15 +94,20 @@ const zh = lang === "zh-CN";
font-size: 12px;
color: #c3c7ce;
}
+ /* The screenshot carries its own window and shadow; only a glow behind it */
.shot {
- border-radius: 14px;
- box-shadow:
- 0 0 0 1px rgba(255, 255, 255, 0.14),
- 0 50px 120px -40px rgba(0, 0, 0, 0.9),
- 0 30px 100px -40px rgb(var(--a1) / 0.35);
- overflow: hidden;
+ position: relative;
transition: transform 500ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
+ .shot::before {
+ content: "";
+ position: absolute;
+ inset: 12% 8%;
+ z-index: -1;
+ border-radius: 40px;
+ background: radial-gradient(closest-side, rgb(var(--a1) / 0.22), rgb(var(--a2) / 0.1) 60%, transparent);
+ filter: blur(40px);
+ }
.shot img {
display: block;
width: 100%;
diff --git a/src/components/pages/CorePage.astro b/src/components/pages/CorePage.astro
index 7b4b80f..0dc7d1a 100644
--- a/src/components/pages/CorePage.astro
+++ b/src/components/pages/CorePage.astro
@@ -79,7 +79,8 @@ const fileLink = "break-all text-[var(--color-brand-1)] transition-colors hover:
{c.motion.announce(version)}→
)}
- {zh ? {c.hero.titleA} : c.hero.titleA}{c.hero.titleHighlight}
+ {/* Chinese: the break goes after 的, so neither half runs under the terminal */}
+ {zh ? {c.hero.titleA.trim()}
: c.hero.titleA}{c.hero.titleHighlight}
{c.hero.sub}
diff --git a/src/components/pages/LitePage.astro b/src/components/pages/LitePage.astro
index d030e41..869fd88 100644
--- a/src/components/pages/LitePage.astro
+++ b/src/components/pages/LitePage.astro
@@ -212,12 +212,12 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
13.3M$9.34
-
-
{c.menubar.title}
-
{c.menubar.body}
-
+ {c.menubar.title}
+ {c.menubar.body}
+
+
-
{c.motion.bento.clientsCount}
@@ -240,21 +240,36 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
+
- claude-codemain$4.21
- codexmain$2.87
- cursorbudget$0.93
+
{feat.keys.title}
{feat.keys.body}
-
+
{feat["dry-run"].title}
{feat["dry-run"].body}
-
+
+
+ {c.motion.bento.notices.map((n, i) => (
+ -
+
+
+ ThinkWatch Lite
+ {n.title}
+ {n.body}
+
+
+ ))}
+
+
{c.motion.bento.noticesTitle}
{c.menubar.notices}
@@ -1357,20 +1372,24 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
.step-shot {
display: none;
}
+ /* No frame of its own: the screenshots already carry the macOS window and its
+ shadow, so the screen only adds a soft brand glow behind them */
.screen {
position: sticky;
- top: calc(50vh - 220px);
+ top: calc(50vh - 240px);
aspect-ratio: 2360 / 1540;
max-width: 100%;
- border-radius: 14px;
- box-shadow:
- 0 0 0 1px rgba(255, 255, 255, 0.14),
- 0 50px 120px -40px rgba(0, 0, 0, 0.9),
- 0 30px 100px -40px rgb(var(--a1) / 0.35);
- overflow: hidden;
- background: #0f1012;
transition: transform 300ms ease-out;
}
+ .screen::before {
+ content: "";
+ position: absolute;
+ inset: 12% 8%;
+ z-index: -1;
+ border-radius: 40px;
+ background: radial-gradient(closest-side, rgb(var(--a1) / 0.22), rgb(var(--a2) / 0.1) 60%, transparent);
+ filter: blur(40px);
+ }
.screen img {
position: absolute;
inset: 0;
@@ -1455,20 +1474,21 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
}
.t-menubar {
grid-column: span 4;
- grid-row: span 2;
display: grid;
- grid-template-columns: minmax(0, 1fr) 250px;
- gap: 24px;
- align-items: stretch;
+ grid-template-columns: minmax(0, 1fr) 260px;
+ gap: 32px;
+ align-items: center;
+ padding-bottom: 0;
+ min-height: 340px;
}
- /* the menu bar chip at the top, the text at the bottom, the menu on the right */
.t-menubar .tile-text {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
+ padding-bottom: 28px;
}
- .t-menubar .menu-shot {
+ /* the menu rises from the bottom edge of the tile */
+ .menu-stage {
align-self: end;
+ height: 312px;
+ overflow: hidden;
}
.chip-bar {
align-self: flex-start;
@@ -1494,14 +1514,14 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
font-weight: 500;
}
.menu-shot {
- width: 250px;
- margin-bottom: -60px;
+ display: block;
+ width: 260px;
border-radius: 12px;
box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.14);
transition: transform 500ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.t-menubar:hover .menu-shot {
- transform: translateY(-14px);
+ transform: translateY(-10px);
}
.t-clients,
.t-formats,
@@ -1518,7 +1538,7 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
}
.t-dry,
.t-notices {
- grid-column: span 3;
+ grid-column: span 2;
}
.crop-wrap {
position: relative;
@@ -1545,18 +1565,26 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
border-bottom: 1px solid rgb(var(--a1) / 0.6);
animation: scan 3.6s ease-in-out infinite;
}
+ /* One picture height for the whole row, so the headings below line up */
+ .vis {
+ height: 168px;
+ margin-bottom: 22px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+ .t-dry .vis {
+ height: 196px;
+ margin: -28px -28px 22px;
+ }
.dry-shot {
display: block;
width: 100%;
- height: 170px;
- margin: -28px 0 22px;
+ height: 100%;
object-fit: cover;
object-position: 60% 30%;
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
-webkit-mask-image: linear-gradient(#000 70%, transparent);
mask-image: linear-gradient(#000 70%, transparent);
- width: calc(100% + 56px);
- margin-left: -28px;
}
.big {
margin: 0 !important;
@@ -1601,7 +1629,7 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
animation-delay: 3s;
}
.keyrows {
- margin: 0 0 22px;
+ margin: 0;
padding: 0;
list-style: none;
font-family: var(--font-mono);
@@ -1624,83 +1652,145 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
color: var(--color-text);
font-variant-numeric: tabular-nums;
}
- .bell {
- display: flex;
- flex-direction: column;
- gap: 6px;
- margin-bottom: 22px;
- }
- .bell i {
- display: block;
- height: 30px;
- border-radius: 8px;
- border: 1px solid rgba(255, 255, 255, 0.08);
- background: rgba(255, 255, 255, 0.02);
+ /* Notifications stacked the way macOS groups them: the newest in front,
+ older ones peeking out below and moving back as the next arrives */
+ .notifs {
+ --nh: 92px;
position: relative;
- overflow: hidden;
- }
- .bell i::before {
- content: "";
- position: absolute;
- left: 10px;
- top: 12px;
- width: 6px;
- height: 6px;
- border-radius: 99px;
- background: rgb(var(--a1));
- box-shadow: 0 0 8px rgb(var(--a1));
+ height: calc(var(--nh) + 24px);
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ /* the next one waits above the top edge, out of sight */
+ clip-path: inset(0 -40px -40px -40px);
}
- .bell i::after {
- content: "";
+ .notif {
position: absolute;
- left: 26px;
- right: 30%;
- top: 13px;
- height: 4px;
- border-radius: 4px;
- background: rgba(255, 255, 255, 0.1);
- }
- .bell i:nth-child(2)::before {
- background: var(--color-accent);
- box-shadow: 0 0 8px var(--color-accent);
+ inset: 0 0 auto;
+ height: var(--nh);
+ display: flex;
+ gap: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ border-radius: 14px;
+ padding: 10px 12px;
+ background: #202227;
+ box-shadow: 0 12px 30px -12px rgba(0, 0, 0, 0.8);
+ transform-origin: 50% 100%;
+ animation: notif 9s ease-in-out infinite;
+ }
+ .notif:nth-child(1) {
+ z-index: 3;
+ }
+ .notif:nth-child(2) {
+ z-index: 2;
+ transform: translateY(12px) scale(0.94);
+ filter: brightness(0.7);
+ animation-delay: -3s;
+ }
+ .notif:nth-child(3) {
+ z-index: 1;
+ transform: translateY(24px) scale(0.88);
+ filter: brightness(0.45);
+ animation-delay: -6s;
+ }
+ .notif-icon {
+ flex-shrink: 0;
+ display: grid;
+ place-items: center;
+ width: 30px;
+ height: 30px;
+ border-radius: 8px;
+ background: linear-gradient(180deg, #0d0f22, #020208);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
- .bell i:nth-child(2)::after {
- right: 45%;
+ .notif-text {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ font-size: 12.5px;
+ line-height: 1.4;
}
- .bell i:nth-child(3)::before {
- background: rgb(var(--a2));
- box-shadow: 0 0 8px rgb(var(--a2));
+ .notif-head {
+ display: flex;
+ justify-content: space-between;
+ gap: 8px;
+ font-size: 11px;
+ color: var(--color-dim);
}
- .bell i:nth-child(3)::after {
- right: 20%;
+ .notif-head b {
+ font-weight: 500;
}
- .bell i {
- animation: notice 6s ease-in-out infinite;
+ .notif-title {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-weight: 600;
+ color: var(--color-text);
}
- .bell i:nth-child(2) {
- animation-delay: 2s;
+ .notif-body {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ color: var(--color-muted);
}
- .bell i:nth-child(3) {
- animation-delay: 4s;
+ @keyframes notif {
+ /* above the one stepping back while it slides in */
+ 0% {
+ z-index: 4;
+ opacity: 1;
+ transform: translateY(calc(-100% - 2px));
+ filter: none;
+ }
+ 5% {
+ z-index: 4;
+ opacity: 1;
+ transform: none;
+ filter: none;
+ }
+ 33% {
+ z-index: 3;
+ opacity: 1;
+ transform: none;
+ filter: none;
+ }
+ 38%,
+ 66% {
+ z-index: 2;
+ opacity: 1;
+ transform: translateY(12px) scale(0.94);
+ filter: brightness(0.7);
+ }
+ 71%,
+ 95% {
+ z-index: 1;
+ opacity: 1;
+ transform: translateY(24px) scale(0.88);
+ filter: brightness(0.45);
+ }
+ 100% {
+ z-index: 1;
+ opacity: 0;
+ transform: translateY(24px) scale(0.88);
+ filter: brightness(0.45);
+ }
}
@media (max-width: 1023px) {
.bento {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.t-menubar,
- .t-mcp {
+ .t-mcp,
+ .t-notices {
grid-column: span 2;
}
.t-clients,
.t-formats,
.t-keys,
- .t-dry,
- .t-notices {
+ .t-dry {
grid-column: span 1;
}
- .t-keys {
- grid-column: span 2;
- }
}
@media (max-width: 700px) {
.bento {
@@ -1718,9 +1808,11 @@ const archLabel = { x64: "x64", arm64: "ARM64" } as const;
}
.t-menubar {
grid-template-columns: 1fr;
+ gap: 0;
}
- .menu-shot {
- margin: 0 auto -80px;
+ .menu-stage {
+ justify-self: center;
+ height: 260px;
}
.t-mcp {
grid-template-columns: 1fr;
diff --git a/src/i18n/pages/lite.ts b/src/i18n/pages/lite.ts
index f57f069..71eeb00 100644
--- a/src/i18n/pages/lite.ts
+++ b/src/i18n/pages/lite.ts
@@ -81,6 +81,21 @@ export const liteCopy = {
clientsCount: "7",
clientsCountSub: "set up in one step · 3 more by instructions",
noticesTitle: "System notifications",
+ notices: [
+ {
+ title: "Blocked Bash Call from “relay”",
+ body: "It matched the rule “Download and run”, so the response was cut off.",
+ },
+ {
+ title: "“chatgpt” Subscription Quota Used Up",
+ body: "The 5-hour usage limit has been reached and resets in about 2 hours. Requests through this upstream will be rejected.",
+ },
+ {
+ title: "Disconnected from “homelab”",
+ body: "Reconnecting. Until the connection is restored, the app shows the state at the time of the disconnect.",
+ },
+ ],
+ now: "now",
},
},
status: {
@@ -310,6 +325,12 @@ export const liteCopy = {
clientsCount: "7",
clientsCountSub: "款一键接入 · 另有 3 款按说明配置",
noticesTitle: "系统通知",
+ notices: [
+ { title: "已拦截 relay 返回的 Bash 调用", body: "命中规则「下载即执行」,响应已切断。" },
+ { title: "chatgpt 的订阅额度已用完", body: "5 小时额度已用完,约 2 小时后重置。经此上游的请求会被拒绝。" },
+ { title: "与 homelab 的连接已断开", body: "正在重新连接。连接恢复前,应用中的内容停留在断开时的状态。" },
+ ],
+ now: "现在",
},
},
status: {