/* RHoFL visual layer - tokens and rules from docs/STYLE-GUIDE.md.
   Heading/body typefaces are stacks: the guide specifies the serif/sans
   pairing as intent (S12.2) but leaves families open. */

:root {
  /* Colour, opacity and type tokens live in tokens.css, which is linked
     before this file and shared with the staff portal. Only the storefront's
     own column rhythm belongs here - the portal lays out on Django's grid and
     must never honour these three. */

  /* One gutter and one content ceiling for every page, so the storefront
     lines up column-for-column from the catalog through to My Orders. */
  --gutter: clamp(1rem, 3.5vw, 2.5rem);
  --content-max: 1440px;
  --rhythm: clamp(2rem, 4vw, 3.25rem);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100vh;
}

body {
  /* Column so the nav takes its own height and .page fills the rest -
     .page can then stay a centring grid without a 100vh that would push
     the fold down by the height of the header. */
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  color: var(--ink);
  /* FLAT. The weather is the .aurora layer in css/aurora.css, shared with
     the portal - a fixed full-viewport sibling that does not scroll away. */
  background: var(--bg-base);
}

/* --- Molecular background: atmosphere, never content ------------------- */

/* .molbg used to be the fixed layer itself. It is now the watermark INSIDE
   .aurora (see aurora.css's .aurora-mol), so it no longer positions itself -
   it only masks and shifts the structure. The portal still applies the class
   for the same masking. */
.molbg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  /* The generated lattice does not cover every viewport aspect; fading its
     edges reads as atmosphere instead of a clipped rectangle. */
  -webkit-mask-image: radial-gradient(115% 100% at 50% 45%, #000 38%, transparent 72%);
  mask-image: radial-gradient(115% 100% at 50% 45%, #000 38%, transparent 72%);
}

/* Shift the structure into the whitespace beside the centred card - both
   default to dead centre, which hides the molecule entirely. */
.aurora-mol svg,
.molbg svg {
  transform: scale(0.9) translate(-8%, -6%);
  transform-origin: center;
}

/* --- Page scaffolding --------------------------------------------------- */

.page {
  position: relative;
  z-index: 1;
  /* CLIP, not hidden, and not absent.

     The mark's arc is deliberately wider than the viewport so its ends leave
     the screen. Without something clipping it that width becomes real page
     width: a 300vw arc shipped a horizontal scrollbar to the live login page
     and shoved the whole layout sideways.

     `clip` rather than `hidden` because hidden creates a scroll container,
     and a scroll container between a sticky element and the viewport kills
     the sticking. The nav is a sibling of .page rather than a child, so it
     is safe either way here - but the next sticky thing put INSIDE .page
     would not be, and this is one word's difference. */
  overflow-x: clip;
  flex: 1;
  display: grid;
  /* An `auto` track is sized from its content's max-content width, so a wide
     card would widen the track past the viewport and take the page with it.
     minmax(0, 1fr) caps the track at the container, which is what makes the
     percentage widths below actually bound anything. */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: var(--rhythm) var(--gutter);
}
/* The site mark and its arc lived here until 2026-08-07. They are now
   static/css/site-mark.css, loaded by BOTH this page and the staff portal -
   the two sign-in screens show the same mark above the same card, and one
   definition is the only way that stays true. See that file's header. */

/* The top navigation lived here until 2026-08-03. It is now
   static/css/site-nav.css, loaded by BOTH this page and the staff portal -
   see the header of that file for why it had to leave. */

/* --- Card ---------------------------------------------------------------- */

.card {
  background: var(--surface);
  background-clip: padding-box;
  /* Border, radius, outline and shadow all come from aurora.css's window
     rule - a solid border here would fill the very gap the aurora shows
     through. */
  padding: 2rem;
}

.auth-wrap {
  width: min(420px, 100%);
}

.card h2 {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.45rem;
  margin: 0 0 1.25rem;
}

/* --- Forms ---------------------------------------------------------------- */

.card form p {
  margin: 0 0 1rem;
}

/* Scoped to as_p form rows: a bare ".card label" would out-cascade any
   label-based component card (the storefront variant options are labels). */
.card form p label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 0.35rem;
}

/* THE LIST IS THE RULE - there is no ".card input" here, and adding one is
   not the shortcut it looks like. `.card` is worn by the product purchase
   panel, the variant selector and the cart, so a bare selector would land on
   the quantity stepper, the reconstitution checkbox and the tier radios, none
   of which are text fields.

   It cost the password-reset form its styling. That form is an EmailField, so
   Django renders type="email", which was in neither line above it - and an
   input that matches no rule does not look unstyled next to a styled one, it
   looks like a different site: white fill, hairline border, browser default
   width, under the same mark and above the same pill button. The staff
   portal had already enumerated the same six types (portal.css "Forms"), and
   .my-peps-form-grid had already worked around the same gap for selects, so
   this was the third place to meet it.

   NUMBER IS DELIBERATELY ABSENT. .qty-stepper input[type="number"] is inside
   .card on the product page and re-declares everything here except
   border-radius - so listing it would round the corners of the middle of a
   pill-shaped stepper group, and only there. */
.card input[type="text"],
.card input[type="password"],
.card input[type="email"],
.card input[type="tel"],
.card input[type="url"],
.card input[type="search"] {
  width: 100%;
  padding: 0.65rem 0.75rem;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
}

.card input[type="text"]:focus,
.card input[type="password"]:focus,
.card input[type="email"]:focus,
.card input[type="tel"]:focus,
.card input[type="url"]:focus,
.card input[type="search"]:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 1px;
  border-color: var(--glow-magenta);
}

.card .helptext,
.card .helptext ul {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 0.35rem 0 0;
  padding-left: 1.1rem;
}

.card .helptext {
  display: block;
  padding-left: 0;
}

.errorlist {
  color: #a03232;
  font-size: 0.85rem;
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
}

/* --- Pill button (STYLE-GUIDE S6, action button) ------------------------- */

.btn {
  display: block;
  width: 100%;
  padding: 0.85rem 1.5rem;
  border: none;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
  margin-top: 0.5rem;
}

.btn:hover {
  filter: brightness(0.96);
}

.btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.btn:disabled {
  /* Was #d9dde3 - a light-theme grey that read as a hole on the dark page.
     Inert, not dimmed: the treatment has to say "this does nothing", which
     a merely faded accent does not. */
  background: var(--surface-tile);
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--hairline);
  cursor: not-allowed;
}

/* --- Secondary text and links -------------------------------------------- */

.alt-link {
  text-align: center;
  font-size: 0.9rem;
  margin: 1.25rem 0 0;
}

.card a {
  color: var(--link-ink);
  text-decoration: none;
}

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

.card .lede {
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.55;
  margin: 0 0 1rem;
}

/* ======================================================================== */
/* Storefront (spec §8, STYLE-GUIDE §5-§7, §10). Additive: nothing above    */
/* this line changes for the auth screens.                                  */
/* ======================================================================== */

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

.shop-wrap {
  width: min(var(--content-max), 100%);
  align-self: start;
}

.shop-wrap--narrow {
  width: min(680px, 100%);
}

/* Wide enough for the two product-detail columns to each hold a full
   variant row without the option cards collapsing. */
.shop-wrap--sheet {
  width: min(1160px, 100%);
}

/* --- Breadcrumb ---------------------------------------------------------- */
/* Sits on the page gradient rather than on a card, so it takes the same
   white-plus-shadow treatment the wordmark does (S-G §3) rather than --ink,
   which would be unreadable there.

   Generous vertical room on purpose: it is the first thing under a sticky
   header, and crowding it against the header reads as part of the chrome
   rather than as part of the page. */
.breadcrumb {
  padding: 1.35rem 0.15rem 1.15rem;
}

.breadcrumb-trail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.1rem 0.55rem;
  margin: 0;
  padding: 0;
  list-style: none;
  /* Comfortably above the 0.8rem nav links: this is page content a shopper
     reads, not chrome they scan. */
  font-size: 0.95rem;
  line-height: 1.5;
}

.breadcrumb-item {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: #ffffff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
}

.breadcrumb-link {
  color: #ffffff;
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.45);
  padding-bottom: 1px;
}

.breadcrumb-link:hover {
  border-bottom-color: var(--accent);
  color: var(--accent);
}

.breadcrumb-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* The separator is decorative and aria-hidden; dimmed so the names carry the
   line rather than the punctuation between them. */
.breadcrumb-sep {
  color: rgba(255, 255, 255, 0.55);
  text-shadow: none;
}

/* A rung that is real but not navigable - the catalog has no per-category
   route. Full white would promise a link that is not there. */
.breadcrumb-here-muted {
  color: rgba(255, 255, 255, 0.82);
}

.breadcrumb-current {
  font-weight: 700;
}

a.site-mark {
  display: block;
  text-decoration: none;
}

.shop-empty {
  width: min(420px, 100%);
  margin: 0 auto;
}

.shop-empty .lede {
  margin: 0;
  text-align: center;
}

/* --- Catalog: filter rail beside a 6-across grid (S-G §5) ---------------- */

/* The category sections and their generated molecular header bands were
   removed on 2026-08-02. They could not coexist with a Category facet - a
   page already split by category has nothing for that facet to do - and the
   bands read as clutter against the aurora. The generator moved to the
   product backdrops, where it does more work than it did here. */

.catalog-layout {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 2rem;
  width: 100%;
  max-width: var(--content-max, 1600px);
  align-items: start;
}

.catalog-main {
  min-width: 0;
}

.catalog-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
}

.catalog-head h1 {
  margin: 0;
  font-size: clamp(1.8rem, 3.2vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.catalog-lede {
  margin: 0.25rem 0 0;
  color: var(--muted);
}

.catalog-search {
  flex: 1 1 22rem;
  width: min(30rem, 100%);
  max-width: 30rem;
  margin-left: auto;
}

.catalog-search input {
  width: 100%;
  min-width: 0;
}

.catalog-search .site-nav-search-results {
  right: 0;
  width: min(30rem, calc(100vw - 2 * var(--gutter)));
}

@media (max-width: 48rem) {
  .catalog-head {
    align-items: stretch;
    flex-direction: column;
  }

  .catalog-search {
    flex-basis: auto;
    width: 100%;
    max-width: none;
    margin-left: 0;
  }
}

.catalog-empty {
  padding: 2.5rem;
  text-align: center;
}

/* --- The filter rail ----------------------------------------------------- */

.filter-rail {
  position: sticky;
  top: 1.5rem;
  padding: 1.25rem;
}

/* A native <details>: closed by default, no script. The summary is the head. */
.filter-rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  cursor: pointer;
  list-style: none;
}

.filter-rail-head::-webkit-details-marker {
  display: none;
}

.filter-rail-head h2::before {
  content: "▸";
  display: inline-block;
  margin-right: 0.45rem;
  font-size: 0.8em;
  transition: transform 0.15s;
}

.filter-rail[open] .filter-rail-head h2::before {
  transform: rotate(90deg);
}

.filter-rail-body {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  margin-top: 1.25rem;
}

.filter-rail-head h2 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
}

.filter-clear {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: none;
}

.filter-clear:hover {
  text-decoration: underline;
}

.filter-group {
  margin: 0;
  padding: 0 0 1.1rem;
  border: 0;
  border-bottom: 1px solid var(--rule);
}

.filter-group:last-of-type {
  border-bottom: 0;
  padding-bottom: 0;
}

.filter-group legend {
  padding: 0 0 0.6rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.filter-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.28rem 0;
  font-size: 0.88rem;
  cursor: pointer;
}

.filter-option input {
  accent-color: var(--accent);
}

.filter-label {
  flex: 1;
  min-width: 0;
}

.filter-count {
  color: var(--muted);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.filter-range {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.filter-range input {
  width: 100%;
  min-width: 0;
  padding: 0.4rem 0.55rem;
  background: var(--surface-tile);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  color: var(--ink);
  font: inherit;
}

.filter-apply {
  width: 100%;
}

/* --- Toolbar: count, sort, view toggle ----------------------------------- */

.catalog-toolbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 0 0 1.25rem;
  flex-wrap: wrap;
}

.catalog-count {
  margin: 0;
  margin-right: auto;
  color: var(--muted);
  font-size: 0.86rem;
}

.catalog-sort {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.86rem;
  color: var(--muted);
}

.catalog-sort select {
  padding: 0.4rem 0.6rem;
  background: var(--surface);
  background-clip: padding-box;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  color: var(--ink);
  font: inherit;
}

.catalog-view-toggle {
  display: flex;
  gap: 0.25rem;
}

.view-toggle {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
}

.view-toggle.is-active {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 18px var(--edge-glow);
}

/* --- Grid and list ------------------------------------------------------- */

/* 6 across beside the rail, per the reference. This retires S-G §5's old
   "never 4" rule, whose stated reason was rhythm against a 5-wide variant
   row that the rail now displaces anyway.

   minmax(0, ...) throughout, not 1fr: a bare 1fr track has an automatic
   minimum of min-content, so the longest product name sets a floor the grid
   cannot go below and the row runs off the side of a narrow window. */
.product-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1.25rem;
}

.product-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

@media (max-width: 1500px) {
  .product-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (max-width: 1100px) {
  .catalog-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .filter-rail {
    position: static;
  }

  .product-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 760px) {
  .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 460px) {
  .product-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- Product card: its only job is to be clicked (S-G §6) ---------------- */

.product-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  padding: 1rem 1rem 1.35rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
  text-align: center;
  text-decoration: none;
  color: var(--ink);
  min-width: 0;
  transition: transform 140ms ease, box-shadow 140ms ease;
}

/* Just barely floating - 3px and a deeper shadow, no scale, no zoom. */
.product-card:hover,
.product-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 12px 26px rgba(0, 0, 0, 0.16);
}

.product-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .product-card {
    transition: none;
  }

  .product-card:hover,
  .product-card:focus-visible {
    transform: none;
  }
}

.product-tile {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1;
  background: var(--surface-tile);
  border-radius: 10px;
  /* See .product-hero - same two reasons, same ribbon. */
  position: relative;
  overflow: hidden;
}

.product-tile img {
  /* The composite carries its own generous margin, so shrinking it again
     here left the vial small in a large empty tile. */
  max-width: 96%;
  max-height: 96%;
  object-fit: contain;
}

.product-name {
  font-weight: 400;
  font-size: 0.95rem;
  line-height: 1.35;
  /* Product codes are single unbreakable tokens; without this they set the
     card's min-content width and push the grid wider than the viewport. */
  overflow-wrap: anywhere;
}

.product-price {
  font-weight: 700;
  font-size: 1.15rem;
}

/* --- Product detail: two columns (S-G §5, spec §8 order) ----------------- */

.product-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}

/* Below the tablet breakpoint the columns stack image-first, which is
   already the source order - nothing needs reordering. */
@media (max-width: 900px) {
  .product-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
  }
}

.product-media {
  min-width: 0;
}

.product-figure {
  padding: 1.5rem;
}

.product-hero {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1;
  background: var(--surface-tile);
  border-radius: 10px;
  /* Both for the SALE ribbon: relative to anchor it to this corner, hidden
     to clip it into a ribbon rather than a bar hanging off the tile. */
  position: relative;
  overflow: hidden;
}

.product-hero img {
  max-width: 68%;
  max-height: 68%;
  object-fit: contain;
}

/* Stacked, a full-width square tile would swallow the fold. Placed after the
   base rule rather than in the block above: same specificity, later wins. */
@media (max-width: 900px) {
  .product-hero {
    width: min(420px, 100%);
    margin: 0 auto;
  }
}

.product-purchase {
  min-width: 0;
  padding: 1.75rem;
}

.product-title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(1.6rem, 2.6vw, 2.2rem);
  line-height: 1.15;
  margin: 0 0 1rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--hairline);
}

.product-detail-price {
  font-weight: 800;
  font-size: clamp(1.7rem, 2.4vw, 2.05rem);
  letter-spacing: -0.01em;
  margin: 0 0 1.35rem;
}

/* The accent rule is a pseudo-element on purpose: the price text is
   rewritten with textContent on every variant change. */
.product-detail-price::after {
  content: "";
  display: block;
  width: 52px;
  height: 3px;
  margin-top: 0.75rem;
  border-radius: 2px;
  background: var(--accent);
  /* The price line became a flex row when it grew sale parts, which would
     otherwise put this rule BESIDE the price instead of under it. A full
     basis forces it onto its own line. */
  flex: 0 0 100%;
}

.product-blurb {
  margin: 0 0 1.5rem;
  padding: 1.1rem 1.25rem;
  background: var(--surface-tile);
  border-radius: 12px;
  font-size: 0.95rem;
  line-height: 1.6;
}

.product-blurb p {
  margin: 0 0 0.8rem;
}

.product-blurb p:last-child {
  margin-bottom: 0;
}

/* Stock describes the selection just made, so it sits below the selector
   (S-G §5). The colours are AA on white; the wording carries the meaning. */
.stock-line {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 600;
  margin: 0 0 1.25rem;
}

.stock-line::before {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}

.stock-line.stock-in {
  color: #23694a;
}

.stock-line.stock-limited {
  color: var(--warn-ink);
}

.stock-line.stock-out {
  color: var(--muted);
}

/* --- Variant selector: option cards, up to 5 across (S-G §6 states) ------ */

.variant-selector {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0 0 1rem;
  padding: 0;
  border: 0;
  /* A fieldset's default min-inline-size is min-content, which stops it
     shrinking with its grid column. */
  min-inline-size: 0;
}

.variant-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  /* Sized so five still fit across the purchase column (S-G §6). */
  width: clamp(76px, 18%, 92px);
  padding: 0.65rem 0.5rem 0.6rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

.variant-option:focus-within {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

.variant-image {
  width: 64%;
  aspect-ratio: 1;
  object-fit: contain;
}

.variant-label {
  font-size: 0.82rem;
  font-weight: 600;
}

/* Hollow radio indicator; the real input is visually hidden inside. */
.variant-radio {
  position: relative;
  width: 15px;
  height: 15px;
  border: 2px solid var(--muted);
  border-radius: 50%;
}

/* Out of stock: dashed border, muted label - still selectable by design. */
.variant-option.is-out {
  border-style: dashed;
}

.variant-option.is-out .variant-label {
  color: var(--muted);
  font-weight: 400;
}

/* Selected: accent fill, border matching, solid dark radio dot. */
.variant-option.is-selected {
  background: var(--accent);
  border-color: var(--accent);
}

.variant-option.is-selected .variant-label {
  color: var(--accent-ink);
}

.variant-option.is-selected .variant-radio {
  border-color: var(--accent-ink);
}

.variant-option.is-selected .variant-radio::after {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  background: var(--accent-ink);
}

/* Selected + out of stock: accent fill AND dashed border; the border goes
   ink so the dash stays visible against the fill. */
.variant-option.is-selected.is-out {
  border-style: dashed;
  border-color: var(--accent-ink);
}

/* --- Quantity stepper and action button (S-G §6) ------------------------- */

/* Stepper and pill share one row, the pill taking the remaining width; it
   drops to its own full-width line only when 12rem of it no longer fits. */
.buy-row {
  display: flex;
  align-items: stretch;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.buy-form {
  margin: 0 0 0.5rem;
}

.qty-stepper {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  overflow: hidden;
}

.qty-btn {
  padding: 0 1rem;
  border: 0;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
}

.qty-btn:hover:not(:disabled) {
  background: var(--surface-tile);
}

.qty-btn:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: -2px;
}

.qty-btn:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

.qty-stepper input[type="number"] {
  width: 3.2rem;
  padding: 0.6rem 0;
  border: 0;
  border-left: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
  background: var(--surface-tile);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}

.qty-stepper input[type="number"]::-webkit-outer-spin-button,
.qty-stepper input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.qty-stepper input[type="number"]:disabled {
  color: var(--muted);
}

.qty-stepper input[type="number"]:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: -2px;
}

.buy-btn {
  flex: 1 1 12rem;
  width: auto;
  min-width: 0;
  margin-top: 0;
}

.btn.btn-inert,
.btn.btn-inert:disabled {
  background: #d9dde3;
  color: var(--muted);
  cursor: not-allowed;
}

.btn.btn-inert:hover {
  filter: none;
}

.preorder-note {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0.75rem 0 0;
}

/* --- Expandable sections: Details / Research / Mechanism of Action ------- */
/* S-G §6: one white card each, serif heading, circular accent chevron that
   rotates when open. Collapsed panels carry the hidden attribute, so the
   card is the trigger row alone. */

.expando-stack {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 1.25rem;
}

/* Softer lift than the standard card: three of these stack, and the full
   card shadow reads as mud at that spacing. */
.expando {
  padding: 0;
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
}

/* And no aurora emission either. `overflow: hidden` above would clip the
   glow pseudo-layers (css/aurora.css) to nothing - but each would still
   hold a compositor layer and run its 31s fade, which is two invisible
   animations per expando. Removing them makes the softer look explicit
   instead of an accident of clipping. */
.expando::before,
.expando::after {
  content: none;
}

/* Two classes deep to out-cascade the generic ".card h2". */
.expando > .expando-head {
  margin: 0;
  font-size: inherit;
}

.expando-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding: 1.05rem 1.25rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 1.15rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}

.expando-trigger:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: -2px;
}

.expando-chevron {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  transition: transform 140ms ease;
}

.expando-chevron svg {
  display: block;
  width: 14px;
  height: 14px;
}

.expando.is-open .expando-chevron {
  transform: rotate(180deg);
}

.expando-panel {
  margin: 0 1.25rem;
  padding: 1rem 0 1.25rem;
  border-top: 1px solid var(--hairline);
}

.expando-body {
  font-size: 0.95rem;
  line-height: 1.6;
  animation: expando-reveal 140ms ease-out;
}

.expando-body p {
  margin: 0 0 0.9rem;
}

.expando-body p:last-child {
  margin-bottom: 0;
}

@keyframes expando-reveal {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .expando-chevron {
    transition: none;
  }

  .expando-body {
    animation: none;
  }
}

/* ======================================================================== */
/* Orders wave (spec §4/§8): flash banners, the reconstituted option, cart  */
/* and My Orders. Additive: nothing above this line changes.                */
/* ======================================================================== */

/* --- Flash banners -------------------------------------------------------- */

.flash-list {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
}

.flash {
  border-radius: 10px;
  padding: 0.7rem 1rem;
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  text-align: left;
}

.flash-error {
  background: #faeceb;
  color: #a03232;
  border: 1px solid #eccfcd;
}

.flash-success {
  background: #eaf6ef;
  color: #23694a;
  border: 1px solid #cfe8da;
}

.flash-info,
.flash-warning {
  background: var(--accent-wash);
  color: var(--ink);
  border: 1px solid #f0e4b8;
}

/* --- Reconstituted option (spec §8: between selector and stepper) --------- */

.recon-option {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
  padding: 0.55rem 1rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.9rem;
}

