@font-face{font-family:'Fraunces';src:url('fraunces.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap;}
/* ==========================================================================
   Recipe site theme
   --------------------------------------------------------------------------
   Written for the markup in static_site/render.py, not adapted from a mockup.
   The earlier stylesheet was lifted from prototypes in which every photograph
   was a CSS gradient block, so it contained no rule for <img> and real files
   spilled out of the layout. Every selector below corresponds to markup the
   renderer emits; tools/check_css.py asserts that in both directions.

   Order: tokens, reset, type, layout, chrome, index, article, recipe card,
   sidebar, ads, footer, states, print.

   Theme: one light design, committed to deliberately — a recipe site is read
   in a kitchen and photographed food sits better on white. There is no dark
   variant, so every colour is painted explicitly and the page looks the same
   whatever the reader's system is set to. The footer keeps its dark band.
   ========================================================================== */

:root{
  /* ground and surfaces */
  --bg:#ffffff;
  --bg-alt:#faf8f6;
  --surface:#ffffff;
  --sunk:#f7f5f2;

  /* text */
  --ink:#191a1c;
  --ink-2:#55565e;
  /* Was #8b8c95, which is 3.34:1 on white and 3.00 on the sunk panel — under
     the 4.5:1 that small text needs, and this is the colour the dates, the
     counts and the captions are set in. #6b6c76 is 4.90 and 4.40; the panel is
     lightened below so the pairing clears it there too. */
  --ink-3:#6b6c76;

  /* lines */
  --line:#e7e3de;
  --line-2:#f2efeb;

  /* accent */
  --accent:#cf4a12;
  --accent-hover:#a93a0c;
  --accent-soft:#fdf5f1;
  /* The accent as words on that wash. Same colour for most brands;
     darker for the pale ones, which cannot be read at their own value. */
  --accent-ink:#a93a0c;
  --on-accent:#ffffff;

  /* the footer keeps one dark identity in both themes */
  --deep:#221d1b;
  --deep-2:#191413;
  --deep-ink:#f2ede9;
  --deep-ink-2:#b0a69f;
  --deep-line:#39312e;

  /* elevation */
  --shadow-1:0 1px 2px rgba(25,26,28,.05);
  --shadow-2:0 1px 2px rgba(25,26,28,.05), 0 10px 28px -18px rgba(25,26,28,.32);
  --shadow-3:0 2px 6px rgba(25,26,28,.07), 0 20px 44px -22px rgba(25,26,28,.4);

  /* type */
  /* Three roles, not two. --display is the headline and the masthead, where a
     site either has a voice or reads like a system dialog; --sans is the
     navigation, the labels and the chips, where the reader's own interface font
     is not a compromise but the right answer at ten pixels in uppercase. A site
     that sets no display font gets --sans here and looks exactly as it did. */
  --display:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --serif:Charter,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;

  /* rhythm */
  --gap-1:6px;  --gap-2:10px; --gap-3:16px; --gap-4:24px;
  --gap-5:36px; --gap-6:52px; --gap-7:72px;

  --r-1:6px; --r-2:10px; --r-3:14px; --r-4:20px; --r-pill:999px;

  /* Two widths, because two different jobs.
     --wrap is the page: the masthead, the footer and the galleries, which are
     grids of photographs and get better the more of the screen they have. On a
     1710px monitor the old single width used 69% of it and left 265 blank
     pixels down each side.
     --wrap-read is the article, which does not. Running text past about
     seventy characters is text a reader loses their place in, and the article
     page is a column of prose beside a sidebar — it keeps the proportions it
     had. */
  --wrap:1400px;      /* page chrome and galleries */
  --wrap-read:1180px; /* the article page, beside its sidebar */
  --read:68ch;        /* comfortable measure for running text */
}

/* ── reset ───────────────────────────────────────────────── */

*,*::before,*::after{ box-sizing:border-box; }
html{
  -webkit-text-size-adjust:100%; scroll-behavior:smooth;
  /* The colour of anything that is not the page: the rubber-band overscroll at
     the bottom of a phone, and any rounding left under the last element. The
     page ends in a dark footer, so that space reading as more footer is right
     — where white reads as the site having broken off, which is exactly what
     it looked like. */
  background:var(--deep);
}
body{
  margin:0;
  /* Tall enough to fill the window even when the page is short, so the footer
     sits at the bottom instead of halfway down with white below it. A sitemap
     or a contact page is legitimately shorter than a phone screen, and that
     empty band under a dark footer reads as the site having broken off. */
  min-height:100vh;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--serif);
  font-size:18px;
  line-height:1.72;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img,svg{ max-width:100%; }
button{ font:inherit; color:inherit; }
a{ color:var(--accent); text-decoration-thickness:1px; text-underline-offset:2px; }

/* ── type ────────────────────────────────────────────────── */

/* h1 and h2 carry the voice; h3 and h4 are furniture and stay on the UI face.
   The tracking is per-role too: a display face is drawn with its spacing already
   right and tightening it further is what makes a headline look squeezed, so the
   negative tracking below applies only where the system stack is doing the job. */
h1,h2{ font-family:var(--display); text-wrap:balance; margin:0; }
h3,h4{ font-family:var(--sans); text-wrap:balance; margin:0; }
h1{ font-size:clamp(33px,5.2vw,54px); line-height:1.04; letter-spacing:-.02em; font-weight:700; }
h2{ font-size:clamp(25px,3.2vw,32px); line-height:1.14; letter-spacing:-.016em; font-weight:700; }
h3{ font-size:19px; line-height:1.3; letter-spacing:-.016em; font-weight:700; }
p{ margin:0 0 var(--gap-3); }
strong{ font-weight:640; }
.skip{
  position:absolute; left:-9999px; top:0; z-index:100;
  background:var(--accent); color:var(--on-accent);
  padding:12px 20px; border-radius:0 0 var(--r-2) 0; font-family:var(--sans); font-weight:650;
}
.skip:focus{ left:0; }

/* ── layout ──────────────────────────────────────────────── */

/* The body is a column (see above). Whatever holds the page's own content is
   what stretches; the masthead and the footer keep their height. The sticky
   bar is not listed: it is position:fixed, so it is not a flex item at all. */
.page,.shell{ flex:1 0 auto; }
.masthead,.foot{ flex:none; }

.page{ max-width:var(--wrap); margin:0 auto; padding:0 22px; }
.shell{
  max-width:var(--wrap-read); margin:0 auto; padding:0 22px;
  display:grid; grid-template-columns:minmax(0,1fr) 332px;
  gap:var(--gap-7); align-items:start;
}
@media (max-width:1060px){ .shell{ grid-template-columns:1fr; gap:0; } }
.main{ min-width:0; max-width:var(--read); }
section{ margin:0 0 var(--gap-6); }

/* ── frames: the only place a real photograph lives ──────── */

.frame{
  position:relative; overflow:hidden; border-radius:var(--r-3);
  background:var(--sunk); display:block;
}
.frame img{ display:block; width:100%; height:100%; object-fit:cover; }
/* The shapes are a site setting, not a constant. Photographs are shot to
   whatever ratio the site works in — 4:5 out of Midjourney, say — and a frame
   built for 3:2 crops away half of a portrait picture without saying so. The
   renderer writes these variables from site.json; the fallbacks here are only
   what applies when a site has not chosen. */
.frame.hero{ aspect-ratio:var(--ar-hero,3/2); margin:0 0 var(--gap-2); }
.frame.body{ aspect-ratio:var(--ar-body,3/2); margin:var(--gap-4) 0 var(--gap-2); }
.frame.card{ aspect-ratio:var(--ar-card,4/3); }
.frame.square{ aspect-ratio:var(--ar-tile,1/1); }
.frame.portrait{ aspect-ratio:var(--ar-author,4/3.2); border-radius:0; }
/* No crop at all: the photograph keeps the shape it was saved in. Wins over the
   rules above, which is why it carries a class of its own rather than a ratio —
   there is no ratio to give when the answer is "whatever the file says". */
.frame.free{ aspect-ratio:auto; }
.frame.free img{ height:auto; object-fit:contain; }

/* ── the labelled ingredients picture ────────────────────────
   Not a photograph of the dish but a reference panel: everything measured out,
   with the name written beside each one, sitting directly above the ingredient
   list. A reader uses it the way they use the list itself — checking whether
   they have the things — so it is framed as part of the list rather than as
   another picture in the gallery.

   Narrower than a body photograph on purpose. A full-width tray on a desktop
   pushes the ingredient list it belongs to below the fold, which puts the
   answer and the question on different screens. */
.frame.ingredients{
  max-width:520px;
  margin:var(--gap-4) auto var(--gap-5);
  background:var(--bg-alt);
  border:1px solid var(--line);
  padding:10px;
  border-radius:var(--r-3);
}
.frame.ingredients img{ border-radius:var(--r-2); }

/* ── the author's photograph on the About page ───────────── */
.abouthero{ margin:0 0 var(--gap-4); max-width:340px; }
.abouthero img{ display:block; width:100%; height:auto; border-radius:var(--r-3);
  background:var(--sunk); }
@media (min-width:760px){
  /* Beside the opening paragraphs rather than above them: on a wide screen a
     full-width portrait pushes every word of the page below the fold. */
  .abouthero{ float:right; margin:4px 0 var(--gap-4) var(--gap-5); max-width:300px; }
}

/* ── a logo that is a picture rather than the site's name ── */
/*
   Height is the size that was asked for; width is what it takes to honour it.
   Those two fought, and width won.

   A wordmark is wide — the name written out is often ten or more times as wide
   as it is tall. At the 44px height set here, one of those wants around 550px
   of width, and a 230px cap does not clip it: the image keeps its proportions,
   so capping the width drags the height down with it. The logo was arriving at
   about seventeen pixels tall, far below what anybody chose, and reading as a
   smudge in the corner.

   So the cap is now wide enough that even a long name gets the height it was
   given, and it is written in vw as well as px so it gives way on a phone
   rather than crowding the navigation off the bar.
*/
.logo.mark{ padding:8px 0; min-width:0; }
.logo.mark img{ display:block; width:auto; height:var(--logo-h,44px);
  max-width:min(520px, 52vw); object-fit:contain; }

/* ── the subscribe box ───────────────────────────────────── */
/*
   Sits at the foot of an article, where a reader who has finished one is the
   likeliest to want the next. Quiet: a card on the page's own surface with the
   accent spent on the one button, rather than a coloured slab that reads as an
   advert and gets scrolled past.
*/
.subbox{
  margin:var(--gap-5) 0 0; padding:var(--gap-4);
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-4); box-shadow:var(--shadow-1);
}
/* The lift, added once when it scrolls into view. The box is already visible
   without it — this is a nudge, not the thing that reveals it, so JavaScript
   failing costs the movement and nothing else. */
.subbox.seen{ animation:subrise .5s ease both; }
@keyframes subrise{
  from{ transform:translateY(10px); box-shadow:none; }
  to{ transform:none; box-shadow:var(--shadow-2); }
}
@media (prefers-reduced-motion:reduce){ .subbox.seen{ animation:none; } }
.subt{ margin:0 0 6px; font-family:var(--display); font-size:24px;
  line-height:1.2; color:var(--ink); text-wrap:balance; }
.subd{ margin:0 0 var(--gap-3); font-family:var(--sans); font-size:15px;
  color:var(--ink-2); max-width:52ch; }
.subform{ display:flex; flex-wrap:wrap; gap:10px; align-items:stretch; }
.subin{
  flex:1 1 260px; min-width:0; padding:0 14px; height:48px;
  font-family:var(--sans); font-size:16px;   /* 16px: any less and iOS zooms */
  color:var(--ink); background:var(--bg);
  border:1px solid var(--line); border-radius:var(--r-2);
}
.subin:focus{ outline:2px solid var(--accent); outline-offset:1px;
  border-color:var(--accent); }
/* The label is written in --on-accent, which is the token for words that sit
   ON the accent. Not --accent-ink: that one is the accent written as words on
   a pale wash — a darker orange — and on the accent itself it is dark orange
   on orange at 1.41:1. The button came out with no label at all. */
.subbtn{
  flex:0 0 auto; height:48px; padding:0 26px; cursor:pointer;
  font-family:var(--sans); font-size:14px; font-weight:750;
  letter-spacing:.06em; text-transform:uppercase;
  color:var(--on-accent); background:var(--accent);
  border:1px solid var(--accent); border-radius:var(--r-2);
  transition:filter .15s ease;
}
.subbtn:hover:not(:disabled){ filter:brightness(1.07); }
.subbtn:disabled{ opacity:.6; cursor:default; }
.subn{ margin:var(--gap-2) 0 0; font-family:var(--sans); font-size:12.5px;
  color:var(--ink-3); }
.submsg{ margin:var(--gap-2) 0 0; font-family:var(--sans); font-size:15px;
  font-weight:650; }
.submsg.ok{ color:var(--accent); }
.submsg.bad{ color:#b3261e; }
/* The honeypot: away from the page rather than display:none, which is the one
   thing a form-filling script does check. */
.subpot{ position:absolute; left:-9999px; width:1px; height:1px;
  overflow:hidden; }
/* The label is there for a screen reader; the placeholder is for everyone
   else. A placeholder is not a label — it goes the moment you type. */
.subsr{ position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
@media (prefers-reduced-motion:reduce){ .subbtn{ transition:none; } }

/* ── Pin it, in the corner of a photograph ───────────────── */
/*
   Always there rather than on hover. Hover does not exist on a phone, which is
   where most of this traffic is, and a button that appears only for people
   with a mouse is a button most readers never see. It is small and sits in the
   corner; the photograph is still the photograph.

   Pinterest's own red, because this is their button and a reader recognises it
   before they read it.
*/
.pinit{
  position:absolute; top:12px; right:12px; z-index:3;
  display:inline-flex; align-items:center; gap:7px;
  min-height:40px; padding:0 15px 0 13px;
  background:#e60023; color:#fff; text-decoration:none;
  font-family:var(--sans); font-size:14px; font-weight:700; line-height:1;
  border-radius:var(--r-pill); box-shadow:0 2px 10px rgba(0,0,0,.28);
  transition:transform .15s ease, background .15s ease;
}
.pinit:hover,.pinit:focus-visible{ background:#c5001d; transform:scale(1.04); }
.pinit svg{ flex:none; }
@media (max-width:560px){
  /* The label goes and the glyph stays: at this width the words are most of
     the button, and most of the button is a lot of a photograph. */
  .pinit{ padding:0; width:40px; justify-content:center; }
  .pinit span{ display:none; }
}
@media (prefers-reduced-motion:reduce){ .pinit{ transition:none; } }

/* ── masthead ────────────────────────────────────────────── */

.masthead{
  background:var(--surface); border-bottom:1px solid var(--line);
  /* Stays at the top of the window while the page moves under it. -webkit- is
     still needed for older Safari on iOS, where the unprefixed value is simply
     ignored and the bar scrolls away. */
  position:-webkit-sticky; position:sticky; top:0; z-index:40;
  transition:box-shadow .2s ease;
}
/* A shadow only once the page has moved, so the bar reads as sitting above the
   content rather than being part of it. */
.masthead.lifted{ box-shadow:0 2px 10px -6px rgba(25,26,28,.45); }
/* The site asked for a header that scrolls away with the page. */
.masthead.loose{ position:static; box-shadow:none; }
.mh{
  max-width:var(--wrap); margin:0 auto; padding:14px 22px;
  display:flex; align-items:center; gap:var(--gap-4); font-family:var(--sans);
}
.logo{ text-decoration:none; color:var(--ink); display:flex; align-items:baseline;
  gap:2px; white-space:nowrap; margin-right:auto; }
.logo .b,.logo .p{ font-family:var(--display); font-size:28px;
  font-weight:600; letter-spacing:-.015em; }
.logo .p{ color:var(--accent); }
.logo .sub{ font-family:var(--sans); font-size:9.5px; font-weight:750;
  letter-spacing:.22em; text-transform:uppercase; color:var(--ink-3); margin-left:6px; }
.mnav{ display:flex; align-items:center; }
.mnav a{ color:var(--ink); text-decoration:none; font-size:15px; font-weight:620;
  padding:0 18px; border-right:1px solid var(--line); white-space:nowrap; }
.mnav a:last-child{ border-right:none; }
.mnav a:hover{ color:var(--accent); }
/* On a narrow screen the bar becomes a scrolling row rather than disappearing.
   It used to be display:none below 960px with nothing in its place, so a phone
   — where most of the traffic is — had no navigation at all. Six categories fit
   a swipe; the seventh is the All Categories link at the end of the row. */
@media (max-width:960px){
  .mh{ flex-wrap:wrap; row-gap:8px; }
  .mnav{
    order:3; width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch;
    scrollbar-width:none; padding-bottom:2px;
  }
  .mnav::-webkit-scrollbar{ display:none; }
  .mnav a{
    font-size:14px; padding:6px 13px; border-right:none;
    border:1px solid var(--line); border-radius:999px; margin-right:7px;
  }
  .mnav a:last-child{ margin-right:0; }
  .mnav a.more-cats{ border-color:var(--accent); color:var(--accent); }
  /* The navigation has moved to its own row, so the name is no longer sharing
     a line with it and can have the width it needs. 52vw was written for a
     bar where all three sat side by side. */
  .logo.mark img{ max-width:calc(100vw - 120px); }
}
/* ── the bar on a phone ───────────────────────────────────
   Two rows on a screen this size is a lot of a window to spend before the
   article starts, and it is spent on every scroll because the bar is sticky.
   So the padding comes in, the rows tighten, and the name gets the rest.
*/
@media (max-width:560px){
  .mh{ padding:9px 14px; gap:10px; row-gap:6px; }
  .logo .b,.logo .p{ font-size:23px; }
  .logo .sub{ display:none; }        /* a kicker at 9.5px is texture, not words */
  .logo.mark img{ height:var(--logo-h-sm,34px); max-width:calc(100vw - 104px); }
  .sbtn{ width:38px; height:38px; margin-left:var(--gap-2); }
  .mnav a{ font-size:13.5px; padding:5px 11px; margin-right:6px; }
}
.sbtn{
  width:42px; height:42px; flex:none; border:none; border-radius:50%;
  background:var(--accent); color:var(--on-accent); cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  margin-left:var(--gap-3); transition:background .15s;
}
.sbtn:hover{ background:var(--accent-hover); }
.sdraw{ display:none; background:var(--surface); border-bottom:1px solid var(--line); padding:14px 22px; }
.sdraw.on{ display:block; }
.sdraw input{
  display:block; width:100%; max-width:var(--wrap); margin:0 auto;
  font-family:var(--sans); font-size:17px; padding:13px 16px;
  border:1px solid var(--line); border-radius:var(--r-2);
  background:var(--bg); color:var(--ink);
}

/* ── category pills ──────────────────────────────────────── */

.pills{ background:var(--surface); border-bottom:1px solid var(--line); }
.pillrow{
  max-width:var(--wrap); margin:0 auto; padding:12px 22px;
  display:flex; gap:9px; overflow-x:auto; scrollbar-width:none;
}
.pillrow::-webkit-scrollbar{ display:none; }
.pill{
  font-family:var(--sans); font-size:14px; font-weight:640; white-space:nowrap;
  background:var(--sunk); color:var(--ink); text-decoration:none;
  padding:9px 17px; border-radius:var(--r-2); transition:background .15s,color .15s;
}
.pill:hover{ background:var(--accent-soft); color:var(--accent-ink); }
.pill.on{ background:var(--accent); color:var(--on-accent); }

/* ── hero band ───────────────────────────────────────────── */

/* Less air above the first photograph.
   Measured on a live site: the first picture began 427px down an 870px
   screen, so half of what a reader saw first was a sentence on white. The
   headline is still the opening — this only stops it being the whole of it,
   and gives back more of the fold the narrower the screen gets. */
.hband{ text-align:center; padding:clamp(20px,4vw,var(--gap-6)) 0
        clamp(16px,3vw,var(--gap-5)); }
.hband h1{ margin:0 0 var(--gap-2); }
/* A line that says what the site is before the headline says it well. The
   headline is a sentence with a voice; this is the plain fact under it, and a
   reader landing from a search result wants the fact first. */
.eyebrow{
  display:inline-flex; align-items:center; gap:8px;
  margin:0 0 var(--gap-3); padding:6px 14px 6px 12px;
  font-family:var(--sans); font-size:12px; font-weight:750;
  letter-spacing:.10em; text-transform:uppercase;
  color:var(--ink); background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-pill);
}
.cdot{ width:7px; height:7px; border-radius:50%; background:var(--accent);
  flex:none; }

/* ── the category strip ──────────────────────────────────── */
/*
   These used to be square photographs in an auto-fit grid, and with two
   categories that came out as two tiles 560 pixels across: over a thousand
   pixels of page spent on the way IN to the patterns, before a single
   pattern. The photograph was never the point either — somebody reading
   "Blankets" does not need a picture of a blanket to know what it means.

   One line does the same job. It scrolls sideways on a phone rather than
   wrapping to four rows, which is the one place a strip beats a grid.
*/
.catbar{
  display:flex; align-items:center; gap:4px;
  margin:0 0 var(--gap-6); padding:8px;
  background:var(--deep); border-radius:var(--r-pill);
  overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch;
}
.catbar::-webkit-scrollbar{ display:none; }
.cchip{
  display:inline-flex; align-items:center; gap:8px; flex:none;
  padding:9px 16px; border-radius:var(--r-pill);
  font-family:var(--sans); font-size:14px; font-weight:600;
  color:var(--deep-ink); text-decoration:none; white-space:nowrap;
  transition:background .15s, color .15s;
}
.cchip:hover,.cchip:focus-visible{ background:rgba(255,255,255,.10); color:#fff; }
.cchip.more{ color:var(--accent); font-weight:700; }
@media (prefers-reduced-motion:reduce){ .cchip{ transition:none; } }
.tags{ font-family:var(--sans); font-size:17px; color:var(--ink-2);
  margin:0 auto; max-width:52ch; }

/* ── section heading ─────────────────────────────────────── */

.shead{ display:flex; align-items:center; gap:var(--gap-3); margin:0 0 var(--gap-4); }
.shead h2{ white-space:nowrap; }
.dots{ flex:1; min-width:20px; border-bottom:2px dotted var(--line); }
.arw{
  width:38px; height:38px; flex:none; border-radius:var(--r-2);
  border:1.5px solid var(--accent); color:var(--accent);
  display:flex; align-items:center; justify-content:center; text-decoration:none;
  transition:background .15s,color .15s;
}
.arw:hover{ background:var(--accent); color:var(--on-accent); }

/* ── category tiles ──────────────────────────────────────── */

/* auto-fit, so four categories spread across the row instead of bunching into
   the first four of six columns and leaving a gap. */
/* auto-fit with a 1fr ceiling means the tiles are sized by how many there
   are, not by what they are. A site with two categories gave them half a row
   each: two square tiles 560 pixels across, a pair of billboards that pushed
   the patterns themselves off the first screen entirely. A site with six got
   sensible ones. The same stylesheet, two completely different pages.

   A ceiling fixes it. Two categories are now two tiles the size a tile should
   be, and the row starts at the left rather than stretching to fill. */
.cgrid{
  display:grid; gap:var(--gap-3);
  grid-template-columns:repeat(auto-fit,minmax(160px,240px));
  justify-content:start;
}
@media (max-width:560px){
  /* Two to a row on a phone, and they may fill it — there is no width here to
     spend on a tile being smaller than the space it has. */
  .cgrid{ grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); }
}
.ccard{
  position:relative; display:block; text-decoration:none;
  border-radius:var(--r-3); overflow:hidden; box-shadow:var(--shadow-2);
  transition:transform .2s,box-shadow .2s;
}
.ccard:hover{ transform:translateY(-4px); box-shadow:var(--shadow-3); }
.ccard .frame{ border-radius:var(--r-3); }
.veil{
  position:absolute; inset:0; border-radius:var(--r-3);
  background:linear-gradient(to top,rgba(0,0,0,.76) 0%,rgba(0,0,0,.3) 44%,transparent 70%);
}
.nm{
  position:absolute; left:0; right:0; bottom:13px; padding:0 10px; text-align:center;
  font-family:var(--sans); font-size:16px; font-weight:750; letter-spacing:-.015em;
  color:#fff; text-shadow:0 1px 10px rgba(0,0,0,.6);
}

/* ── recipe grid ─────────────────────────────────────────── */

/* Sized by the card, not by a count. Four was written for one page width, so
   a wider page made every card bigger rather than showing another pattern —
   which is the opposite of what the width was for. This way 1180px still
   gives four, and a large monitor gives five or six. */
.rgrid{ display:grid; gap:var(--gap-5) var(--gap-4);
  grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); }
/* Two on a tablet and one on a phone, said outright: a card narrower than
   this stops being a photograph with a title and becomes a thumbnail. */
@media (max-width:1000px){ .rgrid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:520px){ .rgrid{ grid-template-columns:1fr; } }
.rc{ display:block; text-decoration:none; color:inherit; font-family:var(--sans); position:relative; }
.rc .shot{ box-shadow:var(--shadow-2); margin-bottom:var(--gap-2); }
.rc .frame img{ transition:transform .3s ease; }
.rc:hover .frame img{ transform:scale(1.045); }
/* Two lines of room whether or not the title needs them. Titles here run to
   one line or two — "Crochet Sunflower Pattern" against "Crochet Christmas
   Table Decor" — and a grid whose text starts at the same height but ends at
   different ones reads as a grid that has gone slightly wrong. */
.rc h3{ margin:0; transition:color .15s;
  min-height:calc(2 * 1.25em); }
.rc:hover h3{ color:var(--accent); }
.cat{
  /* 12px is the floor for text a reader is expected to read, and this is the
     only thing on a card that says what the recipe is. At 10.5 it was
     decoration. */
  font-family:var(--sans); font-size:12px; font-weight:700; letter-spacing:.10em;
  text-transform:uppercase; color:var(--accent); margin-bottom:5px;
}
.fav{
  /* 44px, which is the size a finger actually is. It was 34, and on a phone —
     where most of this traffic is — a save button that small is one that gets
     pressed by accident or not at all. The mark inside is unchanged; only the
     area you can hit grew. */
  position:absolute; top:8px; right:8px; z-index:2;
  width:44px; height:44px; border:none; border-radius:50%; cursor:pointer;
  background:rgba(255,255,255,.94); color:#57575f;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 2px 8px rgba(0,0,0,.2); transition:transform .15s,color .15s;
}
.fav:hover{ transform:scale(1.1); }
.fav.on{ color:var(--accent); }
.fav.on svg path{ fill:currentColor; }
.chip{
  /* Measured from the bottom of the PHOTOGRAPH, which is the box it now sits
     inside. It used to be a sibling of the frame, positioned three-quarters of
     the way down the whole card — photo plus category plus title — in the hope
     of landing inside the picture. Where it actually landed depended on how
     long the title was, which is why it drifted onto the edge of the image on
     some cards and below it on others. */
  position:absolute; right:10px; bottom:10px; z-index:2;
  display:flex; align-items:center; gap:5px;
  background:rgba(255,255,255,.94); color:#3a3a42;
  font-family:var(--sans); font-size:12px; font-weight:650;
  font-variant-numeric:tabular-nums; padding:5px 11px 5px 8px;
  border-radius:var(--r-pill); box-shadow:0 2px 8px rgba(0,0,0,.2);
}
.empty{ display:none; font-family:var(--sans); font-size:15.5px;
  color:var(--ink-3); padding:var(--gap-5) 0; }

/* ── author strip (index) ────────────────────────────────── */

.abox{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-4);
  box-shadow:var(--shadow-2); overflow:hidden;
  display:grid; grid-template-columns:300px 1fr; align-items:stretch;
}
@media (max-width:760px){ .abox{ grid-template-columns:1fr; } }
.abox > .frame{ border-radius:0; height:100%; min-height:280px; aspect-ratio:auto; }
.abox .txt{ padding:var(--gap-5) var(--gap-5); font-family:var(--sans); }
.abox .k{
  font-size:10.5px; font-weight:750; letter-spacing:.14em; text-transform:uppercase;
  color:var(--accent); margin-bottom:var(--gap-2);
}
.abox h2{ margin:0 0 var(--gap-2); }
.abox p{ font-family:var(--serif); font-size:17px; line-height:1.65; color:var(--ink-2); }
.creds{ display:flex; flex-wrap:wrap; gap:8px; margin-top:var(--gap-3); }
.creds span{
  font-family:var(--sans); font-size:12px; font-weight:640; color:var(--ink-2);
  border:1px solid var(--line); border-radius:var(--r-pill); padding:5px 12px;
}

/* ── article head ────────────────────────────────────────── */

.crumb{ font-family:var(--sans); font-size:13px; color:var(--ink-3);
  padding:var(--gap-4) 0 0; margin:0; }
.crumb a{ color:var(--ink-3); text-decoration:none; }
.crumb a:hover{ color:var(--accent); }
.crumb span{ margin:0 7px; opacity:.55; }
.eyebrow{
  font-family:var(--sans); font-size:11px; font-weight:750; letter-spacing:.14em;
  text-transform:uppercase; color:var(--accent); margin:var(--gap-4) 0 var(--gap-2);
}
.byline{
  font-family:var(--sans); font-size:13.5px; color:var(--ink-2);
  display:flex; flex-wrap:wrap; align-items:center; gap:9px;
  margin:var(--gap-3) 0 var(--gap-4);
}
.byline a{ color:var(--ink); font-weight:660; text-decoration:none; }
.byline a:hover{ color:var(--accent); }
.byline .dot{ color:var(--ink-3); }
.avatar{
  width:32px; height:32px; flex:none; border-radius:50%; overflow:hidden;
  background:var(--sunk); border:1px solid var(--line);
}
.avatar img{ width:100%; height:100%; object-fit:cover; display:block; }

.facts{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(128px,1fr));
  gap:1px; background:var(--line); border:1px solid var(--line);
  border-radius:var(--r-3); overflow:hidden; margin:0 0 var(--gap-4);
}
.fact{ background:var(--surface); padding:13px 15px; }
/* Three facts in a two-column grid left a fourth cell empty, which reads as a
   missing value rather than as a recipe that has three. The last one takes the
   room instead. */
