/* ==========================================================================
   Three Hearts Connect — shared components
   ==========================================================================

   Load order, and it is load-bearing:

     <link rel="stylesheet" href="css/tokens.css">
     <link rel="stylesheet" href="css/base.css">
     <link rel="stylesheet" href="css/components.css">   <- this file
     <link rel="stylesheet" href="css/sign-in.css">      <- or office.css

   WHY THIS FILE EXISTS. These four components were written for the sign-in
   screens and lived in sign-in.css. M5's office screens need every one of
   them — a panel, a tinted panel, a notice, and the problem notice with its
   navy edge. Copying forty lines into office.css would be two places
   asserting the same thing about what a panel is, and this project has a rule
   about that: the copy is the one that drifts.

   The same argument brought the cards here from office.css at M6 (§3 below)
   and the brand lockup here from sign-in.css at M9 (§6 below). D-096 wrote
   four more here from the start, because every shell draws them: the marks
   (§7), the tab bar (§8), the list rows More is built from (§9) and the way
   back (§10). The test of
   whether a rule belongs in this file is not how general it looks: it is
   whether more than one shell needs it, because base.html links this file for
   every page and nothing else is linked for every page.

   base.css says at the top of itself that component rules do not go there —
   it is the reset, the page furniture and the accessibility floor — so this
   is a third file rather than an edit to the second, and it is linked from
   templates/base.html so every page has it.

   EVERY VALUE HERE COMES FROM A TOKEN. Not one hex code, not one invented
   size, not one nudged colour. Moved verbatim from sign-in.css, comments and
   all, so the sign-in screens render byte for byte as they did.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Panels — an aside, a callout, a thing worth checking
   --------------------------------------------------------------------------
   The preview calls these .sent-to and .callout. Same two things: a raised
   panel with a soft edge, and a tinted one for the aside about ten minutes.

   Both set their own colour as well as their own background. base.css §7 has
   the full argument: a surface that sets only a background inherits whatever
   text colour it happens to land in, which is harmless on the page ground and
   invisible everywhere else.
   -------------------------------------------------------------------------- */

