/* AppStore theme — card expand transition (P2.1 rewrite of P2's model).
   §1: two layers instead of one clone — .expand-art (the flying cover/icon)
   and .expand-sheet (the app-detail-page panel, built at its FINAL position
   from the very first frame and revealed via clip-path). Geometry lives in
   expand.js; this file only owns paint. See docs/p2.1-* for the full
   rationale behind ditching P2's whole-card clone-and-scale model. */

.expand-overlay {
  position: fixed; inset: 0; z-index: 90;
}
/* p2.1.3 §1/§3: mask opacity and backdrop-filter are BOTH driven directly
   from expand.js now (WAAPI .animate() for opacity, a plain property write
   for backdrop-filter) — no CSS transition here at all, since a CSS
   transition can't be individually reversed/rate-adjusted mid-flight the
   way a tracked Animation object can (§4's interrupt handling needs that).
   Base state below is just the CLOSED resting look. */
.expand-mask {
  position: absolute; inset: 0;
  opacity: 0;
  cursor: default;
}
.expand-overlay.is-desktop .expand-mask { cursor: pointer; }
.expand-overlay.is-desktop .expand-mask { background: rgb(0 0 0 / .32); }
:root[data-theme="light"] .expand-overlay.is-desktop .expand-mask { background: rgb(255 255 255 / .32); }
/* mobile: no mask background/blur at all — the sheet already fills the
   whole viewport, §3 explicitly rules out backdrop-filter cost there. */

/* ==================== .expand-art — the flying cover/icon ====================
   Built natively at the LARGER of (start rect, 96px icon) on each axis —
   NOT always at the 96px final size the spec's own formula implied — so
   the transform is a downscale (or identity) at every single frame in
   either direction, never an upscale past native resolution. See the
   build notes for why: browsers rasterize an <img> at roughly its laid-out
   CSS size, and a later transform:scale() blowing that texture up is what
   was actually causing "emoji 在动画中被拉糊" — building at the true max
   needed size removes the upscale case entirely instead of just making it
   monotonically improve. */
.expand-art {
  position: fixed;
  overflow: hidden;
  will-change: transform;
}
.expand-art .cover { position: absolute; inset: 0; }

/* ==================== .expand-sheet — the app-detail-page panel ====================
   Positioned/sized at the FINAL modal rect from frame 1 — .post-head (icon
   slot + titles) is always visible immediately; only .post-unfurl (stats +
   tags + body) is clipped and animates open, "内容从封面底缘向下铺开". */
.expand-sheet {
  position: fixed;
  overflow: hidden;
  background: var(--bg-elevated);
  box-shadow: 0 24px 64px rgb(0 0 0 / .4);
}
.expand-sheet.is-morphing { pointer-events: none; }

/* P3 表单 A(post.php 直达页):没有来源卡可以飞、没有 JS 算出来的目标
   rect 可用(也不该依赖 JS 才能把核心版式摆对——真实页面加载,不是 SPA
   内的点击过渡),所以这块尺寸用 getTargetRect() 同一套数值改写成纯 CSS
   静态规则:position:fixed + inset:0 + margin:auto 是不需要 JS 也能让一
   个定宽定高的 fixed 盒子在视口内居中的经典写法。expand.js 打开态用的
   内联 top/left/width/height 完全不涉及这条规则——两条路径分别覆盖各自
   的场景,互不冲突。 */
.expand-sheet.is-standalone {
  top: 0; left: 0; right: 0; bottom: 0; margin: auto;
  width: clamp(600px, 62vw, 780px);
  height: min(90vh, 1125px);
  border-radius: 18px;
}
@media (max-width: 699px) {
  .expand-sheet.is-standalone {
    inset: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    border-radius: 0;
  }
}