.fact:last-child:nth-child(odd){ grid-column:1 / -1; }
.fact .k{
  font-family:var(--sans); font-size:10.5px; font-weight:700; letter-spacing:.11em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:3px;
}
.fact{ display:flex; flex-direction:column; justify-content:flex-start; }
.fact .v{
  font-family:var(--sans); font-size:15.5px; font-weight:700; line-height:1.35;
  font-variant-numeric:tabular-nums; color:var(--ink); overflow-wrap:anywhere;
}

/* ── action bar ──────────────────────────────────────────── */

.actions{ display:flex; flex-wrap:wrap; gap:10px; margin:0 0 var(--gap-5); }
.act{
  display:inline-flex; align-items:center; gap:9px; white-space:nowrap; cursor:pointer;
  font-family:var(--sans); font-size:15.5px; font-weight:750; letter-spacing:-.005em;
  background:var(--accent); color:var(--on-accent); border:none; text-decoration:none;
  padding:13px 25px; border-radius:var(--r-pill);
  transition:background .15s,transform .12s;
}
.act:hover{ background:var(--accent-hover); }
.act:active{ transform:translateY(1px); }
.act svg{ flex:none; }
@media (max-width:520px){ .act{ font-size:14.5px; padding:12px 20px; } }

/* ── article body ────────────────────────────────────────── */