.panel {
  background-color: var(--surface-raised);
  color: var(--text-body);              /* 10.13:1 on the raised surface */
  border: 1px solid var(--border-soft); /* decorative: the fill identifies it */
  border-radius: var(--radius-lg);
  padding: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.panel--tint {
  background-color: var(--surface-tint);
  color: var(--text-body);              /* 9.41:1 on the tint */
  border-color: transparent;
}

/* base.css gives <p> a bottom margin and adds margin-top between siblings.
   Inside a panel the gap above does the spacing, so both are cleared or the
   panel gets two lots. */
.panel > p {
  margin: 0;
}

.panel > p + p {
  margin-top: 0;
}

/* The address itself, and the name on the signed-in screen. Bigger and navy,
   because it is the thing the person is being asked to check.

   overflow-wrap: anywhere rather than word-break: break-all. break-all splits
   ordinary short addresses that would have fitted; `anywhere` only breaks a
   run that genuinely cannot fit, which at 320px is what a long address is. */
.panel-address,
.panel-name {
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  color: var(--text-strong);            /* 12.62:1 on the raised surface */
  line-height: var(--leading-snug);
  overflow-wrap: anywhere;
}

/* The eyebrow above it. base.css sets this at --size-body and weight 900,
   which is right; only the panel's own spacing is adjusted. */
.panel .eyebrow {
  margin: 0;
}

/* Links inside a panel are bold, as the designed screens have them
   (deploy/preview/style.css: `.sent-to a { font-weight: 700; }`). Both of the
   ones this flow has — the phone number and "Not the right address? Change
   it" — live in a panel, and both are things you tap rather than things you
   read past. */
.panel a {
  font-weight: var(--weight-bold);
}

/* A phone number is a 44px target like anything else you tap. An inline <a>
   would be one line of text high — about 29px — which grounding §8 does not
   allow, and which is genuinely hard to hit on the screen where somebody has
   already failed at something.

   The height itself now comes from `.tap-link` in base.css §4, which is where
   that comment always said it belonged and where the address-panel's change
   link takes it from too. Written out twice, the two copies drift; the one in
   base.css is the floor, and this class only says what is different about a
   phone number, which is nothing at all any more except its company. Kept as
   a named class rather than collapsed into `.tap-link` in the templates so
   that "this is the number to ring" stays sayable in the markup — screen.html
   and signed_in.html both reach for it by that name. */
.phone {
  font-weight: var(--weight-bold);
}


/* --------------------------------------------------------------------------
   2. Notices
   --------------------------------------------------------------------------
   Two kinds: something happened (signed out, code sent again), and something
   needs checking (wrong code, malformed address, throttled).

   NEVER COLOUR ALONE — grounding §8. The brand palette has no red and none is
   invented here: what distinguishes a problem is a heavy navy edge, a bold
   opening phrase that names the problem in words, and aria-invalid plus
   aria-describedby on the control it belongs to. Somebody who cannot see
   colour, and somebody listening rather than looking, both get the whole
   message.
   -------------------------------------------------------------------------- */

.notice {
  background-color: var(--surface-tint);
  color: var(--text-body);              /* 9.41:1 on the tint */
  border-radius: var(--radius-md);
  padding: var(--space-sm);
}

.notice > p {
  margin: 0;
}

/* Two or more messages in one strip are two or more separate things that have
   happened, and they were running together as one paragraph: base.css's
   sibling margin is cleared above and nothing put a gap back. The office
   redirects after every save and Django's message framework carries whatever
   has queued up, so more than one is ordinary here rather than exotic. */
.notice > p + p {
  margin-top: var(--space-2xs);
}

.notice--problem {
  background-color: var(--surface-raised);
  color: var(--text-body);              /* 10.13:1 on the raised surface */
  border: 1px solid var(--border-soft);
  /* The edge. --border-strong is navy at 12.62:1 against the panel it sits
     on, well past SC 1.4.11's 3:1 for a non-text mark. */
  border-inline-start: var(--space-3xs) solid var(--border-strong);
  padding-inline-start: var(--space-xs);
}


/* --------------------------------------------------------------------------
   MOVED HERE AT M6, FROM office.css
   --------------------------------------------------------------------------
   Everything below was written for the office and is now used by two packages:
   templates/office/ and templates/day/. The day screens do not load office.css
   — it is the office's stylesheet and a doorstep is not the office — so the
   choice was to move these or to write them twice. base.html already settled
   which: "two copies of 'what a panel is' would drift. They are shared
   components now."

   Nothing below has been changed. The comments are the ones that were written
   with the rules, because the reasoning is the part worth keeping.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   3. Cards — a person, a family member, a consent record
   --------------------------------------------------------------------------
   One column at every width. A two-column grid at 640px would fit, and it
   would also mean two names side by side on a screen whose whole job is
   making sure the right one is being looked at.
   -------------------------------------------------------------------------- */

.cards {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.cards > li {
  background-color: var(--surface-raised);
  color: var(--text-body);              /* 10.13:1 on the raised surface */
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-sm);
}

/* A whole card that is one link. `display: block` and not flex, so the tap
   target is the whole rectangle rather than the text inside it. */
.card--link {
  display: block;
  text-decoration: none;
  color: var(--text-body);
}

.card--link:hover .card-name,
.card--link:focus-visible .card-name {
  text-decoration: underline;
}

/* Something that has ended: still legible, visibly not the live thing.
   NOT by opacity alone — the border says it too, and the words in the
   template say it in full.

   THE CHILD COMBINATOR IS NOT DECORATION. Written as a bare `.card--quiet`
   this rule was dead: (0,1,0) against `.cards > li` at (0,1,1) — one class
   against one class and one element — so `.cards > li` won both declarations
   and every quiet card rendered byte for byte like a live one. Three screens
   were affected and none of them looked broken, which is why it stood:
   templates/office/consent.html (a superseded consent record),
   templates/office/person.html ("People I have stopped") and
   templates/office/team.html (somebody not attending visits). Being later in
   the file does not help — source order is only consulted when specificity
   ties, and this did not tie.

   `.cards > .card--quiet` is (0,2,0) and wins outright rather than by
   position, so re-ordering this file cannot silently switch it off again.
   Rejected: `li.card--quiet`, which ties at (0,1,1) and wins only because it
   is further down the file; and bumping `.cards > li` down, which would have
   moved the problem to whatever is written next.

   Every card that carries this class ALSO says its state in words — "Stopped
   23 August 2026" and the reason, a "Replaced" pill, a "Not attending visits"
   pill — so nothing here is colour or texture carrying meaning on its own
   (grounding §8, WCAG 1.4.1). That is the templates' job and they do it; this
   rule is the second signal, not the first. */
.cards > .card--quiet {
  background-color: var(--surface-page);
  border-style: dashed;
}

.card-name {
  display: block;
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  color: var(--text-strong);            /* 12.62:1 on the raised surface */
  line-height: var(--leading-snug);
  overflow-wrap: anywhere;
}

/* A card name that is a LINK is a target, not a reference.

   `.card--link` on the home page makes the whole rectangle the anchor, so
   that one is fine. The family-and-friends cards on a person's page are the
   other shape: `<li class="card"><a class="card-name">Ruth</a>`, where the
   anchor is the name and nothing else. At --size-md on --leading-snug that
   line box is 20 x 1.35 = 27px tall — measured on the rendered page, not
   estimated — which is under grounding section 8's floor on the most-used
   piece of navigation in the office.

   WCAG 2.2 SC 2.5.8 would have accepted it at 24x24. Grounding section 8 does
   not, and base.css says why at length where it puts the same floor on
   checkboxes and radios: 44px is a fingertip measure and this audience is on
   a phone. It is the same rule `.tap-link` exists for — "opted into on the
   links that are targets rather than references" — applied to a link that is
   plainly a target.

   flex + align-items rather than padding, so a name that wraps to two lines
   simply gets taller instead of being pushed off centre. */
a.card-name {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
}

.card-sub {
  display: block;
  margin: var(--space-3xs) 0 0;
  color: var(--text-muted);             /* 6.17:1 on the raised surface */
}

.card .pill,
.cards > li > .pill {
  margin-top: var(--space-2xs);
}

/* A list of what somebody can see. Not a bulleted list of nouns — each line
   is a whole sentence from Grant.choices, in Tanya's own voice. */
.ticks {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.ticks > li {
  padding-inline-start: var(--space-sm);
  border-inline-start: var(--space-3xs) solid var(--accent);  /* 3.26:1, SC 1.4.11 */
  font-weight: var(--weight-semibold);
}


/* --------------------------------------------------------------------------
   4. Forms
   --------------------------------------------------------------------------
   base.css §6 already gives every control its 44px floor, its 18px type and
   its 2px --border-control outline. Only the copy around a control is here.
   -------------------------------------------------------------------------- */

.field-help {
  margin: 0 0 var(--space-3xs);
  color: var(--text-muted);             /* 5.96:1 on the page ground */
}

/* What went wrong, before the box rather than after it — so a screen reader
   reaches it on the way to the control rather than once somebody has already
   retyped everything.

   NEVER COLOUR ALONE. The brand palette has no red and none is invented: the
   mark is a heavy navy edge (12.19:1 against the page, well past SC 1.4.11's
   3:1 for a non-text mark), and the words carry the meaning. Django puts
   aria-invalid and aria-describedby on the control itself, pointing here. */
.field-problem {
  margin: 0 0 var(--space-2xs);
  padding-inline-start: var(--space-xs);
  border-inline-start: var(--space-3xs) solid var(--border-strong);
  font-weight: var(--weight-bold);
  color: var(--text-strong);            /* 12.19:1 on the page ground */
}

/* A box reached by a link keeps its label in view.

   The checklist on somebody's page links straight to the box it asks for —
   /change/#id_address_line1, /change/#id_postcode (D-089) — so the details
   form opens at that box instead of with it below the fold. A browser scrolls
   the box itself to the top edge of the screen, and that put its label, the
   only thing saying what the box is for, just above the edge and out of sight.

   The margin is room for a one-line label: 18px at the body leading plus its
   gap is about 2rem, and --space-xl is 3rem. Both are rem, so they grow
   together at 200% text. No script: the fragment and this are all of it. */
.field > :is(input, select, textarea) {
  scroll-margin-top: var(--space-xl);
}

fieldset {
  border: 0;
  padding: 0;
  margin: 0;
}

fieldset + fieldset,
fieldset + .field,
.field + fieldset {
  margin-top: var(--space-lg);
}

legend {
  padding: 0;
  font-size: var(--size-lg);
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  color: var(--text-strong);
  line-height: var(--leading-snug);
}

.choices .choice + .choice {
  margin-top: var(--space-2xs);
}

/* A choice whose label is more than one line — the name on top, and what
   tells it from the choice next to it underneath.

   "Somebody already on my books" is the screen this exists for. Its options
   were one line each, and two contacts rendered byte for byte the same, so
   the wrong radio put a family member on a person they have no business
   seeing. See templates/office/link_existing.html.

   NO SIZING HERE. The 44px in both axes is on the control itself in base.css
   §6, and `.choice` is the flex row it sits in; this only lets the label be
   two lines without the row collapsing round it. The <label> keeps the whole
   text block, so tapping either line hits the control. */
.choice--stacked > label {
  flex: 1;
  cursor: pointer;
}

.choice-name {
  display: block;
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  color: var(--text-strong);            /* 12.19:1 on the page ground */
  line-height: var(--leading-snug);
  overflow-wrap: anywhere;
}

.choice-sub {
  display: block;
  margin-top: var(--space-3xs);
  font-weight: var(--weight-regular);
  color: var(--text-muted);             /* 5.96:1 on the page ground */
}

/* Said when two options carry the same name and the screen cannot honestly
   tell them apart. Same navy edge as .field-problem and the same reason: the
   brand palette has no red, and grounding §8 forbids colour as a lone signal,
   so the mark is a heavy edge (12.19:1 — SC 1.4.11 wants 3:1) and the words
   carry the whole meaning on their own. */
.choice-warn {
  margin: var(--space-2xs) 0 0;
  padding: var(--space-2xs) var(--space-xs);
  border-inline-start: var(--space-3xs) solid var(--border-strong);
  background-color: var(--surface-tint);
  color: var(--text-body);              /* 9.41:1 on the tint */
  border-radius: var(--radius-sm);
}

/* Two buttons under one form sit apart rather than side by side: at 320px a
   row of two 44px targets is two targets a thumb can hit by mistake. */
form > button + button,
form > button + .button {
  margin-top: var(--space-xs);
}


/* --------------------------------------------------------------------------
   5. The page that shows a key-safe code
   --------------------------------------------------------------------------
   There are exactly two of them - templates/office/getting_in.html and
   templates/day/getting_in.html - and both are audited on the way in (D-018,
   D-041). They carry the same heavy navy edge as `.guarded` on a person's page
   so that the two read as one kind of place: a room you are let into rather
   than a page you land on.

   NEVER COLOUR ALONE (grounding section 8): the edge is a non-text mark at
   12.19:1, well past SC 1.4.11's 3:1, and the sentence inside it carries the
   whole meaning on its own.
   -------------------------------------------------------------------------- */

.guarded-note {
  background-color: var(--surface-raised);
  color: var(--text-body);              /* 10.13:1 on the raised surface */
  border: 1px solid var(--border-soft);
  border-inline-start: var(--space-2xs) solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-sm);
  font-weight: var(--weight-semibold);
}

.guarded-note p {
  margin: 0;
}

.guarded-note p + p {
  margin-top: var(--space-2xs);
}


/* --------------------------------------------------------------------------
   MOVED HERE AT M9, FROM sign-in.css
   --------------------------------------------------------------------------
   Same argument as the M5 and M6 moves above, and a worse symptom than either
   of them had. `templates/_lockup.html` was written for the sign-in screens,
   when they were the only screens with a masthead. Five shells include it now
   — signin/screen.html, signed_in.html, office/base.html, day/base.html and
   family/base.html — and only the first two load sign-in.css, where these
   five rules lived.

   So on EVERY office, doorstep and family page the mark had no width rule of
   any kind. The only svg rule in the project is base.css §1 — the reset, in
   the `img, picture, video, canvas, svg` group, not §2 — which sets
   `max-width: 100%` and `height: auto` and no width at all, so an inline
   <svg> with a viewBox and no width attribute fell back to the replaced-
   element default — 300px wide, and square, because the viewBox is 1:1. A
   300px heart at the top of a 390px phone pushed the page heading off the
   first screen. The wordmark lost its column, its size, its weight and its
   colour at the same time and simply read as two lines of body text.

   D-096 took the masthead bands off the office, the doorstep and the family
   screens, and their per-shell anchors (`.office-lockup`, `.day-lockup`,
   `.family-lockup`) went with them: the tab bar is the way home now. The
   lockup is still drawn by the sign-in screens, "My own account" and the error
   pages, and 500.html loads no sheet but this one and base.css, so the
   component stays here. What is HERE is the component itself: the mark, the
   wordmark, and the relationship between them, which is the same thing
   wherever it is drawn because it is the brand and not a layout.

   Nothing below has been changed except the two lines the wrapping fix needed
   (marked M9). The comments are the ones written with the rules.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   6. The lockup
   --------------------------------------------------------------------------
   Grounding §9 / the Brand Toolkit: three overlapping hearts, then the
   wordmark. Never below 40px on screen, clear space equal to the height of
   one heart.
   -------------------------------------------------------------------------- */

.lockup {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  /* M9. Without this the mark and the wordmark are one unbreakable row, and a
     masthead is the widest thing on the page. At 320px with text at 200% the
     mark is 112px (3.5rem, and rem scales with the browser's text size) and
     "Three Hearts" is wider than what is left, so the row overflowed and the
     WHOLE PAGE scrolled sideways — grounding §8 requires 200% to stay usable,
     and a page you have to drag left and right to read is not.

     Wrapping puts the wordmark under the mark instead. It only happens when
     it has to: at every ordinary size the row still fits and nothing moves.
     Rejected: shrinking the mark below 3.5rem, which the Brand Toolkit's
     40px floor forbids, and truncating the wordmark, which would hide the
     name of the business on its own masthead. */
  flex-wrap: wrap;
}

.lockup-mark {
  width: 3.5rem;
  height: 3.5rem;
  flex: none;
}

.wordmark {
  display: flex;
  flex-direction: column;
  line-height: var(--leading-tight);
  /* M9, and it is the half of the wrapping fix that is easy to miss. A flex
     item defaults to `min-width: auto`, which is its MIN-CONTENT width, and a
     flex item cannot be squeezed below that however much its parent wraps —
     so with this line missing the wordmark still held the row open at the
     full width of "Three Hearts" and the sideways scroll stayed. Zero lets it
     shrink to its longest word and wrap at the space. */
  min-width: 0;
}

.wordmark-name {
  font-family: var(--font-ui);          /* Nunito. The wordmark is not a heading */
  font-weight: var(--weight-heavy);     /* 800, per the toolkit */
  font-size: var(--size-md);
  /* navy: 12.19:1 on the page ground, which is every screen that still draws
     the lockup. It was also 12.62:1 on the raised office, day and family
     mastheads until D-096 removed them. */
  color: var(--text-strong);
}

/* The one place a smaller size than --size-body is right: this is a wordmark
   subtitle, not body copy, and the toolkit sets it in small caps under the
   name. Weight 700 and the wide tracking carry it. --text-muted rather than
   sage, which measures 3.26:1 on cream and is prohibited as small text in
   tokens.css. */
.wordmark-sub {
  font-family: var(--font-ui);
  font-weight: var(--weight-bold);
  font-size: 0.85rem;
  /* 5.96:1 on the page ground, where every lockup now sits. */
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.22em;
}


/* --------------------------------------------------------------------------
   6a. The page header (D-097)
   --------------------------------------------------------------------------
   The heading of a screen with its heart and one action beside it, and a line
   under both: Today in the office ("Today", the heart and a Month button), and
   the month ("September" and its two arrows).

   ONE SHELL USES THIS TODAY, which is less than D-069 asks of this file
   ("more than one shell includes it"). Both users, office/diary_planner.html
   and office/diary.html, also load diary.css. It is here because the build
   plan put it here, ahead of a second user: the approved design draws the
   relative's Visiting page with the same heart beside its heading. That page
   (D-099) was built with its own `.family-hello` in family.css instead, so
   until one of the two is folded into the other there ARE two copies of
   "what a page header is", and this comment does not pretend otherwise.

   The heart is templates/_heart_mark.html, sized by `.page-mark` below: an
   inline <svg> with a viewBox and no CSS size is 300px wide (D-069). Marks
   inside the action are the shared `.icon` (§7), sized there.

   THE ACTION WRAPS ONTO ITS OWN LINE AT LARGE TEXT and then sits at the
   inline end, not under the heading's start: `margin-inline-start: auto`,
   D-083's fix for a wrapped row. The heading row is (0,1,1) against base.css's
   `* + :is(h1…)` at (0,0,1), so the heart before the <h1> never pushes it
   down by a heading's top margin.
   -------------------------------------------------------------------------- */

.page-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.page-head-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}

