/* Activity workspace
 *
 * Activity is a household ledger assembled from per-device reports. This
 * stylesheet keeps that hierarchy visible without changing the shared card,
 * chip or event vocabulary in app.css. All selectors stay on #activity so the
 * rest of the portal can keep evolving independently. */

#activity > .activity-workspace {
  grid-column: 1 / -1;
  display: grid;
  gap: 16px;
  min-width: 0;
}

/* F1.1 — her reading column is 820px and LEFT-ALIGNED (flush with the screen
   header), not centred: `margin-inline: auto` would centre it inside the
   1198px main and is deliberately absent here.

   The cap sits on the workspace's CHILDREN rather than on
   `#activity > .activity-workspace` itself, which is what the plan's literal
   text asks for. Reason, measured: activity-ui.test.js's desktop assertion is
   `assert.equal(desktop.workspaceWidth, desktop.screenWidth)` — it exists to
   catch the workspace falling into ONE track of `section.screen`'s two-column
   grid instead of spanning 1/-1. Capping the workspace makes that assertion
   fail for a reason that is not the bug it guards. Capping the children is
   pixel-identical on screen (a stretched grid item with `max-width` fills from
   the start edge) and leaves the guard doing its job. */
#activity > .ovhead,
#activity > #activityMsg,
#activity > .activity-workspace > * { max-width: 820px; }

#activity > .activity-workspace.is-empty { grid-template-columns: minmax(0, 1fr); }

#activity .activity-filter-card,
#activity .activity-ledger-card {
  min-width: 0;
  margin: 0;
  /* MEASURED 2026-09-20: app.css's `:root[data-theme="light"] .card
     { border-color: var(--line) }` overrides `.card`'s own
     `border: 1px solid var(--border-subtle)` and paints EVERY light card edge
     rgba(22,35,63,.16). The owner RULED card edges rgba(22,35,63,.08) and
     control edges .16, and hers measures .08 on every card on this artboard.
     That override is a global app.css defect affecting every screen, not just
     this one — it is written up in .scratch/fidelity-appcss-activity.patch for
     the coordinator. This line only puts THIS surface right in the meantime
     and should be deleted once app.css is fixed. */
  border-color: var(--border-subtle);
}

#activity .activity-filter-card { padding: 0; }

#activity .activity-filter-head,
#activity .activity-ledger-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

#activity .activity-filter-toggle {
  align-items: center;
  min-height: 64px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
}

#activity .activity-filter-toggle::-webkit-details-marker { display: none; }

#activity .activity-filter-toggle::after {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--sky);
  border-bottom: 2px solid var(--sky);
  transform: rotate(-45deg);
}

#activity .activity-filter-card[open] > .activity-filter-toggle::after { transform: rotate(45deg); }

#activity .activity-filter-toggle:focus-visible {
  outline: 3px solid var(--glow);
  outline-offset: -3px;
  border-radius: 14px;
}

#activity .activity-filter-heading {
  display: grid;
  min-width: 0;
}

#activity .activity-filter-selection {
  display: none;
  min-width: 0;
  overflow: hidden;
  color: var(--haze);
  font-size: 11.5px;
  font-weight: 650;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#activity .activity-filter-body {
  padding: 0 16px 16px;
  border-top: 1px solid var(--line);
}

#activity .activity-kicker,
#activity .activity-filter-label,
#activity .actmeta {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .08em;
  line-height: 1.25;
  text-transform: uppercase;
}

#activity .activity-kicker {
  margin: 0 0 3px;
  color: var(--attention-text);
}

#activity .activity-panel-title {
  margin: 0;
  color: var(--cloud);
  font-size: 16px;
  line-height: 1.25;
}

#activity #activityFilters {
  display: grid;
  gap: 14px;
  margin: 14px 0 0;
}

#activity .activity-filter-group + .activity-filter-group {
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* A flex row's max-content width must not become the grid item's minimum.
   Real households can expose every event category at once; without this, the
   phone page itself grows wider than the viewport before the chip row gets a
   chance to scroll. */
#activity .activity-filter-group { min-width: 0; max-width: 100%; }

#activity .activity-filter-label {
  margin-bottom: 7px;
  color: var(--haze);
}

#activity #activityFilters > .activity-filter-group > .chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 0;
}

#activity .filterchip {
  min-height: 38px;
  padding: 7px 11px;
}

#activity .activity-filter-help { margin: 14px 0 0; }

#activity #activityClearBtn {
  display: block;
  margin: 12px 0 0 auto;
}

