/* Cool People Have Dogs — global stylesheet
   Ported from the original design-canvas inline styles into one
   responsive stylesheet (mobile <= 768px, desktop above). */

/* Three faces, each with one job. Knewave is the lettering voice and carries
   every heading, echoing the hand-lettered "COOL PEOPLE HAVE" in the logo;
   Caveat is the brush "dogs" and carries notes, quotes and asides; Fredoka is
   the legible one and runs everything a reader has to get through.

   Loaded from base.html rather than an @import here: an @import inside CSS
   cannot start until this file has been fetched AND parsed, so the headline
   face arrived late on every page. */

:root {
  --cream: #FBF7F0;
  --cream-2: #F1ECE3;
  --ink: #1F1E1C;
  --ink-2: #2E2C28;
  --line: #D8D2C8;
  --muted: #665F58;
  --link: #2E2C28;
  --accent: #A1561F;  /* burnt amber */

  /* The palette collapsed to two inks on cream, 2026-10-15. Hue carried no
     information any more, so the 118 colours that used to be here were mapped
     by LIGHTNESS rather than hue - a pale green callout and a pale pink one
     both became --cream-2, a dark green rule and a dark red one both became
     --ink-2. Every box kept its light/dark structure and lost only its hue.

     One accent survives the collapse: burnt amber, added 2026-10-15 after the
     two-colour version left nothing to carry emphasis. It is deliberately
     dark - a brighter amber (#CC7722) measures 3.4:1 under white button text
     and 2.9:1 on --cream-2, both under AA. This one is 5.4 / 5.1 / 4.6, so it
     is safe as a button fill, as an icon, and as small text on any surface.
     Everything already marked var(--accent) picks it up: buttons, badges,
     eyebrow labels, inline links, rules and hovers. Nothing else should.

     --muted is #665F58 rather than something lighter because 5.1:1 on
     --cream-2 is the tightest contrast on the site and anything lighter drops
     under AA. Do not lighten it without re-checking that pairing.

     The aliases below are kept so the ~200 existing references still resolve;
     they are all the same value now and new rules should use the base token. */
  --line-2: var(--line);
  --muted-2: var(--muted);
  --muted-3: var(--muted);
  --muted-4: var(--muted);

  /* One display face. Knewave carries the page title and every heading under
     it; Chewy was dropped 2026-10-14 so the lettering voice is single rather
     than split by size. Knewave has one weight by design, so no rule that
     uses it sets font-weight, and it sits on a bigger body with strokes that
     nearly touch across lines - every rule using it sets generous leading and
     none set negative tracking, which would close the gaps it needs.
     It is scoped to type 24px and over - the hero, the article title, the
     section headings - and everything smaller uses the body face at 600,
     which is what legibility wants. Caveat is the accent face and never sets
     body copy. Fredoka reads well at text sizes and carries everything long. */
  --font-title: 'Knewave', 'Comic Sans MS', cursive;
  --font-display: 'Knewave', 'Comic Sans MS', cursive;
  --font-hand: 'Caveat', 'Bradley Hand', cursive;
  --font-body: 'Fredoka', system-ui, -apple-system, sans-serif; /* was #665F58 - 3.89:1 on cream, under AA for link text */
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; font-family: var(--font-body); background-color: var(--cream); color: var(--ink); }
a { color: inherit; text-decoration: none; }
/* height:auto is what lets the width/height attributes act as a ratio hint
   (their job is to reserve space and avoid layout shift) instead of a fixed
   height. Without it a declared aspect-ratio is ignored, because a box with
   both a definite width and a definite height has no ratio left to apply -
   which is why the homepage hero rendered 350x720 on a phone. Every image
   that genuinely wants to fill its box sets height:100% itself. */
img { max-width: 100%; height: auto; display: block; }
.font-display { font-family: var(--font-display); }