.page-head-title {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-inline-size: 0;
}

.page-head-row h1 {
  margin: 0;
  overflow-wrap: anywhere;
}

/* base.css's `:is(h1…) + *` gives whatever follows a heading a top margin,
   which in a centred row would push the action down out of line. */
.page-head-row > * {
  margin-block: 0;
}

/* The heart beside a heading: 36px at the default size, the height of the
   <h1>'s own line, as the mockup draws it. */
.page-mark {
  inline-size: 2.25rem;
  block-size: 2.25rem;
  flex: none;
}

.page-head-sub {
  margin: 0;
  font-size: var(--size-md);
  color: var(--text-body);                /*  9.79:1 on the page */
}

/* A round secondary button, as drawn. (0,2,0), so it outranks `.button`'s
   radius and padding on specificity rather than on file order. */
.button.page-head-action {
  margin-inline-start: auto;
  padding-inline: var(--space-sm);
  border-radius: var(--radius-pill);
}


/* ==========================================================================
   7. The marks (D-096)
   ==========================================================================

   One stroked 24px set, drawn from the sprite in templates/_icons.html by the
   tab bar, the rows on More and the back links. The burger menu of D-082 and
   D-083 is gone, and with it every `.menu*` rule that used to be here: the tab
   bar below replaced it.

   THE SPRITE IS NEVER DRAWN. base.css §1 gives every `svg` `display: block`,
   which beats the browser's own `[hidden] { display: none }`, and an `<svg>`
   with no size is 300 × 150. Without this rule the sprite is a blank band
   across the top of every signed-in page. A `<use>` still finds a `<symbol>`
   inside a sprite that is not displayed.
   -------------------------------------------------------------------------- */

