/* ==========================================================================
   V7 Home Master — Family OS mobile home
   ==========================================================================

   This file is the *master page* layer for the UI line. It is loaded after
   app.css / graphite.css / v7.css so it can retune the composition without
   forking any of them.

   Why a new file instead of editing v7.css
   ----------------------------------------
   v7.css is shared by every surface (care home, tasks, inventory, knowledge,
   desktop). Retuning the home composition inside it would move pages that are
   out of this round's scope. This layer is scoped to the Family home shell
   (`.reference-page.mobile-dashboard`) wherever the rule could leak, and only
   uses unscoped selectors for genuinely global concerns (the font stack).

   What it fixes, and why each was a defect rather than a taste call
   ----------------------------------------------------------------
   1. FONT. The stack resolved to Microsoft YaHei on Windows: the first four
      families (-apple-system / BlinkMacSystemFont / PingFang SC / Hiragino Sans
      GB) do not exist there, and the machine ships only msyh.ttc + msyhbd.ttc —
      two weights. So every 500/600/700 in the sheets collapsed onto 400 or 700
      and the reference's heavy display type was unreachable. `Noto Sans SC VF`
      is now embedded as a variable face (wght 100–900), so 760 / 800 / 900 are
      real interpolations rather than synthetic emboldening.

   2. CARD WALL. Every module rendered as an identical full-width rounded card,
      which is what made the page read as a report rather than an editorial
      home. The brief is explicit: "普通 section 不添加背景卡片 … 优先使用细分
      隔线". List modules now sit on the page surface behind a hairline.

   3. RHYTHM. `dashboard-widget` sizes already encode importance
      (FEATURED / STANDARD / COMPACT) but all three rendered at the same weight.
      COMPACT modules now pair into a 2-up row — which is also how the reference
      draws its bottom band (今日提醒 / 本周重点).

   4. NAV. graphite.css raised the `＋` 18px above the bar with
      `position:absolute; left:50%`, and in a 6-column grid 50% lands on a column
      *boundary*, not a gap — so it floated over the 任务 label. It is now a
      normal grid item in a dedicated centre column.

   5. TYPE SCALE. The greeting was `clamp(2.9rem, 14vw, 3.6rem)` — 54.6px at 390
      and 57.6px at 430, i.e. 42% of the viewport against the reference's 33.9%.
      vw-based type is also explicitly out of bounds ("不要使用基于 viewport 连续
      缩放的字号；使用明确的移动/桌面断点尺寸"), so this is now two explicit
      breakpoint sizes.
   ========================================================================== */

/* --- §0 Typography: the embedded face ------------------------------------- */
/* One variable file covers every weight. Subset to the project's own character
   set (templates + Python + GB2312 + Latin), which takes it from 17MB to 1.8MB
   so the page does not pay a CJK-sized download on every cold load. */
@font-face {
  font-family: "Noto Sans SC VF";
  src: url("../fonts/noto-sans-sc-vf.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --font-ui: "Noto Sans SC VF", -apple-system, BlinkMacSystemFont, "Segoe UI",
    "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif;

  /* Master-page metrics, measured off reference_family_os_v7.png (864x1536 =
     430 CSS px at 2x). Kept as tokens so the other pages can adopt them. */
  --home-pad: 16px;          /* reference page margin */
  --home-gap-section: 26px;  /* between editorial sections */
  --home-radius-hero: 16px;
}

body,
button,
input,
select,
textarea {
  font-family: var(--font-ui);
}

/* The face carries real weights 100–900, so a synthesised bold is never wanted:
   it thickens the stroke without moving the sidebearings, which is exactly the
   "fake heavy" look the reference does not have. */
body {
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
}

/* --- §1 Page frame ---------------------------------------------------------
   graphite.css ships `.shell:has(.reference-page){padding:28px 20px 116px}`.
   The reference's margin is 16px, and the 116px bottom reserve existed to clear
   a flush bar — the bar is a floating capsule now, so the reserve is smaller.

   The two `:not(:has(...))` guards are load-bearing, not decoration.
   `.reference-page` is not a mobile-only class: the desktop workspace wraps its
   own content in `inventory-page reference-page workspace-page`. Without the
   guards this rule also matched every desktop page and clamped it to a 430px
   centred column. It outranked graphite.css's own desktop frames —
   `@media(min-width:900px){.shell:has(.workspace-shell){max-width:none}}` and
   `.shell:has(.inventory-layout){width:100%;max-width:none;padding:0}` — because
   it has equal specificity (0,2,0) and, unlike those, sits OUTSIDE any media
   query while being loaded from a later file, so it won at every width.

   Measured at 1280px before the fix: `main.shell` box 430px wide with
   `margin: 0 425px`, sidebar at x=441, inventory content column 178px. Affected
   the two inventory-family desktop pages (总览 and 库存); the pages that use
   `.workspace-shell` were unaffected. The guards restore the existing desktop
   frames and leave the mobile page frame byte-identical. */
.shell:has(.reference-page):not(:has(.workspace-shell)):not(:has(.inventory-layout)) {
  width: 100%;
  max-width: 430px;
  margin-inline: auto;
  padding: 20px var(--home-pad) 96px;
}

/* --- §2 Editorial header --------------------------------------------------
   The reference stacks three rows: the OS mark and the date pill share the
   first, the greeting owns the second at full width, the sub-line the third.
   The template nests brand/greeting/sub inside one bare `<div>`, so a plain
   two-column grid on `.v7-header` would give the pill the full height of that
   block and squeeze the greeting into whatever the pill left behind — measured
   at 177.8px of a 390px page, which wrapped the sub-line onto two lines and left
   a dead quarter-page beside the greeting.

   `display: contents` on that wrapper dissolves its box so its three children
   become grid items in their own right, and the pill can sit on row 1 alone.
   No template change, so the sealed markup checks still see the same tree. */
.reference-page.mobile-dashboard .v7-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 2px 10px;
  margin: 0 0 18px;
}

.reference-page.mobile-dashboard .v7-header > div:first-child { display: contents; }

.reference-page.mobile-dashboard .v7-header .v7-header__brand {
  grid-area: 1 / 1 / 2 / 2;
  gap: 6px;
  margin: 0;
  font-size: 1.0625rem;   /* 17px */
  font-weight: 700;
  letter-spacing: -.01em;
}

.reference-page.mobile-dashboard .v7-header__actions { grid-area: 1 / 2 / 2 / 3; }

.reference-page.mobile-dashboard .v7-header__greeting { grid-area: 2 / 1 / 3 / -1; }
.reference-page.mobile-dashboard .v7-header__sub { grid-area: 3 / 1 / 4 / -1; }

.reference-page.mobile-dashboard .v7-header__brand .ui-icon {
  width: 17px;
  height: 17px;
}

/* The page's first visual centre. 46px at 390 / 50px at 430 reproduces the
   reference's greeting ink width (33.9% of the viewport) in the embedded face;
   the old 54.6px was 42% and pushed the sub-line off the first screen. */
.reference-page.mobile-dashboard .v7-header__greeting {
  font-size: 46px;
  line-height: 1;
  font-weight: 900;
  letter-spacing: -.02em;
}

.reference-page.mobile-dashboard .v7-header__sub {
  margin: 0;
  font-size: .875rem;     /* 14px */
  font-weight: 450;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* The utilities row stays — role switcher, layout editor and density control are
   real controls and must remain reachable — but it recedes to a quiet line on
   the page's left axis. The brief requires one shared left baseline for titles,
   cards and images; this row was right-aligned, so it broke that axis and read
   as a stray toolbar floating between the header and the hero.
   `__who` ("<name>的首页") is owner-requested removal: it restated the page
   identity the greeting already carries. */
.reference-page.mobile-dashboard .v7-header-utils {
  justify-content: flex-start;
  gap: 10px;
  margin: 0 0 16px;
  font-size: .6875rem;    /* 11px */
  color: var(--text-tertiary);
}

.reference-page.mobile-dashboard .v7-header-utils > * { margin-left: 0; }

.reference-page.mobile-dashboard .v7-header-utils__who { display: none; }

/* --- DatePill --------------------------------------------------------------
   Superseded by reference_family_os_v8, whose pill is ONE quiet line: a
   light-grey circular icon disc, the date in bold, the weekday in grey, and
   **no chevron control at all**. The v7 mockup's circular warm-brown chevron
   disc used to be drawn from here as a `::after` (rather than added to the
   template, so the sealed Slice 8 markup checks kept seeing the same element).

   The element stays a link, so dropping the chevron is a presentation change and
   not a reachability one — and the Home header already carries an explicit
   「编辑首页」 link two rows below, which is what the chevron was duplicating.
   The v8 look itself lives in §8.1 with the rest of the header; only the
   geometry that predates it is retired here. */
.reference-page.mobile-dashboard .v7-date-pill {
  align-items: center;
  column-gap: 6px;
  min-height: 0;
  padding: 4px 11px 4px 5px;
  border-radius: 999px;
  box-shadow: none;
}

.reference-page.mobile-dashboard .v7-date-pill strong {
  font-size: .8125rem;    /* 13px */
  font-weight: 750;
}

.reference-page.mobile-dashboard .v7-date-pill small {
  font-size: .75rem;      /* 12px */
}

/* The v7 chevron disc (`::after`) is deleted rather than neutralised: nothing
   else referenced it, and the v8 pill draws no control. */

/* --- §3 Hero ---------------------------------------------------------------
   The previous pass already calibrated the ratio (2.62:1) and the media column.
   What it could not do is hold the card near that ratio, because the prenatal
   hero carries two rows the reference does not (the due-date caveat and the
   birth-registration link), so content drove the height to 223px against the
   reference's 134px. The rows are real product content and stay; the padding and
   row gaps tighten so the card lands closer to the reference's weight. */
.reference-page.mobile-dashboard .v7-hero {
  margin-bottom: var(--home-gap-section);
  border-radius: var(--home-radius-hero);
  box-shadow: none;
}

.reference-page.mobile-dashboard .v7-hero__body {
  gap: 3px;
  padding: 12px 15px;
  min-height: calc(100cqw / 2.62);
}

/* The text column takes 53% instead of an even split. The reference's own split
   is 49.7%, but its hero carries four rows while this one carries six (the
   due-date caveat and the birth-registration entry are real product content that
   the reference page simply does not have). Four points of width are invisible
   and they are what stop the caveat and the entry from wrapping.

   The mask is the single biggest visual difference between the two heroes. The
   reference's photograph does not start at an edge: it emerges from the card's
   own cream, so the card reads as one surface with a picture inside it. Without
   the fade this is a card with a picture bolted to its right half, which is
   exactly how the earlier versions looked. */
.reference-page.mobile-dashboard .v7-hero__media {
  width: 47%;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 30%);
  mask-image: linear-gradient(to right, transparent 0%, #000 30%);
}

.reference-page.mobile-dashboard .v7-hero__label { font-size: .6875rem; font-weight: 600; }

.reference-page.mobile-dashboard .v7-hero__title,
.reference-page.mobile-dashboard .v7-hero__body > h2 {
  font-size: 1.3125rem;   /* 21px — the card's headline, one step under the greeting */
  line-height: 1.18;
  font-weight: 800;
  letter-spacing: -.02em;
}

/* The headline figure keeps the reference's weight: tabular so a two- and a
   three-digit value occupy the same column and the row never reflows. 30px
   rather than 34px because this hero's figure is a label-plus-value string
   (「孕 38+6」) rather than the reference's bare number (「23 天」); at 34px the
   string ate the whole column and pushed the card past its ratio. */
.reference-page.mobile-dashboard .v7-hero__metric,
.reference-page.mobile-dashboard .v7-hero__metric strong {
  font-size: 1.875rem;
  line-height: 1;
  font-weight: 800;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}

/* No rule between the body and the foot. The reference's hero has none — its
   ring, its countdown and its arrow button sit directly on the card — and the
   hairline was adding 9px of height plus a division the composition does not
   want. */
.reference-page.mobile-dashboard .v7-hero__foot {
  margin-top: 4px;
}

.reference-page.mobile-dashboard .v7-hero__note {
  font-size: .625rem;     /* 10px — a caveat, deliberately the smallest text on the page */
  line-height: 1.4;
  color: var(--text-tertiary);
}

/* The birth-registration entry: a small editorial link, not a second button.
   As a bordered 40px control it competed with the hero's own CTA and added
   ~30px of card height for a once-per-lifetime action. It is nowrap because a
   two-line underlined link reads as a broken control rather than a link. */
.reference-page.mobile-dashboard .v7-hero__link {
  align-self: flex-start;
  padding: 0;
  border: 0;
  border-radius: 0;
  font-size: .625rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line-soft);
}

