/* =========================================================================
   The landing page.

   Page styling for index.html, and for nothing else — no other page links this
   file. It loads AFTER app.css, so where the two name the same class the
   landing page's version wins on this page and nowhere else. That layering is
   what makes it safe to keep the marketing page's own `.card`, `.eyebrow` and
   `.meta` without touching the catalogue tiles that share those names.

   WHAT CAME OUT ON THE WAY IN, and why the file starts at what used to be line
   355 of apps/site/index.html: everything above it was a second copy of things
   the storefront already owns — the whole palette, the type and space ramps,
   `*`, `html`, `body`, `.wrap`, `.bleed`, `.measure`, `.skip`, `.pad`,
   `:focus-visible`, the reduced-motion block, and the entire bespoke masthead
   and footer. The shell provides all of it now. A merge that carried those
   across would have been two design systems in one document, with the loser
   decided by link order.

   The one rule reinstated from that dropped range is the navigation, below, and
   it comes back as page content rather than as chrome.
   ========================================================================= */

/* -------------------------------------------------------------------------
   The section switcher.

   This is the marketing page's old masthead nav, moved INSIDE <main>. It is not
   chrome any more: the storefront's masthead is the app's navigation, and this
   is the landing page's own way of moving between its sections — the same
   relationship the catalogue's filter bar has to the shop.

   It keeps the behaviour it was given for a phone: five destinations scroll
   sideways rather than wrapping the row taller. The `order`/`flex` pair from
   the original is gone with the flex row it was hanging on.

   A ROW THAT SCROLLS HAS TO CLIP AT THE SCREEN EDGE. The negative margin and
   the matching padding are what put the last item half off the glass instead of
   stopping short with a tidy margin after it: the first reads as "there is
   more", the second reads as a bug.
   ------------------------------------------------------------------------- */
.sectionnav{
  display:flex; flex-wrap:nowrap; min-width:0; gap:var(--space-1);
  margin:0 0 var(--space-5);
  overflow-x:auto; overscroll-behavior-x:contain; scrollbar-width:none;
  scroll-snap-type:x proximity;
  margin-inline:calc(-1 * max(var(--gutter), env(safe-area-inset-left,0px)))
                calc(-1 * max(var(--gutter), env(safe-area-inset-right,0px)));
  padding:0 max(var(--gutter), env(safe-area-inset-right,0px)) var(--space-1)
            max(var(--gutter), env(safe-area-inset-left,0px));
  scroll-padding-inline:max(var(--gutter), env(safe-area-inset-left,0px));}
.sectionnav::-webkit-scrollbar{display:none;}
.sectionnav a{flex:0 0 auto; scroll-snap-align:start;
  display:inline-flex; align-items:center; justify-content:center;
  min-height:var(--tap); padding:0 var(--space-3); border-radius:var(--radius-md);
  font-family:var(--sans); font-size:var(--fs-small); line-height:1.2;
  color:var(--ink-2); text-decoration:none; white-space:nowrap;
  touch-action:manipulation;}
.sectionnav a[aria-current="page"]{background:var(--silver-soft); color:var(--silver); font-weight:640;}
.sectionnav a:active{background:var(--sunk); color:var(--ink);}

.view{display:none;}
.view.on{display:block;}
.furred{position:relative; overflow:hidden;}
.furred>canvas.bg{position:absolute; inset:0; width:100%; height:100%;
  opacity:var(--texture-web-content); pointer-events:none;}
.furred>.wrap{position:relative;}

/* view-shop is the site's front door once live (see the boot code at the
   bottom of landing.js) -- the first thing anyone sees, same role as
   .coat-hero's own canvas, which the rule above never touches and which
   nothing clamps. --texture-web-content (5%) is right for an ORDINARY
   section like guides or dogs sharing this same .furred wrapper; on the
   surface standing in for the hero it read as a flat, textureless ground
   instead of the brand's coat, because 5% is imperceptible against
   --undercoat at this size. Higher specificity (#id + class beats two
   classes) rather than editing the shared rule, so guides/dogs/join keep
   the ordinary ceiling and only the one surface acting as a hero is
   exempted from it -- same shape as FR-R3's banned surfaces, opposite
   direction. */
#view-shop>canvas.bg{opacity:1;}

/* full-bleed live coat */
/* The hero claims the horizontal gesture and nothing else.

   `touch-action:none` used to say "this element handles everything", which on a
   phone meant a finger landing anywhere on the coat — most of the first screen —
   could not scroll the page. `pan-y` gives vertical panning back to the browser
   while horizontal movement still reaches the pointer handlers, which is exactly
   the split the coat needs. The second declaration adds pinch-zoom back for
   anyone who needs to magnify; browsers too old to parse the keyword keep the
   plain `pan-y` above it, and either way the page scrolls. */
.coat-hero{position:relative; touch-action:pan-y; touch-action:pan-y pinch-zoom;
  /* the primary gesture here is a drag, so text selection has to be off or
     dragging across the headline highlights it instead of parting the coat */
  user-select:none; -webkit-user-select:none; -webkit-touch-callout:none;
  /* 78vh of a phone is 658px, and `vh` on iOS is measured against the SHORTER
     URL-bar-collapsed viewport, so the coat was taller than the screen on
     arrival and the headline started below the fold. `svh` is the small
     viewport — what you can actually see before scrolling — with a `vh`
     fallback on the line above for browsers that do not know the unit. The
     coat still owns the first screen; it just no longer owns more than one. */
  min-height:min(74vh,600px); min-height:min(74svh,600px);
  display:flex; align-items:flex-end;
  background:var(--sunk); overflow:hidden; border-bottom:1px solid var(--rule); cursor:grab;}