.recon-option:focus-within {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

.recon-option input[type="checkbox"] {
  accent-color: var(--glow-magenta);
  width: 1rem;
  height: 1rem;
  margin: 0;
}

.recon-label {
  font-weight: 600;
}

.recon-fee {
  color: var(--muted);
  font-weight: 600;
}

/* Agent-only pricing choice (spec §8). A two-pill segmented control in the
   same family as the reconstitution pill above; the selected pill takes the
   accent fill the variant selector uses for its selected card. */
.tier-choice {
  display: flex;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
  padding: 0;
  border: 0;
}

.tier-legend {
  padding: 0 0 0.4rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.tier-option {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 600;
  background: var(--surface);
}

.tier-option.is-selected {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.tier-option:focus-within {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

.tier-option input[type="radio"] {
  accent-color: var(--glow-magenta);
  width: 1rem;
  height: 1rem;
  margin: 0;
}

/* --- Cart ------------------------------------------------------------------ */

.cart-title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.7rem;
  margin: 0 0 1.25rem;
  text-align: center;
}

.cart-lines {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
}

.cart-line {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) auto auto;
  gap: 1rem;
  align-items: center;
  padding: 1rem 0;
  border-top: 1px solid var(--hairline);
  text-align: left;
}

.cart-line:last-child {
  border-bottom: 1px solid var(--hairline);
}

.cart-thumb {
  width: 64px;
  height: 64px;
  object-fit: contain;
  background: var(--surface-tile);
  border-radius: 8px;
  padding: 4px;
}

/* Flex and grid children default to min-width:auto, so a long product name
   would widen the sheet instead of wrapping inside it. */
.cart-line-body,
.order-line-name {
  min-width: 0;
  overflow-wrap: anywhere;
}

.cart-line-body p {
  margin: 0 0 0.2rem;
}

.cart-line-name {
  font-weight: 600;
}

/* Underlined on hover rather than always: a cart is a list of things the
   shopper already chose, and permanently underlining every one of them turns
   a receipt into a page of links. */
.cart-line-link {
  color: inherit;
  text-decoration: none;
}

.cart-line-link:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.cart-line-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

.cart-line-variant {
  color: var(--muted);
  font-weight: 700;
  margin-left: 0.3rem;
}

.cart-line-price,
.cart-line-recon {
  color: var(--muted);
  font-size: 0.82rem;
}

.cart-line-tier {
  margin: 0.15rem 0;
}

/* Badges a cart line Personal or Resale so a mixed agent cart reads at a
   glance (spec §8). Only an agent's cart can hold both. */
.tier-badge {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--surface-tile);
  color: var(--muted);
}

.tier-badge--personal {
  background: var(--accent);
  color: var(--accent-ink);
}

.line-warning {
  color: #a03232;
  font-size: 0.85rem;
  font-weight: 600;
}

.line-preorder {
  color: var(--muted);
  font-size: 0.82rem;
  font-style: italic;
}

.cart-line-controls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.4rem;
}

.cart-qty-form {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
}

.cart-qty-input {
  width: 3.2rem;
  padding: 0.35rem 0.2rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-tile);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  text-align: center;
}

.cart-qty-input:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: -1px;
}

.cart-line-btn {
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface);
  color: var(--glow-purple);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.35rem 0.9rem;
  cursor: pointer;
}

.cart-line-btn:hover {
  border-color: var(--glow-magenta);
  background: var(--surface-tile);
}

.cart-line-btn:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

.cart-line-remove {
  color: #a03232;
}

.cart-line-total {
  font-weight: 700;
  font-size: 1.05rem;
  margin: 0;
  justify-self: end;
}

/* The coupon box, taking the note field's treatment so the two text inputs
   on this page match. Not rendered for an agent at all (spec §4). */
.cart-coupon {
  margin: 1.25rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--hairline);
}

.cart-coupon-form {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.cart-coupon-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.cart-coupon-input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.65rem 0.75rem;
  font: inherit;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
}

.cart-coupon-input:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 1px;
  border-color: var(--glow-magenta);
}

.cart-coupon-applied {
  flex: 1 1 12rem;
  min-width: 0;
  margin: 0 0 0.35rem;
}

/* The shipping section, taking the coupon box's divider so the two blocks
   between the lines and the totals read as one stack. */
.cart-shipping {
  margin: 1.25rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--hairline);
  text-align: left;
}

.cart-shipping-title {
  margin: 0 0 0.5rem;
  font-size: 1rem;
}

.cart-shipping-mode {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.cart-shipping-address {
  margin-bottom: 0.75rem;
}

.cart-shipping-forms {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: flex-end;
  margin-bottom: 0.75rem;
}

.cart-shipping-forms form {
  margin: 0;
}

.cart-service-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.35rem;
}

.cart-service {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0.75rem;
  background: var(--surface-tile);
  border-radius: 8px;
  font-size: 0.92rem;
}

.cart-service--chosen {
  font-weight: 600;
}

/* USPS's own transit estimate, beside the service it belongs to. Quiet and
   secondary: it is a standard, not a guarantee, and it should not compete
   with the price the customer is choosing on. */
.cart-service-eta {
  margin-left: 0.5rem;
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--muted);
}

.cart-shipping-add {
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
}

.cart-shipping-add summary {
  cursor: pointer;
}

.cart-service-apply {
  margin-top: 0.5rem;
}

/* Choosing a service is a round trip that may include a rate call. The list
   dims and stops taking clicks until the reply lands, so a customer does not
   pick a second option while the first is still in flight. Set by cart.js;
   without JS the form never enters this state. */
.cart-service-list--busy .cart-service-list {
  opacity: 0.55;
  pointer-events: none;
}

.cart-service-choice {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Reads as a subtraction rather than another charge. */
.cart-discount dd,
.invoice-credit dd {
  color: var(--glow-red);
}

/* Under the total, not inside it: what is actually applied is decided at
   submission, so this states a fact about the account rather than doing
   arithmetic the cart's figure does not reflect. */
.cart-credit {
  margin: -0.75rem 0 1.5rem;
  text-align: right;
  color: var(--muted);
  font-size: 0.9rem;
}

.cart-credit strong {
  color: var(--glow-red);
}

/* The star on an invite that has bought something - the line that is
   actually earning the discount. Gold rather than the site's teal, because it
   is the one thing on that table worth spotting at a glance. */
.invite-star {
  color: #d4a017;
  font-size: 1.05em;
  margin-left: 0.25rem;
}

/* "Joins your sales group", under the status line rather than beside it: the
   status is what happened to the invite, this is what it carries, and running
   them together on one line reads as a single sentence that is two facts. */
.invite-group {
  display: block;
  font-size: 0.85em;
  opacity: 0.75;
}

.invite-row--qualified td {
  font-weight: 600;
}

.invite-table code {
  font-size: 0.85em;
  letter-spacing: 0.08em;
}

/* The orders table gets its cell rhythm from .orders-cell; these tds carry
   only data-label, so without this they render flush against each other -
   the email ran straight into the code. Same numbers as .orders-cell. */
.invite-table td {
  padding: 0.65rem 0.6rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}

/* The one column that can be arbitrarily long, allowed to break so it cannot
   shove the rest of the row into single-word wraps. */
.invite-table td[data-label="Email"] {
  overflow-wrap: anywhere;
}

.invite-table td[data-label="Status"] {
  white-space: nowrap;
}

/* The invite history is five columns on desktop, but it is a list of records
   on a phone. Letting the table keep its min-content width makes its card
   wider than the viewport (email and code are especially stubborn), so each
   row becomes a labelled stack at the same breakpoint as the orders table. */
@media (max-width: 560px) {
  .invite-table,
  .invite-table tbody,
  .invite-table tr,
  .invite-table td {
    display: block;
    width: 100%;
  }

  .invite-table thead {
    display: none;
  }

  .invite-table tr {
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--rule);
  }

  .invite-table td {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr);
    gap: 0.65rem;
    padding: 0.3rem 0;
    border: 0;
    overflow-wrap: anywhere;
    /* The desktop status column holds its date on one line; stacked rows
       have no columns to protect and a nowrap here would overflow the card. */
    white-space: normal;
  }

  .invite-table td::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  .invite-actions {
    width: auto;
    text-align: left;
  }
}

.invite-banner {
  border-left: 3px solid var(--glow-red);
  padding-left: 0.75rem;
}

/* --- Business card downloads on the agent code card --------------------- */

/* Download tiles rather than text links: these are files to take, not pages
   to visit, so they dress like the account surfaces instead of wearing
   --link-ink. */
.card-print {
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--hairline);
}

.card-print-previews {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  margin-bottom: 1rem;
}

.card-print-previews img {
  width: min(46%, 15rem);
  height: auto;
  border-radius: 8px;
  border: 1px solid var(--hairline);
}

.card-print-title {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
}

.card-print-lede {
  margin: 0 0 0.9rem;
  color: var(--muted);
  font-size: 0.88rem;
}

.card-files {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10.5rem, 100%), 1fr));
  gap: 0.6rem;
}

.card-file {
  display: block;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: var(--surface-tile);
  color: var(--ink);
  text-decoration: none;
}

.card-file:hover {
  border-color: var(--glow-magenta);
}

.card-file:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

/* The file a print shop actually asks for, marked out from the variants. */
.card-file--primary {
  border-color: var(--glow-magenta);
  background: var(--accent-wash);
}

.card-file-kind {
  display: inline-block;
  padding: 0.1rem 0.35rem;
  border: 1px solid currentColor;
  border-radius: 4px;
  color: var(--glow-magenta);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 0.35rem;
}

.card-file-name {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
}

.card-file-meta {
  display: block;
  margin-top: 0.15rem;
  color: var(--muted);
  font-size: 0.75rem;
}

.account-credit-figure {
  margin: 0 0 0.5rem;
  font-size: 1.8rem;
  font-weight: 600;
  color: var(--glow-red);
}

.cart-totals {
  margin: 0 0 1.5rem;
  text-align: right;
}

.cart-totals div {
  display: flex;
  justify-content: flex-end;
  gap: 1.5rem;
  margin-bottom: 0.3rem;
}

.cart-totals dt {
  color: var(--muted);
  font-size: 0.9rem;
}

.cart-totals dd {
  margin: 0;
  min-width: 5.5rem;
  font-weight: 600;
}

.cart-grand dt,
.cart-grand dd {
  color: var(--ink);
  font-weight: 700;
  font-size: 1.15rem;
}

.cart-submit-form {
  text-align: left;
}

.cart-note-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.cart-note {
  width: 100%;
  padding: 0.65rem 0.75rem;
  font: inherit;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  resize: vertical;
  margin-bottom: 1rem;
}

.cart-note:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 1px;
  border-color: var(--glow-magenta);
}

.cart-track-peps {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  margin: 0 0 1rem;
  padding: 0.8rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface) 82%, var(--glow-magenta));
  font-size: 0.88rem;
}

.cart-track-peps input { margin-top: 0.2rem; }

.cart-aside {
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  margin: 0.9rem 0 0;
}

.cart-empty {
  text-align: center;
}

/* --- My Peps --------------------------------------------------------------- */

.my-peps-page { max-width: 1180px; }

.my-peps-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
}

.my-peps-head h1,
.my-peps-form-card h1 { margin: 0 0 0.25rem; }
.my-peps-head p { margin: 0; color: var(--muted); }
.my-peps-head > .btn { width: auto; flex: 0 0 auto; margin-top: 0; }

.my-peps-tabs {
  display: flex;
  gap: 1.5rem;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 1.2rem;
}

.my-peps-tabs a {
  color: var(--muted);
  text-decoration: none;
  padding: 0.65rem 0;
  border-bottom: 2px solid transparent;
}

.my-peps-tabs a.is-active {
  color: var(--ink);
  border-color: var(--glow-magenta);
}

.my-peps-settings-list { display: grid; gap: 0.9rem; margin-bottom: 1.1rem; }
.my-peps-settings-row { padding: 0.75rem 0.8rem; border: 1px solid var(--hairline); border-radius: 0.6rem; }
.my-peps-settings-row > div { display: flex; align-items: center; gap: 0.5rem; }
.my-peps-settings-row > div label { margin: 0; cursor: pointer; }
.my-peps-settings-row input[type="checkbox"] { width: auto; margin: 0; accent-color: var(--series-teal); }
.my-peps-settings-row > .helptext { display: block; margin-top: 0.35rem; line-height: 1.45; }

/* The "reminders moved" line. It used to sit BELOW the card, bare on the
   aurora, and it was the only content on this page with nothing under it -
   the same fault the guide's tables and legal footer were given a panel for
   (5b1d58a, 4073b3c). Here there was no need to invent a second panel: the
   sentence is about these settings, so it comes inside the one that is
   already here, as a footer row in the .my-peps-chart-note idiom - hairline
   above, --muted below.
   Measured on the live background, --muted was 2.14:1 where a ribbon crossed
   the line; on the card's surface it is 7.68:1 against --surface-worst-case. */
.my-peps-settings-footnote {
  margin: 1.1rem 0 0;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

/* The link was not styled at all out there - outside a card nothing matches
   `.card a`, so it fell back to the browser's own blue, which measured
   2.08:1 on the ribbon behind it. It cannot simply inherit `.card a` either:
   --glow-purple is 3.78:1 on --surface-worst-case, under AA for text this
   size. So the WORD carries --ink and the purple moves to the underline,
   where 3.78:1 clears the 3:1 that non-text needs. Always underlined, since
   colour alone is not an affordance. */
.my-peps-settings-footnote a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--glow-purple);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

.my-peps-settings-footnote a:hover {
  text-decoration-color: var(--ink);
}

.my-peps-settings-footnote a:focus-visible {
  outline: 2px solid var(--glow-purple);
  outline-offset: 2px;
  border-radius: 3px;
}

.my-peps-list { display: grid; gap: 0.85rem; margin-top: 1rem; }
.my-peps-card { padding: 1rem; }

.my-peps-card-head {
  display: grid;
  grid-template-columns: 42px 1fr auto;
  align-items: center;
  gap: 0.75rem;
}

.my-peps-card-head h2 { margin: 0; font-size: 1.05rem; }
.my-peps-card-head p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.82rem; }
.my-peps-card-head > strong { text-align: right; }
.my-peps-card-head > strong small { display: block; color: var(--muted); font-weight: 400; }

