/* =========================================================================
   Woofline — design system
   Hand-written, no build step. Tokens first, then primitives, then blocks.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Tokens
   Complete light palette on bare :root. Dark redefines ONLY what changes,
   twice: once for the system preference (guarded so an explicit light
   choice wins) and once for the explicit dark choice.
   ------------------------------------------------------------------------- */

:root {
  /* Warm stone & ink — the page is mid-stone and *surfaces are the same hue*:
     in this design elevation is drawn by the paired shadows below, not by a
     lighter fill. --surface-bright is the exception, reserved for the few
     things that must read as lifted-and-lit (active cards, avatar rings). */
  --bg: #e6e2da;
  --bg-tint: #ded8cc;
  --surface: #eeebe4;
  --surface-2: #e9e5dd;
  --surface-3: #e0dbd1;
  --surface-bright: #fdfcf9;
  --border: #d9d3c7;
  --border-strong: #c9c2b4;
  --text: #241f18;
  --text-muted: #6b6155;
  --text-faint: #8d8579;

  /* Brand — park green (was warm amber) */
  --brand: #2f7d4f;
  --brand-hover: #205c39;
  --brand-soft: #e7f0e9;
  --brand-ring: #2f7d4f33;
  --on-brand: #ffffff;

  /* Earth accent — the brown the park surfaces use */
  --accent: #846542;
  --accent-soft: #f2ece1;

  /* Semantic */
  --like: #e14f62;
  --like-soft: #fdecee;
  --success: #2f7d4f;
  --success-soft: #e7f0e9;
  --danger: #ae3e39;
  --danger-soft: #f7e9e6;
  --info: #2c6183;
  --info-soft: #e8f0f5;
  /* Warm amber for the needs-your-attention register (the verify-email
     banner) — warning previously borrowed brand green, which read as
     praise. */
  --warn: #7a5b1e;
  --warn-soft: #f3e6cf;
  --focus: #2f7d4f;

  /* Shape */
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-xl: 28px;
  --radius-full: 999px;

  /* Space */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;

  /* Neumorphic elevation. Light source top-left, always. The grammar the
     whole UI follows: raised = actionable, pressed (inset) = active/selected,
     inset well = input. Shadow pairs live behind --shade/--shade-hi so the
     dark theme swaps two values instead of ten. */
  /* Sage-cast, per the Nav-and-Fields artboard — every neutral shadow leans
     green there (#ccd6c9), which is where the design's ambient green tint
     comes from. The tinted --shade-* mixes below inherit the cast. */
  --shade: #ccd6c9;
  --shade-hi: #ffffff;
  --raise-sm: 4px 4px 8px var(--shade), -4px -4px 8px var(--shade-hi);
  --raise: 6px 6px 12px var(--shade), -6px -6px 12px var(--shade-hi);
  --raise-lg: 8px 8px 18px var(--shade), -8px -8px 18px var(--shade-hi);
  --press-sm: inset 4px 4px 8px var(--shade), inset -4px -4px 8px var(--shade-hi);
  --press: inset 5px 5px 10px var(--shade), inset -5px -5px 10px var(--shade-hi);
  --pop: var(--float-sm);

  /* Tinted shades — the artboard's rule: a coloured surface casts a shadow of
     its own hue (accent-soft casts #d9cdb9, info-soft #c9d8e2, green things a
     green-grey), and elements sitting INSIDE a tinted card inherit the tint.
     Mixed from the live tokens so the dark theme derives its own versions. */
  /* oklch, not srgb: averaging a saturated colour into warm grey in srgb
     cancels the hue (blue + warm grey = grey); oklch keeps the tint, which is
     the entire point of these. */
  --shade-brand: color-mix(in oklch, var(--brand) 22%, var(--shade));
  --shade-accent: color-mix(in oklch, var(--accent) 38%, var(--shade));
  --shade-info: color-mix(in oklch, var(--info) 36%, var(--shade));
  --shade-warn: color-mix(in oklch, var(--warn) 30%, var(--shade));
  --raise-brand: 6px 6px 12px var(--shade-brand), -6px -6px 12px var(--shade-hi);
  --raise-brand-sm: 4px 4px 8px var(--shade-brand), -4px -4px 8px var(--shade-hi);
  --raise-accent: 6px 6px 12px var(--shade-accent), -6px -6px 12px var(--shade-hi);
  --raise-accent-sm: 4px 4px 8px var(--shade-accent), -4px -4px 8px var(--shade-hi);
  --raise-info: 6px 6px 12px var(--shade-info), -6px -6px 12px var(--shade-hi);
  --raise-info-sm: 4px 4px 8px var(--shade-info), -4px -4px 8px var(--shade-hi);
  --raise-warn-sm: 4px 4px 8px var(--shade-warn), -4px -4px 8px var(--shade-hi);
  --press-brand-sm: inset 3px 3px 6px var(--shade-brand), inset -3px -3px 6px var(--shade-hi);
  --press-info-sm: inset 3px 3px 7px var(--shade-info), inset -3px -3px 7px var(--shade-hi);

  /* Legacy elevation names, remapped so the long tail of components joins the
     system without each rule being edited: resting/small = raised, hover lift
     = larger raise. --shadow-lg is the floating tier's drop (D1 --float): its
     users are detached overlays (menus, typeahead, the sheet), and an element
     detached from the surface cannot cast a neumorphic pair. */
  --shadow-sm: var(--raise);
  --shadow-md: var(--raise-lg);
  --shadow-lg: var(--float);

  /* ---- Depth & layers (D1) ------------------------------------------------
     Tier 1 ground, Tier 3 glass/float, Tier 4 deck. Every themed value here
     is redefined in BOTH dark blocks below; test_design_system guards the
     parity, so a token added to one block and not the other fails the suite. */

  /* Tier 1 — ambient ground. Geometry is per-page (body[data-page]), the
     stops are per-theme; the composed gradient is theme-neutral and lives
     only here. */
  --ground-0: #efece5;
  --ground-1: #e2ddd3;
  --ground-2: #d6d0c4;
  --ground-mid: 62%;
  --ground-size: 130% 110%;
  --ground-x: 50%;
  --ground-y: -10%;
  --ground: radial-gradient(var(--ground-size) at var(--ground-x) var(--ground-y),
    var(--ground-0) 0%, var(--ground-1) var(--ground-mid), var(--ground-2) 100%);

  /* Tier 2 additions */
  --well: #f2efe8;                                   /* inset field fill */
  --well-shadow: inset 2px 2px 5px #ccd6c9, inset -1px -1px 2px #ffffff;
  --text-body: #241f18;                              /* = --text in light; warmer in dark */
  --brand-text: #205c39;                             /* text-sized green: links, active pill labels */
  --accent-chip: #e9d9c3;
  --gold: #f2c14e;                                   /* rating star, the active moon — same both themes */
  --raise-lift: 10px 10px 22px #c6d3c4, -10px -10px 22px #ffffff;
  --raise-tint: 6px 6px 14px #c8d5c6, -6px -6px 14px #ffffff;
  --surface-tint: linear-gradient(150deg, #eef4ec, #e6efe6);
  --press-active: var(--press-brand-sm);             /* inset-green "selected"; a literal in dark */

  /* Tier 3 — floating. Drop-only shadows: a detached thing casts no pair. */
  --float: 0 22px 40px -18px rgb(36 31 24 / 0.4);
  --float-sm: 0 14px 26px -12px rgb(36 31 24 / 0.35);
  --float-up: 0 -14px 30px -18px rgb(36 31 24 / 0.35); /* bottom-stuck bars */
  --float-brand: 0 14px 26px -12px rgb(47 125 79 / 0.5); /* green pills, map pins */
  --lift-brand: 0 12px 22px -9px rgb(47 125 79 / 0.6);   /* green CTA, the FAB */
  --glass-bg: rgb(253 252 249 / 0.85);                /* panels, chips, sheets */
  --glass-bar-bg: rgb(238 235 228 / 0.8);             /* chrome bars: header, tab bar, sticky row */
  --glass-border: rgb(255 255 255 / 0.6);
  --glass-blur: blur(14px) saturate(1.2);             /* theme-neutral */
  --glass-shadow: var(--float);                       /* theme-neutral alias */
  --glass-hero-bg: rgb(255 255 255 / 0.16);           /* on a green/photo hero — same both themes */
  --glass-hero-border: rgb(255 255 255 / 0.25);
  --glass-hero-blur: blur(10px);

  /* Tier 4 — deck (stacked cards). */
  --peek-bg: #e4e0d8;
  --peek-shadow: 5px 5px 12px #ccd6c9;
  --deck-shadow: 0 20px 36px -18px rgb(36 31 24 / 0.45);

  /* Segmented theme toggle */
  --seg-active-bg: #fdfcf9;
  --seg-active-fg: #846542;

  /* Scene (auth + wizard): the scrim over the blurred feed, fields on glass,
     the one very long drop the floating auth card casts. */
  --scrim: rgb(36 31 24 / 0.44);
  --glass-well: rgb(255 255 255 / 0.6);
  --glass-well-shadow: inset 2px 2px 5px rgb(160 168 152 / 0.5), inset -1px -1px 2px #ffffff;
  --glass-rim: inset 0 1px 0 rgb(255 255 255 / 0.8);
  --float-lg: 0 40px 80px -28px rgb(36 31 24 / 0.6);

  /* Type — Manrope everywhere; the artboard separates roles by weight
     (700/800), not by family, so display = body. */
  --font-body: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: var(--font-body);

  /* Motion */
  --ease: cubic-bezier(0.2, 0.8, 0.3, 1);
  --dur-fast: 120ms;
  --dur: 200ms;

  /* Layout */
  --shell-max: 68rem;
  --rail: 20rem;
  /* Bottom clearance the fixed .tabbar demands of bottom-stuck elements
     (the sticky save row). 0 on desktop; the tabbar's own media block
     raises it, so stuck elements track the bar's presence exactly. */
  --tabbar-clearance: 0px;
  --header-h: 3.75rem;        /* the 60px glass bar; .rail--sticky offsets from it */

  /* Z scale — the informal ladder made explicit. */
  --z-sticky: 10;    /* .form-actions--sticky, floating map controls */
  --z-popover: 30;   /* picker, tip, combobox */
  --z-sheet: 35;     /* the Parks bottom sheet */
  --z-chrome: 40;    /* header, tab bar */
  --z-menu: 50;      /* .menu__panel */
  --z-overlay: 60;   /* reserved: scrims/modals */
  --z-skip: 100;     /* skip link */

  /* Categorical series, assigned in fixed order — never cycled, never
     generated. Slot 0 is the neutral used for "Other"; re-validated against
     the stone surface in the Phase C audit. */
  /* Breed-mix capsules — the artboard's earthy register (its dogs screen
     draws exactly #2c6183/#8a6a45/#5f8f6b/#c4bcae). Separate from the chart
     --series tokens, which stay high-contrast for data bars. Identity never
     rests on these hues: the bar always ships with the visible percentage
     line or the legend. */
  --mix-0: #c4bcae;
  --mix-1: #2c6183;
  --mix-2: #8a6a45;
  --mix-3: #5f8f6b;
  --mix-4: #7d6379;
  --mix-5: #b08d63;
  --mix-6: #5a7182;
  --mix-7: #79855a;
  --mix-8: #a4685a;

  --series-0: #c9bda6;
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
  --series-6: #008300;
  --series-7: #4a3aa7;
  --series-8: #e34948;
}

/* System preference — skipped when the user explicitly chose light */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #232019;
    --bg-tint: #2a261e;
    --surface: #2a261f;
    --surface-2: #39342a;
    --surface-3: #211d16;
    --surface-bright: #2f2b23;
    --border: #3a342a;
    --border-strong: #4b4437;
    --text: #ece7dd;
    --text-muted: #a89a8e;
    --text-faint: #7d7168;

    --brand: #4e9d6e;
    --brand-hover: #63b381;
    --brand-soft: #23352b;
    --brand-ring: #4e9d6e3d;
    --on-brand: #10241a;

    --accent: #b08d63;
    --accent-soft: #322a20;

    --like: #e0657a;
    --like-soft: #3a2529;
    --success: #4e9d6e;
    --success-soft: #23352b;
    --danger: #e8695f;
    --danger-soft: #34201e;
    --info: #6ba9cf;
    --info-soft: #1b2a33;
    --warn: #d3a54e;
    --warn-soft: #33291a;
    --focus: #4e9d6e;

    /* Dark neumorphism reads through restraint: the dark half of each pair
       deepens toward true shadow while the highlight is a whisper of warm
       light, never white — white rims on dark stone read as wireframe. The
       depth-spec pair: drop #16130e, up-light #322d24 (often at low alpha). */
    --shade: #16130e;
    --shade-hi: #322d24;

    /* Depth & layers (D1) — keep identical to the other dark block. */
    --ground-0: #26231c;
    --ground-1: #201d16;
    --ground-2: #1a170f;
    --ground-mid: 55%;
    --ground-size: 120% 90%;
    --ground-x: 75%;
    --ground-y: 0%;
    --well: #211e17;
    --well-shadow: inset 2px 2px 5px #131009, inset -1px -1px 2px #322d24;
    --text-body: #e6ddcf;
    --brand-text: #6cc493;
    --accent-chip: #3a2f20;
    --raise-lift: 10px 10px 22px #131009, -6px -6px 16px #35302733;
    --raise-tint: 6px 6px 14px #131009, -6px -6px 14px #322d2433;
    --surface-tint: linear-gradient(150deg, #20302a, #1c2a24);
    --press-active: inset 3px 3px 6px #16130e, inset -2px -2px 5px #2c4536;
    --float: 0 24px 44px -18px rgb(0 0 0 / 0.75);
    --float-sm: 0 14px 26px -12px rgb(0 0 0 / 0.6);
    --float-up: 0 -14px 30px -18px rgb(0 0 0 / 0.6);
    --float-brand: 0 14px 26px -12px rgb(78 157 110 / 0.45);
    --lift-brand: 0 12px 22px -9px rgb(78 157 110 / 0.55);
    --glass-bg: rgb(51 46 38 / 0.7);
    --glass-bar-bg: rgb(42 38 31 / 0.9);
    --glass-border: rgb(255 255 255 / 0.1);
    --peek-bg: #211e17;
    --peek-shadow: 5px 5px 12px #131009;
    --deck-shadow: 0 20px 36px -18px rgb(0 0 0 / 0.8);
    --seg-active-bg: #322d24;
    --seg-active-fg: #f2c14e;
    --scrim: rgb(0 0 0 / 0.58);
    --glass-well: rgb(33 30 23 / 0.6);
    --glass-well-shadow: inset 2px 2px 5px #131009, inset -1px -1px 2px #322d24;
    --glass-rim: inset 0 1px 0 rgb(255 255 255 / 0.1);
    --float-lg: 0 40px 80px -28px rgb(0 0 0 / 0.8);

    /* The same six hues re-stepped for the dark surface, validated as a set —
       not an automatic flip of the light values. */
    --mix-0: #55503f;
    --mix-1: #4a7fa3;
    --mix-2: #a3835c;
    --mix-3: #74a381;
    --mix-4: #967b93;
    --mix-5: #c2a37e;
    --mix-6: #758ea1;
    --mix-7: #94a175;
    --mix-8: #ba8175;

    --series-0: #4d4438;
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;

    color-scheme: dark;
  }
}

/* Explicit dark choice — wins in both directions */
:root[data-theme="dark"] {
  --bg: #232019;
  --bg-tint: #2a261e;
  --surface: #2a261f;
  --surface-2: #39342a;
  --surface-3: #211d16;
  --surface-bright: #2f2b23;
  --border: #3a342a;
  --border-strong: #4b4437;
  --text: #ece7dd;
  --text-muted: #a89a8e;
  --text-faint: #7d7168;

  --brand: #4e9d6e;
  --brand-hover: #63b381;
  --brand-soft: #23352b;
  --brand-ring: #4e9d6e3d;
  --on-brand: #10241a;

  --accent: #b08d63;
  --accent-soft: #322a20;

  --like: #e0657a;
  --like-soft: #3a2529;
  --success: #4e9d6e;
  --success-soft: #23352b;
  --danger: #e8695f;
  --danger-soft: #34201e;
  --info: #6ba9cf;
  --info-soft: #1b2a33;
  --warn: #d3a54e;
  --warn-soft: #33291a;
  --focus: #4e9d6e;

  /* Dark neumorphism reads through restraint: the dark half of each pair
     deepens toward true shadow while the highlight is a whisper of warm
     light, never white — white rims on dark stone read as wireframe. The
     depth-spec pair: drop #16130e, up-light #322d24 (often at low alpha). */
  --shade: #16130e;
  --shade-hi: #322d24;

  /* Depth & layers (D1) — keep identical to the other dark block. */
  --ground-0: #26231c;
  --ground-1: #201d16;
  --ground-2: #1a170f;
  --ground-mid: 55%;
  --ground-size: 120% 90%;
  --ground-x: 75%;
  --ground-y: 0%;
  --well: #211e17;
  --well-shadow: inset 2px 2px 5px #131009, inset -1px -1px 2px #322d24;
  --text-body: #e6ddcf;
  --brand-text: #6cc493;
  --accent-chip: #3a2f20;
  --raise-lift: 10px 10px 22px #131009, -6px -6px 16px #35302733;
  --raise-tint: 6px 6px 14px #131009, -6px -6px 14px #322d2433;
  --surface-tint: linear-gradient(150deg, #20302a, #1c2a24);
  --press-active: inset 3px 3px 6px #16130e, inset -2px -2px 5px #2c4536;
  --float: 0 24px 44px -18px rgb(0 0 0 / 0.75);
  --float-sm: 0 14px 26px -12px rgb(0 0 0 / 0.6);
  --float-up: 0 -14px 30px -18px rgb(0 0 0 / 0.6);
  --float-brand: 0 14px 26px -12px rgb(78 157 110 / 0.45);
  --lift-brand: 0 12px 22px -9px rgb(78 157 110 / 0.55);
  --glass-bg: rgb(51 46 38 / 0.7);
  --glass-bar-bg: rgb(42 38 31 / 0.9);
  --glass-border: rgb(255 255 255 / 0.1);
  --peek-bg: #211e17;
  --peek-shadow: 5px 5px 12px #131009;
  --deck-shadow: 0 20px 36px -18px rgb(0 0 0 / 0.8);
  --seg-active-bg: #322d24;
  --seg-active-fg: #f2c14e;
  --scrim: rgb(0 0 0 / 0.58);
  --glass-well: rgb(33 30 23 / 0.6);
  --glass-well-shadow: inset 2px 2px 5px #131009, inset -1px -1px 2px #322d24;
  --glass-rim: inset 0 1px 0 rgb(255 255 255 / 0.1);
  --float-lg: 0 40px 80px -28px rgb(0 0 0 / 0.8);

  /* The same six hues re-stepped for the dark surface, validated as a set —
     not an automatic flip of the light values. */
  --mix-0: #55503f;
  --mix-1: #4a7fa3;
  --mix-2: #a3835c;
  --mix-3: #74a381;
  --mix-4: #967b93;
  --mix-5: #c2a37e;
  --mix-6: #758ea1;
  --mix-7: #94a175;
  --mix-8: #ba8175;

  --series-0: #4d4438;
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;
  --series-8: #e66767;

  color-scheme: dark;
}

/* -------------------------------------------------------------------------
   2. Reset & base
   ------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* The ground's outer stop, so overscroll and toolbar-collapse gaps match. */
  background: var(--ground-2);
}