.icon-sprite {
  display: none;
}

/* EVERY MARK IS SIZED HERE. base.css §1's only svg rule sets `max-width: 100%`
   and `height: auto` and never a width, so an <svg> with a viewBox and no CSS
   size renders at the 300px replaced-element default. That is D-069 — the
   300px heart on every page behind the sign-in for four milestones. The
   templates also carry width and height attributes, for a stale sheet.

   The stroke is here too rather than on the markup, so a symbol added later
   cannot come out unstyled by forgetting an attribute. The colour is the
   colour of the words beside it. */
.icon {
  inline-size: var(--space-md);           /* 24px */
  block-size: var(--space-md);
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* A chevron: quieter than a mark, and it says only "this goes somewhere". */
.icon--small {
  inline-size: var(--space-sm);           /* 16px */
  block-size: var(--space-sm);
}


/* ==========================================================================
   8. The tab bar (D-096)
   ==========================================================================

   templates/_tabbar.html, drawn by templates/base.html after the footer on
   every page that has tabs. What is on it is apps/common/menu.py's decision.

   STICKY, NOT FIXED. The bar is the last thing drawn in <body> (only a
   doorstep's script tag comes after it), `position: sticky` to the foot of the
   screen. It keeps its own space in the page, so at the end
   of every page nothing is underneath it — not "How I let you know", not the
   last button — however tall large text makes it. A fixed bar would need the
   page padded by its height, and that height changes with the text size and
   when a tab wraps; a wrong guess hides the footer link the weekly email
   promises. If sticky ever stops working (an ancestor gaining `overflow`),
   the bar sits at the end of the page and hides nothing, which is the right
   way for it to fail.

   AROUND IT:
     * `env(safe-area-inset-bottom)` keeps the tabs above a phone's home
       indicator. It needs `viewport-fit=cover`, in base.html's viewport.
     * `scroll-padding-block-end` on the root stops the browser scrolling a
       control that Tab focuses under the bar (WCAG 2.2 SC 2.4.11, C43). The
       figure is the bar's height at ordinary text: 8 + 4 + 32 + 4 + 21.6 + 8
       = 77.6px, plus the inset.
     * the body becomes a flex column, so on a short page the footer and the
       bar sit at the foot of the screen, as the sign-in screens already do.
     * tabs wrap to a second row rather than overlapping.
     * SHORT SCREENS. Under `max-height: 30em` the bar stops following the
       screen and sits at the end of the page. `em` in a media query is the
       browser's default text size, 16px, so 30em is 480px of visible height
       at ordinary settings. A larger default text size raises that figure,
       and page zoom shrinks the height it is compared with, so both trip it
       sooner: a phone on its side does, and so, probably from about 150% text
       in Safari with its toolbars showing, does large text in portrait. Then Tanya
       scrolls to the end to change tab rather than losing a quarter of the
       screen to it. Accepted (D-096); Jon's phone check records the step at
       which it happens.

   NO SCRIPT, and no inline style (config/csp.py). Links, not ARIA tabs.

   THE COLOURS, MEASURED (WCAG 2.2 relative luminance):
     navy bar against the page, raised and tint grounds   12.19, 12.62, 11.73
     plain label and mark, --text-on-inverse-soft          9.74 on navy
     lit label, --text-on-inverse                         12.49 on navy
     lit pill, sage-light, against the bar                 5.85 (a mark: 3 needed)
     navy mark on the sage-light pill                      5.85
     focus ring, sage-light, on the bar                    5.85 (navy would be 1.00)
   The lit and plain LABEL colours are 1.28:1 apart, which nobody can see, so
   colour is not the signal. The pill and the 800-against-600 weight are, keyed
   on `aria-current` so a screen reader hears the same fact. No new token: the
   pill is --text-on-inverse-accent, which is sage-light and legal only on navy.
   ========================================================================== */

.with-tabbar {
  scroll-padding-block-end: calc(
    2 * var(--space-2xs) + 2 * var(--space-3xs) + var(--space-lg)
    + var(--size-body) * var(--leading-tight) + env(safe-area-inset-bottom, 0px)
  );
}

.with-tabbar body {
  display: flex;
  flex-direction: column;
}

.with-tabbar main {
  flex-grow: 1;
}

.tabbar {
  position: sticky;
  inset-block-end: 0;
  z-index: 10;                            /* under the skip link's 100 */

  /* SURFACES DESCRIBE THEMSELVES (base.css §7). */
  background-color: var(--surface-inverse);
  color: var(--text-on-inverse-soft);     /* 9.74:1 on navy */

  padding-block: var(--space-2xs) calc(var(--space-2xs) + env(safe-area-inset-bottom, 0px));
  padding-inline:
    max(var(--space-2xs), env(safe-area-inset-left, 0px))
    max(var(--space-2xs), env(safe-area-inset-right, 0px));
}

.tabbar-list {
  display: flex;
  flex-wrap: wrap;
  max-inline-size: var(--container-max);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

/* Equal columns. At 200% text or on the narrowest phone a tab wraps to a new
   row instead of overlapping its neighbour. */
.tabbar-list > li {
  flex: 1 1 0;
  min-inline-size: max-content;
}

/* Not underlined: this is a bar of places, not a link in a sentence. The mark
   and the label, a whole column tall, are what say it can be tapped. */
.tabbar-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3xs);
  min-block-size: var(--tap-min);
  padding-block-start: var(--space-3xs);
  border-radius: var(--radius-md);
  color: var(--text-on-inverse-soft);     /* 9.74:1 on navy */
  text-decoration: none;
}