/* F1.8 (CSS half) — hers is a standalone SOFT PANEL, not a bordered white card
   nested inside a bordered white card: #F7F8FA fill, no border, 18px radius,
   18px/22px padding. No token matches #F7F8FA exactly (nearest is
   --surface-raised #F1F3F6); her ruling says take her literal. In dark the
   panel falls back to the raised surface so it does not become a white slab.
   The remaining half — moving #activityNoDevice OUT of .activity-ledger-card
   to be a sibling between the chip row and the ledger, and updating the
   index.html comment that explains why it was put inside — is index.html and
   is reported to the coordinator. */
#activity .activity-no-device {
  border: 0;
  border-radius: 18px;
  background: #F7F8FA;
  gap: 16px;
  /* 18px vertical / 22px horizontal, and this is MEASURED, not assumed. Her
     panel is 76px tall around a 40px-tall two-line text block: 18 + 40 + 18.
     The 32px avatar inside it sits at 22px from the top, which is what a
     22px-all-round padding would look like and is NOT what it is — the avatar
     is simply centred against the taller text. Setting 22px all round made
     ours 84px, eight pixels taller than hers. */
  padding: 18px 22px;
  margin-bottom: 22px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #activity .activity-no-device { background: var(--surface-raised); }
}
:root[data-theme="dark"] #activity .activity-no-device { background: var(--surface-raised); }

#activity .activity-no-device .avatar { width: 32px; height: 32px; }
#activity .activity-no-device .an { font-size: 14px; font-weight: 700; color: var(--cloud); }
#activity .activity-no-device .asub { font-size: 12.5px; line-height: 1.5; color: var(--text-tertiary); }
#activity .activity-no-device a {
  color: var(--brand-deep);
  font-weight: 700;
  font-size: 13px;
  text-decoration: none;
}
#activity .activity-no-device a:hover,
#activity .activity-no-device a:focus-visible { text-decoration: underline; }

/* DF-ACT-1 (CSS half) — HER LEDGER HAS NO OUTER CARD AT ALL.
   MEASURED off Activity.dc.html at 1600px: between the chip row and the first
   day label there is a soft no-device panel and nothing else — no white sheet
   wrapping the ledger, no head bar, no kicker, no summary line. Each DAY is
   its own white card (`.activity-day-group` below) and the day label sits
   outside it.

   So this element stops being a card and becomes a plain container. Its
   children keep every job they had: the head is now a quiet caption line
   rather than a toolbar, `#activityNoDevice` becomes the standalone panel it
   already looked like, and the day cards below are the ledger.

   `overflow: hidden` is dropped WITH the card skin and must not come back —
   it exists to clip children to a rounded corner this element no longer has,
   and it would clip the day cards' own 6px/18px shadow. */
#activity .activity-ledger-card {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* Her screen has no ledger head, so ours becomes ONE quiet caption line above
   the first day label instead of a bar inside a card.

   NOTHING IS DROPPED. `#activitySummary` (role=status, aria-live) keeps its
   honest count, both caveats keep their place directly under it, and Refresh
   keeps its 44px target — those are the three things a parent acts on or
   relies on, and renderActivity() writes into all of them. Only
   `#activityLedgerLabel` goes visually quiet, and only because it is the one
   element here that is pure decoration on screen: it is `data-i18n` static
   text ("Recent household snapshot"), NOTHING in app.js or this module ever
   writes to it, and the snapshot framing it carried is still on screen in the
   summary's own words ("… in this recent snapshot"). It stays in the
   accessibility tree — sr-only, never display:none — because it is both the
   screen's level-2 heading and `#activityList`'s aria-labelledby target, and
   hiding it outright would leave the ledger unnamed. */
#activity .activity-ledger-head {
  align-items: flex-start;
  padding: 0 0 16px;
  border-bottom: 0;
  background: none;
}

#activity #activityLedgerLabel {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

#activity .activity-summary {
  min-height: 20px;
  margin: 0;
  color: var(--text-tertiary);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
}

/* The two honesty caveats her artboard has no place for. They KEEP their place
   directly under the summary line: the limit note is what stops a parent
   reading a snapshot as a complete history, and the partial note stays
   --waiting-text so a degraded read still reads as amber/waiting. */
#activity .activity-limit-note,
#activity .activity-partial-note {
  margin: 4px 0 0;
  color: var(--text-tertiary);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
}

#activity .activity-partial-note { color: var(--waiting-text); }