.navlink:hover, .menulink:hover, .footerlink:hover { color: var(--accent); }
.tagpill:hover { background: #F1ECE3; border-color: var(--accent); }
.card:hover .card-title { color: var(--accent); }
/* The personal voice: a note in the margin, a pull quote, an aside that sounds
   like one of them talking rather than the article explaining. Caveat runs
   small, so it is set a size up from whatever it sits beside. */
.hand { font-family: var(--font-hand); font-size: 1.22em; line-height: 1.35; }
.aside-note { font-family: var(--font-hand); font-size: 23px; line-height: 1.4;
  color: var(--muted); margin: 22px 0; padding-left: 16px;
  border-left: 3px solid var(--accent); }

.inlinelink { color: var(--accent); text-decoration: underline; text-decoration-color: #D8D2C8; text-underline-offset: 2px; }
.inlinelink:hover { text-decoration-color: var(--accent); }
/* A link inside a sentence is exempt from the 24px minimum (WCAG 2.5.8), and
   padding it would wreck the line height of the prose. A link that is the
   whole list item is not inline - that is the "On this page" table of
   contents, six 17px-tall targets stacked on every long article - so
   :only-child picks out exactly those and leaves running text alone. */
li > .inlinelink:only-child { display: block; padding: 4px 0; }

/* Breadcrumbs: 14px text in a row, so 14px-tall hit areas on every page.
   inline-flex keeps them on one line while min-height does the work. */
.navlink { display: inline-flex; align-items: center; min-height: 24px; }
.subbtn:hover { filter: brightness(1.1); }
.hamburger:hover { background: var(--cream-2); }

/* Progress bar */
.progress-track { position: sticky; top: 0; z-index: 60; width: 100%; height: 4px; background: var(--line); }
.progress-fill { height: 100%; width: var(--progress, 62%); background: var(--accent); }

/* Header */
/* top:0, not 4px. A 4px offset left a strip of page above the stuck
   header, and since the header is translucent with a blur behind it,
   content scrolled visibly through that strip - artwork and headings
   sliding past above the logo. It read as a gap at the top of the nav. */
.site-header { position: sticky; top: 0; z-index: 50; width: 100%; height: 72px; background: rgba(251,247,240,0.96); backdrop-filter: blur(6px); border-bottom: 1px solid var(--line); display: flex; align-items: center; padding: 0 48px; }
/* 1024, not 1120. Page containers are a 1120px box with 48px of padding
   inside it, so their content starts 1024px wide. The header and footer put
   their 48px on the outer element and their inner box at the full 1120, which
   left the logo, the nav and the footer columns sitting 48px to the left of
   every headline, card and paragraph on the page from about 1180px up. Same
   number, same edge: 1120 - 48 - 48. Narrowing these three is what aligns
   them; widening the page containers instead would have changed the measure
   of every article on the site. */
.site-header .inner { width: 100%; max-width: 1024px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; }
/* The wordmark is the hand-lettered logo itself, not type standing in for it.
   Sized by height so the header rhythm holds whatever the file's width is. */
.brand { display: flex; align-items: center; }
/* Two stacked lines of lettering, so the mark needs height to stay readable -
   at 34px each line was about 15px tall and the wordmark measured 57px across,
   smaller than the plain text it replaced. */
.brand img { display: block; height: 46px; width: auto; }
@media (max-width: 900px) { .brand img { height: 40px; } }
@media (max-width: 640px) { .brand img { height: 34px; } }
.menu-panel .brand img { height: 40px; }
.desktop-nav { display: flex; align-items: center; gap: 26px; }
.desktop-nav a { font-size: 14px; font-weight: 500; color: #2E2C28; }
.nav-sep { width: 1px; height: 20px; background: var(--line); }
.hamburger { width: 44px; height: 44px; border-radius: 2px; border: 1px solid var(--line); background: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.mobile-toggle { display: none; }

@media (max-width: 768px) {
  .site-header { height: 60px; padding: 0 20px; justify-content: space-between; }
  .brand span { font-size: 15px; }
  /* The hamburger and the slide-in panel exist precisely so this row of links
     does not have to fit. Without this rule both render and the ~420px nav
     forces the whole document wider than the viewport. */
  .desktop-nav, .nav-sep { display: none; }
  .header-actions { display: flex; align-items: center; gap: 8px; }
}
@media (min-width: 769px) {
  .header-actions { display: none; }
}

/* Slide-in mobile menu (pure CSS checkbox hack, no JS required).

   This used to toggle display:none to display:flex, which is why it had no
   animation: display is not an animatable property, so the panel appeared and
   vanished in a single frame, in and out. The panel is now always laid out and
   simply parked off the right edge, so what changes on open is a transform -
   which the compositor can animate without touching layout.

   visibility carries the part display used to do. Hidden, the panel cannot be
   clicked or tabbed into, which matters because it is in the document on every
   page including desktop, where the hamburger that opens it is not. On the way
   in, visibility flips immediately; on the way out it waits for the slide to
   finish, which is what the 0s delay on the closed state is for - without it
   the panel would disappear instantly and the transition would never be seen.

   The easing is a decelerating curve rather than ease-in-out: a drawer that
   arrives quickly and settles reads as a physical thing being pulled out,
   where a symmetric curve reads as a fade. Out is faster than in, which is the
   usual asymmetry - dismissing should feel immediate. */
#menu-toggle { display: none; }
.menu-overlay { position: fixed; inset: 0; background: rgba(20,18,14,0.45); z-index: 70; opacity: 0; visibility: hidden; transition: opacity 220ms ease, visibility 0s linear 220ms; }
.menu-panel { position: fixed; top: 0; right: 0; height: 100vh; width: 320px; max-width: 85vw; background: var(--cream); z-index: 80; box-shadow: -8px 0 30px rgba(0,0,0,0.15); padding: 24px; display: flex; flex-direction: column; gap: 20px; overflow-y: auto; transform: translateX(100%); visibility: hidden; will-change: transform; transition: transform 240ms cubic-bezier(0.4, 0, 0.6, 1), visibility 0s linear 240ms; }
#menu-toggle:checked ~ .menu-overlay { opacity: 1; visibility: visible; transition: opacity 260ms ease, visibility 0s; }
#menu-toggle:checked ~ .menu-panel { transform: translateX(0); visibility: visible; transition: transform 320ms cubic-bezier(0.16, 0.84, 0.44, 1), visibility 0s; }
/* Someone who has asked the system for less motion gets the old behaviour:
   the panel is simply there, with no slide to sit through. */
@media (prefers-reduced-motion: reduce) {
  .menu-overlay, .menu-panel,
  #menu-toggle:checked ~ .menu-overlay,
  #menu-toggle:checked ~ .menu-panel { transition: none; }
}
.menu-panel nav { display: flex; flex-direction: column; gap: 4px; }
.menu-panel nav a { font-family: var(--font-body); font-weight: 600; font-size: 21px; color: var(--ink); padding: 11px 4px; border-bottom: 1px solid var(--line); }
.menu-panel nav a:last-child { border-bottom: none; }
.menu-close { align-self: flex-end; width: 40px; height: 40px; border-radius: 2px; border: 1px solid var(--line); background: #fff; display: flex; align-items: center; justify-content: center; }
/* The foot of the menu used to be a lone Subscribe button pointing at the
   homepage signup form. It is a headline and a button now, aimed at the
   Library - the page that is actually every article in one place.

   The headline takes the display face at 24px, the same call made for the
   signup block: the one thing in a panel that asks the reader to go somewhere
   gets to sound like the logo. 24 is the floor for Knewave, not a coincidence. */
.menu-cta { padding-top: 18px; border-top: 1px solid var(--line); }
.menu-cta-title { font-family: var(--font-display); font-weight: 400; font-size: 24px; line-height: 1.3; color: var(--ink); margin: 0 0 12px; }
.menu-cta-btn { display: block; text-align: center; font-size: 15px; font-weight: 600; color: var(--cream); background: var(--accent); padding: 14px 18px; border-radius: 2px; }

/* Main / breadcrumb / hero */
main.page { width: 100%; max-width: 1120px; margin: 0 auto; padding: 56px 48px 0; }

/* One palette per article, set as a class on main.page so the signup block,
   the in-body printable CTA and the sidebar promo all inherit it.

   These six values were 240 hand-maintained hex scalars in front matter -
   signup_bg, signup_tint, signup_eyebrow_color, printable_bg,
   printable_accent, printable_eyebrow_color, copied into all 44 articles. They
   are not independent: the signup trio determines the printable trio, and both
   track the sidebar promo. So an article names one palette and gets all of it.

   The eyebrow gets its own value rather than reusing the accent because gold,
   plum and rose use a darker shade for small text on a tinted ground;
   flattening that would have dulled them. .pal-clay has no printable values
   because neither of its articles shows that block. */
.pal-rust  { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-sage  { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-gold  { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-plum  { --sg-bg: #F1ECE3; --sg-tint: #D8D2C8; --sg-eye: #2E2C28; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #2E2C28; }
.pal-rose  { --sg-bg: #F1ECE3; --sg-tint: #D8D2C8; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-cocoa { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-slate { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
.pal-clay  { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; }
.pal-moss  { --sg-bg: #F1ECE3; --sg-tint: #F1ECE3; --sg-eye: #665F58; --pr-bg: #F1ECE3; --pr-accent: #665F58; --pr-eye: #665F58; }
@media (max-width: 768px) { main.page { padding: 28px 20px 0; } }
/* Horizontal gutter only, so adding it to the standalone pages does not
   change their vertical rhythm. */
.page-shell { width: 100%; max-width: 1120px; margin: 0 auto; padding: 0 48px; }
@media (max-width: 768px) { .page-shell { padding: 0 20px; } }

.breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 13px; color: var(--muted-4); margin-bottom: 18px; }
@media (max-width: 768px) { .breadcrumb { font-size: 12px; margin-bottom: 14px; } }
/* On a phone an article's own title is too long to sit beside Home and the
   category, so the trail wrapped and left the separator dangling at the end of
   the first line with nothing after it. Drop the current page and the
   separator leading to it: the h1 immediately below says the same thing, and
   what is left is the part that is actually navigable. */
@media (max-width: 768px) {
  .breadcrumb .current, .breadcrumb .sep-current { display: none; }
}
.breadcrumb a { color: var(--muted-4); }
.breadcrumb .current { color: #2E2C28; }

.topic-tag { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: 0.06em; color: var(--cream); background: var(--accent); padding: 6px 14px; border-radius: 2px; }
@media (max-width: 768px) { .topic-tag { font-size: 11px; padding: 5px 12px; } }

/* Knewave's strokes nearly touch across lines, so it gets more leading and
   no negative tracking - tightening a brush face closes the gaps it needs. */
h1.article-title { font-family: var(--font-title); font-size: clamp(30px, 4.2vw, 46px); line-height: 1.26; letter-spacing: 0; margin: 18px 0 0; }
.article-subhead { font-size: clamp(16px, 1.6vw, 20px); line-height: 1.5; color: var(--muted); margin: 12px 0 0; max-width: 640px; }
.meta-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 24px; font-size: 14px; color: var(--muted-2); }
@media (max-width: 768px) { .meta-row { font-size: 13px; margin-top: 16px; } }
.dot { width: 3px; height: 3px; border-radius: 50%; background: var(--line-2); }
.disclosure-line { font-size: 13px; color: var(--muted-3); margin: 32px 0 0; padding-top: 18px; border-top: 1px solid var(--line-2); max-width: 640px; }
@media (max-width: 768px) { .disclosure-line { font-size: 12px; } }

.hero { position: relative; margin-top: 32px; width: 100%; aspect-ratio: 16/8.5; border-radius: 2px; background: repeating-linear-gradient(135deg, #F1ECE3, #F1ECE3 12px, #D8D2C8 12px, #D8D2C8 24px); border: 1px solid var(--line); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; overflow: hidden; }
.hero img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
@media (max-width: 768px) { .hero { margin-top: 20px; aspect-ratio: 16/11; border-radius: 2px; } }
.hero-placeholder-text { font-size: 13px; color: #665F58; font-style: italic; }
.pin-btn { position: absolute; top: 16px; right: 16px; text-decoration: none; font-family: inherit; display: flex; align-items: center; gap: 6px; background: #E60023; color: #fff; border: none; border-radius: 2px; padding: 9px 16px; font-size: 13px; font-weight: 700; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,0.18); }

/* Content grid */
.content-grid { display: grid; grid-template-columns: 1fr 2fr; gap: 56px; margin-top: 44px; align-items: start; }
@media (max-width: 900px) { .content-grid { grid-template-columns: 1fr; gap: 0; } }

aside.sidebar { position: sticky; top: 96px; display: flex; flex-direction: column; }
@media (max-width: 900px) {
  /* The sidebar is first in the markup so it can stick beside the article. On
     one column that order would put the author block and the share row above
     the opening paragraph, so reorder rather than hide: sharing and tags are
     what a phone reader actually reaches for. */
  article.body { order: 1; }
  aside.sidebar { order: 2; position: static; display: flex; margin-top: 4px; }
  /* The portrait is what does not belong at full phone width, not the block.
     A small circle keeps the introduction without handing a phone a
     four-fifths-ratio slab of placeholder. */
  aside.sidebar .author-block { text-align: center; padding: 30px 0; border-top: 1px solid var(--line); }
  aside.sidebar .author-photo { width: 92px; margin: 0 auto; aspect-ratio: 1/1; border-radius: 50%; }
}

.author-block { padding-bottom: 30px; text-align: center; }
/* Square rather than the 4/5 portrait it was, and sourced from
   authors-square.jpg rather than cropping the tall one: the square file
   is composed for this shape, so the frame is not deciding where to cut
   the two of them off. The phone version was already a circle and now
   takes the square file too, which is less of the picture thrown away. */
.author-photo { width: 100%; aspect-ratio: 1/1; border-radius: 2px; overflow: hidden; background: repeating-linear-gradient(135deg, #F1ECE3, #F1ECE3 12px, #D8D2C8 12px, #D8D2C8 24px); border: 1px solid var(--line); display: flex; align-items: center; justify-content: center; }
.author-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* The introduction used to be the word "Hello!" over a sentence about being
   obsessed with dogs, which is true of everyone who reads this site and so
   says nothing. The greeting is Austin's own wording, split across two faces:
   "Hey there," handwritten, "Welcome!" lettered.

   Three faces doing three jobs in six lines: Caveat for the greeting,
   because a handwritten hello is a person speaking rather than a heading;
   Knewave for the names at 24px, its floor; Fredoka for the part that has to
   be read. These were inline styles on the template, including a 22px display
   face that sat under that floor. */
.author-greeting { font-family: var(--font-hand); font-weight: 600; font-size: 22px; line-height: 1; color: var(--accent); margin: 18px 0 0; }
.author-name { font-family: var(--font-display); font-weight: 400; font-size: 24px; line-height: 1.3; color: var(--ink); margin: 4px 0 0; }
.author-bio { font-size: 14px; color: var(--muted); line-height: 1.65; margin: 12px 0 0; }
.author-bio strong { color: var(--ink); font-weight: 600; }
.sidebar-block { padding: 26px 0; border-top: 1px dotted var(--line-2); }
.sidebar-label { font-size: 13px; font-weight: 700; letter-spacing: 0.04em; color: var(--muted-2); margin-bottom: 14px; }
.share-row { display: flex; align-items: center; gap: 12px; }
/* The four glyphs carry fill="currentColor" rather than a hardcoded hex, so
   this colour is the one that reaches them. They were pinned at #1F1E1C in the
   markup - a black that is not in the palette and is a shade off the --ink used
   by everything around them - which also meant the hover below could tint the
   button but never the icon. Every other link on the site goes to --accent on
   hover; these now do too, instead of just dimming. */
.share-row a, .share-row button { border: none; background: none; padding: 0; cursor: pointer; color: var(--ink);
  display: flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; border-radius: 2px;
  transition: color 140ms ease, background-color 140ms ease; }
.share-row a:hover, .share-row button:hover { background: var(--cream-2); color: var(--accent); }
.tag-row { display: flex; flex-wrap: wrap; gap: 8px; }
.tagpill { font-size: 13px; font-weight: 500; padding: 7px 13px; border-radius: 2px; background: #F1ECE3; }
/* The sidebar lead-magnet promo. This whole component was already written
   here and had never been used once: all 37 articles that show it pasted ~960
   characters of equivalent markup into their own front matter instead, which
   is where 259 of the site's inline style attributes were hiding. They use it
   now, through templates/_leadmagnet.html.

   The four colours are the only thing that varied between articles, so they
   are custom properties and each palette is one line. The default values are
   the ones this rule already carried. */
.leadmagnet {
  --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8;
  margin-top: 26px; padding: 30px 24px; background: var(--lm-bg); border-radius: 2px; text-align: center;
}
.leadmagnet > svg { margin: 0 auto; }
.leadmagnet .title { font-family: var(--font-body); font-weight: 600; font-size: 20px; color: var(--cream); margin-top: 10px; }
.leadmagnet p { font-size: 13px; color: var(--lm-body); line-height: 1.6; margin: 10px 0 0; }
/* The emphasised phrase in the blurb goes cream against the dark panel. */
.leadmagnet p strong { color: var(--cream); }
.leadmagnet .subbtn { display: block; margin-top: 18px; padding: 12px; background: var(--cream); color: var(--lm-ink); font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-align: center; border-radius: 2px; }
.leadmagnet .fine { display: block; font-size: 11px; color: var(--lm-fine); margin-top: 10px; }
/* The base values above are rust; it needs a class of its own now that the
   template always names a palette. */
.leadmagnet.is-rust  { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-sage  { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-gold  { --lm-bg: #665F58; --lm-body: #F1ECE3; --lm-ink: #665F58; --lm-fine: #D8D2C8; }
.leadmagnet.is-plum  { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-rose  { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-cocoa { --lm-bg: #2E2C28; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-slate { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-fern  { --lm-bg: #665F58; --lm-body: #F1ECE3; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }
.leadmagnet.is-moss  { --lm-bg: #665F58; --lm-body: #D8D2C8; --lm-ink: #2E2C28; --lm-fine: #D8D2C8; }

.related-list-heading { font-family: var(--font-body); font-weight: 600; font-size: 21px; color: var(--ink); padding-bottom: 14px; border-bottom: 1px solid var(--line); }
.related-row { display: flex; flex-direction: column; gap: 12px; padding: 20px 0; border-bottom: 1px dotted var(--line-2); }
.related-row:last-child { border-bottom: none; }
.related-thumb { width: 100%; aspect-ratio: 16/11; border-radius: 2px; background: #F1ECE3; display: flex; align-items: center; justify-content: center; }
.card-title { font-family: var(--font-body); font-weight: 600; font-size: 17px; line-height: 1.35; }
.read-more { display: flex; align-items: center; gap: 5px; margin-top: 10px; font-size: 13px; font-weight: 600; color: var(--accent); }

article.body { min-width: 0; font-size: 17px; line-height: 1.75; color: var(--ink-2); }
@media (max-width: 900px) { article.body { margin-top: 0; font-size: 16px; line-height: 1.7; } }
/* Section headings. 244 identical copies of this declaration used to live
   inline in the content files, which is why the mobile size below needed
   !important to get past them. They carry .body-h2 now, so the stylesheet
   governs them and the override is an ordinary rule.

   There is no `article.body h2` element rule any more, deliberately: it would
   be more specific than .body-h2 and would win inside the media query, putting
   mobile headings back to 26px.

   Two things the old rule did that are preserved exactly here rather than
   quietly corrected. The 40px variant below is real - 21 headings opened with
   a little more air than the other 223. And the old mobile rule also asked for
   `margin: 28px 0 12px`, with no !important, so the inline margin always beat
   it and those tighter mobile margins have never once applied. Reinstating
   them would be a visible change, so it is a decision, not a cleanup. */
.body-h2 { font-family: var(--font-display); font-size: 26px; line-height: 1.3; margin: 36px 0 14px; }
.body-h2-loose { margin-top: 40px; }

/* The quiz and signup panel headings. They are a size of their own, and the
   old blanket `article.body h2` mobile rule was shrinking them to 21px along
   with everything else - a side effect, but a visible one, so it is kept. */
.panel-h2 { font-family: var(--font-body); font-weight: 600; font-size: 24px; line-height: 1.2; margin: 12px 0 0; }
.panel-h2-tight { margin-top: 10px; }

/* Knewave needs room. 21px was fine for a geometric sans and too small for
   hand lettering, so the headline steps down less far and the panel heading,
   which is smaller again, uses the body face instead. */
@media (max-width: 900px) { .body-h2 { font-size: 23px; } .panel-h2 { font-size: 21px; } }
/* The signup block is the one panel on the page that asks the reader for
   something, so it gets the display face while every other panel stays quiet
   in the body face. It is a separate class rather than a change to .panel-h2
   for exactly that reason. Declared after the 900px rule above because both
   are one class deep and source order is what decides it.

   It never drops below 24px: Knewave is hand lettering and turns to mush
   under that, which is the whole reason the face is scoped in the first
   place. */
.panel-h2-display { font-family: var(--font-display); font-weight: 400; font-size: 30px; line-height: 1.3; }
@media (max-width: 900px) { .panel-h2-display { font-size: 26px; } }
@media (max-width: 640px) { .panel-h2-display { font-size: 25px; } }
article.body ul, article.body ol { margin: 14px 0; padding-left: 22px; line-height: 1.9; }
article.body p { margin: 0 0 16px; }
/* The in-body "free printable" CTA. This class was the fourth written-but-
   never-used rule on the site; the 36 articles that show the block were
   interpolating the same declaration inline from front matter instead. The
   layout here was already right - only the two colours varied, and those now
   come from the article's palette.

   The children carry classes rather than being selected as elements: a bare
   `.callout-printable p` is one class and one element, which loses to
   `article.body p`, and the paragraph would pick up the body margin. */
.callout-printable { background: var(--pr-bg, #F1ECE3); border-left: 4px solid var(--pr-accent, var(--accent)); border-radius: 2px; padding: 18px 22px; margin-bottom: 32px; display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.callout-printable .pr-eyebrow { font-size: 12px; font-weight: 700; letter-spacing: 0.06em; color: var(--pr-eye, var(--pr-accent, var(--accent))); margin-bottom: 6px; }
.callout-printable .pr-text { margin: 0; font-size: 15px; line-height: 1.6; max-width: 440px; }
.callout-printable .subbtn { flex-shrink: 0; padding: 12px 20px; background: var(--pr-accent, var(--accent)); color: var(--cream); font-size: 13px; font-weight: 700; border-radius: 2px; white-space: nowrap; }
.callout-note { background: #FBF7F0; border: 1px solid #D8D2C8; border-radius: 2px; padding: 16px 20px; margin: 24px 0; font-size: 15px; line-height: 1.6; }
/* .callout-note above was written and then never used: all 10 note boxes
   pasted the identical declaration inline instead. They use the class now.

   The other two boxes that repeat through the articles. The label colour is
   what distinguishes them, so it is inherited from the box rather than set on
   every label. .is-slate is the single gear-coloured quick answer. */
.callout-toc { background: #FBF7F0; border: 1px solid #D8D2C8; border-radius: 2px; padding: 20px 24px; margin: 32px 0; }
.callout-toc > .callout-list { margin: 0; padding-left: 18px; line-height: 1.9; }
/* .callout-list carries a class rather than relying on `.callout-toc > ul`,
   which loses to `article.body ul` - one class and two elements beats one
   class and one element. Two classes win it back. */
.callout-quick { background: #F1ECE3; border-left: 4px solid #665F58; border-radius: 2px; padding: 18px 22px; margin-bottom: 32px; }
.callout-quick.is-slate { border-left-color: #665F58; }
.callout-label { font-size: 12px; font-weight: 700; letter-spacing: 0.06em; color: #665F58; margin-bottom: 10px; }
.callout-quick > .callout-label { color: #665F58; margin-bottom: 6px; }
.callout-quick.is-slate > .callout-label { color: #665F58; }
.download-cta { background: #F1ECE3; border-radius: 2px; padding: 36px; margin-top: 40px; display: grid; grid-template-columns: 1fr auto; gap: 32px; align-items: center; }
@media (max-width: 900px) { .download-cta { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .download-cta { padding: 26px 20px; border-radius: 2px; } }
.download-cta form { margin-top: 20px; display: flex; flex-direction: column; gap: 10px; max-width: 340px; }
.download-cta input[type=email] { padding: 12px 16px; border-radius: 2px; border: 1px solid #D8D2C8; background: #FFFFFF; color: var(--ink); font-size: 14px; font-family: var(--font-body); }
.subbtn { cursor: pointer; }

/* Read next / card grids */
.read-next { margin-top: 64px; padding: 48px 0 64px; border-top: 1px solid var(--line); }
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 24px; }
/* A card grid that follows a page heading needs air above it. This was the
   same inline margin written three times - collection.html, author.html and
   library.md - and an inline style outranks this file, so the spacing could
   not be adjusted from here. */
.card-grid.is-below-head { margin-top: 28px; }
@media (max-width: 900px) { .card-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 20px; } }
@media (max-width: 640px) { .card-grid { grid-template-columns: 1fr; } }
.article-card { display: block; border-radius: 2px; overflow: hidden; background: #FFFFFF; }
.article-card .thumb { width: 100%; aspect-ratio: 16/10; background: #F1ECE3; display: flex; align-items: center; justify-content: center; }
.article-card .pad { padding: 18px; }
.article-card .cat { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.05em; color: var(--accent); text-transform: uppercase; }
.article-card .rule { height: 1px; background: #F1ECE3; margin: 14px 0 10px; }
.article-card .sub { font-size: 11px; color: var(--muted-3); }

/* Footer */
footer.site-footer { width: 100%; background: var(--cream-2); padding: 56px 48px 0; border-top: 1px solid var(--line); }
@media (max-width: 768px) { footer.site-footer { padding: 40px 20px 0; } }
/* 13px links in a flex column gave 15px-tall hit areas. Padding does the work
   and the gap shrinks to match, so the spacing looks the same.
   This was inside a max-width:768px query, which fixed the phone the
   complaint came from and left the same 15px targets on every tablet and
   desktop - a runtime sweep counted 327 of them at 834px and above. WCAG
   2.5.8 is not a mobile rule and a trackpad is not a precise pointer, so it
   applies at every width. */
.footer-col { gap: 1px; }
.footerlink { padding: 8px 0; }
.footer-grid { max-width: 1024px; margin: 0 auto; display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 32px; }
@media (max-width: 768px) { .footer-grid { grid-template-columns: 1fr 1fr; gap: 28px; } }
.footer-col { display: flex; flex-direction: column; gap: 10px; }
.footer-col-label { font-size: 12px; font-weight: 700; letter-spacing: 0.05em; color: var(--muted-3); margin-bottom: 4px; }
.footerlink { font-size: 13px; color: var(--link); }
.footer-bottom { max-width: 1024px; margin: 40px auto 0; padding: 20px 0; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted-4); }
/* The footer band, tiled. It is drawn as a repeating background rather than
   an <img> because it is decoration - there is no alt text worth writing, and
   repeat-x is what fills the page edge to edge at a size that does not
   dominate. Negative margins cancel the footer's own side padding.

   The tile carries 18px of transparent gutter on each side: without it the
   play-bow pair at the right end butts straight into the tug-of-war dog at
   the left end on every join. Anchored bottom so the dogs always stand on the
   page edge whatever the band height.

   Height and background-size move together - the box has no content to give
   it height, so the two values must match or the dogs get clipped. */
.dog-lineup { margin: 0 -48px; height: 204px;
  background-image: url('../images/footer-dogs.png');
  background-repeat: repeat-x;
  background-position: center bottom;
  background-size: auto 204px; }
@media (max-width: 768px) {
  .dog-lineup { margin: 0 -20px; height: 118px; background-size: auto 118px; }
}
}

.subbtn.primary { background: var(--accent); color: var(--cream); }

/* Buttons / generic */
.btn { display: inline-block; font-weight: 700; border-radius: 2px; padding: 12px 20px; }

/* Draft notice. Only rendered on articles with status: draft in front matter -
   they build so they can be previewed at their real URL, but carry noindex and
   appear in no listing. */
.draft-banner{background:#2E2C28;color:#fff;font:600 13px/1.4 system-ui,sans-serif;
  letter-spacing:.02em;text-align:center;padding:10px 16px}

/* A tag with no collection page yet - every article carrying it is still a
   draft - so the pill shows the tag without pretending to be a link. */
.tagpill-plain{cursor:default;opacity:.75}

/* Collection pagination. Numbers plus an explicit Last, because "jump to the
   end" is the one move a reader cannot make by clicking Next repeatedly. */
.pagination{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:8px;margin:44px 0 8px;font-size:14px}
.pagination a,.pagination span{display:inline-flex;align-items:center;justify-content:center;
  min-width:38px;height:38px;padding:0 12px;border-radius:2px;border:1px solid var(--line-2);
  background:#FFFFFF;color:var(--ink);text-decoration:none}
.pagination a:hover{background:#FBF7F0}
.pagination .is-current{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
.pagination .is-off{opacity:.38}
.pagination .is-gap{border:0;background:none;min-width:12px;padding:0 2px;opacity:.6}
@media (max-width:640px){
  .pagination{gap:6px;font-size:13px}
  .pagination a,.pagination span{min-width:34px;height:34px;padding:0 9px}
}


/* ------------------------------------------------------------------ *
 * Responsive layout for markup that used to carry inline styles.
 * An inline grid-template-columns or padding cannot be overridden by a
 * media query, so the homepage and the signup block rendered their
 * desktop column counts on phones - four pin cards inside 294px. The
 * geometry moved here; the content files keep only what genuinely
 * varies per page (accent colours, backgrounds).
 * ------------------------------------------------------------------ */

/* Homepage section shell. Same geometry as main.page, but the homepage is a
   stack of sections rather than one container. */
.hp-section { width: 100%; max-width: 1120px; margin: 0 auto; padding: 64px 48px 0; box-sizing: border-box; }

/* The homepage opener. The front page used to begin with a grid of cards and
   said nothing at all; then it said who we are, which put the authors in front
   of the subject. It states what the whole site is for instead - one question
   that covers behaviour, food, names and games alike - over a picture of the
   dogs themselves. Sized by its content, never the viewport, so the articles
   are still in the first screen on a laptop. */
.hp-hero { width: 100%; max-width: 1120px; margin: 0 auto; padding: 52px 48px 0;
  box-sizing: border-box; }
/* One column. The opener carried four square portraits of their own dogs
   beside the statement; it pointed at them rather than at the reader, and the
   card grid directly below already puts twelve photographs on the page. The
   statement gets the space instead, and grows into it. */
.hp-hero-inner { display: grid; grid-template-columns: 1fr; gap: 0; }
/* No max-width. The 16ch cap this started with broke the statement into three
   short lines filling just over half the container - a hole down the right of
   the page rather than a margin. Measured at every width from 360 to 2560:
   20ch, 24ch and no cap produce identical line breaks everywhere except 768,
   where 20ch alone drops to three lines at 56% fill. The cap was never doing
   the work; .hp-hero's own 1120px max-width is what holds the line length.
   text-wrap: balance does the rest, and the accent "dog." lands at the right
   edge from 1280 up. */
/* Centred to agree with the illustration below it, which is capped at 720px
   and centred like the 404's. Left-aligned text over centred art read as two
   different layouts stacked. */
.hp-hero-inner { text-align: center; }
.hp-hero h1 { font-family: var(--font-title); font-size: clamp(38px, 6.4vw, 80px);
  line-height: 1.22; letter-spacing: 0; color: var(--ink); margin: 0;
  text-wrap: balance; }
.hp-hero h1 em { font-style: normal; color: var(--accent); }
.hp-hero .sub { font-size: clamp(17px, 1.6vw, 20px); line-height: 1.6;
  color: var(--muted); margin: 22px auto 0; max-width: 44ch; }
/* The opener had no action in it at all - a reader's only move was to scroll.
   Primary goes to the featured article, secondary to the Library, so the two
   are not the same destination wearing different words. */
.hp-hero-actions { display: flex; flex-wrap: wrap; justify-content: center;
  gap: 12px; margin-top: 30px; }
.hero-btn { font-family: var(--font-body); font-size: 15px; font-weight: 600;
  padding: 14px 22px; border-radius: 2px; background: var(--accent);
  color: var(--cream); }
.hero-btn.is-ghost { background: transparent; color: var(--ink);
  border: 1px solid var(--line-2); }
.hero-btn:hover { filter: brightness(1.06); }


/* The gutter drops at 768, not 900. Everything else on the page - the header,
   the card sections, the footer, .page-shell - narrows its gutter from 48px to
   20px at 768. The hero was doing it at 900, so between 769 and 900 the
   statement sat 28px to the left of the cards directly beneath it. On an iPad
   held upright that reads as the headline hugging the edge of the screen. */
@media (max-width: 900px) {
  .hp-hero-actions { margin-top: 26px; }
}
@media (max-width: 768px) {
  .hp-hero { padding: 32px 20px 0; }
}
}

}
.hp-section.is-first { padding-top: 56px; }
.hp-section.is-flush { padding-top: 0; margin-top: 72px; }
.hp-section.is-flush-tight { padding-top: 0; margin-top: 24px; }
.hp-section.is-note { margin-top: 56px; padding-top: 24px; border-top: 1px solid var(--line); }
@media (max-width: 768px) {
  .hp-section { padding-left: 20px; padding-right: 20px; padding-top: 44px; }
  .hp-section.is-first { padding-top: 28px; }
  .hp-section.is-flush { padding-top: 0; margin-top: 44px; }
  .hp-section.is-note { margin-top: 40px; }
}

.hp-grid-hero { display: grid; grid-template-columns: 1.4fr 1fr; gap: 28px; align-items: stretch; margin-top: 36px; }
.hp-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 24px; margin-top: 28px; }
.hp-grid-pins { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 20px; margin-top: 28px; }
.hp-grid-topics { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 18px; margin-top: 28px; }
/* Popular Topics runs four across so the live tags sit on one row. Browse
   by Topic keeps three, because it is the full list and will grow past one
   row whatever the column count.

   Behind min-width rather than written flat: .is-popular .hp-grid-topics is
   more specific than the bare .hp-grid-topics in the max-width blocks below,
   so a flat rule outranked them and held four columns all the way down to
   390px, overflowing the page. Scoped this way the mobile rules win again. */
@media (min-width: 901px) {
  .is-popular .hp-grid-topics { grid-template-columns: repeat(4, minmax(0,1fr)); }
}
@media (max-width: 900px) {
  .hp-grid-hero { grid-template-columns: 1fr; gap: 24px; margin-top: 24px; }
  .hp-grid-3, .hp-grid-topics { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 20px; }
  .hp-grid-pins { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 640px) {
  .hp-grid-3, .hp-grid-topics { grid-template-columns: 1fr; gap: 16px; }
  /* Pins stay two across: they are 2/3 portrait, so a single column would
     give each one most of the screen height. */
  .hp-grid-pins { gap: 12px; }
}

/* The hero row is one tall card beside two stacked ones, and the photos are
   meant to absorb whatever height difference is left over. flex:1 did that by
   sizing from zero, which works only while a sibling fixes the row height -
   collapse the grid to one column and the photo resolves to 0 and vanishes.
   flex:1 1 auto with an aspect-ratio gives the same growth plus a floor: the
   ratio is the starting height, and growing past it is allowed. */
.hp-feat-img { display: block; width: 100%; aspect-ratio: 16/11; flex: 1 1 auto; min-height: 0; object-fit: cover; border-radius: 2px; border: 1px solid var(--line); }
.hp-sec-img { display: block; width: 100%; aspect-ratio: 16/10; flex: 1 1 auto; min-height: 0; object-fit: cover; border-radius: 2px; border: 1px solid var(--line); }
@media (max-width: 900px) { .hp-feat-img, .hp-sec-img { flex: none; } }

/* Homepage newsletter block and the per-article signup include. */
/* The printable preview sits in a dog's mouth. Two elements rather than one
   picture, so the preview stays the REAL cover at its true proportions - swap
   the printable and the dog still holds it.

   The numbers come from the drawing it was cut from: the sheet there was 0.84x
   the dog's width, gripped at his right edge 13% down his height. So the dog is
   1/0.84 = 119% of the preview's width, his right edge against its left edge,
   lifted 13% of his own height so his jaws meet its top corner. translateY on a
   transform resolves against the element's own height, which aspect-ratio makes
   known - percentages on `top` would resolve against the container instead and
   drift with the cover's size. */
/* Width lives here rather than in an inline attribute on home.md, because
   the dog hanging off its left edge is sized as a percentage of it - the two
   numbers have to be read together, and an inline style outranks this file. */
.pdf-figure { position: relative; width: 100%; max-width: 210px; }
.pdf-figure > .pdf-figure-dog {
  position: absolute; right: 100%; top: 0;
  width: 119%; aspect-ratio: 900 / 1258;
  transform: translateY(-13%);
  background: url('../images/dog-pdf.png') no-repeat center / contain;
  pointer-events: none;
}
/* The dog is positioned off the left edge of the preview at 119% of the
   preview's own width, so whether it clears the copy depends on how much
   empty space the grid leaves between the two columns - not on any one
   breakpoint. Measured across every width from 769 to 1440: it cuts into real
   glyphs - the field label, the bold kit name, the fine print - everywhere
   below 1120. At 1120 and up the only thing its box crosses is the ragged
   right edge of the headline's first line, which is the effect it is for.
   Hidden below that; the preview and the copy are complete without it. */
@media (max-width: 1119px) { .pdf-figure > .pdf-figure-dog { display: none; } }
/* Back to 210px / 1119 after a trip to 232px / 1199. The bigger preview was
   made on a misread - the illustration that needed enlarging was the one in
   the welcome email - and at 232px the dog crossed the paragraph and the
   email field at ordinary desktop widths. 210/1119 is the pairing that was
   actually measured across 769-1440; it is not a number to nudge without
   re-measuring, because the dog is sized as a percentage of the preview and
   both move together. */

.hp-signup { border-radius: 2px; padding: 48px; display: grid; grid-template-columns: 1.3fr 1fr; gap: 40px; align-items: center; box-sizing: border-box; }
/* The signup block runs horizontal: the printable on the left at the
   proportions of the page it actually is, the ask on the right with the field
   and the button on one row. The form used to stack inside a 340px column,
   which made this the tallest element in any article - an eyebrow, a heading,
   a paragraph, a label, a field, a button and a line of fine print, each on
   its own row - for the sake of collecting one email address. */
.signup-block { background: var(--sg-bg, #F1ECE3); border-radius: 2px; padding: 28px 32px; margin-top: 40px; display: grid; grid-template-columns: 170px 1fr; gap: 30px; align-items: center; box-sizing: border-box; }
/* ---- The Library ----
   The full index of every published guide, newest first. It is not the nav
   with bigger hit areas: five places on the site link here promising "all
   articles" / "view all" / "browse everything at once", and until 2026-10-14
   this page answered that with three category cards. Categories slice by
   section, /tags/ slices by what the reader is sorting out, and this is the
   flat list. One row per article so it still scans at forty of them. */
.lib-jump { display: flex; flex-wrap: wrap; gap: 8px; margin: 22px 0 0; }
.lib-jump a { font-size: 13px; font-weight: 600; color: var(--ink); background: var(--cream-2);
              border: 1px solid var(--line); border-radius: 2px; padding: 7px 15px; }
.lib-jump a:hover { background: #FFFFFF; color: var(--accent); }
/* The badge column and the date both go at the narrow end - the title is the
   only part worth the width on a phone. */
@media (max-width: 640px) {
        }

/* The anchor target for every "#newsletter" CTA. The progress bar (4px) and
   the header (72px at top:4) are both sticky, so 76px of the viewport is
   already spoken for; without this the block lands underneath them. */
.signup-block { scroll-margin-top: 92px; }
.signup-block .sg-preview { width: 100%; }
/* The cover is the PDF's own first page and keeps a Letter page's ratio
   rather than a crop of it: a printable previewed at the wrong shape reads as
   a different document from the one that arrives. */
.signup-block .sg-cover { display: block; width: 100%; height: auto; aspect-ratio: 17/22; object-fit: cover; object-position: top center; background: #FFFFFF; border: 1px solid var(--line); border-radius: 2px; box-shadow: 0 16px 34px rgba(34,31,26,0.16); transform: rotate(-2.5deg); }
/* The stand-in for an article whose printable has no cover yet. */
.signup-block .sg-card { width: 100%; aspect-ratio: 3/4; background: #FFFFFF; border: 1px solid var(--line); border-radius: 2px; box-shadow: 0 16px 34px rgba(34,31,26,0.14); padding: 15px; box-sizing: border-box; transform: rotate(-2.5deg); display: flex; flex-direction: column; gap: 7px; }
.signup-block .sg-card-kicker { font-size: 9px; color: var(--muted-4); }
.signup-block .sg-card-title { font-family: var(--font-body); font-weight: 600; font-size: 13px; line-height: 1.3; }
.signup-block .sg-card-rule { height: 1px; background: var(--line); margin: 3px 0; }
.signup-block .sg-card-bars { display: flex; flex-direction: column; gap: 4px; }
/* Two classes, not `.signup-block p`, so the blurb is not outranked by
   `article.body p` - the block renders inside the article body. */
.signup-block .sg-eyebrow { display: inline-flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--sg-eye, #665F58); }
.signup-block .sg-blurb { font-size: 14px; color: #2E2C28; line-height: 1.6; margin: 12px 0 0; max-width: 460px; }
.signup-block .sg-bar { height: 4px; background: var(--sg-tint, #F1ECE3); border-radius: 2px; }
/* The field's basis is 150px, not a comfortable 230. Inside a two-column
   article the copy column is about 380px wide, and the longest button on the
   site - "Get my free recipe cards" - is about 200px. 150 + 200 + the 10px
   gap is 360, which fits; 230 did not, and the button wrapped onto its own
   line and undid the point of the row. The field still grows into whatever is
   left. Below roughly 350px of column it wraps anyway, which is the right
   answer on a phone and should not happen on a desktop. */
.signup-block .sg-form { margin-top: 18px; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.signup-block .sg-field { flex: 1 1 150px; min-width: 0; padding: 12px 16px; border-radius: 2px; border: 1px solid #D8D2C8; background: #FFFFFF; color: var(--ink); font-size: 14px; font-family: var(--font-body); }
.signup-block .sg-submit { flex: 0 0 auto; white-space: nowrap; padding: 13px 18px; border-radius: 2px; border: none; background: var(--accent); color: var(--cream); font-size: 14px; font-weight: 600; }
.signup-block .sg-fine { flex: 1 0 100%; font-size: 12px; color: var(--muted-4); margin: 0; }
/* Between 901px and 1119px the page is still two columns but the article
   column is not yet at its cap, which leaves the copy under about 355px -
   not enough for a usable field beside the button. Measured across every
   published article: at 1120 and above the row holds with at least 172px of
   field; below it, some articles wrapped and some did not, purely on the
   length of their button copy. Rather than let the layout be decided by a
   label, the band stacks on purpose and the button takes the full width. */
@media (min-width: 901px) and (max-width: 1119px) {
  .signup-block .sg-submit { flex: 1 0 100%; }
}
/* The label is read, not seen. The placeholder vanishes the moment someone
   types, which leaves a screen reader with a required field and no name. */
.sg-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 900px) {
  .hp-signup { grid-template-columns: 1fr; gap: 28px; padding: 32px; }
}
@media (max-width: 640px) {
  .hp-signup { padding: 24px 20px; border-radius: 2px; }
  .signup-block { grid-template-columns: 1fr; gap: 20px; padding: 24px 20px; border-radius: 2px; }
  .signup-block .sg-preview { width: 148px; }
  .signup-block .sg-submit { flex: 1 0 100%; }
}

/* The dog-names banner is a two-ended flex row with no wrap declared. */
.quickbanner-link { flex-wrap: wrap; background: var(--ink); }

/* Article quiz panel. */
@media (max-width: 640px) { .quiz-panel { padding: 24px 18px !important; border-radius: 2px; } }

/* Tables keep their inline styling from the ported design; this only gives a
   narrow screen somewhere to scroll instead of crushing three columns of
   prose into 300px. Wrapper added in build.py's render_body(). */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > table { min-width: 460px; }

/* Comparison tables in article bodies. Every cell used to carry its own
   padding and border inline - 109 of them across 7 tables - even though the
   values only ever varied by table, never by cell. Descendant selectors mean
   the cells need no attributes at all now.

   The two modifiers are real drift, kept rather than normalised: the three
   tables in can-dogs-eat-blueberries use 9px cell padding where the other four
   use 10px, and the one in stare is a size down with no margin of its own. */
.data-table { width: 100%; border-collapse: collapse; margin: 24px 0; font-size: 15px; }
.data-table th { text-align: left; padding: 10px 12px; border-bottom: 2px solid #D8D2C8; }
.data-table td { padding: 10px 12px; border-bottom: 1px solid #F1ECE3; }
.data-table tr.is-tint { background: #F1ECE3; }
.data-table.is-tight td { padding: 9px 12px; }
.data-table.is-compact { margin: 0; font-size: 14px; line-height: 1.55; }
/* The closing row of the stare table is deliberately ruleless. */
.data-table td.no-rule { border-bottom: 0; }

/* A single unbreakable token - a long URL or binomial name - is enough to
   widen the whole document. Cheap insurance, body copy only. */
article.body, .article-subhead, .card-title { overflow-wrap: break-word; }

/* Grids inside the standalone pages (About, Library, 404). Same reason as the
   homepage: the column count was inline, so no media query could reach it. */
.pg-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 24px; margin: 24px 0; }
.pg-grid-2.is-library { gap: 16px; margin: 36px 0 0; }
.pg-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 16px; }
@media (max-width: 640px) {
  .pg-grid-2, .pg-grid-3 { grid-template-columns: 1fr; gap: 14px; }
}

/* Homepage components that arrived from the mockup with their look inline and
   no stylesheet rules at all - so they rendered, but nothing responded to a
   pointer. Hover only; the inline styles still own the layout until the
   inline-style extraction lands. */
/* ---- Collection illustrations ---------------------------------------- *
 * One drawing per collection, found by filename (see collection_art in
 * build.py). The topic card keeps its flex row and swaps the 44px icon chip
 * for the art at 72px - a full scene at 44 is a smudge, and 72 is the largest
 * the row carries before the label stops sitting beside it on a phone.
 *
 * No background behind either one: the drawings are black line on cream and
 * the card is already white, so a tinted chip would box them in. */
 * Width and height differ on purpose. A square box punishes a wide scene:
 * the tug-of-war render is 1966x1025, so in a 72x72 slot it drew 72 wide by
 * 38 tall and wasted half its space. Separate dimensions let each scene use
 * whichever one it needs. */
.topic-art { flex-shrink: 0; width: 120px; height: 96px; object-fit: contain; }

/* The collection header stacks: a small icon, then the heading under it.
   It ran as a row for a while with the art at 170px beside the title, which
   gave the drawing more weight than the heading it was introducing. Above
   and small reads as a mark on a page rather than a picture with a caption. */
.collection-head-text { min-width: 0; }
/* The mark sits inline at the end of the heading, after the last word.
   Sized in em so it tracks the heading's own clamp instead of needing its
   own breakpoints, and width:auto so a wide drawing (the tug rope is 1744 x
   767) stays in proportion rather than being squeezed into a square.
   vertical-align sits it on the baseline by eye - these are drawings with
   no consistent optical centre, so a keyword lands differently per file. */

/* The room scene, beside the heading rather than above it. One drawing shared
   by every collection: it is the house the whole site is written from, not an
   illustration of any one subject, so it does not need to vary per page - and
   the per-collection mark is already doing that job above the title. */
.collection-scene { display: none; }
@media (min-width: 901px) {
  .collection-head { display: grid; grid-template-columns: 1fr 0.78fr;
                     gap: 40px; align-items: center; }
  /* aspect-ratio comes from the file itself, inline, so a cropped or
     landscape scene fills its box instead of being letterboxed inside a
     square it does not have. */
  .collection-scene { display: block; width: 100%; max-width: 380px;
                      justify-self: end;
                      background-repeat: no-repeat; background-position: center;
                      background-size: contain; }
}

@media (max-width: 640px) {
  .topic-art { width: 96px; height: 76px; }
  }

.topiccard:hover { background: var(--cream-2); }
.topiccard:hover .topic-icon { filter: brightness(0.96); }
.pin:hover .pin-img { filter: brightness(1.04); }
.hero-cta:hover { text-decoration: underline; text-underline-offset: 3px; }
.quickbanner-link:hover { background: var(--ink-2); }

/* The in-place signup confirmation. Replaces the form where it stood, so a
   reader who signs up mid-article keeps their place instead of being moved to
   a thank-you page for typing one field. */
/* The panel drops its grid when it is answered - the preview and the copy
   column have both done their job - and the confirmation is centred in the
   space they were using. More specific than the .hp-signup / .signup-block
   rules it overrides, so no !important. */
.hp-signup.is-signed-up,
.signup-block.is-signed-up,
.modal-card.is-signed-up { display: block; text-align: center; }

.signup-done { max-width: 440px; margin: 0 auto; }
/* 230px -> 340px. This is a terminal state - the form is gone and there is
   nothing else on screen competing for attention - sitting inside a block
   that runs the full width of the page, so the old size read as a thumbnail
   marooned in a large empty panel. The source is 1907px wide, so there is
   nothing to upscale. Capped again inside the modal below, where the card is
   only 380px and its proportions are tuned to a reference. */
.signup-done-art { width: 100%; max-width: 340px; margin: 0 auto 18px;
                   aspect-ratio: 1907 / 800;
                   background: url('../images/thanks-mailtruck.png')
                               no-repeat center / contain; }
/* The modal keeps the size its anatomy was drawn around; 340px would fill
   almost the whole 380px card. */
.modal-card .signup-done-art { max-width: 230px; }
.signup-done-h { margin: 0; font-family: var(--font-title);
                 font-size: clamp(24px, 3.4vw, 32px); line-height: 1.2;
                 color: var(--ink); }
.signup-done-p { margin: 12px 0 0; font-size: 15px; line-height: 1.6;
                 color: var(--muted); }
.signup-done-fine { margin: 12px 0 0; font-size: 13px; line-height: 1.6;
                    color: var(--muted); }
.signup-done-fine a { color: var(--ink); font-weight: 600; }

@media (max-width: 640px) {
  .signup-done-art { max-width: 260px; }
}

/* ---- Lead-capture modal ---------------------------------------------- *
 * Anatomy follows the reference Austin sent: illustration, display-font
 * headline, one line of body, one action. The differences from that
 * reference are deliberate - it was a coach-mark pointing at an app control,
 * so the pointer arrow is gone and the card is centred, and the single
 * acknowledge button becomes a field plus a submit because this one has a
 * job to do.
 *
 * Everything here is class-based: the modal markup carries no style
 * attributes, so it adds nothing to the inline-style ceiling build.py
 * enforces.
 *
 * .modal-art is a background-image rather than an <img> so the illustration
 * can be swapped from CSS alone, and so a missing file degrades to empty
 * space rather than a broken-image glyph in the middle of the card. */

/* The hidden attribute alone does NOT hide this. [hidden]{display:none} lives
   in the UA stylesheet, and ANY author display declaration outranks it - so
   `display: flex` below left the scrim rendered at all times: invisible, but
   fixed across the whole viewport at z-index 60, swallowing every click on the
   site. This rule is what actually hides it, and it has to stay above the
   display:flex it is overriding. */
.modal-scrim[hidden] { display: none; }

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(31, 30, 28, 0);
  transition: background 220ms ease;
}

.modal-scrim.is-open { background: rgba(31, 30, 28, 0.55); }

.modal-card {
  position: relative;
  width: 100%;
  max-width: 380px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  box-sizing: border-box;
  padding: 28px 28px 22px;
  text-align: center;
  background: var(--cream);
  border-radius: 2px;
  box-shadow: 0 24px 60px rgba(31, 30, 28, 0.28);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 220ms ease, transform 220ms ease;
}

.modal-scrim.is-open .modal-card { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .modal-scrim, .modal-card { transition: none; }
  .modal-card { transform: none; }
}

.modal-close {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  color: var(--muted);
  background: none;
  border: none;
  border-radius: 2px;
  cursor: pointer;
}

.modal-close:hover { color: var(--ink); background: var(--cream-2); }

.modal-art {
  width: 100%;
  height: 150px;
  margin: 4px 0 14px;
  background: url('../images/dog-pdf.png') no-repeat center bottom / contain;
}

.modal-title {
  margin: 0;
  font-size: 25px;
  line-height: 1.2;
}

.modal-blurb {
  margin: 12px 0 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

.modal-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 20px;
  text-align: left;
}

.modal-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
}

.modal-field {
  box-sizing: border-box;
  padding: 13px 16px;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
  background: #FFFFFF;
  border: 1px solid var(--line);
  border-radius: 2px;
}

.modal-submit {
  padding: 14px 22px;
  font-size: 15px;
  font-weight: 600;
  color: var(--cream);
  background: var(--accent);
  border: none;
  border-radius: 2px;
  cursor: pointer;
}

.modal-fine {
  font-size: 12px;
  color: var(--muted);
}

.modal-dismiss {
  margin-top: 14px;
  padding: 4px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--muted);
  text-decoration: underline;
  background: none;
  border: none;
  cursor: pointer;
}

.modal-dismiss:hover { color: var(--ink); }

/* Scroll lock. overflow:hidden alone lets iOS Safari scroll the page behind
   the card, but position:fixed there loses the reader's place on close, so
   the lock stays conservative and the scrim catches the touch instead. */
.modal-locked { overflow: hidden; }

@media (max-width: 480px) {
  .modal-scrim { padding: 16px; align-items: flex-end; }
  .modal-card { padding: 24px 20px 18px; }
  .modal-art { height: 112px; }
  .modal-title { font-size: 22px; }
}

/* Brevo's honeypot. Their markup carries display:none in a style attribute;
   _modal.html uses the class alone so the modal stays free of inline styles,
   which means the rule has to exist here or the trap field renders as a
   visible empty box under the fine print. */
.input--hidden { display: none !important; }

/* Homepage hero illustration. A decorative div rather than an <img> for the
   same reason the footer band is: the art is a single ink colour knocked out
   to transparency, so it takes whatever sits behind it, and a missing file
   degrades to empty space instead of a broken-image glyph under the headline.
   aspect-ratio holds the space before the PNG arrives, so the buttons above
   it do not jump when it loads. */
.hp-hero-art {
  width: 100%;
  /* Capped and centred, same treatment as the 404 illustration. At full
     container width this is 627px tall, which pushed the second row of dogs
     and the whole featured grid below the fold on a laptop. */
  max-width: 720px;
  margin: 34px auto 0;
  aspect-ratio: 2516 / 1641;
  background: url('../images/hero-dogs.png') no-repeat center bottom / contain;
}

@media (max-width: 768px) {
  .hp-hero-art { margin-top: 22px; }
}

/* Thank-you page illustration. Same decorative-div treatment as the hero
   band and the footer: single ink colour knocked out to transparency, and a
   missing file degrades to empty space rather than a broken-image glyph
   above the headline. */
.thanks-art {
  width: 100%;
  max-width: 420px;
  margin: 0 auto 26px;
  aspect-ratio: 1907 / 800;
  background: url('../images/thanks-mailtruck.png') no-repeat center / contain;
}

@media (max-width: 640px) {
  .thanks-art { max-width: 300px; margin-bottom: 18px; }
}

/* Slide-in nav illustration. margin-top:auto moves off .menu-cta and onto
   this, so the pair get pushed to the bottom of the panel together and the
   art sits directly above the rule that tops the CTA. The panel is 320px
   wide with 24px padding, so the art is drawn into 272px. */
.menu-art {
  width: 100%;
  aspect-ratio: 1791 / 1227;
  margin-top: auto;
  background: url('../images/nav-dogs.png') no-repeat center bottom / contain;
}

/* Short phones: the panel scrolls, and the art is the first thing that
   should give up its space rather than pushing the CTA out of reach. */
@media (max-height: 640px) {
  .menu-art { display: none; }
}

/* ---- Contact page ------------------------------------------------------ */

.contact-shell { max-width: 620px; margin: 0 auto; }
.contact-lede { font-size: 17px; line-height: 1.65; color: var(--muted);
                margin: 22px 0 0; }

.contact-form { margin-top: 30px; display: flex; flex-direction: column;
                gap: 18px; }

/* The honeypot. Netlify matches it by name; a human must never see or tab
   into it, and display:none is not enough on its own because some password
   managers still fill hidden inputs. */
.contact-hp { position: absolute; left: -9999px; top: 0; width: 1px;
              height: 1px; overflow: hidden; margin: 0; }

.contact-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 560px) { .contact-row { grid-template-columns: 1fr; } }

.contact-field { display: flex; flex-direction: column; gap: 7px; }
.contact-label { font-family: var(--font-body); font-weight: 600;
                 font-size: 14px; color: var(--ink); }

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form select,
.contact-form textarea {
  font-family: var(--font-body); font-size: 16px; color: var(--ink);
  background: var(--cream); border: 1.5px solid var(--line);
  border-radius: 2px; padding: 11px 13px; width: 100%;
  -webkit-appearance: none; appearance: none;
}
.contact-form select {
  /* The arrow is drawn here because appearance:none removes the native one,
     and the native one does not match anything else on the site. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231F1E1C' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center;
  padding-right: 36px;
}
.contact-form textarea { resize: vertical; min-height: 150px;
                         line-height: 1.6; }
.contact-form input:focus-visible,
.contact-form select:focus-visible,
.contact-form textarea:focus-visible {
  outline: none; border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(31,30,28,0.12);
}

.contact-submit { align-self: flex-start; border: 0; cursor: pointer; }
.contact-fineprint { font-size: 13px; line-height: 1.55; color: var(--muted);
                     margin: 0; }
.contact-alt { font-size: 15px; line-height: 1.6; color: var(--muted);
               margin: 28px 0 0; padding-top: 22px;
               border-top: 1px solid var(--line); }
.contact-alt a { color: var(--ink); font-weight: 600; }

/* ---- after sending ----------------------------------------------------- */

.contact-shell.is-sent { text-align: center; }
.contact-done { max-width: 460px; margin: 0 auto; }
.contact-done-art {
  width: 100%; max-width: 330px; margin: 10px auto 18px;
  /* Ratio is the file's own, so the block reserves its space before the
     image loads. If the art is ever replaced, change this in the same
     commit - .hp-hero-art went stale three times doing exactly this. */
  aspect-ratio: 1810 / 869;
  background: url('../images/contact-mail-dog.png') no-repeat center / contain;
}
.contact-done-h { font-family: var(--font-display); font-weight: 400;
                  font-size: clamp(26px, 6vw, 34px); line-height: 1.2;
                  margin: 0; color: var(--ink); }
.contact-done-p { font-size: 16px; line-height: 1.65; color: var(--muted);
                  margin: 16px 0 26px; }

/* About page: the real photo and the "who we are" copy, side by side at
   the foot of the page. The photo is fixed-width rather than a grid
   column so it keeps its 4:5 shape whatever the text does. */
.about-us { display: flex; gap: 26px; align-items: flex-start; margin: 26px 0 8px; }
.about-us-photo { flex: 0 0 220px; aspect-ratio: 4/5; border-radius: 2px;
                  overflow: hidden; border: 1px solid var(--line); }
.about-us-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about-us-text > p:first-child { margin-top: 0; }
@media (max-width: 560px) {
  .about-us { flex-direction: column; gap: 18px; }
  .about-us-photo { flex: 0 0 auto; width: 100%; max-width: 260px; }
}

/* ---- Printables page --------------------------------------------------- */

/* Eyebrow labels across the site carry the one accent - see the :root note
   in style.css. */
.pt-kicker { display: block; font-size: 11px; font-weight: 700;
             letter-spacing: 0.08em; color: var(--accent); }
.pt-meta { display: block; font-size: 12px; color: var(--muted);
           margin-top: 12px; }

/* The featured bundle. Its cover is three real covers fanned, because the
   kit IS those three files - there is no fourth artwork to draw, and if any
   of the three is redrawn this picture updates with it. */
.pt-hero { margin: 34px 0 0; padding: 30px; background: var(--cream-2);
           border: 1px solid var(--line); border-radius: 2px;
           display: grid; grid-template-columns: 260px 1fr; gap: 34px;
           align-items: center; }
.pt-hero-shots { position: relative; height: 215px; }
.pt-fan { position: absolute; top: 0; width: 150px; height: auto;
          border: 1px solid var(--line); border-radius: 2px;
          box-shadow: 0 6px 18px rgba(31,30,28,0.14); background: var(--cream); }
.pt-fan:nth-child(1) { left: 0;    transform: rotate(-7deg);  z-index: 1; }
.pt-fan:nth-child(2) { left: 46px; transform: rotate(1.5deg); z-index: 2; }
.pt-fan:nth-child(3) { left: 92px; transform: rotate(8deg);   z-index: 3; }

/* Second cell of .pt-hero's grid. min-width:0 because a grid item defaults
   to min-content width, so one long unbroken word in the blurb would widen
   the column past the card instead of wrapping. */
.pt-hero-copy { min-width: 0; }
.pt-hero-title { font-family: var(--font-display); font-weight: 400;
                 font-size: clamp(24px, 4.6vw, 31px); line-height: 1.18;
                 color: var(--ink); margin: 8px 0 0; }
.pt-hero-blurb { font-size: 16px; line-height: 1.6; color: var(--muted);
                 margin: 12px 0 0; max-width: 46ch; }

.pt-sep { font-family: var(--font-display); font-weight: 400; font-size: 24px;
          color: var(--ink); margin: 48px 0 0; padding-bottom: 12px;
          border-bottom: 1px solid var(--line); }

.pt-grid { margin-top: 26px; display: grid; gap: 26px;
           grid-template-columns: repeat(2, minmax(0, 1fr)); }

.pt-card { display: flex; flex-direction: column;
           border: 1px solid var(--line); border-radius: 2px;
           background: var(--cream); overflow: hidden; }
.pt-shot { padding: 22px 22px 0; background: var(--cream-2); }
.pt-cover { display: block; width: 100%; height: auto;
            border: 1px solid var(--line); border-top-left-radius: 2px;
            border-top-right-radius: 2px; border-bottom: 0;
            box-shadow: 0 -2px 14px rgba(31,30,28,0.08); background: var(--cream); }
.pt-body { padding: 20px 22px 24px; display: flex; flex-direction: column;
           flex: 1; }
.pt-title { font-family: var(--font-body); font-weight: 600; font-size: 18px;
            line-height: 1.3; color: var(--ink); margin: 7px 0 0; }
.pt-blurb { font-size: 14.5px; line-height: 1.6; color: var(--muted);
            margin: 9px 0 0; }

/* The form sits at the foot of the card whatever the blurb's length, so a
   row of cards has its fields on one line. */
.pt-form { margin-top: auto; padding-top: 16px; display: flex; gap: 8px;
           flex-wrap: wrap; }
.pt-field { flex: 1 1 150px; min-width: 0; font-family: var(--font-body);
            font-size: 15px; color: var(--ink); background: var(--cream);
            border: 1.5px solid var(--line); border-radius: 2px;
            padding: 10px 12px; -webkit-appearance: none; appearance: none; }
.pt-field:focus-visible { outline: none; border-color: var(--ink);
                          box-shadow: 0 0 0 3px rgba(31,30,28,0.12); }
/* .subbtn on its own is only cursor:pointer - every other button on the
   site pairs it with a class that supplies the fill (.sg-submit,
   .modal-submit, .callout-printable .subbtn). This one did not, so the
   primary call to action on the page rendered as bare text on the panel.
   Sized to match .pt-field's height so the two sit level in the row. */
.pt-submit { flex: 0 0 auto; border: 0; cursor: pointer; white-space: nowrap;
             padding: 11px 20px; background: var(--accent); color: var(--cream);
             font-family: var(--font-body); font-size: 15px; font-weight: 600;
             border-radius: 2px; }
.pt-form.is-hero { margin-top: 20px; max-width: 460px; }

.pt-foot { font-size: 14px; line-height: 1.65; color: var(--muted);
           margin: 34px 0 0; padding-top: 22px;
           border-top: 1px solid var(--line); }

@media (max-width: 760px) {
  .pt-grid { grid-template-columns: 1fr; }
  .pt-hero { grid-template-columns: 1fr; gap: 24px; padding: 24px; }
  /* Fanned on a phone the three covers either overflow or shrink to
     thumbnails, so the kit shows one cover at a readable size instead. */
  .pt-hero-shots { height: auto; max-width: 210px; }
  .pt-fan { position: static; transform: none; width: 100%;
            box-shadow: 0 6px 18px rgba(31,30,28,0.14); }
  .pt-fan:nth-child(2), .pt-fan:nth-child(3) { display: none; }
}

/* ---- shared page head -------------------------------------------------

   The breadcrumb / h1 / lede trio at the top of a content page. This was
   five inline style attributes repeated verbatim in library.md and again in
   printables.md, which is ten of the inline-style budget spent on one
   pattern - and an inline style outranks this stylesheet, so none of it
   could be themed or fixed from here. */
.page-crumb { display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
              font-size: 13px; color: var(--muted); margin-bottom: 18px; }
.page-crumb a { color: var(--muted); }
.page-crumb .is-here { color: var(--ink-2); }
.page-h1 { font-family: var(--font-display); font-size: clamp(28px, 7vw, 44px);
           line-height: 1.15; letter-spacing: -0.01em; margin: 0; }
.page-lede { font-size: 17px; line-height: 1.55; color: var(--muted);
             margin: 14px 0 0; max-width: 620px; }