/* The mockup's pill: 64 × 32, empty until the tab is lit. */
.tabbar-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--space-2xl);
  block-size: var(--space-lg);
  border-radius: var(--radius-pill);
}

.tabbar-label {
  font-size: var(--size-body);
  line-height: var(--leading-tight);
  font-weight: var(--weight-semibold);
}

.tabbar-tab:hover .tabbar-label {
  text-decoration: underline;
}

/* base.css §5's navy ring would be 1.00:1 on the bar. */
.tabbar-tab:focus-visible {
  outline-color: var(--focus-ring-on-inverse);  /* 5.85:1 on navy */
}

.tabbar-tab[aria-current] {
  color: var(--text-on-inverse);          /* 12.49:1 on navy */
}

.tabbar-tab[aria-current] .tabbar-mark {
  background-color: var(--text-on-inverse-accent);  /* 5.85:1 against the bar */
  color: var(--surface-inverse);                     /* 5.85:1 on the pill */
}

.tabbar-tab[aria-current] .tabbar-label {
  font-weight: var(--weight-heavy);
}

@media (max-height: 30em) {
  .with-tabbar {
    scroll-padding-block-end: 0;
  }

  .tabbar {
    position: static;
  }
}

/* Forced colours drop backgrounds, so the pill would vanish and take the lit
   tab's sighted signal with it. An outline survives. */