.coat-hero:active{cursor:grabbing;}
.coat-hero>canvas{position:absolute; inset:0; width:100%; height:100%;}
/* Only the very bottom fades, so the coat stays visible. Legibility is handled
   by the eyebrow's own ground and the headline scrim below, not by veiling the
   whole hero. */
.coat-hero .fade{position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg,transparent 0%,transparent 58%,var(--undercoat) 100%);}
/* THE COPY IS NOT A LID ON THE COAT.

   index.html's hero binds its pointer handlers to `.coat-hero` itself, so a
   drag starting on the headline bubbles up and parts the coat anyway.
   standard.html wears the same markup but is painted by js/fur.js, which binds
   to the CANVAS — and there `.hero-in` swallowed every gesture in the bottom
   half of a 660px hero, including the one over "drag to part the coat", which
   is the single worst place for the invitation not to work.

   Click-through, then, with real controls opting back in. This is the same
   arrangement `.hero-banner-body` uses on the catalogue (app.css) and the same
   one `.signin-main > .wrap` uses on the way-in: a decorative layer above a
   draggable one must not take the gesture. */
.hero-in{position:relative; width:100%; padding:var(--space-8) 0 var(--space-6);
  pointer-events:none;}
.hero-in a, .hero-in button{pointer-events:auto;}
/* The eyebrow sits over live fur, where --ink-3 disappears. It gets its own
   ground so it stays legible at any coat density, in either theme. */
.hero-in .eyebrow{display:inline-block; color:var(--ink); background:var(--undercoat);
  border:1px solid var(--rule); border-radius:var(--radius-pill);
  padding:var(--space-1) var(--space-3); font-weight:600;
  box-shadow:var(--shadow);}
.hero-in h1, .hero-in .lede{text-shadow:0 1px 0 var(--undercoat), 0 0 18px var(--undercoat);}
.stroke-hint{font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-2); margin:var(--space-5) 0 0;
  display:flex; align-items:center; gap:var(--space-2);}
.stroke-hint::before{content:""; width:26px; height:1px; background:var(--ink-3); flex:0 0 auto;}
@media (min-width:700px){
  .coat-hero{min-height:min(78vh,660px); min-height:min(78svh,660px);}
  .hero-in{padding:clamp(56px,8vw,96px) 0 clamp(34px,5vw,54px);}
}

/* The shed band between sections. It was a flat 104px at EVERY width — a
   decorative strip costing an eighth of a phone screen, sitting in the middle
   of the largest gap on the page (bottom `.pad` + band + top `.pad` came to
   190px between one call to action and the next heading). It is proportional
   now: enough to read as a band, not enough to be a scroll. */
.band{height:var(--space-9); position:relative; overflow:hidden;
  border-top:1px solid var(--rule-2); border-bottom:1px solid var(--rule-2);
  background:var(--sunk);}
.band canvas{position:absolute; inset:0; width:100%; height:100%;}
@media (min-width:700px){ .band{height:104px;} }

/* ---- type ------------------------------------------------------------ */
.eyebrow{font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-3); margin:0 0 var(--space-3);}
/* The floor is 29px, not 34. A display size chosen against a 1080px column
   spends a phone's whole width on four or five words; at 29px the same
   headline lands in two lines instead of three and the lede below it gets to
   be part of the first screen. The desktop end is untouched. */
h1{font-family:var(--sans); font-size:clamp(29px,7.4vw,70px); line-height:1.06;
  letter-spacing:-.042em; font-weight:700; margin:0 0 var(--space-4); text-wrap:balance;}
h2{font-family:var(--sans); font-size:clamp(21px,5vw,34px); line-height:1.15;
  letter-spacing:-.03em; font-weight:660; margin:0 0 var(--space-3); text-wrap:balance;}
/* `line-height` is the point of this rule. h3 declared none, so every heading
   in it inherited body's 1.62 — a SERIF READING leading applied to sans
   display type. At 23px in the guide feature and 30px on a dog's name that is
   a two-line heading with a hole in the middle, and it only shows up at phone
   widths because that is where these headings wrap. 1.375 is the token
   scale's own `heading-3` leading. */
h3{font-family:var(--sans); font-size:17px; font-weight:660; letter-spacing:-.012em;
  line-height:1.375; margin:0 0 var(--space-2);}
@media (min-width:900px){ h3{font-size:16px;} }
p{margin:0 0 var(--space-4);}
.lede{font-size:var(--fs-lede); line-height:1.5; color:var(--ink-2);}
a{color:var(--silver); text-underline-offset:2px;}
.sub{font-family:var(--sans); font-size:var(--fs-small); color:var(--ink-3); line-height:1.5;}
/* An unbreakable string — an API path, a filename in the IA notes — is all it
   takes to give the whole page a horizontal scrollbar. */