/* The list no longer pads a card it is inside — the day cards below carry
   their own 24px. Flush left means the day label starts on the same vertical
   as the screen header and the chip row, which is where hers starts. */
#activity #activityList { padding: 0; }

/* DF-ACT-1 — ONE WHITE CARD PER DAY. Every value here is her measured literal,
   and each one is already carried by a token, so nothing new is introduced:
     her border   rgba(22,35,63,.08)  = --border-subtle (the owner's ruled card edge)
     her radius   22px                = --radius-card
     her shadow   0 1px 2px rgba(22,35,63,.03), 0 6px 18px rgba(22,35,63,.04)
                                      = --shadow-card, exactly
     her padding  8px 24px
     her gap to the next day label    22px
   `js/activity.js` builds the box; this draws it. */
#activity #activityList .activity-day-group {
  padding: 8px 24px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  background: var(--dusk);
  box-shadow: var(--shadow-card);
  margin: 0 0 22px;
}

#activity #activityList .activity-day-group:last-child { margin-bottom: 0; }

#activity #activityList:focus-visible {
  outline: 3px solid var(--glow);
  outline-offset: -3px;
  border-radius: 12px;
}

#activity #activityList .activity-empty {
  margin: 0;
  padding: 28px 12px;
  border: 1px dashed var(--line);
  border-radius: 14px;
  color: var(--haze);
  text-align: center;
}

/* F1.7 — her day label is a quiet Nunito kicker sitting OUTSIDE and above the
   rows, with no bottom rule and no opaque backplate. The `background:
   var(--dusk)` existed only to mask rows scrolling under a sticky label; it
   becomes pointless once F1.6a removes the nested scroller, and the sticky
   rule goes with it (a transparent sticky label would let rows read through).
   Colour is var(--text-tertiary) = #6D778A, a deliberate, pinned 1-step
   deviation from her #8A93A3 (the 2026-09-20 third ruling moved tertiary TEXT
   to #6D778A for 4.51:1). Do not "fix" it back to --text-tertiary-ui. */
#activity #activityList .actday {
  margin: 0 0 12px;
  padding: 0;
  border-bottom: 0;
  background: none;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .04em;
  line-height: 1.5;
  color: var(--text-tertiary);
}

/* `.actday + .arow.actrow { padding-top: 0 }` USED TO LIVE HERE AND WAS A BUG,
   not a refinement. MEASURED on the live screen before this pass: the first
   row of each day was 64px tall while every other row was 80px, because that
   rule took 16px off the top of exactly one row per group and left the icon
   riding the day label. Hers is 67/67/67/66 — uniform, 16px top and bottom on
   every row including the first. The day-group card's own 8px of padding is
   what separates the label from the first row now. */

#activity #activityList .arow.actrow {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  gap: 14px;
  /* HER OFFSETS, WITHOUT HER FRAGILITY. Measured on her row: tile at +16 from
     the row's top edge, sentence at +21, timestamp at +23 — i.e. the 34px tile
     sets the content height and the shorter text boxes are centred against it.

     `align-items: center` reproduces that in one declaration AND WAS WRONG,
     which an adversarial reviewer caught on the phone render: her rows never
     wrap, ours do. The moment a sentence takes two lines the tile centres
     against the WHOLE block and floats 25-85px below the line it belongs to.
     At 390px, where every sentence wraps and the meta is a second line, that
     is every row.

     So the row aligns to `start` and the two text boxes take her exact
     offsets instead. One-line rows land pixel-identical to hers; a wrapped
     row keeps the tile anchored to its first line, which is the only place it
     means anything. */
  align-items: start;
  padding: 16px 0;
  /* Her row divider is rgba(22,35,63,.07) — a hairline. app.css paints this
     with --line (rgba(22,35,63,.16)), more than twice as dark, which is what
     made our ledger read as a ruled table rather than her sheet.
     --border-subtle (.08) is the owner's ruled card-edge value and is the
     nearest token to her .07; no new hex is introduced. */
  border-bottom: 1px solid var(--border-subtle);
}

/* `:last-child` is now the last row OF ITS DAY CARD, which is exactly where
   hers drops the divider (measured: her last row is 66px, one pixel shorter
   than the 67px rows above it, because it alone has no bottom border). */
#activity #activityList .arow.actrow:last-child { border-bottom: 0; }