@media (forced-colors: active) {
  .tabbar {
    border-block-start: 2px solid CanvasText;
  }

  .tabbar-tab[aria-current] .tabbar-mark {
    outline: 2px solid CanvasText;
  }
}


/* ==========================================================================
   9. List rows (D-096)
   ==========================================================================

   A mark, the words, and a chevron, with a rule between rows: the row the
   approved mockups draw for the More page and for a person's page. More is
   the first screen built from it.

   A ROW, NOT A TEXT LINK, and the underline is dropped deliberately. base.css
   underlines every `<a>`, and tokens.css says why: "Never colour alone —
   links carry an underline as well." That rule is about a link in PROSE, where
   the only thing telling it from the sentence around it would otherwise be its
   colour. Nothing here is prose: each row is the full width of the column,
   has a mark at one end and a chevron at the other, is separated from its
   neighbours by a rule, and underlines its label under a pointer. Its
   link-ness is carried by structure, so SC 1.4.1 is met without decoration.

   MEASURED on the page ground: the label, navy, 12.19:1; the line under it and
   the chevron, --text-muted, 5.96:1; the rule, --border-soft, 1.11:1, which is
   trim and separates nothing on its own. A tinted hover would be 1.04:1 and is
   not used; the hover is the label's underline.
   ========================================================================== */