article h2{ margin:var(--gap-6) 0 var(--gap-3); scroll-margin-top:84px; }
article h3{ margin:var(--gap-4) 0 var(--gap-2); font-size:18px; }
article p{ margin:0 0 var(--gap-3); }
article ul,article ol{ margin:0 0 var(--gap-4); padding-left:24px; }
article li{ margin-bottom:10px; }
article li::marker{ color:var(--accent); }
article ol{ list-style:none; padding-left:0; counter-reset:step; }
article ol li{ counter-increment:step; position:relative; padding-left:48px; margin-bottom:var(--gap-3); }
article ol li::before{
  content:counter(step); position:absolute; left:0; top:1px;
  width:32px; height:32px; border-radius:50%;
  background:var(--accent-soft); color:var(--accent-ink);
  font-family:var(--sans); font-size:14.5px; font-weight:750;
  display:flex; align-items:center; justify-content:center;
}
article figure{ margin:0; }
.lede{ font-size:20.5px; line-height:1.6; color:var(--ink-2); }
.lede strong{ color:var(--ink); }

/* ── recipe card ─────────────────────────────────────────── */

.rcard{
  background:var(--surface); border:2px solid var(--ink); border-radius:var(--r-4);
  box-shadow:var(--shadow-2); overflow:hidden;
  margin:var(--gap-6) 0; scroll-margin-top:84px;
}