.my-peps-card-metrics { display: flex; align-items: flex-start; justify-content: flex-end; gap: 0.65rem; min-width: 7.5rem; }
.my-peps-card-metric { display: grid; justify-items: center; gap: 0.2rem; min-width: 3.25rem; }
.my-peps-card-metric > small { color: var(--muted); font-size: 0.6rem; line-height: 1; white-space: nowrap; }
.my-peps-vial-metric { max-width: 12rem; }
.my-peps-vial-stack { display: flex; align-items: flex-end; justify-content: center; gap: 0.22rem; min-height: 3.35rem; }
.my-peps-inventory-vial,
.my-peps-coverage-calendar { position: relative; display: grid; place-items: center; width: 3.15rem; height: 3.35rem; color: var(--series-teal); }
.my-peps-inventory-vial { overflow: hidden; margin-top: 0.18rem; height: 3.08rem; border: 2px solid currentColor; border-radius: 0.35rem 0.35rem 0.55rem 0.55rem; background: rgba(8, 12, 22, 0.82); }
.my-peps-inventory-vial::before { content: ""; position: absolute; z-index: 0; inset: auto 0 0; height: calc(var(--remaining) * 1%); max-height: 100%; background: color-mix(in srgb, currentColor 68%, transparent); transition: height 180ms ease; }
.my-peps-inventory-vial::after { content: ""; position: absolute; z-index: 2; top: -0.2rem; left: 50%; width: 1.8rem; height: 0.36rem; border: 2px solid currentColor; border-radius: 0.2rem; background: var(--surface); transform: translateX(-50%); }
.my-peps-inventory-vial > i { position: absolute; z-index: 1; inset: 0; background: linear-gradient(110deg, transparent 18%, rgba(255,255,255,.14) 19%, transparent 31%); }
.my-peps-inventory-vial.has-neon-art { width: 2.2rem; height: 3.35rem; margin-top: 0; border: 0; border-radius: 0.45rem; background: #020204; box-shadow: 0 0 12px color-mix(in srgb, currentColor 38%, transparent); }
.my-peps-inventory-vial.has-neon-art::before,
.my-peps-inventory-vial.has-neon-art::after { display: none; }
.my-peps-inventory-vial.has-neon-art > img { position: absolute; z-index: 1; top: 50%; left: 50%; width: 190%; max-width: none; height: auto; transform: translate(-50%, -50%); }
.my-peps-liquid-chamber { position: absolute; z-index: 2; right: 28%; bottom: 13%; left: 28%; height: 54%; overflow: hidden; border-radius: 0.2rem 0.2rem 0.35rem 0.35rem; background: rgba(6, 8, 18, 0.9); box-shadow: inset 0 0 5px rgba(255,255,255,.12); }
.my-peps-liquid-chamber > i { position: absolute; inset: auto 0 0; height: calc(var(--remaining) * 1%); border-radius: 0.14rem 0.14rem 0.3rem 0.3rem; background: color-mix(in srgb, currentColor 72%, #25114a); box-shadow: 0 -1px 5px color-mix(in srgb, currentColor 68%, transparent), inset 0 1px rgba(255,255,255,.38); transition: height 180ms ease; }
.my-peps-reserve-vial { position: relative; display: block; overflow: hidden; width: 2.1rem; height: 3.05rem; margin-bottom: 0.02rem; border-radius: 0.35rem; background: #020204; box-shadow: 0 0 10px rgba(177, 78, 255, 0.2); }
.my-peps-reserve-vial > img { position: absolute; top: 50%; left: 50%; width: 225%; max-width: none; height: auto; transform: translate(-50%, -50%); }
.my-peps-reserve-vial > i { position: absolute; inset: 0.2rem; border: 1px solid #b14eff; border-radius: 0.2rem 0.2rem 0.35rem 0.35rem; }
.my-peps-reserve-vial > b { position: absolute; z-index: 2; inset: 0; display: grid; place-items: center; color: var(--ink); font-size: 0.9rem; line-height: 1; text-shadow: 0 1px 4px #080c16, 0 0 8px #080c16; }
.my-peps-inventory-vial > b,
.my-peps-coverage-calendar > b { position: relative; z-index: 3; color: var(--ink); font-size: 0.9rem; line-height: 1; text-shadow: 0 1px 4px #080c16; }
.my-peps-coverage-calendar { height: 3.25rem; border: 2px solid currentColor; border-radius: 0.55rem; background: color-mix(in srgb, currentColor 9%, transparent); }
.my-peps-coverage-calendar::before { content: ""; position: absolute; inset: 0.62rem 0 0; border-top: 2px solid currentColor; background: repeating-linear-gradient(90deg, transparent 0 0.6rem, color-mix(in srgb, currentColor 14%, transparent) 0.6rem 0.66rem); opacity: 0.75; }
.my-peps-coverage-calendar > i::before,
.my-peps-coverage-calendar > i::after { content: ""; position: absolute; z-index: 2; top: -0.22rem; width: 0.28rem; height: 0.62rem; border-radius: 999px; background: currentColor; }
.my-peps-coverage-calendar > i::before { left: 0.7rem; }
.my-peps-coverage-calendar > i::after { right: 0.7rem; }
.my-peps-coverage-calendar > b { margin-top: 0.55rem; font-size: 1.05rem; }
.my-peps-coverage-calendar > b.is-overflow { font-size: 0.78rem; }
.my-peps-inventory-vial.is-warning,
.my-peps-coverage-calendar.is-warning { color: var(--series-amber); }
.my-peps-inventory-vial.is-danger,
.my-peps-coverage-calendar.is-danger { color: #ff5d73; }

.my-peps-vial-icon {
  width: 40px;
  height: 40px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  color: var(--glow-magenta);
  border: 1px solid var(--hairline);
}

.my-peps-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
  margin: 0.9rem 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--hairline);
}

.my-peps-facts dt { color: var(--muted); font-size: 0.72rem; }
.my-peps-facts dd { margin: 0.2rem 0 0; font-size: 0.86rem; font-weight: 600; }
.my-peps-facts dd small { display: block; margin-top: 0.15rem; color: var(--muted); font-weight: 400; }
.my-peps-titration-prompt { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.8rem; margin: 0.75rem 0; padding: 0.75rem; border: 1px solid rgba(167, 139, 250, 0.55); border-radius: 0.65rem; background: rgba(167, 139, 250, 0.08); }
.my-peps-titration-prompt strong { color: #c4afff; font-size: 0.78rem; }
.my-peps-titration-prompt p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.7rem; line-height: 1.45; }
.my-peps-titration-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.35rem; flex-wrap: wrap; }
.my-peps-titration-actions form { margin: 0; }
.my-peps-setup-guide { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.65rem 1rem; align-items: center; margin: 0.75rem 0; padding: 0.8rem; border: 1px solid rgba(255, 180, 84, 0.55); border-radius: 0.65rem; background: rgba(255, 180, 84, 0.07); }
.my-peps-setup-guide strong { color: var(--series-amber); font-size: 0.82rem; }
.my-peps-setup-guide p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.72rem; line-height: 1.45; }
.my-peps-setup-guide ol { grid-column: 1 / -1; margin: 0; padding-left: 1.25rem; color: var(--ink); font-size: 0.72rem; line-height: 1.6; }
.my-peps-setup-guide > .my-peps-action { grid-column: 2; grid-row: 1; }
.my-peps-setup-guide.is-ready { grid-template-columns: 1fr; border-color: rgba(85, 214, 182, 0.55); background: rgba(85, 214, 182, 0.07); }
.my-peps-setup-guide.is-ready strong { color: var(--series-teal); }

.my-peps-card-actions { display: flex; justify-content: flex-end; gap: 0.45rem; flex-wrap: wrap; }
.my-peps-card-actions form { margin: 0; }

.my-peps-action,
.my-peps-event button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 32px;
  padding: 0.35rem 0.65rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  text-decoration: none;
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.my-peps-action--primary { border-color: var(--glow-magenta); color: var(--glow-magenta); }
.my-peps-action--danger { border-color: var(--glow-red); color: var(--glow-red); }
.my-peps-stock { margin: 0.7rem 0 0; color: var(--muted); font-size: 0.76rem; }

.my-peps-outlook { padding: clamp(1rem, 2.5vw, 1.5rem); min-width: 0; }
.my-peps-chart-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.my-peps-chart-toolbar h2 { margin: 0; font-size: 1rem; }
.my-peps-chart-toolbar p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.75rem; }
.my-peps-chart-legend { display: flex; justify-content: flex-end; gap: 0.35rem 0.75rem; flex-wrap: wrap; font-size: 0.7rem; color: var(--muted); }
.my-peps-chart-legend span { display: inline-flex; align-items: center; gap: 0.3rem; }
.my-peps-chart-legend i { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--series-color); box-shadow: 0 0 9px color-mix(in srgb, var(--series-color) 55%, transparent); }
.my-peps-chart-legend small { color: var(--series-color); font-size: .62rem; text-transform: uppercase; letter-spacing: .05em; }
.my-peps-chart-legend span.is-secondary i { border-radius: 2px; opacity: .5; }
.my-peps-chart-ranges { display: inline-flex; flex-wrap: wrap; gap: 0.2rem; margin: 0 0 0.55rem; padding: 0.2rem; border: 1px solid var(--hairline); border-radius: 0.6rem; background: var(--surface-tile); }
.my-peps-chart-ranges a { padding: 0.25rem 0.55rem; border-radius: 0.4rem; color: var(--muted); font-size: 0.7rem; font-weight: 650; text-decoration: none; }
.my-peps-chart-ranges a:hover,
.my-peps-chart-ranges a:focus-visible { color: var(--ink); }
.my-peps-chart-ranges a:focus-visible { outline: 2px solid var(--series-teal); outline-offset: 1px; }
.my-peps-chart-ranges a.is-active { color: #071713; background: var(--series-teal); box-shadow: 0 0 12px rgba(85, 214, 182, 0.24); }
.my-peps-chart-current { display: flex; gap: 0.35rem 0.75rem; flex-wrap: wrap; margin: 0 0 0.55rem; color: var(--muted); font-size: 0.68rem; }
.my-peps-chart-current span { display: inline-flex; align-items: center; gap: 0.25rem; }
.my-peps-chart-current i { width: 0.42rem; height: 0.42rem; border-radius: 50%; background: var(--series-color); }
.my-peps-chart-current strong { color: var(--ink); }
.my-peps-chart-event-key { display: flex; align-items: center; justify-content: flex-end; gap: 0.75rem; margin: -0.2rem 0 0.45rem; color: var(--muted); font-size: 0.62rem; }
.my-peps-chart-event-key span { display: inline-flex; align-items: center; gap: 0.3rem; }
.my-peps-chart-event-key i { width: 0.55rem; height: 0.55rem; border-radius: 999px; }
.my-peps-chart-event-key i.is-completed { background: var(--glow-magenta); box-shadow: 0 0 5px color-mix(in srgb, var(--glow-magenta) 55%, transparent); }
.my-peps-chart-event-key i.is-automatic { border: 2px dotted var(--series-amber); background: color-mix(in srgb, var(--series-amber) 18%, var(--surface)); }
.my-peps-chart-event-key i.is-scheduled { border: 2px solid var(--glow-magenta); background: var(--surface); }
.my-peps-chart-event-key i.is-assumed { border: 2px dotted var(--glow-magenta); background: color-mix(in srgb, var(--glow-magenta) 18%, var(--surface)); }
.my-peps-chart-event-key i.is-skipped { border: 1px solid var(--glow-red); background: linear-gradient(45deg, transparent 42%, var(--glow-red) 43% 57%, transparent 58%), linear-gradient(-45deg, transparent 42%, var(--glow-red) 43% 57%, transparent 58%); }
.my-peps-chart-event-key i.is-without-skips { width: 0.85rem; height: 0; border-radius: 0; border-top: 2px dashed var(--glow-magenta); }
.my-peps-series-0 { --series-color: var(--series-teal); }
.my-peps-series-1 { --series-color: var(--series-violet); }
.my-peps-series-2 { --series-color: var(--glow-magenta); }
.my-peps-series-3 { --series-color: var(--series-amber); }
.my-peps-series-4 { --series-color: var(--series-blue); }
.my-peps-series-5 { --series-color: var(--glow-red); }
.my-peps-chart-frame { width: 100%; overflow: hidden; }
.my-peps-chart { display: block; width: 100%; height: auto; overflow: visible; }
.my-peps-chart-grid line { stroke: var(--hairline); stroke-width: 1; vector-effect: non-scaling-stroke; }
.my-peps-today-marker rect { fill: rgba(85, 214, 182, .055); stroke: var(--series-teal); stroke-width: 1; stroke-dasharray: 3 3; opacity: .9; vector-effect: non-scaling-stroke; }
.my-peps-today-marker text { fill: var(--series-teal); font-size: 9px; font-weight: 750; paint-order: stroke; stroke: var(--surface); stroke-width: 3px; }
.my-peps-dose-line { stroke: var(--hairline); stroke-width: 1; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; }
.my-peps-titration-line { stroke: var(--series-color); stroke-width: 1.5; stroke-dasharray: 2 3; opacity: 0.7; vector-effect: non-scaling-stroke; }
.my-peps-titration-marker { fill: var(--surface); stroke: var(--series-color); stroke-width: 2; vector-effect: non-scaling-stroke; }
.my-peps-chart-line { fill: none; stroke: var(--series-color); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 4px color-mix(in srgb, var(--series-color) 38%, transparent)); }
.my-peps-chart-line.is-secondary { stroke-dasharray: 7 5; opacity: .8; }
.my-peps-without-skips-line { fill: none; stroke: var(--series-color); stroke-width: 2; stroke-dasharray: 5 5; opacity: .38; vector-effect: non-scaling-stroke; }
.my-peps-chart-band { fill: color-mix(in srgb, var(--series-color) 24%, transparent); stroke: color-mix(in srgb, var(--series-color) 70%, transparent); stroke-width: 1; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; }
.my-peps-dose-marker { fill: var(--surface); stroke: var(--series-color); stroke-width: 2; vector-effect: non-scaling-stroke; }
.my-peps-completed-dose-marker { fill: var(--series-color); stroke: var(--ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 3px var(--series-color)); }
.my-peps-automatic-dose-marker { fill: color-mix(in srgb, var(--series-amber) 18%, var(--surface)); stroke: var(--series-amber); stroke-width: 2; stroke-dasharray: 1.5 1.5; vector-effect: non-scaling-stroke; }
.my-peps-assumed-dose-marker { fill: color-mix(in srgb, var(--series-color) 18%, var(--surface)); stroke: var(--series-color); stroke-width: 2; stroke-dasharray: 1.5 1.5; vector-effect: non-scaling-stroke; }
.my-peps-skipped-dose-marker circle { fill: var(--surface); stroke: var(--glow-red); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.my-peps-skipped-dose-marker line { stroke: var(--glow-red); stroke-width: 1.5; stroke-linecap: round; vector-effect: non-scaling-stroke; pointer-events: none; }
.my-peps-chart-labels { fill: var(--muted); font-size: 10px; }
.my-peps-chart-note { display: flex; align-items: flex-start; gap: 0.45rem; margin: 0.65rem 0 0; padding-top: 0.65rem; border-top: 1px solid var(--hairline); color: var(--muted); font-size: 0.7rem; line-height: 1.45; }
.my-peps-chart-note i { flex: 0 0 auto; margin-top: 0.1rem; color: var(--series-color, var(--series-teal)); }
.my-peps-muted { color: var(--muted); }
.my-peps-chart-empty { min-height: 180px; display: grid; place-items: center; align-content: center; gap: 0.7rem; color: var(--muted); border-block: 1px solid var(--hairline); }
.my-peps-chart-empty i { font-size: 2rem; color: var(--glow-magenta); }
.my-peps-disclaimer { color: var(--muted); font-size: 0.72rem; }
.my-peps-empty { padding: 2rem; text-align: center; }
.my-peps-empty > i { font-size: 2.5rem; color: var(--glow-magenta); }

.my-peps-form-card { padding: 1.25rem; }

.my-peps-telegram-card { margin-bottom: 1rem; }
.my-peps-telegram-status {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0;
  padding: 0.85rem 1rem;
  border: 1px solid rgba(66, 230, 164, 0.45);
  border-radius: 0.75rem;
  background: rgba(66, 230, 164, 0.08);
}
.my-peps-telegram-status i { color: #42e6a4; font-size: 1.4rem; }
.my-peps-telegram-steps {
  list-style: none;
  counter-reset: telegram-step;
  display: grid;
  gap: 0.8rem;
  margin: 1.1rem 0 0;
  padding: 0;
}
.my-peps-telegram-steps > li {
  counter-increment: telegram-step;
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.8rem;
  padding: 0.85rem;
  border: 1px solid var(--hairline);
  border-radius: 0.75rem;
  background: rgba(255, 255, 255, 0.025);
}
.my-peps-telegram-steps > li::before {
  content: counter(telegram-step);
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  color: #fff;
  font-weight: 800;
  background: linear-gradient(135deg, var(--glow-purple), var(--glow-red));
}
.my-peps-telegram-steps p { margin: 0.2rem 0 0; color: var(--muted); }
.my-peps-step-waiting { color: var(--muted); font-size: 0.9rem; }
.my-peps-telegram-fallback { margin-top: 1rem; }
.my-peps-telegram-fallback summary { cursor: pointer; font-weight: 700; }
.my-peps-telegram-command code { overflow-wrap: anywhere; }

@media (max-width: 620px) {
  .my-peps-telegram-steps > li { grid-template-columns: 2.25rem minmax(0, 1fr); }
  .my-peps-telegram-steps > li > :last-child:not(div) { grid-column: 2; justify-self: start; }
}
.my-peps-back { display: inline-block; margin-bottom: 0.8rem; }
.my-peps-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
.my-peps-form-grid .account-field {
  min-width: 0;
  margin: 0 0 1rem;
}

.my-peps-form-grid .account-field > label {
  display: block;
  margin-bottom: 0.35rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.my-peps-form-grid .errorlist {
  margin: 0.35rem 0 0;
}

/* Django renders different native controls for decimal, time and choice
   fields. Keep the whole My Peps form family on the same visual system as
   the site's text fields instead of letting selects and number inputs fall
   back to bright browser defaults. Checkboxes are handled separately below
   so they never inherit the full-column width. */
.my-peps-form-grid input:not([type="checkbox"]):not([type="radio"]),
.my-peps-form-grid select,
.my-peps-form-grid textarea {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background-color: var(--surface);
  color: var(--ink);
  color-scheme: dark;
  font: inherit;
}

.my-peps-form-grid input:not([type="checkbox"]):not([type="radio"]):focus,
.my-peps-form-grid select:focus,
.my-peps-form-grid textarea:focus {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 1px;
  border-color: var(--glow-magenta);
}

.my-peps-form-grid input:not([type="checkbox"]):not([type="radio"]):disabled,
.my-peps-form-grid select:disabled,
.my-peps-form-grid textarea:disabled {
  cursor: not-allowed;
  opacity: 0.58;
}

.my-peps-form-grid input[type="checkbox"],
.my-peps-form-grid input[type="radio"] {
  width: 1.1rem;
  height: 1.1rem;
  margin: 0.15rem 0 0;
  accent-color: var(--glow-magenta);
  vertical-align: top;
}

.my-peps-form-grid input[type="checkbox"]:disabled,
.my-peps-form-grid input[type="radio"]:disabled {
  cursor: not-allowed;
  opacity: 0.58;
}
.my-peps-recon-card { width: 100%; }
.my-peps-recon-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; margin-bottom: 1.15rem; }
.my-peps-recon-head h1 { margin: 0; }
.my-peps-recon-head p { margin: 0.35rem 0 0; color: var(--muted); }
/* A fact, not a control. It wore a teal border and teal text, which on this
   page is the costume every pressable thing wears - so the strength readout
   looked like the button nothing happens when you press. Teal survives on the
   icon, where a colour with no box around it cannot be mistaken for a button. */
.my-peps-recon-vial { display: inline-flex; align-items: center; gap: 0.45rem; flex: 0 0 auto; padding: 0.5rem 0.7rem; border: 1px solid var(--hairline); border-radius: 0.65rem; background: var(--surface-tile); color: var(--ink); font-weight: 700; }
.my-peps-recon-vial > i { color: var(--series-teal); }
/* The QUESTIONS are the wider column. They used to be the narrower one
   (0.95fr against the preview's 1.05fr), which put the thing being asked
   second in rank behind the thing reporting on it. */
.my-peps-recon-layout { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(min(340px, 100%), 0.85fr); gap: 1rem; align-items: start; }
.my-peps-recon-panel { min-width: 0; padding: 1rem; border: 1px solid var(--hairline); border-radius: 0.8rem; background: var(--surface-tile); }
.my-peps-recon-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.8rem; margin-bottom: 0.9rem; }
.my-peps-recon-panel-head h2 { margin: 0; font-size: 1rem; }
.my-peps-recon-panel-head p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.75rem; }
.my-peps-recon-panel-head > i { color: var(--series-teal); font-size: 1.3rem; }
.my-peps-mixing-head { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--hairline); }
.my-peps-schedule-summary { display: flex; justify-content: space-between; align-items: center; gap: 0.8rem; margin: 0 0 1rem; padding: 0.65rem 0.75rem; border-left: 3px solid var(--series-teal); border-radius: 0.45rem; background: rgba(85, 214, 182, 0.08); font-size: 0.78rem; }
.my-peps-schedule-summary span { color: var(--muted); text-align: right; }
.my-peps-recon-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 0 0.75rem; }
.my-peps-recon-fields .account-field { min-width: 0; margin: 0 0 0.85rem; }
.my-peps-recon-fields .account-field > label { display: block; margin-bottom: 0.35rem; font-size: 0.8rem; font-weight: 650; }
.my-peps-recon-fields .my-peps-field-wide { grid-column: 1 / -1; }
.my-peps-dose-control { display: grid; grid-template-columns: minmax(0, 1fr) 7.5rem; gap: 0.45rem; }
.my-peps-dose-control > div { min-width: 0; }
.my-peps-current-week { display: inline-flex; margin-top: 0.45rem; padding: 0.2rem 0.45rem; border-radius: 999px; background: rgba(85, 214, 182, 0.09); color: var(--series-teal); font-size: 0.68rem; }
.my-peps-split-field { padding: 0.65rem 0.75rem; border: 1px solid var(--hairline); border-radius: 0.6rem; background: rgba(255, 255, 255, 0.018); }
.my-peps-split-field > input { width: auto; margin: 0 0.4rem 0 0; accent-color: var(--series-teal); }
.my-peps-recon-fields .my-peps-split-field > label { display: inline; margin: 0; cursor: pointer; }
.my-peps-split-field > p { margin: 0.45rem 0 0; color: var(--series-teal); font-size: 0.72rem; line-height: 1.4; }
.my-peps-recon-fields input:not([type="checkbox"]):not([type="radio"]),
.my-peps-recon-fields select,
.my-peps-recon-fields textarea { display: block; width: 100%; min-height: 44px; padding: 0.65rem 0.75rem; border: 1px solid var(--hairline); border-radius: 8px; background-color: var(--surface); color: var(--ink); color-scheme: dark; font: inherit; }
.my-peps-recon-fields input:not([type="checkbox"]):not([type="radio"]):focus,
.my-peps-recon-fields select:focus,
.my-peps-recon-fields textarea:focus { outline: 2px solid var(--series-teal); outline-offset: 1px; border-color: var(--series-teal); }
.my-peps-recon-fields input:invalid { border-color: rgba(255, 65, 94, 0.7); }
.my-peps-recon-fields .errorlist { margin: 0.35rem 0 0; }
.my-peps-recon-preview { position: sticky; top: 5.5rem; }
.my-peps-preview-dose { display: grid; place-items: center; min-height: 112px; margin: 0.4rem 0 0.9rem; padding: 0.8rem; border-radius: 0.7rem; background: rgba(85, 214, 182, 0.07); text-align: center; }
.my-peps-preview-dose > span { color: var(--muted); font-size: 0.75rem; }
.my-peps-preview-dose strong { color: var(--series-teal); font-size: clamp(2rem, 4vw, 3rem); line-height: 1; letter-spacing: -0.04em; }
.my-peps-preview-dose small { color: var(--muted); }
.my-peps-syringe { position: relative; height: 68px; margin: 1rem 0 0; }
.my-peps-barrel { position: absolute; inset: 8px 1.5%; overflow: hidden; border: 2px solid rgba(210, 225, 238, 0.48); border-radius: 0.35rem; background: linear-gradient(180deg, rgba(226, 239, 248, 0.13), rgba(175, 196, 212, 0.04) 48%, rgba(226, 239, 248, 0.1)); box-shadow: inset 0 1px rgba(255, 255, 255, 0.18), inset 0 -1px rgba(0, 0, 0, 0.32); }
.my-peps-barrel::after { content: ""; position: absolute; inset: 5px 8px auto; height: 7px; border-radius: 999px; background: linear-gradient(90deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.04)); pointer-events: none; }
.my-peps-syringe-fill { position: absolute; inset: 2px auto 2px 0; width: 0; border-right: 2px solid var(--series-teal); border-radius: 0 0.35rem 0.35rem 0; background: linear-gradient(180deg, rgba(116, 235, 209, 0.44), rgba(42, 171, 151, 0.64)); box-shadow: inset 0 7px rgba(255, 255, 255, 0.08); transition: width 180ms ease; }
.my-peps-syringe-ticks { position: absolute; inset: 0; background: repeating-linear-gradient(90deg, transparent 0 calc(var(--syringe-minor-step, 2%) - 1px), rgba(228, 239, 246, 0.17) calc(var(--syringe-minor-step, 2%) - 1px) var(--syringe-minor-step, 2%)), repeating-linear-gradient(90deg, transparent 0 calc(var(--syringe-major-step, 10%) - 1px), rgba(238, 246, 251, 0.48) calc(var(--syringe-major-step, 10%) - 1px) var(--syringe-major-step, 10%)); mask-image: linear-gradient(180deg, #000 0 42%, transparent 42%); pointer-events: none; }
.my-peps-syringe-scale { display: grid; grid-template-columns: repeat(3, 1fr); margin: -0.25rem 1.5% 0; color: var(--muted); font-size: 0.66rem; }
.my-peps-syringe-scale span:first-child { grid-column: 1; }
.my-peps-syringe-scale span:nth-child(2) { text-align: center; }
.my-peps-syringe-scale span:last-child { text-align: right; }
.my-peps-recon-status { margin: 0.85rem 0 1rem; padding: 0.65rem 0.75rem; border: 1px solid var(--hairline); border-radius: 0.55rem; color: var(--muted); font-size: 0.78rem; }
.my-peps-recon-status.is-ok { border-color: rgba(85, 214, 182, 0.55); color: var(--series-teal); background: rgba(85, 214, 182, 0.07); }
.my-peps-recon-status.is-warning { border-color: rgba(255, 180, 84, 0.6); color: var(--series-amber); background: rgba(255, 180, 84, 0.07); }
.my-peps-recon-status.is-error { border-color: rgba(255, 65, 94, 0.65); color: var(--glow-red); background: rgba(255, 65, 94, 0.08); }
.my-peps-recon-preview.is-incompatible .my-peps-syringe-fill { background: rgba(255, 65, 94, 0.34); border-color: var(--glow-red); }
.my-peps-compare-head h3 { margin: 0; font-size: 0.9rem; }
.my-peps-compare-head p { margin: 0.2rem 0 0.65rem; color: var(--muted); font-size: 0.72rem; }
.my-peps-water-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; }
.my-peps-water-option { min-width: 0; padding: 0.65rem; border: 1px solid var(--hairline); border-radius: 0.55rem; background: var(--surface); color: var(--ink); text-align: left; cursor: pointer; }
.my-peps-water-option strong,
.my-peps-water-option span { display: block; }
.my-peps-water-option strong { font-size: 0.78rem; }
.my-peps-water-option span { margin-top: 0.15rem; color: var(--muted); font-size: 0.7rem; }
.my-peps-water-option > i { display: block; height: 0.3rem; overflow: hidden; margin-top: 0.5rem; border-radius: 999px; background: var(--hairline); }
.my-peps-water-option > i > b { display: block; height: 100%; background: var(--series-teal); }
/* These are radio buttons wearing cards. They responded to nothing but a
   border colour, so neither "you can press this" nor "this one is picked"
   ever landed. Now: the tile moves under the finger, and both states are
   carried by a solid chip that says the word. */
.my-peps-water-option:hover { border-color: var(--edge-glow); background: var(--surface-tile); }
.my-peps-water-option:active { transform: translateY(1px); }
.my-peps-water-option:focus-visible { border-color: var(--series-teal); outline: 2px solid var(--series-teal); outline-offset: 1px; }
.my-peps-water-option.is-selected { border: 2px solid var(--series-teal); padding: calc(0.65rem - 1px); background: rgba(85, 214, 182, 0.08); box-shadow: 0 0 12px rgba(85, 214, 182, 0.14); }
.my-peps-water-option.is-incompatible { opacity: 0.75; }
.my-peps-water-option.is-incompatible > i > b { background: var(--glow-red); }
.my-peps-water-chip { display: inline-block; margin-top: 0.45rem; padding: 0.16rem 0.45rem; border-radius: 999px; font-size: 0.64rem; font-style: normal; font-weight: 700; letter-spacing: 0.02em; }
.my-peps-water-chip.is-on { background: var(--series-teal); color: #071713; }
.my-peps-water-chip.is-unfit { background: rgba(255, 65, 94, 0.09); color: var(--glow-red); box-shadow: inset 0 0 0 1px rgba(255, 65, 94, 0.55); }
.my-peps-recon-note { display: flex; align-items: flex-start; gap: 0.45rem; margin: 0.85rem 0 0; padding-top: 0.75rem; border-top: 1px solid var(--hairline); color: var(--muted); font-size: 0.7rem; line-height: 1.45; }
.my-peps-recon-note i { flex: 0 0 auto; color: var(--series-teal); }
.my-peps-titration-forecast { margin: 1rem 0; padding: 0.85rem; border: 1px solid var(--hairline); border-radius: 0.7rem; background: rgba(255, 255, 255, 0.018); }
.my-peps-titration-forecast[hidden] { display: none; }
.my-peps-titration-alert { margin-bottom: 0.65rem; padding: 0.65rem 0.75rem; border-radius: 0.55rem; font-size: 0.75rem; line-height: 1.4; }
.my-peps-titration-alert.is-strong { border: 1px solid rgba(255, 65, 94, 0.65); color: var(--glow-red); background: rgba(255, 65, 94, 0.09); }
.my-peps-titration-alert.is-soft { border: 1px solid rgba(255, 180, 84, 0.55); color: var(--series-amber); background: rgba(255, 180, 84, 0.07); }
.my-peps-titration-rows { display: grid; gap: 0.4rem; }
.my-peps-titration-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 0.65rem; padding: 0.55rem 0.65rem; border: 1px solid var(--hairline); border-radius: 0.5rem; font-size: 0.72rem; }
.my-peps-titration-row span strong,
.my-peps-titration-row span small { display: block; }
.my-peps-titration-row span small { margin-top: 0.1rem; color: var(--muted); }
.my-peps-titration-row > b { color: var(--ink); font-size: 0.8rem; }
.my-peps-titration-row > em { min-width: 4.6rem; color: var(--series-teal); font-style: normal; text-align: right; }
.my-peps-titration-row.is-high > em,
.my-peps-titration-row.is-full > em { color: var(--series-amber); }
.my-peps-titration-row.is-incompatible > em { color: var(--glow-red); }
/* Was one element: a full-width teal-bordered paragraph that happened to be a
   <button>, so the sentence looked pressable and the press looked like prose.
   Split - the reading is text, the action is a solid button under it. */
.my-peps-recommendation { display: grid; margin-top: 0.9rem; padding: 0.7rem 0.75rem; border: 1px solid var(--hairline); border-radius: 0.55rem; background: rgba(85, 214, 182, 0.06); }
.my-peps-recommendation > p { margin: 0 0 0.6rem; color: var(--ink); font-size: 0.72rem; line-height: 1.45; }
.my-peps-recommendation > .my-peps-action { justify-self: end; }
/* The offer became a fact. Teal is data and status on this page, and "you are
   already on the best fit" is a status - the button is hidden by the script,
   so the card collapses to the sentence and stops being pressable when
   pressing it would do nothing. */
.my-peps-recommendation.is-current { border-color: rgba(85, 214, 182, 0.55); }
.my-peps-recommendation.is-current > p { margin-bottom: 0; color: var(--series-teal); }
/* `[hidden]` is a USER-AGENT rule and loses to any author display, and
   .my-peps-action is inline-flex - so setting hidden on the button did
   nothing at all and the confirmation state kept a button that could only
   re-apply what was already applied. Same cascade fact as `.wiz [hidden]`,
   two classes deep so it outranks the single-class display with no
   !important. The .wiz version does not reach here: this page is not one. */
.my-peps-recon-card [hidden] { display: none; }
.my-peps-delete-actions { display: flex; align-items: center; gap: 0.65rem; margin-top: 1.25rem; flex-wrap: wrap; }
.my-peps-delete-actions form { margin: 0; }
.my-peps-result { border: 1px solid var(--glow-magenta); border-radius: 9px; padding: 0.8rem; margin: 0 0 1rem; }
.my-peps-result h2 { margin: 0; }
.my-peps-result span { display: block; color: var(--muted); font-size: 0.82rem; }
.my-peps-warning { color: var(--warning, #f5a623); }

.my-peps-week-nav { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto auto auto; align-items: center; gap: 0.55rem; margin: 0 0 0.9rem; padding: 0.55rem; border: 1px solid var(--hairline); border-radius: 0.65rem; background: rgba(8, 12, 22, 0.58); }
.my-peps-week-nav > strong { text-align: center; font-size: 0.82rem; }
.my-peps-week-nav > a,
.my-peps-week-nav > span { padding: 0.38rem 0.5rem; border: 1px solid var(--hairline); border-radius: 0.4rem; color: var(--ink); font-size: 0.7rem; text-decoration: none; white-space: nowrap; }
.my-peps-week-nav > a:hover,
.my-peps-week-nav > a:focus-visible,
.my-peps-week-nav > a[aria-current] { border-color: var(--glow-magenta); outline: none; }
.my-peps-week-nav > span[aria-disabled="true"] { opacity: 0.38; }
.my-peps-past-due { margin-bottom: 0.9rem; padding: 0.9rem; border-color: rgba(255, 180, 84, 0.48); }
.my-peps-past-due > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.7rem; }
.my-peps-past-due > header h2 { margin: 0; color: var(--series-amber); font-size: 1rem; }
.my-peps-past-due > header p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.72rem; }
.my-peps-past-due > header > strong { display: grid; place-items: center; min-width: 1.8rem; min-height: 1.8rem; border-radius: 999px; color: #071713; background: var(--series-amber); }
.my-peps-past-due-list { display: grid; gap: 0.5rem; }
/* minmax(min(Xrem, 100%), ...) rather than minmax(Xrem, ...): minmax's first
   argument is a HARD floor, so a 10rem + 14rem pair overflows any container
   narrower than the sum of its floors - valid CSS, no browser warning, and
   the reason `manage.py healthcheck` carries a check for the pattern. The media
   queries below rescue the narrow end, but between breakpoints the floors
   still bind. min(X, 100%) renders identically wherever the track has room
   and shrinks instead of overflowing where it does not. */
.my-peps-past-due-event { display: grid; grid-template-columns: minmax(min(10rem, 100%), 1fr) minmax(min(14rem, 100%), auto) auto; align-items: end; gap: 0.55rem; padding: 0.65rem; border: 1px solid var(--hairline); border-left: 3px solid var(--series-color); border-radius: 0.5rem; background: rgba(8, 12, 22, 0.45); }
.my-peps-past-due-event > div:first-child { display: grid; gap: 0.1rem; }
.my-peps-past-due-event > div:first-child > strong { color: var(--series-color); }
.my-peps-past-due-event time,
.my-peps-past-due-event span,
.my-peps-past-due-blocked small { color: var(--muted); font-size: 0.7rem; }
.my-peps-past-due-event form { margin: 0; }
.my-peps-past-due-event button,
.my-peps-past-due-blocked a { min-height: 32px; padding: 0.35rem 0.55rem; border: 1px solid var(--hairline); border-radius: 7px; color: var(--ink); background: var(--surface); font: inherit; font-size: 0.7rem; text-decoration: none; cursor: pointer; }
.my-peps-past-due-blocked { display: grid; gap: 0.15rem; }
.my-peps-past-due-blocked > strong { color: var(--series-amber); font-size: 0.7rem; }
.my-peps-mark-taken { display: flex; align-items: end; gap: 0.4rem; flex-wrap: wrap; }
.my-peps-mark-taken label { display: grid; gap: 0.2rem; color: var(--muted); font-size: 0.62rem; }
.my-peps-mark-taken input[type="datetime-local"] { min-height: 32px; max-width: 13rem; padding: 0.3rem 0.45rem; border: 1px solid var(--hairline); border-radius: 7px; color: var(--ink); background: var(--surface); font: inherit; font-size: 0.7rem; color-scheme: dark; }
.my-peps-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.55rem; margin-top: 0.9rem; }
.my-peps-day { min-height: 220px; padding: 0.7rem; }
.my-peps-day[data-current-day="true"] { scroll-margin-top: 5rem; }
.my-peps-day h2 { margin: 0 0 0.8rem; font-size: 0.9rem; }
.my-peps-day h2 small { display: block; color: var(--muted); font-weight: 400; }
.my-peps-event { border: 1px solid color-mix(in srgb, var(--series-color) 28%, var(--hairline)); border-left: 3px solid var(--series-color); padding: 0.55rem; background: color-mix(in srgb, var(--series-color) 7%, var(--surface)); border-radius: 6px; }
.my-peps-event.is-completed { border-color: color-mix(in srgb, var(--series-teal) 46%, var(--hairline)); border-left-color: var(--series-teal); background: rgba(85, 214, 182, 0.07); }
.my-peps-event.is-skipped,
.my-peps-event.is-missed,
.my-peps-event.is-cancelled { border-color: var(--hairline); border-left-color: var(--muted); background: rgba(255, 255, 255, 0.02); }
.my-peps-event.is-dragging { opacity: .45; }
.my-peps-day.is-drop-target { outline: 2px dashed var(--glow-magenta); outline-offset: -4px; }
.my-peps-event strong,
.my-peps-event time,
.my-peps-event > span { display: block; font-size: 0.75rem; }
.my-peps-event strong { color: var(--series-color); }
.my-peps-event time,
.my-peps-event > span { color: var(--muted); }
.my-peps-event > div { display: flex; gap: 0.25rem; margin-top: 0.5rem; flex-wrap: wrap; }
.my-peps-event > .my-peps-event-state { display: grid; gap: 0.12rem; padding: 0.42rem 0.48rem; border-radius: 0.4rem; background: rgba(85, 214, 182, 0.09); }
.my-peps-event-state > b { color: var(--series-teal); font-size: 0.7rem; }
.my-peps-event-state > small { color: var(--muted); font-size: 0.6rem; line-height: 1.25; }
.my-peps-event-state.is-skipped,
.my-peps-event-state.is-missed,
.my-peps-event-state.is-rescheduled { background: rgba(255, 255, 255, 0.035); }
.my-peps-event-state.is-skipped > b,
.my-peps-event-state.is-missed > b,
.my-peps-event-state.is-rescheduled > b { color: var(--muted); }
.my-peps-event-state.is-unconfirmed { background: rgba(255, 180, 84, 0.08); }
.my-peps-event-state.is-unconfirmed > b { color: var(--series-amber); }
.my-peps-dose-inventory { margin-top: 0.55rem; padding: 0.5rem; border: 1px solid rgba(255, 180, 84, 0.48); border-radius: 0.45rem; background: rgba(255, 180, 84, 0.07); }
.my-peps-dose-inventory > strong { display: block; color: var(--series-amber); font-size: 0.7rem; line-height: 1.25; }
.my-peps-dose-inventory > p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.64rem; line-height: 1.35; }
.my-peps-dose-inventory.is-paused { border-color: var(--hairline); background: rgba(255, 255, 255, 0.025); }
.my-peps-dose-inventory.is-paused > strong { color: var(--muted); }
.my-peps-dose-inventory-actions { display: flex; align-items: center; gap: 0.28rem; margin-top: 0.45rem; flex-wrap: wrap; }
.my-peps-dose-inventory-actions form { display: inline-flex; }
.my-peps-dose-inventory-actions a,
.my-peps-dose-inventory-actions button { display: inline-flex; align-items: center; min-height: 1.7rem; padding: 0.25rem 0.4rem; border: 1px solid color-mix(in srgb, var(--series-amber) 55%, var(--hairline)); border-radius: 0.35rem; background: rgba(8, 12, 22, 0.72); color: var(--ink); font: inherit; font-size: 0.62rem; line-height: 1.1; text-decoration: none; cursor: pointer; }
.my-peps-dose-inventory-actions a:hover,
.my-peps-dose-inventory-actions a:focus-visible,
.my-peps-dose-inventory-actions button:hover,
.my-peps-dose-inventory-actions button:focus-visible { border-color: var(--series-amber); outline: none; }
.my-peps-event-actions button:disabled { opacity: 0.45; cursor: not-allowed; }
.my-peps-vial-statuses { display: flex; gap: .35rem; flex-wrap: wrap; margin: .7rem 0; }
.my-peps-vial-statuses span { padding: .25rem .5rem; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: .72rem; }
.my-peps-vial-statuses form { display: inline; margin: 0 0 0 .25rem; }
.my-peps-vial-statuses button { padding: 0; border: 0; background: transparent; color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer; }
.my-peps-vial-statuses .is-received,
.my-peps-vial-statuses .is-active { color: var(--ink); border-color: var(--glow-magenta); }
.my-peps-event form { margin: 0; }