body {
  min-height: 100dvh;
  background: none;
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

/* Tier 1 ground: a fixed pseudo-layer rather than a fixed-attachment
   background — that property repaints the gradient every scroll frame in
   Chrome and is ignored by iOS Safari; a fixed layer is composited once. Never a flat
   fill: the grade is what lets the soft shadows read. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--ground);
  pointer-events: none;
}

@media print {
  body::before {
    display: none;
  }
}

/* Per-page focal point — geometry only, theme-neutral, so one rule serves
   both palettes. Pages opt in with {% block page %} in their template;
   anything not listed keeps the canonical ground. */
body[data-page="feed"] {
  --ground-x: 30%;
}

body[data-page="dogs"],
body[data-page="profile"] {
  --ground-size: 120% 90%;
  --ground-x: 80%;
  --ground-y: 0%;
}

body[data-page="parks"] {
  --ground-size: 120% 90%;
  --ground-x: 75%;
  --ground-y: 0%;
}

body[data-page="sniff"] {
  --ground-x: 25%;
}

img,
svg {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

a {
  /* Text-sized green: the brand hue itself is 4.2:1 on a card, short of AA. */
  color: var(--brand-text);
  text-decoration-color: color-mix(in srgb, var(--brand-text) 35%, transparent);
  text-underline-offset: 2px;
}

a:hover {
  color: var(--brand-hover);
  text-decoration-color: currentColor;
}

p {
  text-wrap: pretty;
}

h1,
h2,
h3,
h4 {
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem);
}
h2 {
  font-size: clamp(1.2rem, 1.05rem + 0.7vw, 1.5rem);
}
h3 {
  font-size: 1.0625rem;
  font-weight: 700;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection {
  background: var(--brand-soft);
  color: var(--text);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* -------------------------------------------------------------------------
   3. Utilities
   ------------------------------------------------------------------------- */

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: -100%;
  z-index: var(--z-skip);
  padding: var(--space-3) var(--space-5);
  background: var(--brand);
  color: var(--on-brand);
  font-weight: 600;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  text-decoration: none;
}

.skip-link:focus {
  inset-block-start: 0;
  color: var(--on-brand);
}

.muted {
  color: var(--text-muted);
}

.text-sm {
  font-size: 0.875rem;
}

.stack > * + * {
  margin-block-start: var(--space-4);
}

/* -------------------------------------------------------------------------
   4. App shell
   ------------------------------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell-max);
  margin-inline: auto;
  padding: var(--space-6) var(--space-4) var(--space-12);
  flex: 1;
}

/* Two-column layout that actually stacks on small screens. */
.shell--split {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

@media (width >= 60rem) {
  .shell--split {
    grid-template-columns: minmax(0, 1fr) var(--rail);
    gap: var(--space-8);
  }
}

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

@media (width >= 60rem) {
  .rail--sticky {
    position: sticky;
    top: calc(var(--space-4) + var(--header-h));
  }
}

.page-head {
  margin-block-end: var(--space-6);
}

.page-head__sub {
  color: var(--text-muted);
  font-size: 0.9375rem;
  margin-block-start: var(--space-1);
}

/* -------------------------------------------------------------------------
   5. Header
   ------------------------------------------------------------------------- */

.header {
  position: sticky;
  top: 0;
  z-index: var(--z-chrome);
  /* Tier 3 chrome: the bar is glass over whatever scrolls beneath it. */
  background: var(--glass-bar-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-end: 1px solid var(--border);
}

.header__inner {
  max-width: var(--shell-max);
  margin-inline: auto;
  padding: var(--space-2) var(--space-4);
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  /* space-2, not space-3: at 60rem the full chrome (paw + wordmark, four 58px
     pills, the search well at its 96px minimum, the weather chip capped at
     9rem, two 44px buttons, the 78px theme well, the 44px avatar) fits with a
     little to spare. The row never wraps: the search well is the one
     shrinkable member, so the bar stays 60px tall at every width. */
  gap: var(--space-2);
  flex-wrap: nowrap;
}

/* Signed out there are no nav tiles, so nothing separates the brand from
   the action cluster; push the first right-side control (the theme toggle,
   which directly follows the brand only when signed out) to the far edge. */
.brand + [data-theme-toggle] {
  margin-inline-start: auto;
}

/* Below 48rem the search hides — and it was the search's own
   `margin-inline-start: auto` doing all the pushing, which a display:none
   element does not contribute. So the whole action cluster packed against
   the paw and the right half of the bar sat empty. Push from the brand
   instead: it is always the first child, so this holds at every width the
   search is absent, signed in or out. */
@media (width < 48rem) {
  .brand {
    margin-inline-end: auto;
  }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  color: var(--text);
  text-decoration: none;
}

.brand:hover {
  color: var(--text);
}

.brand__mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  /* Rounded square, not a circle — the spec's 32px mark. Raised with the
     green-cast shade the primary button uses. */
  border-radius: 0.625rem;
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: 5px 5px 10px color-mix(in srgb, var(--brand) 22%, var(--shade)),
    -4px -4px 8px var(--shade-hi);
  flex: none;
}

.brand__mark svg {
  width: 1.125rem;
  height: 1.125rem;
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.9375rem;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.nav__link:hover {
  background: var(--surface-2);
  color: var(--text);
}

.nav__link[aria-current="page"] {
  background: var(--brand-soft);
  color: var(--brand);
}

.nav__link svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* Header search. Hidden on phones, where the header's search icon links to
   the search page instead. */
.header-search {
  display: none;
  align-items: center;
  gap: var(--space-1);
  padding-inline-start: var(--space-2);
  background: var(--well);
  border: 0;
  border-radius: var(--radius-md);
  /* A shallow well, like every input now: search is a place to put something. */
  box-shadow: var(--well-shadow);
  transition: box-shadow var(--dur-fast) var(--ease);
  margin-inline-start: auto;
}

@media (width >= 48rem) {
  .header-search {
    display: flex;
  }
}

.header-search:focus-within {
  box-shadow: var(--press-sm), 0 0 0 3px var(--brand-ring);
}

.header-search {
  /* Grows to the spec's 150px, shrinks first when the row is under pressure. */
  flex: 1 1 6rem;
  max-width: 12.5rem;
  min-width: 0;
}

.header-search .form-control {
  width: 100%;
  min-width: 0;
  padding: var(--space-2) var(--space-2);
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  font-size: 0.875rem;
}

.header-search .form-control:focus {
  outline: none;
  box-shadow: none;
}

/* ---- Search page: the head and sections live in the D14 section at the
   end of the file, shared with Sniff around. ---- */

/* Menus built on <details> — keyboard-accessible with no JavaScript. */
.menu {
  position: relative;
}

.menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1);
  border-radius: var(--radius-full);
  border: 0;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  font-weight: 600;
  font-size: 0.9375rem;
  transition: box-shadow var(--dur-fast) var(--ease);
}

.menu > summary::-webkit-details-marker {
  display: none;
}

.menu > summary:hover {
  box-shadow: var(--raise);
}

.menu[open] > summary {
  box-shadow: var(--press-sm);
}

.menu__panel {
  position: absolute;
  inset-inline-end: 0;
  margin-block-start: var(--space-2);
  min-width: 13rem;
  padding: var(--space-2);
  /* Opaque on purpose, no backdrop-filter: the panel hangs out of the glass
     header, and a blur nested inside a blur only sees its parent's backdrop,
     not the page beneath — it would look unblurred. Long drop + glass rim. */
  background: var(--surface-bright);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  box-shadow: var(--float);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: var(--z-menu);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
}

.menu__item:hover {
  background: var(--surface-2);
  color: var(--text);
}

.menu__item svg {
  width: 1.125rem;
  height: 1.125rem;
  color: var(--text-muted);
  flex: none;
}

.menu__sep {
  height: 1px;
  background: var(--border);
  margin: var(--space-2) var(--space-1);
  border: 0;
}

/* The mobile nav disclosure that used to live here was replaced by the fixed
   bottom tab bar (.tabbar below) — the artboard's phone frames navigate by
   tabs, not a hamburger. */

/* -------------------------------------------------------------------------
   6. Buttons
   ------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: 0;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.9375rem;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--raise-sm);
  transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}

/* Raised at rest, pressed while active — the shadow swap IS the affordance,
   which is why .btn never translates: motion would fight the illusion. */
.btn:active {
  box-shadow: var(--press-sm);
}

.btn svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.btn--primary {
  background: var(--brand);
  color: var(--on-brand);
  /* The dark half of the pair picks up the button's own green — a grey shade
     under a saturated surface reads as dirt, not depth. */
  box-shadow: 5px 5px 10px color-mix(in srgb, var(--brand) 22%, var(--shade)),
    -4px -4px 8px var(--shade-hi);
}

.btn--primary:hover {
  background: var(--brand-hover);
  color: var(--on-brand);
}

.btn--primary:active {
  box-shadow: inset 4px 4px 8px color-mix(in srgb, var(--brand-hover) 45%, #000 8%),
    inset -3px -3px 7px color-mix(in srgb, var(--brand) 55%, #fff 12%);
}

.btn--ghost {
  background: var(--surface);
  color: var(--text-muted);
}

.btn--ghost:hover {
  background: var(--surface-bright);
  color: var(--text);
}

.btn--info {
  background: var(--info);
  color: #ffffff;
  box-shadow: var(--raise-info-sm);
}

.btn--info:hover {
  color: #ffffff;
  background: color-mix(in srgb, var(--info) 85%, #000000);
}

.btn--danger {
  background: var(--surface);
  color: var(--danger);
}

.btn--danger:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.btn--sm {
  padding: var(--space-2) var(--space-4);
  font-size: 0.875rem;
}

.btn--block {
  width: 100%;
}

/* Icon-only control. Accessible name lives on the button, not the SVG. */
.btn-icon {
  display: inline-grid;
  place-items: center;
  /* 44px: the artboard's touch-target floor, kept everywhere. */
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}

.btn-icon svg {
  width: 1.25rem;
  height: 1.25rem;
}

.btn-icon:hover {
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text);
}

.btn-icon--bordered {
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.btn-icon--bordered:active {
  box-shadow: var(--press-sm);
}

.btn-icon[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* -------------------------------------------------------------------------
   7. Surfaces
   ------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--raise);
}

.card__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-block-end: 1px solid var(--border);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9375rem;
}

.card__head svg {
  width: 1.125rem;
  height: 1.125rem;
  color: var(--text-muted);
}

.card__body {
  padding: var(--space-5);
}

.card__foot {
  padding: var(--space-3) var(--space-5);
  border-block-start: 1px solid var(--border);
}

/* -------------------------------------------------------------------------
   8. Avatars
   ------------------------------------------------------------------------- */

.avatar {
  --size: 2.75rem;
  width: var(--size);
  height: var(--size);
  border-radius: var(--radius-full);
  object-fit: cover; /* non-square uploads no longer distort */
  background: var(--surface-2);
  border: 2px solid var(--surface-bright);
  box-shadow: var(--raise-sm);
  flex: none;
}

.avatar--sm {
  --size: 2rem;
}
.avatar--lg {
  --size: 4rem;
}
.avatar--xl {
  --size: 7.5rem;
}

/* -------------------------------------------------------------------------
   9. Post card
   ------------------------------------------------------------------------- */

.feed {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Once HTMX prepends a real post, the empty state must get out of the way. */
.feed:has(.post) .empty-state {
  display: none;
}

.post {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--raise);
  transition: box-shadow var(--dur) var(--ease), translate var(--dur) var(--ease);
}

/* The spec's slight raise on hover — grounded cards only. */
.post:hover {
  box-shadow: var(--raise-lg);
  translate: 0 -1px;
}

/* On its own page the woof is the one lifted-and-lit surface. */
.post--solo,
.post--solo:hover {
  background: var(--surface-bright);
  border-radius: 1.375rem;
  box-shadow: var(--raise-lift);
  translate: none;
}

.post__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: 0.875rem;
  margin-block-end: var(--space-2);
}

.post__author {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--text);
  text-decoration: none;
}

.post__author:hover {
  color: var(--brand);
  text-decoration: underline;
}

.post__time {
  color: var(--text-faint);
}

.post__dot {
  color: var(--text-faint);
}

/* Full body — no truncation, anywhere. */
.post__body {
  font-size: 1.0625rem;
  line-height: 1.55;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* Photo attached to a post. Capped so a tall portrait can't run the card
   off the page; object-fit keeps the crop centred. */
.post__photo {
  display: block;
  margin-block-start: var(--space-3);
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 4px solid var(--surface-bright);
  box-shadow: var(--raise-sm);
  background: var(--surface-bright);
}

.post__photo img {
  width: 100%;
  max-height: 26rem;
  object-fit: cover;
}

.post__actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-block-start: var(--space-3);
  margin-inline-start: calc(var(--space-2) * -1);
}

/* ---- Replies -------------------------------------------------------- */

.thread {
  margin-block-start: var(--space-2);
  border-block-start: 1px solid var(--border);
  padding-block-start: var(--space-2);
}

.thread__toggle {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  margin-inline-start: calc(var(--space-3) * -1);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.thread__toggle::-webkit-details-marker {
  display: none;
}

.thread__toggle:hover {
  background: var(--surface-2);
  color: var(--text);
}

.thread__toggle svg {
  width: 1.125rem;
  height: 1.125rem;
}

.thread__body {
  padding-block-start: var(--space-3);
}

.comments {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.comment {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
}

.comment__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.comment__author {
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
}

.comment__author:hover {
  color: var(--brand);
}

.comment__body {
  font-size: 0.9375rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.comment-form__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block-start: var(--space-3);
}

/* ---- Breed mix ------------------------------------------------------
   A stacked bar for one dog's composition, plus a labelled legend. The
   2px gaps are the secondary encoding that keeps adjacent segments apart
   for colour-vision-deficient readers; the legend is what makes the
   lower-contrast hues legible at all. */

.breedmix {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: 100%;
}

/* The breedmeter crop: full capsule segments with visible gaps, resting in
   a shallow F2-style well. */
.breedbar {
  display: flex;
  gap: 4px;
  height: 12px;
  width: 100%;
  padding: 3px;
  border-radius: var(--radius-full);
  background: var(--well);
  box-shadow: var(--well-shadow);
  box-sizing: content-box;
}

.breedbar__seg {
  flex: var(--pct) 1 0;
  /* Wide enough that even a small breed stays a legible capsule. */
  min-width: 12px;
  border-radius: var(--radius-full);
  background: var(--mix-0);
}

/* Rounded data-ends, anchored to the ends of the bar. */
.breedbar__seg:first-child {
  border-start-start-radius: 4px;
  border-end-start-radius: 4px;
}

.breedbar__seg:last-child {
  border-start-end-radius: 4px;
  border-end-end-radius: 4px;
}

.breedbar__seg[data-slot="1"] { background: var(--mix-1); }
.breedbar__seg[data-slot="2"] { background: var(--mix-2); }
.breedbar__seg[data-slot="3"] { background: var(--mix-3); }
.breedbar__seg[data-slot="4"] { background: var(--mix-4); }
.breedbar__seg[data-slot="5"] { background: var(--mix-5); }
.breedbar__seg[data-slot="6"] { background: var(--mix-6); }
.breedbar__seg[data-slot="7"] { background: var(--mix-7); }
.breedbar__seg[data-slot="8"] { background: var(--mix-8); }

.breedlegend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.breedlegend__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 0.875rem;
}

.breedlegend__swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: none;
  background: var(--series-0);
}

.breedlegend__swatch[data-slot="1"] { background: var(--mix-1); }
.breedlegend__swatch[data-slot="2"] { background: var(--mix-2); }
.breedlegend__swatch[data-slot="3"] { background: var(--mix-3); }
.breedlegend__swatch[data-slot="4"] { background: var(--mix-4); }
.breedlegend__swatch[data-slot="5"] { background: var(--mix-5); }
.breedlegend__swatch[data-slot="6"] { background: var(--mix-6); }
.breedlegend__swatch[data-slot="7"] { background: var(--mix-7); }
.breedlegend__swatch[data-slot="8"] { background: var(--mix-8); }

/* Text wears text tokens, never the series colour. */
.breedlegend__name {
  flex: 1;
  min-width: 0;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.breedlegend__pct {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.breedmix--compact .breedbar {
  height: 8px;
}

/* ---- Breed formset rows --------------------------------------------- */

.breed-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.breed-row {
  display: grid;
  grid-template-columns: 4.75rem minmax(0, 1fr) 5.75rem auto;
  gap: var(--space-2);
  align-items: center;
}

.breed-row__cell {
  min-width: 0;
}

/* The header row above the inputs — the visible "both parts, % required"
   contract. aria-hidden in the markup: the widgets carry their own labels. */
.breed-row--head {
  margin-block-end: calc(var(--space-2) * -1);
}

.breed-row__collabel {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-muted);
}

/* Errors are full-width rows of the grid, never squeezed into the 4.5rem
   percentage cell where "This field is required." wrapped into an unreadable
   sliver on phones. */
.breed-row__error {
  grid-column: 1 / -1;
  margin: 0;
}

/* Percentages read as a column of numbers, so they align right. */
.breed-row input[type="number"] {
  text-align: right;
}

@media (width < 48rem) {
  .breed-row {
    grid-template-columns: minmax(0, 1fr) 4.5rem auto;
  }
  .breed-row__rank {
    display: none;
  }
}

.breed-row .field,
.breed-row .field__control {
  margin: 0;
}

/* The first row is the largest share once saved, so it is labelled as the
   primary breed rather than left as an unmarked row. */
.breed-row__rank {
  display: inline-flex;
  justify-content: center;
  padding: var(--space-1) 0;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.6875rem;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.breed-row__rank--primary {
  background: var(--brand-soft);
  color: var(--brand-hover);
  box-shadow: none;
}

.breed-row__delete {
  color: var(--text-faint);
}

.breed-row__delete:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.breed-row__icon-undo {
  display: none;
}

/* Staged for removal — visible, struck through, reversible until save. */
.breed-row.is-removed .field__label,
.breed-row.is-removed .breed-row__rank {
  text-decoration: line-through;
}

.breed-row.is-removed .form-control {
  opacity: 0.5;
  text-decoration: line-through;
  border-style: dashed;
}

.breed-row.is-removed .breed-row__icon-remove {
  display: none;
}

.breed-row.is-removed .breed-row__icon-undo {
  display: block;
}

.breed-row.is-removed .breed-row__delete {
  color: var(--brand);
}

.breed-total {
  font-size: 0.8125rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- Conversation threads ------------------------------------------- */

.thread-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.thread-card {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-inline-end: var(--space-3);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--raise-sm);
  transition: box-shadow var(--dur) var(--ease);
}

.thread-card:hover {
  box-shadow: var(--raise);
}

.thread-card__link {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-4);
  text-decoration: none;
  color: inherit;
}

.thread-card__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thread-card__link:hover .thread-card__title {
  color: var(--brand);
}

.thread-card__meta {
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.thread-card__delete {
  color: var(--text-faint);
}

.thread-card__delete:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

/* ---- Transcript ------------------------------------------------------ */

.transcript {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.turn {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.turn__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  margin-block-end: var(--space-2);
}

.turn__label svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--brand);
}

/* The question sits inset and tinted; the answer sits on the page. This
   keeps a long thread readable without alternating full-width blocks. */
.turn__q {
  padding: var(--space-4) var(--space-5);
  background: var(--brand-soft);
  border-radius: var(--radius-md);
}

.turn__q p {
  font-size: 1rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.turn__a {
  padding-inline-start: var(--space-5);
  border-inline-start: 2px solid var(--border);
}

.turn__answer {
  font-size: 0.9375rem;
  line-height: 1.65;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.turn__time {
  display: block;
  margin-block-start: var(--space-3);
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.turn.htmx-added {
  animation: pop-in 220ms var(--ease);
}

/* ---- Ask panel ------------------------------------------------------
   The question and its answer share one card. The answer region is part of
   the same surface, separated by a rule rather than floated as its own card. */

.ask__suggestions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
}

.ask__suggestions-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

/* The old bordered .chip that lived here was superseded by the neumorphic
   .chip in the phase-A component block further down. */

.ask__answer {
  border-block-start: 1px solid var(--border);
  padding: var(--space-5);
  background: var(--surface-2);
  border-end-start-radius: var(--radius-lg);
  border-end-end-radius: var(--radius-lg);
}

/* Inside the card the answer no longer needs its own inset box. */
.ask__answer .ai-response {
  background: transparent;
  border: 0;
  padding: 0;
  max-height: none;
}

/* Like button — the HTMX swap target. */
.like {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.like:active {
  box-shadow: var(--press-sm);
}

.like svg {
  width: 1.125rem;
  height: 1.125rem;
}

.like:hover {
  background: var(--like-soft);
  color: var(--like);
}

.like.is-liked {
  color: var(--like);
}

.like[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* Persistent live-region wrappers around the swapped like/follow buttons. */
.like-wrap,
.follow-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.action-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.action-link svg {
  width: 1.125rem;
  height: 1.125rem;
}

.action-link:hover {
  background: var(--info-soft);
  color: var(--info);
}

/* -------------------------------------------------------------------------
   10. Profile
   ------------------------------------------------------------------------- */

.profile-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-5);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--raise);
}

.profile-hero__name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.25rem;
}

.profile-hero__handle {
  color: var(--text-muted);
  font-size: 0.9375rem;
  margin-block-start: calc(var(--space-1) * -1);
}

.profile-hero__bio {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}

.social-links {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  justify-content: center;
}

/* Facts separated by dots without the template counting which are present. */
.fact-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 var(--space-2);
  font-size: 0.875rem;
  color: var(--text-muted);
}

.fact-row span + span::before {
  content: "·";
  margin-inline-end: var(--space-2);
  color: var(--text-faint);
}

/* The age and its human equivalent are one unit: tight internally, normal
   spacing to whatever sits above and below. */
.fact-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.age-human {
  font-size: 0.75rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  cursor: help;
}

.stat-row {
  display: flex;
  gap: var(--space-5);
  justify-content: center;
  font-size: 0.875rem;
}

.stat__value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.125rem;
  display: block;
}

.stat__label {
  color: var(--text-muted);
}

/* Compact person rows in the Follows / Followed by cards */
.person-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.person {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-md);
}

.person:hover {
  background: var(--surface-2);
}

.person__name {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--text);
  text-decoration: none;
  margin-inline-end: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.person__name:hover {
  color: var(--brand);
}

/* Profile grid (profile_list, followers) */
.profile-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

.profile-card {
  display: flex;
  flex-direction: column;
  /* Centred, per the artboard (align-items:center; text-align:center) —
     first pass guessed left-aligned and was corrected on review. */
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-5);
  /* grounded (D14): the page's one lit surface is the search well */
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--raise);
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

/* The artboard's Sniff avatar: 84px, radius 28. */
.profile-card .avatar {
  --size: 5.25rem;
  border-radius: var(--radius-xl);
}

.profile-card__name {
  font-size: 1.0625rem;
}

.profile-card__bio {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: 0;
}

/* The follow control anchors the card, full width. */
.profile-card .follow-wrap {
  width: 100%;
  margin-block-start: auto;
}

.profile-card .follow-wrap > button,
.profile-card .follow-wrap .btn {
  width: 100%;
  justify-content: center;
}

.profile-card:hover {
  box-shadow: var(--raise-lg);
  transform: translateY(-2px);
}

.profile-card__name {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
}

.profile-card__name:hover {
  color: var(--brand);
}

.profile-card__handle {
  color: var(--text-muted);
  font-size: 0.875rem;
  margin-block-start: calc(var(--space-2) * -1);
}

/* .profile-card__meta retired — the artboard's card carries no updated-date. */

/* -------------------------------------------------------------------------
   11. Forms
   ------------------------------------------------------------------------- */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field + .field {
  margin-block-start: var(--space-4);
}

.field__label {
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--text);
}

.field__hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.field__hint ul {
  margin: 0;
  padding-inline-start: var(--space-5);
}

.field__error {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--danger);
}

/* `.form-control` is kept as the class name so forms.py widget attrs
   need no changes — it is simply restyled here. */
.form-control,
.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="url"],
.field textarea {
  width: 100%;
  padding: 0.875rem 1rem;
  background: var(--well);
  color: var(--text);
  /* F2 "shallow well": half the old inset depth, plus a hairline lip on the
     lit edges so the rim stays legible at a glance. */
  border: 0;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
  border-radius: calc(var(--radius-md) + 2px);
  font-size: 0.9375rem;
  box-shadow: var(--well-shadow);
  transition: box-shadow var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.form-control::placeholder {
  color: var(--text-faint);
}

.form-control:focus,
.field textarea:focus {
  /* F2 focus: the well brightens to paper — "this is where the ink goes" —
     with a green-cast inset and a 2px ring. */
  outline: none;
  background: var(--surface-bright);
  border-top-color: color-mix(in srgb, var(--brand) 30%, var(--border));
  border-left-color: color-mix(in srgb, var(--brand) 30%, var(--border));
  box-shadow: inset 2px 2px 5px var(--shade-brand), 0 0 0 2px var(--brand-ring);
}

.field textarea,
textarea.form-control {
  min-height: 6rem;
  resize: vertical;
  line-height: 1.55;
}

/* Widget wrapper. Flow inline and wrap, so a ClearableFileInput's
   "Currently / Clear / Change" cluster reads as one line of small print
   instead of four stacked rows. */
.field__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* Django emits a <br> in the clearable widget; flex-wrap already handles it. */
.field__control br {
  display: none;
}

.field__control input[type="checkbox"] {
  width: auto;
  accent-color: var(--brand);
  margin: 0;
}

/* Text inputs and textareas still take a full row inside the wrapper.
   The clearable widget's checkbox is excluded so it stays inline with
   its "Clear" label rather than claiming a row of its own. */
.field__control > .form-control,
.field__control > input:not([type="checkbox"]),
.field__control > textarea {
  flex: 1 1 100%;
  min-width: 0;
}

/* ---- File inputs ------------------------------------------------------
   The native control is a button plus a filename label. Drop the boxed
   treatment and style the button half to match .btn--ghost.btn--sm, so
   "Choose File" reads like every other button on the site. The filename
   text beside it is browser chrome — `color` is all we can reach. */
.field input[type="file"] {
  flex: 1 1 100%;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--text-muted);
  font-size: 0.8125rem;
  cursor: pointer;
}

.field input[type="file"]::file-selector-button {
  margin-inline-end: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.field input[type="file"]::file-selector-button:hover {
  background: var(--surface-bright);
}

.field input[type="file"]:focus-visible::file-selector-button {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.field--invalid .form-control {
  box-shadow: inset 2px 2px 5px color-mix(in srgb, var(--danger) 18%, var(--shade)),
    0 0 0 2px color-mix(in srgb, var(--danger) 30%, transparent);
}

.form-actions {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
  margin-block-start: var(--space-6);
}

/* Long forms: the save row is the form's LAST child and sticks to the bottom
   edge while the form extends past it, settling into flow at the form's end.
   Sticky, not fixed — no clearance hacks, and immune to the backdrop-filter
   containing-block trap (see .tabbar). z-index 10: above the fields it slides
   over, below the combobox/picker overlays (30) — an open suggestion list
   paints over the bar, never under it. The translucent surface is
   unconditional so scrolled content is never legible through it even without
   JS; the elevation arrives only while detached (is-stuck, a woofline.js
   sentinel), matching raised-equals-floating. NOTE: backdrop-filter makes
   this a containing block — never nest a position:fixed element inside. */
.form-actions--sticky {
  position: sticky;
  bottom: var(--tabbar-clearance, 0px);
  z-index: var(--z-sticky);
  padding: var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--glass-bar-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  transition: box-shadow var(--dur-fast) var(--ease);
}

.form-actions--sticky.is-stuck {
  box-shadow: var(--float-up);
}

/* A field focused by validation or keyboard must scroll clear of the stuck
   bar and the tabbar. */
html:has(.form-actions--sticky) {
  scroll-padding-block-end: calc(var(--tabbar-clearance, 0px) + 5.5rem);
}

@media (prefers-reduced-motion: reduce) {
  .form-actions--sticky {
    transition: none;
  }
}

.auth-shell {
  width: 100%;
  max-width: 26rem;
  margin-inline: auto;
  padding-block: var(--space-8);
}

.auth-shell__head {
  text-align: center;
  margin-block-end: var(--space-6);
}

.auth-shell__head p {
  color: var(--text-muted);
  margin-block-start: var(--space-2);
}

.auth-alt {
  text-align: center;
  margin-block-start: var(--space-5);
  font-size: 0.9375rem;
  color: var(--text-muted);
}

/* Composer */
.composer__count {
  font-size: 0.8125rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   12. Messages & banners
   ------------------------------------------------------------------------- */

.messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: var(--shell-max);
  margin: var(--space-4) auto 0;
  padding-inline: var(--space-4);
}

.banner[hidden] {
  display: none;
}

.banner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--raise-sm);
  background: var(--surface-2);
  font-size: 0.9375rem;
}

.banner__text {
  flex: 1;
  min-width: 0;
}

.banner--success {
  background: var(--success-soft);
  color: var(--text);
  box-shadow: var(--raise-brand-sm);
}

.banner--error {
  background: var(--danger-soft);
  box-shadow: 4px 4px 8px color-mix(in srgb, var(--danger) 16%, var(--shade)), -4px -4px 8px var(--shade-hi);
}

.banner--info {
  background: var(--info-soft);
  box-shadow: var(--raise-info-sm);
}

.banner--warning {
  background: var(--warn-soft);
  box-shadow: var(--raise-warn-sm);
}

/* The verify banner's trailing control (Resend form / Settings link) —
   keeps the button and its inline error on one flex row of the banner. */
.banner__act {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
}

/* Inline error revealed by hx-on::response-error */
.inline-error {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--danger);
  padding-inline: var(--space-2);
}

.inline-error[hidden] {
  display: none;
}

/* -------------------------------------------------------------------------
   13. Empty states
   ------------------------------------------------------------------------- */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-6);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
}