.expand-scroll {
  position: absolute; inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

.expand-textghost {
  position: fixed;
  z-index: 91;
  pointer-events: none;
  display: flex;
  align-items: center;
  transition: opacity 120ms ease;
}

/* ==================== post: application-detail-page structure ====================
   P3's post.php target DOM. Modeled directly on the App Store's own app
   detail page (real-device reference screenshot) — icon + title/subtitle
   with no button under it, a 4-column stat strip, tags, body. No more
   full-width hero banner or sticky mini-attribution bar (P2's .post-hero/
   .post-meta) — the topbar's two circular buttons are the only persistent
   chrome now, same role a real App Store page gives its back/share buttons. */
/* p2.1.1 §1: single source of truth for the left/right inset every section
   below shares — head icon's left edge, stats' first cell text, the first
   tag chip, and body copy all read off this ONE variable instead of each
   picking its own padding/margin, which is exactly how they drifted out
   of alignment in the first place. */
.post {
  min-height: 100%;
  padding-top: 68px;
  --post-pad: 28px; /* p2.1.2 §3: was 24px — bumped alongside the 1.25x card enlarge */
}
@media (max-width: 699px) {
  .post { --post-pad: 20px; }
}

/* p3.23①:page.php(独立页面,如导航栏"关于")复用 .post-body 同一套排
   版规则(标题/段落/引用块/图片),但它不在 .post 容器里(那个容器绑
   着展开卡片专属的一整套 JS 行为,不该为了借一个 CSS 变量就套上去)——
   这里单独给同一个变量、同一份响应式断点,数值和 .post 保持一致,不
   是另起一套。 */
.page-body {
  --post-pad: 28px;
}
@media (max-width: 699px) {
  .page-body { --post-pad: 20px; }
}

.post-topbar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 6;
  display: flex;
  justify-content: space-between;
  padding: 16px;
  pointer-events: none; /* the bar itself is a click-through gap between the two buttons */
}
.btn-back, .btn-share {
  pointer-events: auto;
  width: 36px; height: 36px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  --glass-tint: #1c1c1e;
  transition: transform var(--dur-hover) var(--ease), background-color var(--dur-hover) var(--ease);
}
.btn-back svg, .btn-share svg { display: block; }
/* p3.26:两颗圆按钮原来只有 :active(触屏按下反馈),鼠标移上去没反
   应——补一个轻微放大,和这一轮别处按钮的 hover 语言一致。 */
@media (hover: hover) {
  .btn-back:hover, .btn-share:hover { transform: scale(1.08); background: rgb(255 255 255 / .12); }
}
/* §3 (P2.2 backlog): share opens a share sheet — not built yet, but the
   button needs a visible pressed state now so it doesn't read as dead. */
.btn-share:active { opacity: .6; }
.btn-back:active { opacity: .6; }
@media (prefers-reduced-motion: reduce) {
  .btn-back:hover, .btn-share:hover { transform: none; }
}

/* p3.18⑥:分享钮左边挂音乐球/多语言球的一排——pointer-events:auto 是
   因为父级 .post-topbar 整条本身是 pointer-events:none(见上面注释,
   两个按钮之间是可以点穿到下面封面的空隙),这一排和里面的球都要能点
   击,得自己重新打开。 */
.post-topbar__right {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ask-topbar-balls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.post-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding-inline: var(--post-pad);
  padding-bottom: 20px;
}
/* P3 §emoji放大: bumped 96→128px alongside base.css's cover-emoji
   font-size going 48→108px — a 108px glyph in the OLD 96px box would
   have been larger than its own container (overflow:hidden below would
   have silently clipped it, contradicting "在...块内更饱满" — fuller
   inside the block, not cropped by it). 128px keeps the glyph at a
   comfortable ~84% fill, matching the same "bigger but still fully
   contained, not cropped" look the row/story bumps got for free (their
   boxes were already large enough that 40px/88px never approached the
   edge). expand.js reads this box's live rect for the flight animation's
   target geometry (iconRect = iconSlot.getBoundingClientRect()) so the
   size bump needs no JS changes — the animation just targets whatever
   this CSS says now. */
.post-icon {
  width: 128px; height: 128px;
  border-radius: var(--radius-icon);
  overflow: hidden;
  flex: 0 0 auto;
  position: relative;
}
.post-icon .cover { position: absolute; inset: 0; }
.post-titles {
  flex: 1 1 auto;
  min-width: 0;
  padding-top: 8px;
}
.post-titles h1 {
  font-size: var(--fs-title2);
  font-weight: 700;
  letter-spacing: var(--tracking-title);
  line-height: 1.25;
  margin: 0 0 4px;
  color: var(--text-primary);
}
.post-titles .sub {
  font-size: var(--fs-subhead);
  color: var(--text-secondary);
  line-height: 1.4;
}