@media (max-width: 900px) {
  .my-peps-recon-layout { grid-template-columns: 1fr; }
  .my-peps-recon-preview { position: static; }
  .my-peps-week { grid-template-columns: repeat(2, 1fr); }
  .my-peps-past-due-event { grid-template-columns: 1fr 1fr; }
  .my-peps-past-due-event > div:first-child { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
  .my-peps-head { align-items: flex-start; flex-direction: column; }
  .my-peps-facts,
  .my-peps-form-grid,
  .my-peps-recon-fields,
  .my-peps-week { grid-template-columns: 1fr; }
  .my-peps-week-nav { grid-template-columns: 1fr 1fr 1fr; }
  .my-peps-week-nav > strong { grid-column: 1 / -1; grid-row: 1; }
  .my-peps-week-nav > a,
  .my-peps-week-nav > span { text-align: center; }
  .my-peps-past-due-event { grid-template-columns: 1fr; align-items: stretch; }
  .my-peps-past-due-event > div:first-child { grid-column: auto; }
  .my-peps-mark-taken input[type="datetime-local"] { max-width: none; width: 100%; }
  .my-peps-recon-fields .my-peps-field-wide { grid-column: auto; }
  .my-peps-dose-control { grid-template-columns: minmax(0, 1fr) 6.75rem; }
  .my-peps-recon-head { flex-direction: column; }
  .my-peps-titration-row { grid-template-columns: minmax(0, 1fr) auto; }
  .my-peps-titration-row > em { grid-column: 1 / -1; text-align: left; }
  .my-peps-schedule-summary { align-items: flex-start; flex-direction: column; }
  .my-peps-schedule-summary span { text-align: left; }
  .my-peps-vial-projection { align-items: flex-start; flex-direction: column; }
  .my-peps-vial-projection span { text-align: left; }
  .my-peps-titration-prompt { grid-template-columns: 1fr; }
  .my-peps-titration-actions { justify-content: flex-start; }
  .my-peps-setup-guide { grid-template-columns: 1fr; }
  .my-peps-setup-guide > .my-peps-action { grid-column: 1; grid-row: auto; justify-self: start; }
}

/* --- My Orders -------------------------------------------------------------- */

.order-block {
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 1rem 1.25rem;
  margin: 0 0 1.25rem;
  text-align: left;
}

.order-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}

.order-number {
  font-weight: 700;
}

.order-date {
  color: var(--muted);
  font-size: 0.85rem;
  margin-left: auto;
}

.status-pill {
  display: inline-block;
  padding: 0.2rem 0.75rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--surface-tile);
  color: var(--muted);
}

.status-pending {
  background: var(--accent-wash);
  color: #7a6414;
}

.status-accepted {
  background: #e8f3f5;
  color: var(--glow-purple);
}

.status-partially_shipped,
.status-shipped {
  background: #eaf6ef;
  color: #23694a;
}

.status-cancelled {
  background: #faeceb;
  color: #a03232;
}

/* The inbound statuses (purchasing). Mapped onto the palette above rather
   than given a second vocabulary: ordered reads like accepted and received
   like shipped, which is what they are pointed the other way. `cancelled` is
   shared - a purchase order and an order name it the same.

   Draft takes .status-pill's own muted default and is named here anyway, so
   that "every status has a rule" is a thing a test can assert rather than a
   thing that happens to look right. */
.status-draft {
  background: var(--surface-tile);
  color: var(--muted);
}

.status-ordered {
  background: #e8f3f5;
  color: var(--glow-purple);
}

.status-received {
  background: #eaf6ef;
  color: #23694a;
}

/* Not a status - a derived display state (design §1), rendered as a second
   pill beside the stored one. Warn-toned because it marks the order that
   still needs somebody to do something. */
.status-partial {
  background: var(--accent-wash);
  color: #7a6414;
}

.order-lines {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
}

.order-line {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.45rem 0;
  border-top: 1px solid var(--rule);
  font-size: 0.92rem;
}

.order-line-name {
  font-weight: 600;
}

.order-line-variant {
  color: var(--muted);
  font-weight: 700;
  margin-left: 0.25rem;
}

.order-line-recon {
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 0.5rem;
}

.order-line-ship {
  color: var(--muted);
  font-size: 0.82rem;
  margin-left: auto;
}

.order-line-total {
  font-weight: 600;
  min-width: 4.5rem;
  text-align: right;
}

.order-total {
  text-align: right;
  font-weight: 700;
  margin: 0;
}

/* --- The orders table on /account/orders ---------------------------------- */
/* The row is the summary - number, date, status, total, balance - and the
   panel underneath adds the lines. Same shape as the staff portal's
   changelist: the panel is server-rendered inside the last cell and moves
   into a spanning row on first toggle (static/js/account-orders.js). */

.orders-table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
  font-size: 0.92rem;
}

.orders-table th {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.orders-sort {
  color: inherit;
  text-decoration: none;
}

.orders-sort:hover {
  color: var(--ink);
  text-decoration: underline;
}

/* The active sort column states its direction with an arrow rather than
   color alone, so it survives without relying on aria-sort being announced. */
.orders-table th[aria-sort="ascending"] .orders-sort::after,
.orders-table th[aria-sort="descending"] .orders-sort::after {
  margin-left: 0.25rem;
  font-size: 0.7em;
}

.orders-table th[aria-sort="ascending"] .orders-sort::after {
  content: "\2191";
}

.orders-table th[aria-sort="descending"] .orders-sort::after {
  content: "\2193";
}

.orders-cell {
  padding: 0.65rem 0.6rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}

.orders-num {
  text-align: right;
  white-space: nowrap;
}

.orders-placed {
  color: var(--muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

.orders-time {
  display: block;
  font-size: 0.78rem;
}

.orders-balance {
  font-weight: 600;
}

/* Money still owed on a live order is the one figure a customer scans for. */
.orders-balance--due {
  color: #a03232;
}

.orders-actions {
  width: 2.5rem;
  text-align: right;
}

.order-toggle {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface-tile);
  color: var(--muted);
  cursor: pointer;
}

.order-chevron {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform 140ms ease;
}

.orders-row--open .order-chevron {
  transform: rotate(180deg);
}

.orders-row--open > .orders-cell {
  border-bottom-color: transparent;
}

.order-subrow > td {
  padding: 0 0.6rem 0.9rem;
  border-bottom: 1px solid var(--rule);
}

.order-panel {
  border-left: 2px solid var(--hairline);
  padding: 0.25rem 0 0 0.75rem;
}

@media (max-width: 560px) {
  /* Five columns on a phone: tighten rather than drop one - every column
     here is part of the answer the row is supposed to give on its own. The
     time of day is the only thing that can go. */
  .orders-table {
    font-size: 0.85rem;
  }

  .orders-cell,
  .orders-table th {
    padding: 0.5rem 0.3rem;
  }

  .orders-time {
    display: none;
  }

  .cart-line {
    grid-template-columns: 48px minmax(0, 1fr);
  }

  .cart-line-controls,
  .cart-line-total {
    grid-column: 2;
    justify-self: start;
    align-items: flex-start;
  }
}

/* --- Account page --------------------------------------------------------- */
/* Two cards - details, then orders - on one column. */
.account-stack {
  display: grid;
  gap: var(--rhythm);
  min-width: 0;
}

.account-sheet {
  min-width: 0;
}

/* The account sub-nav: real links (not JS tabs), so /account and
   /account/orders each read as their own page - drawn as folder tabs on the
   top edge of the card below them.

   The tab row sits ON the card, not above it: the negative bottom margin eats
   the stack's gap and then two pixels more, so the active tab's fill runs
   down over the card's top border and the seam between the two disappears.
   The whole row is z-index 1 because the card's emission pseudo-layers ring
   its border box and would otherwise draw that seam back on. */
.account-nav {
  position: relative;
  z-index: 1;
  display: flex;
  gap: 0.35rem;
  /* Tabs start in from the corner - a folder tab never sits on the fold. */
  padding: 0 1.5rem;
  /* The gap, and then the 2px that puts the tabs' bottoms on the card's
     outline rather than a hair above it. */
  margin-bottom: calc(var(--rhythm) * -1 - 2px);
}

.account-nav-link {
  flex: 0 0 auto;
  display: inline-block;
  /* Ample, and deliberately more than the old pill: a tab is a place, not a
     chip. The 3px on the bottom is what reaches over the card's edge. */
  padding: 0.75rem 1.4rem;
  /* Inactive tabs sit lower, so the active one stands proud of the row. */
  margin-top: 4px;
  border: 1px solid var(--hairline);
  border-bottom: 0;
  border-radius: 12px 12px 0 0;
  background: var(--surface-tile);
  color: var(--muted);
  font-weight: 600;
  font-size: 0.85rem;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}

.account-nav-link:hover,
.account-nav-link:focus-visible {
  color: var(--ink);
  text-decoration: none;
}

/* The open tab: the card's own surface, so tab and card are one sheet of
   paper. The accent it used to carry as a fill is now the top edge - the
   folder's coloured lip - which keeps the marker without cutting the tab off
   from the card it belongs to. */
.account-nav-link.is-active {
  margin-top: 0;
  padding-bottom: calc(0.75rem + 3px);
  margin-bottom: -3px;
  background: var(--surface);
  color: var(--ink);
  box-shadow: inset 0 3px 0 var(--accent);
}

.account-nav-label-compact {
  display: none;
}

/* The two long labels take compact phone copy so all five destinations remain
   visible. Hiding Orders behind a sideways scroll made it especially hard to
   leave the adjacent Invite tab. */
@media (max-width: 640px) {
  .account-nav {
    /* Sideways scroll makes overflow-y auto too, which would clip the 3px the
       active tab reaches down by - so that 3px is padding here, and the
       negative margin grows to match. */
    padding: 0 0.75rem 3px;
    margin-bottom: calc(var(--rhythm) * -1 - 5px);
    justify-content: space-between;
    gap: 0.15rem;
    overflow-x: hidden;
    scrollbar-width: none;
  }

  .account-nav::-webkit-scrollbar {
    display: none;
  }

  .account-nav-link {
    min-width: 0;
    padding: 0.7rem 0.25rem;
    font-size: 0.72rem;
  }

  .account-nav-link.is-active {
    padding: 0.7rem 0.25rem calc(0.7rem + 3px);
  }

  .account-nav-label-wide {
    display: none;
  }

  .account-nav-label-compact {
    display: inline;
  }
}

.account-group {
  margin: 0 0 1.5rem;
  padding: 0;
  border: 0;
  /* A fieldset's default min-inline-size is min-content, which stops it
     shrinking with the card. */
  min-inline-size: 0;
}

.account-legend {
  padding: 0 0 0.6rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.account-field {
  text-align: left;
}

/* The saved-address list. Same tile treatment as .account-gate so the card
   reads as one component rather than a list bolted onto a form. */
.address-list {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.address-item {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: flex-start;
  justify-content: space-between;
  padding: 0.8rem 1rem;
  background: var(--surface-tile);
  border-radius: 10px;
  text-align: left;
}

.address-body {
  min-inline-size: 0;
}

.address-label {
  margin: 0 0 0.2rem;
  font-weight: 600;
}

/* The word before the nickname, quiet and small: it explains what the line
   IS without competing with it. A nickname reading "323 Lamplighter Lane"
   above the same street looked like a duplicate until it said so. */
.address-label-tag {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.address-default {
  margin: 0 0 0.2rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

.address-recipient {
  margin: 0 0 0.1rem;
  font-size: 0.92rem;
  font-weight: 600;
}

.address-line {
  margin: 0;
  font-size: 0.92rem;
}

.address-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
}

.address-actions form {
  margin: 0;
}

/* --- Order list additions -------------------------------------------------- */

.order-payment {
  color: var(--muted);
  font-size: 0.85rem;
  text-align: right;
  margin: 0.2rem 0 0;
}

.order-detail-link {
  font-size: 0.85rem;
  margin: 0.5rem 0 0;
}

/* Reorder sits with the panel's other actions, with its caveat beside it
   rather than in a tooltip: "at today's prices" is the whole point of the
   button and is not a detail to discover after pressing it. */
.order-reorder {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0.6rem 0 0;
}

.order-reorder-note {
  color: var(--muted);
  font-size: 0.8rem;
}

/* --- Invoice --------------------------------------------------------------- */

.invoice-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.invoice-title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.7rem;
  margin: 0;
}

/* The payment code, beside the figures it settles. Floated right so the
   meta list and the line table keep their full width on a narrow screen,
   where the code drops below rather than squeezing them. */
.invoice-pay {
  float: right;
  margin: 0 0 1rem 1.5rem;
  text-align: center;
  max-width: 40%;
}

.invoice-pay-code {
  display: block;
  width: 100%;
  max-width: 220px;
  height: auto;
  margin: 0 auto;
}

.invoice-pay-note {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
}

.invoice-pay-steps {
  margin: 0.4rem 0 0;
  padding-left: 1.2rem;
  text-align: left;
  font-size: 0.8rem;
  color: var(--muted);
}

@media (max-width: 640px) {
  .invoice-pay {
    float: none;
    margin: 0 auto 1.25rem;
    max-width: 220px;
  }
}

/* The list states a balance; this is where it says how to settle it. */
.order-pay-link {
  display: inline-block;
  margin-left: 0.4rem;
}

.invoice-meta {
  display: grid;
  gap: 0.35rem;
  margin: 0 0 1.5rem;
  text-align: left;
}

.invoice-meta div {
  display: flex;
  gap: 0.75rem;
}

.invoice-meta dt {
  min-width: 6rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.invoice-meta dd {
  margin: 0;
  font-weight: 600;
}

.invoice-block {
  margin: 0 0 1.5rem;
  text-align: left;
}

.invoice-subhead {
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 700;
  margin: 0 0 0.4rem;
}

.invoice-address {
  margin: 0;
  line-height: 1.5;
}

.invoice-address--missing {
  color: var(--muted);
  font-style: italic;
}

/* Tracking, under the address it belongs to. The number is monospace
   because it is read aloud, typed into a carrier's site, or checked digit
   by digit against a printed label - all three of which a proportional face
   makes harder. */
.invoice-tracking-head {
  margin: 0.9rem 0 0.2rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.invoice-tracking {
  margin: 0;
  padding: 0;
  list-style: none;
  line-height: 1.6;
}

.invoice-tracking-number {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  letter-spacing: 0.03em;
}

.invoice-tracking-n {
  display: inline-block;
  min-width: 5.5em;
  color: var(--muted);
  font-size: 0.85em;
}

.invoice-tracking-service {
  color: var(--muted);
  font-size: 0.85em;
}

.invoice-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 1.25rem;
  text-align: left;
  font-size: 0.92rem;
}

.invoice-table th,
.invoice-table td {
  padding: 0.55rem 0.5rem;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}

.invoice-table thead th {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.invoice-num {
  text-align: right;
  white-space: nowrap;
}

.invoice-item {
  min-width: 0;
  overflow-wrap: anywhere;
  font-weight: 600;
}

.invoice-fee {
  display: block;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 400;
}

.invoice-note {
  margin: 0;
  padding: 0.8rem 1rem;
  background: var(--surface-tile);
  border-radius: 10px;
  font-size: 0.92rem;
}

/* --- Purchase order document (purchasing/_po_body.html) -------------------
   Two columns the customer invoice has no equivalent of.

   Landed/unit is DIMMED because no invoice anywhere says that number: it is
   what the shop worked out the unit really cost once freight was counted, not
   what anybody charged. Sitting at full weight beside the figures the vendor
   did charge would read as one more of them. */
.po-landed {
  color: var(--muted);
  font-weight: 400;
}

/* A short line is the whole reason to read the Received column, so it is the
   half that is emphasised - the same move .invoice-unverified makes for money
   that has not been checked. A complete line needs no attention and gets
   none. */
.po-short {
  font-weight: 700;
  color: #7a6414;
}

.po-landed-note {
  margin-top: 0.5rem;
  text-align: right;
}

/* The profile gate's own line: an instruction, not an error, so it takes the
   accent rather than the error red. */
.account-gate {
  margin: 0 0 1.25rem;
  padding: 0.7rem 0.9rem;
  border-left: 3px solid var(--accent);
  background: var(--surface-tile);
  font-size: 0.9rem;
  text-align: left;
}

/* The gate now names each outstanding requirement rather than saying
   "your details", so it carries a list. */
.account-gate-lede {
  margin: 0;
  font-weight: 600;
}

.account-gate-list {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
}

.account-gate-item {
  margin: 0.2rem 0;
}

/* The nav's needs-attention dot. Its words live in the anchor's aria-label,
   so this is decoration and nothing else. */
.account-nav-flag {
  margin-left: 0.35rem;
  color: var(--accent);
  font-size: 1.1em;
  line-height: 0;
  vertical-align: middle;
}

/* --- Cancelling an order --------------------------------------------------- */
/* Quiet, not accent: cancelling is available, not encouraged. The primary
   action on this page is nothing at all. */
.btn--quiet {
  width: auto;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--hairline);
  text-transform: none;
  letter-spacing: 0.02em;
  font-weight: 600;
}

.btn--quiet:hover {
  border-color: #a03232;
  color: #a03232;
  filter: none;
}

/* My Peps reminder and customer-readable activity center. */
.my-peps-activity { max-width: 980px; margin: 2rem auto; }
.my-peps-activity .section-heading,
.activity-card,
.activity-actions { display: flex; align-items: center; gap: 1rem; }
.my-peps-activity .section-heading { justify-content: space-between; align-items: flex-start; }
.activity-list { display: grid; gap: .8rem; margin: 1rem 0 2rem; }
.activity-card { padding: 1rem; border: 1px solid var(--hairline); border-radius: 14px; background: var(--surface-tile); }
.activity-card.is-unread { border-color: rgba(255, 42, 42, .55); box-shadow: 0 0 18px var(--edge-glow); }
.activity-card > i { font-size: 1.35rem; color: var(--accent); }
.activity-copy { flex: 1; }
.activity-copy h3,
.activity-copy p { margin: .15rem 0; }
.activity-copy time,
.activity-actor { color: var(--muted); font-size: .8rem; }
.activity-actions { flex-wrap: wrap; }
.activity-heading-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .5rem; }
.activity-heading-actions form { margin: 0; }
.activity-actions form { display: flex; gap: .5rem; margin: 0; }
.my-peps-activity .section-heading .btn,
.activity-actions .btn { display: inline-flex; width: auto; margin: 0; }
@media (max-width: 650px) {
  .my-peps-activity .section-heading,
  .activity-card { align-items: flex-start; flex-direction: column; }
  .activity-heading-actions { justify-content: flex-start; }
  .activity-actions { width: 100%; }
}

.invoice-cancel {
  padding-top: 1.25rem;
  border-top: 1px solid var(--hairline);
}

.invoice-cancel-note {
  margin: 0 0 0.75rem;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.5;
}

.invoice-cancelled,
.invoice-cancel-closed {
  margin: 1.25rem 0 0;
  padding: 0.7rem 0.9rem;
  border-left: 3px solid var(--hairline);
  background: var(--surface-tile);
  color: var(--muted);
  font-size: 0.88rem;
  text-align: left;
}

/* ---------------------------------------------------------------------------
   The storefront assistant (product pages only).

   STYLE-GUIDE §6 surface: white, 14px radius, hairline border, a soft lift
   and no more. Every colour below is a token from :root - this section
   introduces no new value.

   Not a modal (§10's contract, and the exception is argued in
   templates/chat/_widget.html): no scrim, and nothing here traps focus. The
   product page stays fully usable behind the panel, which is the whole point
   of a panel you consult while reading.
   --------------------------------------------------------------------------- */
/* THE DOCK. The launcher's ground, from Greg's mockup 2026-08-03: a bar of
   blurred glass that runs off the right edge of the screen, ringed in the
   aurora's red, with the chevron that slides it away at its left end.

   Blurred, NOT filled. --surface is translucent (0.72) and the backdrop blur
   is what makes the catalog behind it a tone rather than a picture. The chat
   panel next door reaches for --surface-float instead, and the difference is
   deliberate: a conversation is a wall of text that has to be read on its
   ground, while this is one pill and a mascot with plenty of edge of their
   own. Filling this one to 0.93 would put an opaque slab in the corner of
   every page, which is the thing the mockup is getting away from.

   The blur is allowed for the same reason it is allowed on .chat-panel and
   .site-nav: aurora.css forbids backdrop-filter on a WINDOWED panel, whose
   border is a transparent gap onto the aurora. This border is painted -
   --edge-glow, emission rather than transmission - so there is no ring for
   the filter to spoil.

   It runs OFF the edge: right: 0, no right border, and the left corners
   rounded alone. A bar that stops short of the edge with a chevron on it
   reads as a thing you could have dragged; one that runs off reads as a thing
   that came from off-screen and can go back. */
.chat-dock {
  position: fixed;
  right: 0;
  bottom: 1.25rem;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 1.5rem 0.4rem 0.3rem;
  background: var(--surface);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--edge-glow);
  border-right: 0;
  border-radius: 18px 0 0 18px;
  box-shadow:
    0 0 18px var(--accent-wash),
    0 14px 36px rgba(0, 0, 0, 0.45);
  /* The slide. transform only - animating `right` would relayout the bar on
     every frame, and this sits over a catalog that is already painting. */
  transition: transform 240ms ease;
}

/* Slid out: far enough right that only the chevron's own column is left
   on screen, which is the padding either side of it plus its width. The
   tray goes with it, so the distance is the tray's width and the gap. */
.chat-dock.is-away {
  transform: translateX(calc(100% - 2.6rem));
}

/* Off-screen is not the same as gone. Without this the launcher stays in the
   tab order and a screen reader still finds it, so a shopper who put the
   assistant away would tab into a button they cannot see. The delay lets it
   finish travelling before it disappears; coming back it is instant, because
   there is nothing to watch it arrive if it is invisible on the way. */
.chat-dock-tray {
  display: flex;
  align-items: center;
  transition: visibility 0s linear 0s;
}

.chat-dock.is-away .chat-dock-tray {
  visibility: hidden;
  transition: visibility 0s linear 240ms;
}

/* The chevron's button. A real target rather than the glyph's own box: at
   the size the mockup draws it that would be about 10px square. */
.chat-dock-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2rem;
  height: 2.75rem;
  border: 0;
  border-radius: 10px;
  padding: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 150ms ease, background-color 150ms ease;
}

.chat-dock-toggle:hover {
  color: var(--ink);
  background: var(--accent-wash);
}

.chat-dock-toggle:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

/* Two borders on a rotated square. It points the way the bar will TRAVEL -
   right while the assistant is on screen (push it away), left once it is
   gone (pull it back) - rather than at the assistant, which would have it
   saying the same thing in both states. */
.chat-dock-chevron {
  display: block;
  width: 9px;
  height: 9px;
  border: 2px solid currentColor;
  border-left: 0;
  border-bottom: 0;
  /* Rotated right: the corner points at the right edge. */
  transform: translateX(-2px) rotate(45deg);
  transition: transform 240ms ease;
}

.chat-dock.is-away .chat-dock-chevron {
  transform: translateX(2px) rotate(-135deg);
}

@media (prefers-reduced-motion: reduce) {
  .chat-dock,
  .chat-dock-chevron {
    transition: none;
  }

  .chat-dock.is-away .chat-dock-tray {
    transition: none;
  }
}

/* The launcher is the mascot with a speech bubble beside it, so the button
   itself paints nothing - it is a transparent wrapper around the two. It sits
   in the dock's flow now rather than being pinned to the corner itself; the
   dock is the thing that is fixed. */
.chat-launcher {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
}

.chat-launcher-bubble {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  padding: 0.55rem 0.85rem;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  line-height: 1.3;
  color: var(--ink);
  box-shadow: 0 2px 10px rgba(26, 43, 74, 0.12);
  white-space: nowrap;
}

/* The tail, drawn twice: the first triangle is the hairline and the second
   is the fill, inset a pixel over it, so the border runs unbroken around the
   whole bubble instead of stopping where the tail starts. */
.chat-launcher-bubble::before,
.chat-launcher-bubble::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 100%;
  width: 0;
  height: 0;
  border: 7px solid transparent;
  border-left-color: var(--hairline);
  transform: translateY(-50%);
}

.chat-launcher-bubble::after {
  margin-left: -1px;
  border-left-color: var(--surface);
}

/* The nudged bubble. The printed label is one short line and never wraps;
   a nudge is a sentence or two, so the nudged state trades nowrap for a
   clamped block. Width and clamp are the guarantee the dock keeps its
   footprint - the server caps the text, but the layout does not rely on it. */
.chat-launcher.is-nudged .chat-launcher-bubble {
  white-space: normal;
  max-width: 15rem;
  text-align: left;
}

.chat-launcher.is-nudged .chat-launcher-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.chat-launcher-more {
  display: none;
}

.chat-launcher.is-nudged .chat-launcher-more {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.75rem;
  font-weight: 600;
  opacity: 0.7;
}

/* A drop-shadow filter rather than box-shadow: the PNG is transparent around
   the figure, and box-shadow would draw a rectangle round the empty square. */
.chat-launcher-avatar {
  display: block;
  width: 76px;
  height: auto;
  filter: drop-shadow(0 3px 6px rgba(26, 43, 74, 0.22));
  transition: transform 150ms ease;
}

.chat-launcher:hover .chat-launcher-avatar {
  transform: translateY(-2px);
}

.chat-launcher:hover .chat-launcher-bubble {
  border-color: var(--glow-magenta);
}

.chat-launcher:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 14px;
}