.empty-state__icon {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-full);
  background: var(--brand-soft);
  color: var(--brand);
}

.empty-state__icon svg {
  width: 1.75rem;
  height: 1.75rem;
}

.empty-state__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
}

.empty-state__text {
  color: var(--text-muted);
  font-size: 0.9375rem;
  max-width: 32ch;
}

/* -------------------------------------------------------------------------
   14. Pagination
   ------------------------------------------------------------------------- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-block: var(--space-6);
}

.pagination__status {
  font-size: 0.875rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   15. AI page
   ------------------------------------------------------------------------- */

.ai-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: minmax(0, 1fr);
}

@media (width >= 60rem) {
  .ai-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.ai-response {
  min-height: 12rem;
  max-height: 26rem;
  overflow-y: auto;
  padding: var(--space-4);
  background: var(--well);
  border: 0;
  box-shadow: var(--press-sm);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ai-card {
  padding: var(--space-5);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--raise);
}

.ai-card__prompt {
  display: flex;
  gap: var(--space-3);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  overflow-wrap: anywhere;
}

.ai-card__prompt svg {
  width: 1.125rem;
  height: 1.125rem;
  color: var(--brand);
  flex: none;
  margin-block-start: 0.25rem;
}

.ai-card__result {
  margin-block-start: var(--space-3);
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--border);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ai-card__result--clamped {
  display: -webkit-box;
  -webkit-line-clamp: 6;
  line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ai-card__foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block-start: var(--space-3);
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.ai-card__foot .action-link {
  margin-inline-start: auto;
}

/* Skeleton for the Gemini call — communicates shape, not just "waiting".
   It lives outside the swap target so it survives the swap; hx-indicator
   puts .htmx-request on the shell. */
.skeleton {
  display: none;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
}

.ai-response-shell.htmx-request .skeleton {
  display: flex;
}

.ai-response-shell.htmx-request #ai-response {
  display: none;
}

.skeleton__line {
  height: 0.75rem;
  border-radius: var(--radius-full);
  background: linear-gradient(
    90deg,
    var(--surface-2) 0%,
    var(--surface-3) 50%,
    var(--surface-2) 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.4s var(--ease) infinite;
}

.skeleton__line:nth-child(2) {
  width: 92%;
}
.skeleton__line:nth-child(3) {
  width: 78%;
}
.skeleton__line:nth-child(4) {
  width: 85%;
}
.skeleton__line:nth-child(5) {
  width: 60%;
}

@keyframes shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}

/* -------------------------------------------------------------------------
   16. HTMX state
   The htmx-indicator class does nothing without this CSS.
   ------------------------------------------------------------------------- */

.htmx-indicator {
  opacity: 0;
  transition: opacity 150ms var(--ease);
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* Exit faster than entrance. */
.htmx-swapping {
  opacity: 0;
  transition: opacity 100ms var(--ease);
}

.htmx-settling {
  opacity: 0;
}

.post.htmx-added {
  animation: pop-in 220ms var(--ease);
}

@keyframes pop-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

[aria-busy="true"] {
  cursor: progress;
}

/* -------------------------------------------------------------------------
   17. Activity tracking
   ------------------------------------------------------------------------- */

.activity-panel .card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* ---- Weekly summary ---- */

.actstats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

.actstat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.actstat__value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.actstat__of {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-faint);
}

.actstat__label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* ---- Target ---- */

.actgoal {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.actgoal__bar {
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--surface-3);
  overflow: hidden;
}

.actgoal__fill {
  width: calc(var(--pct) * 1%);
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--series-2);
  transition: width var(--dur) var(--ease);
}

.actgoal__fill.is-met {
  background: var(--success);
}

.actgoal__text {
  font-size: 0.8125rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- 14-day chart ----
   Single series, so no legend — the caption names it. The colour is the
   validated categorical slot 2 rather than the brand token: the brand's dark
   step sits at L 0.75, outside the dark band, and glares as a large fill. */

.actchart {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.actchart__plot {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 5rem;
}

.actchart__col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  min-width: 0;
}

.actchart__track {
  flex: 1;
  display: flex;
  align-items: flex-end;
  background: var(--surface-2);
  border-radius: 4px;
}

.actchart__bar {
  width: 100%;
  height: calc(var(--h) * 1%);
  min-height: 2px;
  background: var(--series-2);
  border-radius: 4px 4px 0 0;
  transition: height var(--dur) var(--ease);
}

/* A day with nothing logged keeps its track but has no mark — the gap is the
   point, so it must be visible rather than absent. */
.actchart__bar.is-empty {
  min-height: 0;
  background: none;
}

.actchart__tick {
  font-size: 0.625rem;
  color: var(--text-faint);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.actchart__caption {
  font-size: 0.75rem;
  color: var(--text-faint);
}

/* ---- Add form ---- */

.actform__grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
}

.actform__grid .field + .field {
  margin-block-start: 0;
}

.actform__more {
  margin-block-start: var(--space-3);
}

.actform__more > summary {
  cursor: pointer;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-muted);
  padding-block: var(--space-2);
}

.actform__more > summary:hover {
  color: var(--text);
}

/* ---- History list ---- */

.actlist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.actrow {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--raise-sm);
}

.actrow__kind {
  flex: none;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  background: var(--brand-soft);
  color: var(--brand);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
}

.actrow__body {
  flex: 1;
  min-width: 0;
}

.actrow__main {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: baseline;
  font-size: 0.9375rem;
}

.actrow__effort,
.actrow__dist {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.actrow__notes {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin-block-start: var(--space-1);
  overflow-wrap: anywhere;
}

.actrow__meta {
  font-size: 0.75rem;
  color: var(--text-faint);
  margin-block-start: var(--space-1);
}

.actrow__delete {
  color: var(--text-faint);
  flex: none;
}

.actrow__delete:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.actrow.htmx-added {
  animation: pop-in 220ms var(--ease);
}

/* -------------------------------------------------------------------------
   18. People on a dog
   ------------------------------------------------------------------------- */

.role-chip {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.6875rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
  background: var(--surface-2);
  color: var(--text-muted);
}

.role-chip--owner {
  background: var(--brand-soft);
  color: var(--brand);
}

.role-chip--co_owner {
  background: var(--success-soft);
  color: var(--success);
}

/* A dog walker reads as a caretaker, so it takes the sitter's treatment rather
   than a fourth hue — the distinction is the label, not the colour. */
.role-chip--sitter,
.role-chip--dog_walker {
  background: var(--info-soft);
  color: var(--info);
}

.role-chip--ai {
  background: var(--brand-soft);
  color: var(--brand);
}

.role-chip--pending,
.role-chip--private {
  background: var(--surface-3);
  color: var(--text-muted);
}

/* The switch plus its reason, for an owner whose plan cannot delegate yet. */
.member__locked {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.member__locked-note {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.6875rem;
  color: var(--text-muted);
}

.badge {
  display: inline-grid;
  place-items: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding-inline: 0.35rem;
  margin-inline-start: auto;
  border-radius: var(--radius-full);
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

.member-list,
.invite-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.member {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--raise-sm);
  background: var(--surface);
}

.member__body {
  flex: 1;
  min-width: 0;
}

.member__name {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--text);
  text-decoration: none;
}

.member__name:hover {
  color: var(--brand);
}

.member__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-start: var(--space-1);
}

.member__remove {
  color: var(--text-faint);
  flex: none;
}

.member__remove:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.invite-form {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: minmax(0, 1fr) minmax(0, 11rem);
  /* Both columns sit on the same baseline rather than stretching to match
     the taller one. */
  align-items: start;
}

/* Inside a grid the two fields are adjacent siblings, so the generic
   .field + .field top margin pushes the second column down out of line with
   the first. */
.invite-form .field + .field {
  margin-block-start: 0;
}

@media (width < 48rem) {
  .invite-form {
    grid-template-columns: minmax(0, 1fr);
  }
  .invite-form .field + .field {
    margin-block-start: var(--space-3);
  }
}

/* ---- Invitations awaiting an answer ---- */

.invite-section {
  margin-block-end: var(--space-8);
}

.invite-section h2 {
  font-size: 1.0625rem;
  padding-block-end: var(--space-2);
  margin-block-end: var(--space-4);
  border-block-end: 1px solid var(--border);
}

.invite {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-4);
  background: var(--brand-soft);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
}

.invite__body {
  flex: 1;
  min-width: 12rem;
}

.invite__text {
  font-size: 0.9375rem;
}

.invite__note {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin-block-start: var(--space-1);
}

.invite__actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* ---- Thread privacy ---- */

.privacy-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-start: var(--space-3);
}

.privacy-toggle__note {
  font-size: 0.8125rem;
  color: var(--text-faint);
}

/* -------------------------------------------------------------------------
   19. Notifications
   ------------------------------------------------------------------------- */

.notif-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.notif {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface);
  border: 0;
  border-radius: 1.125rem;
  /* Read = a grounded row on the ambient ground. */
  box-shadow: var(--raise);
}

/* Unread = lifted onto the tinted surface — the spec's still-in-your-way
   treatment. Still marked by the dot AND the text weight below, never
   elevation alone. */
.notif.is-unread {
  background: var(--surface-tint);
  box-shadow: var(--raise-tint);
}

.notif__icon {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  color: var(--text-muted);
}

.notif__icon svg {
  width: 1rem;
  height: 1rem;
}

.notif__body {
  flex: 1;
  min-width: 0;
}

/* The row is a submit button so that following it marks it read on the way
   through; it must not look like one. */
.notif__link {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: start;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.notif__text {
  font-size: 0.9375rem;
  line-height: 1.45;
}

.notif.is-unread .notif__text {
  font-weight: 600;
}

.notif__quote {
  font-size: 0.8125rem;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notif__time {
  font-size: 0.75rem;
  color: var(--text-faint);
}

.notif__dot {
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--brand);
  flex: none;
}

/* ---- Mentions ---- */

.mention {
  font-weight: 600;
  text-decoration: none;
}

.mention:hover {
  text-decoration: underline;
}

/* ---- Typeahead ----
   The menu overlays the content below rather than pushing it down, so the
   form doesn't jump around while you type. */

.picker {
  position: relative;
}

.picker__pending {
  position: absolute;
  inset-inline-end: var(--space-3);
  inset-block-start: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  font-size: 1.25rem;
  line-height: 1;
  pointer-events: none;
}

.picker__menu {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(100% + var(--space-1));
  z-index: var(--z-popover);
}

/* Not :empty — an HTMX swap leaves whitespace text nodes behind, and :empty
   only matches an element with no children AND no text at all. Keying off the
   absence of element children is what actually holds. */
.picker__menu:not(:has(*)) {
  display: none;
}

/* Who is about to be invited, shown with the same avatar and name the list
   used — a username alone doesn't confirm you picked the right person. */
.picker__chosen {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-start: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--success-soft);
  border: 0;
  box-shadow: var(--press-sm);
  border-radius: var(--radius-md);
}

.picker__chosen[hidden] {
  display: none;
}

.picker__chosen-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.picker__chosen-who {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1;
  min-width: 0;
}

.suggest__list {
  list-style: none;
  margin: 0;
  padding: var(--space-1);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  box-shadow: var(--float);
  max-height: 16rem;
  overflow-y: auto;
}

.suggest__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  text-align: start;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.suggest__item:hover,
.suggest__item:focus-visible {
  background: var(--surface-2);
}

.suggest__who {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.suggest__handle {
  font-weight: 600;
  font-size: 0.9375rem;
}

.suggest__name {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.suggest__empty {
  padding: var(--space-3);
  font-size: 0.875rem;
  color: var(--text-muted);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  box-shadow: var(--float);
}

/* -------------------------------------------------------------------------
   20. Landing page
   ------------------------------------------------------------------------- */

.landing {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  padding-block: var(--space-8) var(--space-6);
}

/* Hero: pitch left, product vignettes right; stacks below 60rem. */
.landing__hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 26rem;
  gap: var(--space-10);
  align-items: center;
}

@media (width < 60rem) {
  .landing__hero {
    grid-template-columns: minmax(0, 1fr);
  }
}

.landing__pitch h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2rem, 4.4vw, 3.25rem);
  line-height: 1.08;
  letter-spacing: -0.03em;
  margin: 0 0 var(--space-4);
  text-wrap: balance;
}

.landing__lede {
  margin: 0 0 var(--space-6);
  font-size: 1.125rem;
  color: var(--text-muted);
  max-width: 44ch;
  text-wrap: pretty;
}

.landing__cta {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}

.landing__cta .btn--lg {
  padding: 0.9375rem 1.75rem;
  font-size: 1rem;
}

/* The login page's Google pill is full-width; inline in the hero. */
.landing__cta .auth-google {
  width: auto;
}

.landing__fineprint {
  margin: var(--space-3) 0 0;
  font-size: 0.8125rem;
  color: var(--text-faint);
}

/* --- product vignettes ---------------------------------------------------- */

.landing__mocks {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.landing__mock {
  border-radius: var(--radius-lg);
  background: var(--surface-bright);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--raise-lg);
}

.landing__mock--ring {
  background: var(--brand-soft);
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.landing__mock--park {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.landing__mock-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block-end: var(--space-2);
}

.landing__mock-face {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 999px;
  background: var(--shade);
  flex: none;
}

.landing__mock-name {
  margin: 0;
  font-weight: 800;
  font-size: 0.875rem;
}

.landing__mock-dim {
  margin: 0;
  font-size: 0.75rem;
  color: var(--text-faint);
}

.landing__mock-body {
  margin: 0 0 var(--space-3);
  font-size: 0.875rem;
}

.landing__mock-chips {
  display: flex;
  gap: var(--space-2);
}

.landing__mock-ringwrap {
  position: relative;
  width: 4.75rem;
  height: 4.75rem;
  flex: none;
}

.landing__mock-ringwrap svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.landing__mock-pct {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 1rem;
}

.landing__mock-pin {
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  border-radius: var(--radius-sm);
  background: var(--brand-soft);
  color: var(--link);
  flex: none;
  box-shadow: var(--raise-sm);
}

.landing__mock-pin svg {
  width: 1.0625rem;
  height: 1.0625rem;
}

.landing__mock-park {
  min-width: 0;
}

.landing__mock-nav {
  margin-inline-start: auto;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--info);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 800;
  flex: none;
}

/* --- benefit cards --------------------------------------------------------- */

.landing__features {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

@media (width < 48rem) {
  .landing__features {
    grid-template-columns: minmax(0, 1fr);
  }
}

.landing__feature {
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: var(--space-5);
  box-shadow: var(--raise);
}

.landing__feature h2 {
  margin: 0 0 var(--space-2);
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.landing__feature p {
  margin: 0;
  font-size: 0.875rem;
  color: var(--text-muted);
}

.landing__feature-icon {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  color: #fff;
  margin-block-end: var(--space-3);
  box-shadow: var(--raise-sm);
}

.landing__feature-icon svg {
  width: 1.25rem;
  height: 1.25rem;
}

.landing__feature-icon--brand { background: var(--brand); }
.landing__feature-icon--accent { background: var(--accent); }
.landing__feature-icon--info { background: var(--info); }

/* --- closing band ---------------------------------------------------------- */

.landing__close {
  border-radius: var(--radius-xl);
  background: var(--brand);
  padding: var(--space-8) var(--space-8);
  box-shadow: var(--raise-lg);
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.landing__close-copy {
  min-width: 0;
  flex: 1;
}

.landing__close h2 {
  margin: 0 0 var(--space-1);
  font-weight: 800;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  color: #fff;
}

.landing__close p {
  margin: 0;
  font-size: 0.9375rem;
  /* Fixed, not a token: the band stays green in both themes, and dark mode's
     --brand-soft is a dark tint that would sink into it. */
  color: #dbe6d6;
}

/* Paper button on the green band — the one place raise tokens would paint
   stone-coloured shadows on a non-stone ground, so it uses --pop. */
.landing__close-btn {
  background: var(--surface-bright);
  color: var(--link);
  font-weight: 800;
  padding: 0.9375rem 1.75rem;
  font-size: 1rem;
  box-shadow: var(--pop);
  flex: none;
}


/* -------------------------------------------------------------------------
   21. Footer
   ------------------------------------------------------------------------- */

.footer {
  border-block-start: 1px solid var(--border);
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-faint);
  font-size: 0.875rem;
}

.footer__brand {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--text-muted);
}


/* --- Destructive surfaces -------------------------------------------------
   card--danger borrows the existing --danger tokens, which already carry
   dark-mode overrides, so this needs no second definition further down. */
.card--danger {
  box-shadow: var(--raise), inset 0 0 0 1px color-mix(in srgb, var(--danger) 30%, transparent);
}

.card--danger .card__head {
  color: var(--danger);
}

.delete-summary {
  margin: 0 0 var(--space-4);
  padding-inline-start: var(--space-5);
  display: grid;
  gap: var(--space-2);
}

.delete-summary li { color: var(--text-muted); }

/* The delete controls on a post and on a reply sit next to content, so they
   stay quiet until the row is hovered or the button is focused. Focus is
   included deliberately: hover-only would make them keyboard-invisible. */
.post__delete,
.comment__delete {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.post__delete-btn,
.comment__delete-btn {
  opacity: 0;
  transition: opacity 120ms ease-out, color 120ms ease-out;
}

.post:hover .post__delete-btn,
.post__delete-btn:focus-visible,
.comment:hover .comment__delete-btn,
.comment__delete-btn:focus-visible {
  opacity: 1;
}

.post__delete-btn:hover,
.comment__delete-btn:hover {
  color: var(--danger);
}

/* Touch devices get no hover, so the controls are simply always visible. */
@media (hover: none) {
  .post__delete-btn,
  .comment__delete-btn { opacity: 1; }
}



/* --- Dog parks -------------------------------------------------------------
   Reuses the existing card, btn, form-control and token vocabulary; only the
   layout specific to park search and park detail lives here. */

.page--narrow { max-width: 44rem; }

.park-search {
  display: grid;
  gap: var(--space-3);
  margin-block-end: var(--space-5);
}

.park-search__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* The text input takes the slack; the radius select stays its natural width.
   flex-wrap above means this collapses to stacked rows on a narrow phone
   without a media query. */
.park-search__row input[type="search"] { flex: 1 1 14rem; min-width: 0; }

/* width:auto beats .form-control's 100%, or the select claims a full row and
   the artboard's single-line search stack collapses. */
.park-search__radius { flex: 0 0 auto; width: auto; }

/* The form's feedback line: the HTMX "Searching…" indicator plus the two
   error slots. Collapsed entirely when idle — the indicator hides by
   opacity, which still occupies layout, and an always-reserved empty row
   was the mystery gap between the chips and the map. */
.park-search__status {
  display: none;
  align-items: center;
  gap: var(--space-3);
}

.park-search.htmx-request .park-search__status,
.park-search__status:has([role="alert"]:not([hidden])) {
  display: flex;
}

.park-results__count {
  color: var(--text-muted);
  font-size: .875rem;
  margin: 0 0 var(--space-3);
}

.park-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* minmax(0,1fr), or long park names size the column past the card edge */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
}

.park-row {
  min-width: 0;
}

/* Compact rail rows, per the parks reference: icon tile, bold name, one
   meta line. The whole row is one link; park-map.js upgrades the click to
   select-and-pan when the map is mounted. */
.park-row {
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.park-row__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  text-decoration: none;
  color: inherit;
}

.park-row__tile {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--brand-soft);
  color: var(--brand-hover);
}

.park-row__tile svg {
  width: 1.125rem;
  height: 1.125rem;
}

.park-row__body {
  min-width: 0;
}