.tagline{
  font-family:var(--sans); font-size:10.5px; font-weight:750; letter-spacing:.15em;
  text-transform:uppercase; color:var(--accent); margin:0 0 var(--gap-2);
}
.rcard h2{ margin:0 0 var(--gap-1); }

.note{ background:var(--accent-soft); border-radius:var(--r-2);
  padding:15px 18px; font-size:15.5px; margin:var(--gap-1) 0 0; }

/* ── sidebar ─────────────────────────────────────────────── */

.side{
  display:flex; flex-direction:column; gap:var(--gap-4);
  font-family:var(--sans); padding-top:var(--gap-4);
}
@media (max-width:1060px){
  .side{ max-width:420px; margin:var(--gap-6) auto 0; padding-top:0; }
}
.acard{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-4);
  box-shadow:var(--shadow-2); overflow:hidden; text-align:center;
}
.acard .in{ padding:var(--gap-4); }
.acard h2{ font-size:29px; margin:0 0 var(--gap-2); }
.acard p{ font-size:15.5px; line-height:1.6; color:var(--ink-2); margin:0 0 var(--gap-3); }
.fl{ font-size:14.5px; font-weight:750; margin:0 0 var(--gap-2); }
.socials{ display:flex; gap:10px; justify-content:center; }
/* The homepage strip sets its text flush left, so a centred row of icons would
   sit adrift of every line above it. */