/* display: flex above beats the attribute, which is a UA `display: none`. */
.chat-dock[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .chat-launcher-avatar {
    transition: none;
  }

  .chat-launcher:hover .chat-launcher-avatar {
    transform: none;
  }
}

.chat-panel {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 41;
  display: flex;
  flex-direction: column;
  width: 380px;
  height: 560px;
  max-height: calc(100dvh - 2.5rem);
  /* Floats over the CATALOG, not over the aurora, and that is why it does not
     use --surface. At 0.72 the grid behind it came through the panel: product
     names, prices and vial photographs sat underneath the conversation, and
     the header and composer - the two places with the least text of their own
     to hide behind - were the worst of it.

     The blur is allowed here. aurora.css forbids backdrop-filter on a WINDOWED
     panel, because the filter region is clipped to the border box and the
     aurora seen through the ring would come out pre-blurred. This panel has no
     such ring: it carries an ordinary opaque border, so there is nothing the
     blur can spoil. Same reasoning as the site nav.

     The shadow is what separates it from the page now that it is nearly
     opaque - without one it reads as a hole cut in the catalog. */
  background: var(--surface-float);
  -webkit-backdrop-filter: blur(20px) saturate(120%);
  backdrop-filter: blur(20px) saturate(120%);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
  /* NOT hidden: the mascot in the header overhangs the top edge, and clipping
     here would shave its head off. Nothing else needs clipping - the log
     scrolls inside its own overflow-y, and no child paints a background that
     could escape the rounded corners. */
  overflow: visible;
}

.chat-panel[hidden] {
  display: none;
}

.chat-head {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0 1rem 0.5rem 1rem;
  border-bottom: 1px solid var(--hairline);
}

.chat-head-title {
  margin: 0;
  /* The h2 is a wrapper for the image and nothing else; without this its line
     box adds a few pixels under the picture that read as a misaligned rule. */
  line-height: 0;
}

/* The overhang. The negative margin lifts the head above the panel's top edge
   rather than the image being positioned out of flow, so the header still
   reserves the part of it that sits inside the panel and the rule underneath
   lands where it should. */
.chat-head-avatar {
  display: block;
  width: 132px;
  height: auto;
  margin-top: -40px;
  filter: drop-shadow(0 4px 8px rgba(26, 43, 74, 0.18));
}

.chat-close {
  position: absolute;
  top: 0.35rem;
  right: 0.5rem;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  padding: 0.15rem 0.35rem;
  cursor: pointer;
}

.chat-log {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem;
  font-family: var(--font-sans);
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink);
}

.chat-empty {
  margin: 0;
  color: var(--muted);
  font-size: 0.88rem;
}

/* pre-wrap because the assistant's answers carry real newlines - and because
   the research disclaimer arrives as a paragraph break inside done.text
   rather than as an element of its own. Nothing may style it separately: it
   is part of the answer, not an annotation on one. */
/* The log as a conversation: two sides, two shades, tails from opposite
   corners. Same triangle technique as .chat-launcher-bubble above, but a
   SINGLE triangle per bubble rather than that rule's border-plus-fill pair -
   these bubbles are borderless, and two translucent triangles stacked at
   0.85 alpha show every overlap as a darker seam. */
.chat-msg {
  position: relative;
  margin: 0 0 0.85rem 0;
  padding: 0.55rem 0.8rem;
  max-width: 85%;
  border-radius: 12px;
  white-space: pre-wrap;
}

/* The shopper sits on the right, in the brand red's shade of the tile. */
.chat-msg--shopper {
  margin-left: auto;
  background: var(--bubble-shopper);
  border-bottom-right-radius: 4px;
}

.chat-msg--shopper::after {
  content: "";
  position: absolute;
  bottom: 0.4rem;
  left: 100%;
  border: 6px solid transparent;
  border-left-color: var(--bubble-shopper);
}

/* The assistant answers from the left, on the plain tile. */
.chat-msg--assistant {
  margin-right: auto;
  background: var(--surface-tile);
  border-bottom-left-radius: 4px;
}

.chat-msg--assistant::after {
  content: "";
  position: absolute;
  bottom: 0.4rem;
  right: 100%;
  border: 6px solid transparent;
  border-right-color: var(--surface-tile);
}

/* An error keeps the assistant's side and shade - it arrives where an answer
   was expected - and only the ink goes quiet. The selector also covers the
   streamed answer that FAILED mid-turn, where chat.js adds this class to a
   node that is already .chat-msg--assistant. */
.chat-msg--error {
  margin-right: auto;
  background: var(--surface-tile);
  color: var(--muted);
}

/* The escalation notice, and "starting a new conversation". Said quietly:
   these are the widget talking about itself, not the assistant answering. */
.chat-note {
  margin: -0.4rem 0 0.85rem 0;
  font-size: 0.82rem;
  color: var(--muted);
}

.chat-status {
  margin: 0;
  padding: 0 1rem 0.6rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-style: italic;
  color: var(--muted);
}

.chat-status[hidden] {
  display: none;
}

/* The assistant offering to take the shopper to a page.

   Inside the log rather than beside the follow-up chips, because it belongs
   to the answer it came with: scrolling back to an older reply should still
   show the door it opened. Deliberately quieter than .btn - going somewhere
   is an offer, not the page's primary action. */
.chat-goto {
  margin: 0.35rem 0 0;
}

.chat-goto-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--glow-magenta);
  border-radius: 999px;
  background: var(--surface);
  color: var(--glow-purple);
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
}

.chat-goto-link:hover {
  background: var(--glow-magenta);
  color: #ffffff;
}

.chat-goto-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.chat-followups {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0 1rem 0.75rem 1rem;
}

.chat-followups[hidden] {
  display: none;
}

.chat-chip {
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.82rem;
  padding: 0.35rem 0.7rem;
  cursor: pointer;
}

.chat-chip:hover {
  border-color: var(--glow-magenta);
}

.chat-form {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  padding: 0.75rem 1rem 1rem 1rem;
  border-top: 1px solid var(--hairline);
}

.chat-input-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.chat-input {
  flex: 1 1 auto;
  /* Without this the textarea's default intrinsic width sets a floor that
     flex will not shrink past, and the composer overflows its own panel. */
  min-width: 0;
  resize: none;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 0.5rem 0.6rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--ink);
  /* A well, not another sheet of the same glass. --surface here was lighter
     than the panel it sits in, so the field read as a raised area rather than
     as somewhere to type; the tile is darker than its surround, which is what
     an input is meant to look like. */
  background: var(--surface-tile);
}

.chat-input:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 1px;
}

/* Styled from nothing rather than from .btn, and the template says why: .btn
   is display:block/width:100%, which in this row claims the whole composer
   and crushes the textarea to a scrollbar. It is also the page's primary CTA
   treatment - Add to Cart is meant to be the loudest thing on a product page,
   not answered by an equally loud button in the corner. So this is the quiet
   pill the launcher and the chips already wear. */
.chat-send {
  flex: 0 0 auto;
  width: auto;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.88rem;
  font-weight: 600;
  padding: 0.55rem 1.1rem;
  cursor: pointer;
}

.chat-send:hover:not([disabled]) {
  border-color: var(--glow-magenta);
}