/* --- §4 Sections: retire the card wall ------------------------------------
   Every module used to draw the same full-width rounded card. The brief says
   ordinary sections carry no background card and hierarchy comes from titles,
   spacing and hairlines. The card treatment is therefore removed from list
   modules and kept only for genuinely independent objects (the hero). */
.reference-page.mobile-dashboard .widget-stack {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--home-gap-section) 12px;
}

/* Size already encodes importance in the registry (FEATURED / STANDARD /
   COMPACT), so it now also drives the grid: featured and standard modules own a
   full row, compact modules pair up. */
.reference-page.mobile-dashboard .dashboard-widget--featured,
.reference-page.mobile-dashboard .dashboard-widget--standard,
.reference-page.mobile-dashboard .dashboard-widget[data-widget-key="quick_task"] {
  grid-column: 1 / -1;
}

/* Compact modules — and any module whose state resolver reports `empty` — pair
   into a 2-up row and take a soft surface, because that is how the reference
   draws the same band (its 今日提醒 / 本周重点 pair are light warm cards, not bare
   rows). Full-width, populated modules stay open on the page surface.

   The empty case matters more than it looks. A module that resolves to `empty`
   has one short line to say, and at full width it claimed an entire row for it —
   four of those in a row is what made this page read as a stack of one-line
   bands rather than as the reference's multi-column rhythm. Half-width lets them
   share rows again:

     tasks(empty) + reminders(empty)  |  inventory + household(empty)  |  capture

   `quick_task` is excluded: it is the page's capture entry and owns a full row.
   `data-widget-state` is set by `resolve_widget_state` from real data, so this
   grouping follows the content rather than guessing at it. */
.reference-page.mobile-dashboard .dashboard-widget--compact:not([data-widget-key="quick_task"]),
.reference-page.mobile-dashboard .dashboard-widget[data-widget-state="empty"] {
  grid-column: span 1;
  padding: 13px 14px;
  border-radius: var(--radius-small);
  background: var(--bg-card);
}

.reference-page.mobile-dashboard .dashboard-widget--compact .widget-heading h2,
.reference-page.mobile-dashboard .dashboard-widget[data-widget-state="empty"] .widget-heading h2 {
  font-size: 1.0625rem;
  font-weight: 750;
}

.reference-page.mobile-dashboard .dashboard-widget--compact .widget-heading a,
.reference-page.mobile-dashboard .dashboard-widget[data-widget-state="empty"] .widget-heading a {
  min-height: 28px;
  font-size: .6875rem;
}

.reference-page.mobile-dashboard .dashboard-widget--compact .v7-row:first-of-type,
.reference-page.mobile-dashboard .dashboard-widget--compact .quiet-empty {
  border-top: 0;
}

/* v7.css lays the empty tasks/reminders pair out as a two-column row with a
   bottom hairline — right on the page surface, wrong inside a half-width card.
   Stacked, with the card providing the boundary instead of the rule. */
.reference-page.mobile-dashboard
  .dashboard-widget[data-widget-state="empty"]:is([data-widget-key="tasks"], [data-widget-key="reminders"]) {
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  min-height: 0;
  border-bottom: 0;
}

.reference-page.mobile-dashboard
  .dashboard-widget[data-widget-state="empty"]:is([data-widget-key="tasks"], [data-widget-key="reminders"])
  .v7-list-card {
  justify-self: start;
  padding: 0;
}

.reference-page.mobile-dashboard
  .dashboard-widget[data-widget-state="empty"]:is([data-widget-key="tasks"], [data-widget-key="reminders"])
  .quiet-empty {
  padding: 0;
  border-top: 0;
}

/* The stack owns the vertical rhythm, so a module must not also contribute its
   own bottom margin. v7.css gives `.v7-focus`, `.v7-list-card` and
   `.widget-heading` their own `margin-bottom`, and stacking that on top of the
   26px grid gap opened a ~60px hole between the knowledge module and the focus
   note. */
.reference-page.mobile-dashboard .dashboard-widget { margin: 0; }
.reference-page.mobile-dashboard .dashboard-widget > *:last-child { margin-bottom: 0; }
.reference-page.mobile-dashboard .dashboard-widget .v7-focus,
.reference-page.mobile-dashboard .dashboard-widget .v7-list-card,
.reference-page.mobile-dashboard .dashboard-widget .widget-heading { margin-bottom: 0; }

.reference-page.mobile-dashboard .widget-heading {
  align-items: center;
  gap: 10px;
  margin: 0 0 4px;
}

.reference-page.mobile-dashboard .widget-heading h2,
.reference-page.mobile-dashboard .v7-section-title h2 {
  font-size: 1.3125rem;   /* 21px */
  line-height: 1.25;
  font-weight: 800;
  letter-spacing: -.02em;
}

.reference-page.mobile-dashboard .widget-heading a {
  min-height: 32px;
  font-size: .75rem;
  color: var(--text-tertiary);
}