/* F1.11 — hers is a quiet 400-weight tertiary timestamp, not a 700-weight
   secondary one. The remaining half of F1.11 (dropping the zero-padded hour:
   `hour: '2-digit'` -> `'numeric'`) lives in app.js's activityClock() and is
   reported to the coordinator. */
#activity #activityList .acttime {
  width: auto;
  /* +23 from the row's top edge is where hers sits (row 378, time 402, with
     16px of row padding above it) — see the align-items note on .actrow. */
  padding-top: 7px;
  color: var(--text-tertiary);
  font-size: 12.5px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

#activity #activityList .acticon {
  width: 34px;
  height: 34px;
  margin-top: 0;
  border-radius: 11px;
}
/* F1.2 / C3 — all seven categories used to paint one identical blue. Hers are
   semantically tinted. Every token below ALREADY carries her exact light
   literal and already has a dark value, so nothing new is introduced:
   --brand-tint #E8F1FA / --brand-deep #1B4A73, --waiting-tint #FDF1DF,
   --done-tint #E3F5EA, --attention-tint #ECE8FB. The GLYPH reads the
   *-text token, never --mint/--amber/--sky (those are fill tokens and a test
   guards `color: var(--mint)` at zero sites).

   In dark every *-tint resolves to var(--dusk2), so the four backgrounds
   collapse to one and only the glyph hue differs — that is how the set is
   designed, and it is what the old color-mix already approximated.

   `protection` takes indigo: her artboard paints "tablet was locked (bedtime)"
   lavender and "daily limit reached" blue, and both are our single
   `protection` category, so no 1:1 map reproduces her artboard. Lavender is
   the pick because it matches the row a parent notices most and matches
   indigo = needs-attention. `pin` joins it (pin_fail / hatch_used /
   recoveryPin.revealed are tamper signals). */
#activity #activityList .acticon-web,
#activity #activityList .acticon-apps,
#activity #activityList .acticon-device { color: var(--brand-deep); background: var(--brand-tint); }

#activity #activityList .acticon-requests { color: var(--waiting-text); background: var(--waiting-tint); }

#activity #activityList .acticon-engage { color: var(--done-text); background: var(--done-tint); }

#activity #activityList .acticon-protection,
#activity #activityList .acticon-pin { color: var(--attention-text); background: var(--attention-tint); }

/* FOUND WHILE VERIFYING F1.2 ON THE LIVE SCREEN, not in the plan. `other` is
   app.js:28959's real fallback for an event kind the portal does not know yet
   (`category: 'other', unmapped: true`) — a device shipping a new kind before
   the portal learns it lands here. NOTHING styles `.acticon-other` in app.css
   or in any css/*.css, so the tile inherited a WHITE background on a WHITE
   card and the glyph was invisible: measured rgb(255,255,255) on the live
   ledger. It takes the neutral raised surface and the tertiary glyph, which is
   the honest reading — "we recorded this, we cannot name it yet" — rather than
   borrowing a semantic hue that would assert a meaning we do not have. */
#activity #activityList .acticon-other {
  color: var(--text-tertiary);
  background: var(--surface-raised);
}

/* F1.10 — her event sentence is 16px/24px, with the child NAME at 700/ink and
   the rest of the sentence at 400/secondary. The size, rhythm and the
   secondary body colour are CSS and land here; splitting the leading name into
   a `<span class="actwho">` is app.js's activityEventSentence() and is reported
   to the coordinator. `.actwho` is pre-styled below so that change lands
   visually the moment it is applied, with no second CSS pass.

   16/24 also agrees with the pinned >=900px body scale, so this is not in
   tension with it. */
#activity #activityList .actrow > .an {
  min-width: 0;
  /* +21 from the row's top edge is where her sentence sits (row 378, text
     399) — see the align-items note on .actrow. */
  padding-top: 5px;
  color: var(--haze);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
}

#activity #activityList .actrow > .an .actwho {
  font-weight: 700;
  color: var(--cloud);
}

#activity #activityList .actrow > .an .asub {
  margin-top: 3px;
  color: var(--text-tertiary);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.5;
}