.list-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}

.list-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-block-size: calc(var(--space-xl) + var(--space-2xs));  /* 56px, as drawn */
  padding-block: var(--space-2xs);
  color: var(--text-strong);              /* 12.19:1 on the page */
  text-decoration: none;
}

.list-rows > li + li > .list-row {
  border-block-start: 1px solid var(--border-soft);
}

.list-row-words {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-inline-size: 0;
}

.list-row-label {
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  overflow-wrap: anywhere;
}

.list-row:hover .list-row-label {
  text-decoration: underline;
}

.list-row-sub {
  color: var(--text-muted);               /* 5.96:1 on the page */
  line-height: var(--leading-snug);
}

.list-row-chevron {
  color: var(--text-muted);               /* 5.96:1, a mark: 3 needed */
}

/* "Signed in as", tucked under the More heading as the mockup draws it.
   (0,2,0), so it outranks `.stack > * + *` rather than relying on order. */
.stack > .more-who {
  margin-block-start: var(--space-3xs);
}


/* ==========================================================================
   10. The way back (D-096)
   ==========================================================================

   templates/office/_back.html, day/_back.html and family/_back.html: a back
   chevron, then the name of the place. `.tap-link` (base.css §4) gives it the
   44px; each shell's wrapper class gives it its spacing, and nothing else.

   Not underlined, as the mockups draw it, for the row's reason above: it is
   alone on its own line at the top of the screen, led by a chevron, and never
   inside a sentence. Bold, navy (12.19:1), and underlined under a pointer.
   ========================================================================== */

.back-link {
  gap: var(--space-3xs);
  font-weight: var(--weight-bold);
  text-decoration: none;
}

.back-link:hover .back-link-label {
  text-decoration: underline;
}