.chat-send:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.chat-send[disabled],
.chat-input[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Full-screen at the same breakpoint .product-layout stops being two columns:
   below it there is no page left worth reading around, and a 380px card on a
   390px phone is a bad card.

   100dvh, never 100vh - the latter is the viewport with the iOS toolbar
   pretending not to be there, which puts the composer underneath it. */
@media (max-width: 900px) {
  /* THE VISIBLE VIEWPORT, not the screen. dvh accounts for the browser's own
     toolbars and knows nothing about the software keyboard: with the keyboard
     up, the visible strip is about half the screen while a 100dvh fixed panel
     is still the whole of it. Safari then scrolls the LAYOUT viewport to bring
     the focused composer into view, and the panel - anchored to that layout
     viewport, not to what is on screen - is carried up with it, taking the
     header and the close button that is the only way out of a full-screen
     panel off the top.

     So both the height and the top come from visualViewport, which is the only
     thing that reports a keyboard. chat.js sets them while the panel is open.
     The fallbacks are what a browser without visualViewport gets, and they are
     the behaviour this rule had before: correct until a keyboard opens.

     `top` must stay AFTER `inset`, which sets it too. */
  .chat-panel {
    inset: 0;
    top: var(--chat-vv-top, 0px);
    width: 100%;
    height: var(--chat-vv-height, 100dvh);
    max-height: var(--chat-vv-height, 100dvh);
    border-radius: 0;
    border: 0;
  }

  /* No overhang here: the panel's top edge IS the top of the screen, so a
     negative margin would crop the mascot against the viewport rather than
     lifting it out of anything. It sits inside the header instead. */
  .chat-head {
    padding-top: 0.5rem;
  }

  .chat-head-avatar {
    margin-top: 0;
    width: 104px;
  }

  /* Full-screen, this is the ONLY way out - there is no Escape key on a
     phone and no page showing round the edges to tap. So it gets a real
     target rather than a 1.4rem glyph. */
  .chat-close {
    min-width: 44px;
    min-height: 44px;
    font-size: 1.6rem;
  }

  /* 16px is a THRESHOLD, not a size chosen for how it looks. iOS Safari zooms
     the whole page whenever focus lands in a field whose font is under 16px,
     by exactly the ratio that would bring it up to 16 - 0.9rem zoomed the
     storefront by 16/14.4 = 1.111x. Nothing overflows when that happens: the
     panel is still exactly one layout viewport wide, but the shopper can only
     see 90% of it, so the conversation pans left and right and Send sits off
     the right edge. The zoom does not undo itself when the field is blurred,
     either - it is there for the rest of the visit.

     Only at this breakpoint. The panel is a 380px card on a desktop, where
     0.9rem is the size the composer is meant to be and no browser zooms. */
  .chat-input {
    font-size: 16px;
  }
}

/* --- Catalog card and row extras (badges, sequence, purity, weight) ------ */

/* The card is a WINDOW: .glass gives it a padding-box-clipped fill so its
   1px border shows the aurora through it. See css/aurora.css. */
.product-card {
  position: relative;
}

.badge {
  position: absolute;
  top: 0.6rem;
  left: 0.6rem;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The SALE ribbon. One component, two sizes: the detail hero and the grid
   card. Its container must clip (overflow: hidden) - that is what turns a
   rotated bar into a corner ribbon. The list row does not carry one; its
   tile is 64px and the text would land near 5px. */
.sale-ribbon {
  position: absolute;
  top: 0;
  right: 0;
  width: 104px;
  height: 104px;
  overflow: hidden;
  pointer-events: none;
  z-index: 2;
}

.sale-ribbon > span {
  position: absolute;
  display: block;
  width: 160px;
  top: 23px;
  right: -42px;
  padding: 0.3rem 0;
  transform: rotate(45deg);
  background: var(--accent);
  color: #fff;
  text-align: center;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  box-shadow: 0 2px 12px var(--edge-glow);
}

.product-tile .sale-ribbon {
  width: 78px;
  height: 78px;
}

.product-tile .sale-ribbon > span {
  width: 122px;
  top: 17px;
  right: -32px;
  padding: 0.22rem 0;
  font-size: 0.56rem;
}

/* THREE CORNERS, THREE CLAIMS. A tile can carry all of them at once and they
   never overlap, because each owns a corner of its own:

     top-left      .flag-ribbon   Popular / New   (operator flags)
     top-right     .sale-ribbon   Sale            (this variant is discounted)
     bottom-left   .rank-ribbon   #1 - #5         (counted from units sold)

   They are three different statements about the same product, so they are
   three ribbons rather than one slot they take turns in. Bottom-right stays
   empty on purpose: a tile ringed on all four corners is a sticker sheet.

   All three are the same geometry rotated into their own corner, so a card
   reads as one system. Grid only - the list row states these as pills, its
   tile being 64px, where a ribbon's text lands near 5px. */
.flag-ribbon {
  position: absolute;
  top: 0;
  left: 0;
  width: 78px;
  height: 78px;
  overflow: hidden;
  pointer-events: none;
  z-index: 2;
}

.flag-ribbon > span {
  position: absolute;
  display: block;
  width: 122px;
  top: 17px;
  left: -32px;
  padding: 0.22rem 0;
  transform: rotate(-45deg);
  text-align: center;
  font-size: 0.56rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* The rank ribbon, in the bottom-left. Mirrored vertically from the flag
   above it, so the left edge reads as a pair rather than two angles. */
.rank-ribbon {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 78px;
  height: 78px;
  overflow: hidden;
  pointer-events: none;
  z-index: 2;
}

.rank-ribbon > span {
  position: absolute;
  display: block;
  width: 122px;
  bottom: 17px;
  left: -32px;
  padding: 0.22rem 0;
  transform: rotate(45deg);
  text-align: center;
  font-size: 0.56rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* THE FIVE METALS, #1-#5: gold, silver, bronze, copper, pewter. Counted from
   units sold - see catalog/ranking.py.

   FLAT FILLS DO NOT WORK AT FIVE PLACES. As solid colour, bronze (#cd7f32)
   and copper (#b87333) are the same warm brown and silver (#c0c6cb) and
   pewter (#8e9294) are the same grey, so ranks 3/4 and 2/5 stop being
   distinguishable at 78px. What separates them is what separates real metal:
   a specular sweep across the band, a dark rolled edge either side of it,
   and a lit top lip. Each metal gets its own ramp - copper's runs pink-
   orange where bronze's runs olive-brown, which is exactly the difference a
   flat mid-tone throws away.

   Declared as CUSTOM PROPERTIES rather than finished rules, because the same
   five metals dress two shapes: the grid card's corner ribbon and the list
   row's pill. Custom properties inherit, so .rank-N goes on the outer
   element and the inner span reads --metal off it. One definition of gold.

   The rank number carries the information and the metal only reinforces it,
   so a shopper who cannot tell bronze from copper still reads "#3" and "#4".
   That is why this is allowed to be decoration. */
.rank-1 {
  --metal-ink: #3d2c00;
  --metal-lip: rgba(255, 249, 219, 0.85);
  --metal-glow: rgba(224, 178, 42, 0.55);
  --metal: linear-gradient(
    90deg,
    #6d5106 0%,
    #b8902a 22%,
    #fff2b0 50%,
    #c99a2e 74%,
    #7a5a08 100%
  );
}

.rank-2 {
  --metal-ink: #23292d;
  --metal-lip: rgba(255, 255, 255, 0.9);
  --metal-glow: rgba(178, 190, 198, 0.5);
  --metal: linear-gradient(
    90deg,
    #61686d 0%,
    #a9b2b8 22%,
    #ffffff 50%,
    #b3bcc2 74%,
    #6b7378 100%
  );
}

/* Bronze pushed OLIVE and copper pushed PINK, away from the brown they
   share, or #3 and #4 are one colour with two numbers on it. */
.rank-3 {
  --metal-ink: #2e1a06;
  --metal-lip: rgba(255, 228, 184, 0.7);
  --metal-glow: rgba(180, 122, 52, 0.5);
  --metal: linear-gradient(
    90deg,
    #59340f 0%,
    #9c6a25 22%,
    #e8c184 50%,
    #a87231 74%,
    #603a13 100%
  );
}

.rank-4 {
  --metal-ink: #35130a;
  --metal-lip: rgba(255, 205, 180, 0.7);
  --metal-glow: rgba(190, 96, 54, 0.5);
  --metal: linear-gradient(
    90deg,
    #6b2a12 0%,
    #b0522a 22%,
    #ffb894 50%,
    #bd5c31 74%,
    #742f16 100%
  );
}

/* Pewter is the dull one on purpose: it is fifth, and a fifth place that
   glitters as hard as the podium is a ribbon competing with the three that
   mean something. Lower contrast, no bright specular. */
.rank-5 {
  --metal-ink: #1d2124;
  --metal-lip: rgba(214, 221, 226, 0.55);
  --metal-glow: rgba(120, 130, 137, 0.4);
  --metal: linear-gradient(
    90deg,
    #4d5357 0%,
    #767d82 22%,
    #a8afb4 50%,
    #7c8388 74%,
    #545a5e 100%
  );
}

/* Shape one: the corner ribbon. The gradient runs at 90deg - across the
   band's short axis, the band itself already being rotated - so the
   highlight reads as a sheen along the ribbon rather than a fade from one
   end of it to the other. */
.rank-ribbon > span {
  color: var(--metal-ink);
  background: var(--metal);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
  box-shadow:
    inset 0 1px 0 var(--metal-lip),
    inset 0 -1px 0 rgba(0, 0, 0, 0.42),
    0 2px 12px var(--metal-glow);
}

.flag-new > span {
  color: #04231a;
  background: var(--ok-ink);
  box-shadow: 0 2px 12px rgba(62, 207, 142, 0.5);
}

.flag-popular > span {
  color: #17062e;
  background: var(--info-ink);
  box-shadow: 0 2px 12px rgba(155, 77, 255, 0.5);
}

/* The price line, in one order everywhere: saving, old price, price charged.
   Only the saving takes the accent - the ribbon above is already accent, and
   a red struck price competes with the number being charged. */
.price-save {
  color: var(--accent);
  font-weight: 800;
  font-size: 0.72em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.price-was {
  color: var(--muted);
  font-weight: 500;
  font-size: 0.8em;
  text-decoration: line-through;
}

/* The row's price is a flex line so the three parts share a baseline and do
   not wrap mid-figure. */
.product-price,
.product-detail-price {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  flex-wrap: wrap;
}

.product-price {
  justify-content: center;
}

.product-row .product-price {
  justify-content: flex-end;
  flex-wrap: nowrap;
  white-space: nowrap;
}

/* Shape two: the list row's rank pill, in the same five metals. The row's
   tile is 64px, where a corner ribbon's text lands near 5px - so the row
   states the rank as a pill instead, and reads --metal off the .rank-N the
   template puts on this same element. */
.badge-rank {
  color: var(--metal-ink);
  background: var(--metal);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
  box-shadow:
    inset 0 1px 0 var(--metal-lip),
    0 0 12px var(--metal-glow);
}

.badge-new {
  color: var(--ok-ink);
  background: rgba(62, 207, 142, 0.16);
  box-shadow: 0 0 14px rgba(62, 207, 142, 0.3);
}

.badge-popular {
  color: var(--info-ink);
  background: rgba(155, 77, 255, 0.16);
  box-shadow: 0 0 14px rgba(155, 77, 255, 0.3);
}

/* The chain. Monospace and small: it is a reference string, not prose, and
   it must wrap anywhere - a long sequence is one unbreakable token and would
   otherwise set the card's min-content width. */
.product-sequence {
  font-family: var(--font-family-monospace, ui-monospace, monospace);
  font-size: 0.68rem;
  line-height: 1.35;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.product-purity {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--accent);
}

.product-mw {
  font-size: 0.72rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- List row ------------------------------------------------------------ */

.product-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1rem;
  text-decoration: none;
  color: var(--ink);
}

.product-row:focus-visible,
.product-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.product-row-tile {
  display: grid;
  place-items: center;
  width: 64px;
  aspect-ratio: 1;
  background: var(--surface-tile);
  border-radius: 8px;
}

.product-row-tile img {
  max-width: 82%;
  max-height: 82%;
  object-fit: contain;
}

.product-row-main {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.product-row-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* In a row the badge is inline, not pinned to a corner. */
.product-row-head .badge {
  position: static;
}

.product-row-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.15rem;
}

@media (max-width: 700px) {
  .product-row {
    grid-template-columns: 48px minmax(0, 1fr) auto;
  }

  .product-row-meta {
    display: none;
  }
}

/* --- Product detail: identity block, and the dark control states --------- */

.product-identity {
  margin: 0 0 0.75rem;
}

.product-identity .product-sequence {
  margin: 0 0 0.35rem;
}

.product-identity-meta {
  display: flex;
  gap: 1rem;
  margin: 0;
}

/* The selected variant was an accent-yellow fill on white. On the dark
   ground that reads as a hole, so selection is a wash plus a glow ring -
   the same treatment the active view toggle and the portal's active nav
   pill use, so "selected" looks like one idea across the site. */
.variant-option {
  background: var(--surface);
  background-clip: padding-box;
  border: 1px solid var(--hairline);
  color: var(--ink);
}

/* In stock announces itself. Out of stock always has (dashed border), but
   the two UNSELECTED states were identical at a glance, so stock was only
   discoverable by selecting each variant in turn - S-G §6 gains this as a
   fourth treatment. The glow is --glow-purple, the arc's cold end, rather
   than the accent: red means "selected" on these cards, and a second red
   ring would read as two selections.

   IT BREATHES, AND HOW IT BREATHES IS THE PANEL EMISSION'S TECHNIQUE, not
   the panel emission's mistake: the shadow is painted ONCE onto a
   pseudo-element and only its OPACITY animates, which the compositor does
   without repainting a pixel. Animating box-shadow itself is the exact bug
   retired from aurora.css on 2026-08-04 - five cards doing it would be the
   catalog's frame bursts in miniature. Slow and shallow on purpose: a
   breath, not a beacon, and all in-stock cards breathe in unison - five
   cards on five clocks read as faulty wiring, one shared swell reads as
   stock. */
.variant-option:not(.is-out) {
  position: relative;
  border-color: rgba(155, 77, 255, 0.5); /* --glow-purple, half strength */
}

.variant-option:not(.is-out)::after {
  content: "";
  position: absolute;
  /* -1px out from the padding box is this card's border box: the ring must
     originate where the element's own border sits, or it reads as a second
     outline hovering off the card. */
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    0 0 0 1px rgba(155, 77, 255, 0.38),
    0 0 16px rgba(155, 77, 255, 0.3);
  animation: stock-breathe 4.8s ease-in-out infinite alternate;
}

/* Selected keeps its own ring - the red one. Without this the purple layer
   would ride under the selected glow and the card would wear both. */
.variant-option.is-selected::after {
  content: none;
}

@keyframes stock-breathe {
  from {
    /* Deep enough to SEE. The first cut breathed 0.65 to 1 and was measured
       running while a person looking straight at it saw nothing - a breath
       below perception is just a static glow that costs a layer. */
    opacity: 0.2;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* The still is the breath's high mid-point, held. The glow is information
     - stock - so it stays; only the motion goes. */
  .variant-option:not(.is-out)::after {
    animation: none;
    opacity: 0.85;
  }
}

.variant-option.is-selected {
  background: var(--accent-wash);
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 20px var(--edge-glow);
}

/* Out of stock stays SELECTABLE by design (S-G §6): a customer needs to be
   able to select one to see its price and pre-order state. */
.variant-option.is-out {
  border-style: dashed;
  color: var(--muted);
}

.variant-option.is-selected.is-out {
  background: var(--accent-wash);
  border-style: dashed;
}

/* Disabled is INERT, not dimmed - the flat grey pill from the light theme,
   translated. */
.btn.is-inert,
.btn-primary[disabled],
.btn-primary.is-inert {
  background: var(--surface-tile);
  border-color: var(--hairline);
  color: var(--muted);
  box-shadow: none;
  cursor: not-allowed;
}

.expando-chevron {
  border: 1px solid var(--accent);
  color: var(--accent);
  background: transparent;
}

.expando-chevron:hover {
  background: var(--accent-wash);
  box-shadow: 0 0 16px var(--edge-glow);
}

/* --- The colophon dock ---------------------------------------------------- */

/* The assistant's dock, mirrored onto the left edge. Built from the same
   parts on purpose: two bars in different idioms would read as two unrelated
   widgets fighting for the corners, where two bars in one idiom read as a
   pair. Everything here that differs from .chat-dock differs for a stated
   reason - it is a quieter thing than the assistant and should look like it.

   Runs off the LEFT edge: left: 0, no left border, right corners rounded
   alone. Same argument as its twin - a bar that stops short of the edge reads
   as something you could have dragged there, one that runs off reads as
   something that came from off-screen and can go back. */
.colophon-dock {
  position: fixed;
  left: 0;
  bottom: 1.25rem;
  /* 39, one BELOW .chat-dock, and the gap is the whole point (Greg,
     2026-08-15). Both docks used to sit at 40, which is not an ordering at
     all - equal z-index falls back to DOM order, and this include comes after
     the chat widget in base.html, so it won painted-last. Invisible until the
     assistant nudges: the launcher's bubble expands leftward across the
     screen, reaches this corner, and slides underneath it mid-sentence.

     The assistant wins because it is the one saying something. This bar is
     passive chrome and is still above everything else on the page - the top
     nav is 15 and the aurora is 0 - so nothing else changes. */
  z-index: 39;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.3rem 0.4rem 1.25rem;
  background: var(--surface);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--edge-glow);
  border-left: 0;
  border-radius: 0 18px 18px 0;
  box-shadow:
    0 0 18px var(--accent-wash),
    0 14px 36px rgba(0, 0, 0, 0.45);
  transition: transform 240ms ease;
}

/* Slid out: far enough LEFT that only the chevron's own column is left on
   screen. Negative, where the assistant's is positive - the only real
   difference between the two states. */
.colophon-dock.is-away {
  transform: translateX(calc(-100% + 2.6rem));
}

/* Off-screen is not gone: without this the link stays in the tab order and a
   screen reader still finds it, so a visitor who put it away would tab into
   something they cannot see. The delay lets it finish travelling; coming back
   it is instant, because there is nothing to watch arrive if it is invisible
   on the way. */
.colophon-dock-tray {
  display: flex;
  align-items: center;
  transition: visibility 0s linear 0s;
}

.colophon-dock.is-away .colophon-dock-tray {
  visibility: hidden;
  transition: visibility 0s linear 240ms;
}

.colophon-dock-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2rem;
  height: 2.75rem;
  border: 0;
  border-radius: 10px;
  padding: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 150ms ease, background-color 150ms ease;
}

.colophon-dock-toggle:hover {
  color: var(--ink);
  background: var(--accent-wash);
}

.colophon-dock-toggle:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 2px;
}

/* Points the way the bar will TRAVEL - left while it is on screen, right once
   it is gone - rather than at the colophon, which would have it saying the
   same thing in both states. */
.colophon-dock-chevron {
  display: block;
  width: 9px;
  height: 9px;
  border: 2px solid currentColor;
  border-right: 0;
  border-bottom: 0;
  transform: translateX(2px) rotate(-45deg);
  transition: transform 240ms ease;
}

.colophon-dock.is-away .colophon-dock-chevron {
  transform: translateX(-2px) rotate(135deg);
}

/* The link paints nothing itself - it is a transparent wrapper around the
   mark and its label, exactly as .chat-launcher is around the mascot and its
   bubble. No speech-bubble tail here: a tail means something is SPEAKING, and
   this is a footnote rather than an offer of help. */
.colophon-launcher {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.25rem 0.15rem;
  border-radius: 14px;
  text-decoration: none;
  color: var(--ink);
}

.colophon-launcher-mark {
  display: flex;
  flex: none;
  /* Emission, not transmission - the same reasoning as every other glow on
     this surface. A box-shadow would draw a rectangle around a transparent
     glyph; a drop-shadow follows the strokes. */
  filter: drop-shadow(0 0 6px var(--edge-glow));
  transition: transform 200ms ease, filter 200ms ease;
}

.colophon-launcher-label {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: var(--muted);
  white-space: nowrap;
  transition: color 150ms ease;
}

/* The mark turns very slightly on approach. The arc inside it is struck
   between the hexagon's two points, so a rotation reads as the CELL turning
   rather than as an icon being nudged - which is the difference between it
   looking like an object and looking like a sticker. */
.colophon-launcher:hover .colophon-launcher-mark,
.colophon-launcher:focus-visible .colophon-launcher-mark {
  transform: rotate(-8deg) scale(1.06);
  filter: drop-shadow(0 0 10px var(--glow-magenta));
}

.colophon-launcher:hover .colophon-launcher-label,
.colophon-launcher:focus-visible .colophon-launcher-label {
  color: var(--ink);
}

.colophon-launcher:focus-visible {
  outline: 2px solid var(--glow-magenta);
  outline-offset: 3px;
}

.colophon-dock[hidden] {
  display: none;
}

/* On a phone the label is most of the bar's width, so the mark alone says
   where the left dock goes. Both docks remain on the same bottom line; the
   colophon now arrives collapsed, so their trays cannot overlap there. */
@media (max-width: 34rem) {
  .colophon-launcher-label {
    display: none;
  }

  .colophon-dock {
    padding-left: 0.9rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .colophon-dock,
  .colophon-dock-chevron,
  .colophon-launcher-mark {
    transition: none;
  }

  .colophon-dock.is-away .colophon-dock-tray {
    transition: none;
  }

  .colophon-launcher:hover .colophon-launcher-mark,
  .colophon-launcher:focus-visible .colophon-launcher-mark {
    transform: none;
  }
}

/* --- The tuck's flash ----------------------------------------------------- */

/* Both docks put themselves away a couple of seconds after the page settles,
   and then blink once so you know where they went. Without the blink the bar
   is simply gone by the time you look up, and a chevron on the edge of the
   screen is not something anybody notices unaided.

   AFTER the slide, not during it. A glow on a moving object draws the eye to
   the movement and tells you nothing about the destination; a glow on the
   parked bar points at where it parked, which is the whole purpose.

   box-shadow only. It composites, it does not relayout, and these sit over a
   catalog that may still be painting - the same rule the docks' own slide
   follows. */
/* ON THE CHEVRON'S BUTTON, NOT ON THE BAR - and that is a performance rule
   before it is a design one.

   The first version animated box-shadow and border-color on .chat-dock and
   .colophon-dock themselves. Those elements carry backdrop-filter, so any
   animation on them makes the browser re-sample and re-blur everything behind
   them on every frame - and what is behind them is the live arc. The flash
   was not just paying for its own paint, it was pushing the arc through a
   blur pass 60 times a second for the better part of two seconds. The page
   flickered, and it flickered for the entire flash.

   Moving it to the toggle fixes that and is also simply more correct: once
   the bar has tucked, the chevron's column is the ONLY part still on screen.
   The old version glowed mostly off-screen pixels and blurred the backdrop
   for all of them. */
@keyframes dock-noticed {
  0%,
  100% {
    color: var(--muted);
    background: transparent;
    box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  }
  40% {
    color: var(--ink);
    background: var(--accent-wash);
    box-shadow: 0 0 14px var(--glow-magenta);
  }
}

.chat-dock.is-noticed .chat-dock-toggle,
.colophon-dock.is-noticed .colophon-dock-toggle {
  /* Twice. Once reads as a rendering hiccup; three times is a notification
     demanding to be dealt with, which this is not. */
  animation: dock-noticed 900ms ease-in-out 2;
  border-radius: 10px;
}

/* Nothing here under reduced motion. The bar having moved is already the
   information; the glow on top of it is decoration, and decoration is what
   that preference is asking us to drop. dock-tuck.js checks the same query
   before adding the class, so this is the belt to its braces. */
@media (prefers-reduced-motion: reduce) {
  .chat-dock.is-noticed .chat-dock-toggle,
  .colophon-dock.is-noticed .colophon-dock-toggle {
    animation: none;
  }
}

/* SOMETHING IS WAITING BEHIND THE CHEVRON (Greg, 2026-08-20).

   The nudge used to slide the whole bar back onto the screen. Now it stays
   where the shopper left it and this is what says a remark arrived: a slow
   pulse on the chevron, which is the only part of a tucked dock still
   visible.

   Different from .is-noticed above in the two ways that matter. That one
   fires twice and stops, because it is only pointing at where the bar
   parked - information with a shelf life of a second. This one runs until
   the chevron is pressed (chat.js clears the class on that click, and on the
   panel opening), because what it points at does not expire: the remark is
   on the session's transcript and is still there on the next page.

   Slower and shallower than the notice flash for the same reason - it is
   going to be on screen for as long as the shopper ignores it, and a bright
   two-a-second blink in the corner of a page somebody is trying to read is
   an alarm. This is a light left on.

   On the toggle, not the bar, and that is the performance rule stated above:
   .chat-dock carries backdrop-filter, and animating it re-blurs the live arc
   behind it every frame - for a loop with no end, forever. */
@keyframes dock-pending {
  0%,
  100% {
    color: var(--muted);
    background: transparent;
    box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  }
  50% {
    color: var(--ink);
    background: var(--accent-wash);
    box-shadow: 0 0 12px var(--glow-magenta);
  }
}

.chat-dock.is-pending .chat-dock-toggle {
  animation: dock-pending 2200ms ease-in-out infinite;
  border-radius: 10px;
}

/* NOT `animation: none` here, unlike the notice flash. That one drops to
   nothing because the bar having moved was already the information and the
   glow was decoration on top of it. This one IS the information - it is the
   only thing on the page saying a remark is waiting - so under reduced
   motion it becomes a lit chevron rather than a pulsing one. Still on until
   pressed, just not moving. */
@media (prefers-reduced-motion: reduce) {
  .chat-dock.is-pending .chat-dock-toggle {
    animation: none;
    color: var(--ink);
    background: var(--accent-wash);
    box-shadow: 0 0 12px var(--glow-magenta);
  }
}
/* Source-specific My Peps data is public on the product page; the printed
   flyer shows only its featured compact edition. */
.research-guide { margin-top: 24px; padding: clamp(20px, 4vw, 36px); }
.research-guide-intro { max-width: 850px; margin-bottom: 20px; }
.research-guide-intro h2 { margin: 4px 0 10px; }
.research-guide-intro > p:last-child { color: var(--muted, #aeb2c3); }
.research-guide-reference { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: baseline; margin: 0 0 16px; padding: 12px 14px; border-left: 3px solid #b14eff; background: rgba(177,78,255,.08); color: var(--muted, #aeb2c3); font-size: .9rem; }
.research-guide-reference strong { color: var(--text, #fff); }
.research-guide-reference a { margin-left: auto; }
.research-guide-picker { display: grid; gap: 6px; max-width: 680px; margin: 0 0 16px; color: var(--muted, #aeb2c3); font-size: .84rem; }
.research-guide-picker select { width: 100%; min-height: 48px; padding: 10px 42px 10px 13px; border: 1px solid rgba(255,255,255,.2); border-radius: 10px; background: rgba(8,8,18,.72); color: var(--text, #fff); }
.research-guide-unavailable { display: inline-flex; align-items: center; padding: 8px 12px; color: var(--muted, #aeb2c3); font-size: .88rem; }
.research-guide-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); }
.research-guide-card { border: 1px solid rgba(255,255,255,.14); border-radius: 14px; padding: 18px; background: rgba(8,8,18,.36); }
.research-guide-card.is-featured { border-color: rgba(177,78,255,.65); }
.research-guide-head { display: flex; gap: 10px; align-items: start; justify-content: space-between; }
.research-guide-head h3 { margin: 0; font-size: 1.05rem; }
.research-guide-head span { border: 1px solid currentColor; border-radius: 999px; padding: 2px 8px; color: #c993ff; font-size: .72rem; text-transform: uppercase; }
.research-guide-route, .research-guide-note { color: var(--muted, #aeb2c3); font-size: .86rem; }
.research-guide-fit { padding: 10px 12px; border-radius: 9px; background: rgba(177,78,255,.1); font-size: .9rem; }
.research-guide-fit.is-warning { background: rgba(255,180,60,.1); }
.research-guide-table { margin: 12px 0; border-top: 1px solid rgba(255,255,255,.11); }
.research-guide-table > div { display: grid; grid-template-columns: 1.2fr .8fr 1fr .75fr; gap: 8px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,.08); font-size: .84rem; }

/* --- What's Next -------------------------------------------------------- */

.next-page {
  max-width: 70rem;
}

.next-head,
.next-section-head,
.next-inventory-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.next-head h1 {
  margin: 0;
  font-size: clamp(2rem, 5vw, 3.35rem);
  line-height: 1.05;
}

.next-eyebrow,
.next-kicker,
.next-when {
  margin: 0;
  color: var(--accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.next-lede {
  max-width: 44rem;
  margin: 0.75rem 0 0;
  color: var(--muted);
  font-size: 1rem;
}

.next-alert-link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 0.7rem;
  border-radius: 10px;
  color: var(--ink);
  text-decoration: none;
}

.next-alert-link:hover,
.next-alert-link:focus-visible {
  background: var(--accent-wash);
}

.next-summary {
  display: grid;
  /* min(15rem, 100%) for .my-peps-past-due-event's reason: a bare 15rem is a
     hard floor that overflows a narrower container rather than shrinking. */
  grid-template-columns: minmax(0, 1.45fr) minmax(min(15rem, 100%), 0.55fr);
  gap: 1rem;
  margin-top: 1.75rem;
}

.next-summary--single {
  grid-template-columns: 1fr;
}

.next-focus,
.next-inventory,
.next-row {
  border: 1px solid var(--hairline);
  border-radius: 16px;
  background: var(--surface);
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.18);
}

.next-focus {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  padding: 1.35rem;
  border-color: color-mix(in srgb, var(--accent) 55%, var(--hairline));
}

.next-focus--clear {
  border-color: var(--hairline);
}

.next-icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: 0 0 auto;
  border-radius: 12px;
  background: var(--surface-tile);
  color: var(--accent);
  font-size: 1.2rem;
}

.next-copy h2,
.next-copy h3,
.next-inventory h2 {
  margin: 0.2rem 0 0;
  font-size: 1rem;
  line-height: 1.25;
}

.next-copy p:not(.next-kicker),
.next-inventory p {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

.next-primary,
.next-secondary {
  white-space: nowrap;
}

.next-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  margin-top: 0;
  padding: 0.7rem 0.9rem;
  border-radius: 10px;
  font-size: 0.72rem;
  text-decoration: none;
}

.next-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--hairline);
  border-radius: 9px;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 700;
  text-decoration: none;
}

.next-secondary:hover,
.next-secondary:focus-visible {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.next-inventory {
  padding: 1.2rem;
}

.next-inventory-head > i {
  color: var(--accent);
  font-size: 1.4rem;
}

.next-inventory-value {
  margin-top: 1rem;
  font-size: 2rem;
  font-weight: 700;
}

.next-progress {
  height: 0.48rem;
  margin-top: 0.65rem;
  overflow: hidden;
  border-radius: 999px;
  background: var(--surface-tile);
}

.next-progress span {
  display: block;
  height: 100%;
  max-width: 100%;
  border-radius: inherit;
  background: var(--accent);
}

.next-section-head {
  align-items: baseline;
  margin-top: 2rem;
}

.next-section-head h2 {
  margin: 0;
  font-size: 1.15rem;
}

.next-section-head span {
  color: var(--muted);
  font-size: 0.78rem;
}

.next-timeline {
  display: grid;
  gap: 0.7rem;
  margin-top: 0.75rem;
}

.next-row {
  display: grid;
  grid-template-columns: auto 6rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
}

.next-row--attention {
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
}

.next-empty {
  padding: 1.25rem;
  color: var(--muted);
}

.next-empty p {
  margin: 0;
}

@media (max-width: 760px) {
  .page {
    padding-bottom: calc(4.7rem + env(safe-area-inset-bottom));
  }

  .next-summary {
    grid-template-columns: 1fr;
  }

  .next-focus {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .next-focus .next-primary {
    grid-column: 1 / -1;
    width: 100%;
    text-align: center;
  }

  .next-row {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .next-row .next-when {
    align-self: start;
  }

  .next-row .next-copy,
  .next-row .next-primary,
  .next-row .next-secondary {
    grid-column: 2;
  }

  .next-row .next-primary,
  .next-row .next-secondary {
    justify-self: start;
  }

  .chat-dock,
  .colophon-dock {
    bottom: calc(5.4rem + env(safe-area-inset-bottom));
  }
}

@media (max-width: 34rem) {
  .next-head {
    align-items: flex-start;
  }

  .next-alert-link {
    padding-inline: 0.45rem;
  }
}
.research-guide-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.research-guide-actions a { display: inline-flex; padding: 8px 12px; border: 1px solid rgba(255,255,255,.22); border-radius: 8px; }
@media (max-width: 560px) { .research-guide-table > div { grid-template-columns: 1fr 1fr; } }

/* The tracking card (S10), and the number that opens it. The card chrome
   itself comes from staffportal/modal.css - the one modal language shared
   with the portal - so only the card's own contents are described here. */

a.invoice-tracking-number {
  color: inherit;
  text-decoration-color: var(--muted);
  text-underline-offset: 3px;
}

a.invoice-tracking-number:hover,
a.invoice-tracking-number:focus-visible {
  color: var(--accent);
}

.rh-tracking-facts {
  margin: 0;
  display: grid;
  gap: 0.55rem;
}

.rh-tracking-facts > div {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: 0.5rem;
  align-items: baseline;
}

.rh-tracking-facts dt {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.rh-tracking-facts dd {
  margin: 0;
}

.rh-tracking-number {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  letter-spacing: 0.03em;
}

.rh-tracking-status {
  font-weight: 600;
}

.rh-tracking-note {
  margin: 0.9rem 0 0;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.5;
}

.rh-tracking-actions {
  margin: 1.1rem 0 0;
}

/* A pill, like every other action in a card. */
.rh-tracking-link {
  display: inline-block;
  padding: 0.5rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--line, rgba(255, 255, 255, 0.18));
  text-decoration: none;
  font-weight: 600;
}

/* Small screens: the label column stops earning its width. */
@media (max-width: 480px) {
  .rh-tracking-facts > div {
    grid-template-columns: 1fr;
    gap: 0.15rem;
  }
}

/* The staff full page, which has no scrim to centre the card for it. */
.rh-modal--page {
  max-width: 520px;
  margin: 2rem auto;
}

.rh-tracking-page {
  max-width: 520px;
  margin: 2rem auto;
}

.rh-tracking-back {
  margin: 0 0 0.8rem;
  font-size: 0.9rem;
}

/* --- The install offer and the PWA controls ------------------------------- */

/* Fixed, and ABOVE the two docks rather than beside them. The colophon dock
   and the assistant's launcher both sit at z-index 40 in the bottom corners,
   so a banner at the same level would be a race between three fixed elements
   for the same strip of screen. On a narrow viewport it clears them
   vertically instead - see the media query below.

   --surface-float and a blur, not --surface: this floats over page content
   rather than over the aurora, and 0.72 opacity over a catalog grid is
   product photography showing through the sentence you are reading. */
.pwa-banner {
  position: fixed;
  z-index: 45;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  width: min(30rem, calc(100vw - 2rem));
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--edge-glow);
  border-radius: 1rem;
  background: var(--surface-float);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  backdrop-filter: blur(18px) saturate(130%);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}

.pwa-banner[hidden] {
  display: none;
}

.pwa-banner-body {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
}

.pwa-banner-mark {
  flex: 0 0 auto;
  width: 3rem;
  height: 3rem;
  border-radius: 0.7rem;
}

.pwa-banner-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.9rem;
  line-height: 1.4;
}

.pwa-banner-text strong {
  font-size: 1rem;
}

.pwa-banner-text span {
  color: var(--muted);
}

.pwa-banner-actions {
  display: flex;
  gap: 0.6rem;
}

/* Both buttons full-width-ish and side by side: "Not now" has to be as easy
   to hit as the offer, or a one-time prompt becomes a thing people dismiss by
   accident and then cannot find again. */
.pwa-banner-action {
  flex: 1;
  padding: 0.7rem 1rem;
  border: 1px solid var(--edge-glow);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}

.pwa-banner-action--primary {
  border-color: transparent;
  background: var(--accent);
  color: var(--accent-ink);
}

.pwa-banner-action:hover {
  filter: brightness(0.96);
}

.pwa-banner-steps {
  margin: 0;
  padding-left: 1.2rem;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.6;
}

.pwa-banner-steps[hidden] {
  display: none;
}

/* Above the docks on a narrow screen, where there is no horizontal room to
   sit between them. */
@media (max-width: 40rem) {
  .pwa-banner {
    bottom: 5.5rem;
  }
}

/* --- The Account settings card -------------------------------------------- */

.pwa-state {
  margin: 0 0 1rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.pwa-row {
  margin-top: 1rem;
}

.pwa-row[hidden] {
  display: none;
}

/* Empty until the script has something to report, and it must take no space
   when it has nothing - an always-present blank line under a checkbox reads
   as a layout bug. */
.pwa-message {
  margin: 0.6rem 0 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.pwa-message:empty {
  display: none;
}

/* Firefox on iOS, which has no Add to Home Screen at all - the steps above it
   are unfollowable there, so this replaces them rather than qualifying them. */
.pwa-banner-blocked {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

.pwa-banner-blocked[hidden],
.pwa-banner-action[hidden] {
  display: none;
}

/* The docks, in the installed app. Same problem the mobile nav has at the
   bottom of site-nav.css: with no browser toolbar beneath them, a control
   anchored 1.25rem off the bottom edge sits in the strip iOS reserves for the
   home-indicator swipe - and the chat launcher is a tap target in the exact
   corner that gesture starts from (Greg, 2026-08-15, iPhone).

   Here rather than beside the nav's copy of this: base.html loads site.css
   after site-nav.css, and these two selectors are defined above, so an
   override in the other file would lose on source order and silently do
   nothing.

   The inset term is zero until the site asks for `viewport-fit=cover`, which
   it deliberately does not - see the long note in site-nav.css. The fixed
   part is what does the work. */
@media (display-mode: standalone) {
  .colophon-dock,
  .chat-dock {
    bottom: calc(2.5rem + env(safe-area-inset-bottom));
  }
}

/* Below 760px the mobile nav owns the bottom edge, and the docks have to park
   ABOVE it rather than a fixed distance off a screen edge it no longer ends
   at. Anchored 1.25rem up they landed inside it, so the tucked chevrons sat
   over "Catalog" and "My Peps" (Greg, 2026-08-15, installed app).

   --mobile-nav-h is measured by static/js/dock-tuck.js, because that height is
   a sum of icon, gap, label and a bottom padding that itself varies with
   display-mode and the safe-area inset. A constant copied here would drift
   away from the nav the next time one of those changed, silently, which is
   how the collision arrived. The fallback is a rough version of the same sum,
   for the moment before the script runs and for the case where it never does.

   AFTER the standalone rule above on purpose: both match inside an installed
   app on a phone, equal specificity means source order decides, and this is
   the one that should win - the measurement already includes whatever extra
   padding standalone gave the nav, so adding the two would double it. The
   rule above is then doing its real job on the case this one does not cover:
   the app installed on a desktop, where there is no mobile nav at all. */
@media (max-width: 760px) {
  .colophon-dock,
  .chat-dock {
    bottom: calc(var(--mobile-nav-h, 4rem) + 0.6rem);
  }
}

/* ==========================================================================
   Peptide Tracker - the shelf
   --------------------------------------------------------------------------
   Replaced the My Peps Overview / Schedule / Settings split. The peptide is
   the object: a rail of vials selects one, and its lane holds everything the
   old overview card carried plus that peptide's own curve.

   Colour discipline (STYLE-GUIDE section 3): a status pill and a vial halo
   take a SEMANTIC ink because they say whether a thing is in trouble. A dot,
   a lane accent and a chart line take the peptide's SERIES colour because
   they only say which peptide this is. The two systems never swap.
   ========================================================================== */

.tk-page { max-width: 1240px; }

.tk-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 1.1rem;
}
.tk-head h1 { margin: 0 0 0.25rem; }
.tk-head p { margin: 0; color: var(--muted); }
.tk-head-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* --- the view toggle --- */
/* One pill: the three views and the Automation disclosure. No overflow
   clipping - the disclosure's panel has to escape the pill - so the end caps
   are rounded on the first and last segments instead. */
.tk-seg {
  display: inline-flex;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface);
}
.tk-seg-views { display: contents; }
.tk-seg-views button,
.tk-seg > .tk-automation > summary {
  padding: 0.45rem 0.95rem;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.88rem;
  cursor: pointer;
}
.tk-seg-views button:first-child { border-radius: 999px 0 0 999px; }
.tk-seg-views button + button,
.tk-seg > .tk-automation > summary { border-left: 1px solid var(--rule); }
.tk-seg-views button:hover,
.tk-seg > .tk-automation > summary:hover { color: var(--ink); }
.tk-seg-views button[aria-pressed="true"] { background: var(--accent-wash); color: var(--ink); }

/* --- automation disclosure (this is where the Settings tab went) --- */
.tk-automation { position: relative; }
.tk-automation > summary {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
  border-radius: 0 999px 999px 0;
  list-style: none;
}
.tk-automation > summary::-webkit-details-marker { display: none; }
.tk-automation[open] > summary { background: var(--accent-wash); color: var(--ink); }
.tk-automation-card {
  /* Floats over the PAGE and may blur, so --surface-float, not --surface.
     See STYLE-GUIDE section 3, "which surface a panel takes". */
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.5rem);
  right: 0;
  width: min(23rem, calc(100vw - 1.5rem));
  /* The three switches carry real explanations - what consumes inventory,
     what is retroactive - and on a short viewport that panel is taller than
     the screen. Cap it and let it scroll rather than trimming copy that
     answers "will this change my numbers". */
  max-height: min(32rem, calc(100vh - 9rem));
  /* The switches scroll; the Save button does not. Capping the whole panel
     put the one control that commits the change below the fold, which is the
     worst thing to hide in a box that is already easy to dismiss. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 1rem 1.1rem 1.15rem;
  border: 1px solid var(--hairline);
  border-radius: 0.9rem;
  background: var(--surface-float);
  backdrop-filter: blur(14px);
  box-shadow: 0 18px 40px rgba(2, 3, 8, 0.55);
}
/* Tighter than the old full-page settings card: this is a popover, and the
   markup is no longer shared with anything else. */
.tk-automation-card form { display: flex; flex-direction: column; min-height: 0; }
.tk-automation-card .my-peps-settings-list {
  gap: 0.6rem;
  margin-bottom: 0.9rem;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline) transparent;
}
.tk-automation-card .my-peps-settings-list::-webkit-scrollbar { width: 6px; }
.tk-automation-card .my-peps-settings-list::-webkit-scrollbar-track { background: transparent; }
.tk-automation-card .my-peps-settings-list::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: var(--hairline);
}
.tk-automation-card .my-peps-settings-row { padding: 0.6rem 0.7rem; }
.tk-automation-card .my-peps-settings-row > div label { font-size: 0.88rem; font-weight: 600; }
.tk-automation-card .my-peps-settings-row > .helptext { font-size: 0.76rem; line-height: 1.45; color: var(--muted); }
.tk-automation-card .my-peps-settings-footnote { margin-top: 0.9rem; padding-top: 0.75rem; font-size: 0.78rem; }
.tk-automation-card h2 { margin: 0 0 0.2rem; font-size: 1.05rem; }
.tk-automation-card > p { margin: 0 0 0.7rem; font-size: 0.85rem; }

/* --- past-due strip: above every view, never inside one --- */
.tk-pastdue {
  margin-bottom: 1.1rem;
  padding: 0.95rem 1.1rem 1.05rem;
  border: 1px solid color-mix(in srgb, var(--warn-ink) 42%, transparent);
  border-radius: 0.9rem;
  background: var(--surface);
}
.tk-pastdue > header { margin-bottom: 0.7rem; }
.tk-pastdue h2 { margin: 0; font-size: 1.05rem; color: var(--warn-ink); }
.tk-pastdue > header p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.85rem; }
.tk-pastdue-list { display: grid; gap: 0.55rem; }
.tk-pastdue-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.85rem;
  align-items: center;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--series-color, var(--muted));
  border-radius: 0.55rem;
  background: var(--surface-tile);
}
.tk-pastdue-who { display: inline-flex; gap: 0.4rem; align-items: center; font-weight: 600; }
.tk-pastdue-when { color: var(--muted); font-size: 0.88rem; }
.tk-pastdue-actions { display: inline-flex; flex-wrap: wrap; gap: 0.45rem; align-items: flex-end; margin-left: auto; }
.tk-pastdue-actions label { display: grid; gap: 0.15rem; color: var(--muted); font-size: 0.72rem; }
.tk-pastdue-blocked { display: grid; gap: 0.15rem; margin-left: auto; max-width: min(24rem, 100%); }
.tk-pastdue-blocked strong { color: var(--danger-ink); font-size: 0.85rem; }
.tk-pastdue-blocked small { color: var(--muted); }

/* --- the shelf rail --- */
/* The vial stage is a fixed block and every slot starts at the same y, so
   the vials land on ONE baseline no matter how many lines a peptide's name
   or sub-line takes. Bottom-aligning the slots instead - the obvious first
   guess - floats a vial upward the moment its label wraps, which is exactly
   what a shelf must not do. */
/* The shelf is a panel, and the rail inside it is a tile.
   Two grounds, not one: --surface is the windowed panel every other card on
   the page takes, and --surface-tile is what sits inside a card behind
   imagery - which is exactly what the rail is, a row of vial renders. The
   nesting is what makes the vials read as standing IN a cabinet rather than
   floating on the aurora, and the aurora's brightest arcs were running
   straight between them. */
.tk-shelf {
  position: relative;
  margin-bottom: 1.4rem;
  padding: 0.75rem 0.8rem 0.85rem;
  border: 1px solid var(--hairline);
  border-radius: 0.9rem;
  background: var(--surface);
  --tk-stage-h: 5.6rem;
  --tk-rail-pad: 0.7rem;
}
.tk-shelf-rail {
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  overflow-x: auto;
  padding: var(--tk-rail-pad) 0.55rem 0.9rem;
  border-radius: 0.6rem;
  background: var(--surface-tile);
  scroll-snap-type: x proximity;
  /* The default bar is a thick light slab directly under the vials, and it
     reads as a piece of the design rather than as chrome. */
  scrollbar-width: thin;
  scrollbar-color: var(--hairline) transparent;
}
.tk-shelf-rail::-webkit-scrollbar { height: 6px; }
.tk-shelf-rail::-webkit-scrollbar-track { background: transparent; }
.tk-shelf-rail::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: var(--hairline);
}
.tk-shelf-rail::-webkit-scrollbar-thumb:hover { background: var(--rule); }
/* The plank is the line the vials stand on, so it is drawn AT the foot of
   the stage rather than under the whole rail - under the labels it reads as
   a stray rule, or worse, as the scrollbar. */
.tk-shelf-plank {
  position: absolute;
  z-index: 0;
  /* Shelf padding + rail padding + the slot's own top padding, so the line
     lands at the foot of the stage wherever those are retuned. */
  top: calc(var(--tk-stage-h) + 2.15rem);
  right: 1.35rem;
  left: 1.35rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--hairline) 10%, var(--hairline) 90%, transparent);
  box-shadow: 0 6px 18px rgba(2, 3, 8, 0.8);
}
.tk-slot {
  display: grid;
  flex: 0 0 auto;
  gap: 0.3rem;
  justify-items: center;
  width: 10.5rem;
  padding: 0.7rem 0.6rem 0.8rem;
  border: 1px solid transparent;
  border-radius: 0.85rem 0.85rem 0.4rem 0.4rem;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  scroll-snap-align: center;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
/* The rail's ground is already --surface-tile, so a slot lifts by going
   LIGHTER against it, and the selected one is told apart by its lit border
   and the lift rather than by a second fill. */
.tk-slot:hover { background: var(--surface); border-color: var(--rule); }
.tk-slot[aria-selected="true"] {
  border-color: var(--edge-glow);
  background: var(--surface);
  transform: translateY(-4px);
}
.tk-slot[aria-selected="true"] .tk-slot-name { color: var(--ink); }
.tk-slot-name {
  display: inline-flex;
  gap: 0.35rem;
  align-items: center;
  color: var(--muted);
  font-weight: 600;
  font-size: 0.92rem;
}
.tk-slot-sub { color: var(--muted); font-size: 0.72rem; line-height: 1.35; }
.tk-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 999px;
  background: var(--series-color, var(--muted));
  box-shadow: 0 0 8px color-mix(in srgb, var(--series-color, transparent) 60%, transparent);
}

/* --- the vial on the shelf --- */
.tk-vial-stage {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: end center;
  width: 100%;
  height: var(--tk-stage-h, 5.6rem);
  margin-bottom: 0.35rem;
}
/* The 0.657 width:height ratio is not arbitrary - it is the overview card's
   (2.2rem / 3.35rem), and the liquid chamber's percentages below are quoted
   against it. The neon render is a photo at a fixed aspect; stretch the box
   away from that ratio and the drawn liquid stops lining up with the glass
   in the picture. Change one, recheck the other. */
.tk-vial-main {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  width: 3.2rem;
  height: 4.87rem;
  border-radius: 0.5rem;
  background: #020204;
  color: var(--ok-ink);
  box-shadow: 0 0 16px color-mix(in srgb, currentColor 40%, transparent);
}
.tk-vial-main.is-warn { color: var(--warn-ink); }
.tk-vial-main.is-danger { color: var(--danger-ink); }
.tk-vial-main.is-muted { color: var(--muted); }
.tk-vial-main > img {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 50%;
  width: 190%;
  max-width: none;
  height: auto;
  transform: translate(-50%, -50%);
}
.tk-vial-main > .at-vial-stand { position: relative; z-index: 1; font-size: 2.6rem; }
/* Same clipped chamber the overview card used: the neon render is a photo of
   a full vial, so the liquid level is drawn over it rather than in it. */
.tk-liquid-chamber {
  position: absolute;
  z-index: 2;
  right: 28%;
  bottom: 13%;
  left: 28%;
  height: 54%;
  overflow: hidden;
  border-radius: 0.2rem 0.2rem 0.35rem 0.35rem;
  background: rgba(6, 8, 18, 0.9);
  box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.12);
}
.tk-liquid-chamber > i {
  position: absolute;
  inset: auto 0 0;
  height: calc(var(--remaining) * 1%);
  border-radius: 0.14rem 0.14rem 0.3rem 0.3rem;
  background: color-mix(in srgb, var(--series-color, currentColor) 72%, #25114a);
  box-shadow: 0 -1px 5px color-mix(in srgb, var(--series-color, currentColor) 68%, transparent),
    inset 0 1px rgba(255, 255, 255, 0.38);
  transition: height 180ms ease;
}
.tk-vial-fill {
  position: relative;
  z-index: 3;
  color: var(--ink);
  font-size: 0.95rem;
  line-height: 1;
  text-shadow: 0 1px 4px #080c16, 0 0 8px #080c16;
}
.tk-vial-fill.is-setup { color: var(--warn-ink); font-size: 0.68rem; letter-spacing: 0.06em; }
/* Racked BEHIND the working vial and half a step to the right, so the shelf
   reads as depth. Positioned against the stage's centre rather than its
   right edge - the stage is as wide as the slot, and pinning to the edge
   detached the reserves from the vial they belong to. They keep the main
   vial's aspect ratio: object-fit: cover squashed a transparent PNG of a
   vial into something that no longer looked like one. */
.tk-vial-reserve {
  position: absolute;
  z-index: 0;
  bottom: 0;
  left: 50%;
  display: flex;
  align-items: flex-end;
  transform: translateX(-10%);
}
.tk-vial-reserve img,
.tk-vial-reserve i {
  width: 2.4rem;
  height: 3.65rem;
  border-radius: 0.4rem;
  background: #020204;
  opacity: 0.5;
  filter: saturate(0.55) brightness(0.8);
}
.tk-vial-reserve > * + * { margin-left: -1.4rem; }
.tk-vial-reserve > b {
  align-self: center;
  margin-left: 0.15rem;
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 600;
}
.tk-vial-main { z-index: 1; }

.tk-pill {
  padding: 0.16rem 0.55rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.tk-pill.is-ok { background: color-mix(in srgb, var(--ok-ink) 18%, transparent); color: var(--ok-ink); }
.tk-pill.is-warn { background: color-mix(in srgb, var(--warn-ink) 18%, transparent); color: var(--warn-ink); }
.tk-pill.is-danger { background: color-mix(in srgb, var(--danger-ink) 20%, transparent); color: var(--danger-ink); }
.tk-pill.is-muted { background: var(--surface-tile); color: var(--muted); }

/* Sized and aligned like a slot so it stands on the same plank, rather than
   as a box floating beside the shelf. */
.tk-slot-add {
  justify-items: center;
  align-content: start;
  width: 7.5rem;
  padding-top: calc(var(--tk-stage-h, 5.6rem) - 2.6rem);
  border: 1px dashed var(--hairline);
  color: var(--muted);
}
.tk-slot-add:hover { border-color: var(--edge-glow); color: var(--ink); }
.tk-slot-add .tk-plus { font-size: 1.8rem; line-height: 1; }
.tk-slot-add .tk-slot-name { font-size: 0.82rem; }

/* The lane's primary action is filled; every other control there is an
   outlined pill. Outline-vs-outline made "Log a dose now" and "Delete" read
   as the same weight. */
.tk-lane-actions .my-peps-action--primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
}
.tk-lane-actions .my-peps-action--primary:hover { background: var(--accent-deep); }

/* The empty state's Add action is the page's only one when there is no shelf
   to carry the slot, so it keeps .btn's full-width block treatment and sits
   centred under the message. */
.my-peps-empty .btn { max-width: 16rem; margin: 1rem auto 0; }

/* --- the lane --- */
.tk-lane {
  padding: 1.1rem 1.2rem 1.25rem;
  border: 1px solid var(--hairline);
  border-top: 2px solid var(--series-color, var(--hairline));
  border-radius: 0.9rem;
  background: var(--surface);
}
.tk-lane-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  align-items: center;
  margin-bottom: 1rem;
}
.tk-lane-head h2 { display: inline-flex; gap: 0.5rem; align-items: center; margin: 0; font-size: 1.3rem; }
.tk-lane-variant { color: var(--muted); font-size: 0.85rem; }
.tk-lane-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-left: auto; }
.tk-lane-actions form { display: contents; }

.tk-lane-grid { display: grid; gap: 0.85rem; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tk-cell {
  padding: 0.85rem 0.95rem 0.95rem;
  border: 1px solid var(--rule);
  border-radius: 0.7rem;
  background: var(--surface-tile);
}
.tk-cell-span2 { grid-column: span 2; }
.tk-cell-span3 { grid-column: 1 / -1; }
.tk-cell h3 { margin: 0 0 0.6rem; font-size: 0.82rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }

.tk-facts { display: grid; gap: 0.45rem; margin: 0; }
.tk-facts > div { display: flex; gap: 0.6rem; justify-content: space-between; align-items: baseline; }
.tk-facts dt { color: var(--muted); font-size: 0.82rem; }
.tk-facts dd { margin: 0; text-align: right; font-size: 0.9rem; }
.tk-facts dd.is-warning { color: var(--warn-ink); }
.tk-facts dd.is-danger { color: var(--danger-ink); }

.tk-notice { margin-top: 0.8rem; padding: 0.7rem 0.8rem; border-radius: 0.55rem; font-size: 0.85rem; }
.tk-notice.is-danger { border: 1px solid color-mix(in srgb, var(--danger-ink) 40%, transparent); background: color-mix(in srgb, var(--danger-ink) 10%, transparent); }
.tk-notice-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.6rem; }
.tk-notice-actions form { display: contents; }

/* --- titration ladder --- */
.tk-ladder { display: flex; gap: 0.35rem; align-items: flex-end; justify-content: flex-start; overflow-x: auto; padding-bottom: 0.3rem; }
/* Capped width: a three-week plan stretched to fill the cell drew rungs so
   wide they stopped reading as a bar chart. */
.tk-rung { display: grid; flex: 0 1 3.2rem; min-width: 2.6rem; max-width: 4.5rem; gap: 0.3rem; justify-items: center; align-content: end; color: var(--muted); font-size: 0.68rem; line-height: 1.3; text-align: center; }
.tk-rung-bar { width: 100%; border-radius: 0.25rem 0.25rem 0 0; background: color-mix(in srgb, var(--series-color, var(--muted)) 26%, transparent); }
.tk-rung.is-current { color: var(--ink); font-weight: 600; }
.tk-rung.is-current .tk-rung-bar { background: var(--series-color, var(--ink)); box-shadow: 0 0 10px color-mix(in srgb, var(--series-color, transparent) 55%, transparent); }
.tk-rung.is-next .tk-rung-bar { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--series-color, var(--muted)) 45%, transparent) 0 4px, transparent 4px 8px); outline: 1px dashed color-mix(in srgb, var(--series-color, var(--muted)) 60%, transparent); }
.tk-titration-note { margin: 0.7rem 0 0.5rem; font-size: 0.85rem; }
.tk-titration-actions { display: flex; flex-wrap: wrap; gap: 0.45rem; }

/* --- lane sparkline --- */
.tk-spark { margin: 0; }
.tk-spark .my-peps-chart { width: 100%; height: 7rem; }
/* On the full chart the today band is a day-wide window and reads as one.
   Cropped into a 7rem sparkline it became a broad maroon slab competing with
   the curve, so here it is a wash with a lit leading edge - the "now" line,
   not a block. */
.tk-spark-today { fill: color-mix(in srgb, var(--accent) 7%, transparent); }
.tk-spark-now { stroke: color-mix(in srgb, var(--accent) 70%, transparent); stroke-width: 1.5; }
.tk-spark-note { margin: 0.5rem 0 0; color: var(--muted); font-size: 0.75rem; }

/* --- dose lists --- */
.tk-dose-row { display: flex; gap: 0.5rem; justify-content: space-between; align-items: baseline; padding: 0.4rem 0; border-top: 1px solid var(--rule); font-size: 0.82rem; }
.tk-dose-row:first-of-type { border-top: 0; }
.tk-dose-row.is-past { opacity: 0.72; }
.tk-dose-when { color: var(--muted); }
.tk-dose-amt { font-weight: 600; }
.tk-dose-tag { color: var(--muted); font-size: 0.72rem; white-space: nowrap; }
.tk-dose-tag.is-completed { color: var(--ok-ink); }
.tk-dose-tag.is-skipped, .tk-dose-tag.is-missed { color: var(--muted); }
.tk-dose-tag.is-danger { color: var(--danger-ink); }

/* --- vial lifecycle chips --- */
.tk-chip-row { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.tk-chip { display: inline-flex; gap: 0.4rem; align-items: center; padding: 0.3rem 0.6rem; border: 1px solid var(--rule); border-radius: 999px; background: var(--surface); font-size: 0.78rem; }
/* A counted group is a summary, not a vial - quieter, and dashed so it does
   not read as one more individually-tracked item. */
.tk-chip.is-count { border-style: dashed; background: transparent; color: var(--muted); }
.tk-chip-state { font-size: 0.68rem; letter-spacing: 0.03em; text-transform: uppercase; }
.tk-chip-state.is-active { color: var(--ok-ink); }
.tk-chip-state.is-received, .tk-chip-state.is-setup_needed { color: var(--info-ink); }
.tk-chip-state.is-shipped, .tk-chip-state.is-ordered { color: var(--warn-ink); }
.tk-chip-state.is-depleted, .tk-chip-state.is-archived { color: var(--muted); }
.tk-chip form { display: contents; }
.tk-chip button { padding: 0.15rem 0.5rem; border: 1px solid var(--hairline); border-radius: 999px; background: transparent; color: var(--link-ink); font: inherit; font-size: 0.72rem; cursor: pointer; }

/* --- setup guidance --- */
.tk-setup { border-color: color-mix(in srgb, var(--warn-ink) 35%, transparent); }
.tk-setup.is-ready { border-color: color-mix(in srgb, var(--ok-ink) 35%, transparent); }
.tk-setup-steps { margin: 0 0 0.3rem; padding-left: 1.1rem; font-size: 0.86rem; line-height: 1.6; }

.tk-week-hint { margin: 0.8rem 0 0; color: var(--muted); font-size: 0.78rem; }


@media (max-width: 900px) {
  .tk-lane-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tk-cell-span2, .tk-cell-span3 { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .tk-head-actions { width: 100%; }
  .tk-seg { flex: 1 1 auto; }
  .tk-seg-views button { flex: 1 1 0; }
  .tk-lane-grid { grid-template-columns: minmax(0, 1fr); }
  .tk-lane-actions { margin-left: 0; width: 100%; }
  /* The rail keeps a peek of the next vial rather than wrapping: the shelf
     only reads as a shelf while the vials sit in a row. */
  .tk-shelf-rail { scroll-snap-type: x mandatory; padding-right: 20%; }
  .tk-slot { width: 8.4rem; }
  .tk-vial-stage { height: 6.6rem; }
  .tk-vial-main { width: 2.7rem; height: 6rem; }
  .tk-pastdue-actions, .tk-pastdue-blocked { margin-left: 0; width: 100%; }
  /* No left/right flip here. Automation is the LAST segment of a full-width
     pill, so its left edge is most of the way across the screen: anchoring
     the panel there sent it off the right of the viewport. Anchored on the
     right - which is the page's own right edge - it opens leftward and stays
     on screen. */
}

/* --- "there is more shelf" --- */
/* The fade says something continues; the button gives you a way to get
   there. Both only appear when the rail actually has somewhere to scroll,
   which JS decides - a permanently visible arrow that does nothing is worse
   than no arrow. */
.tk-shelf::before,
.tk-shelf::after {
  content: "";
  position: absolute;
  z-index: 2;
  top: 0.76rem;
  bottom: 0.86rem;
  width: 3rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms ease;
}
.tk-shelf::before {
  left: 0.81rem;
  border-radius: 0.6rem 0 0 0.6rem;
  background: linear-gradient(90deg, var(--surface-tile), transparent);
}
.tk-shelf::after {
  right: 0.81rem;
  border-radius: 0 0.6rem 0.6rem 0;
  background: linear-gradient(270deg, var(--surface-tile), transparent);
}
.tk-shelf.has-more-left::before,
.tk-shelf.has-more-right::after { opacity: 1; }

/* Tall rather than a small disc: it runs the height of the vial it sits
   beside, which makes it a target you can hit with a thumb without looking,
   and reads as an edge of the shelf rather than as a dot floating on it. */
.tk-rail-nudge {
  position: absolute;
  z-index: 3;
  /* Centred on the card, not on the vial stage. .tk-shelf is the containing
     block, so 50% is simply half the card - no offset to keep in step with
     the shelf's padding, and the height follows the card if a longer label
     makes the rail taller. */
  top: 50%;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 62%;
  padding: 0 0 0.15rem;
  border: 1px solid var(--hairline);
  border-radius: 0.6rem;
  background: var(--surface-float);
  color: var(--ink);
  font: inherit;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transform: translateY(-50%);
}
.tk-rail-nudge:hover { border-color: var(--edge-glow); }
.tk-rail-nudge.is-left { left: 0.35rem; }
.tk-rail-nudge.is-right { right: 0.35rem; }

/* ==========================================================================
   Add a peptide - the stepped form
   --------------------------------------------------------------------------
   One form, one POST; the steps are presentation. See manual_vial.html for
   why, and mypeps-manual.js for the rule that a field outside the current
   answers is DISABLED rather than merely hidden.
   ========================================================================== */

/* `[hidden] { display: none }` is a USER-AGENT rule, so ANY author rule that
   sets display beats it - and .wiz-field is grid, .wiz-alt is inline-flex,
   .my-peps-action is inline-flex, .btn is block. Setting hidden on those did
   nothing at all: every step, both "not in the catalog" links and the submit
   button rendered at once, which read as a page whose links were dead.

   Two classes, so it outranks the single-class display rules above without
   !important. Anything here given a display by a two-class selector needs
   this one raised to match - hiding is not optional behaviour. */
.wiz [hidden],
.tk-rail-nudge[hidden] { display: none; }

.wiz h1 { margin-bottom: 0.9rem; }
.wiz h2 { margin: 0 0 0.3rem; font-size: 1.15rem; }
.wiz-hint { margin: 0 0 1rem; color: var(--muted); font-size: 0.87rem; line-height: 1.5; }

.wiz-progress {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.5rem;
  margin: 0 0 1.4rem;
  padding: 0;
  list-style: none;
  counter-reset: wiz;
}
.wiz-progress li {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
  color: var(--muted);
  font-size: 0.76rem;
  counter-increment: wiz;
}
.wiz-progress li::before {
  content: counter(wiz);
  display: grid;
  place-items: center;
  width: 1.35rem;
  height: 1.35rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  font-size: 0.7rem;
}
.wiz-progress li + li::after {
  content: "";
  order: -1;
  width: 0.9rem;
  height: 1px;
  background: var(--hairline);
}
.wiz-progress li.is-current { color: var(--ink); }
.wiz-progress li.is-current::before { border-color: var(--accent); color: var(--accent); }
.wiz-progress li.is-done::before {
  content: "✓";
  border-color: var(--ok-ink);
  color: var(--ok-ink);
}

.wiz-choices { display: grid; gap: 0.6rem; margin-bottom: 1rem; }
.wiz-choice {
  display: grid;
  gap: 0.2rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--hairline);
  border-radius: 0.7rem;
  background: var(--surface-tile);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.wiz-choice:hover { border-color: var(--edge-glow); }
.wiz-choice[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-wash);
}
.wiz-choice b { font-size: 0.95rem; }
.wiz-choice span { color: var(--muted); font-size: 0.82rem; line-height: 1.45; }

.wiz-field { display: grid; gap: 0.3rem; margin-bottom: 0.9rem; }
.wiz-field > label { font-size: 0.87rem; font-weight: 600; }
.wiz-field > label i { font-weight: 400; font-style: normal; color: var(--muted); }
.wiz-field--narrow { max-width: 10rem; }
.wiz-field input:not([type="checkbox"]):not([type="radio"]),
.wiz-field select {
  width: 100%;
  min-height: 44px;
  padding: 0 0.7rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background-color: var(--surface);
  color: var(--ink);
  font: inherit;
}
.wiz-field .helptext { color: var(--muted); font-size: 0.78rem; line-height: 1.45; }
.wiz-row { display: grid; gap: 0 0.75rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* The other way to answer this step, and a real button rather than a line of
   underlined text: it changes what the form asks next, which is a bigger
   thing than a link's worth of weight suggested, and it needs a tap target. */
.wiz-alt {
  display: inline-flex;
  gap: 0.45rem;
  align-items: center;
  min-height: 44px;
  margin-top: 0.2rem;
  padding: 0.55rem 1rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface-tile);
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}
.wiz-alt:hover { border-color: var(--edge-glow); background: var(--surface); }
.wiz-alt:focus-visible { outline: 2px solid var(--link-ink); outline-offset: 2px; }

.wiz-summary {
  margin: 1rem 0 0;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  background: var(--surface-tile);
  color: var(--muted);
  font-size: 0.85rem;
}

.wiz-blocker {
  margin: 0 0 0.8rem;
  color: var(--danger-ink);
  font-size: 0.85rem;
}

.wiz-nav {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 1.4rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}
.wiz-nav [data-nav="next"] {
  margin-left: auto;
  transition: border-color 200ms ease, color 200ms ease, box-shadow 200ms ease;
}
.wiz-nav .btn { width: auto; margin-left: auto; }

/* Answered. The resting Next is quiet on purpose so that this reads as a
   change rather than as the button's normal state - it is the page's only
   reply to "was that enough?". --accent, not the magenta the button rests
   on, so the two are told apart at a glance.

   The glow breathes for as long as the step stays answered, rather than
   pulsing once and settling: it is the thing on the page asking to be
   pressed, and a single pulse is missed by anyone who was reading the field
   they had just filled in. Slow and shallow on purpose - it sits under the
   text somebody is still reading. */
.wiz-nav [data-nav="next"].is-ready {
  border-color: var(--accent);
  color: var(--ink);
  animation: wiz-ready 2.4s ease-in-out infinite;
}

/* Two shadows on one element, doing two jobs. The first is a ring that grows
   out of the border and fades - the expanding edge - and the second is the
   glow, breathing under it. Both on a single loop so the ring's departure and
   the glow's peak belong to the same beat instead of drifting apart. */
@keyframes wiz-ready {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent),
      0 0 7px color-mix(in srgb, var(--accent) 22%, transparent);
  }
  70% {
    box-shadow: 0 0 0 10px color-mix(in srgb, var(--accent) 0%, transparent),
      0 0 18px color-mix(in srgb, var(--accent) 62%, transparent);
  }
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent),
      0 0 7px color-mix(in srgb, var(--accent) 22%, transparent);
  }
}

/* The lit border still says "answered" without the movement, so the meaning
   survives; only the breathing stops. */
@media (prefers-reduced-motion: reduce) {
  .wiz-nav [data-nav="next"] { transition: none; }
  .wiz-nav [data-nav="next"].is-ready {
    animation: none;
    box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 40%, transparent);
  }
}