code{overflow-wrap:anywhere;}
/* ---- LABEL READER: the judgement, with its working attached ---------- */
/* CONFRONT direction. Both labels are on screen at once — same paper, same
   face, same size — beneath ONE scoreboard whose every row carries BOTH
   verdicts. Nothing sits behind a toggle, so the contrast that does the
   persuading is never one click away, and a one-sided read of the standard is
   structurally impossible. The two panels differ only by the damage done to
   them; that difference IS the argument.
   READING ORDER is deliberate: plates, then six marks against six marks, then
   the ingredient panels. The verdict and its evidence share the first
   viewport; the raw labels fall into the evidence position below.
   THEME. The shell is var(--sunk), which is the recessed paper in BOTH
   directions — under var(--undercoat) in light and under it in dark too. The
   ledger and the two panels are var(--vellum), the lightest paper token in
   both themes. So "two documents laid out on an examination table" survives
   the flip with no conditional rule anywhere. The only saturated surfaces are
   the two verdict plates and the struck slabs; --alert and --moss are retuned
   per theme by the token block above, so both stay legible either way.
   COLOUR IS NEVER ALONE. A failure is a filled slab AND a line through the
   words AND a superscript rule number AND a drawn cross AND, for a screen
   reader, spoken text. A clear is a hairline underline AND a drawn tick.
   Print the page in greyscale and every verdict still reads.

   THE TRAY IS NOT DRAWN ON A PHONE, and that is the single largest width win
   on this page. The examination table is a third surface wrapped around two
   surfaces that already carry their own borders and their own paper — on a
   1080px column that reads as a tray, and on a 390px one it is just 18px of
   padding and a border on each side, stacked on top of the page gutter and
   then again on top of the panel's own padding. Measured, the ingredient text
   sat 57px in from the glass on both sides: 29% of the screen spent on three
   nested boxes saying the same thing. Below 700px the tray keeps its content
   and its reading order and gives up its chrome, so the panels inset once
   instead of three times. */
.confront{padding:0; background:none; border:0; box-shadow:none;}
.cf-head{max-width:64ch; margin:0 0 var(--space-2);}
.cf-eyebrow{font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-2); margin:0 0 var(--space-3);}
.cf-head h2{margin:0;}
/* One sentence. The old component spent its opening on exposition; the
   scoreboard below now does that work without being read. */
.cf-lede{max-width:64ch; margin:0 0 var(--gap-block);}
.cf-lede p{font-family:var(--sans); font-size:var(--fs-small); line-height:1.55;
  color:var(--ink-2); margin:0;}
/* `align-items:flex-start`, not `center`. The rule is a 1px pseudo-element and
   this label wraps to two lines at phone widths now that it is set at a
   readable size — centred, the rule floated in the gutter between the two
   lines. It hangs off the FIRST line, which is where a lead-in rule belongs. */
.cf-hint{display:flex; align-items:flex-start; gap:var(--space-2); font-family:var(--mono);
  font-size:var(--fs-label); line-height:1.5; letter-spacing:.11em; text-transform:uppercase;
  color:var(--ink-2); margin:var(--space-3) 0 0;}
.cf-hint::before{content:""; width:24px; height:1px; background:var(--ink-2); flex:0 0 auto;
  margin-top:.72em;}
@media (min-width:700px){
  .confront{background:var(--sunk); border:1px solid var(--rule);
    border-radius:var(--radius-2xl); padding:var(--pad-card); box-shadow:var(--shadow);}
}

/* ---- the scoreboard -------------------------------------------------- */
/* Six exclusions, one row each, both answers on the same line. The two
   verdict plates are the column heads, so six crosses stack directly under
   the word REJECTED and six ticks under STOCKED. That register is the
   two-second read, and because each row also names the test and cites the
   evidence, the persuasion and the substance are the same object.

   THE PHONE SHAPE IS THE BASE now, not a `max-width` subtraction: the test
   name takes its own line and the two verdict cells sit side by side beneath
   it, so the comparison survives the stack instead of being replaced by a
   summary. The three-column shape returns at 700px, which is where the
   citation column stops having to truncate. */
.cf-ledger{background:var(--vellum); border:1px solid var(--rule);
  border-radius:var(--radius-xl); overflow:hidden; margin:0 0 var(--gap-card);}
.cf-lhead,.cf-lrow{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);}
.cf-lcap{display:none;}

/* the verdict plates — the only place a word is set this large */
.cf-plate{display:flex; align-items:center; gap:var(--space-2); min-width:0;
  padding:var(--space-3); color:var(--vellum);}
.cf-plate--fail{background:var(--alert);}
.cf-plate--pass{background:var(--moss);}
.cf-plate-glyph{font-size:22px; flex:0 0 auto;}
.cf-plate-word{font-family:var(--sans); font-size:19px; font-weight:700;
  letter-spacing:-.038em; line-height:1; margin:0 0 5px; color:var(--vellum);}
/* The plate's tally — "Fails 6 of 6" / "Clears 6 of 6" — is the line that
   settles the comparison this whole section builds to, and it was the smallest
   text on the site at 11px. --fs-label (12px) IS this site's label size; the
   `- 1px` stepped one below it, to the 11px floor the scale above says never to
   go under. The other five uses of that calc are true labels (.tag, .mins,
   .care-note .k) and keep it. The plate is 62px tall with the verdict word at
   19px above, so the extra pixel costs no layout. */
.cf-plate-sub{font-family:var(--mono); font-size:var(--fs-label);
  letter-spacing:.06em; text-transform:uppercase; line-height:1.25; margin:0;
  color:var(--vellum);}
.cf-long{display:none;}
.cf-short{display:inline;}

.cf-rows{list-style:none; margin:0; padding:0;}
.cf-rows li{border-top:1px solid var(--rule);}
.cf-lrow{width:100%; margin:0; padding:0; border:0; background:none; font:inherit; color:inherit;
  text-align:left; cursor:pointer; align-items:stretch; transition:background 120ms linear;
  touch-action:manipulation;}
.cf-lrow:active{background:color-mix(in srgb, var(--ink) 8%, transparent);}
.cf-lrow:focus-visible{outline-offset:-3px;}
.cf-lname{grid-column:1 / -1; display:flex; align-items:center; gap:var(--space-2);
  min-width:0; padding:var(--space-3) var(--pad-card) var(--space-1);}
.cf-num{font-family:var(--mono); font-size:var(--fs-label); color:var(--ink-2); flex:0 0 auto;
  font-variant-numeric:tabular-nums;}
.cf-test{font-family:var(--sans); font-size:var(--fs-small); font-weight:640;
  letter-spacing:-.012em; color:var(--ink); flex:1; min-width:0;}