.park-row__name {
  display: block;
  font-weight: 800;
  font-size: 0.9375rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.park-row__meta {
  display: block;
  font-size: 0.8125rem;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tag {
  font-size: .75rem;
  font-weight: 700;
  padding: .2rem .6rem;
  border-radius: var(--radius-full);
  border: 0;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
}

/* Amenities: the D12 tiles (.amenity-grid), at the end of the file. */

.park-map {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border: 0;
  border-radius: var(--radius-lg);
}


/* -------------------------------------------------------------------------
   Neumorphic shared components (redesign Phase A)
   The vocabulary the artboard repeats on every screen. Grammar, once more:
   raised = actionable, pressed = active/selected, inset well = input.
   ------------------------------------------------------------------------- */

/* Subnav pills — brought inline with the N2 header tiles: free-standing
   raised pills with no surrounding well, and the current one pressed into a
   green bed. State is carried by aria (aria-current for nav, aria-selected
   for tabs, aria-pressed for toggles), never by a class alone, so the
   pressed look and the accessibility tree cannot disagree. */
.seg {
  display: inline-flex;
  gap: var(--space-3);
  max-width: 100%;
  overflow-x: auto;
  /* Breathing room so raised pills' shadows are not clipped by the scroll box. */
  padding: 4px;
  margin: -4px;
}

.seg__btn {
  padding: 0.75rem 1.375rem;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  font-family: inherit;
  font-weight: 700;
  font-size: 0.9375rem;
  line-height: 1;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.seg__btn:hover {
  color: var(--text);
}

.seg__btn[aria-current],
.seg__btn[aria-selected="true"],
.seg__btn[aria-pressed="true"] {
  /* N2's active treatment, same as the header tiles. */
  background: var(--brand-soft);
  box-shadow: var(--press-brand-sm);
  color: var(--brand-hover);
  font-weight: 800;
}

.seg__btn svg {
  width: 1.0625rem;
  height: 1.0625rem;
}

/* Filter chip — smaller sibling of .seg__btn for amenity filters and tag
   rows; colored variants echo the artboard's park accents. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.3rem 0.75rem;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-muted);
}

button.chip {
  cursor: pointer;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

button.chip:hover {
  color: var(--brand-hover);
}

.chip[aria-pressed="true"] {
  box-shadow: var(--press-sm);
  color: var(--text);
}

.chip--green { background: var(--brand-soft); color: var(--brand-hover); box-shadow: var(--raise-brand-sm); }
.chip--brown { background: var(--accent-soft); color: var(--accent); box-shadow: var(--raise-accent-sm); }
.chip--blue  { background: var(--info-soft);  color: var(--info); box-shadow: var(--raise-info-sm); }

/* Neumorphic toggle. A real <button role="switch" aria-checked> — the thumb
   and track react to aria-checked, so the control cannot look on while
   reporting off. Sized so the thumb travel is legible at a glance. */
.switch {
  position: relative;
  flex: none;
  width: 3.25rem;
  height: 1.875rem;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--press-sm);
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}

.switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: calc(1.875rem - 6px);
  height: calc(1.875rem - 6px);
  border-radius: var(--radius-full);
  background: var(--surface-bright);
  box-shadow: var(--raise-sm);
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}

.switch[aria-checked="true"] {
  background: var(--brand-soft);
}

.switch[aria-checked="true"]::after {
  transform: translateX(1.375rem);
  background: var(--brand);
}

.switch[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* The same control driven by a real checkbox rather than aria-checked, for use
   inside a form that posts. Follows .chip--toggle: the input stays real and
   focusable, so :focus-visible lands on it and the ring is drawn on the track,
   and the on state keys off :checked — the switch cannot look on while
   submitting off. The label wraps the input, so the whole row is the hit area
   and no `for` is needed. */
.switchfield {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
}

.switchfield .switch {
  display: block;
}

.switchfield .switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.switchfield .switch:has(input:checked) {
  background: var(--brand-soft);
}

.switchfield .switch:has(input:checked)::after {
  transform: translateX(1.375rem);
  background: var(--brand);
}

.switchfield .switch:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.switchfield__label {
  display: block;
  font-weight: 600;
}

/* Progress ring — the dogs dashboard's weekly-minutes dial. The SVG is
   supplied by the template (r=42 → circumference ≈ 264, which the artboard's
   ring-in keyframe hardcodes); this styles and animates it. */
.stat-ring {
  position: relative;
  display: grid;
  place-items: center;
}

.stat-ring svg {
  transform: rotate(-90deg);
}

.stat-ring circle {
  fill: none;
  stroke-width: 10;
}

.stat-ring__track {
  stroke: var(--surface-3);
}

.stat-ring__fill {
  stroke: var(--brand);
  stroke-linecap: round;
  animation: ring-in 700ms var(--ease);
}

.stat-ring__label {
  position: absolute;
  text-align: center;
  transform: none;
}

@keyframes ring-in {
  from { stroke-dashoffset: 264; }
}

@media (prefers-reduced-motion: reduce) {
  .stat-ring__fill { animation: none; }
  .switch, .switch::after, .seg__btn, .chip, .btn { transition: none; }
}


/* -------------------------------------------------------------------------
   Shell navigation (redesign B1; the desktop nav is now the D1 text pills).
   Phones: the floating bottom tab bar (.tabbar--float in the D1 section
   restyles the bar below). 48rem is the line.
   ------------------------------------------------------------------------- */

/* Unread presence dot — a statement, not a number; the count lives in the
   accessible label and on the notifications page. Red, per the spec: it is
   an alert badge, not brand decoration. Alerts left the tab bar, so the
   header bell shows on phones too. */
.header__bell {
  position: relative;
}

.dot {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--like);
  /* Ring of surface so the dot stays legible over the bell glyph. */
  box-shadow: 0 0 0 2px var(--surface);
}

/* The bar itself. Fixed to the viewport — which is why it is included from
   base.html, never inside .header: backdrop-filter up there would make the
   header this element's containing block. */
.tabbar {
  display: none;
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: var(--z-chrome);
  padding: var(--space-2) var(--space-3);
  /* Keyboards and home indicators: respect the safe area. */
  padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
  background: var(--glass-bar-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--float-up);
  gap: var(--space-2);
}

@media (width < 48rem) {
  .tabbar {
    display: flex;
  }

  /* Content must never hide behind the floating bar. */
  .shell {
    padding-block-end: calc(var(--space-12) + 7rem);
  }

  .footer {
    padding-block-end: 7rem;
  }

  :root {
    /* 12px gap + the 72px bar + the FAB poking 14px above it + breathing
       room, plus the home indicator. Consumed by .form-actions--sticky and
       the Parks sheet. */
    --tabbar-clearance: calc(7rem + env(safe-area-inset-bottom, 0px));
  }
}

.tabbar__tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  /* 44px floor, comfortably cleared. */
  min-height: 3rem;
  padding: var(--space-2) var(--space-1);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  font-size: 0.6875rem;
  font-weight: 700;
  text-decoration: none;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.tabbar__tab svg {
  width: 1.25rem;
  height: 1.25rem;
}

.tabbar__tab[aria-current="page"] {
  box-shadow: var(--press-sm);
  color: var(--text);
}

@media (prefers-reduced-motion: reduce) {
  .tabbar__tab { transition: none; }
}


/* -------------------------------------------------------------------------
   Feed (redesign B2)
   ------------------------------------------------------------------------- */

/* The one lifted-and-lit surface on a page — the composer earns it because
   writing is the page's primary action. */
.card--bright {
  background: var(--surface-bright);
}

.composer {
  margin-block-end: var(--space-5);
}

.composer__row {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.composer__fields {
  flex: 1;
  min-width: 0;
}

/* The row beneath the well: dog tags, then the photo chip, the count label,
   and "Woof it" pushed to the right — the artboard's arrangement. */
.composer__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
}

.composer__count {
  font-size: 0.75rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.composer__submit {
  margin-inline-start: auto;
}

/* The photo chip wraps the real file input, which stays focusable for the
   ring but paints nothing itself — the chip is the visible control. */
.composer__photo {
  cursor: pointer;
  cursor: pointer;
  position: relative;
}

.composer__photo input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.composer__photo:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.composer__photo svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* The staged photo. Inset, because it is a thing being held rather than a
   control to press — the same raised/inset grammar the rest of the redesign
   uses. This is the only evidence a file is attached: the input itself is
   transparent by design. */
/* The assistant's wall: the month's questions are gone, or a burst was too fast.
   Inset because it is a state being reported, not a control — and it lands where
   the answer would have been, so it reads as the reply to what was asked. */
.aiwall {
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--press-sm);
}

.aiwall__message {
  font-weight: 600;
  color: var(--text);
}

.aiwall__meter,
.aiwall__note {
  margin-block-start: var(--space-2);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* A pause is not a sales opportunity, so it stays visually quieter. */
.aiwall--pause .aiwall__message {
  font-weight: 400;
  color: var(--text-muted);
}

/* What is left this month. Absent entirely on an unlimited plan. */
.aimeter {
  font-size: 0.75rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.aimeter--spent {
  color: var(--danger);
}

.composer__staged {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block-start: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--press-sm);
}

/* Required, not belt-and-braces: the display above beats the UA's [hidden]
   rule, so without this the empty row and its Remove button sit on the page
   with nothing attached. */
.composer__staged[hidden] {
  display: none;
}

.composer__staged-thumb {
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  object-fit: cover;
}

/* min-width:0 so a long filename ellipsises instead of pushing Remove out of
   the card. */
.composer__staged-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.feed-tabs {
  margin-block-end: var(--space-5);
}


/* -------------------------------------------------------------------------
   Dogs dashboard (redesign B3)
   ------------------------------------------------------------------------- */

.actgoal {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-block: var(--space-4);
}

.stat-ring__label strong {
  display: block;
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.stat-ring__label span {
  font-size: 0.75rem;
  color: var(--text-muted);
}

.stat-ring__fill.is-met {
  stroke: var(--success);
}

.quickadd {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-end: var(--space-4);
}

.quickadd form {
  display: contents;   /* the chips flow as one row; the forms are plumbing */
}

.quickadd__label {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-muted);
  margin-inline-end: var(--space-1);
}


/* -------------------------------------------------------------------------
   Parks (redesign B4)
   ------------------------------------------------------------------------- */

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  border: 0;
  padding: 0;
  margin: 0;
}

/* A chip that is really a checkbox. The input stays real and focusable —
   :focus-visible lands on it and the ring is drawn on the chip — and the
   pressed state keys off :checked, so the look cannot drift from the state. */
.chip--toggle {
  cursor: pointer;
  position: relative;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.chip--toggle input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.chip--toggle:has(input:checked) {
  /* Solid green when on, per the parks reference. */
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--raise-brand-sm);
}

.chip--toggle:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.placeholder-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-block: var(--space-3);
}

.placeholder-row__title {
  font-weight: 700;
  margin: 0;
}


/* -------------------------------------------------------------------------
   Feed rail & tab row (faithful pass)
   ------------------------------------------------------------------------- */

.feed-tabs-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-end: var(--space-5);
}

.feed-new {
  margin-inline-start: auto;
}

.feed-new__quiet {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-faint);
}

.feed-new__pill {
  text-decoration: none;
}

.feed-new__dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--brand);
}

.feed-note {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin-block: var(--space-2) var(--space-5);
}

/* The rail's green moment (per the outdoors crop): a pale green card, the
   ring raised on a circular plinth with only the percentage inside, and
   three equal tiles beneath — Dashboard the green one. */
.week-card {
  background: linear-gradient(155deg,
    color-mix(in srgb, var(--brand) 6%, var(--surface-bright)),
    color-mix(in srgb, var(--brand) 12%, var(--surface)));
  box-shadow: 8px 8px 18px var(--shade-brand), -8px -8px 18px var(--shade-hi);
}

.week-card__ring {
  display: grid;
  place-items: center;
  margin-block: var(--space-2) var(--space-4);
}

.week-card__plinth {
  display: grid;
  place-items: center;
  padding: var(--space-3);
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--brand) 3%, var(--surface-bright));
  box-shadow: var(--raise-brand);
}

.week-card .stat-ring__track {
  stroke: color-mix(in srgb, var(--brand) 10%, var(--surface));
}

.week-card__title {
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  text-align: center;
  margin: 0;
}

.week-card__meta {
  font-size: 0.9375rem;
  color: var(--text-muted);
  text-align: center;
  margin: var(--space-2) 0 var(--space-4);
  font-variant-numeric: tabular-nums;
}

.week-card__actions {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
}

/* Three equal tiles: the forms are plumbing but must carry the flex. */
.week-card__actions form {
  flex: 1;
  display: flex;
}

.week-card__actions form .btn,
.week-card__actions .week-card__dash {
  flex: 1;
  justify-content: center;
}

/* Dashboard is the pressed tile of the three — the spec's inset green "Dash". */
.week-card__dash {
  background: var(--brand-soft);
  color: var(--brand-text);
  box-shadow: var(--press-brand-sm);
}

.week-card__dash:hover {
  color: var(--brand-text);
  background: color-mix(in srgb, var(--brand) 14%, var(--surface));
}

/* The prompt card is the artboard's warm-brown moment: accent-soft surface
   casting an accent-tinted shade, and its button keeps the tint from inside. */
.prompt-card {
  background: var(--accent-soft);
  box-shadow: var(--raise-accent);
}

.prompt-card .btn {
  box-shadow: var(--raise-accent-sm);
}

.prompt-card__label {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--space-2);
}

.prompt-card__text {
  font-weight: 700;
  margin: 0 0 var(--space-3);
}

/* Your own dog's row presses into a green bed — the artboard's highlight.
   Negative inline margin lets the tint run wider than the list gutter. */
.leaderboard__row.is-mine {
  background: var(--brand-soft);
  box-shadow: var(--press-brand-sm);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-2);
  margin-inline: calc(var(--space-2) * -1);
}

.leaderboard {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.leaderboard__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.leaderboard__rank {
  width: 1.25rem;
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--text-faint);
  text-align: center;
}

.leaderboard__name {
  flex: 1;
  min-width: 0;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.leaderboard__name:hover {
  color: var(--brand-hover);
}

.leaderboard__minutes {
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}


/* -------------------------------------------------------------------------
   Feed fidelity pass 2
   ------------------------------------------------------------------------- */

/* The filter note rides in the tab row, to the right of the pill. */
.feed-tabs-row .feed-note {
  margin: 0;
  font-size: 0.9375rem;
}

/* Header weather chip — hidden below 60rem, where the bar is already full.
   Capped and ellipsised so a long condition string can never wrap the
   nowrap bar (the pills freed the width the old icon tiles took). */
.header__weather {
  display: none;
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (width >= 60rem) {
  .header__weather {
    display: inline-flex;
  }
}

/* Composer dog-tag chips carry the dog's face, as drawn. */
.composer__meta .chip .avatar {
  --size: 1.25rem;
  border-width: 1px;
  box-shadow: none;
}


/* -------------------------------------------------------------------------
   Header avatar tile (N2). The N2 icon-over-label nav tiles were replaced by
   the depth & layers text pills (.pills / .pill, in the D1 section).
   ------------------------------------------------------------------------- */

/* The header avatar is a rounded-square tile in N2, not a circle. */
.menu--tile > summary {
  border-radius: var(--radius-md);
  padding: 0 0 2px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
}

.menu--tile .avatar {
  border-radius: calc(var(--radius-md) - 2px);
  --size: 2.75rem;
}



/* -------------------------------------------------------------------------
   Sniff around (faithful pass)
   ------------------------------------------------------------------------- */

/* The Parks hand-off: the accent-brown moment above the grid. The head that
   used to sit above it is the D14 .sniff-bar, at the end of the file. */
.park-cta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  /* the same 20px the rest of the page keeps between its blocks */
  margin-block: 0 var(--space-5);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--accent-soft);
  box-shadow: var(--raise-accent);
}

.park-cta__mark {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: #ffffff;
  box-shadow: var(--raise-accent-sm);
}

.park-cta__mark svg {
  width: 1.375rem;
  height: 1.375rem;
}

.park-cta__text {
  min-width: 0;
}

.park-cta__title {
  font-weight: 800;
  margin: 0;
}

.park-cta__sub {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: 0;
}

.park-cta__btn {
  margin-inline-start: auto;
  background: var(--accent);
  color: #ffffff;
  box-shadow: var(--raise-accent-sm);
  flex: none;
}

.park-cta__btn:hover {
  color: #ffffff;
  background: color-mix(in srgb, var(--accent) 85%, #000000);
}


/* -------------------------------------------------------------------------
   Dog care dashboard (faithful pass)
   ------------------------------------------------------------------------- */

/* The switcher bar and the three-up stat grid became the hero and its
   straddling strip (redesign D9, at the end of the file). */

.statcard {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--raise);
}

/* Time outdoors is the green stat, matching the feed's week card. */
.statcard--green {
  background: color-mix(in srgb, var(--brand) 7%, var(--surface));
  box-shadow: var(--raise-brand);
}

.statcard--green .stat-ring__track {
  stroke: color-mix(in srgb, var(--brand) 10%, var(--surface));
}

.statcard--green .statcard__btn {
  box-shadow: var(--raise-brand-sm);
}

.statcard__label {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}

.statcard__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.statcard__big {
  font-size: 1.625rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0;
  line-height: 1.1;
}

.statcard__unit {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0;
}

.statcard__sub {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0;
}

.statcard__actions {
  display: flex;
  gap: var(--space-2);
  margin-block-start: auto;
}

.statcard__actions form {
  flex: 1;
  display: flex;
}

.statcard__btn {
  flex: 1;
  justify-content: center;
  padding: var(--space-2) var(--space-3);
  font-size: 0.875rem;
}

.statcard__soon {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-faint);
  margin: 0;
}

.stat-ring--sm .stat-ring__label strong {
  font-size: 0.9375rem;
}

/* The assistant is the page's blue moment. */
.ask--tinted {
  background: var(--info-soft);
  box-shadow: var(--raise-info);
  margin-block-end: var(--space-6);
}

.ask--tinted .chip {
  box-shadow: var(--raise-info-sm);
}

.ask__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-end: var(--space-3);
}

.ask__title {
  font-weight: 800;
  font-size: 1.0625rem;
  margin: 0;
}

.ask__hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0;
}

/* Care log: day | what | minutes | who. */
.carelog {
  margin-block-end: var(--space-6);
}

.carelog__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-block-end: var(--space-3);
}

.carelog__who,
.packwoof__more {
  margin-inline-start: auto;
  font-size: 0.8125rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.packwoof__more {
  font-weight: 700;
  text-decoration: none;
}

.carelog__list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
}

.carelog__row {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr) auto auto;
  gap: var(--space-3);
  align-items: baseline;
  padding-block: var(--space-2);
  border-block-end: 1px solid var(--border);
  font-size: 0.875rem;
}

.carelog__row:last-child {
  border-block-end: 0;
}

.carelog__day {
  font-weight: 800;
  color: var(--text-muted);
}

.carelog__what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.carelog__mins {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.carelog__by {
  color: var(--text-faint);
}

/* The rail's dog identity card. */
/* The identity card became the hero (D9); the people chips stay. */
.doghero__people {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}

.doghero__people-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}

/* Chips that are people: the face rides inside the pill. */
.doghero__people-row .chip {
  text-decoration: none;
  padding-inline-start: var(--space-1);
}

.doghero__people-row .avatar {
  --size: 1.375rem;
  border-width: 1px;
  box-shadow: none;
}

.doghero__role {
  font-size: 0.625rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.75;
}

/* Pack woofs in the rail. */
.packwoof {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding-block: var(--space-2);
}

.packwoof__body {
  min-width: 0;
}

.packwoof__text {
  font-size: 0.875rem;
  margin: 0;
}

.packwoof__meta {
  font-size: 0.75rem;
  color: var(--text-faint);
  margin: 0;
}


/* Info tip — the little "i" beside truncated lists. The panel opens on hover
   OR keyboard focus; hover-only tooltips are invisible to keyboards. */
.tipwrap {
  position: relative;
  display: inline-flex;
}

.tip__btn {
  display: inline-grid;
  place-items: center;
  width: 1.125rem;
  height: 1.125rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  font-size: 0.6875rem;
  font-weight: 800;
  font-style: italic;
  font-family: inherit;
  cursor: help;
}

.tip__panel {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-popover);
  min-width: 12rem;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--float);
  font-size: 0.8125rem;
  text-align: start;
  display: none;
}

.tipwrap:hover .tip__panel,
.tipwrap:focus-within .tip__panel {
  display: block;
}

.tip__panel {
  flex-direction: column;
  gap: var(--space-1);
}

.tipwrap:hover .tip__panel,
.tipwrap:focus-within .tip__panel {
  display: flex;
}

.tip__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}

.tip__pct {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}


/* -------------------------------------------------------------------------
   Parks map (faithful pass)
   ------------------------------------------------------------------------- */

/* The map card itself is the D11 .parkscape (the search form). */
.park-mapcanvas--empty {
  display: grid;
  place-items: center;
  color: var(--text-muted);
  font-size: 0.875rem;
}

/* The selected park's text, shared with the D11 glass card (.parkscape__card). */
.park-selected__text {
  min-width: 0;
  flex: 1;
}

.park-selected__name {
  font-weight: 800;
  margin: 0;
}

.park-selected__meta {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.park-mapnote {
  margin-block-start: var(--space-4);
}


/* -------------------------------------------------------------------------
   Parks page, reference layout
   ------------------------------------------------------------------------- */

/* The hero that used to sit above the split is gone: the search form is the
   map now (D11, .parkscape), and the rail list's rules live there too. */

/* The blue privacy moment, top of the rail. */
.park-privacy {
  background: var(--info-soft);
  box-shadow: var(--raise-info);
}

.park-privacy__summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
}

.park-privacy__summary::-webkit-details-marker {
  display: none;
}

.park-privacy__chevron {
  display: grid;
  place-items: center;
  transition: transform var(--dur-fast) var(--ease);
}

.park-privacy__chevron svg {
  width: 1rem;
  height: 1rem;
  color: var(--info);
}

.park-privacy[open] .park-privacy__chevron {
  transform: rotate(90deg);
}

.park-privacy[open] .park-privacy__summary {
  padding-block-end: 0;
}

.park-privacy .card__body {
  padding-block-start: var(--space-3);
}

.park-privacy__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 800;
  font-size: 1.0625rem;
  margin: 0;
}

.park-privacy__mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  background: var(--info);
  color: #ffffff;
}

.park-privacy__mark svg {
  width: 1rem;
  height: 1rem;
}

.park-privacy__label {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: var(--space-3) 0 var(--space-2);
}

.park-privacy .seg {
  /* Wrap inside the card rather than clipping "Nobody" at its edge. */
  flex-wrap: wrap;
  overflow: visible;
}

.park-privacy .seg__btn {
  box-shadow: var(--raise-info-sm);
  padding: 0.625rem 1rem;
}

.park-privacy__note {
  margin-block-start: var(--space-3);
}

.park-selected__name a {
  color: var(--text);
  text-decoration: none;
  font-weight: 800;
  /* the reference's 21px */
  font-size: 1.3125rem;
  letter-spacing: -0.02em;
}

.park-selected__name a:hover {
  color: var(--brand-hover);
}

.park-selected__dist {
  color: var(--info);
  font-weight: 700;
  font-size: 0.875rem;
  margin: 0;
}

.park-selected__rating {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* The rating is fetched on request, so it needs an affordance. Deliberately
   quiet — a text button, not a call to action: most people do not want a Google
   star, and every press is a billed Places lookup. */
.park-selected__ratingbtn {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--info);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  cursor: pointer;
}

.park-selected__ratingbtn:hover {
  color: var(--text);
}

.park-selected__ratingbtn[disabled] {
  color: var(--text-faint);
  text-decoration: none;
  cursor: default;
}

.park-selected__ratingbtn[hidden] {
  display: none;
}


/* -------------------------------------------------------------------------
   Parks, reference pass 2: search pill, map pill-markers, rail fidelity
   ------------------------------------------------------------------------- */

/* The one search control: an inset pill with the icon inside. */
.searchwell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.75rem 1rem;
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: inset 2px 2px 5px var(--shade), inset -1px -1px 2px var(--shade-hi);
  cursor: text;
  min-width: 0;
}

.searchwell svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--text-muted);
}

.searchwell input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 0.9375rem;
  color: var(--text);
}

.searchwell input:focus {
  outline: none;
}

.searchwell:focus-within {
  background: var(--surface-bright);
  box-shadow: inset 2px 2px 5px var(--shade-brand), 0 0 0 2px var(--brand-ring);
}

/* Map pins: the D11 section (.map-pill) — toned rings, the chosen one filled. */

/* Rail fidelity: brown section label and bright rows. The tiles' colours are
   the parks' real tones now (D11, data-tone), not a rotation by position. */
.rail-label--accent {
  color: var(--accent);
}

.park-row {
  background: var(--surface-bright);
}

/* Privacy switch rows as bright sub-cards, per the reference. */
.park-privacy__row {
  background: var(--surface-bright);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  box-shadow: var(--raise-info-sm);
  margin-block: var(--space-2);
}


/* Suggest-a-park fee block: the amounts only appear once "a pass is
   required" is picked — CSS, no JS, so the no-script form still works.
   A browser without :has simply shows the amounts all the time. */
.park-fee__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.park-fee__amounts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3);
  margin-block-start: var(--space-3);
}

.park-fee__amounts label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.park-fee__or {
  grid-column: 1 / -1;
  font-size: 0.8125rem;
  color: var(--text-faint);
  margin: 0;
}

.park-fee:not(:has(input[name="pass"][value="yes"]:checked)) .park-fee__amounts {
  display: none;
}


/* -------------------------------------------------------------------------
   Controls row + scrollbars
   ------------------------------------------------------------------------- */

/* (the one-row .park-controls arrangement was superseded by .park-hero) */

/* Scrollbars in the house style, everywhere: a slim sage thumb on a quiet
   stone gutter. Firefox first (the standard properties), then WebKit. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--shade) transparent;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--shade);
  border-radius: var(--radius-full);
  border: 2px solid var(--surface);
}

::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--brand) 25%, var(--shade));
}


/* -------------------------------------------------------------------------
   Profile: the identity rail became the hero bar (redesign D8, at the end
   of the file). The dogs tab's cards stay here.
   ------------------------------------------------------------------------- */

/* The dogs tab: the artboard's two-up dog cards. */
.dogcard-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
}

.dogcard {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  text-decoration: none;
  color: inherit;
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.dogcard:hover {
  box-shadow: var(--raise-lg);
  transform: translateY(-2px);
}

.dogcard__face {
  --size: 4.75rem;
  border-radius: var(--radius-lg);
  flex: none;
}

.dogcard__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: flex-start;
}

.dogcard__name {
  font-weight: 800;
  font-size: 1.0625rem;
}

.dogcard__facts {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.dogcard__mix {
  width: 9.5rem;
  display: block;
}


/* The pet form's live mix preview. */
.mixpreview {
  margin-block-end: var(--space-4);
}

.mixpreview:empty {
  display: none;
}


/* The Dogs tab's add card: a quiet dashed invitation among the dog cards. */
.dogcard--add {
  background: var(--surface);
  box-shadow: var(--press-sm);
  align-items: center;
}

.dogcard--add:hover {
  box-shadow: var(--press-sm);
  transform: none;
  background: var(--brand-soft);
}

.dogcard__addmark {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--brand-soft);
  color: var(--brand-hover);
  font-size: 1.5rem;
  font-weight: 800;
  box-shadow: var(--raise-brand-sm);
}


/* -------------------------------------------------------------------------
   Sign in with Google
   ------------------------------------------------------------------------- */

/* "or" rule between the password form and the Google button. */
.auth-or {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block: var(--space-4);
  color: var(--text-faint);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.auth-or::before,
.auth-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.auth-google {
  width: 100%;
  justify-content: center;
  background: var(--surface-bright);
}

/* -------------------------------------------------------------------------
   Edit-as-preview pages (edit profile, edit dog)
   The page's own identity card sits beside the form and repaints as you
   type. The card is aria-hidden — a mirror, never the only copy of a value.
   ------------------------------------------------------------------------- */

.editsplit-page {
  max-width: 64rem;
  margin-inline: auto;
}

.editsplit__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-block-end: var(--space-6);
}

/* .editsplit__actions retired — the save row now lives at the end of the
   form (.form-actions--sticky), like every other form on the site. */

.editsplit__head h1 {
  margin: 0 0 var(--space-1);
}

.editsplit__head .muted {
  margin: 0;
}

.editsplit {
  display: grid;
  grid-template-columns: 19rem minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}

@media (width < 60rem) {
  .editsplit {
    grid-template-columns: minmax(0, 1fr);
  }
}