.reference-page.mobile-dashboard .dashboard-widget .v7-list-card {
  gap: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

/* Rows: a hairline carries the separation the card used to, and the pitch is
   tightened from 9px to 6px because the reference's list rows are denser. */
.reference-page.mobile-dashboard .dashboard-widget .v7-row,
.reference-page.mobile-dashboard .dashboard-widget .v7-list-card__row {
  gap: 2px 10px;
  padding: 8px 0;
  border-top: 1px solid var(--line-subtle);
}

.reference-page.mobile-dashboard .dashboard-widget .v7-row strong,
.reference-page.mobile-dashboard .dashboard-widget .v7-list-card__row strong {
  font-size: .9375rem;
  font-weight: 600;
  line-height: 1.35;
}

.reference-page.mobile-dashboard .dashboard-widget .v7-row small,
.reference-page.mobile-dashboard .dashboard-widget .v7-list-card__row small {
  font-size: .75rem;
  font-weight: 450;
  line-height: 1.4;
  color: var(--text-tertiary);
}

/* Compact modules sit side by side, so their rows must survive a 173px column:
   long titles wrap instead of pushing the card wide. */
.reference-page.mobile-dashboard .dashboard-widget--compact .v7-row strong,
.reference-page.mobile-dashboard .dashboard-widget--compact .v7-row small {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.reference-page.mobile-dashboard .dashboard-widget .quiet-empty {
  margin: 0;
  padding: 8px 0;
  border-top: 1px solid var(--line-subtle);
  font-size: .8125rem;
  color: var(--text-tertiary);
}

/* 阶段提示, drawn in the band's card language.

   The module itself is untouched: the dark focus card is still the page's one
   emphasis surface (V7 §4.5.1) and `.attention-focus` must still appear exactly
   once on the Home. What changes is the surface it is drawn on — on the Family
   Home the module reads as one more 「家里现在」-style card instead of either a
   full-bleed dark block or the left-ruled stage note it was.

   Drawing it light in *both* states is also what keeps "one dark card" true. The
   module turns itself dark the moment anything is actually due, so leaving the
   dark variant alone would give this page two dark cards exactly when the
   household has work to do — a rhythm the reference never shows. The page's
   single dark card is the 待办任务 card in the band.

   The `--quiet` selector is spelled out because the rule this replaces was
   `.reference-page.mobile-dashboard .v7-focus.attention-focus--quiet` — four
   classes — so a plain three-class `.v7-focus` override loses to it. */
.reference-page.mobile-dashboard .v7-focus,
.reference-page.mobile-dashboard .v7-focus.attention-focus--quiet {
  display: grid;
  /* The reference's row between the hero and the band is SHORT — three compact
     metric cards, ~63px at the 390px target. This module carries prose instead
     of metrics, so it cannot be that short, but it must not be 2.5× it either:
     the gap:4px / 16px-heading / 12px@1.55 body combination added up to 155px
     and made the whole page read as airy next to the reference. Every value
     here is one step down, which lands the card near 124px while keeping the
     text at the reference's own small-text scale (~11.5px). */
  gap: 3px;
  margin: 0;
  padding: 11px 13px;
  border: 0;
  border-radius: var(--radius-small);
  background: var(--bg-card);
  color: var(--text-primary);
  box-shadow: none;
}

.reference-page.mobile-dashboard .v7-focus__head { display: block; }

.reference-page.mobile-dashboard .v7-focus__label,
.reference-page.mobile-dashboard .attention-focus--quiet .v7-focus__label {
  font-size: .6875rem;
  font-weight: 650;
  letter-spacing: .01em;
  color: var(--text-secondary);
}

.reference-page.mobile-dashboard .v7-focus__head h2,
.reference-page.mobile-dashboard .attention-focus--quiet .v7-focus__head h2 {
  margin: 1px 0 3px;
  font-size: .9375rem;    /* 15px */
  font-weight: 750;
  line-height: 1.35;
  letter-spacing: -.02em;
  color: var(--text-primary);
}

.reference-page.mobile-dashboard .v7-focus__text,
.reference-page.mobile-dashboard .attention-focus--quiet .v7-focus__text {
  margin: 0;
  font-size: .71875rem;   /* 11.5px */
  line-height: 1.5;
  color: var(--text-secondary);
}

.reference-page.mobile-dashboard .v7-focus .attention-ledger {
  border-top-color: var(--line-subtle);
  color: var(--text-primary);
}

.reference-page.mobile-dashboard .v7-focus .attention-ledger strong { color: var(--text-primary); }
.reference-page.mobile-dashboard .v7-focus .attention-ledger small { color: var(--text-tertiary); }
.reference-page.mobile-dashboard .v7-focus .quiet-empty { color: var(--text-tertiary); }

/* --- §5 Quick actions ------------------------------------------------------
   reference_family_os_v7.png draws 快捷动作 as a row of four flat pills: each is
   a rounded rectangle carrying an icon and its label side by side. Not the four
   vertical circles graphite.css drew, and not the bordered shadow cards v7.css
   turned them into — the circles are what read as "not the reference".

   The container class is deliberately untouched. Slice 8 matches the literal
   `<div class="round-actions">` markup with an exact regex and asserts the six
   actions inside it, so this is a stylesheet-only change and the same treatment
   therefore also reaches the Baby OS quick-record row, which had drifted into a
   different design language from the Family Home. */
.reference-page.mobile-dashboard .round-actions {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 0;
}

.reference-page.mobile-dashboard .round-actions > a,
.reference-page.mobile-dashboard .round-actions > button {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  min-height: 50px;
  padding: 0 8px;
  border: 1px solid var(--line-subtle);
  border-radius: 14px;
  background: var(--bg-card);
  box-shadow: none;
  color: var(--text-primary);
  /* 11px is not a taste call: four pills plus their gaps have to share a 358px
     row at the 390px target, which leaves ~83px per pill. */
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: -.01em;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
}

.reference-page.mobile-dashboard .round-actions > a .ui-icon,
.reference-page.mobile-dashboard .round-actions > button .ui-icon {
  width: 18px;
  height: 18px;
  flex: none;
  color: var(--text-secondary);
}

/* The markup wraps each icon in a `<span>`; the circle that span used to draw is
   exactly what is being removed, so it collapses to a plain flex box. */
.reference-page.mobile-dashboard .round-actions > a > span {
  display: flex;
  flex: none;
  width: auto;
  height: auto;
  border: 0;
  border-radius: 0;
  background: transparent;
}

/* The capture entry is the page's primary action, so it keeps a surface — but a
   flat one, without the shadow that made it read as just another card. */
.reference-page.mobile-dashboard .quick-task.tape-action {
  min-height: 54px;
  padding: 0 16px;
  border-radius: var(--radius-button);
  background: var(--bg-card);
  box-shadow: none;
  font-size: .9375rem;
  font-weight: 650;
}

/* --- §5b 「家里现在」card row ------------------------------------------------
   The reference's signature band: three compact cards in a row — one dark, two
   photographic — each carrying a small label, a headline figure and a short
   list. The dark one is always the "what is actually pending" card.

   The project's real modules map onto it one for one, which is why this is a
   composition change and not a mock-up:

     tasks     -> 待办任务   dark, "N 项待完成" + the tasks themselves
     inventory -> 家庭库存   photo, "N 种需关注" + the items
     household -> 家里现在   photo, the ventilation / cleaning state

   Those three are pinned as one row, so the band is the reference's band and not
   merely three cards that happen to sit near each other.

   Cards rather than open rows is the right call *here* specifically: these are
   genuinely independent objects, which is the one case the brief reserves a
   surface for. The modules that stay open are the ones that are really a slice
   of a longer list.

   The grid is explicit because auto-placement cannot express this from a flat
   list of modules: the band is three-up, the knowledge/reminder pair is two-up
   and the hero is full width, and those spans are not derivable from the widget
   list. Rows are pinned by `data-widget-key`, so a module that is absent for a
   given role or stage leaves no row behind at all — an unoccupied row track is
   never created, so the prenatal and the postnatal Home each stay gap-free
   rather than carrying a blank band.

   One row is pinned to something that is not a module: the 「家里现在」 band
   header. It is emitted only when at least one band module actually rendered, so
   header and band go absent together and that row never exists on its own. */
.reference-page.mobile-dashboard .widget-stack {
  display: grid;
  /* v8 revision. The 48-track split that used to live here encoded the *older*
     mockup's band, where a dark 待办任务 card ran 127 design px against two 107px
     photographic cards beside it. reference_family_os_v8 has no such row: every
     band on it is one card that *contains* its members — 「家里现在」 holds three
     sub-cards, 「快捷动作」 holds four pills — so the container is a real element
     in the template and each band is a single full-width grid item. With nothing
     left sharing a row, a second track would only manufacture implicit columns
     (which is exactly what the 1..8 / 1..49 `grid-area` pins below used to do),
     so the honest grid is one column and the row gap is the whole rhythm. */
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
}

/* The modules carry their own `margin-bottom` for the old single-column flow.
   Inside a grid those margins would stack on top of every row gap and double
   the spacing, so the direct child is flattened and the gap becomes the only
   spacing there is. */
.reference-page.mobile-dashboard .widget-stack > .dashboard-widget > * { margin: 0; }

/* v7.css collapses an *empty* tasks/reminders module into a 48px pointer row:
   `display: grid`, 14px of padding and a rule underneath. That is the right
   answer for a stack of open rows — §10's point is that an empty module must not
   masquerade as a large blank panel — but it is the wrong answer for a band card
   that carries its own empty line. Left in place it shrinks the card to 84.66px
   inside a 112.66px cell, so the 待办任务 card renders visibly narrower than the
   two cards beside it, and its label and figure wrap.

   The module is still visibly empty: the card says so in words, at the band's
   size. What is removed is only the second, outer collapse.

   `gap`, `align-items` and `grid-template-columns` have to be reset explicitly
   rather than just swapped: `gap` applies to flex containers too, so the leftover
   `gap: 8px 16px` would space the card's rows, and `align-items: center` would
   size the card to its content instead of to the cell. */
.reference-page.mobile-dashboard .dashboard-widget[data-widget-state="empty"]:is([data-widget-key="tasks"], [data-widget-key="reminders"]) {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  min-height: 0;
  padding: 0;
  border-bottom: 0;
  background: transparent;
}

/* §4 gave every compact or empty module a half-width *section* surface — 14px of
   padding and its own card background — because in the open-row design the
   section itself was the card. Every module in this band now carries its own
   card, so that section-level padding nests one card inside another and takes
   28px off the inner card's width: the 家庭库存 card rendered 84.67px wide inside
   a 112.66px cell, which is what truncated its label to 「家...」.

   Scoped to the modules that were converted, so a module still drawn as an open
   row keeps §4's treatment. */
.reference-page.mobile-dashboard .dashboard-widget:is(
  [data-widget-key="tasks"],
  [data-widget-key="inventory"],
  [data-widget-key="household"],
  [data-widget-key="knowledge"],
  [data-widget-key="reminders"]
) {
  padding: 0;
  background: transparent;
  border-radius: 0;
}

/* The grid row already makes every cell the same height; this makes the card
   inside the cell use that height, so the band reads as three equal cards
   rather than three cards of whatever height their own content happens to be. */
.reference-page.mobile-dashboard .widget-stack > .dashboard-widget {
  display: flex;
  flex-direction: column;
}

.reference-page.mobile-dashboard .widget-stack > .dashboard-widget > * { flex: 1 1 auto; }

/* --- the card itself ------------------------------------------------------ */
.reference-page.mobile-dashboard .v7-now {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 126px;
  padding: 12px 13px;
  border-radius: var(--radius-small);
  background: var(--bg-card);
  overflow: hidden;
}

.reference-page.mobile-dashboard .v7-now__head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 20px;
}

/* The label is a whisper and the figure speaks. `h2` is accepted alongside
   `span` because the knowledge card must keep rendering its literal
   `<h2>今天知道</h2>` — the sealed Phase 14 heading test reads that exact markup
   and counts the string once in the file — so the element cannot be swapped for
   a `<span>` to suit the styling. */
.reference-page.mobile-dashboard .v7-now__head :is(span, h2) {
  margin: 0;
  min-width: 0;
  font-size: .6875rem;    /* 11px */
  font-weight: 650;
  letter-spacing: .01em;
  color: var(--text-secondary);
  /* A band card is a third of the content width, so a four-character label plus
     its link only just fits. Wrapping is what actually happened first, and it
     broke 「待办任务」 across two lines and knocked the whole band's rhythm out;
     clipping is the lesser failure and the labels here are all short. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.reference-page.mobile-dashboard .v7-now__head a {
  margin-left: auto;
  display: grid;
  place-items: center;
  min-width: 24px;
  min-height: 24px;
  font-size: .6875rem;
  color: var(--text-tertiary);
  text-decoration: none;
}

/* Label and figure share one line, exactly as the reference sets 「5 项待完成」:
   the number carries the weight and the unit stays small beside it. */
.reference-page.mobile-dashboard .v7-now__figure {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 1px 0 3px;
  font-size: .75rem;
  font-weight: 550;
  color: var(--text-secondary);
  /* 「0 项待完成」 must stay on one line: the number is 28px and the unit is the
     only thing that can give, so it must not be allowed to wrap. */
  white-space: nowrap;
  min-width: 0;
}

.reference-page.mobile-dashboard .v7-now__figure strong {
  font-size: 1.75rem;     /* 28px */
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.reference-page.mobile-dashboard .v7-now__list {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: .6875rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* On the photographic cards the reference puts the item names in a narrow
   right-aligned column and gives the bottom-LEFT corner to the picture, so the
   two never overlap. Measured off the reference, that column is ~45% of the card
   and the photograph ~60%, meeting in the middle.

   This column is wider (76%) and a step smaller (10px) than the reference's,
   because the reference's names are three characters long (「纸尿裤」) and a real
   inventory name is not (「桂月集·月盈礼盒—糕点组合」, 13). At the reference's own
   proportions a real name clipped mid-word into 「洁都在…」, which reads as a
   rendering fault rather than as truncation. The percentages resolve against the
   card's *content* box, not its border box, so 76% is what actually yields the
   ~64px that holds thirteen characters in the two-line clamp. */
.reference-page.mobile-dashboard .v7-now--photo .v7-now__list {
  align-self: flex-end;
  justify-self: end;
  max-width: 76%;
  justify-items: end;
  text-align: right;
  font-size: .625rem;     /* 10px */
}

/* The reference's photographic cards carry no bullet — the right-aligned column
   IS the list. Only the dark 待办任务 card has markers, and there they are the
   reference's outlined check circles rather than filled dots. */
.reference-page.mobile-dashboard .v7-now--photo .v7-now__list li {
  display: block;
  grid-template-columns: none;
}

.reference-page.mobile-dashboard .v7-now--photo .v7-now__list li::before { display: none; }

/* Real content is longer than the reference's demo content — a real item name is
   「桂月集·月盈礼盒—糕点组合」 where the reference's is 「纸尿裤」 — so a line is
   clamped to two rows instead of being cut with an ellipsis. 「现在没有需要处」
   reads as a broken string; two wrapped lines read as a shortened one, which is
   what it is. */
.reference-page.mobile-dashboard .v7-now__list li {
  display: grid;
  grid-template-columns: 4px minmax(0, 1fr);
  gap: 7px;
  align-items: baseline;
  min-width: 0;
}

.reference-page.mobile-dashboard .v7-now__list li > :is(a, span) {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  /* `-webkit-line-clamp` is not honoured for every box type, and it was not
     honoured here, so the two-line limit is also enforced by height: the list
     sets `line-height: 1.5`, so two lines are exactly 3em. */
  max-height: 3em;
}

.reference-page.mobile-dashboard .v7-now__list li::before {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--warm-brown-1);
  transform: translateY(-2px);
}

.reference-page.mobile-dashboard .v7-now__list a { color: inherit; text-decoration: none; }

.reference-page.mobile-dashboard .v7-now__empty {
  margin: 0;
  font-size: .6875rem;
  line-height: 1.5;
  color: var(--text-tertiary);
}

/* --- dark variant (待办任务) ----------------------------------------------- */
.reference-page.mobile-dashboard .v7-now--dark { background: var(--accent-dark); }

.reference-page.mobile-dashboard .v7-now--dark .v7-now__head span,
.reference-page.mobile-dashboard .v7-now--dark .v7-now__figure { color: rgba(255, 255, 255, .70); }

/* The reference's dark card puts a translucent circular control at the head's
   right edge, not a bare text link. The target here is still the real 今天
   filter — this only changes how the control is drawn. (The reference's circle
   holds a ＋ that opens task creation; that affordance already exists as the
   记一件事 pill in 快捷动作, and duplicating it here would add a second entry
   point to a write path this card has never owned.) */
.reference-page.mobile-dashboard .v7-now--dark .v7-now__head a {
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .15);
  color: rgba(255, 255, 255, .90);
  font-size: .6875rem;
}
.reference-page.mobile-dashboard .v7-now--dark .v7-now__figure strong { color: #fff; }
.reference-page.mobile-dashboard .v7-now--dark .v7-now__list li { color: rgba(255, 255, 255, .84); }
.reference-page.mobile-dashboard .v7-now--dark .v7-now__list a { color: rgba(255, 255, 255, .84); }
/* Outlined check circles, as the reference draws them — an 11px ring, not a dot. */
.reference-page.mobile-dashboard .v7-now--dark .v7-now__list li {
  grid-template-columns: 11px minmax(0, 1fr);
  gap: 8px;
}
.reference-page.mobile-dashboard .v7-now--dark .v7-now__list li::before {
  width: 11px;
  height: 11px;
  border: 1.5px solid rgba(255, 255, 255, .48);
  border-radius: 50%;
  background: transparent;
  transform: translateY(-1px);
}
.reference-page.mobile-dashboard .v7-now--dark .v7-now__empty { color: rgba(255, 255, 255, .62); }

/* --- photographic variant (家庭库存 / 清洁维护) ----------------------------
   The reference anchors the photograph in the card's BOTTOM-LEFT corner and
   keeps the text in the clean cream band to its right: the item names sit in a
   right-aligned column and never touch the picture. The first attempt here had
   it the other way round — photograph bottom-right under a heavy wash — which
   both killed the picture's contrast and still let the list land on top of it.

   With the photograph moved out of the text column, the wash is not needed at
   all: the mask alone melts its top-right edge into the card, and the picture
   keeps the real contrast that is the entire point of using a photograph. */
.reference-page.mobile-dashboard .v7-now__media {
  position: absolute;
  left: 0;
  bottom: 0;
  /* 50%, not the reference's ~60%: the extra ten points go to the item-name
     column, which is the side that actually runs out of room with real data.
     The overlap with that column lands in the picture's top-right, which the
     mask above has already faded out, so the two never fight. */
  width: 50%;
  /* 50%, not the 58% a pure corner-anchor would suggest: the head row plus the
     figure occupy the card's top ~52px, and a taller figure would put the
     photograph's opaque corner directly under the figure's own left edge. */
  height: 50%;
  margin: 0;
  z-index: 0;
  border-bottom-left-radius: var(--radius-small);
  /* Opaque at the bottom-left corner, fading toward the top-right — toward the
     figure above and the list beside. The direction is load-bearing: `to top
     left` would leave the picture's hardest edge exactly where the label sits. */
  -webkit-mask-image: linear-gradient(to top right, #000 42%, transparent 94%);
  mask-image: linear-gradient(to top right, #000 42%, transparent 94%);
}

.reference-page.mobile-dashboard .v7-now__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* There is deliberately no wash over the photograph any more. The earlier
   version needed one because the picture sat underneath the list; now that it
   owns the bottom-left corner and the list owns the right column, a wash would
   only grey the picture back down — which is exactly what made the first attempt
   read as a smudge instead of a photograph. */

.reference-page.mobile-dashboard .v7-now--photo > *:not(.v7-now__media) {
  position: relative;
  z-index: 2;
}

/* --- §5c the knowledge card's own two lines --------------------------------
   The knowledge card carries a sentence rather than a figure, so it needs a
   line style the band cards do not: the sentence is the card's subject, and the
   provenance line under it stays a footnote. */
.reference-page.mobile-dashboard .v7-now__lead {
  margin: 0 0 4px;
  font-size: .8125rem;
  font-weight: 650;
  line-height: 1.45;
  letter-spacing: -.01em;
  color: var(--text-primary);
}

.reference-page.mobile-dashboard .v7-now__lead a { color: inherit; text-decoration: none; }

.reference-page.mobile-dashboard .v7-now__source {
  display: block;
  font-size: .6875rem;
  line-height: 1.5;
  color: var(--text-tertiary);
}

/* --- §5d 阶段提示 ----------------------------------------------------------
   The module's card treatment lives in §4, next to the rule it replaces, because
   the selector it has to beat is a four-class `.v7-focus.attention-focus--quiet`
   and splitting the two across the file would invite them to drift apart. */

/* --- §5e 快捷动作 ----------------------------------------------------------
   The reference's fourth band: a section label with a one-line promise, then a
   row of four flat pills sitting directly on the page — no enclosing card, which
   is why this band has no `.v7-now` surface.

   Each pill is an existing canonical route, and each is capability-gated in the
   template, so the row shows what this member can actually do and never a
   placeholder action. Four is the reference's count, and the pills share one
   358px row at the 390px target — hence the same 11px label the quick-action row
   uses, for the same reason. */
.reference-page.mobile-dashboard .v7-quick-head,
.reference-page.mobile-dashboard .v7-band-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 10px;
}

/* The band header is a grid row of its own, so the row gap already spaces it
   from the cards below; a bottom margin here would be added on top of that gap
   and open a hole the reference does not have. The quick header keeps its 10px
   because the pills sit inside the same module, not in a row of their own. */
.reference-page.mobile-dashboard .v7-band-head { margin: 0; }

.reference-page.mobile-dashboard .v7-quick-head h2,
.reference-page.mobile-dashboard .v7-band-head h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 750;
  letter-spacing: -.02em;
}

.reference-page.mobile-dashboard .v7-quick-head span,
.reference-page.mobile-dashboard .v7-band-head span {
  margin-left: auto;
  font-size: .6875rem;
  color: var(--text-tertiary);
}

/* Flex rather than a fixed four-column grid: the row is capability-gated, so a
   member who cannot manage inventory gets three pills, and a four-column grid
   would leave the fourth slot empty. Each pill grows to fill whatever row it is
   in, so the band stays balanced at any count. */
.reference-page.mobile-dashboard .v7-quick-row {
  display: flex;
  gap: 8px;
}

.reference-page.mobile-dashboard .v7-quick-pill {
  display: flex;
  flex: 1 1 0;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  min-height: 48px;
  padding: 0 9px;
  border: 1px solid var(--line-subtle);
  border-radius: 14px;
  background: var(--bg-card);
  color: var(--text-primary);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: -.01em;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
}

.reference-page.mobile-dashboard .v7-quick-pill .ui-icon {
  width: 17px;
  height: 17px;
  flex: none;
  color: var(--text-secondary);
}

/* --- §6 Bottom navigation --------------------------------------------------
   The reference draws the bar as a floating capsule that the page scrolls
   behind. graphite.css draws it flush with a top border and a translucent blur,
   which reads as a system tab bar rather than the reference's detached control.

   The IA is now four destinations plus the centre action — the reference's own
   five-slot shape, and the count `UI_QUALITY_GATE.md` approves ("Four icon/text
   navigation entries", "roughly 4–5 entries").

   `更多` is HIDDEN, not retired (owner, 2026-09-25: 「隐藏就好 … 不是源头去除」).
   The distinction is deliberate and must survive future edits:
     * the route, the template and every link into them stay exactly as they are,
       so nothing is unreachable and no bookmark breaks;
     * it is simply no longer a *tab* — the owner's intent is that its functions
       get re-homed into the proper modules later, and until then the Home
       header's utilities row is what exposes the daily ones;
     * so this rule hides the fifth anchor in the tab bar and nothing else. Do
       not delete the markup, and do not retarget the other anchors' indices —
       `nth-of-type` counts the hidden anchor too.

   On centring the `＋`
   --------------------
   With four destinations the bar has five slots, so the centre column IS the
   middle one and the `＋` lands on the midpoint by construction. (Three
   destinations would give four slots — an even count with no middle slot, which
   is why keeping 首页 is what makes the centring work rather than what breaks it.)

   The entries are placed by `nth-of-type` rather than by reordering the
   template: Slice 8 parses this nav's markup, so the DOM stays untouched and
   only the visual order moves. The fifth entry is hidden rather than removed for
   the same reason — the route stays alive, it just is not a tab any more. */
.reference-nav {
  position: fixed;
  z-index: 7;
  left: 50%;
  bottom: max(8px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(406px, calc(100% - 24px));
  display: grid;
  grid-template-columns: 1fr 1fr 56px 1fr 1fr;
  align-items: center;
  gap: 0;
  padding: 7px 8px;
  border: 0;
  border-radius: 28px;
  background: var(--bg-section);
  box-shadow: 0 10px 26px rgba(40, 30, 24, .10);
  backdrop-filter: none;
}

.reference-nav > a:nth-of-type(1) { grid-column: 1; }
.reference-nav > a:nth-of-type(2) { grid-column: 2; }
.reference-nav > a:nth-of-type(3) { grid-column: 4; }
.reference-nav > a:nth-of-type(4) { grid-column: 5; }
/* The fifth destination (`更多`) is retired from the tab bar. */
.reference-nav > a:nth-of-type(5) { display: none; }

.reference-nav a {
  grid-row: 1;
  min-height: 52px;
  gap: 3px;
  padding-bottom: 0;
  font-size: .71875rem;   /* 11.5px */
  font-weight: 550;
  color: var(--text-tertiary);
}

.reference-nav .ui-icon { width: 22px; height: 22px; }

/* The active state is weight + colour, not the legacy underline bar: the
   reference's capsule has no rule under the current tab. */
.reference-nav a[aria-current] {
  color: var(--text-primary);
  font-weight: 700;
}

.reference-nav a[aria-current]::after { display: none; }

.reference-nav > .nav-plus {
  grid-column: 3;
  grid-row: 1;
  position: relative;
  left: auto;
  /* The disc is a raised FAB that straddles the bar's top edge, not a chip that
     sits inside it.

     Measured on the mockup (`ref/v8-bot.png`): bar top y=348, bar bottom y=525,
     disc top y=327, disc ~118px across. Scaled by the bar (177px ↔ our 66px,
     k=0.3729) that is a 44px disc whose top clears the bar's top by 21k = **7.8px**
     — and, equivalently, a disc centre sitting 50.5k = **18.8px above the bar's
     centre**.

     `-19px` is the offset from the grid's own centred position (781px for a 46px
     disc in a 66px bar), NOT the 7.8px clearance: an earlier pass wrote `-8px`
     by reading the clearance as if it were the shift, which left the disc 2px
     *inside* the bar instead of 9px above its edge. `position: relative` keeps
     the shift out of layout, so the five columns are untouched and the ＋ stays
     on the exact midpoint. */
  top: -19px;
  transform: none;
  width: 46px;
  height: 46px;
  min-height: 46px;
  margin: 0 auto;
  border: 0;
  /* The global `button` reset at app.css:14 gives every button
     `padding: 10px 18px`. On this 46px disc that leaves a content box of only
     46 - 36 = 10px, while the `＋` needs a 24px grid track — so the track
     *overflowed* the content box and began at the content origin instead of
     being centred, pinning the glyph 7px right of the disc centre. Measured:
     glyph 190..214 inside a 172..218 disc, identically at 390 and 430, while
     the disc itself was perfectly centred — which is why an earlier probe that
     only measured the disc reported no problem. `place-items:center` cannot
     rescue an overflowed track, so the padding has to go; `place-content:center`
     then keeps the glyph centred even if a future one is wider than the disc. */
  padding: 0;
  place-content: center;
  border-radius: 50%;
  background: var(--accent-dark);
  color: var(--text-on-dark);
  /* The mockup's disc reads as lifted off the bar rather than painted on it.
     Now that it also overhangs the bar's edge, a shadow is what keeps the
     overhang legible as elevation instead of as a clipping accident. */
  box-shadow: 0 6px 16px rgba(26, 22, 17, .22);
}

/* The glyph is the icon set's own `plus`, not the fullwidth `＋` character.
   The character carries a CJK em box and a much heavier stem than the mockup's
   thin cross, and it was the one mark on the page drawn outside the icon
   system — every other glyph on this bar is `stroke-width: 1.65` on a 24
   viewBox. The `<span>` wrapper stays: it is the grid item `place-content:
   center` positions, and the note above records that its track sizing is what
   once put the glyph 7px off centre. */
.reference-nav .nav-plus > span {
  display: grid;
  place-items: center;
  line-height: 1;
}

.reference-nav .nav-plus > span .ui-icon { width: 22px; height: 22px; }

/* --- §7 Breakpoints --------------------------------------------------------
   Two explicit sizes rather than a continuous vw ramp, per the brief. */
@media (min-width: 400px) {
  .reference-page.mobile-dashboard .v7-header__greeting { font-size: 50px; }
}

@media (max-width: 374px) {
  .shell:has(.reference-page) { padding-inline: 14px; }

  .reference-page.mobile-dashboard .v7-header__greeting { font-size: 42px; }

  .reference-nav { width: calc(100% - 16px); }

  .reference-page.mobile-dashboard .round-actions > a > span { width: 50px; height: 50px; }
}

/* ==========================================================================
   §8  reference_family_os_v8 — the card language
   ==========================================================================

   The owner replaced the reference on 2026-09-25 (941x1672). This section is
   the v8 retune. It is appended last so it wins on source order against §1–§7,
   and it is scoped to the Family home shell wherever a rule could leak.

   What the new reference actually is
   ----------------------------------
   A light, airy page: one warm off-white ground, then a column of near-white
   cards. Every card opens with the same header row — a tinted rounded-square
   icon tile, a bold title, a quiet right-hand link — and holds one of three
   body shapes: a row of inset sub-cards, a row of pills, or rows with a
   circular mark. There is **no dark emphasis card anywhere**; the only dark
   surfaces on the page are the ＋ button and the last quick-action pill. v7's
   dark 待办任务 band was an artefact of the older mockup and is gone.

   Measured off the mockup, anchored on its ＋ disc (89px across = the 46px
   button, so 1.935 ref px per design px; screen x 91.5..846.5):

     ground #E8E7E5 · cards #F2F0ED · hero #E8E1D9 → #C1B8AF
     「家庭 OS」 ink 31.0dp → ~35dp type   greeting / sub-line ~14dp
     hero headline ink 19.1dp → ~21.5dp   countdown number ~32dp
     trio card 113dp wide · 12dp gutter · page margin ~15dp

   One thing deliberately does NOT carry over: the ground. The sealed
   test_v7_visual_acceptance asserts `body` is rgb(247,246,242) (= --bg-page),
   so the reference's stronger page/card contrast is pushed *into the cards*
   instead — near-white on the token ground, rather than grey on a deeper one.
   ========================================================================== */

.reference-page.mobile-dashboard {
  --v8-card: #FDFCFA;        /* container cards read as paper */
  --v8-card-soft: #F3F0EA;   /* their members read as inset */
  --v8-hero: #EFE9DE;        /* the one warm sand surface */
  --v8-hero-deep: #DCD2C4;
  --v8-ink: #1A1611;
  --v8-ink-2: #6B6259;
  --v8-ink-3: #A69C90;
  --v8-dark: #2E2822;        /* ＋ disc and the accent pill */
  --v8-line: rgba(26, 22, 17, .075);
  --v8-tile-sage: #E2E7DD;
  --v8-tile-sand: #F0E7D8;
  --v8-tile-rose: #F3E3DE;
  --v8-tile-stone: #EAE7E1;
  --v8-r-card: 18px;
  --v8-r-sub: 14px;
  color: var(--v8-ink);
}

/* --- §8.1 Header ---------------------------------------------------------
   Small greeting, huge OS mark, quiet sub-line; on the right a date pill above
   a two-line aside. v7 had the greeting huge and the mark small, and that
   inversion is the most visible difference in the header. */
.reference-page.mobile-dashboard .v8-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 0 12px;
  margin: 0 0 16px;
}

/* The reference's header is three rows, not two columns: the greeting and the
   date pill share row 1, the OS mark owns row 2 *across the full width*, and the
   sub-line and the handwritten aside share row 3. Both wrappers are dissolved so
   their children become grid items in their own right — the pill and the aside
   have to land in different rows, which a single flex column cannot do. This is
   also what removes the collision the two-column version had: at 35px 「家庭 OS」
   measured 159px and the aside left the first track only 153px. */
.reference-page.mobile-dashboard .v8-head__main { display: contents; }
.reference-page.mobile-dashboard .v8-head__aside { display: contents; }

.reference-page.mobile-dashboard .v8-head__greetline { grid-area: 1 / 1 / 2 / 2; }
.reference-page.mobile-dashboard .v8-head__brand { grid-area: 2 / 1 / 3 / 3; }
.reference-page.mobile-dashboard .v8-head__sub { grid-area: 3 / 1 / 4 / 2; }
.reference-page.mobile-dashboard .v8-head__quote { grid-area: 3 / 2 / 4 / 3; justify-self: end; }
.reference-page.mobile-dashboard .v7-date-pill { grid-area: 1 / 2 / 2 / 3; justify-self: end; }

.reference-page.mobile-dashboard .v8-head__greetline {
  display: flex;
  align-items: baseline;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-head__hello,
.reference-page.mobile-dashboard .v8-head__comma {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--v8-ink-2);
}

.reference-page.mobile-dashboard .v8-head__brand {
  margin: 1px 0 4px;
  font-size: 33px;
  font-weight: 800;
  line-height: 1.05;
  /* The sealed heading name is 「家庭 OS」 *with a space*. An earlier pass
     tightened it with `word-spacing: -0.24em` on the belief that the mockup set
     the two tight; measuring says otherwise.

     Measured as INK gaps (mockup is a 2.0x render, so ref px / 2 = design px):
     「家」ink ends x=191 and 「庭」starts x=196 → 2.5 design px, the face's normal
     CJK gap; 「庭」ends x=255 and 「O」starts x=263 → **3.5 design px**, i.e. only
     ~1px wider than ordinary glyph spacing. At `-0.24em` the space advance
     netted 0.3px and the two runs measured as a single continuous ink run
     (x=33..253) — the title read 「家庭OS」 with the glyphs touching, tighter than
     the mockup. Tuned attempts at `-0.13em` / `-0.04em` landed at 0.5 / 2.0
     design px, still under the target and still shrinking the space below what
     the face draws by itself, so the tightening is dropped entirely rather than
     fitted to a threshold-sensitive ink measurement. */
  word-spacing: normal;
  letter-spacing: -0.015em;
  color: var(--v8-ink);
}

.reference-page.mobile-dashboard .v8-head__sub {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--v8-ink-2);
}

/* `.v8-head__aside` is dissolved by `display: contents` in §8.1, so it needs no
   box of its own; the pill and the quote are placed as grid items directly. */

/* The date pill. It also carries the sealed `.v7-date-pill` hook that
   test_v7_visual_acceptance waits for, so both names stay on the element. */
.reference-page.mobile-dashboard .v7-date-pill,
.reference-page.mobile-dashboard .v8-date-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px 4px 5px;
  border: 0;
  border-radius: 999px;
  background: var(--v8-card);
  box-shadow: 0 1px 2px rgba(26, 22, 17, .05);
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1.3;
  color: var(--v8-ink);
  text-decoration: none;
}

/* A light-grey CIRCLE with the glyph in dark ink — the inverse of the dark chip
   this used to draw, and what the mockup has. Measured off `ref/zoom/z-pill.png`
   (4x of a 2.0x render): the disc is ~22 design px across and its fill sits one
   step darker than the pill, so the icon reads as a quiet badge rather than as
   the pill's focal point. */
.reference-page.mobile-dashboard .v8-date-pill__icon {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #E4E2DE;
  color: var(--v8-ink);
}

.reference-page.mobile-dashboard .v8-date-pill__icon .ui-icon { width: 14px; height: 14px; }

.reference-page.mobile-dashboard .v8-date-pill small {
  font-size: 12px;
  font-weight: 500;
  color: var(--v8-ink-3);
}

/* The handwritten aside. The project ships no handwriting face, so the body
   face at a light weight carries the composition and the drawn stroke under it
   carries the "written by hand" cue. */
.reference-page.mobile-dashboard .v8-head__quote {
  position: relative;
  margin: 0;
  padding-right: 4px;
  font-family: "Segoe Script", "Ink Free", KaiTi, STKaiti, var(--font-ui);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.55;
  text-align: right;
  color: var(--v8-ink-2);
  transform: rotate(-1.5deg);
}

.reference-page.mobile-dashboard .v8-head__quote-rule {
  display: block;
  width: 52px;
  height: 1px;
  margin: 5px 1px 0 auto;
  background: var(--v8-ink-3);
  transform: rotate(-2.5deg);
}

/* The utilities row (role switcher / layout editor / density) is a control
   strip, not editorial content: it stays small and quiet under the header. */
.reference-page.mobile-dashboard .v7-header-utils {
  margin: 0 0 14px;
  gap: 8px;
  font-size: 11.5px;
  color: var(--v8-ink-3);
}

/* --- §8.2 The card ------------------------------------------------------- */
.reference-page.mobile-dashboard .v8-card {
  display: flex;
  flex-direction: column;
  gap: 11px;
  padding: 13px 14px;
  border: 0;
  border-radius: var(--v8-r-card);
  background: var(--v8-card);
  box-shadow: 0 1px 2px rgba(26, 22, 17, .035);
}

.reference-page.mobile-dashboard .v8-card__head {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-card__head h2 {
  margin: 0;
  min-width: 0;
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--v8-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The right-hand link and the quiet meta line both ride at the far edge. */
.reference-page.mobile-dashboard .v8-card__link,
.reference-page.mobile-dashboard .v8-card__meta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: auto;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--v8-ink-3);
  white-space: nowrap;
}

.reference-page.mobile-dashboard .v8-card__meta { text-overflow: ellipsis; overflow: hidden; }
.reference-page.mobile-dashboard .v8-card__link .ui-icon { width: 13px; height: 13px; }

/* --- §8.2b The module wrapper is a layout box, never a card ---------------
   §4 (lines ~320-351) gives a COMPACT or `empty` module's *section* its own
   surface — 14px of padding, a card background and a `span 1` grid column —
   because in the open-row design the section itself was the card. Every module
   on this page now draws its own `.v8-card` / `.v8-sub`, so that section-level
   padding nests one card inside another, takes 28px off the inner card, and
   shifts it down by 13px.

   That is not hypothetical: 「本周重点」 is COMPACT and resolves to `empty` on a
   household with no WEEK-tier tasks, so it inherited `padding: 13px 14px`. Its
   card measured 146px inside a 174px pair column (against 「今日提醒」's 174px),
   sat 13px lower, was 26px shorter — and its 58px header needed 51px, so the
   reference's own card title rendered as 「本周…」.

   `span 1` is reset here too, and for a second reason: the size-driven span
   (`.dashboard-widget--standard { grid-column: 1 / -1 }`) is correct on the
   top-level 2-up grid but wrong inside a container body, where every member owns
   exactly one column. Without this the 家里现在 band would stack into three
   full-width rows the moment a household had a non-empty task list, because only
   the *empty* case was being caught by §4's span override.

   Scoped to the three container contexts, so the top-level grid keeps its
   size-driven spans. */
.reference-page.mobile-dashboard .v8-card__body > .dashboard-widget,
.reference-page.mobile-dashboard .v8-pair > .dashboard-widget {
  grid-column: span 1;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* Same reset on the page's own stack, where the span must be left alone. */
.reference-page.mobile-dashboard .widget-stack > .dashboard-widget {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* The tinted rounded-square tile every card header opens with. The reference
   rotates its tint by band — sand, sage, rose — so the tiles are named, not
   derived from the module. */
.reference-page.mobile-dashboard .v8-tile {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: var(--v8-tile-stone);
  color: #4A423A;
}

.reference-page.mobile-dashboard .v8-tile .ui-icon { width: 17px; height: 17px; }
.reference-page.mobile-dashboard .v8-tile--sage  { background: var(--v8-tile-sage); }
.reference-page.mobile-dashboard .v8-tile--sand  { background: var(--v8-tile-sand); }
.reference-page.mobile-dashboard .v8-tile--rose  { background: var(--v8-tile-rose); }
.reference-page.mobile-dashboard .v8-tile--stone { background: var(--v8-tile-stone); }

/* Body shapes. */
.reference-page.mobile-dashboard .v8-card__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

/* The 3-up row inside 「家里现在」. The reference's three sub-cards are equal. */
.reference-page.mobile-dashboard .v8-card__body--3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

/* The pill row inside 「快捷动作」. Four columns at the reference's own count; the
   gap is tight because the labels are four characters each and every pixel of
   column width is a pixel of label. */
.reference-page.mobile-dashboard .v8-card__body--4 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}

/* The band's sub-card row when a capability filter leaves two members (or one).
   A count-driven body can never lay out an empty trailing column. */
.reference-page.mobile-dashboard .v8-card__body--2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.reference-page.mobile-dashboard .v8-card__body--1 {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
}

/* The band modules are grandchildren of `.widget-stack` now (the container sits
   between them), so §5b's flattening rules — which only reach direct children —
   no longer apply. Repeat them one level down, and stretch so the three
   sub-cards in a row share a height instead of each sizing to its own text. */
.reference-page.mobile-dashboard .v8-card__body > .dashboard-widget {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-card__body > .dashboard-widget > * {
  flex: 1 1 auto;
  margin: 0;
}

/* --- §8.3 Sub-card -------------------------------------------------------
   A member of a container card: a CIRCLE icon disc, then the label row (label
   + chevron) and the value, both left-aligned to the right of the disc.

   Every number in this block is a measurement off the mockup, not a taste call.
   Measured on `ref/zoom/d-home.png` (2.6x of a 2.0x render, so crop/5.2 = design
   px) with the ink of sub-card 1's label starting at crop x=250 and the sub-card
   itself at crop x=47: the text column begins (250-47)/5.2 = **39.0 design px**
   into a **100 design px** sub-card. 10px padding + 23px disc + 8px gap = 41px
   reproduces that within a pixel.

   The shape it replaces was 11px padding + a 27px SQUARE tile + 9px gap, which
   started the text at 47px and left the column **45px** wide inside a 103px
   sub-card. Every value in the band wrapped, and the optional detail line was
   clipped mid-word (`1 项临近日期 / 桂月嫂 · 月蓝礼…`). Two fixes: the disc is
   now the mockup's circle at the mockup's size, and the third line is gone —
   the mockup's sub-card is two lines and nothing else.

   The tint rotates per member the way the mockup's does (sage / rose / sage),
   keyed off the icon tile's own tone with `:has()` so a member declares its
   tint exactly once instead of carrying a second class. */
.reference-page.mobile-dashboard .v8-sub {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  padding: 10px;
  border-radius: var(--v8-r-sub);
  background: var(--v8-card-soft);
  color: inherit;
  text-decoration: none;
}

/* A circle here; only the card HEADER's tile is a rounded square. */
.reference-page.mobile-dashboard .v8-sub .v8-tile {
  width: 23px;
  height: 23px;
  border-radius: 50%;
}

.reference-page.mobile-dashboard .v8-sub .v8-tile .ui-icon { width: 13px; height: 13px; }

.reference-page.mobile-dashboard .v8-sub:has(.v8-tile--sage)  { background: #E9EDE5; }
.reference-page.mobile-dashboard .v8-sub:has(.v8-tile--rose)  { background: #F2E7E2; }
.reference-page.mobile-dashboard .v8-sub:has(.v8-tile--sand)  { background: #F0E9DD; }
.reference-page.mobile-dashboard .v8-sub:has(.v8-tile--stone) { background: var(--v8-card-soft); }

.reference-page.mobile-dashboard .v8-sub__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 auto;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-sub__head {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-sub__label {
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--v8-ink);
  white-space: nowrap;
}

.reference-page.mobile-dashboard .v8-sub__head .ui-icon {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  color: var(--v8-ink-3);
}

/* The value WRAPS rather than clipping. The mockup's values are one line each
   because its value type resolves to ~9.4 design px (sub-card 3's 「下次阿姨
   9月13日」 is 59.8 design px of ink across 9.5 em), which is below this app's
   11px floor — and that floor is the owner's own 全端字阶基线 (§9). Keeping the
   floor and letting the line run to two is the honest trade; ellipsing it hid
   half the string. `overflow-wrap: anywhere` stops a long unbroken run from
   pushing the card wider than its grid column. */
.reference-page.mobile-dashboard .v8-sub__value {
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--v8-ink-2);
  overflow-wrap: anywhere;
}

/* Where a value has to wrap it must break between its count and its noun
   phrase, not inside the phrase. At 390 the text column is 52.3px and
   「0 项需要处理」 needs ~66px, so a wrap is unavoidable at the 11px floor; left
   to itself the browser broke it as 「0 项需要 / 处理」. Each chunk is atomic, so
   the only break opportunity is the one between them. At 430 the column is
   65.7px and both chunks fit on one line, so nothing changes there. */
.reference-page.mobile-dashboard .v8-sub__chunk { display: inline-block; }

/* The quiet second line. This is the reminder row's time under its title —
   「补充维生素D3 / 今天 08:00」, which IS the mockup's 今日提醒 shape — and it is
   the only remaining user of the class: the 家里现在 sub-cards used it as a
   THIRD line (an item-name list) and no longer emit it, because the mockup's
   sub-card is two lines and the list was clipping inside a 52px column. So the
   two-line clamp is gone with the list; what stays is a plain quiet line that
   wraps if it has to instead of hiding half of itself. */
.reference-page.mobile-dashboard .v8-sub__list {
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--v8-ink-3);
  overflow-wrap: anywhere;
}

/* --- §8.4 Pills ----------------------------------------------------------
   The reference puts the icon *left of* the label inside a soft pill; v7
   stacked them. The last pill is the page's second and final dark surface. */
.reference-page.mobile-dashboard .v8-pill {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 0;
  padding: 10px 4px;
  border-radius: 13px;
  background: var(--v8-card-soft);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--v8-ink);
  text-decoration: none;
}

.reference-page.mobile-dashboard .v8-pill .ui-icon {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: #4A423A;
}

.reference-page.mobile-dashboard .v8-pill > span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.reference-page.mobile-dashboard .v8-pill--accent { background: var(--v8-dark); color: #fff; }
.reference-page.mobile-dashboard .v8-pill--accent .ui-icon { color: #fff; }

/* --- §8.5 Rows, notes and the quiet text --------------------------------- */
.reference-page.mobile-dashboard .v8-row {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  padding: 10px 0;
  border-top: 1px solid var(--v8-line);
  color: inherit;
  text-decoration: none;
}

.reference-page.mobile-dashboard .v8-row:first-child { border-top: 0; padding-top: 2px; }

/* The open marker: an empty ring, matching the reference's unchecked rows. */
.reference-page.mobile-dashboard .v8-row__dot {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  border: 1.5px solid #C6BEB2;
  border-radius: 50%;
}

.reference-page.mobile-dashboard .v8-row__title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--v8-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.reference-page.mobile-dashboard .v8-row__meta {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--v8-ink-3);
}

/* The mirror image, used by 今日提醒: a light inset row with the ring at the
   far right. The ring is a status mark, never a control — confirming a
   reminder is a typed write owned by the reminder centre. */
.reference-page.mobile-dashboard .v8-sub--check { align-items: center; padding: 11px 12px; }
.reference-page.mobile-dashboard .v8-sub--check .v8-sub__body { gap: 2px; }

.reference-page.mobile-dashboard .v8-check {
  flex: 0 0 auto;
  margin-left: auto;
  width: 19px;
  height: 19px;
  border: 1.5px solid #C6BEB2;
  border-radius: 50%;
}

.reference-page.mobile-dashboard .v8-note {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--v8-ink-2);
}

.reference-page.mobile-dashboard .v8-lead {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--v8-ink);
}

.reference-page.mobile-dashboard .v8-source,
.reference-page.mobile-dashboard .v8-empty {
  margin: 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--v8-ink-3);
}

/* --- §8.6 「{宝宝}现在」 ---------------------------------------------------
   Header, then one row: a round photograph, the gestational figure, and three
   stats divided by vertical hairlines. */
.reference-page.mobile-dashboard .v8-babynow__row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-babynow__lead {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 auto;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-babynow__avatar {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--v8-tile-sand);
  color: #4A423A;
  overflow: hidden;
}

.reference-page.mobile-dashboard .v8-babynow__avatar .ui-icon { width: 26px; height: 26px; }

.reference-page.mobile-dashboard .v8-babynow__value {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--v8-ink-2);
  white-space: nowrap;
}