/* ---- stat strip (real-device reference: 评分/年龄/排行榜/开发者, 4 等分 + 发丝竖线) ----
   p2.1.1 tried CSS Grid + clamp() + ellipsis (shrink-to-fit, never scroll)
   because a 412px viewport was clipping "Hello World" out of the last
   cell — but ellipsis on a real 作者 name is itself data loss, just less
   visible than a hard clip. p2.1.2 §1 reverses direction: let each cell
   size to its own content (flex, no shrinking), and when four full-size
   cells don't fit, the STRIP scrolls instead of the TEXT truncating —
   fully-readable data over a compact layout. mask-image fades both edges
   as a scroll affordance, applied unconditionally (harmless when nothing
   overflows, since --post-pad's own inset already keeps real content
   clear of the 12px fade zone — confirmed via screenshot, not assumed). */
.post-stats {
  display: flex;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  padding-inline: var(--post-pad);
  padding-bottom: 20px;
  mask-image: linear-gradient(to right, transparent, black 12px, black calc(100% - 12px), transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, black 12px, black calc(100% - 12px), transparent);
}
.post-stats::-webkit-scrollbar { display: none; }
.stat {
  flex: 1 0 auto;
  min-width: 23%;
  max-width: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 3px;
  padding-inline: clamp(6px, 2vw, 16px);
  border-left: 1px solid var(--separator);
  color: inherit; /* the 分类 cell is an <a> now — reset link color/underline */
  text-decoration: none;
}
/* the container's own --post-pad already insets the strip — without this,
   the first cell's own clamp() padding stacked ON TOP of that (p1.14
   施工笔记 has the same bug once already, on a different property). */
.stat:first-child { border-left: none; padding-inline-start: 0; }
/* p2.3 correction §3: the desktop card went from a flat 900px to
   clamp(600px,62vw,780px) — at the 600px floor the stat strip has notably
   less room than the old 900px card did, so these three sizes are now
   fluid (clamp keyed off viewport width, the same variable the card's own
   width formula uses) instead of one flat desktop value. Ceiling matches
   the old flat desktop size (reached once the card itself has hit its own
   780px ceiling, ~1258px viewport); floor lands close to the <700px
   mobile override just below it, so there's no visible jump crossing that
   breakpoint. The mobile media query below still wins under 700px — this
   only reshapes the >=700px range. */
.stat-top {
  font-size: clamp(10px, 7.5px + .36vw, 12px);
  color: var(--text-tertiary);
  white-space: nowrap;
}
.stat-main {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: clamp(18px, 13px + .72vw, 22px);
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
}
.stat-main .fe { height: clamp(16px, 11px + .72vw, 20px); width: auto; display: block; flex: 0 0 auto; }
.stat-sub {
  font-size: clamp(10px, 7.5px + .36vw, 12px);
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
}
/* p2.3 A2: mobile-only compaction, one further notch down past whatever
   the fluid clamp above already reached at 699px — main value/top-sub
   labels/inter-line gap/emoji all scaled down together so the strip reads
   as a smaller, tighter version of itself rather than one piece shrinking
   out of proportion with the rest. */
@media (max-width: 699px) {
  .stat { gap: 2px; }
  .stat-top, .stat-sub { font-size: 10px; }
  .stat-main { font-size: 18px; }
  .stat-main .fe { height: 16px; }
}

/* .post-unfurl wraps everything below the head (stats/tags/body) — the one
   thing that actually animates open, via clip-path, synced to .expand-art's
   flight (both 480ms / --ease-spring, started together in expand.js).
   Scoped to .expand-overlay .post-unfurl (P3 fix) — this "fully clipped"
   baseline is only meaningful as the animated sheet's CLOSED starting
   point, which expand.js's buildSheet() sets as an inline style anyway
   and settleToOpen()/collapseCard() explicitly override afterward (see
   that file's own comment on why cancel()-without-an-explicit-inline-
   style falls back to whatever this baseline says, same bug class p2.1
   already hit once). post.php's standalone .post-unfurl is a plain
   server-rendered div with no JS ever touching it — it lives directly
   under <body>, never inside .expand-overlay — so an UNSCOPED rule here
   left it permanently clipped to zero height on every direct page load,
   the exact same failure mode, just triggered by "no JS ran at all"
   instead of "cancel() ran without a follow-up inline style". */