@media (max-width: 520px) {
  .wiz-row { grid-template-columns: minmax(0, 1fr); }
  .wiz-progress li span { display: none; }
}

/* ==========================================================================
   Buttons that look like buttons
   --------------------------------------------------------------------------
   One rule, and My Peps now obeys it everywhere:

     solid --accent fill      = this acts
     raised --surface-tile    = this acts, quietly
     teal                     = data and status, never the only thing telling
                                a control apart from a decorated box

   A 1px coloured border on a transparent ground is not a button. It was the
   costume Next, the water tiles and the "use this setup" recommendation all
   wore, and none of the three read as pressable.
   ========================================================================== */
.my-peps-action--solid {
  background: var(--accent);
  color: var(--accent-ink);
  /* Transparent rather than absent, so it keeps metrics with the quiet
     buttons it sits beside and the row does not jump by a pixel. */
  border: 1px solid transparent;
  font-weight: 700;
}
.my-peps-action--solid:hover { filter: brightness(0.96); box-shadow: 0 0 14px var(--edge-glow); }
.my-peps-action--solid:active { transform: translateY(1px); box-shadow: none; }
.my-peps-action--solid:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.my-peps-action--solid:disabled {
  background: var(--surface-tile);
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--hairline);
  cursor: not-allowed;
}