.editsplit__rail {
  position: sticky;
  top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.editsplit__rail .card--danger {
  margin-block-start: var(--space-4);
}

@media (width < 60rem) {
  .editsplit__rail {
    position: static;
  }
}

.editsplit__preview-label {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.editsplit__preview-note {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--text-faint);
  text-align: center;
  text-wrap: balance;
}

.editsplit__forms {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.editsplit__caredby-label {
  margin: var(--space-4) 0 var(--space-2);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.editsplit__caredby {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
}

.editsplit__below {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-block-start: var(--space-6);
}

/* Two fields sharing a line (first/last name, weight/target). Column gap
   only — each .field keeps its own block spacing. */
.fieldrow {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-4);
  margin-block-start: var(--space-4);
}

/* Same fix the actform grid needed: the .field + .field top margin would
   push the second column down out of line with the first. The row carries
   the block spacing instead. */
.fieldrow .field + .field {
  margin-block-start: 0;
}

.fieldrow:first-child,
.banner + .fieldrow {
  margin-block-start: 0;
}

.fieldrow + .field,
.fieldrow + .fieldrow {
  margin-block-start: var(--space-4);
}

.fieldrow .field {
  margin-block-start: 0;
}

@media (width < 48rem) {
  .fieldrow {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Current photo beside its file field, so "change the picture" starts from
   what the picture is now. The thumbnail mirrors a fresh pick (woofline.js). */
.photorow {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* .field + .field can't see across the photorow wrapper, so the row hands
   the next field its block spacing itself. */
.photorow + .field {
  margin-block-start: var(--space-4);
}

.photorow .field + .field,
.photorow .field {
  margin-block-start: 0;
}

.photorow .field {
  flex: 1;
  min-width: 0;
}

.photorow__current {
  flex: none;
}

/* Subheading between field groups inside one card (the Links block). */
.fieldgroup__label {
  margin: var(--space-5) 0 var(--space-3);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Live character counter under a limited textarea. aria-hidden — the field's
   help text already states the limit for assistive tech. */
.charcount {
  margin: calc(var(--space-2) * -1) 0 var(--space-4);
  font-size: 0.75rem;
  color: var(--text-faint);
  text-align: right;
}

.charcount--over {
  color: var(--danger);
  font-weight: 700;
}

/* Sign-in & privacy: info-tinted, like the water/trail chips family. */
.card--info {
  background: var(--info-soft);
  box-shadow: var(--raise-info, var(--raise));
}

.card--info .card__head {
  color: var(--info);
}

.signin-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.signin-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-3);
  font-weight: 600;
  font-size: 0.9375rem;
}

.signin-row + .signin-row {
  border-block-start: 1px solid var(--border);
}

.signin-row .btn svg {
  width: 1rem;
  height: 1rem;
}

/* -------------------------------------------------------------------------
   Breed combobox (edit dog)
   The name input in each breed row filters a suggestion list. The panel is
   a true overlay over the following rows, so it uses --pop, not the raise
   pair. Focus/selection follow the ARIA combobox pattern in woofline.js.
   ------------------------------------------------------------------------- */

.combobox-anchor {
  position: relative;
}

.combobox-list {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  z-index: var(--z-popover);
  border-radius: var(--radius-md);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--float);
  padding: var(--space-1);
  margin: 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
}

.combobox-list[hidden] {
  display: none;
}

.combobox-option {
  padding: var(--space-2) var(--space-3);
  border-radius: calc(var(--radius-md) - 5px);
  font-size: 0.875rem;
  cursor: pointer;
}

.combobox-option b {
  font-weight: 800;
}

.combobox-option[aria-selected="true"],
.combobox-option:hover {
  background: var(--brand-soft);
  color: var(--brand-hover);
  font-weight: 600;
}

.combobox-note {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: 0.6875rem;
  color: var(--text-faint);
  border-block-start: 1px solid var(--surface);
  margin-block-start: var(--space-1);
}

/* -------------------------------------------------------------------------
   Edit-dog page specifics (the EditDog artboard)
   ------------------------------------------------------------------------- */

/* The form wrapper is one flex child of the column, so the cards inside it
   need their own stack gap. */
.editsplit__forms > form {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Card titles on these pages are the artboard's uppercase micro-labels. */
.cardlabel {
  margin: 0 0 var(--space-4);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.cardlabel--bare {
  margin: 0;
}

/* Breed-mix card header: label left, running total right. */
.mixhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-block-end: var(--space-3);
}

.mixhead .breed-total {
  font-size: 0.75rem;
  color: var(--text-faint);
}

.mixpreview--card {
  margin-block-end: var(--space-4);
}

.breed-add {
  margin-block-start: var(--space-4);
  border-radius: var(--radius-full);
}

/* The preview card: centered, bright, the dog at a glance. */
.petpreview__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
}

.petpreview__face {
  border-radius: var(--radius-xl);
}

.petpreview__name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1875rem;
  letter-spacing: -0.02em;
}

.petpreview__meta {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.petpreview__mix {
  margin: 0;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.petpreview__body .mixpreview {
  width: 100%;
}

/* Tiny role tag inside a chip (the owner chip). */
.chip__micro {
  font-size: 0.5625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.75;
}

/* -------------------------------------------------------------------------
   Edit-profile page specifics (the Main artboard)
   ------------------------------------------------------------------------- */

.petpreview__bio {
  margin: 0;
  font-size: 0.875rem;
  color: var(--text);
}

.cardlabel--danger {
  color: var(--danger);
}

.photorow__hint {
  font-size: 0.75rem;
  color: var(--text-faint);
  flex: none;
  max-width: 11rem;
}

/* Label-less link wells with the service's icon inside, per the artboard.
   Each widget carries its own aria-label. */
.linkgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

@media (width < 48rem) {
  .linkgrid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.linkwell {
  position: relative;
  min-width: 0;
}

.linkwell__icon {
  position: absolute;
  inset-inline-start: 0.875rem;
  /* Fixed offset, not 50%: an error line under the input would drag a
     centered icon out of the well. */
  inset-block-start: 1rem;
  display: grid;
  place-items: center;
  color: var(--text-muted);
  pointer-events: none;
}

.linkwell__icon svg {
  width: 1rem;
  height: 1rem;
}

.linkwell .form-control {
  width: 100%;
  padding-inline-start: 2.5rem;
}

/* Sign-in & privacy: the artboard's compact banner. Closed it is the mock;
   "Manage" is the disclosure that opens the real rows. */
.signin-details {
  background: var(--info-soft);
  box-shadow: var(--raise-info, var(--raise));
}

.signin-summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  list-style: none;
}

.signin-summary::-webkit-details-marker {
  display: none;
}

.signin-summary__icon {
  display: grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 13px;
  background: var(--info);
  color: #fff;
  flex: none;
}

.signin-summary__icon svg {
  width: 1.125rem;
  height: 1.125rem;
}

.signin-summary__copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.signin-summary__title {
  font-weight: 800;
  font-size: 0.875rem;
}

.signin-summary__sub {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.signin-summary__btn {
  margin-inline-start: auto;
  background: var(--surface-bright);
  color: var(--info);
  font-weight: 800;
  box-shadow: var(--raise-info-sm, var(--raise-sm));
  flex: none;
}

.signin-details[open] .signin-summary__btn {
  box-shadow: var(--press-sm);
}

.signin-details__body {
  padding-block-start: 0;
}

/* The live reading ("72° · trails dry") is one phrase — never wrapped. */
.header__weather {
  white-space: nowrap;
}

.header__weather svg {
  width: 1rem;
  height: 1rem;
  flex: none;
}

/* -------------------------------------------------------------------------
   Direct messages (restyled in redesign D10)
   Two panes in one rounded shell on the ambient ground. The conversation
   list is grounded rows with the open one lifted onto the tinted surface;
   the thread's incoming bubbles stay grounded, yours ride the green floating
   tier, and the composer is the pane's one lifted-and-lit surface. Every
   colour, radius and shadow is a token, so dark mode derives. Below 60rem
   the list pane hides on the thread page and dm_list is the inbox.
   ------------------------------------------------------------------------- */

/* The messages pages are the app-like screen: the page itself never scrolls.
   The body takes the viewport's height, so the page container is exactly
   what the header, a banner and the tab bar leave; it turns into a column
   and the shell fills it, panes scrolling inside. The footer steps aside, as
   it does for the auth scene. A viewport too short for the shell's minimum
   simply scrolls. */
body:has(.dm-shell) {
  height: 100dvh;
}

body:has(.dm-shell) .shell {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

body:has(.dm-shell) .footer {
  display: none;
}

.dm-shell {
  flex: 1 1 auto;
  min-height: 24rem;
  max-height: 60rem;
  display: grid;
  grid-template-columns: 20rem minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  border-radius: 1.5rem;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--glass-border);
  box-shadow: var(--float);
}

/* --- the conversation pane ---------------------------------------------- */

.dm-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: color-mix(in srgb, var(--bg) 45%, transparent);
  border-inline-end: 1px solid var(--border);
}

.dm-pane__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-4) var(--space-3);
}

.dm-pane__title {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

/* The pane's one green control: a new chat. */
.dm-pane__new {
  margin-inline-start: auto;
  display: inline-grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  flex: none;
  border-radius: 0.6875rem;
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--lift-brand);
  text-decoration: none;
}

.dm-pane__new:hover {
  background: var(--brand-hover);
  color: var(--on-brand);
}

.dm-pane__new svg {
  width: 1.0625rem;
  height: 1.0625rem;
}

.dm-pane__empty {
  padding: var(--space-4);
}

.dm-threads {
  list-style: none;
  margin: 0;
  padding: 0 var(--space-3) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
}

.dm-thread {
  flex: none;
}

.dm-thread__link {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  padding: var(--space-3);
  border-radius: 0.9375rem;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text);
  text-decoration: none;
  transition: box-shadow var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.dm-thread__link:hover {
  color: var(--text);
  box-shadow: var(--raise);
}

/* The open thread: lifted onto the tinted surface. */
.dm-thread__link[aria-current="page"] {
  background: var(--surface-tint);
  box-shadow: var(--raise-tint);
}

.dm-thread__face {
  flex: none;
}

.dm-thread__face--group {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-full);
  background: var(--brand-soft);
  color: var(--brand-text);
  box-shadow: var(--press-brand-sm);
}

.dm-thread__face--group svg {
  width: 1.25rem;
  height: 1.25rem;
}

.dm-thread__body {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.dm-thread__name {
  font-weight: 800;
  font-size: 0.875rem;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.dm-thread__name .dot {
  position: static;
}

.dm-thread__snippet {
  font-size: 0.78125rem;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Unread: bolder text plus the dot — never colour alone. */
.dm-thread.is-unread .dm-thread__snippet {
  color: var(--text);
  font-weight: 700;
}

.dm-thread__time {
  flex: none;
  align-self: flex-start;
  margin-block-start: 2px;
  font-size: 0.6875rem;
  color: var(--text-faint);
}

/* --- the thread pane ------------------------------------------------------ */

.dm-chat {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

/* The header is the pane's glass bar. */
.dm-chat__head {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.875rem 1.375rem;
  background: var(--glass-bar-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-end: 1px solid var(--border);
}

.dm-chat__back {
  flex: none;
}

@media (width >= 60rem) {
  .dm-chat__back {
    display: none;
  }
}

.dm-chat__who {
  min-width: 0;
  flex: 1;
}

.dm-chat__name {
  margin: 0;
  font-weight: 800;
  font-size: 0.9375rem;
}

.dm-chat__meta {
  margin: 0;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.dm-chat__actions {
  margin-inline-start: auto;
}

/* The header's controls are the spec's raised squares. */
.dm-chat__more,
.dm-chat__actions > summary.btn {
  min-height: 2.5rem;
  border-radius: 0.6875rem;
  background: var(--surface);
  color: var(--text-muted);
  box-shadow: var(--raise-sm);
  list-style: none;
}

.dm-chat__more {
  width: 2.5rem;
  height: 2.5rem;
}

.dm-chat__more::-webkit-details-marker,
.dm-chat__actions > summary::-webkit-details-marker {
  display: none;
}

.dm-chat__more svg {
  width: 1.125rem;
  height: 1.125rem;
}

.dm-people {
  min-width: 18rem;
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dm-people__add {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.dm-people__add .form-control {
  flex: 1;
  min-width: 0;
}

/* The inbox page's right pane: nothing open yet. */
.dm-chat--empty {
  display: grid;
  place-items: center;
  padding: var(--space-8);
  text-align: center;
}

.dm-chat__placeholder {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  max-width: 22rem;
}

.dm-chat__placeholder-icon {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 1.125rem;
  background: var(--surface);
  color: var(--text-faint);
  box-shadow: var(--press-sm);
  margin-block-end: var(--space-2);
}

.dm-chat__placeholder-icon svg {
  width: 1.5rem;
  height: 1.5rem;
}

.dm-chat__placeholder-title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.dm-chat__placeholder-text {
  margin: 0;
  font-size: 0.875rem;
  color: var(--text-muted);
}

/* --- transcript ----------------------------------------------------------- */

.dm-transcript {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: 1.375rem;
  scrollbar-width: thin;
}

.dm-transcript__empty {
  text-align: center;
  margin-block: auto;
}

/* The moment a first bubble appends (send or poll), the empty prompt is no
   longer alone in the transcript — CSS retires it, no JS involved. */
.dm-transcript__empty:not(:only-child) {
  display: none;
}

.dm-day {
  align-self: center;
  margin: var(--space-4) 0 var(--space-2);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.dm-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 70%;
}

.dm-row--own {
  align-self: flex-end;
  align-items: flex-end;
}

/* Consecutive same-side bubbles group tight. */
.dm-row + .dm-row {
  margin-block-start: var(--space-2);
}

.dm-row + .dm-row--own,
.dm-row--own + .dm-row:not(.dm-row--own) {
  margin-block-start: var(--space-4);
}

.dm-row__sender {
  font-size: 0.6875rem;
  font-weight: 800;
  color: var(--text-faint);
  margin: 0 0 2px var(--space-3);
}

/* Incoming: grounded. Yours: the green floating tier. The tight corner is
   the one nearest the sender. */
.dm-bubble {
  background: var(--surface);
  border-radius: 18px 18px 18px 5px;
  padding: 0.6875rem 0.9375rem;
  box-shadow: var(--raise-sm);
}

.dm-bubble--own {
  background: linear-gradient(150deg, color-mix(in srgb, var(--brand) 86%, #fff 14%), var(--brand));
  color: var(--on-brand);
  border-radius: 18px 18px 5px 18px;
  box-shadow: var(--lift-brand);
}

.dm-bubble__body {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

/* The meta line under the bubble: the time and, on yours, the delete control. */
.dm-row__meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 1.5rem;
  margin: 2px var(--space-2) 0;
}

.dm-row--own .dm-row__meta {
  flex-direction: row-reverse;
}

.dm-row__time {
  font-size: 0.6875rem;
  color: var(--text-faint);
}

.dm-row.htmx-added .dm-bubble {
  animation: dm-in 200ms ease-out;
}

@keyframes dm-in {
  from { opacity: 0; translate: 0 6px; }
  to { opacity: 1; translate: 0 0; }
}

/* --- composer ------------------------------------------------------------- */

.dm-chat__foot {
  flex: none;
  padding: var(--space-3) 1.375rem 1.25rem;
}

/* The bar: .surface--lifted supplies the bright fill and the lifted raise;
   the field sits flat inside it and the bar, not the field, shows focus. */
.dm-compose {
  position: relative;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: 0.5rem 0.5rem 0.5rem 1rem;
  border-radius: 1rem;
}

.dm-compose:focus-within {
  box-shadow: var(--raise-lift), 0 0 0 2px var(--brand-ring);
}

textarea.dm-compose__field,
textarea.dm-compose__field:focus {
  flex: 1 1 8rem;
  min-width: 0;
  min-height: 2.625rem;
  max-height: 9rem;
  padding: 0.6875rem 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  resize: none;
  font-size: 0.875rem;
  line-height: 1.35;
}

.dm-compose__send {
  flex: none;
  width: 2.625rem;
  height: 2.625rem;
  padding: 0;
  border-radius: 0.75rem;
  box-shadow: var(--lift-brand);
}

.dm-compose__send svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* "Sending…" rides just under the bar while the request is out. */
.dm-compose__pending {
  position: absolute;
  inset-inline-end: 3.75rem;
  inset-block-end: -1.125rem;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--text-faint);
}

.dm-compose .inline-error {
  flex-basis: 100%;
  margin: 0 0 var(--space-1);
}

/* Below 60rem the thread page is the thread alone and the inbox page is the
   list alone. */
@media (width < 60rem) {
  .dm-shell {
    grid-template-columns: minmax(0, 1fr);
  }

  .dm-split .dm-rail,
  .dm-chat--empty {
    display: none;
  }

  .dm-pane {
    border-inline-end: 0;
  }
}

@media (width < 48rem) {
  .dm-shell {
    border-radius: 1.25rem;
  }

  .dm-chat__head {
    padding: var(--space-3) var(--space-4);
  }

  .dm-transcript {
    padding: var(--space-4);
  }

  .dm-chat__foot {
    padding: var(--space-3) var(--space-3) var(--space-4);
  }

  .dm-row {
    max-width: 84%;
  }
}

/* The Message button is a one-field form; inline so it sits in action rows
   and idcard CTAs alike. As an action-link it sheds the button chrome. */
.dm-start {
  display: inline-flex;
}

.dm-start button.action-link {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
}

/* Deleting your own message: the control sits on the meta line beside the
   time and stays invisible until the row is hovered or focused — always
   focusable, so keyboard users reach it without a pointer. */
.dm-row__delete {
  display: inline-flex;
}

.dm-row__delete-btn {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-sm);
  opacity: 0;
  transition: opacity 120ms ease-out;
}

.dm-row:hover .dm-row__delete-btn,
.dm-row:focus-within .dm-row__delete-btn {
  opacity: 1;
}

.dm-row__delete-btn svg {
  width: 0.8125rem;
  height: 0.8125rem;
}

@media (prefers-reduced-motion: reduce) {
  .dm-row.htmx-added .dm-bubble {
    animation: none;
  }

  .dm-row__delete-btn,
  .dm-thread__link {
    transition: none;
  }
}

/* Reply likes: the post control at reply scale. */
.like--sm {
  font-size: 0.75rem;
  padding: 2px 8px;
}

.like--sm svg {
  width: 0.875rem;
  height: 0.875rem;
}

.comment__like {
  margin-block-start: 2px;
  display: inline-flex;
}

/* Trending sniffs — the discovery list in the home rail. */
.trending {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.trending__row {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
}

.trending__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.trending__link {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.trending__link:hover {
  color: var(--link);
}

.trending__meta {
  font-size: 0.75rem;
  color: var(--text-faint);
}

/* The week card's title-as-picker: the avatar-menu pattern, dressed as the
   title line. Chevron only when it IS a menu; panel is the house menu__panel. */
.week-card__picker {
  position: relative;
  display: flex;
  justify-content: center;
}

.week-card__title--picker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  cursor: pointer;
  list-style: none;
  border-radius: var(--radius-sm);
  padding-inline: var(--space-2);
}

.week-card__title--picker::-webkit-details-marker {
  display: none;
}

.week-card__title--picker svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--text-faint);
}

.week-card__picker[open] .week-card__title--picker svg {
  rotate: 180deg;
}

.week-card__picker .menu__panel {
  left: 50%;
  translate: -50% 0;
  right: auto;
  min-width: 15rem;
}

.week-card__picker .menu__item[aria-current="true"] {
  box-shadow: var(--press-sm);
  background: var(--bg);
}

.week-card__picker .menu__item .muted {
  margin-inline-start: auto;
  font-size: 0.75rem;
}

/* Dog tags on a woof card, and the "Woofs about <dog>" list on the dog page. */
.post__dogtags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-end: var(--space-3);
}

.post__dogtag {
  text-decoration: none;
}

.post__dogtag .avatar,
.composer__dogtag .avatar {
  --size: 1.25rem;
  border-width: 1px;
}

.dogwoofs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dogwoof {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.dogwoof__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.dogwoof__link {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.dogwoof__link:hover {
  color: var(--link);
}

.dogwoof__meta {
  font-size: 0.75rem;
  color: var(--text-faint);
}

/* Collapsible dashboard sections on the dog page: heading-as-summary with
   the count badge; the full history lives on the See-all pages. */
.dogsection {
  margin-block-start: var(--space-8);
}

.dogsection__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  list-style: none;
  margin-block-end: var(--space-4);
  width: fit-content;
  border-radius: var(--radius-sm);
  padding-inline-end: var(--space-2);
}

.dogsection__head::-webkit-details-marker {
  display: none;
}

.dogsection__head::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid var(--text-faint);
  border-bottom: 2px solid var(--text-faint);
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate 120ms ease-out;
}

.dogsection[open] > .dogsection__head::after {
  rotate: 225deg;
  translate: 0 2px;
}

.dogsection__head h2 {
  margin: 0;
}

.dogsection__all {
  margin-block-start: var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
  .dogsection__head::after {
    transition: none;
  }
}

/* The bookmark chip: pressed-in when saved, the aria state driving it —
   same grammar as every other toggle. */
.save-wrap {
  display: inline-flex;
  align-items: center;
}

.save-wrap .chip[aria-pressed="true"] {
  box-shadow: var(--press-sm);
  background: var(--brand-soft);
  color: var(--brand-hover);
}

/* Safety chrome (owner-requested placement): the name row's circle buttons
   (flag = report, chat = message) and the Mute | Block pair under Follow. */
.idcard__namerow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

.idcard__namerow .idcard__name {
  margin: 0;
}

.idcard__namebtn {
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  flex: none;
}

.idcard__namebtn svg {
  width: 1rem;
  height: 1rem;
}

.silence-pair {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  margin-block-start: var(--space-2);
}

.silence-pair .btn[aria-pressed="true"] {
  box-shadow: var(--press-sm);
  background: var(--bg);
}

.report-reasons {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-start;
}

/* Park presence: the check-in form and the here-now badge. */
.park-presence__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.park-presence__duration {
  width: auto;
  font-size: 0.875rem;
}

.park-row__here {
  font-size: 0.6875rem;
  padding: 2px 8px;
  margin-inline-start: var(--space-2);
}

.park-privacy__choice {
  display: inline-flex;
  margin: 0 var(--space-2) var(--space-2) 0;
}

/* Park page: details left, presence rail right; the named list expands per
   person to the dogs they brought. */
.park-page__main {
  min-width: 0;
}

.presence-list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.presence-row__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
  cursor: pointer;
  list-style: none;
}

.presence-row__head::-webkit-details-marker {
  display: none;
}

.presence-row[open] > .presence-row__head {
  box-shadow: var(--press-sm);
  background: var(--bg);
}

.presence-row__who {
  font-weight: 700;
  font-size: 0.875rem;
  min-width: 0;
  flex: 1;
}

.presence-row__count {
  font-size: 0.75rem;
  color: var(--text-faint);
  flex: none;
}

.presence-row__chevron {
  width: 0.4rem;
  height: 0.4rem;
  border-right: 2px solid var(--text-faint);
  border-bottom: 2px solid var(--text-faint);
  rotate: 45deg;
  translate: 0 -1px;
  transition: rotate 120ms ease-out;
  flex: none;
}

.presence-row[open] .presence-row__chevron {
  rotate: 225deg;
  translate: 0 1px;
}

.presence-row__dogs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
  .presence-row__chevron {
    transition: none;
  }
}

/* The map footer's dog faces: an overlapping strip beside the count. */
.park-selected__faces {
  display: inline-flex;
  align-items: center;
  flex: none;
}

.park-selected__face {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 999px;
  border: 2px solid var(--surface-bright);
  background: var(--shade);
  object-fit: cover;
}

.park-selected__face + .park-selected__face {
  margin-inline-start: -0.5rem;
}

.park-selected__face--more {
  display: inline-grid;
  place-items: center;
  width: auto;
  padding-inline: 0.5rem;
  font-size: 0.625rem;
  font-weight: 800;
  color: var(--text-muted);
  background: var(--surface);
}

.park-selected__face--anon {
  display: inline-grid;
  place-items: center;
  color: var(--text-faint);
  background: var(--surface);
}

.park-selected__face--anon svg {
  width: 1rem;
  height: 1rem;
}

/* The Who's-here aggregate row for dogs whose humans stay unnamed. */
.presence-row__head--static {
  cursor: default;
}

.presence-anonpaw {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 999px;
  display: inline-grid;
  place-items: center;
  color: var(--text-faint);
  background: var(--bg);
  box-shadow: var(--press-sm);
  flex: none;
}

.presence-anonpaw svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* Suggest-a-park: the pin-drop canvas inside the form card. */
.park-mapcanvas--suggest {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  box-shadow: var(--press-sm);
  overflow: hidden;
}

/* Tips from the pack: one row per tip, delete only where allowed. */
.tips-form {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin-block-end: var(--space-4);
}

.tips-form .form-control {
  flex: 1;
  min-width: 0;
}

.tips-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.tip {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
}

.tip__body {
  flex: 1;
  min-width: 0;
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

.tip__meta {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
}

.tip__who {
  font-weight: 700;
  text-decoration: none;
  color: inherit;
}

.tip__who:hover {
  text-decoration: underline;
}

/* -------------------------------------------------------------------------
   Ops console
   ------------------------------------------------------------------------- */

.ops-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-end: var(--space-5);
}

.ops-nav .chip {
  text-decoration: none;
}

/* The house selected treatment: pressed-inset, like nav tiles. */
.chip--pressed {
  box-shadow: var(--press-sm);
  background: var(--bg);
}

.ops-nav__badge {
  display: inline-grid;
  place-items: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding-inline: 0.25rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 800;
  background: var(--brand);
  color: var(--on-brand);
}

.ops-queues {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  gap: var(--space-3);
  margin: 0;
}

.stat {
  padding: var(--space-3);
  border-radius: var(--radius-md);
  box-shadow: var(--press-sm);
  background: var(--bg);
}

.stat dt {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-block-end: var(--space-1);
}

.stat dd {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* Fourteen days of signups and woofs as paired bars — heights are inline
   percentages of the fortnight's peak, colors from tokens only. */
.ops-bars {
  display: flex;
  align-items: flex-end;
  gap: var(--space-1);
}

.ops-bars__day {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
}

.ops-bars__pair {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 6rem;
  width: 100%;
  justify-content: center;
}

.ops-bars__bar {
  width: 0.375rem;
  min-height: 2px;
  border-radius: 2px 2px 0 0;
}

.ops-bars__bar--signups {
  background: var(--brand);
}

.ops-bars__bar--woofs {
  background: var(--text-faint);
}

.ops-bars__label {
  font-size: 0.625rem;
  color: var(--text-faint);
}

.ops-key {
  display: inline-block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 2px;
  vertical-align: baseline;
}

.ops-key--signups {
  background: var(--brand);
}

.ops-key--woofs {
  background: var(--text-faint);
}

/* A closed report's expanded body: stacked details rather than dog chips. */
.closed-report__body {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

/* A report row's two action rows: verdicts above, sanctions below. */
.ops-report__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  flex: none;
}

/* The account-visibility control inside Sign-in & privacy. */
.signin-row--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.visibility-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}



/* -------------------------------------------------------------------------
   Plan & billing
   ------------------------------------------------------------------------- */

/* Two cards, not the dashboard's auto-fit tiles: plan and meter belong side by
   side and should not reflow into a third column that does not exist. */
.statgrid--billing {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

/* "3 of 10" — the denominator is context, so it recedes rather than competing
   with the number that matters. */
.statcard__of {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0;
}

.statcard__big--spent {
  color: var(--danger);
}

/* The usage bar. Inset, because it reports a state rather than inviting a
   press — the same raised/pressed grammar as the rest of the redesign. */
.meterbar {
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--surface);
  box-shadow: var(--press-sm);
  overflow: hidden;
}

.meterbar__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--brand);
  transition: width var(--dur) var(--ease);
}

.meterbar__fill--spent {
  background: var(--danger);
}

@media (prefers-reduced-motion: reduce) {
  .meterbar__fill { transition: none; }
}

/* What Plus includes. Bones rather than bullets — the app's own mark, and it
   keeps a plain list from reading like fine print. */
.plusfeatures {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.plusfeatures li {
  position: relative;
  padding-inline-start: var(--space-5);
  font-size: 0.9375rem;
}

.plusfeatures li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.55em;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: var(--radius-full);
  background: var(--brand);
}

/* The two prices sit on one row and wrap together, so the yearly option is
   never stranded below the fold on a narrow screen. */
.pricerow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-block-end: var(--space-3);
}

.pricerow__note {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0;
}

/* The interval, quieter than the amount inside the same button. */
.btn__unit {
  font-weight: 500;
  opacity: 0.85;
}

.billing__off {
  margin-block-start: var(--space-4);
}


/* Ops revenue: the assistant-usage split. A small table of two comparable
   numbers and their total — tabular figures so the digits line up. */
.usagesplit {
  display: grid;
  gap: var(--space-2);
  margin-block-end: var(--space-4);
}

.usagesplit__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding-block-end: var(--space-2);
  border-block-end: 1px solid var(--surface-3);
}

.usagesplit__row--total {
  border-block-end: 0;
  padding-block-end: 0;
  font-weight: 700;
}

.usagesplit__label {
  color: var(--text-muted);
  font-size: 0.9375rem;
}

.usagesplit__row--total .usagesplit__label {
  color: var(--text);
}

.usagesplit__value {
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;
  font-size: 1.125rem;
}

/* A stat that wants looking at — past-due cards are money already lost unless
   someone acts. Inset ring rather than colour alone; the number goes red too. */
.statcard--attention {
  box-shadow: var(--raise), inset 0 0 0 1px var(--danger);
}


/* -------------------------------------------------------------------------
   Stacked cards in a shell
   ------------------------------------------------------------------------- */

/* .card carries no vertical margin, so consecutive cards inside a shell sat
   flush against each other. The ops pages worked around it with an inline
   margin-block-end on every card, which meant any new page that forgot the
   inline style came out squashed — which is exactly what happened to the
   revenue tab.
   
   Sibling margins collapse (.card has no padding or border of its own at this
   level), so this is idempotent with the inline styles already in place rather
   than doubling the gap. */
.auth-shell > .card + .card,
.auth-shell > .statgrid + .card,
.auth-shell > .card + .statgrid {
  margin-block-start: var(--space-4);
}

/* A standing notice above a console page's content — the "Stripe isn't
   configured" line and its kind. Needs its own breathing room because
   .field__hint is a caption style with no margins of its own. */
.ops-notice {
  margin-block: var(--space-4) var(--space-5);
}


/* The staff-only suspended banner, above the profile's two-column grid. */
.profile-suspended {
  margin-block-end: var(--space-5);
}


/* A withdrawn reply — a suspended author's comment, kept as a tombstone so the
   thread around it still reads. Quiet and italic: it is a note about absent
   content, not content. See docs/SUSPENSION.md. */
.comment--withdrawn .comment__body {
  font-style: italic;
  color: var(--text-faint);
}


/* -------------------------------------------------------------------------
   Onboarding: the setup wizard
   One card per screen inside .auth-shell. The grammar as everywhere: the
   progress track is inset (a well being filled), done segments fill with
   brand, controls are raised, the skip is quiet text — an escape hatch,
   not a competing action.
   ------------------------------------------------------------------------- */

.wizard__stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.wizard__stack--center {
  align-items: center;
  text-align: center;
}

.wizard__title {
  font-size: 1.5rem;
  letter-spacing: -0.015em;
}

.wizard__sub {
  margin-block-start: var(--space-1);
  font-size: 0.9375rem;
  color: var(--text-muted);
}

.wizard-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.wizard-top__label {
  font-weight: 800;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.wizard-skipform {
  display: contents;
}

.wizard-skip {
  border: 0;
  background: none;
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.wizard-skip:hover {
  color: var(--text-muted);
}

.wizard-skip:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.wizard-progress {
  display: flex;
  gap: var(--space-2);
}

.wizard-progress__seg {
  height: 6px;
  flex: 1;
  border-radius: var(--radius-full);
  background: var(--bg-tint);
  box-shadow: inset 1px 1px 3px var(--shade);
}

.wizard-progress__seg--done {
  background: var(--brand);
  box-shadow: none;
}

/* The handle field: an inset well whose leading text is the public URL
   prefix, with a borderless input riding inside it. */
.urlprefix {
  display: flex;
  align-items: center;
  width: 100%;
  padding-inline-start: 1rem;
  background: var(--well);
  border: 0;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
  border-radius: calc(var(--radius-md) + 2px);
  box-shadow: var(--well-shadow);
  transition: box-shadow var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.urlprefix__host {
  font-size: 0.9375rem;
  color: var(--text-faint);
  white-space: nowrap;
}

.urlprefix__input {
  flex: 1;
  min-width: 0;
  padding: 0.875rem 1rem 0.875rem var(--space-1);
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text);
}

.urlprefix__input:focus {
  outline: none;
}

.urlprefix:focus-within {
  background: var(--surface-bright);
  border-top-color: color-mix(in srgb, var(--brand) 30%, var(--border));
  border-left-color: color-mix(in srgb, var(--brand) 30%, var(--border));
  box-shadow: inset 2px 2px 5px var(--shade-brand), 0 0 0 2px var(--brand-ring);
}

.handle-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.handle-check--ok {
  color: var(--success);
  font-weight: 600;
}

.handle-check--bad {
  color: var(--danger);
  font-weight: 600;
}

.handle-check__dot {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--brand);
  color: var(--on-brand);
}

.handle-check__dot svg {
  width: 12px;
  height: 12px;
}

/* Step 2's nearby-parks glimpse — a raised aside, chips inside. */
.wizard-preview {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.wizard-preview__label {
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* One added dog in the wizard's stack. */
.wizard-dogrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.wizard-dogrow__text {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.wizard-dogrow__name {
  font-weight: 800;
}

.wizard-dogrow__meta {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* "Add another dog": a dashed disclosure — inert-looking on purpose, the
   raised primary beneath it ("That's everyone") is the main path. */
.btn--dashed {
  background: var(--surface-2);
  border: 1.5px dashed var(--border-strong);
  box-shadow: none;
  color: var(--text);
  font-weight: 700;
}

.btn--dashed:hover {
  border-color: var(--brand);
  box-shadow: none;
}

.wizard-adder > summary {
  list-style: none;
}

.wizard-adder > summary::-webkit-details-marker {
  display: none;
}

.wizard-adder[open] > summary {
  margin-block-end: var(--space-4);
}

/* The handoff card's paw badge. */
.wizard-badge {
  display: inline-grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 22px;
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--raise-brand-sm);
}

.wizard-badge svg {
  width: 34px;
  height: 34px;
}


/* -------------------------------------------------------------------------
   Onboarding: the feed checklist
   The getting-started card at the top of the feed column, its collapsed
   strip under the composer, and the one-time "all done" state. Done rows
   press in (settled), undone rows stay raised (actionable) — the same
   grammar as everything else.
   ------------------------------------------------------------------------- */

.checklist-card {
  border-top: 3px solid var(--brand);
  margin-block-end: var(--space-4);
}

.checklist {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.checklist__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.checklist__intro {
  flex: 1;
  min-width: 0;
}

.checklist__title {
  font-size: 1.125rem;
  letter-spacing: -0.01em;
}

.checklist__subtitle {
  margin-block-start: var(--space-1);
  font-size: 0.875rem;
  color: var(--text-muted);
}

.checklist__gauge {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  flex: none;
}

.checklist__count {
  font-size: 0.8125rem;
  font-weight: 800;
  color: var(--brand);
}

.checklist__meter {
  width: 96px;
}

.checklist__rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.checklist__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.checklist__row--done {
  background: var(--well);
  box-shadow: var(--well-shadow);
}

.checklist__row--done .checklist__label {
  text-decoration: line-through;
  color: var(--text-faint);
  font-weight: 600;
}

.checklist__tick {
  width: 26px;
  height: 26px;
  flex: none;
  border: 2.5px solid var(--border-strong);
  border-radius: var(--radius-full);
}

.checklist__tick--done {
  display: inline-grid;
  place-items: center;
  border: 0;
  background: var(--brand);
  color: var(--on-brand);
}

.checklist__tick--done svg {
  width: 15px;
  height: 15px;
}

.checklist__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.checklist__label {
  font-weight: 700;
  font-size: 0.9375rem;
}

.checklist__sub {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* The collapsed residue, under the composer. */
.checklist-strip {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block-end: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.checklist-strip__meter {
  width: 110px;
  flex: none;
}

.checklist-strip__text {
  flex: 1;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text);
}

.checklist-strip__show {
  border: 0;
  background: none;
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--brand);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.checklist-strip__show:hover {
  color: var(--brand-hover);
}

.checklist-strip__show:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* All four done: one last friendly row, then it sees itself out. */
.checklist-done {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.checklist-done__badge {
  width: 38px;
  height: 38px;
  border-radius: 13px;
}

.checklist-done__badge svg {
  width: 20px;
  height: 20px;
}

/* "Show me around": the handoff's one-shot outline on the checklist card. */
.tour-outline {
  animation: tour-pulse 1.1s var(--ease) 2;
}

@keyframes tour-pulse {
  0%, 100% { box-shadow: var(--raise); }
  50% { box-shadow: var(--raise), 0 0 0 6px var(--brand-ring); }
}

@media (prefers-reduced-motion: reduce) {
  .tour-outline {
    animation: none;
    box-shadow: var(--raise), 0 0 0 4px var(--brand-ring);
  }
}

/* The rail's needs-a-photo rows ride existing card styles; only the row. */
.rail-dogrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.rail-dogrow + .rail-dogrow {
  margin-block-start: var(--space-3);
}

.rail-dogrow__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.rail-dogrow__name {
  font-weight: 800;
  font-size: 0.9375rem;
}

.rail-dogrow__hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* The hint doubling as the one-tap uploader's label. */
.rail-dogrow__hint--tap {
  color: var(--brand);
  font-weight: 600;
  cursor: pointer;
}

.rail-dogrow__hint--tap:hover {
  color: var(--brand-hover);
}

.rail-dogrow__hint--tap:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* The checklist's Upload label: a .btn already, just needs the pointer and
   a focus ring routed from the hidden input. */
.checklist__upload {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
}

.checklist__uploadlabel {
  cursor: pointer;
}

.checklist__uploadlabel:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}


/* -------------------------------------------------------------------------
   Depth & layers (redesign D1)
   Tier 1 ground is painted by body::before (section 2). This section is the
   Tier 3 / Tier 4 vocabulary plus the nav pills, the segmented theme toggle
   and the floating tab bar. Modifier classes live here, last in the cascade,
   so they beat the base component shadows they override at equal specificity.
   ------------------------------------------------------------------------- */

/* ---- Tier 3: floating glass ---- */
.glass,
.strip-chip {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius-lg);
}

/* Over a green/photo hero: white smoke, white type, in both themes. */
.glass--on-hero {
  background: var(--glass-hero-bg);
  -webkit-backdrop-filter: var(--glass-hero-blur);
  backdrop-filter: var(--glass-hero-blur);
  border: 1px solid var(--glass-hero-border);
  color: #ffffff;
  box-shadow: none;
}

/* Drop-only shadow for anything detached from the surface (no blur). */
.float {
  box-shadow: var(--float);
}

.float--sm {
  box-shadow: var(--float-sm);
}

/* NEVER nest position:fixed inside .glass / .strip-chip / .glass--on-hero:
   backdrop-filter makes the element a containing block (see .tabbar). And a
   blur nested inside a blur only sees its parent's backdrop, which is why
   .menu__panel stays opaque. */

/* ---- Tier 2 additions ---- */
.surface--lifted {
  background: var(--surface-bright);
  box-shadow: var(--raise-lift);
}

.surface--tinted {
  background: var(--surface-tint);
  box-shadow: var(--raise-tint);
}

/* Opt-in hover raise for grounded cards (the spec's "slight raise"). */
.lift-hover {
  transition: box-shadow var(--dur) var(--ease), translate var(--dur) var(--ease);
}

.lift-hover:hover {
  box-shadow: var(--raise-lg);
  translate: 0 -2px;
}

/* ---- Tier 4: deck (stacked cards). `.stack` is the rhythm utility in
   section 3, hence the name. The peek is ::before, so no extra markup. ---- */
.deck {
  position: relative;
  isolation: isolate;          /* keeps the z-index:-1 peek inside the deck */
}

.deck::before {
  content: "";
  position: absolute;
  inset: -6px -6px 6px 8px;    /* top right bottom left — the spec's offsets */
  z-index: -1;
  border-radius: inherit;
  background: var(--peek-bg);
  box-shadow: var(--peek-shadow);
  pointer-events: none;
}

.deck > .deck__front,
.deck > .card {
  position: relative;
  background: var(--surface-bright);
  box-shadow: var(--deck-shadow);
}

/* ---- Hero + straddling strip ----
   .hero is the relative wrapper; .hero__card the coloured surface (screens
   set --hero-h and --hero-bg); .hero__strip hangs frosted .strip-chips over
   the card's bottom edge. On phones the strip becomes a plain 2x2 grid of
   bright cards below the hero. */
.hero {
  --strip-overhang: 2.5rem;     /* 40px below the card's bottom edge */
  --strip-min-h: 5.75rem;       /* chips are at least this tall */
  position: relative;
  margin-block-end: 4rem;       /* the spec's 64px: overhang + breathing room */
}

.hero__card {
  position: relative;
  overflow: hidden;             /* sheen/photo stay inside the radius */
  min-height: var(--hero-h, 12.5rem);
  padding: 1.75rem 2.125rem;    /* 28px 34px */
  border-radius: 1.375rem;      /* r22 */
  background: var(--hero-bg, linear-gradient(140deg, #3f7d55, #245e3a 60%, #1c4b30));
  color: #ffffff;
  box-shadow: var(--deck-shadow);
}

/* Layering caution made structural: with a strip present the hero's own
   content is padded clear of the chips' in-card portion (chip height minus
   overhang, plus a gap). */
.hero:has(.hero__strip) .hero__card {
  padding-block-end: calc(var(--strip-min-h) - var(--strip-overhang) + var(--space-5));
}

.hero__sheen {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 78% 30%, rgb(255 255 255 / 0.14), transparent 45%);
  pointer-events: none;
}

.hero__actions {
  position: absolute;
  right: 1.75rem;
  top: 1.5rem;
  display: flex;
  gap: var(--space-2);
  z-index: 4;
}

.hero__strip {
  position: absolute;
  inset-inline: 2.125rem;       /* 34px */
  bottom: calc(-1 * var(--strip-overhang));
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.875rem;                /* 14px */
}

.strip-chip {
  min-height: var(--strip-min-h, 5.75rem);
  padding: 1rem 1.125rem;       /* 16px 18px */
  border-radius: 1.125rem;      /* r18 */
  color: var(--text);
}

.strip-chip__label {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.strip-chip__value {
  font-size: 1.375rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.strip-chip__sub {
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--text-faint);
}

@media (width < 48rem) {
  .hero {
    margin-block-end: 0;
  }

  .hero:has(.hero__strip) .hero__card {
    padding-block-end: 1.75rem;
  }

  .hero__actions {
    position: static;
    margin-block-start: var(--space-3);
    flex-wrap: wrap;
  }

  .hero__strip {
    position: static;
    margin-block-start: var(--space-4);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
  }

  .strip-chip {
    background: var(--surface-bright);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: 0;
    box-shadow: var(--raise-lift);
  }
}

/* ---- Nav pills (top nav, >= 48rem). Text-only, 58x40, raised at rest; the
   current destination pressed into a green bed. ---- */
.pills {
  display: none;
  gap: var(--space-2);
}

@media (width >= 48rem) {
  .pills {
    display: flex;
  }
}

.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 3.625rem;          /* 58px */
  height: 2.5rem;               /* 40px */
  padding: 0 0.875rem;
  border-radius: 0.75rem;       /* the spec's 12px */
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.pill:hover {
  color: var(--text);
}

.pill[aria-current] {
  background: var(--brand-soft);
  box-shadow: var(--press-active);
  color: var(--brand-text);
  font-weight: 800;
}

.pill--info {
  background: var(--info-soft);
  color: var(--info);
  box-shadow: var(--raise-info-sm);
}

.pill svg {
  width: 1rem;
  height: 1rem;
}

/* ---- Segmented theme toggle: an inset well holding two radio segments; the
   checked one is a raised tile. State is aria-checked, set by woofline.js. ---- */
.seg-toggle {
  display: inline-flex;
  gap: 2px;
  height: 2.75rem;              /* 44px */
  padding: 4px;
  border-radius: var(--radius-md);
  background: var(--well);
  box-shadow: var(--well-shadow);
  flex: none;
}

.seg-toggle__opt {
  display: grid;
  place-items: center;
  width: 2.125rem;              /* 34px */
  height: 2.25rem;              /* 36px */
  padding: 0;
  border: 0;
  border-radius: 0.625rem;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.seg-toggle__opt svg {
  width: 1.125rem;
  height: 1.125rem;
}

.seg-toggle__opt[aria-checked="true"] {
  background: var(--seg-active-bg);
  color: var(--seg-active-fg);
  box-shadow: var(--raise-sm);
  cursor: default;
}

/* ---- Floating tab bar (phones) + FAB ---- */
.tabbar--float {
  inset-inline: 0.75rem;
  bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  height: 4.5rem;               /* 72px */
  padding: 0.375rem 0.5rem;
  border-radius: 1.5rem;
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  align-items: stretch;
}

/* Inside glass, tabs are flat: a raised tile cannot cast a pair on smoke. */
.tabbar--float .tabbar__tab {
  background: transparent;
  box-shadow: none;
  min-height: 0;
  justify-content: center;
}

.tabbar--float .tabbar__tab[aria-current="page"] {
  box-shadow: none;
  color: var(--brand-text);
  font-weight: 800;
}

.tabbar--float .tabbar__tab .avatar {
  --size: 1.5rem;
  border-width: 1px;
  box-shadow: none;
}

.tabbar__tab--fab {
  margin-block-start: -1.25rem; /* the disc pokes 14px above the bar */
}

.fab {
  display: grid;
  place-items: center;
  width: 3.25rem;               /* 52px */
  height: 3.25rem;
  border-radius: 1.125rem;      /* r18 */
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--lift-brand);
}

.fab svg {
  width: 1.5rem;
  height: 1.5rem;
}

.tabbar__tab--fab:active .fab {
  box-shadow: var(--press-brand-sm);
}

/* ---- Phone app bar: logo · search · bell · theme · avatar ---- */
.header__search-link {
  display: inline-grid;
}

@media (width >= 48rem) {
  .header__search-link {
    display: none;
  }
}

@media (width < 48rem) {
  /* Messages stays in the avatar menu on phones, with its count badge. */
  .header__dm {
    display: none;
  }
}

/* Wordmark: always for visitors; for members only where the full chrome fits. */
@media (width < 60rem) {
  .header--auth .brand__name {
    display: none;
  }
}

/* ---- Shared furniture the screen phases build on ---- */
.page-title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-end: var(--space-5);
}

.page-title h1 {
  margin: 0;
}

.page-title__action {
  margin-inline-start: auto;
}

.count-badge {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding-inline: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--like);
  color: #ffffff;
  font-weight: 800;
  font-size: 0.75rem;
}

.group-label {
  display: flex;
  gap: 8px;
  align-items: baseline;
  margin: 0 0 var(--space-3);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.group-label--accent {
  color: var(--accent);
}

.group-label__aside {
  margin-inline-start: auto;
  letter-spacing: 0;
  text-transform: none;
  font-weight: 700;
  color: var(--text-faint);
}

.avstack {
  display: inline-flex;
}

.avstack > * + * {
  margin-inline-start: -0.5rem;
}

.btn--accent {
  background: var(--accent);
  color: #ffffff;
  box-shadow: var(--raise-accent-sm);
}

.btn--info {
  background: var(--info);
  color: #ffffff;
  box-shadow: var(--raise-info-sm);
}

.glass-btn {
  width: 42px;
  height: 42px;
  border-radius: 13px;
  display: grid;
  place-items: center;
  border: 0;
  color: #ffffff;
  cursor: pointer;
}

.glass-btn svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* ---- Motion ---- */
@keyframes floaty {
  from {
    translate: 0 0;
  }
  to {
    translate: 0 -6px;
  }
}

.floaty {
  animation: floaty 3s var(--ease) infinite alternate;
}

@media (prefers-reduced-motion: reduce) {
  .floaty {
    animation: none;
  }

  .pill,
  .seg-toggle__opt,
  .lift-hover {
    transition: none;
  }

  .lift-hover:hover {
    translate: none;
  }
}


/* -------------------------------------------------------------------------
   Scene: the blurred-feed backdrop and the glass auth card (redesign D3)
   Login, register, password reset and the setup wizard sit on a dimmed,
   blurred skeleton of the feed — Tier 3 over Tier 1. The backdrop is a fixed
   layer at z-index -1: above the ground's own fixed layer (later in the DOM),
   below every in-flow element, so the header, banners and the card all paint
   over it. Decorative only — no text, no controls, aria-hidden.
   ------------------------------------------------------------------------- */

.scene {
  position: relative;
  display: grid;
  place-items: center;
  min-height: calc(100dvh - 12rem);
  padding-block: var(--space-6);
}

body:has(.scene) .footer {
  display: none;
}

.backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.backdrop__feed {
  position: absolute;
  left: 50%;
  top: 4rem;
  width: min(40rem, 92vw);
  translate: -50% 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  filter: blur(8px);
  transform: scale(1.06);
}

.backdrop__bar {
  height: 3.25rem;
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.backdrop__card {
  display: grid;
  grid-template-columns: 2.75rem 1fr;
  gap: 0.75rem 1rem;
  align-items: start;
  padding: 1.25rem;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--raise);
}

.backdrop__face {
  grid-row: 1 / span 3;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-full);
  background: var(--surface-2);
}

.backdrop__line {
  height: 0.8rem;
  border-radius: var(--radius-full);
  background: var(--surface-3);
}

.backdrop__line--short {
  width: 40%;
}

.backdrop__photo {
  height: 9rem;
  border-radius: var(--radius-md);
  background: var(--surface-3);
}

.backdrop__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

/* The column on the scrim: white head, the paw mark, the card, the footer line. */
.auth-shell--scene {
  position: relative;
  max-width: 23.75rem;          /* the spec's 380px */
  padding-block: var(--space-4);
}

.auth-mark {
  display: inline-grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  margin-block-end: var(--space-4);
  border-radius: 1.125rem;
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: 0 18px 34px -12px color-mix(in srgb, var(--brand) 70%, transparent);
}

.auth-mark svg {
  width: 1.625rem;
  height: 1.625rem;
}

.auth-shell__head--onscrim h1 {
  color: #ffffff;
}

.auth-shell__head--onscrim p {
  color: rgb(255 255 255 / 0.75);
}

.auth-alt--onscrim {
  color: rgb(255 255 255 / 0.75);
}

.auth-alt--onscrim a {
  color: #ffffff;
  font-weight: 700;
}

.auth-card {
  border-radius: 1.375rem;      /* r22 */
  box-shadow: var(--float-lg), var(--glass-rim);
  text-align: start;
}

.auth-card.glass {
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  backdrop-filter: blur(22px) saturate(1.2);
}

.auth-card .card__body {
  padding: 1.625rem;
}

/* Fields on glass: a translucent well with the spec's sage-cast inset, no lip.
   The :not() selectors outrank the .field input[type=…] rules in section 11. */
.auth-card .form-control,
.auth-card .field input:not([type="checkbox"]):not([type="file"]),
.auth-card .field textarea {
  background: var(--glass-well);
  border-color: transparent;
  box-shadow: var(--glass-well-shadow);
}

.auth-card .btn--primary {
  min-height: 3rem;
  box-shadow: var(--lift-brand);
}

.auth-card .auth-google {
  min-height: 3rem;
  box-shadow: var(--raise-sm);
}

.field__labelrow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
}

.field__labelrow a {
  font-size: 0.8125rem;
  font-weight: 600;
}


/* -------------------------------------------------------------------------
   Scene: the setup wizard's split glass modal (redesign D4)
   The step form on frosted glass at left; a green brand pane at right that
   previews what the step builds (the live profile card, the city's nearby
   parks, what pings you, the dog). Stacks on phones.
   ------------------------------------------------------------------------- */

.modal-split {
  width: 100%;
  max-width: 40rem;
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: var(--float-lg);
}

.modal-split__form {
  padding: 1.75rem 1.625rem;
}

.modal-split__form.glass {
  border: 0;
  border-inline-end: 1px solid var(--glass-border);
  border-radius: 0;
  box-shadow: none;
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  backdrop-filter: blur(20px) saturate(1.2);
}

.modal-split__form .wizard__title {
  font-size: 1.3125rem;
}

/* Fields on glass, as on the auth card (see .auth-card .form-control). */
.modal-split__form .form-control,
.modal-split__form .field input:not([type="checkbox"]):not([type="file"]),
.modal-split__form .field textarea,
.modal-split__form .urlprefix {
  background: var(--glass-well);
  border-color: transparent;
  box-shadow: var(--glass-well-shadow);
}

.modal-split__form .btn--primary {
  min-height: 2.875rem;
  box-shadow: var(--lift-brand);
}

.modal-split__brand {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-4);
  padding: 3.5rem 1.375rem 1.625rem;
  color: #ffffff;
  background: linear-gradient(160deg, var(--brand), var(--brand-hover));
}

.modal-split__label {
  position: absolute;
  top: 1.375rem;
  left: 1.375rem;
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.7);
}

.modal-split__caption {
  margin: 0;
  max-width: 22ch;
  font-size: 0.75rem;
  line-height: 1.5;
  color: rgb(255 255 255 / 0.8);
}

/* The white card floating on the brand pane. */
.preview-card {
  min-width: 12rem;
  max-width: 100%;
  padding: 1.375rem 1.25rem;
  border-radius: var(--radius-lg);
  background: var(--surface-bright);
  color: var(--text);
  box-shadow: 0 30px 50px -18px rgb(0 0 0 / 0.45);
}

.avatar.preview-card__face {
  --size: 4.75rem;
  display: block;
  margin: 0 auto 0.75rem;
  border-radius: 1.5rem;
  border: 0;
  box-shadow: inset 3px 3px 7px var(--shade), inset -2px -2px 5px var(--shade-hi);
}

.preview-card__name {
  margin: 0;
  font-weight: 800;
  font-size: 1.0625rem;
  overflow-wrap: anywhere;
}

.preview-card__handle,
.preview-card__hint {
  margin: 2px 0 0;
  font-size: 0.75rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.preview-card > .chip {
  margin-block-start: 0.625rem;
}

.preview-card--list {
  list-style: none;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  text-align: start;
  font-size: 0.875rem;
}

.preview-card--list li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.preview-card--list svg {
  width: 1rem;
  height: 1rem;
  flex: none;
  color: var(--brand);
}

/* The city step's nearby-parks glimpse lives inside the white card. */
.preview-card .wizard-preview {
  padding: 0;
  background: transparent;
  box-shadow: none;
}

.preview-card #city-preview:empty + .preview-card__hint,
.preview-card #city-preview + .preview-card__hint {
  margin-block-start: var(--space-2);
}

@media (width < 48rem) {
  .modal-split {
    grid-template-columns: 1fr;
  }

  .modal-split__form.glass {
    border-inline-end: 0;
  }

  .modal-split__brand {
    padding: 2.5rem 1.375rem 1.5rem;
  }

  .modal-split__label {
    position: static;
  }
}


/* -------------------------------------------------------------------------
   Notifications (redesign D5)
   Title row with the red count and the floating "Mark all read"; filter
   pills; New rows tinted-lifted, Earlier rows grounded; an action badge on
   the avatar by kind; a trailing thumbnail when the woof has a photo; Follow
   back in place. The rail carries the real analogs of the spec's follow
   requests and quiet hours — care invitations and the email toggle.
   ------------------------------------------------------------------------- */

.seg--pills .seg__btn {
  border-radius: var(--radius-full);
  padding: 0.6rem 1rem;
  font-size: 0.875rem;
}

.notif-filters {
  margin-block-end: var(--space-4);
}

.notif-readall svg {
  width: 1rem;
  height: 1rem;
}

.notif__who {
  position: relative;
  flex: none;
}

.notif__badge {
  position: absolute;
  right: -3px;
  bottom: -3px;
  display: grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: var(--radius-full);
  border: 2px solid var(--surface-bright);
  background: var(--brand);
  color: #ffffff;
}

.notif__badge svg {
  width: 0.75rem;
  height: 0.75rem;
}

.notif__badge--like {
  background: var(--like);
}

.notif__badge--reply,
.notif__badge--mention,
.notif__badge--message,
.notif__badge--recap {
  background: var(--info);
}

.notif__badge--park_published {
  background: var(--accent);
}

.notif__badge--moderation_warning {
  background: var(--danger);
}

.notif__thumb {
  width: 3.25rem;
  height: 3.25rem;
  flex: none;
  border-radius: 12px;
  object-fit: cover;
  box-shadow: 0 0 0 3px var(--surface-bright);
}

.notif__action {
  flex: none;
}

.pings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin-block: var(--space-3);
}

/* The title row's controls: one unit, so they wrap together on phones. */
.page-title__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: auto;
}

.rail .invite {
  border: 0;
  box-shadow: var(--raise-sm);
}


/* -------------------------------------------------------------------------
   Post card + post detail (redesign D6)
   The action bar reads ♥ · Share · ⋯ · bookmark, all raised chips; the reply
   thread's toggle beneath it is the comment chip. On its own page the woof is
   the lifted-and-lit surface with an engagement row, the thread open and the
   reply composer pinned on the floating tier; the rail carries the author
   and more of their woofs.
   ------------------------------------------------------------------------- */

.post__permalink {
  color: inherit;
  text-decoration: none;
}

.post__permalink:hover .post__time {
  text-decoration: underline;
}

.post__actions {
  gap: var(--space-2);
  margin-inline-start: 0;
  flex-wrap: wrap;
}

.like {
  border-radius: 12px;
  padding: 0.5rem 0.875rem;
  font-weight: 700;
}

/* The liked state: the heart tint surface, not only a red glyph. */
.like.is-liked {
  background: var(--like-soft);
  box-shadow: 4px 4px 8px color-mix(in oklch, var(--like) 30%, var(--shade)),
    -3px -3px 6px var(--shade-hi);
}

.like.is-liked:active {
  box-shadow: var(--press-sm);
}

/* Share: a button dressed as the quiet action link, hidden until the script
   confirms the browser can share or copy — no dead buttons, ever. */
.post__share {
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
  padding: 0.5rem 0.75rem;
  color: var(--text-faint);
}

[data-share] {
  display: none;
}

html.has-share [data-share] {
  display: inline-flex;
}

/* ⋯ : a 38px raised tile; its panel is the standard opaque menu panel. */
.post__more > summary {
  display: inline-grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  padding: 0;
  border-radius: 12px;
  color: var(--text-muted);
}

.post__more > summary svg {
  width: 1.125rem;
  height: 1.125rem;
}

.menu__panel button.menu__item {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.menu__item--danger {
  color: var(--danger);
}

.menu__panel .post__delete,
.menu__panel .dm-start {
  display: contents;
}

.menu__panel .inline-error {
  padding: 0 var(--space-3);
  font-size: 0.8125rem;
}

/* The bookmark: right-aligned, icon-only, pressed into green when saved. */
.save-btn {
  margin-inline-start: auto;
  display: inline-grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.save-btn svg {
  width: 1.125rem;
  height: 1.125rem;
}

.save-btn:hover {
  color: var(--text);
}

.save-btn[aria-pressed="true"] {
  background: var(--brand-soft);
  color: var(--brand-text);
  box-shadow: var(--press-brand-sm);
}

.save-btn[aria-pressed="true"] svg {
  fill: currentColor;
}

.save-btn[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* The reply thread's toggle is the bar's comment chip. */
.thread {
  margin-block-start: var(--space-2);
  border-block-start: 0;
  padding-block-start: 0;
}

.thread__toggle {
  margin-inline-start: 0;
  padding: 0.5rem 0.875rem;
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  color: var(--text-muted);
  font-weight: 700;
}

.thread__toggle:hover {
  background: var(--surface);
  color: var(--text);
}

.thread[open] > .thread__toggle {
  box-shadow: var(--press-sm);
  color: var(--text);
}

/* Replies are grounded bubbles; yours is the tinted one. */
.comment__bubble {
  min-width: 0;
  padding: 0.75rem 0.9375rem;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--raise-sm);
}

.comment--mine .comment__bubble {
  background: var(--surface-tint);
  box-shadow: var(--raise-tint);
}

.comment__delete {
  align-self: start;
}

/* On its own page. */
.post--solo .post__photo img {
  max-height: 21.25rem;
}

.post__engage {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
  padding-block: var(--space-3);
  border-block-end: 1px solid var(--border);
  font-size: 0.8125rem;
  color: var(--text-faint);
}

.post__engage strong {
  color: var(--text);
}

.post__reactors .avatar {
  --size: 1.375rem;
  border-width: 2px;
  box-shadow: none;
}

/* The reply composer stays within reach while you read the thread. */
.comment-form--pinned {
  position: sticky;
  bottom: calc(var(--space-3) + var(--tabbar-clearance));
  z-index: var(--z-sticky);
  margin-block-start: var(--space-4);
  padding: 0.75rem 1rem;
  border-radius: 16px;
}

.comment-form--pinned .form-control,
.comment-form--pinned .field textarea {
  min-height: 2.75rem;
  background: var(--glass-well);
  border-color: transparent;
  box-shadow: var(--glass-well-shadow);
}

/* The rail: the author, then more of their woofs. */
.authorcard .card__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
}

.avatar.authorcard__face {
  --size: 4rem;
  border-radius: var(--radius-lg);
}

.authorcard__name {
  margin: 0;
  font-weight: 800;
}

.authorcard__meta {
  margin: 0 0 var(--space-2);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.authorcard__when {
  margin-block-start: var(--space-2);
}

.more-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.625rem;
  margin-block-end: var(--space-3);
}

.more-grid__tile {
  display: block;
  height: 5rem;
  overflow: hidden;
  border-radius: 12px;
  background: var(--well);
  box-shadow: var(--well-shadow);
  color: var(--text-muted);
  text-decoration: none;
}

.more-grid__tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.more-grid__text {
  display: block;
  max-height: 100%;
  padding: 0.5rem;
  font-size: 0.75rem;
  line-height: 1.35;
  overflow: hidden;
}


/* -------------------------------------------------------------------------
   Home feed (redesign D7)
   Ambient ground under everything; the composer is the one lifted-and-lit
   surface; posts stay grounded (D6); the week-outdoors ring rides its green
   plinth; the new-woofs pill drops in on the floating tier. On phones the
   composer is a one-line bar until it has focus.
   ------------------------------------------------------------------------- */

.composer .form-control {
  min-height: 3.5rem;
}

.feed-new__pill {
  height: auto;
  min-width: 0;
  padding: 0.5rem 0.875rem;
  border-radius: var(--radius-full);
  color: var(--brand-text);
  font-size: 0.8125rem;
  font-weight: 800;
  box-shadow: var(--float-brand);
}

.feed-new__pill:hover {
  color: var(--brand-text);
}

@media (width < 48rem) {
  /* One line until it has focus: the well shrinks and the chips row hides,
     leaving the prompt and the Woof button — the spec's phone composer. */
  .composer:not(:focus-within) .form-control {
    /* height, not min-height: the widget's rows attribute sets the height. */
    height: 3rem;
    min-height: 3rem;
    resize: none;
  }

  .composer:not(:focus-within) .composer__meta > :not(.composer__submit) {
    display: none;
  }

  /* ...and the prompt and the Woof button share the one line. Anything that
     can appear beneath (a staged photo, a field error) takes its own row. */
  .composer:not(:focus-within) .composer__fields {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }

  .composer:not(:focus-within) .composer__fields > .form-control {
    flex: 1 1 6rem;
    min-width: 0;
  }

  .composer:not(:focus-within) .composer__meta {
    margin-block-start: 0;
  }

  .composer:not(:focus-within) .composer__staged,
  .composer:not(:focus-within) .field__error,
  .composer:not(:focus-within) .inline-error {
    flex-basis: 100%;
  }
}


/* -------------------------------------------------------------------------
   Profile (redesign D8) — and the hero-bar vocabulary the dog dashboard
   shares: .hero__face / __identity / __name / __sub / __bio / __links /
   __follow / __more on the .hero__card, stat chips straddling its edge.
   ------------------------------------------------------------------------- */

.hero--profile {
  --strip-min-h: 4.75rem;       /* number-over-label chips are shorter */
}

.hero__card--profile {
  --hero-h: 12.5rem;
  display: flex;
  align-items: center;
  gap: 1.75rem;
  padding: 1.875rem 2.125rem;
}

.hero__card--profile .hero__sheen {
  background: radial-gradient(circle at 82% 20%, rgb(255 255 255 / 0.14), transparent 45%);
}

.avatar.hero__face {
  --size: 8rem;                 /* 128px, rounded square */
  position: relative;
  z-index: 2;
  flex: none;
  border: 0;
  border-radius: 2rem;
  box-shadow: inset 3px 3px 8px rgb(0 0 0 / 0.15), 0 18px 30px -14px rgb(0 0 0 / 0.5);
}

.hero__identity {
  position: relative;
  z-index: 2;
  min-width: 0;
  padding-inline-end: 14rem;    /* room for the controls cluster top-right */
}

.hero__name {
  margin: 0;
  font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.375rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  color: #ffffff;
}

.hero__sub {
  margin: var(--space-2) 0 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: rgb(255 255 255 / 0.82);
}

.hero__bio {
  margin: var(--space-3) 0 0;
  max-width: 52ch;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: rgb(255 255 255 / 0.92);
  overflow-wrap: anywhere;
}

.hero__links {
  display: flex;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
}

.hero__links .btn-icon {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 10px;
}

.hero__links .btn-icon:hover,
.hero__actions .btn.glass--on-hero:hover,
.hero__actions .btn-icon.glass--on-hero:hover {
  background: rgb(255 255 255 / 0.28);
  color: #ffffff;
  box-shadow: none;
}

.hero__actions .btn.glass--on-hero {
  font-weight: 800;
}

/* The white CTA on the green: Follow. Following reads as glass. The white is
   a literal, not --surface-bright: the hero stays green in both themes, and
   the dark theme's "bright" surface would paint this dark-on-green. */
.hero__follow .btn--primary {
  background: #fdfcf9;
  color: #1c4b30;
  box-shadow: 0 14px 26px -10px rgb(0 0 0 / 0.4);
}

.hero__follow .btn--ghost {
  background: rgb(255 255 255 / 0.16);
  border: 1px solid rgb(255 255 255 / 0.25);
  color: #ffffff;
  box-shadow: none;
}

.hero__more > summary {
  display: inline-grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border-radius: 12px;
}

/* Mute | Block ride inside the more-menu as plain items. */
.hero__more .menu__panel .silence-pair {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.hero__more .menu__panel .silence-pair form {
  display: contents;
}

.hero__more .menu__panel .silence-pair .btn {
  width: 100%;
  justify-content: flex-start;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  box-shadow: none;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text);
}

.hero__more .menu__panel .silence-pair .btn:hover {
  background: var(--surface-2);
}

/* The straddling stat chips: three, hugging the hero's left edge. */
.hero__strip--stats {
  display: flex;
  gap: var(--space-4);
  inset-inline: 2.125rem auto;
}

.strip-chip--stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 0;
  padding: 0.875rem 1.625rem;
  color: inherit;
  text-decoration: none;
}

a.strip-chip--stat:hover .strip-chip__num {
  color: var(--brand-text);
}

.strip-chip__num {
  font-size: 1.375rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.strip-chip__lbl {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Tabs under the bar, the privacy pill pushed right. */
.profile-tabs-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-block-end: var(--space-5);
}

.profile-tabs-row .feed-tabs {
  margin-block-end: 0;
}

.profile-privacy {
  margin-inline-start: auto;
  height: auto;
  padding: 0.625rem 1.125rem;
  text-decoration: none;
}

/* The dogs tab: the first card is the lifted one, with the long drop. */
.dogcard--lifted {
  background: var(--surface-bright);
  box-shadow: var(--deck-shadow), var(--raise);
}

.dogcard--lifted:hover {
  box-shadow: var(--deck-shadow), var(--raise-lg);
}

@media (width < 48rem) {
  .hero__card--profile {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding: 1.25rem;
  }

  .avatar.hero__face {
    --size: 5.5rem;
  }

  .hero__identity {
    padding-inline-end: 0;
  }

  .hero__strip--stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-2);
    inset-inline: auto;
  }

  .strip-chip--stat {
    padding: 0.75rem 0.5rem;
  }
}