.cf-chev{font-family:var(--mono); font-size:var(--fs-control); color:var(--ink-2); flex:0 0 auto;}
/* The two verdict columns carry a faint wash so they read as columns from a
   distance. The wash is decoration; the drawn glyph is the meaning. */
.cf-cell{display:flex; align-items:center; gap:var(--space-2); min-width:0;
  padding:var(--space-2) var(--space-3) var(--space-3); border-left:0;}
.cf-cell--fail{background:color-mix(in srgb, var(--alert) 6%, transparent);}
.cf-cell--pass{background:color-mix(in srgb, var(--moss) 6%, transparent);
  border-left:1px solid var(--rule);}
.cf-mark{font-size:var(--fs-control); flex:0 0 auto;}
.cf-cell--fail .cf-mark{color:var(--alert);}
.cf-cell--pass .cf-mark{color:var(--moss);}
/* The citation is evidence, so it survives every width — at phone widths it
   wraps rather than being truncated or dropped, so nothing is removed on the
   commonest screen. */
.cf-cite{font-family:var(--mono); font-size:var(--fs-label); color:var(--ink-2); min-width:0;
  white-space:normal; line-height:1.35;}

/* Opening a row shows the case against AND the case for, together. You are
   never given the accusation without the defence. */
.cf-why{display:grid; grid-template-columns:minmax(0,1fr); gap:var(--gap-card);
  padding:var(--space-1) var(--pad-card) var(--space-4);}
.cf-why[hidden]{display:none;}
.cf-wb{background:color-mix(in srgb, var(--ink) 5%, transparent);
  border-radius:var(--radius-md); padding:var(--pad-tight); min-width:0;}
.cf-wh{display:flex; align-items:center; gap:var(--space-2); margin:0 0 var(--space-2);
  font-family:var(--mono); font-size:calc(var(--fs-label) - 1px); letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-2);}
.cf-wb--fail .cf-wh .gl{color:var(--alert);}
.cf-wb--pass .cf-wh .gl{color:var(--moss);}
.cf-wb p{font-family:var(--sans); font-size:var(--fs-small); line-height:1.6; color:var(--ink-2); margin:0;}
.cf-wb b{color:var(--ink); font-weight:640;}

@media (min-width:700px){
  .cf-ledger{margin-bottom:var(--gap-block);}
  .cf-lhead,.cf-lrow{grid-template-columns:minmax(0,1fr) minmax(8.6em,.92fr) minmax(8.6em,.92fr);}
  .cf-lcap{display:flex; align-items:flex-end; min-width:0;
    padding:var(--space-4) var(--pad-card);
    font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.15em;
    text-transform:uppercase; color:var(--ink-2);}
  .cf-plate{gap:var(--space-3); padding:clamp(13px,1.8vw,18px) clamp(12px,1.7vw,17px);}
  .cf-plate-glyph{font-size:clamp(24px,3.1vw,32px);}
  .cf-plate-word{font-size:clamp(21px,2.9vw,29px); margin-bottom:6px;}
  .cf-plate-sub{letter-spacing:.1em;}
  .cf-long{display:inline;}
  .cf-short{display:none;}
  .cf-lname{grid-column:auto; min-height:46px; padding:var(--space-3) var(--pad-card);}
  .cf-cell{padding:var(--space-3) var(--space-4); border-left:1px solid var(--rule);}
  .cf-cite{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
  .cf-why{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}
}
@media (hover:hover) and (pointer:fine){
  .cf-lrow:hover{background:color-mix(in srgb, var(--ink) 5%, transparent);}
}

/* ---- the two ingredient panels --------------------------------------- */
/* One column until 900px. Two ingredient lists side by side need about 380px
   each before the sentences start shredding into two-word lines, and shredded
   type is the one thing that would undo "same paper, same face, same size". */
.cf-grid{display:grid; grid-template-columns:minmax(0,1fr); gap:var(--gap-card);
  align-items:start;}
.cf-card{min-width:0; display:flex; flex-direction:column; overflow:hidden;
  border-radius:var(--radius-xl); padding:var(--pad-card);
  background:var(--vellum); color:var(--ink);}
/* Both panels get an edge that actually contrasts with the tray — --rule never
   clears 1.4:1 against any paper token, so a --rule border is no border. The
   asymmetry is carried by WEIGHT, not by visibility: the rejected panel is on
   tinted paper inside a 2px edge, the stocked panel on plain paper inside a
   1px one. Loud versus quiet, and both still findable. */
.cf-card--fail{background:color-mix(in srgb, var(--alert) 6%, var(--vellum));
  border:2px solid var(--alert);}
.cf-card--pass{border:1px solid var(--moss);}
.cf-cap{display:flex; align-items:center; gap:var(--space-2); font-family:var(--mono);
  font-size:var(--fs-label); letter-spacing:.15em; text-transform:uppercase;
  color:var(--ink-2); margin:0 0 var(--space-3);}
.cf-card--fail .cf-cap .gl{color:var(--alert);}
.cf-card--pass .cf-cap .gl{color:var(--moss);}
/* Identical type on both sides — same family, same size, same leading — so the
   eye reads the difference as damage rather than as styling.

   THE LEADING IS NOT THE TAP TARGET — that was a wrong turn, reverted here.
   Every marked term is a <button> sitting inline inside a sentence, which is
   precisely the case WCAG 2.5.8 exempts: a target "in a sentence, or whose
   size is otherwise constrained by the line-height of non-target text". So
   .cf-term never owed anyone 44px, and buying those pixels with leading cost
   far more than it bought. Because the terms are `display:inline` with
   box-decoration-break:clone, their painted slab height IS the line box plus
   padding — inflating the leading to 2.35 (39.95px at --fs-body) stretched
   every OTHER line of the paragraph too, and most of this paragraph is plain
   prose, not targets. The result at 390 was a double-spaced sentence, and at
   320 an interlocking pink staircase with "rice," and "with" wedged into the
   notches. 1.75 reads as a paragraph again and takes 81px off the card.
   Desktop keeps 2.05 at 700px, where the line is long enough to carry it. */
.cf-label{font-family:var(--sans); font-size:var(--fs-body); line-height:1.75;
  color:var(--ink); margin:0 0 var(--space-5);}
.cf-note{font-family:var(--serif); font-size:var(--fs-body); line-height:1.5; color:var(--ink);
  margin:auto 0 0; padding-top:var(--space-4); border-top:1px solid var(--rule);}
@media (min-width:700px){ .cf-label{font-size:16px; line-height:2.05;} }
@media (min-width:900px){
  .cf-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--gap-block);}
}