.abox .socials{ justify-content:flex-start; }
.abox .fl{ font-size:12px; font-weight:750; letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink-3); margin:var(--gap-4) 0 var(--gap-2); }
.soc{
  width:42px; height:42px; flex:none; border-radius:var(--r-2);
  border:1.5px solid var(--accent); color:var(--accent); text-decoration:none;
  display:flex; align-items:center; justify-content:center;
  transition:background .15s,color .15s;
}
.soc:hover{ background:var(--accent); color:var(--on-accent); }
.more{ display:inline-block; margin-top:var(--gap-3); font-size:13.5px;
  font-weight:660; color:var(--accent); text-decoration:none; }
.more:hover{ text-decoration:underline; }

.swidget{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-4);
  box-shadow:var(--shadow-2); padding:var(--gap-4);
}
.lab{
  font-size:11px; font-weight:750; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-3); margin:0 0 var(--gap-3);
  padding-bottom:10px; border-bottom:1px solid var(--line-2);
}
.pop{
  display:grid; grid-template-columns:68px 1fr; gap:13px; align-items:center;
  text-decoration:none; color:inherit;
  padding:11px 0; border-bottom:1px solid var(--line-2);
}
.pop:last-child{ border-bottom:none; padding-bottom:0; }
.pop .frame{ border-radius:var(--r-1); aspect-ratio:1/1; }
/* Both are spans, and a span is inline — so the title and the meta ran into
   each other: "Dill Pickle Lemonade3 mins · 2 servings". The margin-top below
   was the giveaway, since it does nothing at all on an inline element. */