/* -------------------------------------------------------------------------
   Dog dashboard (redesign D9)
   The editorial hero — portrait right, the switcher as a glass pill, the
   streak, name and facts — with the four stat chips straddling its edge;
   the assistant as the floating blue plane; the care log in the rail.
   ------------------------------------------------------------------------- */

/* The chips carry real controls (quick-adds, Update) and the placeholders'
   copy, so they run taller than the spec's; the clearance follows them and
   the hero grows past 268px rather than letting the strip cover its text. */
.hero--dog {
  --strip-min-h: 9.75rem;
}

.hero__card--dog {
  --hero-h: 16.75rem;           /* the spec's 268px */
  padding: 1.75rem 2.125rem;
}

.avatar.hero__portrait {
  --size: 7.5rem;               /* 120px, right of centre */
  position: absolute;
  right: 2.25rem;
  top: 50%;
  translate: 0 -50%;
  z-index: 1;
  border: 0;
  border-radius: var(--radius-xl);
  box-shadow: 0 12px 20px rgb(0 0 0 / 0.35);
}

.hero__lead {
  position: relative;
  z-index: 2;
  max-width: calc(100% - 9rem);
}

.hero__pills {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* The switcher pill: avatar · name · chevron, a details.menu on glass. */
.dogswitch > summary.dogswitch__summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.35rem 0.75rem 0.35rem 0.35rem;
  border-radius: var(--radius-full);
  color: #ffffff;
  font-weight: 800;
  box-shadow: 0 12px 24px -12px rgb(0 0 0 / 0.45);
}