/* DF-ACT-2 — HER ROW IS ONE LINE. Measured: 67px, one 24px sentence line, and
   nothing under it. Ours stacked a second line ("Ben   Ben's tablet") under
   every sentence, which is what made our rows 80px and our ledger read as
   twice the page hers is.

   THAT LINE IS NOT DELETED and must not be. `.pill` is the row's child
   attribution — it is the ONLY thing that says so for an event whose sentence
   does not name a child, it carries "No child assigned" for an unattributed
   row, and activity-ui.test.js reads it. `.actdevice-name` is which tablet.
   Both stay in the DOM, both stay readable, both keep their aria-labels.

   What changes is that they sit INLINE after the sentence instead of on a
   line of their own, so a row that fits reads as her single line and only a
   row that genuinely does not fit wraps. `inline-flex` (not a flex child of
   `.an`) is deliberate: `.an`'s first child is a bare TEXT NODE, so making
   `.an` a flex container would wrap that sentence in an anonymous flex item
   free to shrink to min-content and shred a long sentence into a narrow
   column beside the meta. In the inline flow the sentence keeps normal line
   breaking and the meta simply follows it.

   NOTE THE REAL COPY TENSION, ticket DF-1, deliberately NOT fixed here: our
   sentences are complete DEVICE-centric sentences that already name both the
   child and the tablet ("Ben asked for more time in TikTok on Ben's tablet."),
   so this meta repeats them verbatim; hers are CHILD-centric fragments after a
   bolded name ("opened YouTube Kids for 22 minutes"). Fixing that means
   rewriting ~30 act.sent.* strings across three locales and needs owner
   sign-off — it is not a stylesheet's call. Until then the duplication is
   real and visible, and quieting the meta is the most this lane may do. */
#activity #activityList .actrow > .an .asub.actrow-meta { margin-top: 0; }

#activity .actrow-meta {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px 6px;
  min-width: 0;
  margin: 0 0 0 10px;
  vertical-align: baseline;
}

#activity .actrow-meta .pill {
  max-width: 126px;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0;
  border: 0;
  background: transparent;
  /* Tertiary, not --haze: the meta is now beside the sentence rather than
     under it, and it must read as the sentence's quiet tail rather than as a
     second thing of equal weight. */
  color: var(--text-tertiary);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

#activity .actrow-meta .actdevice-name {
  min-width: 0;
  max-width: min(100%, 280px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow-wrap: anywhere;
  color: var(--text-tertiary);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.5;
}

/* A separator only when a child pill actually precedes the device name — a
   household-source row has no pill (`_childApplicable === false`) and must not
   be given a leading dot. Decorative punctuation on a non-interactive span:
   it adds no meaning a screen reader loses, and both spans keep their own
   aria-label. */
#activity .actrow-meta .pill + .actdevice-name::before {
  content: "\00b7";
  margin-right: 6px;
  color: var(--text-tertiary);
}

#activity .actmobiletime { display: none; }

/* It used to be inset to clear the ledger card's padding. There is no ledger
   card any more, so it spans the reading column under the last day card. */
#activity #activityMoreBtn {
  width: 100%;
  margin: 16px 0 0;
}

@media (min-width: 700px) and (max-width: 1099px) {
  #activity .activity-filter-card {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  #activity .activity-filter-toggle { display: none; }
  #activity .activity-filter-body { padding: 0; border: 0; }
  #activity #activityFilters {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  #activity .activity-filter-group + .activity-filter-group {
    padding-top: 0;
    padding-left: 14px;
    border-top: 0;
    border-left: 1px solid var(--line);
  }
}