/* Vertical padding on an inline slab is paid twice: it overhangs the line box
   at BOTH ends, so adjacent lines' slabs overlap by 2× this value. 2px keeps
   the overlap at 4px — enough that the slabs read as one damaged block rather
   than as a jagged staircase of separate pills. */
.cf-term{font:inherit; display:inline; margin:0; padding:2px 6px; border:0; background:none;
  color:inherit; cursor:pointer; text-align:left; border-radius:var(--radius-sm);
  box-decoration-break:clone; -webkit-box-decoration-break:clone;
  touch-action:manipulation; transition:box-shadow 120ms linear;}
/* Seven solid slabs breaking the line rhythm on one side, five hairline
   underlines on the other. That density difference is visible across a room
   and it needs no hue at all. */
.cf-term--x{background:var(--alert); color:var(--vellum); font-weight:640;}
.cf-term--x .cf-strike{text-decoration:line-through; text-decoration-thickness:2px;}
.cf-sup{font-family:var(--mono); font-size:.6em; font-weight:700; vertical-align:.52em;
  margin-left:3px; letter-spacing:.02em;}
.cf-term--ok{color:var(--ink); font-weight:640; padding-inline:1px;}
.cf-term--ok .cf-ul{text-decoration:underline; text-decoration-color:var(--moss);
  text-decoration-thickness:2px; text-underline-offset:4px;}
/* The lit ring is --ink, deliberately neutral: "you are looking at this" must
   never be mistakable for a verdict colour. */
.cf-term.is-lit{box-shadow:0 0 0 2px var(--ink);}
@media (hover:hover) and (pointer:fine){
  .cf-term:hover .cf-strike,.cf-term:hover .cf-ul{text-decoration-style:double;}
}

/* ---- glyphs: drawn from boxes, never typed ---------------------------- */
/* Built from pseudo-elements so the SHAPE is guaranteed whatever the font
   stack resolves to, and so shape — not hue — separates pass from fail. */
.gl{position:relative; display:inline-block; width:1em; height:1em; vertical-align:-.13em;}
.gl--x::before,.gl--x::after{content:""; position:absolute; top:50%; left:50%;
  width:.88em; height:.155em; margin:-.0775em 0 0 -.44em;
  background:currentColor; border-radius:.08em;}
.gl--x::before{transform:rotate(45deg);}
.gl--x::after{transform:rotate(-45deg);}
.gl--v::before{content:""; position:absolute; top:50%; left:50%;
  width:.36em; height:.68em; margin:-.40em 0 0 -.18em;
  border:.155em solid currentColor; border-top:0; border-left:0;
  border-radius:.05em; transform:rotate(42deg);}
/* line-through and underline are not announced by any screen reader, so every
   marked term carries the verdict as real text as well. */
.cf-vh{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;}

/* ---- guides --------------------------------------------------------- */
.filters{display:flex; gap:var(--space-2); flex-wrap:wrap; margin:0 0 var(--gap-block);
  align-items:center;}
.flabel{font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-3);}
.chip{font-family:var(--sans); font-size:var(--fs-small); line-height:1.25;
  min-height:var(--tap); padding:var(--space-2) var(--space-4);
  border-radius:var(--radius-pill); border:1px solid var(--rule); background:none;
  color:var(--ink-2); display:inline-flex; align-items:center; justify-content:center;
  cursor:pointer; touch-action:manipulation; white-space:nowrap;}
.chip:active{border-color:var(--ink-3); color:var(--ink); background:var(--sunk);}
.chip[aria-pressed="true"]{background:var(--silver); border-color:var(--silver); color:var(--undercoat);}
.chip[aria-pressed="true"]:active{background:var(--silver); border-color:var(--silver); color:var(--undercoat);}
@media (hover:hover) and (pointer:fine){
  .chip:hover{border-color:var(--ink-3); color:var(--ink);}
  .chip[aria-pressed="true"]:hover{background:var(--silver); border-color:var(--silver); color:var(--undercoat);}
}

/* One column on a phone, always — the 300px media rail plus a text column does
   not fit until there is 700px to split. */
.feature{display:grid; grid-template-columns:minmax(0,1fr); gap:0;
  background:var(--vellum); border:1px solid var(--rule); border-radius:var(--radius-2xl);
  overflow:hidden; box-shadow:var(--shadow); margin:0 0 var(--gap-block);}
.feature canvas{width:100%; height:100%; min-height:150px; display:block; background:var(--sunk);}
.feature-body{padding:var(--pad-card);}
.feature-body h3{font-size:clamp(20px,4.6vw,23px); line-height:1.2; letter-spacing:-.024em;
  margin-bottom:var(--space-2);}