.reference-page.mobile-dashboard .v8-babynow__value small { font-size: 11px; font-weight: 500; }

/* ==========================================================================
   §9  小菠萝 identity layer
   ==========================================================================

   The baby's nickname is 小菠萝. The brief is explicit that this is "a quiet
   signature, not a theme", and it draws a hard line that a visual layer must not
   be allowed to blur:

     - one 20-24px mark beside the product name, OR one 32-48px mark in an empty
       state — never both on one view;
     - `pineapple_emboss_001` in TRUE empty states only, 96-160px at 8-12%;
     - never search / add / task / inventory / warning / navigation meaning;
     - no repeated pattern, no bright yellow, no green crown, no tropical copy.

   So the layer is deliberately three fixed sizes with no scale knob. If a rule
   here could grow the mark, the mark would eventually be used to fill space, and
   the acceptance check 「At most one pineapple identity element is visible per
   view」 would be unenforceable. The opacity ceiling is injected per element from
   the manifest (`identity_mark()['opacity']`) for the same reason.

   These selectors are intentionally unscoped: unlike the composition retunes
   above, the identity layer is a genuinely global concern and has to behave the
   same on the family home, the baby home and the desktop shell.
   ========================================================================== */

.v8-identity {
  display: inline-block;
  flex: 0 0 auto;
  object-fit: contain;
  /* Decoration must never intercept a tap meant for the control underneath. */
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* brand: beside the product name. Sits on the text baseline rather than on the
   box edge, so a 22px mark beside 44px display type reads as a signature
   instead of as a stray bullet. */
.v8-identity--brand {
  width: 22px;
  height: 22px;
  margin-left: .38em;
  vertical-align: -.16em;
  opacity: .55;
}

/* accent: inside a true empty state, above or beside the empty-state copy. */
.v8-identity--accent {
  width: 40px;
  height: 40px;
  opacity: .72;
}

/* watermark: behind a true empty state. `--pin-opacity` arrives from the
   manifest, so the .1 fallback is only for a manifest that omits the field. */
.v8-identity--watermark {
  width: 128px;
  height: 128px;
  opacity: var(--pin-opacity, .1);
}

/* Flow accent: sits above the empty-state copy, centred in its own row. Used by
   the mobile inventory empty state, which owns a real text block and must not be
   turned into an absolutely-positioned watermark stage. */
.v8-empty-mark {
  display: flex;
  justify-content: center;
  margin-bottom: 10px;
}

/* Flow empty-state copy: centred text block with optional actions and an
   optional compact photograph below them. The photograph is capped at 160px
   tall — the brief's 140-190px band — so it can never occupy the first
   viewport the way a hero would. */
.v8-empty-copy {
  display: grid;
  justify-items: center;
  gap: 6px;
  text-align: center;
}

.v8-empty-copy__title {
  margin: 0;
  font-size: 16px;
  font-weight: 650;
  /* 字面量兜底：--v8-* 只定义在 .reference-page.mobile-dashboard 上，空态组件
     也被库存/任务等其它页面复用（见 caregiver_plan.html），无兜底会 invalid
     at computed-value time（与底栏 transparent 同款坑）。 */
  color: var(--v8-ink, #1A1611);
}

.v8-empty-copy__text {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--v8-ink-2, #6B6259);
}

.v8-empty-copy__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 4px;
}

.v8-empty-photo {
  display: block;
  width: 100%;
  max-width: 260px;
  height: 160px;
  margin: 12px auto 0;
  object-fit: cover;
  border-radius: var(--v8-r-sub);
}

/* The record forms mark a non-rendered field with `.field.hidden`, and nothing
   in any stylesheet ever defined `.hidden` — graphite.css only has
   `.hidden-field`, which `inventory/form.html` and `inventory/lot.html` use.
   The field itself still rendered nothing visible (its control was a real
   `<input type="hidden">`), but the wrapper kept its margin and opened a stray
   gap between rows. Scoped to `.record-form` so a generic class name cannot
   reach anything else. A `display:none` wrapper still submits its inputs. */
.record-form .field.hidden { display: none; }

/* Watermark stage: only for a TRUE empty state whose message is short enough
   that a 128px emboss can sit behind it without touching the glyphs. The text
   keeps its own layer so the mark can never overlap the copy. */
.v8-empty-state {
  position: relative;
  display: grid;
  place-items: center;
}

.v8-empty-state__mark {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

.v8-empty-state__body {
  position: relative;
}


.reference-page.mobile-dashboard .v8-babynow__sub {
  margin: 1px 0 0;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--v8-ink-3);
}

.reference-page.mobile-dashboard .v8-babynow__stat {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
  padding-left: 11px;
  border-left: 1px solid var(--v8-line);
}

.reference-page.mobile-dashboard .v8-babynow__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 7px;
  background: var(--v8-tile-stone);
  color: #4A423A;
}