.my-peps-action--quiet { background: var(--surface-tile); border-color: var(--hairline); color: var(--ink); }
.my-peps-action--quiet:hover { background: var(--surface); border-color: var(--edge-glow); }
.my-peps-action--quiet:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 32px is a fine density for a row of card actions on the tracker and too
   small for a control a form asks you to press on a phone. */
.wiz .my-peps-action,
.my-peps-recon-card .my-peps-action { min-height: 44px; padding: 0.55rem 1.1rem; font-size: 0.85rem; }

/* ==========================================================================
   Dose & Vial Setup - three numbered sections beside a docked instrument
   --------------------------------------------------------------------------
   This page was briefly a wizard and is deliberately not one any more; see
   the comment at the top of reconstitution.html. The .wiz-* block above
   belongs to manual_vial.html, which keeps it.
   ========================================================================== */
.my-peps-recon-card h2 { margin: 0; font-size: 1rem; }
.my-peps-recon-goal { margin: 0.5rem 0 0; max-width: 62ch; color: var(--ink); font-size: 0.86rem; line-height: 1.55; }
.my-peps-recon-vialline { margin: 0.3rem 0 0; color: var(--muted); font-size: 0.85rem; }

/* A hairline between sections and a numbered chip on each: enough to read as
   "do this, then this" without a Next button pretending they are gates. */
.my-peps-recon-section + .my-peps-recon-section { margin-top: 1.2rem; padding-top: 1.2rem; border-top: 1px solid var(--rule); }
.my-peps-recon-section h2 { display: flex; align-items: center; gap: 0.5rem; }
.my-peps-recon-num {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 1.6rem;
  height: 1.6rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface-tile);
  color: var(--ink);
  font-size: 0.8rem;
  /* A label, not a control, and not teal - teal on this page is data. */
  font-weight: 700;
}
.my-peps-recon-save { display: flex; justify-content: flex-end; margin-top: 1.3rem; padding-top: 1rem; border-top: 1px solid var(--rule); }
.my-peps-recon-save .btn { width: auto; }

/* The premixed vial's solution volume is a stated fact, not an invitation:
   the field is readonly because clean() still reads it off the POST. */
.my-peps-stated-field input[readonly] { background-color: var(--surface-tile); color: var(--muted); cursor: default; }
.my-peps-stated-field input[readonly]:focus { outline: 2px solid var(--hairline); border-color: var(--hairline); }

.my-peps-premixed-fact { display: flex; align-items: center; gap: 0.45rem; margin: 0 0 0.6rem; padding: 0.55rem 0.7rem; border: 1px solid var(--hairline); border-radius: 0.55rem; background: var(--surface-tile); color: var(--ink); font-size: 0.75rem; }
.my-peps-premixed-fact i { color: var(--series-teal); }

/* The forecast, collapsed. Open by default a red Week-13 advisory was the
   loudest object on a page whose actual question was a date field. The chip
   in the summary is how the urgency survives being folded away. */
details.my-peps-titration-forecast > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  min-height: 44px;
  padding: 0.2rem 0;
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 650;
  cursor: pointer;
  list-style: none;
}
details.my-peps-titration-forecast > summary::-webkit-details-marker { display: none; }
details.my-peps-titration-forecast > summary::after {
  content: "▸";
  order: 1;
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 0.75rem;
  transition: transform 150ms ease;
}
details.my-peps-titration-forecast[open] > summary::after { transform: rotate(90deg); }
details.my-peps-titration-forecast > summary > span { flex: 0 0 auto; }
details.my-peps-titration-forecast > summary > .my-peps-water-chip { margin: 0 auto 0 0; }
details.my-peps-titration-forecast > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.my-peps-forecast-body { padding-top: 0.7rem; }
.my-peps-forecast-note { margin: 0 0 0.65rem; color: var(--muted); font-size: 0.72rem; line-height: 1.45; }
/* Amber: a problem that is real but weeks out, between "fits" and "won't". */
.my-peps-water-chip.is-soon { background: rgba(255, 180, 84, 0.1); color: var(--series-amber); box-shadow: inset 0 0 0 1px rgba(255, 180, 84, 0.55); }

/* The instrument, one line of it, kept on screen while the fields that move
   it are being typed into. Not a second preview - pressing it scrolls to the
   real one. Phone only, and JS only: with no script it never unhides. */
.my-peps-recon-dock {
  display: none;
  position: sticky;
  bottom: 0;
  z-index: 4;
  width: 100%;
  min-height: 56px;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1rem;
  padding: 0.6rem 0.9rem;
  border: 0;
  border-top: 1px solid var(--hairline);
  box-shadow: inset 0 3px 0 0 var(--hairline);
  background: var(--surface-float);
  backdrop-filter: blur(9px);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.my-peps-recon-dock > span { font-size: 0.95rem; font-weight: 700; }
.my-peps-recon-dock > em { color: var(--muted); font-size: 0.76rem; font-style: normal; }
.my-peps-recon-dock[data-state="ok"] { box-shadow: inset 0 3px 0 0 var(--series-teal); }
.my-peps-recon-dock[data-state="ok"] > em { color: var(--series-teal); }
.my-peps-recon-dock[data-state="warn"] { box-shadow: inset 0 3px 0 0 var(--series-amber); }
.my-peps-recon-dock[data-state="warn"] > em { color: var(--series-amber); }
.my-peps-recon-dock[data-state="error"] { box-shadow: inset 0 3px 0 0 var(--glow-red); }
.my-peps-recon-dock[data-state="error"] > em { color: var(--glow-red); }

@media (max-width: 900px) {
  /* `.wiz [hidden]` is two classes deep and this is one, so the dock stays
     hidden until the script unhides it even here. */
  .my-peps-recon-dock { display: flex; }
}

/* Below 760px the mobile nav owns the bottom edge, so bottom: 0 parks this
   UNDERNEATH it - the same collision the colophon and chat docks are already
   offset for, and --mobile-nav-h (measured by dock-tuck.js) is the same
   answer. Between 760 and 900 there is no mobile nav and 0 is correct.

   The right padding is for the chat launcher, which is fixed at this height
   and floats over anything anchored here. Tucked away it shows a 2.6rem
   chevron; this keeps the status word clear of it. A launcher the shopper has
   deliberately opened still covers more, which is true of every bottom-
   anchored element on the site and is their own choice. */
@media (max-width: 760px) {
  .my-peps-recon-dock { bottom: var(--mobile-nav-h, 4rem); padding-right: 3.4rem; }
}
@media (max-width: 520px) {
  .my-peps-recon-card .my-peps-recon-fields { grid-template-columns: minmax(0, 1fr); }
  .my-peps-water-options { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   Time and date controls
   --------------------------------------------------------------------------
   Left alone these render OS-default: a white field with a black clock, on
   every dark page that has one - the dose-time question in the add form, the
   reconstitution setup, quiet hours, and the "when did you take it" box on
   every past-due dose.

   The control stays NATIVE. Its picker is the one the phone already knows how
   to show, and a drawn replacement trades a good picker on the device most of
   these are answered on for one that merely matches.

   The FIELD is already dressed by whichever form owns it - .my-peps-form-grid
   and .wiz-field both do it, and .my-peps-mark-taken has its own compact
   version. What was missing is below, and it was missing everywhere.
   ========================================================================== */

/* The one declaration that reaches inside the control: it darkens the picker
   panel and the spinners, which styling the field cannot touch. Kept in its
   own rule because it applies to the widget rather than the box, and because
   a stylesheet filter that dislikes it must not take the field's skin with
   it - one did, which is how this was found. */
input[type="time"],
input[type="date"],
input[type="datetime-local"] { color-scheme: dark; }

/* The wizard dresses its own fields but never set the above, so its dose-time
   question opened a light picker panel out of a dark page. */
.wiz-field input[type="time"],
.wiz-field input[type="date"],
.wiz-field input[type="datetime-local"] { color-scheme: dark; }

input[type="time"]:hover,
input[type="date"]:hover,
input[type="datetime-local"]:hover { border-color: var(--rule); }

input[type="time"]:focus-visible,
input[type="date"]:focus-visible,
input[type="datetime-local"]:focus-visible {
  border-color: var(--edge-glow);
  outline: 2px solid var(--link-ink);
  outline-offset: 2px;
}

/* The clock and calendar glyphs are painted dark by the UA and vanish on this
   background. Inverted, and quiet until pointed at. */
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  filter: invert(1);
  opacity: 0.55;
  transition: opacity 150ms ease;
}
input[type="time"]:hover::-webkit-calendar-picker-indicator,
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* The editable segments, so an empty hh:mm reads as a prompt rather than as
   text somebody already typed. */
input[type="time"]::-webkit-datetime-edit-fields-wrapper,
input[type="datetime-local"]::-webkit-datetime-edit-fields-wrapper { padding: 0; }
input[type="time"]::-webkit-datetime-edit-text,
input[type="datetime-local"]::-webkit-datetime-edit-text { color: var(--muted); }