.feature-body p{font-family:var(--sans); font-size:var(--fs-small); line-height:1.55;
  color:var(--ink-2); margin:0 0 var(--space-4);}
.feature-body .eyebrow{margin-bottom:var(--space-2);}
@media (min-width:700px){
  .feature{grid-template-columns:minmax(0,300px) minmax(0,1fr);}
  .feature canvas{min-height:190px;}
  .feature-body{padding:clamp(20px,3vw,30px);}
}

/* `auto-fill, minmax(268px, …)` inside a 280px content column is a 268px track
   that fits, and inside a 244px one it is a track that does not — the grid does
   not shrink below its minimum, it overflows, and `.furred { overflow:hidden }`
   then CLIPS it rather than scrolling. One column on a phone; the auto grid
   comes back when there is room for two. */
/* SCOPED AWAY FROM .tile ON PURPOSE.

   `.card` is this page's own card — the guides list. It is ALSO half of the
   catalogue tile's class (`article.card.tile`, from productTile in js/shop.js),
   which the shelf on this page now renders so that its products are clickable.
   landing.css loads after app.css, so an unscoped `.card p` here — specificity
   (0,1,1) — outranks app.css's `.tile-brand` (0,1,0) and silently restyles the
   brand line of every product tile on the landing page and nowhere else.

   app.css:681 carries a five-line comment against writing `.card p` for exactly
   this reason; it broke the catalogue tile once already. The `:not(.tile)` is
   what keeps this file's cards this file's business.

   The other fourteen class names these two stylesheets share are listed in
   apps/store/README.md — none of the rest is reachable by a shell-rendered
   element on this page, but they are worth knowing about. */
.cards{display:grid; grid-template-columns:minmax(0,1fr); gap:var(--gap-card);}
@media (min-width:600px){ .cards{grid-template-columns:repeat(auto-fill,minmax(260px,1fr));} }
.card:not(.tile){background:var(--vellum); border:1px solid var(--rule); border-radius:var(--radius-2xl);
  overflow:hidden; box-shadow:var(--shadow); display:flex; flex-direction:column;}
.card:not(.tile) canvas{width:100%; height:96px; display:block; background:var(--sunk);}
.card-body{padding:var(--pad-card); display:flex; flex-direction:column;
  gap:var(--space-2); flex:1;}
.card:not(.tile) p{margin:0; font-family:var(--sans); font-size:var(--fs-small); line-height:1.5; color:var(--ink-2);}
/* The catalogue's maker line, from js/live.js. It used to be an inline
   `font-size:12px` written into the markup by the renderer, which is a size
   this stylesheet could not see and could not scale with the viewport. */
.card:not(.tile) .brandline{color:var(--ink-3); font-size:var(--fs-micro); margin-bottom:var(--space-1);}
.meta{margin-top:auto; padding-top:var(--space-3); border-top:1px solid var(--rule-2);
  display:flex; gap:var(--space-2); flex-wrap:wrap; align-items:center;}
/* A tag is a label, not a control: it takes the label size, not the tap floor. */
.tag{font-family:var(--mono); font-size:calc(var(--fs-label) - 1px); letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3); border:1px solid var(--rule);
  border-radius:var(--radius-pill); padding:3px var(--space-2); display:inline-block;
  white-space:normal; max-width:100%;}
.mins{font-family:var(--mono); font-size:calc(var(--fs-label) - 1px); color:var(--silver);
  letter-spacing:.06em; margin-left:auto; font-variant-numeric:tabular-nums;}
.empty{font-family:var(--sans); font-size:var(--fs-small); color:var(--ink-3); padding:var(--space-7) 0;}

/* A CARD THAT IS A LINK.

   The guide cards are anchors now — for the whole life of guides.html they were
   `<article>` elements with nowhere to tap, which was the largest defect on the
   page. Wrapping the card in an <a> costs three declarations and they are all
   corrections to defaults: `a { color: var(--silver) }` at app.css:302 would
   otherwise tint the whole card, and a bare <a href> is underlined.

   NO TRANSFORM ON HOVER. A lift would need a reduced-motion branch and would
   put a containing block on a card whose header is a canvas; the border and the
   title underline say "this goes somewhere" at no such cost. It is the same
   affordance `.tile-link` uses for the catalogue, which is the other card in
   this client that is a link. */
.card-link{color:inherit; text-decoration:none;}
.card-link h3{color:var(--ink);}
.card-link:active{border-color:var(--ink-3);}
@media (hover:hover) and (pointer:fine){
  .card-link:hover{border-color:var(--ink-3);}
  .card-link:hover h3{text-decoration:underline; text-underline-offset:2px;}
}

/* ---- one guide, read (guide.html) ----------------------------------- */
/* Back is navigation, not prose, so it takes the tap floor rather than the
   inline-link exemption — the same argument `.crumbs a` makes in app.css, and
   the negative left margin keeps the enlarged box optically flush with the
   heading below it instead of indented by its own padding. */
.guide-back{margin:0 0 var(--space-2);}
.guide-back a{display:inline-block; padding:var(--space-3) var(--space-2);
  margin-left:calc(var(--space-2) * -1); font-family:var(--sans);
  font-size:var(--fs-small); color:var(--ink-2); text-decoration:none;}
.guide-back a:active{color:var(--ink);}
@media (hover:hover) and (pointer:fine){
  .guide-back a:hover{color:var(--ink); text-decoration:underline; text-underline-offset:3px;}
}

/* The same artwork the card carried, at the top of what the card opened. Short
   on a phone and never more than 190px: it is a bookmark, not a hero, and the
   words under it are the reason anybody followed the link. */
.guide-hero{margin:0 0 var(--gap-block); border:1px solid var(--rule);
  border-radius:var(--radius-2xl); overflow:hidden; box-shadow:var(--shadow);}