.pop .t{ display:block; font-size:14.5px; font-weight:660; line-height:1.32;
  letter-spacing:-.012em; }
.pop:hover .t{ color:var(--accent); }
.pop .m{ display:block; font-family:var(--sans); font-size:12px;
  color:var(--ink-3); margin-top:4px; }

/* ── ad slots ────────────────────────────────────────────────
   The height is reserved before anything loads. Ad networks inject after
   render, and a layout that has not held the space shifts the article under
   the reader's eye — which is the Core Web Vitals cost that undoes the whole
   reason for building static pages. */

.ad{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:4px; text-align:center; margin:var(--gap-5) 0; font-family:var(--sans);
}
/* Nothing is held open. A block with no code is not rendered at all, so an
   empty .ad never reaches the page — but a network that fails to fill one
   leaves it collapsed rather than as a hole in the article. */
.ad:empty{ display:none; margin:0; }

/* Alignment, the way the blocks name it. `default` adds no class at all, so
   these are the six that do something. */
.ad-center{ margin-left:auto; margin-right:auto; text-align:center; align-items:center; }
.ad-left  { align-items:flex-start; text-align:left; margin-right:auto; }
.ad-right { align-items:flex-end;   text-align:right; margin-left:auto; }
.ad-fleft { float:left;  margin:4px var(--gap-4) var(--gap-4) 0; }
.ad-fright{ float:right; margin:4px 0 var(--gap-4) var(--gap-4); }
/* No wrapping: the code is emitted with nothing around it doing layout, for a
   network whose own snippet already positions itself. */
.ad-bare{ display:block; margin:0; text-align:inherit; }

/* Per-position spacing. No min-height anywhere except the rail: the network
   decides the size, and a box held open at a guessed height is the layout
   shift these pages exist to avoid, in the other direction. */
.ad-above_header, .ad-header, .ad-footer{
  max-width:var(--wrap); margin:var(--gap-4) auto; padding:0 22px; }
.ad-before_post, .ad-after_content, .ad-after_post,
.ad-before_recipe, .ad-after_recipe,
.ad-before_paragraph, .ad-after_paragraph, .ad-after_intro{
  margin:var(--gap-5) 0; }
.ad-between_posts{ grid-column:1 / -1; margin:var(--gap-4) 0; }
.ad-sidebar{ margin:0; position:sticky; top:88px; }
@media (max-width:1060px){ .ad-sidebar{ position:static; } }

/* ── related ─────────────────────────────────────────────── */

.related{ margin:var(--gap-6) 0 0; }
.cards{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--gap-4); }
@media (max-width:680px){ .cards{ grid-template-columns:1fr; } }

/* ── sticky mobile bar ───────────────────────────────────── */

.sticky{
  display:none; position:fixed; left:0; right:0; bottom:0; z-index:45;
  align-items:center; justify-content:space-between; gap:10px;
  background:var(--surface); border-top:1px solid var(--line);
  padding:10px 14px calc(10px + env(safe-area-inset-bottom));
  font-family:var(--sans); box-shadow:0 -4px 20px rgba(0,0,0,.09);
}
/* Room for the sticky bar, put inside the footer instead of under the page.
 *
 * It was 74px of padding on the body, at every width below 720px, on every
 * page — including the ones with no sticky bar at all. The body is white, so on
 * a phone that is a white band under a dark footer, which reads as the site
 * having stopped rather than ended.
 *
 * The same 74px inside the footer is dark, so it is invisible on the pages that
 * do not need it and does its job on the ones that do — which is why this is
 * not conditional on the bar being there.
 */
@media (max-width:720px){
  .sticky{ display:flex; }
  .foot{ padding-bottom:calc(var(--gap-5) + 74px); }
}
.sticky .name{ font-size:13px; font-weight:650; color:var(--ink-2);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sticky a{
  flex:none; background:var(--accent); color:var(--on-accent); text-decoration:none;
  font-size:13.5px; font-weight:700; padding:11px 18px; border-radius:var(--r-2);
}

/* ── footer ──────────────────────────────────────────────── */

.foot{
  margin-top:var(--gap-7); background:var(--deep); color:var(--deep-ink);
  border-top:3px solid var(--accent); font-family:var(--sans);
}
.fin{
  max-width:var(--wrap); margin:0 auto; padding:var(--gap-6) 22px var(--gap-5);
  display:grid; grid-template-columns:1.6fr 1fr 1fr 1fr; gap:var(--gap-5);
}
@media (max-width:900px){ .fin{ grid-template-columns:1fr 1fr; } }
@media (max-width:520px){ .fin{ grid-template-columns:1fr; } }
.fbrand .logo .b{ color:var(--deep-ink); }
.fbrand .logo .sub{ color:var(--deep-ink-2); }
.ftag{
  font-family:var(--serif); font-style:italic; font-size:15.5px; line-height:1.55;
  color:var(--deep-ink-2); margin:var(--gap-3) 0 var(--gap-4); max-width:38ch;
}
.flab{
  font-size:11px; font-weight:750; letter-spacing:.16em; text-transform:uppercase;
  color:var(--deep-ink); margin:0 0 var(--gap-3);
}
.foot .socials{ justify-content:flex-start; }
.fcol .flab{ padding-bottom:11px; border-bottom:1px solid var(--deep-line); }
.fcol a{ display:block; color:var(--deep-ink); text-decoration:none;
  font-size:15px; font-weight:600; margin-bottom:12px; transition:color .15s; }
.fcol a:hover{ color:var(--accent); }
.fbar{ background:var(--deep-2); border-top:1px solid var(--deep-line); }
.fbin{
  max-width:var(--wrap); margin:0 auto; padding:20px 22px;
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:var(--gap-3);
}
.cp{ font-size:13.5px; color:var(--deep-ink-2); margin:0; }
.cp b{ color:var(--accent); font-weight:700; margin-left:8px; }
.totop{
  display:flex; align-items:center; gap:9px; cursor:pointer;
  background:transparent; border:1px solid var(--deep-line); color:var(--deep-ink);
  font-size:14px; font-weight:620; padding:11px 20px; border-radius:var(--r-2);
  transition:border-color .15s,color .15s;
}
.totop:hover{ border-color:var(--accent); color:var(--accent); }

/* ── states ──────────────────────────────────────────────── */

a:focus-visible,button:focus-visible,input:focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-1);
}
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  /* iteration-count as well as duration. A duration of .01ms on an animation
     set to repeat for ever is not a stopped animation — it is the same
     animation a hundred thousand times a second, which is worse than the one
     the reader asked not to see. */
  *,*::before,*::after{
    transition-duration:.01ms !important;
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
  }
}

/* ── print: the card is what people put on the counter ───── */