.dogswitch > summary.dogswitch__summary svg {
  width: 0.875rem;
  height: 0.875rem;
  opacity: 0.85;
}

.dogswitch .avatar.dogswitch__face {
  --size: 1.875rem;
  border-width: 1px;
  box-shadow: none;
}

.dogswitch .menu__panel {
  inset-inline: 0 auto;         /* opens under the pill's left edge */
}

.dogswitch .menu__item .avatar {
  --size: 1.5rem;
}

.dogswitch .menu__item[aria-current="page"] {
  background: var(--brand-soft);
  color: var(--brand-text);
  font-weight: 800;
}

.hero__streak {
  height: auto;
  min-width: 0;
  padding: 0.375rem 0.75rem;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.hero__card--dog .hero__name {
  margin-block-start: var(--space-4);
  font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem);   /* the spec's 44px */
}

.hero__card--dog .hero__sub {
  font-size: 0.9375rem;
  color: rgb(255 255 255 / 0.85);
}

.hero__date {
  color: rgb(255 255 255 / 0.6);
}

/* The white CTA on the green: Add activity. */
/* Same literal white as the profile's Follow, for the same reason. */
.btn.hero__cta {
  background: #fdfcf9;
  color: #1c4b30;
  font-weight: 800;
  box-shadow: 0 14px 26px -10px rgb(0 0 0 / 0.4);
}

/* Without care access only the public Breed chip remains. */
.hero__strip--solo {
  grid-template-columns: minmax(0, 20rem);
}

.strip-chip .statcard__label {
  margin-block-end: var(--space-1);
}

.strip-chip .statcard__big {
  font-size: 1.375rem;
}

.strip-chip .statcard__actions {
  margin-block-start: var(--space-2);
}

.strip-chip .statcard__soon {
  margin-block-start: var(--space-1);
}

.strip-chip--outdoors .stat-ring--sm svg {
  width: 52px;
  height: 52px;
}

.strip-chip--outdoors .stat-ring--sm .stat-ring__label strong {
  font-size: 0.8125rem;
}

.strip-chip--placeholder .statcard__sub {
  font-size: 0.75rem;
}

/* The Breed chip: pie + tight legend. */
.strip-chip--breed {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.strip-chip__text {
  flex: 1;
  min-width: 0;
}

.breedpie {
  display: grid;
  place-items: center;
  width: 3.25rem;
  height: 3.25rem;
  flex: none;
  border-radius: var(--radius-full);
  box-shadow: 0 6px 14px -6px rgb(0 0 0 / 0.35);
}

.breedpie__hole {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: var(--radius-full);
  background: var(--surface-bright);
}

.breedlegend--tight {
  gap: 3px;
  margin: 0;
}

.breedlegend--tight .breedlegend__row {
  gap: 5px;
  font-size: 0.6875rem;
  font-weight: 700;
}

.breedlegend--tight .breedlegend__swatch {
  width: 7px;
  height: 7px;
  border-radius: 2px;
}

/* The assistant: the floating blue plane. */
.ask--plane {
  margin-block-end: var(--space-6);
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--info-soft) 92%, transparent),
    color-mix(in srgb, var(--info-soft) 84%, transparent));
  border: 1px solid var(--glass-border);
  box-shadow: 0 22px 44px -22px color-mix(in srgb, var(--info) 45%, transparent), var(--glass-rim);
}

.ask--plane .chip {
  box-shadow: var(--raise-info-sm);
}

.people-card .doghero__people {
  align-items: flex-start;
}

.people-card .doghero__people-row {
  justify-content: flex-start;
}

@media (width < 48rem) {
  .hero__card--dog {
    padding: 1.25rem;
  }

  .avatar.hero__portrait {
    --size: 4.5rem;
    right: 1rem;
    top: 1rem;
    translate: none;
  }

  .hero__lead {
    max-width: calc(100% - 5.5rem);
  }

  .hero__card--dog .hero__name {
    font-size: 1.75rem;
  }

  .hero__strip--solo {
    grid-template-columns: 1fr;
  }
}


/* -------------------------------------------------------------------------
   Parks list (redesign D11)
   The search form is the map. Floating glass controls ride over the canvas
   (Tier 3): the search well and radius, the amenity chips, the count pill,
   and the selected park's card at the foot. Pins are rings toned by what
   the park offers; the chosen one fills. The rail mirrors the pins: a
   scrolling, distance-sorted list, Suggest-a-park beneath it, then the
   privacy panel. Without a key the same children lay out in flow.
   ------------------------------------------------------------------------- */

.parkscape-page {
  --rail: 20.625rem;
}