@media (min-width: 1100px) {
  #activity > .activity-workspace {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  #activity > .activity-workspace.is-empty { grid-template-columns: minmax(0, 1fr); }

  #activity .activity-filter-card {
    position: static;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  #activity .activity-filter-toggle { display: none; }
  #activity .activity-filter-body { padding: 0; border: 0; }
  /* F1.5 — hers is ONE inline pill row, 820x40: CHILD CHIPS ONLY, no group
     labels, no second chip set, no fineprint between the header and the
     ledger. Ours carries a second group (EVENT TYPE) her Activity does not
     have, plus two honesty lines.

     NOTHING IS DROPPED, and the previous attempt at this is now corrected.
     It put both groups on ONE wrapping row separated by a 1px vertical rule,
     which LOOKED right in the plan and was wrong on the page: with a real
     household's eight event categories the row wraps, and a wrapped flex line
     carries the separator `::before` to the start of the NEXT line, where it
     renders as a stray 1px vertical tick floating under the child chips with
     nothing on either side of it. MEASURED on the live screen at 1440px,
     2026-09-20, with Jordan/Maya/Ben and real ingested events.

     So the groups are two deliberate ROWS instead of one accidental wrap:
     row one is the child chips, which is hers exactly; row two is our extra
     event-type set, visibly subordinate (see the chip rules below) so the eye
     still reads one primary filter row. `flex-basis: 100%` is what forces the
     break, and it removes the separator's reason to exist.

     `flex-wrap: wrap` is load-bearing and must stay: `.activity-filter-group`
     carries `min-width: 0; max-width: 100%` for the same reason this file's
     own comment above records — a real household can expose every category at
     once, and a nowrap row lets max-content become the grid item's minimum and
     push the page wider than the viewport. */
  #activity #activityFilters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    grid-template-columns: none;
    gap: 12px 8px;
    margin: 0;
  }
  #activity .activity-filter-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
  }
  #activity .activity-filter-group + .activity-filter-group {
    flex-basis: 100%;
    padding: 0;
    border: 0;
  }
  #activity .activity-filter-group + .activity-filter-group::before { content: none; }
  /* sr-only, NOT display:none — the labels are the accessible names of the two
     role="group" chip sets, and activity-ui.test.js reads their textContent
     (`assert.deepEqual(desktop.filterLabels, ['Child', 'Event type'])`). */
  #activity .activity-filter-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  #activity #activityFilters > .activity-filter-group > .chips { gap: 8px; }

  /* C4 — her chips are 13.5px/700 and 40px tall. The 44px touch floor is
     binding and is NOT dropped: 40px is taken only where the pointer is
     genuinely fine, exactly the gate `.reqactions .iconbtn` already uses. This
     RAISES the ungated desktop chip from the 38px it shipped at. */
  #activity .filterchip {
    min-height: 44px;
    padding: 9px 18px;
    border-radius: var(--radius-pill);
    font-size: 13.5px;
    font-weight: 700;
  }

  /* DF-ACT-3 — the EVENT TYPE row is ours, not hers, so it reads as the
     secondary control strip it is rather than as a second equal chip wall.
     Both values are the owner's RULED control pair and already carry tokens:
     fill #F5F7F9 (--field-fill), edge rgba(22,35,63,.16) (--line). The
     PRESSED chip is untouched — `.chip[aria-pressed="true"]` keeps its brand
     fill and its contrast pairing, which is the one thing here that has to
     stay unambiguous, so this is scoped to the unpressed state only. */
  #activity #activityTypeFilter .filterchip[aria-pressed="false"] {
    background: var(--field-fill);
    border-color: var(--line);
    font-size: 12.5px;
    font-weight: 650;
  }

  #activity .activity-filter-help {
    margin-top: 10px;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-tertiary);
  }

  /* F1.6a — the nested scrollbar is gone. A scroll region inside a card is the
     single most obvious non-her artefact on this screen; her page scrolls.
     The `position: sticky` day label goes WITH it, deliberately: sticky only
     ever existed to pin a label above the rows sliding under it inside this
     scroller, and F1.7 removes the opaque backplate that made that legible, so
     a page-level sticky label would let rows read straight through it.

     F1.6b — one card PER DAY — is DONE as of 2026-09-20 and is no longer
     "needs app.js": js/activity.js wraps each day's rows and
     `.activity-day-group` above draws her card. The list's own padding is 0
     because the day cards carry hers (8px 24px). */
}

@media (min-width: 1100px) and (pointer: fine) {
  #activity .filterchip { min-height: 40px; }
}