.expand-overlay .post-unfurl {
  clip-path: inset(0 0 100% 0);
}

/* p2.1.1 §1: dropped max-width:680px/margin-inline:auto — that was a
   SECOND, independent centering mechanism competing with --post-pad (on a
   720px sheet, 24px padding-inline already yields a 672px column, already
   under the 680px reading-width guideline, so the two were never actually
   needed together — the auto-margin one just had to go for the four
   sections to share a single alignment source). */
/* p2.1.2 §3: the 900px desktop sheet (up from 720px) needs a reading-width
   cap again — padding-inline alone would give an 844px column, well past
   comfortable measure. max-width is padding-box-relative (global
   box-sizing:border-box), so 680px content + 2*--post-pad keeps the
   680px target AND keeps the block's own left edge flush with --post-pad
   (a block narrower than its container sticks to the start edge by
   default — no margin-inline:auto needed, which would have re-broken
   p2.1.1's alignment fix by centering independently of the other three
   sections). */
.post-body {
  padding-inline: var(--post-pad);
  padding-block: 4px 64px;
  max-width: calc(680px + var(--post-pad) * 2);
  font-size: 17px;
  line-height: 1.8;
  color: var(--text-primary);
}
/* p2.3 A1: the 680px cap above is right for 形态A (mountDirectAccess()'s
   standalone page, expand.js — no card/sheet width constraining it, so it
   needs its own reading-width cap) but wrong inside the expand-card
   overlay: the sheet there is ALREADY width-constrained (720/900px), so
   capping again just shrinks the column a second time, short of
   .post-stats/.post-tags' own right edge (neither has a max-width, only
   padding-inline) — reads as the body text sitting in an off-center narrow
   strip instead of sharing the same right edge as the rest of the page.
   Un-capping only inside .expand-overlay leaves 形态A untouched. Mobile
   was never affected either way — the overlay's mobile sheet width is
   already under the 736px this cap works out to, so it was always a
   no-op there, capped or not. */
.expand-overlay .post-body {
  max-width: none;
}
.post-tags {
  display: flex; gap: 8px; flex-wrap: wrap;
  padding-inline: var(--post-pad);
  margin-bottom: 24px;
}
.post-tags .chip { height: 30px; padding: 0 12px; font-size: 13px; }
.post-body p { margin: 0 0 20px; }
.post-body img { width: 100%; border-radius: 14px; margin: 8px 0 20px; display: block; }
.post-body blockquote {
  margin: 0 0 20px;
  padding: 4px 0 4px 16px;
  border-left: 3px solid var(--tint);
  color: var(--text-secondary);
  font-style: italic;
}

@media (min-width: 700px) {
  .post-icon { width: 140px; height: 140px; } /* P3 §emoji放大: was 112px, same 96→128 bump reasoning */
  .post-titles h1 { font-size: var(--fs-title1); }
}

/* ==================== reduced motion: plain crossfade ====================
   Skip the fly/unfurl entirely — 150ms fade in/out for the whole overlay,
   sheet lands fully unfurled and the icon fully populated from frame 1. */
/* p2.1.3: reduced-motion no longer needs a CSS media query at all — the
   150ms crossfade is set up directly as inline styles in expandCard()/
   collapseCard()'s own `if (reduced)` branches, alongside every other
   layer's now-JS-driven (WAAPI) styling. A static CSS override here would
   only fight those inline writes for no benefit. */

/* p3.19 B:上面这块 Typecho 默认评论列表/表单的样式(threadedComments()
   走核心默认渲染,套主题视觉)已经整个被 assets/css/reviews.css 的
   iOS App Store 风评分/评论系统取代——.post-comments/.comment-list 等
   核心默认类名不再从 post.php 输出,这里删掉不再有对应 HTML 的死样
   式,不留着"看起来还在维护但其实没有任何元素会用到"的规则。 */