.reference-page.mobile-dashboard .v8-babynow__icon .ui-icon { width: 12px; height: 12px; }

.reference-page.mobile-dashboard .v8-babynow__label {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--v8-ink-3);
}

/* --- §8.7 The two-up pair ------------------------------------------------
   「今日提醒」 and 「本周重点」 sit side by side and share a height. */
.reference-page.mobile-dashboard .v8-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  align-items: stretch;
}

.reference-page.mobile-dashboard .v8-pair > .dashboard-widget {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.reference-page.mobile-dashboard .v8-pair > .dashboard-widget > * {
  flex: 1 1 auto;
  margin: 0;
}

/* --- §8.8 Hero retune ----------------------------------------------------
   v8's hero is a warm sand card, not a dark one, and it carries a countdown
   figure with a progress bar under it. The text column, the headline weight
   and the masked photograph are already right from §3; what changes is the
   surface and the two new rows. */
.reference-page.mobile-dashboard .v7-hero {
  margin-bottom: 0;
  border-radius: var(--v8-r-card);
  background: var(--v8-hero);
  overflow: hidden;
}

/* The eyebrow is ONE line. The reference draws a single short caption here and
   the label carries two facts (`孕期进度 · 现在最值得处理`), so the line is held
   with `nowrap` and the type is set to the size the reference's own caption
   resolves to at this width (the mockup is a 2x render of a 470px design, and
   this page renders at 390px, so 13 design px is ~11px here). The photograph
   column is widened from 47% to 44% to buy the line its last few pixels. */
.reference-page.mobile-dashboard .v7-hero__label {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--v8-ink-2);
}