.guide-hero canvas{display:block; width:100%; height:clamp(110px,24vw,190px);
  background:var(--sunk);}

/* Who wrote it and who checked it. Both are optional on the wire and the row
   simply carries fewer items when they are absent — see the note in js/guide.js
   for why an empty "Reviewed by" is worse than no byline at all. */
.guide-byline{display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2);
  margin:0 0 var(--gap-block); font-family:var(--mono);
  font-size:calc(var(--fs-label) + 1px); letter-spacing:.06em; color:var(--ink-3);}
.guide-byline > * + *::before{content:"·"; margin-right:var(--space-2); color:var(--rule);}
/* A clinician's name is the strongest claim on the page after the standard's
   own signature, so it is the one item in the row that is not grey. */
.guide-byline .vetted{color:var(--moss);}

.guide-body{margin-bottom:var(--gap-block);}

/* ---- the dogs ------------------------------------------------------- */
/* The photograph is the component. Earlier this was a small circular avatar
   floating in a fur field with a colour-swatch strip underneath — which read as
   a product listing with a colourway, exactly the wrong register for the two
   animals the company exists because of.

   `repeat(auto-fit, minmax(320px, 1fr))` was a 320px track inside a 280px
   column on a 320px phone: 40px of both dogs was cut off by the section's
   `overflow:hidden` — silently, with no scrollbar to hint at it. */
.pets{display:grid; grid-template-columns:minmax(0,1fr); gap:var(--gap-block);}
@media (min-width:900px){
  .pets{grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); gap:var(--space-6);}
}
.pet{background:var(--vellum); border:1px solid var(--rule); border-radius:var(--radius-2xl);
  overflow:hidden; box-shadow:var(--shadow); min-width:0;}
.pet-photo{position:relative; aspect-ratio:4/5; background:var(--sunk); overflow:hidden;}
.pet-photo img{width:100%; height:100%; object-fit:cover; display:block;}
/* Where each dog's face sits in its frame. This was `object-position` written
   from a FOCUS table in JavaScript, back when the photographs were data URIs
   injected at runtime. The files are static now, so the framing is too. */
.pet-photo img.pet-marshall{object-position:56% 17%;}
.pet-photo img.pet-toffee{object-position:47% 30%;}
/* THE FIVE LITERALS BELOW ARE ON PURPOSE, and each carries the pragma saying so.
   Everything here is drawn ON TOP OF A PHOTOGRAPH. A photograph has no colour
   scheme — it is the same pixels in light and dark — so a token would flip
   underneath text that has not, and the caption over Marshall would end up dark
   grey on a dark photograph in one of the two schemes. Scrim-and-white over an
   image is one of the few places in this system where the value has to be fixed
   to the medium rather than to the palette. */