@media (max-width: 699px) {
  #activity .activity-filter-card { padding: 0; }
  #activity .activity-filter-toggle {
    min-height: 58px;
    padding: 10px 14px;
  }
  #activity .activity-filter-toggle .activity-kicker { display: none; }
  #activity .activity-filter-selection { display: block; margin-top: 2px; }
  #activity .activity-filter-body { padding: 0 14px 14px; }
  #activity .activity-ledger-head { padding: 0 0 12px; }
  #activity #activityList { padding: 0; }

  /* The day card keeps her shape on a phone with the padding a 390px column
     can actually spare: 8px 14px instead of 8px 24px. Everything else — the
     .08 edge, the 22px radius, the shadow, the 22px gap to the next label —
     is unchanged, because those are what make it read as her card. */
  #activity #activityList .activity-day-group { padding: 8px 14px; }

  /* The closed native disclosure is the phone's concise default. Once opened,
     show every type as a complete chip instead of clipping a label midway at
     the right edge and hoping the parent discovers an invisible swipe rail. */
  #activity #activityFilters { gap: 12px; margin-top: 12px; }
  #activity .activity-filter-group + .activity-filter-group { flex-basis: auto; padding-top: 12px; }
  #activity #activityFilters > .activity-filter-group > .chips {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    flex-wrap: wrap;
    overflow: visible;
    padding: 0;
  }
  #activity #activityFilters .filterchip {
    flex: none;
    min-height: 44px;
    padding: 7px 10px;
  }
  #activity #activityClearBtn { min-height: 44px; }
  #activity .activity-filter-help { margin-top: 10px; }

  #activity #activityList .arow.actrow {
    grid-template-columns: 34px minmax(0, 1fr);
    gap: 8px 10px;
    padding: 14px 0;
  }

  #activity #activityList .acttime { display: none; }
  #activity #activityList .acticon { grid-column: 1; grid-row: 1; }
  #activity #activityList .actrow > .an { grid-column: 2; grid-row: 1; padding-top: 4px; }
  #activity .actmobiletime {
    display: inline;
    color: var(--haze);
    font-variant-numeric: tabular-nums;
  }
  /* DF-ACT-2 IS A DESKTOP SHAPE AND STOPS HERE, on purpose. Her one-line row
     is a 820px reading column's shape. At 390px every sentence wraps anyway,
     so an inline meta lands on the sentence's last line for some rows and on
     a line of its own for others — measured on the real phone render, rows
     came out 85-100px and visibly ragged. It also matters more here: this is
     the width where `.acttime` is hidden and `.actmobiletime` INSIDE this
     meta is the only place the timestamp appears, so it needs a predictable
     place to live rather than wherever the sentence happened to end. */
  #activity #activityList .actrow > .an .asub.actrow-meta {
    display: flex;
    margin: 3px 0 0;
  }

  #activity .actrow-meta .pill { max-width: min(180px, 52vw); }
  #activity .actrow-meta .actdevice-name { max-width: min(220px, 62vw); }

  #activity #activityMoreBtn {
    width: 100%;
    margin: 14px 0 0;
  }
}

/* ========================================================================
   HELP HUB — W4.2's other screen (F1.4, F1.14, F1.15, F1.16, F1.18)

   WHY THESE #support RULES LIVE IN activity.css AND NOT IN app.css's own
   "HELP HUB · 2026-09-04" block, which is where they belong: the W4.2 lane
   owns js/activity.js, css/activity.css and js/support.js, and app.css is held
   by the coordinator for the duration of the design-fidelity waves. Help has
   no stylesheet of its own and index.html's <link> list is coordinator-owned
   too, so this file — which already loads AFTER app.css on every page — is the
   only sheet in the lane that can carry them. Every rule below is written to
   be lifted verbatim into app.css's Help hub block and deleted from here; the
   coordinator has the same text. Nothing here depends on being in this file.
   ==================================================================== */

/* F1.1 — the same 820px LEFT-ALIGNED reading column as Activity. `margin-inline:
   auto` is what centres Help today inside the 1198px main; hers is flush with
   the screen header, so the auto is replaced rather than removed. */
#supportMount {
  max-width: 820px;
  margin-inline: 0;
}

/* F1.4 — Help paints TWO screen titles: the shell's `.toprow .nm` "Help" at
   17px/700, then the module's own eyebrow + h1 22px below. Hers has one.

   sr-only rather than display:none — but NOT for the reason this comment
   originally gave, and the correction matters more than the rule.

   It claimed a keyboard user "would land nowhere" and that app.css's #overview
   copy "records the identical reasoning". BOTH ARE FALSE, and an adversarial
   reviewer caught the second:
     * focusScreen() (app.js:9182) is
       `section.querySelector('.toprow .nm, .ovhead .ov-title') || section`.
       The `|| section` fallback means focus lands on the SECTION, not nowhere.
     * app.css does the OPPOSITE of the cited precedent: `#overview > .toprow`
       (648), `#settings > .toprow` (3930) and `#child > .toprow` (4376) are
       all `display: none`.
   The claim came from the consolidated plan (F1.4), not from measurement, and
   was carried here unchecked.

   sr-only is still the better choice — it keeps the heading in the
   accessibility tree for a screen-reader user scanning headings, which
   display:none removes — so the rule stands on its own smaller merits. What is
   removed is the false justification, because a confident wrong comment is
   exactly what makes a defect invisible to the next reader. */
#support > .toprow {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* F1.14 — her search field is a 52px rounded-rect, not a 49px pill. */
#supportMount .support-search-input {
  border-radius: 14px;
  font-size: 15px;
  padding: 15px 16px 15px 46px;
}
#supportMount .supportsearchicon { width: 17px; height: 17px; left: 17px; }