.reference-page.mobile-dashboard .v7-hero__label .v8-tile {
  width: 24px;
  height: 24px;
  border-radius: 8px;
  background: var(--v8-tile-sand);
}

.reference-page.mobile-dashboard .v7-hero__title,
.reference-page.mobile-dashboard .v7-hero__body > h2 { color: var(--v8-ink); }

/* The countdown: a quiet unit, a heavy figure, a quiet unit. */
.reference-page.mobile-dashboard .v7-hero__count {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 3px 0 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--v8-ink-2);
}

.reference-page.mobile-dashboard .v7-hero__count strong {
  font-size: 32px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--v8-ink);
}

.reference-page.mobile-dashboard .v7-hero__progress {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  max-width: 190px;
}

.reference-page.mobile-dashboard .v7-hero__bar {
  flex: 1 1 auto;
  height: 7px;
  border-radius: 999px;
  background: rgba(26, 22, 17, .10);
  overflow: hidden;
}

.reference-page.mobile-dashboard .v7-hero__bar > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: #4A423A;
}

.reference-page.mobile-dashboard .v7-hero__pct {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--v8-ink-2);
}

/* The photograph must not set the card's height. The asset is a 1080x1920
   portrait, so at 47% of a 358px card it laid out 299px tall and drove the hero
   to 322px against the reference's 131 design px — the single largest reason
   this page ran twice as long as the mockup. Taken out of flow, the text column
   owns the height and the picture is cropped to fill whatever that turns out to
   be. The §3 mask still does the blending, so the card reads as one surface. */