.parkscape-title .page-head__sub {
  margin: 0;
}

.parkscape {
  position: relative;
  display: block;
  min-height: 32.5rem;
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Every Maps control and overlay stays inside the card's stacking
     context, so nothing on the map can rise above the header or tab bar. */
  isolation: isolate;
  background: var(--surface-2);
  box-shadow: var(--float);
}

/* The canvas is its own layer beneath the controls: Google's own controls
   carry huge z-indexes, and a stacking context keeps them under our glass. */
.parkscape__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--surface-2);
}

.parkscape__top,
.parkscape__chips,
.parkscape__count,
.parkscape__card,
.parkscape__status {
  position: absolute;
  z-index: var(--z-sticky);
}

/* --- the search row ------------------------------------------------------- */

.parkscape__top {
  inset: 1rem 1rem auto;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  /* above the filters row: the radius menu opens across it */
  z-index: calc(var(--z-popover) + 1);
}

.parkscape__search {
  flex: 1 1 12rem;
  min-width: 0;
  height: 2.75rem;
  padding: 0 1rem;
  border-radius: 0.875rem;
  box-shadow: var(--float-sm);
}

.parkscape__top .chip {
  flex: none;
  height: 2.75rem;
  padding: 0 0.875rem;
  border-radius: 0.875rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--float-sm);
}

.parkscape__top .chip svg {
  width: 1rem;
  height: 1rem;
}

/* --- the amenity chips and the count pill ------------------------------------ */

.parkscape__chips {
  inset: 4.5rem auto auto 1rem;
  max-width: calc(100% - 12rem);
  /* fieldsets refuse to shrink below min-content by default; the row must. */
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  /* above the card, so an open Filters menu never slides beneath it */
  z-index: var(--z-popover);
}

/* The two dropdowns take the glass skin over the map. */
.parkscape__chips .chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 2.125rem;
  padding: 0.375rem 0.8125rem;
  border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--float-sm);
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
  cursor: pointer;
}

.parkscape__top .chip:hover,
.parkscape__chips .chip:hover {
  box-shadow: var(--float-sm);
}

/* --- the three menus: radius, filters, pass ---------------------------------- */

/* One dropdown look (owner review): the house details menu with a glass
   chip for a summary and radio or checkbox rows in the panel. Radius and
   Pass read the pick back into the summary and close themselves; Filters
   stays open while you tick. */
.parkscape__menu-summary svg {
  width: 0.875rem;
  height: 0.875rem;
  transition: rotate var(--dur-fast) var(--ease);
}

.parkscape__menu[open] > .parkscape__menu-summary svg {
  rotate: 180deg;
}

/* On — open, or narrowing (a ticked filter, a pass other than "any") — the
   spec's inset-green active pill. */
.parkscape__menu[open] > .parkscape__menu-summary,
.parkscape__filters:has(input:checked) > .parkscape__menu-summary,
.parkscape__pass:has(input:checked:not([value=""])) > .parkscape__menu-summary {
  background: var(--brand-soft);
  color: var(--brand-text);
  box-shadow: var(--press-active);
  border-color: transparent;
}

.parkscape__menu-panel {
  inset-inline-start: 0;
  inset-inline-end: auto;
  min-width: 12rem;
}

/* The radius chip sits at the row's right, so its panel hangs to the left —
   inside the map on a phone. */
.parkscape__radius .parkscape__menu-panel {
  inset-inline-start: auto;
  inset-inline-end: 0;
}

.parkscape__option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
}

.parkscape__option:hover {
  background: var(--surface);
}

.parkscape__option input {
  width: 1.0625rem;
  height: 1.0625rem;
  margin: 0;
  accent-color: var(--brand);
  cursor: pointer;
}

.parkscape__option:has(input:checked) {
  color: var(--brand-text);
}

/* Single-choice menus mark the current pick. */
.parkscape__radius .parkscape__option:has(input:checked),
.parkscape__pass .parkscape__option:has(input:checked) {
  background: var(--brand-soft);
}

.parkscape__count {
  inset: 4.5rem 1rem auto auto;
  height: auto;
  min-width: 0;
  margin: 0;
  padding: 0.4375rem 0.8125rem;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--text);
  box-shadow: var(--float-sm);
}

.parkscape__count [data-map-inview]:not([hidden])::before {
  content: " · ";
}

/* --- the selected park's card ------------------------------------------------ */

/* Glass at the foot of the map, slim (owner review: the map wants the
   room): one row of mark, name and meta with the quiet here-now line,
   distance, and the two actions. It stops short of the edge so Google's
   logo and the Map data / Terms strip stay visible beneath it. */
.parkscape__card {
  inset: auto 0.75rem 2.375rem;
  padding: 0.625rem 0.875rem;
  border-radius: 1rem;
  color: var(--text);
}

.parkscape__card[hidden] {
  display: none;
}

.parkscape__card-row {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  flex-wrap: wrap;
}

.parkscape__card-mark {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  border-radius: 0.6875rem;
  background: var(--brand-soft);
  color: var(--brand-text);
}

.parkscape__card-mark svg {
  width: 1rem;
  height: 1rem;
}

.parkscape__card-text {
  flex: 1 1 12rem;
  min-width: 0;
}

.parkscape__card .park-selected__name a {
  font-size: 1rem;
}

.parkscape__card .park-selected__dist {
  flex: none;
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 800;
}

.parkscape__card-actions {
  display: flex;
  gap: 0.5rem;
  flex: none;
}

.parkscape__card-actions .btn {
  padding: 0.4375rem 0.75rem;
  border-radius: 0.6875rem;
  font-size: 0.75rem;
  font-weight: 800;
}

/* Check in reads as glass on glass; Navigate is the blue CTA (its text is
   the on-brand ink, which stays dark on the light dark-theme blue). */
.parkscape__card-checkin {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
  box-shadow: none;
}

.parkscape__card-checkin:hover {
  background: color-mix(in srgb, var(--text) 14%, transparent);
  color: var(--text);
}

.parkscape__card-actions .btn--info {
  color: var(--on-brand);
  box-shadow: 0 10px 20px -8px color-mix(in srgb, var(--info) 60%, transparent);
}

.parkscape__card-actions .btn--info svg {
  width: 1rem;
  height: 1rem;
}

/* The quiet line under the meta: faces, the live count, the rating. */
.parkscape__card-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin: 2px 0 0;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.parkscape__card-foot .park-selected__face {
  width: 1.25rem;
  height: 1.25rem;
}

.parkscape__card-foot .park-selected__rating,
.parkscape__card-foot .park-selected__ratingbtn {
  font-size: 0.75rem;
}

/* Feedback — "Searching…" and the error slots — as a glass pill under the
   chips; the park-search__status rules decide when it shows. */
.parkscape__status {
  inset: 7.5rem auto auto 1rem;
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-full);
  font-size: 0.8125rem;
}

/* --- pins ----------------------------------------------------------------- */

/* A ring toned by what the park offers (green off-leash, blue water, brown
   otherwise); the chosen park's pin fills and grows. The paw stays the
   marker; the name appears beneath it once zoomed in (map-pill--labelled). */
.map-pill[hidden] {
  /* the author display beats the UA's [hidden] rule otherwise */
  display: none;
}

.map-pill {
  --tone: var(--accent);
  position: absolute;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  cursor: pointer;
}

.map-pill[data-tone="brand"] {
  --tone: var(--brand);
}

.map-pill[data-tone="info"] {
  --tone: var(--info);
}

.map-pill__tile {
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: var(--radius-full);
  background: var(--surface-bright);
  color: var(--tone);
  border: 2px solid var(--tone);
  box-shadow: 0 8px 16px rgb(0 0 0 / 0.25);
  transition: width var(--dur-fast) var(--ease), height var(--dur-fast) var(--ease);
}

.map-pill__tile svg {
  width: 1rem;
  height: 1rem;
}

.map-pill.is-active .map-pill__tile {
  width: 2.375rem;
  height: 2.375rem;
  background: var(--tone);
  color: var(--on-brand);
  border-color: var(--surface-bright);
}

.map-pill__label {
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  padding: 3px 10px;
  border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--float-sm);
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
  display: none;
}

.map-pill--labelled .map-pill__label {
  display: block;
}

.map-pill.is-active .map-pill__label {
  background: var(--tone);
  color: var(--on-brand);
  border-color: transparent;
}

/* --- the rail --------------------------------------------------------------- */

/* Rows: the tile wears the park's real tone; the selected row is the bright
   card with the brand's outline. */
.park-row__tile[data-tone="brand"] {
  background: var(--brand-soft);
  color: var(--brand-text);
}

.park-row__tile[data-tone="info"] {
  background: var(--info-soft);
  color: var(--info);
}

.park-row__tile[data-tone="accent"] {
  background: var(--accent-soft);
  color: var(--accent);
}

.park-row.is-selected {
  background: var(--surface-bright);
  box-shadow: var(--raise-lift), 0 0 0 1px color-mix(in srgb, var(--brand) 45%, transparent);
}

.parkscape__rail {
  min-width: 0;
}

.park-results {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.park-results__label {
  margin-block-end: var(--space-3);
}

.park-results .park-list {
  gap: var(--space-2);
  /* The rows' box: rounded like everything else (the scroll edge used to
     cut the rows off square), a faint well so the shape reads, and room
     inside for the rows' raised shadows. */
  padding: 6px;
  margin: 0;
  border-radius: 1.125rem;
  background: color-mix(in srgb, var(--bg) 40%, transparent);
  scrollbar-width: thin;
}

.parkscape__suggest-text {
  margin: 0 0 var(--space-3);
  font-size: 0.875rem;
}

/* Desktop: the rail runs the map's height; the list scrolls inside it and
   Suggest-a-park and the privacy panel keep their place beneath. */
@media (width >= 60rem) {
  .parkscape__rail {
    max-height: 32.5rem;
  }

  .park-results {
    flex: 1 1 auto;
    min-height: 12rem;
  }

  .park-results .park-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
}

/* Stacked: the list caps at a few screens' worth and scrolls. */
@media (width < 60rem) {
  .park-results .park-list {
    max-height: 24rem;
    overflow-y: auto;
  }
}

/* Phones: the two dropdowns share the row; the pill and the feedback drop
   beneath them. (The full-bleed map and the sheet come in D13.) */
@media (width < 48rem) {
  .parkscape__top {
    inset: 0.75rem 0.75rem auto;
  }

  /* Icon-only locate on phones, so the well keeps its width. */
  .parkscape__locate span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .parkscape__locate {
    padding: 0 0.75rem;
  }

  .parkscape__chips {
    inset: 4.25rem 0.75rem auto;
    max-width: none;
  }

  .parkscape__count {
    inset: 7.25rem auto auto 0.75rem;
  }

  .parkscape__status {
    inset: 10rem auto auto 0.75rem;
  }

  .parkscape__card-actions {
    flex: 1 1 100%;
  }
}

/* --- without a key ------------------------------------------------------------ */

/* The same children in flow: no canvas, no card, nothing floats. */
.parkscape--nomap {
  position: static;
  min-height: 0;
  overflow: visible;
  isolation: auto;
  background: none;
  box-shadow: none;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.parkscape--nomap > * {
  position: static;
  inset: auto;
  max-width: none;
}

.parkscape--nomap .parkscape__count {
  align-self: flex-start;
}

.parkscape--nomap .park-mapnote {
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .map-pill__tile {
    transition: none;
  }
}


/* -------------------------------------------------------------------------
   Park detail (redesign D12)
   A themed gradient hero (parks have no photos) with glass controls and a
   strip of four chips straddling its foot; grounded cards below — About,
   the three-state amenity tiles, Tips, the named check-ins — and the
   presence card on the floating tier in the rail. Tokens throughout, so the
   page follows the theme like the rest of Parks.
   ------------------------------------------------------------------------- */

.hero--park {
  --strip-overhang: 2.25rem;    /* the spec's 36px */
  --strip-min-h: 5.5rem;        /* the chips run to three lines; the title clears them */
  margin-block-end: 3.5rem;     /* overhang + the same 20px the cards keep between them */
}

.hero__card--park {
  --hero-h: 21rem;              /* room for the map beneath the title */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 4.25rem 1.5rem 1.5rem;   /* the title starts under the corner controls */
  border-radius: var(--radius-lg);
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--brand) 26%, var(--surface-2)),
    color-mix(in srgb, var(--info) 20%, var(--surface-2)) 55%,
    var(--accent-soft));
  box-shadow: var(--float);
}

.hero__sheen--park {
  background:
    radial-gradient(circle at 30% 25%, color-mix(in srgb, var(--brand) 28%, transparent), transparent 45%),
    radial-gradient(circle at 75% 70%, color-mix(in srgb, var(--info) 22%, transparent), transparent 45%);
}

/* The scrim darkens the top, where the title sits, and leaves the map's
   lower half — and Google's attribution — clear. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgb(20 17 13 / 0.72) 0%, rgb(20 17 13 / 0.38) 45%, transparent 72%);
  pointer-events: none;
}

/* The embed fills the card down to the chip band, so its own logo and terms
   row stays visible above the chips; the band beneath is the card's gradient. */
.hero__map {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  height: calc(100% - (var(--strip-min-h) - var(--strip-overhang)) - 4px);
  border: 0;
  background: var(--surface-2);
}

.hero__back {
  position: absolute;
  left: 1rem;
  top: 1rem;
  z-index: 4;
}

.hero__card--park .hero__actions {
  right: 1rem;
  top: 1rem;
}

.hero__card--park .glass-btn {
  box-shadow: 0 12px 26px -10px rgb(0 0 0 / 0.6);
}

/* The share button is a glass square, not the inline-flex the share rule
   hands every [data-share]. */
html.has-share .hero__actions .glass-btn[data-share] {
  display: grid;
}

.park-hero__title {
  position: relative;
  z-index: 2;
  max-width: 40rem;
  color: #ffffff;
}

.park-hero__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block-end: 0.75rem;
}

/* Tag pills over the hero: white smoke, white type, both themes. */
.pill--on-hero {
  height: auto;
  min-width: 0;
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-full);
  background: var(--glass-hero-bg);
  -webkit-backdrop-filter: var(--glass-hero-blur);
  backdrop-filter: var(--glass-hero-blur);
  border: 1px solid var(--glass-hero-border);
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: 800;
  box-shadow: 0 6px 14px -4px rgb(0 0 0 / 0.35);
}

.park-hero__name {
  margin: 0;
  font-size: clamp(1.75rem, 1.25rem + 1.5vw, 2.125rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: #ffffff;
  text-wrap: balance;
}

.park-hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.875rem;
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: rgb(255 255 255 / 0.82);
}

.park-hero__meta > * + *::before {
  content: "·";
  margin-inline-end: 0.875rem;
  color: rgb(255 255 255 / 0.5);
}

.park-hero__dist {
  font-weight: 800;
  color: #ffffff;
}

/* --- the strip --------------------------------------------------------------- */

.hero__strip--park {
  inset-inline: 1.5rem;
}

.hero__strip--park .strip-chip {
  padding: 0.875rem 1rem;
  border-radius: 1rem;
}

.hero__strip--park .strip-chip > p {
  margin: 0;
}

.hero__strip--park .strip-chip__label {
  margin-block-end: 0.25rem;
}

.hero__strip--park .strip-chip__value {
  font-size: 1.25rem;
  line-height: 1.2;
}

.hero__strip--park .strip-chip__sub {
  margin-block-start: 0.125rem;
}

.herenow--live {
  color: var(--brand-text);
}

.herenow--quiet {
  color: var(--text-muted);
}

/* --- the body ------------------------------------------------------------------ */

/* The main column keeps the rail's rhythm: one even gap between cards. */
.park-page__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.park-section .group-label {
  margin-block-end: 0.75rem;
}

.park-about {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* Amenity tiles, three-state: recorded yes, recorded no, or not recorded —
   the last kept distinct from a "no", which is the honest reading. */
.amenity-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
}

.amenity {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
  padding: 0.75rem;
  border-radius: 0.8125rem;
  background: var(--surface);
  box-shadow: var(--raise-sm);
  font-size: 0.8125rem;
}

.amenity__icon {
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  flex: none;
  border-radius: 0.625rem;
  background: var(--surface-2);
  color: var(--text-muted);
}

.amenity__icon svg {
  width: 1rem;
  height: 1rem;
}

.amenity--yes .amenity__icon {
  background: var(--brand-soft);
  color: var(--brand-text);
}

.amenity--no .amenity__icon {
  background: var(--accent-soft);
  color: var(--accent);
}

.amenity--unknown .amenity__icon {
  color: var(--text-faint);
}

.amenity__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.amenity__label {
  font-weight: 800;
}

.amenity__state {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
}

.amenity--unknown .amenity__state {
  color: var(--text-faint);
  font-style: italic;
}

.park-checkins__empty {
  margin: 0;
}

/* --- the rail ----------------------------------------------------------------- */

.park-presence__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.75rem;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.park-presence__head.is-live {
  color: var(--brand-text);
}

.park-presence__dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--text-faint);
}

.is-live .park-presence__dot {
  background: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 25%, transparent);
}

.park-presence__faces {
  margin-block-end: 0.625rem;
}

.park-presence__face {
  --size: 2.375rem;
}

.park-presence__face--anon {
  display: inline-grid;
  place-items: center;
  background: var(--surface);
  color: var(--text-faint);
}

.park-presence__face--anon svg {
  width: 1rem;
  height: 1rem;
}

.park-presence__note {
  margin-block-end: var(--space-3);
}

.park-location {
  margin: 0 0 var(--space-3);
  font-size: 0.875rem;
}

.park-cta-card .btn--block svg {
  width: 1rem;
  height: 1rem;
}

@media (width < 48rem) {
  /* The chips stack below the card here; the hero keeps the cards' gap. */
  .hero--park {
    margin-block-end: var(--space-5);
  }

  /* Room at the top for the corner controls: the title block rises when a
     long name and a row of tags outgrow the card's minimum. */
  .hero__card--park {
    --hero-h: 15rem;
    padding: 4.25rem 1.25rem 1.25rem;
  }

  /* The controls keep their corners over the gradient. */
  .hero__card--park .hero__actions {
    position: absolute;
    margin: 0;
  }

  /* The chips sit below the card here, so the map can fill it. */
  .hero__map {
    height: 100%;
  }

  .amenity-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}


/* -------------------------------------------------------------------------
   Parks on phones: the bottom sheet (redesign D13)
   With a map, the phone layout is the map full-bleed under the header and
   the list as a glass sheet over it: peeking a third of the way up, dragged
   or tapped to expand, Escape to collapse. Rows carry the selection (the
   card stays hidden); a pin brings its row to the top of the sheet. Without
   a key none of this applies and the list stacks under the controls.
   ------------------------------------------------------------------------- */

.sheet__handle {
  display: none;
}

/* The body wrapper vanishes from layout on desktop, so the rail's column is
   exactly what it was. */
.sheet__body {
  display: contents;
}

.park-row__open {
  display: none;
}

@media (width < 48rem) {
  /* The page is the map: the body takes the viewport, the container fills
     what the header (and any banner) leaves, and the footer steps aside. */
  body:has(.sheet[data-sheet]) {
    --sheet-bottom: calc(5.25rem + env(safe-area-inset-bottom, 0px));   /* the tab bar's top edge */
    height: 100dvh;
  }

  /* The map ends where the tab bar begins, like the sheet. */
  body:has(.sheet[data-sheet]) .shell {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 0 0 var(--sheet-bottom);
  }

  body:has(.sheet[data-sheet]) .footer,
  body:has(.sheet[data-sheet]) .parkscape-title {
    display: none;
  }

  body:has(.sheet[data-sheet]) .parkscape-page {
    display: flex;
    flex-direction: column;
    /* stretch, not the split's start: the map's children are all absolute,
       so anything less leaves it no width at all */
    align-items: stretch;
    flex: 1 1 auto;
    min-height: 0;
    gap: 0;
  }

  body:has(.sheet[data-sheet]) .parkscape {
    flex: 1 1 auto;
    min-height: 0;
    border-radius: 0;
    box-shadow: none;
  }

  /* Rows carry the selection here; the pill's in-view count lives in the
     sheet's label aside. */
  body:has(.sheet[data-sheet]) .parkscape__count,
  body:has(.sheet[data-sheet]) .parkscape__card {
    display: none;
  }

  .sheet[data-sheet] {
    --sheet-offset: 45dvh;                                             /* PEEK in park-sheet.js */
    position: fixed;
    left: 0;
    right: 0;
    bottom: var(--sheet-bottom);
    height: calc(100dvh - var(--header-h) - var(--sheet-bottom) - 0.75rem);
    max-height: none;
    z-index: var(--z-sheet);
    display: flex;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 0 1rem;
    border-radius: 1.625rem 1.625rem 0 0;
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(20px) saturate(1.3);
    backdrop-filter: blur(20px) saturate(1.3);
    border: 1px solid var(--glass-border);
    border-bottom: 0;
    box-shadow: var(--float-up);
    transform: translateY(var(--sheet-offset));
    transition: transform var(--dur) var(--ease);
  }

  .sheet[data-sheet-state="expanded"] {
    --sheet-offset: 0px;
  }

  .sheet.is-dragging {
    transition: none;
  }

  .sheet[data-sheet] .sheet__handle {
    display: grid;
    place-items: center;
    flex: none;
    min-height: 44px;
    padding: 0;
    border: 0;
    background: none;
    cursor: grab;
    touch-action: none;
  }

  .sheet[data-sheet] .sheet__handle:active {
    cursor: grabbing;
  }

  .sheet__grip {
    width: 40px;
    height: 5px;
    border-radius: var(--radius-full);
    background: var(--text-faint);
    opacity: 0.6;
  }

  .sheet[data-sheet] .sheet__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    padding-block-end: 1rem;
  }

  .sheet[data-sheet-state="expanded"] .sheet__body {
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
  }

  /* The body is the scroller: its children keep their natural height, or
     the results box shrinks and its rows spill past the cards below it. */
  .sheet[data-sheet] .sheet__body > * {
    flex: none;
  }

  /* Inside the sheet the body scrolls, not the list. */
  .sheet[data-sheet] .park-results .park-list {
    max-height: none;
    overflow: visible;
  }

  /* The chosen row offers the way in. */
  .sheet[data-sheet] .park-row.is-selected .park-row__open {
    display: inline-flex;
    align-items: center;
    flex: none;
    margin-inline-start: auto;
    padding: 0.25rem 0.625rem;
    border-radius: var(--radius-full);
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 0.6875rem;
    font-weight: 800;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sheet[data-sheet] {
    transition: none;
  }
}


/* -------------------------------------------------------------------------
   Sniff around + Search (redesign D14)
   Two screens, one head: the title beside a lifted glass search well (the
   page's one lit surface), then chips — the sorts on Sniff, the result
   types on Search — with the page's note beside them, the active chip solid
   green. Cards and rows below stay grounded.
   ------------------------------------------------------------------------- */

.sniff-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-block-end: var(--space-4);
}

.sniff-bar__title {
  margin: 0;
  flex: none;
  font-size: 1.625rem;
  letter-spacing: -0.02em;
}

.sniff-bar__form {
  display: flex;
  flex: 1 1 16rem;
  min-width: 0;
}

.searchwell--lifted {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 3rem;
  padding: 0 1rem;
  border-radius: 0.875rem;
}

.searchwell--lifted input {
  font-size: 0.9375rem;
}

/* The chips row: sorts or result types, the note pushed to the right. */
.sniff-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5625rem;
  margin-block-end: var(--space-5);
}

.sniff-chips .pill {
  height: auto;
  min-width: 0;
  padding: 0.5625rem 1rem;
  border-radius: var(--radius-full);
  font-size: 0.8125rem;
}

/* The active chip is solid green (the spec's Sniff chips). */
.sniff-chips .pill[aria-current] {
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--lift-brand);
}

.sniff-chips .pill[aria-current]:hover {
  color: var(--on-brand);
}

.sniff-note {
  margin: 0;
  margin-inline-start: auto;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.search-section {
  margin-block-end: var(--space-8);
}

.search-section > .group-label {
  margin-block-end: var(--space-3);
}

@media (width < 48rem) {
  .sniff-bar__title {
    flex: 1 1 100%;
  }

  .sniff-note {
    flex: 1 1 100%;
    margin-inline-start: 0;
  }
}

/* The row's facts, separated by CSS: no leading dot when the place is
   unknown. */
.park-row__meta > span + span::before {
  content: "  b7 ";
}

/* The Parks hand-off on a phone: text on top, the button across the foot. */
@media (width < 48rem) {
  .park-cta {
    flex-wrap: wrap;
  }

  .park-cta__btn {
    flex: 1 1 100%;
    margin-inline-start: 0;
    justify-content: center;
  }
}