/* F1.16 — hers carries ONE kicker above the questions. Ours stacks an eyebrow
   ("Frequently asked questions") on top of an h2 ("Quick answers"). Both
   ELEMENTS stay — #supportFaqTitle is the section's aria-labelledby target and
   deleting it would leave the region unnamed — but the h2 becomes sr-only and
   the eyebrow takes her kicker treatment, so one heading reads on screen and
   the accessible name is unchanged. Our copy is kept; only the type moves. */
#supportMount .support-faq .support-faq-title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
#supportMount .support-faq > .ov-eyebrow {
  display: block;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .04em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--text-tertiary);
  padding: 0 0 4px;
}

/* F1.15 — her question row is a flex line with the chevron at the RIGHT edge
   and a divider UNDER each question block; ours puts a +/− marker on the left,
   indents the answer and draws the rule above.

   IT MUST STAY A REAL <details>: applySearchFilter() in js/support.js sets
   `.open` on each item so a matched answer opens itself, and `.is-hidden` to
   filter. The summary is restyled, never hidden, and the element is untouched. */
#supportMount .support-faq-item {
  border-top: 0;
  border-bottom: 1px solid var(--line);
  padding: 16px 0;
}
#supportMount .support-faq-item:first-child { padding-top: 0; }
#supportMount .support-faq-item:last-child { border-bottom: 0; }
#supportMount .support-faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 14.5px;
}
#supportMount .support-faq-q::before { content: none; }
#supportMount .support-faq-q::after {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin-right: 4px;
  /* --text-tertiary-UI, not --text-tertiary. This chevron is drawn with
     BORDERS, so it is a non-text glyph and falls on the FILL side of the
     owner's 2026-09-20 role split: her literal #8A93A3 applies, and WCAG
     1.4.3 (which is what --text-tertiary's #6D778A exists to satisfy) does
     not govern it. Flagged by an adversarial reviewer. */
  border-right: 2px solid var(--text-tertiary-ui);
  border-bottom: 2px solid var(--text-tertiary-ui);
  transform: rotate(45deg) translate(-2px, -2px);
}
#supportMount .support-faq-item[open] > .support-faq-q::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}
#supportMount .support-faq-a { margin-top: 8px; padding-left: 0; }
#supportMount .support-faq-a p { font-size: 13.5px; line-height: 1.6; }

/* F1.18 — her "Still need help?" contact card: a navy footer BELOW the FAQ,
   with the action as an auto-width pill on the right rather than a full-width
   button at the top of the page. js/support.js moves the node; this is its
   skin. COPY IS DELIBERATELY NOT HERS: "Our support team usually replies
   within a few hours" is an unmeasured response-time claim and the honesty
   rule forbids stating what the system does not know — ours is kept verbatim. */
#supportMount .support-topic-primary {
  flex-direction: row;
  align-items: center;
  gap: 20px;
  border: 0;
  border-radius: 22px;
  background: #16233F;
  padding: 24px 26px;
  box-shadow: none;
}
#supportMount .support-topic-primary > .support-topic-head { flex: 1 1 auto; align-items: center; }
#supportMount .support-topic-primary .support-topic-icon {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 14px;
  background: rgba(255, 255, 255, .1);
  color: #FFFFFF;
}
#supportMount .support-topic-primary .support-topic-scope {
  background: rgba(255, 255, 255, .12);
  color: #C9D6E4;
}
#supportMount .support-topic-primary .support-topic-title {
  margin: 0 0 4px;
  color: #FFFFFF;
  font-family: var(--font-body);
  font-size: 15.5px;
  font-weight: 800;
}
#supportMount .support-topic-primary .support-topic-sub {
  color: #C9D6E4;
  font-size: 13.5px;
  font-weight: 400;
}
#supportMount .support-topic-primary > .support-topic-actions {
  flex: none;
  margin-top: 0;
}
#supportMount .support-topic-primary .support-hub-action-primary {
  flex: none;
  min-height: 44px;
  padding: 0 22px;
  border: 0;
  border-radius: 999px;
  background: #2B6CB0;
  color: #FFFFFF;
  font-family: var(--font-heading);
  font-size: 14px;
  font-weight: 700;
  box-shadow: none;
}

@media (max-width: 700px) {
  #supportMount .support-topic-primary { flex-direction: column; align-items: stretch; }
  #supportMount .support-topic-primary > .support-topic-actions { width: 100%; }
  #supportMount .support-topic-primary .support-hub-action-primary { width: 100%; }
}