.reference-page.mobile-dashboard .v7-hero { position: relative; display: block; }

.reference-page.mobile-dashboard .v7-hero__media {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: auto;
  width: 44%;
  height: 100%;
  margin: 0;
}

.reference-page.mobile-dashboard .v7-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The text column keeps clear of the picture. `display: block` above is what
   makes this work: graphite.css lays `.v7-hero` out as a two-column grid
   (`179px 179px`), so a percentage width on the body resolved against a 179px
   track rather than the card — 56% came out at 100px and wrapped every line. */
.reference-page.mobile-dashboard .v7-hero__body {
  width: 100%;
  padding-right: calc(44% + 4px);
}

/* The band header as a plain section label — the fallback shape for a Home whose
   layout does not group the band's members. The v8 template no longer emits it
   (container assembly moved to `services.assemble_home_containers`, which always
   groups them), but the rule is kept so a stored layout rendered by any older
   path cannot fall back to an unstyled heading. */
.reference-page.mobile-dashboard .v8-band-label {
  margin: 2px 0 0;
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--v8-ink);
}

/* --- §8.9 Navigation and the retired v7 surfaces ------------------------- */
/* The capsule is a near-white card on the ground, and the ＋ is the page's
   darkest surface.

   Careful: both tokens are declared on `.reference-page.mobile-dashboard`,
   which exists in exactly ONE template (`dashboard/mobile_dashboard.html`, the
   Family Home). The bottom bar is *shared chrome* — it renders on every mobile
   page — so on any other page the var is undefined, `var(--v8-card)` is invalid
   at computed value time, and `background` falls back to `transparent`. The bar
   then vanished into the page and the ＋ disc with it, leaving only its white
   glyph on a near-white ground (measured on `/m/baby/<id>/care/`: navBg and
   plusBg both `rgba(0, 0, 0, 0)`, while the same probe on the home page read
   `rgb(253, 252, 250)` / `rgb(46, 40, 34)`). The literal fallback keeps shared
   chrome self-sufficient without widening the v8 palette to pages that never
   opted into it. */