@media print{
  .masthead,.sdraw,.pills,.ad,.sticky,.related,.foot,.crumb,.actions,
  .side,.arw,.fav,.chip,.skip,.pinit,.subbox{ display:none !important; }
  .shell{ display:block; }
  .main{ max-width:none; }
  body{ background:#fff; color:#000; font-size:12pt; padding:0; }
  .frame{ border:none; }
  .rcard{ border:1px solid #000; box-shadow:none; page-break-inside:avoid; }
  a{ color:#000; text-decoration:none; }
}

/* ── recipe card, rebuilt ─────────────────────────────────────
   A printed-card shape rather than a run of headings: the title and its times
   sit together at the top beside a thumbnail, the actions follow, then the
   ingredients and the steps as separately readable blocks. Someone cooking is
   scanning it at arm's length, not reading it. */

.rhead{
  display:grid; grid-template-columns:minmax(0,1fr) 132px;
  gap:var(--gap-4); padding:var(--gap-4) var(--gap-4) var(--gap-3);
}
@media (max-width:560px){ .rhead{ grid-template-columns:1fr; } }
.rmain{ min-width:0; }
.rhead h2{ font-size:26px; margin:0 0 var(--gap-3); }
.rthumb{ aspect-ratio:1/1; align-self:start; border-radius:var(--r-2); }
.rfacts{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:9px; }
@media (max-width:400px){ .rfacts{ grid-template-columns:1fr; } }
.rf{
  border:1px solid var(--line); border-radius:var(--r-1);
  padding:9px 12px; font-family:var(--sans); min-width:0;
}
.rf .k{ display:block; font-size:12px; font-weight:600; color:var(--ink-2); }
.rf .v{ display:block; font-size:15px; font-weight:750; color:var(--ink);
  line-height:1.3; overflow-wrap:anywhere; }

.ractions{ display:flex; flex-wrap:wrap; gap:9px; padding:0 var(--gap-4) var(--gap-4); }
.rb{
  font-family:var(--sans); font-size:14.5px; font-weight:700; cursor:pointer;
  padding:11px 20px; border-radius:var(--r-1); border:1.5px solid transparent;
  text-decoration:none; transition:background .15s,color .15s,border-color .15s;
}
.rb.ghost{ background:var(--surface); color:var(--ink); border-color:var(--ink); }
.rb.ghost:hover{ background:var(--ink); color:var(--surface); }
.rb.pin{ background:var(--accent); color:var(--on-accent); }
.rb.pin:hover{ background:var(--accent-hover); }
.rb.dark{ background:var(--ink); color:var(--surface); }
.rb.dark:hover{ background:#000; }

.rdesc{
  margin:0; padding:var(--gap-3) var(--gap-4);
  border-top:1px solid var(--line); border-bottom:1px solid var(--line);
  font-size:16.5px; color:var(--ink-2); background:var(--surface);
}
.rcard .rbody{ padding:var(--gap-4); background:var(--bg-alt); }
.rcard .rbody h3{
  font-family:var(--sans); font-size:22px; font-weight:790; letter-spacing:-.024em;
  text-transform:none; color:var(--ink); border:none; padding:0;
  margin:var(--gap-5) 0 var(--gap-3);
}
.rcard .rbody h3:first-child{ margin-top:0; }

.ring{ list-style:none; margin:0; padding:0; }
.ring li{
  position:relative; margin:0; padding:7px 0 7px 22px;
  font-size:16px; border-bottom:1px solid var(--line-2);
}
.ring li:last-child{ border-bottom:none; }
.ring li::before{
  content:""; position:absolute; left:5px; top:16px;
  width:6px; height:6px; border-radius:50%; background:var(--accent);
}

.rsteps{ list-style:none; margin:0; padding:0; counter-reset:rstep; }
.rsteps li{
  counter-increment:rstep; margin:0 0 10px; padding:14px 16px 14px 44px;
  position:relative; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-2); font-size:16px; line-height:1.6;
}
.rsteps li::before{
  content:counter(rstep) "."; position:absolute; left:16px; top:14px;
  font-family:var(--sans); font-weight:800; color:var(--accent);
}
.rsteps b{ font-weight:750; color:var(--ink); }

/* ── nutrition ────────────────────────────────────────────────
   Was a grid of narrow columns with the raw camelCase key as a heading, so it
   printed SERVINGSIZE and clipped SATURATEDFAT, and a serving size written as
   a sentence broke into four lines inside a column built for "320". Rows now:
   label and figure on one line, the serving size across the full width. */
.nut{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:0 var(--gap-4); border:1px solid var(--line);
  border-radius:var(--r-2); padding:4px var(--gap-3); background:var(--surface);
}
.nrow{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--gap-3);
  padding:9px 0; border-bottom:1px solid var(--line-2); font-family:var(--sans);
}
.nrow .k{ font-size:14px; color:var(--ink-2); }
.nrow .v{ font-size:15px; font-weight:750; color:var(--ink);
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.nrow.wide{ grid-column:1/-1; }
.nrow.wide .v{ white-space:normal; text-align:right; }

/* ── tickable ingredients ─────────────────────────────────────
   The label wraps the input so the whole line is the target, which matters when
   someone is cooking. Ticking survives nothing — it is a place-keeper for one
   session, not saved state, and it needs no JavaScript. */
.ring li{ padding:0; }
.ring li label{
  display:flex; align-items:flex-start; gap:12px; cursor:pointer;
  padding:9px 0 9px 2px; font-size:16px;
}
.ring li::before{ display:none; }
.ring input[type="checkbox"]{
  appearance:none; -webkit-appearance:none; flex:none; margin:2px 0 0;
  width:20px; height:20px; border:2px solid var(--ink-3); border-radius:4px;
  background:var(--surface); cursor:pointer; transition:border-color .12s,background .12s;
}
.ring input[type="checkbox"]:hover{ border-color:var(--accent); }
.ring input[type="checkbox"]:checked{
  border-color:var(--accent); background:var(--accent);
}
.ring input[type="checkbox"]:checked::after{
  content:""; display:block; width:5px; height:10px; margin:1px 0 0 5px;
  border:solid var(--on-accent); border-width:0 2.5px 2.5px 0; transform:rotate(43deg);
}
.ring input[type="checkbox"]:checked + span{ color:var(--ink-3); text-decoration:line-through; }

/* ── foldable sections ────────────────────────────────────────
   <details> rather than a scripted panel: it opens with the keyboard, it is
   searchable in the page, and it works with JavaScript switched off. */
.fold{ border:1px solid var(--line); border-radius:var(--r-2);
  background:var(--surface); overflow:hidden; }
.fold + h3{ margin-top:var(--gap-5); }
.fold summary{
  list-style:none; cursor:pointer; padding:15px 46px 15px 18px; position:relative;
  font-family:var(--sans); font-size:15px; font-weight:700; color:var(--ink);
}
.fold summary::-webkit-details-marker{ display:none; }
.fold summary::after{
  content:""; position:absolute; right:20px; top:21px;
  width:9px; height:9px; border:solid var(--ink-2); border-width:0 2px 2px 0;
  transform:rotate(45deg); transition:transform .18s;
}
.fold[open] summary::after{ transform:rotate(-135deg); top:25px; }
.fold summary:hover{ color:var(--accent); }
.foldb{ padding:0 18px 18px; }
.foldb .note{ margin:0; }

/* ── standalone pages ─────────────────────────────────────────
   About, contact, privacy, terms. One column, generous measure, nothing else
   competing for attention. */
.main.narrow{ max-width:64ch; margin:0 auto; padding-bottom:var(--gap-6); }
.main.narrow h1{ margin:var(--gap-3) 0 var(--gap-4); }
.main.narrow article h2{ margin:var(--gap-6) 0 var(--gap-3); font-size:25px; }
.main.narrow article p{ margin:0 0 var(--gap-3); }
.credline{
  display:inline-block; font-family:var(--sans); font-size:13.5px; font-weight:640;
  color:var(--ink-2); border:1px solid var(--line); border-radius:var(--r-pill);
  padding:6px 14px; margin:0 8px 8px 0;
}


/* ── Pages ─────────────────────────────────────────────────────────────────
   Real links, so a reader can send somebody page three and a crawler can
   follow it. Big enough to hit with a thumb: 44px is the size a finger
   actually is, and these are the controls a phone reader uses most. */
.pager{
  display:flex; align-items:center; justify-content:center; flex-wrap:wrap;
  gap:8px; margin:var(--gap-6) 0 var(--gap-4);
  font-family:var(--sans);
}
.pager .nums{ display:flex; align-items:center; gap:6px; }
.pager a, .pager .on, .pager .gap{
  min-width:44px; height:44px; padding:0 14px;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:10px; font-size:15px; font-weight:650; text-decoration:none;
}
.pager a{ color:var(--ink); border:1px solid var(--line); background:var(--surface); }
.pager a:hover{ border-color:var(--accent); color:var(--accent); }
.pager .on{ background:var(--accent); color:var(--on-accent); }
.pager .gap{ color:var(--ink-3); min-width:24px; padding:0; }
.pager .step{ font-weight:700; }
@media (max-width:520px){
  /* The numbers are what a thumb reaches for; the words take the room they
     need on a small screen and are the first thing to go. */
  .pager .step{ padding:0 12px; font-size:14px; }
}

/* ── Category tiles ────────────────────────────────────────────────────── */
.ccard .nm .sub{
  display:block; font-size:11.5px; font-weight:600; opacity:.85;
  letter-spacing:.04em; margin-top:2px;
}

/* ── Sitemap ───────────────────────────────────────────────────────────── */
.smgroup{ margin:var(--gap-5) 0; }
.smgroup h2{ font-size:20px; margin:0 0 10px; }
.smgroup h2 a{ color:var(--ink); text-decoration:none; }
.smgroup h2 a:hover{ color:var(--accent); }
.smgroup h2 .muted{ color:var(--ink-3); font-size:14px; font-weight:500; }
.smlist{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
  gap:2px 22px;
}
.smlist a{
  color:var(--ink-2); text-decoration:none; font-size:15px;
  display:block; padding:7px 0; line-height:1.45;
}
.smlist a:hover{ color:var(--accent); }
@media (max-width:520px){ .smlist{ grid-template-columns:1fr; } }

/* ── Remember it later ─────────────────────────────────────────────────────
   The pin band under the opening photograph. Pinterest red rather than the
   site's accent, deliberately: it is not the site speaking, it is a button for
   somebody else's app, and a reader recognises that red before they read the
   words. A site that would rather it matched sets --pin. */
.pinband{
  --pin:#d0222a;
  background:var(--pin); color:#fff;
  padding:var(--gap-4) var(--gap-3) var(--gap-3);
  margin:0 0 var(--gap-4); text-align:center;
  font-family:var(--sans);
}
.pinband .pt{
  margin:0 0 6px; font-size:clamp(24px,4.4vw,32px); font-weight:800;
  letter-spacing:-.02em; line-height:1.12;
}
.pinband .pd{
  margin:0 0 var(--gap-3); font-size:15px; font-weight:600;
  line-height:1.45; opacity:.96;
}
.pinband .pb{
  display:flex; align-items:center; justify-content:center; gap:10px;
  /* Full width, and 52px tall. It is the one thing on the page the site is
     actually asking for, and on a phone it should be impossible to miss or to
     mis-tap. */
  min-height:52px; padding:12px 18px;
  background:#fdeaea; color:var(--pin); text-decoration:none;
  font-size:17px; font-weight:800; letter-spacing:.03em;
  transition:background .15s, transform .15s;
}
.pinband .pb:hover{ background:#fff; }
.pinband .pb:active{ transform:scale(.99); }
.pinband .pb:focus-visible{ outline:3px solid #fff; outline-offset:-5px; }

/* Every eight seconds the button catches the light and gives a small nudge.
 *
 * Eight seconds, not two: this has to be noticeable to somebody who is
 * reading, and invisible to somebody who is not looking for it. A button that
 * moves constantly is one a reader learns to ignore in about a minute, and
 * then it is costing attention on every page for nothing.
 *
 * transform and opacity only, so the whole thing runs on the compositor and
 * never asks the page to lay out again. The shine is a pseudo-element rather
 * than a background on the button, so it cannot disturb the label.
 */
.pinband .pb{ position:relative; overflow:hidden; isolation:isolate; }
.pinband .pb::after{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none;
  /* The plain rgba first, so a browser without color-mix still gets a sweep
     rather than an invisible one. */
  background:linear-gradient(105deg,
    transparent 34%, rgba(208,34,42,.22) 50%, transparent 66%);
  /* Tinted with the brand red, not white. The button is #fdeaea — a white
     sweep across it is a 0.14 difference in luminance, which is to say
     nothing at all. The point of a shine is that it can be seen. */
  background:linear-gradient(105deg,
    transparent 34%, color-mix(in srgb, var(--pin) 22%, transparent) 50%,
    transparent 66%);
  transform:translateX(-130%);
  animation:pin-shine 8s ease-in-out infinite;
}
@keyframes pin-shine{
  0%,80%{ transform:translateX(-130%); }
  90%,100%{ transform:translateX(130%); }
}
.pinband .pb{ animation:pin-nudge 8s ease-in-out infinite; }
@keyframes pin-nudge{
  0%,80%,100%{ transform:scale(1); }
  85%{ transform:scale(1.028); }
  90%{ transform:scale(.996); }
  94%{ transform:scale(1); }
}
/* A reader who is touching the button does not need to be told it is there,
   and a running animation would otherwise override the press. Stopped rather
   than paused, so the transform below applies cleanly instead of freezing
   wherever the loop happened to be. */
.pinband .pb:hover,
.pinband .pb:active,
.pinband .pb:focus-visible{ animation:none; }
.pinband .pb:hover::after,
.pinband .pb:active::after,
.pinband .pb:focus-visible::after{ animation:none; opacity:0; }
@media (max-width:520px){
  .pinband{ padding:var(--gap-3) var(--gap-2) var(--gap-2); }
  .pinband .pd{ font-size:14px; }
}
@media print{ .pinband{ display:none; } }


/* Classic — the base design, unmodified.

   Every template is _base.css plus one file of its own. The base carries a rule
   for every class the renderer emits, which is what tools/check_css.py checks
   for and what stops a template shipping a half-styled page; a template file
   changes the design and never has to restate the structure.

   Classic adds nothing: it is the reference the others are variations on, and
   an empty file here says that more honestly than a copy of the base would. */


/* --- this site's brand --- */
:root{
  --accent:#3f4c8c;
  --accent-hover:#313b6d;
  --accent-soft:#f3f4f8;
  --accent-ink:#3f4c8c;
  --on-accent:#ffffff;
  --display:Fraunces,Georgia,"Times New Roman",serif;
  --serif:Charter,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
  --r-1:0px;
  --r-2:4px;
  --r-3:10px;
}