.pet-photo .veil{position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg,transparent 45%,rgba(0,0,0,.72) 100%);} /* undercoat-allow-literal: scrim over a photograph, which has no scheme to follow */
.pet-name{position:absolute; left:var(--pad-card); right:var(--pad-card); bottom:var(--pad-card);}
.pet-name h3{font-family:var(--sans); font-size:clamp(26px,6.4vw,30px); line-height:1.13;
  letter-spacing:-.03em; font-weight:700; margin:0 0 var(--space-1);
  color:#fff; text-shadow:0 1px 12px rgba(0,0,0,.55);} /* undercoat-allow-literal: caption over a photograph — fixed to the medium, not the palette */
.pet-name p{margin:0; font-family:var(--serif); font-size:var(--fs-small); line-height:1.4;
  color:rgba(255,255,255,.9); text-shadow:0 1px 10px rgba(0,0,0,.6);} /* undercoat-allow-literal: caption over a photograph — fixed to the medium, not the palette */
.pet-body{padding:var(--pad-card);}
.pet-body p{margin:0 0 var(--space-3); font-size:var(--fs-body); line-height:1.62; color:var(--ink-2);}
.pet-body p:last-child{margin:0;}
/* the care note — the point of the section, not a footnote */
.care-note{margin-top:var(--space-4); padding-top:var(--space-4); border-top:1px solid var(--rule-2);}
.care-note .k{font-family:var(--mono); font-size:calc(var(--fs-label) - 1px); letter-spacing:.13em;
  text-transform:uppercase; color:var(--moss); display:block; margin-bottom:var(--space-1);}
.care-note p{font-family:var(--sans); font-size:var(--fs-small); line-height:1.55; color:var(--ink-2); margin:0;}

/* ---- standard: ZERO texture ----------------------------------------- */
.doc{background:var(--vellum); border:1px solid var(--rule); border-radius:var(--radius-2xl);
  padding:var(--pad-card); box-shadow:var(--shadow);}
@media (min-width:700px){ .doc{padding:var(--space-7);} }
@media (min-width:900px){ .doc{padding:clamp(32px,4vw,44px);} }
.excl{border-top:1px solid var(--rule);}
.excl:last-of-type{border-bottom:1px solid var(--rule);}
.excl summary{list-style:none; cursor:pointer; padding:var(--space-4) 0;
  min-height:var(--tap); display:flex; gap:var(--space-3); align-items:baseline;
  font-family:var(--sans); touch-action:manipulation;}
.excl summary::-webkit-details-marker{display:none;}
.enum{font-family:var(--mono); font-size:var(--fs-label); color:var(--ink-3); flex:0 0 auto;
  font-variant-numeric:tabular-nums;}
.etitle{font-size:var(--fs-control); font-weight:640; letter-spacing:-.012em; line-height:1.375; flex:1;}
.echev{font-family:var(--mono); font-size:var(--fs-control); color:var(--ink-3); flex:0 0 auto;}
.excl[open] .echev{color:var(--silver);}
/* The 34px hanging indent aligned the body under the title, past the ordinal.
   It also spent a tenth of a phone's width lining up with a number, on top of
   the page gutter and the document's own padding. It hangs from 700px up. */
.ebody{padding:0 0 var(--space-5); max-width:60ch;}
.ebody p{margin:0 0 var(--space-3); font-size:var(--fs-body); line-height:1.58;}
.ebody p:last-child{margin:0;}
.ebody .why{font-family:var(--sans); font-size:var(--fs-small); color:var(--ink-3); line-height:1.5;}
@media (min-width:700px){ .ebody{padding-left:34px;} }
@media (hover:hover) and (pointer:fine){ .excl summary:hover .etitle{color:var(--silver);} }
.sigblock{margin-top:var(--space-7); padding-top:var(--space-5); border-top:2px solid var(--ink);
  display:flex; gap:var(--space-4); align-items:flex-start; flex-wrap:wrap;}
.sigmark{width:44px; height:44px; border-radius:var(--radius-pill); border:1.5px solid var(--silver);
  display:grid; place-items:center; font-family:var(--mono); font-size:calc(var(--fs-label) - 1px);
  color:var(--silver); letter-spacing:.06em; flex:0 0 auto;}
.sigtext{font-family:var(--sans); font-size:var(--fs-small); line-height:1.55; color:var(--ink-2);
  flex:1; min-width:min(220px,100%);}
.sigtext b{color:var(--ink); display:block; font-size:var(--fs-control); margin-bottom:2px;}

/* ---- join ----------------------------------------------------------- */
.form{background:var(--vellum); border:1px solid var(--rule); border-radius:var(--radius-2xl);
  padding:var(--pad-card); box-shadow:var(--shadow); max-width:520px;}
@media (min-width:700px){ .form{padding:clamp(24px,4vw,34px);} }
.field{margin:0 0 var(--space-4);}
.field label{display:block; font-family:var(--sans); font-size:var(--fs-small); font-weight:640;
  color:var(--ink); margin:0 0 var(--space-2);}
/* --fs-field, and a real tap floor. At 15px iOS Safari zoomed the page on
   focus and never zoomed back out; at 41.5px tall these were the smallest
   targets on the page that a visitor is actually asked to hit. */
.field input, .field select{width:100%; font-family:var(--sans); font-size:var(--fs-field);
  line-height:1.3; color:var(--ink); background:var(--undercoat);
  border:1px solid var(--rule); border-radius:var(--radius-md);
  min-height:var(--tap); padding:var(--space-2) var(--space-3);}
.field input:focus, .field select:focus{border-color:var(--silver); outline:none; box-shadow:0 0 0 3px var(--silver-soft);}
.hint{font-family:var(--sans); font-size:var(--fs-micro); color:var(--ink-3);
  margin:var(--space-2) 0 0; line-height:1.45;}
/* The whole label is the target, so it takes the tap floor and the checkbox
   grows with it — 16px was a target a thumb cannot reliably hit, on the one
   control that gates the form. */
.consent{display:flex; gap:var(--space-3); align-items:flex-start;
  margin:0 0 var(--gap-block); min-height:var(--tap); padding:var(--space-2) 0;
  font-family:var(--sans); font-size:var(--fs-small); line-height:1.5; color:var(--ink-2);
  cursor:pointer; touch-action:manipulation;}
.consent input{margin-top:2px; flex:0 0 auto; width:22px; height:22px; accent-color:var(--silver);}
button.submit{font-family:var(--sans); font-size:var(--fs-control); font-weight:640;
  background:var(--silver); color:var(--undercoat); border:1px solid var(--silver);
  border-radius:var(--radius-pill); min-height:var(--tap);
  padding:var(--space-2) var(--space-6); cursor:pointer; touch-action:manipulation;
  display:inline-flex; align-items:center; justify-content:center;}
button.submit:active{background:var(--ink); border-color:var(--ink);}
@media (hover:hover) and (pointer:fine){ button.submit:hover{background:var(--ink); border-color:var(--ink);} }
.formnote{margin:var(--space-4) 0 0; font-family:var(--sans); font-size:var(--fs-micro);
  color:var(--ink-3); line-height:1.5;}
.result{margin:var(--space-4) 0 0; padding:var(--pad-tight); border-radius:var(--radius-md);
  border:1px solid var(--moss); background:var(--vellum); font-family:var(--sans);
  font-size:var(--fs-small); color:var(--ink-2); line-height:1.5;}
.result[hidden]{display:none;}

.next{margin-top:var(--gap-section); padding-top:var(--gap-block); border-top:1px solid var(--rule);
  display:flex; gap:var(--space-4); align-items:center; justify-content:space-between; flex-wrap:wrap;}
.next p{margin:0; font-family:var(--sans); font-size:var(--fs-small); color:var(--ink-2); max-width:46ch;}
.notice{border-left:3px solid var(--ginger); background:var(--vellum);
  border-radius:0 var(--radius-md) var(--radius-md) 0; padding:var(--pad-tight);
  margin:var(--gap-block) 0 0; box-shadow:var(--shadow);}
.notice p{margin:0 0 var(--space-3); font-family:var(--sans); font-size:var(--fs-small);
  line-height:1.55; color:var(--ink-2);}
.notice p:last-child{margin:0;}
.notice b{color:var(--ink);}

/* The closing standard section (index.html #standard, filled by js/landing.js).
   A rule above it, because it is not one of the routed views and needs to read
   as the page's conclusion rather than as another switchable section. Zero
   texture by rule — FR-R3, the credibility surface. */
.standard-close{border-top:1px solid var(--rule); background:var(--undercoat);}