.reference-nav {
  background: var(--v8-card, #fdfcfa);
  box-shadow: 0 10px 26px rgba(26, 22, 17, .10);
}

.reference-nav .nav-plus { background: var(--v8-dark, #2e2822); }

/* v7's dark band card (`.v7-now--dark`) and its emphasis surface (`.v7-focus`)
   are no longer emitted by any home template — the band is a container card
   now and the focus module is an ordinary v8 card. These rules neutralise them
   rather than deleting them, so a surface that still renders one of those
   classes anywhere cannot fall back to the old dark treatment. */
.reference-page.mobile-dashboard .v7-now--dark,
.reference-page.mobile-dashboard .v7-focus {
  background: var(--v8-card);
  color: var(--v8-ink);
}

/* --- §9 移动端字阶基线（2026-09-26） ---------------------------------------
   owner：「手机端所有页的标签和字体大小和首页对齐 —— 一个应用里字体肯定要
   follow 首页」。之前每个页面各说一套：首页 h1 是 33px/800，其余移动页却是
   48.75px/760；卡片标题首页 14.5px/700、其余页 23px/650；副标题 14px 与 16px
   混用 —— 于是「一会儿看出来大大小小」。

   基线全部取自**首页实测**（scripts/_measure_home_scale.py，390px 视口）：
     33px/800  「家庭 OS」          页面主标题
     21px/800  「距离预产期 7 天」   hero 级小标题
     14.5px/700「家里现在 / 快捷动作 / 今日提醒」 卡片 / 分组标题
     14px/500  「今天只管值得管的事」  页面副标题 / 上下文名
     13px/600  「临产前保存体力…」    正文强调 / 行标题 / 字段标签
     12.5px/700「待办任务 / 家庭库存」 tile 标签 / 药丸按钮
     12px/400  「好的生活从一个温暖的家开始」 次级正文 / 空态文案
     11.5px/500「每一天，都在靠近见面」 说明 / 提示
     11px/600  「预产期 / 下次产检」   细标签
   首页自己不参与归一化（`:not(.mobile-dashboard)`）—— 它是基线的来源。
   桌面（>700px）不动：桌面壳另有自己的尺度，owner 只说手机端。
   ------------------------------------------------------------------------- */
:root {
  --t-page: 33px;      /* 页面主标题 —— 与首页「家庭 OS」一致 */
  --t-hero: 21px;      /* hero 级小标题 */
  --t-section: 14.5px; /* 卡片 / 分组标题 */
  --t-sub: 14px;       /* 副标题 / 上下文名 */
  --t-body: 13px;      /* 正文强调 / 行标题 / 字段标签 */
  --t-label: 12.5px;   /* tile 标签 / 药丸按钮 */
  --t-note: 12px;      /* 次级正文 / 空态文案 */
  --t-caption: 11.5px; /* 说明 / 提示 */
  --t-micro: 11px;     /* 细标签 */
}

@media (max-width: 700px) {
  /* 页面主标题：一律回到首页的 33px/800（原来是 48.75px/760）。 */
  .reference-page:not(.mobile-dashboard) h1,
  .reference-page:not(.mobile-dashboard) .form-header h1,
  .reference-page:not(.mobile-dashboard) .v9-head__title {
    font-size: var(--t-page);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: 0;
  }
  /* hero 级小标题（页面内最大的那一行，如「只填写真实发生的事实」）。 */
  .reference-page:not(.mobile-dashboard) .v9-hero-title,
  .reference-page:not(.mobile-dashboard) .prenatal-heading h2 {
    font-size: var(--t-hero);
    font-weight: 800;
    line-height: 1.18;
  }
  /* 卡片 / 分组标题（对应首页的「家里现在 / 快捷动作 / 今日提醒」）。 */
  .reference-page:not(.mobile-dashboard) h2,
  .reference-page:not(.mobile-dashboard) .section-headline h2,
  .reference-page:not(.mobile-dashboard) .section-head h2,
  .reference-page:not(.mobile-dashboard) .v9-section__title,
  .reference-page:not(.mobile-dashboard) .v9-group__title {
    font-size: var(--t-section);
    font-weight: 700;
    line-height: 1.35;
  }
  /* 副标题与上下文名。 */
  .reference-page:not(.mobile-dashboard) .context-label,
  .reference-page:not(.mobile-dashboard) .v9-head__name,
  .reference-page:not(.mobile-dashboard) .form-header p,
  .reference-page:not(.mobile-dashboard) .reference-header p,
  .reference-page:not(.mobile-dashboard) .v9-head__sub {
    font-size: var(--t-sub);
    font-weight: 500;
    line-height: 1.5;
  }
  /* 行标题 / 字段标签（正文强调级）。 */
  .reference-page:not(.mobile-dashboard) .v9-row__title,
  .reference-page:not(.mobile-dashboard) .v9-fcard__label,
  .reference-page:not(.mobile-dashboard) .inventory-row strong,
  .reference-page:not(.mobile-dashboard) .planner-row h3 {
    font-size: var(--t-body);
    font-weight: 650;
    line-height: 1.45;
  }
  /* 次级正文与空态/空结果文案。 */
  .reference-page:not(.mobile-dashboard) .muted,
  .reference-page:not(.mobile-dashboard) .quiet-empty,
  .reference-page:not(.mobile-dashboard) .empty-guidance,
  .reference-page:not(.mobile-dashboard) .form-help,
  .reference-page:not(.mobile-dashboard) .v9-emptyflow__text {
    font-size: var(--t-note);
    font-weight: 400;
    line-height: 1.55;
  }
  /* 说明与提示行（字段说明、按钮下说明、行副标题）。 */
  .reference-page:not(.mobile-dashboard) .v9-fcard__note,
  .reference-page:not(.mobile-dashboard) .v9-actions__note,
  .reference-page:not(.mobile-dashboard) .v9-row__sub,
  .reference-page:not(.mobile-dashboard) small {
    font-size: var(--t-caption);
    font-weight: 400;
    line-height: 1.5;
  }
  /* 药丸按钮 / tile 标签。 */
  .reference-page:not(.mobile-dashboard) .v9-empty-cta,
  .reference-page:not(.mobile-dashboard) .v9-pill {
    font-size: var(--t-label);
    font-weight: 700;
  }
  /* 主操作按钮文字。 */
  .reference-page:not(.mobile-dashboard) .v9-cta {
    font-size: 15px;
    font-weight: 700;
  }
  /* 分段按钮与单位后缀。 */
  .reference-page:not(.mobile-dashboard) .v9-seg__opt,
  .reference-page:not(.mobile-dashboard) .v9-fcard__suffix {
    font-size: var(--t-body);
  }
  /* 卡片右上角静语与细标签。 */
  .reference-page:not(.mobile-dashboard) .v9-section__aside,
  .reference-page:not(.mobile-dashboard) .v9-group__aside {
    font-size: var(--t-micro);
    font-weight: 500;
  }
}

@media (min-width: 400px) and (max-width: 700px) {
  /* 首页的「家庭 OS」在 400px 以上放大到 35px（§8.10），其余页同步。 */
  .reference-page:not(.mobile-dashboard) h1,
  .reference-page:not(.mobile-dashboard) .form-header h1,
  .reference-page:not(.mobile-dashboard) .v9-head__title { font-size: 35px; }
}

@media (max-width: 374px) {
  .reference-page:not(.mobile-dashboard) h1,
  .reference-page:not(.mobile-dashboard) .form-header h1,
  .reference-page:not(.mobile-dashboard) .v9-head__title { font-size: 31px; }
}

/* --- §8.10 Breakpoints --------------------------------------------------- */
@media (min-width: 400px) {
  .reference-page.mobile-dashboard .v8-head__brand { font-size: 35px; }
  .reference-page.mobile-dashboard .v7-hero__count strong { font-size: 34px; }
}

@media (max-width: 374px) {
  .reference-page.mobile-dashboard .v8-head__brand { font-size: 31px; }

  .reference-page.mobile-dashboard .v8-card__body--3 { gap: 8px; }
  .reference-page.mobile-dashboard .v8-card__body--4 { gap: 6px; }
  .reference-page.mobile-dashboard .v8-pair { gap: 8px; }

  .reference-page.mobile-dashboard .v8-card { padding: 12px 11px; }
  .reference-page.mobile-dashboard .v8-sub { padding: 10px 9px 11px; }
  .reference-page.mobile-dashboard .v8-pill { padding: 10px 5px; font-size: 11px; }
}

/* --- §8.11 其余手机页的页头：沿用首页已定稿的字重 --------------------------
   v7 把「问候语」当成显示级主行 —— `.v7-header__greeting` 是
   `clamp(2.9rem, 14vw, 3.6rem)`，在 390 视口就是 **57.6px** —— 同时把页面标识
   （`.v7-header .v7-header__brand`）压到 .95rem。首页定稿后把这一对**整个反转**了，
   owner 正是指着其余页面这颗巨大的「早上好」说不好看。

   上一轮确实处理过其余页的 h1（见上面的 35px / 31px 规则），但**漏了这一行**：
   在「宝宝照护」上视觉主行是问候语而不是 h1，所以只量 h1 会得出「已统一」的错误结论。

   这里把首页实测的字重搬过来，并用同一套 grid + display:contents 复现首页行序，
   于是每个手机页都是同一个读法：问候语小、页面标识大、副标题安静。
   **纯 CSS**：不动模板，`get_by_role('heading', name='宝宝照护', exact=True)`
   这类可访问名断言不受影响。 */
.reference-page:not(.mobile-dashboard) .v7-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 0 12px;
  margin: 0 0 16px;
}

/* 溶解内层 wrapper，让 h1 / 问候语 / 副标题各自成为 grid item（首页 §8.1 同法）。 */
.reference-page:not(.mobile-dashboard) .v7-header > div:first-child { display: contents; }

.reference-page:not(.mobile-dashboard) .v7-header__greeting { grid-area: 1 / 1 / 2 / 2; }
.reference-page:not(.mobile-dashboard) .v7-header .v7-header__brand { grid-area: 2 / 1 / 3 / 3; }
.reference-page:not(.mobile-dashboard) .v7-header__sub { grid-area: 3 / 1 / 4 / 2; }
.reference-page:not(.mobile-dashboard) .v7-header__actions { grid-area: 1 / 2 / 2 / 3; justify-self: end; }

.reference-page:not(.mobile-dashboard) .v7-header__greeting {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: normal;
  color: var(--v8-ink-2);
}

.reference-page:not(.mobile-dashboard) .v7-header .v7-header__brand {
  margin: 1px 0 4px;
  font-size: 33px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--v8-ink);
}

.reference-page:not(.mobile-dashboard) .v7-header .v7-header__brand .ui-icon { width: 20px; height: 20px; }

.reference-page:not(.mobile-dashboard) .v7-header__sub {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--v8-ink-2);
}

@media (min-width: 400px) and (max-width: 700px) {
  .reference-page:not(.mobile-dashboard) .v7-header .v7-header__brand { font-size: 35px; }
}

@media (max-width: 374px) {
  .reference-page:not(.mobile-dashboard) .v7-header .v7-header__brand { font-size: 31px; }
}

/* --- §8.12 家长切换浮层不再越出屏幕 -----------------------------------------
   `.role-switcher nav` 是 `position:absolute; right:0`（app.css），也就是从触发点
   的**右边缘**向左长。这在「宝宝照护」上没问题（`.role-switcher{margin-left:auto}`，
   触发点在右侧），但首页的触发点靠在**左边**：面板 min-width 190px，加上
   「添加照护角色」后实际更宽，`right:0` 于是把它的左边缘推到视口外 —— 截图里
   「DEMO 家长」只剩「家长」、「添加照护角色」只剩「护角色」，正是这个原因。

   首页触发点在左半屏，改成从触发点**左边缘向右**长；其余页维持 right:0。
   两种情况都加一条视口宽度上限兜底，窄屏也不会溢出。 */
.reference-page.mobile-dashboard .role-switcher nav {
  left: 0;
  right: auto;
}

.reference-page .role-switcher nav {
  max-width: min(260px, calc(100vw - 32px));
}

/* --- §8.13 「成长历史 / 宝宝时间线」的标题补到基线 ---------------------------
   全端扫最大字号时只有这一页是 31.2px：v7_insights.css 给
   `.timeline-page .timeline-header h1` 写了 `clamp(27px, 8vw, 34px)`，
   在 390 视口 8vw = 31.2px，而它在本层的样式表**之后**加载，同特异性(0,2,1)下它赢。
   这里用 (0,3,1) 压过去，仍然指向同一个 `--t-page`，不是又写一个魔法数字。 */
@media (max-width: 700px) {
  .reference-page:not(.mobile-dashboard) .timeline-header h1 {
    font-size: var(--t-page);
    font-weight: 800;
    letter-spacing: 0;
  }
}

/* --- §8.14 「换一条」 ---------------------------------------------------------
   The source line and the rotate control share one row: the provenance text
   keeps its quiet 11px left-aligned place, the control sits right where a
   right-hand card action already lives on this card (the header's 知识库 link),
   so the card gains a control without gaining a new visual axis. It is a pill,
   matching the card system's other small controls, and it stays a real
   `<button type="submit">` — the rotation is a POST, never a link. */
.reference-page.mobile-dashboard .v8-source-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.reference-page.mobile-dashboard .v8-source-row .v8-source { margin: 0; }

.reference-page.mobile-dashboard .v8-rotate { margin: 0; flex: 0 0 auto; }

.reference-page.mobile-dashboard .v8-rotate button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  padding: 4px 9px 4px 7px;
  border: 1px solid var(--v8-line);
  border-radius: 999px;
  background: var(--v8-card-soft);
  color: var(--v8-ink-2);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.reference-page.mobile-dashboard .v8-rotate button .ui-icon {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
}

.reference-page.mobile-dashboard .v8-rotate button:hover { color: var(--v8-ink); }
.reference-page.mobile-dashboard .v8-rotate button:active { transform: scale(.97); }
