/*
Theme Name: Palesa's Bakes & Treats
Theme URI: https://palesasbakesandtreats.co.za
Author: Tony Dezigner
Author URI: https://tonydezigner.com
Description: Palesa's Bakes & Treats, converted from the approved 32-page static HTML prototype into a real WordPress theme. Every page is a literal markup port of the approved build in "html site/"; no copy or design was rewritten in the conversion. The design system below is copied verbatim from css/palesas.css. Page-specific stylesheets (about, gallery, ecommerce, shop, blog, classes, custom-cakes, edible-printing) live in assets/css/ and are enqueued per template by inc/enqueue.php.
Version: 1.0.0
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
License: Proprietary
Text Domain: palesasbakesandtreats
*/

/* ============================================================================
   Palesa's Bakes & Treats, project #36 - homepage
   THE BERRY VARIANT. 1 September 2026.

   Same page, same template, HER palette instead of the reference's.

   Why this exists. The reference is forest green and gold, drawn for a beauty
   shop, and it reads as spa. Palesa's only brand asset is her logo, and the
   pink in it was sampled straight off the file: #e8477b. Green and gold left
   that pink as the single orphaned colour on the whole page.

   Why the page is not simply turned pink. Measured, #e8477b is 3.74 on white:
   it fails as body text AND as a background for a small white label. It cannot
   do the heavy lifting, so something dark still has to. That dark colour is now
   pulled down from her pink instead of being unrelated to it.

   Gold stays, and it moves. On the light grounds the loud accent is now HER
   pink; gold keeps the metallic detail on the dark berry surfaces, where gold
   looks its best anyway and where it measures 4.89.

   Everything else - structure, type, spacing, motion - is identical to
   palesas.css. Only the palette moved.

   Rebuilt 1 September 2026 to match a template Tony supplied.

   THE REFERENCE
   `beauty-shop/Beauty-Products-main/` in this project folder, and its live
   demo at beauty-products-pearl.vercel.app. Tony asked, twice, whether we
   could build to it pixel perfect. We can, and this is that build.

   WHAT IS AND IS NOT TAKEN FROM IT
   Taken: the design system (palette roles, the three type roles, the two-tone
   heading device, radii, the 1200px container, section rhythm), the section
   order, the composition of each section, and the motion.
   NOT taken: any of its code, any of its markup, and none of its 44 images,
   which are AI-generated and belong to a beauty brand that does not exist.
   Every photograph here is Palesa's own. This is a rebuild from a reference,
   which is one of the two build routes named in CLAUDE.md.

   THE REFERENCE'S OWN PROVENANCE, recorded rather than glossed over: its
   README is titled "Pixel Perfect Clone" and carries the prompt that made it,
   which asked for an exact copy of screenshots of somebody else's site. There
   is no licence file anywhere in it. What survives our port is layout
   convention, which is not protectable, and we ship none of its material.
   See decisions.md, 1 September 2026.

   MOTION IS A DECLARED EXCEPTION
   Everything here follows the reference's motion, not `motion-house`. Tony
   chose that knowing the trade: cubic-bezier(0.16, 1, 0.3, 1) is the exact
   curve motion-house records as rejected, and these reveal durations are
   longer than the house Arrival budget. The house file is NOT edited; this is
   a per-project override and it is recorded in decisions.md.

   CONTRAST IS NOT COPIED BLIND
   The reference fails WCAG in three places and those failures are not
   inherited. Measured on its own values: its body grey #878787 is 3.26 on the
   page ground against a need of 4.5; its gold #b98c34 is 2.78 as text on that
   ground, which fails even the large-text floor of 3.0; and white on its gold
   badges is 3.06 at 10px. The three tokens below are the nearest values that
   pass, and the look is unchanged to the eye.
   ========================================================================= */

:root{

  /* --- Colour. The reference's roles, at values that measure. ------------ */
  --pk-primary:      #6e1937;  /* deep berry, pulled from her pink. White 11.31 */
  --pk-primary-fg:   #ffffff;
  --pk-primary-soft: rgba(255,255,255,.72);   /* body copy on berry: 6.53   */
  --pk-primary-line: rgba(255,255,255,.15);

  /* Gold has three jobs and one value cannot do all three.
     --pk-gold      fills, rules, icons, and LARGE display type.
                    3.69 on white, 3.35 on the page, 3.25 on cream: passes the
                    3.0 large-text floor on every ground this page uses.
                    The reference's own #b98c34 is 2.78 on the page and fails.
     --pk-gold-ink  gold text at normal size, and any fill carrying a small
                    white label. 5.15 on white; white on it is 5.15 too.
     --pk-gold-soft gold on the green only, where it measures 4.68.          */
  --pk-gold:       #a87e2c;
  --pk-gold-ink:   #8a681f;
  --pk-gold-soft:  #d3a33f;   /* on the berry: 4.89 */

  /* Her logo pink, sampled from img/logo.webp rather than guessed. */
  --pk-pink:       #e8477b;   /* FILLS and decoration only. 3.74 as text. */
  --pk-pink-ink:   #d92e68;   /* large display type. 4.62 / 4.20 / 4.07 / 3.50 */

  --pk-bg:         #f4f4f2;   /* the page ground                            */
  --pk-surface:    #ffffff;   /* the bands that sit on it                   */
  --pk-cream:      #f7f0ee;   /* a rosier cream, to sit under the berry */
  --pk-cream-deep: #ecdedd;   /* ink 10.83. muted is 4.14 here, large only */

  --pk-fg:         #2b2b2b;   /* 12.86 on the page                          */
  --pk-muted:      #6a6a6a;   /* 4.91 page / 5.41 white / 4.76 cream        */
  --pk-border:     #ebe8e2;
  --pk-star:       #c8a01f;

  /* --- Type. All three are OFL and can be self-hosted in the theme later.
         Outfit for headings, DM Sans for body, Great Vibes for the one
         signature. Exactly the reference's three roles. ------------------- */
  --pk-display: "Outfit", "Segoe UI", system-ui, -apple-system, sans-serif;
  --pk-sans:    "DM Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --pk-script:  "Great Vibes", "Segoe Script", cursive;

  /* --- Shape. 0.75rem base, pills on controls, ~16px on cards. ---------- */
  --pk-r:      .75rem;
  --pk-r-lg:   1rem;
  --pk-r-xl:   1.5rem;
  --pk-pill:   9999px;

  /* --- Rhythm. The reference's container and band spacing. -------------- */
  --pk-wrap:   1200px;
  --pk-pad:    20px;
  --pk-band:   64px;
  /* The narrow-screen band. Three merged stylesheets had each eyeballed their
     own value for this -- 58px, 55px and 56px -- which is the signature of a
     number chosen by eye rather than derived. One home for it now. */
  --pk-band-sm: 56px;

  /* --- Status. An order is delivered, waiting, or cancelled, and the first two
         need a colour the berry-and-gold palette does not contain. They were
         raw hex in ecommerce.css, which put a colour decision in a page
         stylesheet where the palette could not see it, and left the amber pair
         half-tokenised -- the tell that they were converted by hand and one was
         missed. Measured against their own fill: green 5.72, gold 4.61. Both
         clear 4.5, and the gold has no headroom, so it is a normal-text pair
         only -- do not reuse it under 4.5's large-text allowance. ----------- */
  --pk-ok:      #1e6b3a;
  --pk-ok-bg:   #e7f3ec;
  --pk-warn-bg: #fdf1dc;

  /* --- Depth. Lifted out of the homepage's own card and panel rules on
         2 September 2026 and made tokens, because 29 merged pages had no
         elevation at all and the site flattened the moment you left home.
         Offset down, soft blur, negative spread, tinted to the berry
         rather than neutral black. ------------------------------------ */
  --pk-shadow:    0 30px 60px -18px rgba(31,10,20,.34), 0 6px 16px -8px rgba(31,10,20,.2);
  --pk-shadow-sm: 0 8px 20px -12px rgba(31,10,20,.35);

  /* --- Motion. The reference's, verbatim. See the note above. ----------- */
  --pk-ease:   cubic-bezier(0.16, 1, 0.3, 1);
  --pk-rise:   1s;
  --pk-reveal: .9s;

  /* --- Motion, house standard (motion-house skill). Governs ONLY the header
     overlay/drawer/popover system added 7 Sept 2026 (search, wishlist, cart,
     account) and the mobile drawer's own open/close - never the hero above,
     which keeps --pk-ease/--pk-rise/--pk-reveal untouched. Two different
     motion vocabularies living side by side on purpose: the hero's is the
     approved reference's own, the header system's is the client-site house
     standard, and neither should drift toward the other. ------------------ */
  --ease-ui:       cubic-bezier(0.23, 1, 0.32, 1);
  --ease-entrance: cubic-bezier(0.33, 1, 0.68, 1);
  --ease-move:     cubic-bezier(0.32, 0.72, 0, 1);
  --dur-tap:    140ms;
  --dur-ui:     200ms;
  --dur-panel:  300ms;
  --dur-reveal: 640ms;
  --stagger:    70ms;
  --rise:       48px;

  /* The page furniture above and below the hero: top bar + header + marquee.
     MEASURED live by JS, never guessed, because the top bar wraps to two lines
     on a narrow phone and the header shrinks once it is stuck. A hard-coded
     number here would leave the hero a few pixels short on exactly the devices
     this is for. */
  --pk-chrome: 146px;
  /* THREE consumers, THREE variables, and that is the point rather than an
     oversight. --pk-chrome is topbar + header row + marquee and sizes the
     HERO. --pk-headerrow is the header row alone, which is what a sticky
     element below the header has to clear (ecommerce.css's account sidebar).
     --pk-mnav-top is the mobile drawer's live distance from the top of the
     screen: about 110px at rest on a 390px phone, 64px once the header is
     stuck. One variable feeding all three is exactly what caused [[l-245]],
     and the sidebar was its third, unnoticed victim. These declarations are
     the values used before the footer script has measured anything. */
  --pk-headerrow: 64px;
  --pk-mnav-top: 110px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--pk-bg);color:var(--pk-fg);
  font-family:var(--pk-sans);font-size:14px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
h1,h2,h3,h4{margin:0;font-family:var(--pk-display);font-weight:700;letter-spacing:-.01em;line-height:1.15}
p{margin:0}
ul{margin:0;padding:0;list-style:none}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input{font:inherit}
[hidden]{display:none !important}

/* The focus ring is a TOKEN, and every dark surface sets it.
   Measured 2 September 2026: a berry ring on a berry ground is contrast 1.00,
   which is not a weak focus ring, it is no focus ring at all. It was invisible
   in the top bar, the footer, the berry CTA panel, the sign-up band and the
   class cards, so a keyboard visitor lost their place on every page of the
   site. The fix already existed for ONE of those surfaces, .pk-cstore, and had
   never been generalised. A token with a fallback means a dark surface added
   later cannot inherit the bug. */
:where(a,button,input,summary,[tabindex]):focus-visible{
  outline:2px solid var(--pk-ring, var(--pk-primary));outline-offset:2px;
}
.pk-topbar,.pk-footer,.pk-panel,.pk-signup,.pk-cstore,.pk-pull,
.pk-promo--dark,.cakes-cta,.shop-newsletter,
.cta,.pull,.ec-summary__total,.ds-dark,.ds-row--dark,.join,.pk-marquee{--pk-ring:var(--pk-gold-soft)}

/* The ring is set on CONTAINERS only, and that is not an oversight.
   `outline-offset:2px` paints the ring entirely OUTSIDE the border box, so it
   lands on the PARENT background, never on the element's own fill. A gold
   ring on a berry button measures 4.89 against the button and 2.05 against
   the cream page it is actually drawn on, and 2.05 is the number that counts.
   Element-level overrides were added here for one round and inverted 71 rings
   that had been passing; they are deleted. A .pk-btn--light inside .pk-panel
   correctly inherits gold from the panel it stands on.

   The check that settled this was not a computed-style script from either
   side: it was screenshotting a focused button and reading which pixels
   changed colour. When a contrast number decides a change across 32 pages,
   take the screenshot. */
.pk-skip{
  position:absolute;left:-9999px;top:0;z-index:999;
  background:var(--pk-primary);color:var(--pk-primary-fg);padding:10px 16px;
}
.pk-skip:focus{left:0}
.pk-vh{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* --- the container. One rule, exactly as the reference. ----------------- */
.pk-wrap{width:100%;max-width:var(--pk-wrap);margin-inline:auto;padding-inline:var(--pk-pad)}

/* --- section rhythm ----------------------------------------------------- */
.pk-band{padding-block:var(--pk-band)}
.pk-band--top{padding-top:56px}
.pk-band--bottom{padding-bottom:var(--pk-band)}
.pk-surface{background:var(--pk-surface)}
.pk-cream{background:var(--pk-cream)}

/* --- the reference's two shared heading pieces -------------------------- */
.pk-eyebrow{text-align:center;font-size:14px;font-weight:500;color:var(--pk-muted)}
.pk-h2{
  margin-top:8px;text-align:center;
  font-size:clamp(26px,3.4vw,33.6px);font-weight:700;letter-spacing:-.02em;
}
.pk-h2 em{font-style:normal;color:var(--pk-pink-ink)}   /* the two-tone signature */
.pk-lede{margin-top:12px;font-size:13px;line-height:1.75;color:var(--pk-muted)}

/* Left-aligned section label, added for the Baking Classes archive's
   Upcoming / dates-coming-soon / Past split: three named sections on one
   page, so each needs its own heading, distinct from the centred .pk-h2
   used for a page's single big heading. */
.pk-section-label{
  margin:48px 0 20px;font-family:var(--pk-display);font-size:1.3rem;
  color:var(--pk-primary);
}
.pk-section-label--muted{color:var(--pk-muted);font-size:1.1rem}

/* --- a placeholder string that is not her copy yet. Copywriting is parked;
       nothing on this page is written. Every one is in to-check.md. ------- */
.ph{border-bottom:1px dotted var(--pk-border)}

/* --- controls ----------------------------------------------------------- */
.pk-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:44px;padding:12px 24px;border-radius:var(--pk-pill);
  font-size:13px;font-weight:500;line-height:1;
  background:var(--pk-primary);color:var(--pk-primary-fg);
  transition:transform .3s var(--pk-ease),opacity .3s var(--pk-ease),box-shadow .3s var(--pk-ease);
}
.pk-btn svg{width:14px;height:14px;flex:none}
.pk-btn--sm{min-height:38px;padding:10px 20px;font-size:12px}
.pk-btn--light{background:var(--pk-surface);color:var(--pk-primary)}
.pk-btn--link{
  min-height:44px;padding:0;background:none;color:var(--pk-fg);
  text-decoration:underline;text-underline-offset:4px;
  transition:color .3s var(--pk-ease);
}
.pk-badge{
  display:inline-block;padding:4px 12px;border-radius:var(--pk-pill);
  background:var(--pk-gold-ink);color:#fff;      /* white on it: 5.15 */
  font-size:11px;font-weight:500;line-height:1.5;
}
/* Ghost variant, added for the Baking Classes redesign: a view-only card
   (a past class, a dateless one) needs a control that reads as "look, not
   book" next to the solid primary CTA on a bookable card. Outline, not
   filled - the opposite affordance from .pk-btn. */
.pk-btn--ghost{
  background:none;color:var(--pk-primary);
  border:1.5px solid var(--pk-primary);min-height:41px;
}
@media (hover:hover) and (pointer:fine){
  .pk-btn--ghost:hover{background:var(--pk-primary);color:var(--pk-primary-fg)}
}
/* Muted chip, added for the Baking Classes redesign: "Class completed" /
   "This class has passed" - a fact, not an offer, so it reads calmer than
   the gold .pk-badge (which is used for promotional badges elsewhere). */
.pk-chip{
  display:inline-block;padding:4px 12px;border-radius:var(--pk-pill);
  font-size:11px;font-weight:600;line-height:1.5;text-transform:uppercase;letter-spacing:.02em;
}
.pk-chip--muted{background:var(--pk-border);color:var(--pk-muted)}
@media (hover:hover) and (pointer:fine){
  .pk-btn:hover{transform:translateY(-2px);opacity:.92;box-shadow:0 14px 30px -12px rgba(0,0,0,.5)}
  .pk-btn--link:hover{color:var(--pk-gold-ink);transform:none;box-shadow:none}
}

/* ============================================================================
   MOTION. The reference's three keyframe sets and its reveal system, copied.
   ========================================================================= */
@keyframes pk-rise{
  from{opacity:0;transform:translate3d(0,26px,0);filter:blur(8px)}
  to  {opacity:1;transform:none;filter:blur(0)}
}
.js .pk-rise{opacity:0;animation:pk-rise var(--pk-rise) var(--pk-ease) forwards}

@keyframes pk-marquee{ from{transform:translateX(0)} to{transform:translateX(-50%)} }
/* pk-float and .pk-floaty are gone. Defined, styled, kept through a whole
   merge, and used on ZERO pages. A 6s infinite float nothing renders is
   weight in the one stylesheet every page loads. */

.js .reveal{
  opacity:0;filter:blur(6px);
  transition:opacity var(--pk-reveal) var(--pk-ease),
             transform var(--pk-rise) var(--pk-ease),
             filter var(--pk-reveal) var(--pk-ease);
}
.js .reveal--up   {transform:translate3d(0,44px,0)}
.js .reveal--left {transform:translate3d(-48px,0,0)}
/* The reference's sideways reveals travel 48px, which pushes them past the
   viewport edge and makes the page scroll sideways until they land. It ships
   that bug. Clipped at the SECTION, never at body: overflow-x:hidden on body
   is exactly what makes an overflow check vacuous. [[l-208]] */
.pk-clipx{overflow-x:clip}

/* TOUCH FEEDBACK. Every interactive style on this site sits behind
   @media (hover:hover), which is correct and also means a phone got nothing
   back from a press. These are the touch counterpart. */
.pk-btn:active{transform:scale(.97)}
.pk-icon:active{transform:scale(.92)}
.pk-btn,.pk-icon{transition:transform 140ms var(--pk-ease)}
.pk-cstore__row p a:active,.pk-mnav a:active{opacity:.7}
/* a berry focus ring inside the berry store card is invisible */
.pk-cstore :focus-visible{outline-color:var(--pk-gold-soft)}
/* sticky 64px header would otherwise cover any in-page anchor target */
html{scroll-padding-top:76px}
.js .reveal--right{transform:translate3d(48px,0,0)}
.js .reveal--scale{transform:scale(.94)}
.js .reveal[data-shown="true"]{opacity:1;filter:blur(0);transform:none}

/* The reduced-motion block lives at the END of this file. Put here, every
   component rule written after it won on source order and the marquee kept
   scrolling with reduce set. Do not move it back up. */

/* ============================================================================
   1. TOP BAR   green, 36px, phone left / message centre / socials right
   ========================================================================= */
.pk-topbar{background:var(--pk-primary);color:var(--pk-primary-fg)}
.pk-topbar__row{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:36px;font-size:12px;
}
.pk-topbar__call{display:none;flex:none}
.pk-topbar__note{margin-inline:auto;text-align:center;color:var(--pk-primary-soft)}
.pk-topbar__note a{
  display:inline-block;min-height:24px;padding-block:4px;
  color:var(--pk-gold-soft);text-decoration:underline;text-underline-offset:2px;
}
.pk-topbar__social{display:none;flex:none;align-items:center;gap:10px}
.pk-topbar__social a{
  display:grid;place-items:center;width:24px;height:24px;border-radius:var(--pk-pill);
  background:rgba(255,255,255,.15);transition:background .2s var(--pk-ease);
}
.pk-topbar__social svg{width:11px;height:11px}
@media (min-width:640px){ .pk-topbar__call{display:block} }
@media (min-width:768px){ .pk-topbar__social{display:flex} }
@media (hover:hover) and (pointer:fine){
  .pk-topbar__social a:hover{background:var(--pk-gold)}
}

/* ============================================================================
   2. HEADER   sticky white, 64px, logo / nav / icons
   The reference has no scroll state at all. One is added, because a sticky bar
   that never acknowledges the scroll reads as unfinished.
   ========================================================================= */
.pk-header{
  position:sticky;top:0;z-index:50;background:var(--pk-surface);
  box-shadow:0 1px 0 0 var(--pk-border);
  transition:box-shadow .3s var(--pk-ease);
}
.pk-header[data-stuck]{box-shadow:0 1px 0 0 var(--pk-border),0 10px 30px -20px rgba(0,0,0,.45)}
.pk-header__row{display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:64px}
.pk-logo{display:flex;align-items:center;gap:10px;flex:none}
.pk-logo img{
  height:34px;width:auto;
  mix-blend-mode:multiply;   /* her mark is a jpg on white; this sits it down */
  transition:height .3s var(--pk-ease);
}
.pk-header[data-stuck] .pk-logo img{height:29px}
.pk-nav{display:none}
.pk-nav ul{display:flex;align-items:center;gap:28px}
.pk-nav a{
  display:inline-flex;align-items:center;min-height:26px;padding-block:5px;
  font-size:13px;font-weight:500;color:rgba(43,43,43,.8);
  /* Transparent by default so the active state's coloured underline below
     costs no layout shift when it appears - every tab already reserves the
     2px, whether or not it is drawn. */
  border-bottom:2px solid transparent;
  transition:color .2s var(--pk-ease),border-color .2s var(--pk-ease);
}
.pk-nav a[aria-current="page"]{border-bottom-color:var(--pk-primary)}
.pk-nav a[aria-current="page"],.pk-mnav a[aria-current="page"]{color:var(--pk-primary);font-weight:600}
/* NO .sub-menu STYLING HERE, DELIBERATELY. This is a known open item, not an
   oversight, and it is written down so the same wrong fix is not tried twice.

   If a CHILD item is ever added to the Primary menu in WordPress,
   wp_nav_menu() emits <ul class="sub-menu">, and `.pk-nav ul{display:flex}`
   above will lay the children across the header as a permanently-open row.
   A `.pk-nav .sub-menu{display:none}` was added on 7 September 2026 to stop
   that, and REVERTED the same day by the Reviewer: the mobile drawer that was
   supposed to keep those children reachable is itself `display:none !important`
   above 1024px, so the two rules stood down for each other and left a child
   page reachable at NO width at all. Silently. On a menu the client edits
   herself, per the self-serve rule of 24 August.

   A visible mess somebody can see and undo beats an invisible page nobody can
   find. Building a real dropdown is a design decision for Tony. See
   [[l-245]]. */
.pk-header__tools{display:flex;align-items:center;gap:4px}
.pk-icon{
  display:grid;place-items:center;width:40px;height:40px;position:relative;
  color:rgba(43,43,43,.8);
  /* BOTH properties, in one declaration. This block sits AFTER the 140ms
     transform rule near the top of the file and at equal specificity, so a
     colour-only transition here silently replaced it and .pk-icon:active
     snapped with no easing. */
  transition:color .2s var(--pk-ease),transform 140ms var(--pk-ease);
}
/* The visible button stays 40x40 because five of them plus the logo is all
   the row can hold; the TOUCH target is lifted to the 44x44 this build already
   requires elsewhere (the checkout stepper, 4 Sep) by an overlay that takes
   part in hit-testing and in no layout. At 360px and up the icons sit on a
   44px pitch (40 wide + 4 gap), so these tile exactly and never overlap. The
   narrow-phone cap is in the max-width:359px block below, where the pitch
   drops to 34 and a 44px target would steal its neighbour's taps.

   SCOPED TO THE HEADER TOOLS ON PURPOSE. Every number above is reasoned from
   that one row. `.pk-icon` is a shared class and ecommerce.css sizes a 32px
   variant of it (`.ec-qty .pk-icon`), where the pitch is 36 and a 44px overlay
   would hang 4px over the control beside it, invisibly. */
.pk-header__tools .pk-icon::after{
  content:"";position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);
  width:44px;height:44px;
}
.pk-icon svg{width:18px;height:18px}
.pk-burger{display:grid}
/* 320px only: five icons at 40px plus the gaps overflow the row. Nothing here
   changes at 360 and above.

   THIS BLOCK MUST STAY BELOW THE RULES IT OVERRIDES. It used to sit above
   .pk-header__row, .pk-header__tools and .pk-icon, every one of which is
   declared later at the same specificity, so every declaration in here lost
   and the overflow it describes was still happening: measured at 11px on a
   320px screen on 7 September 2026, under a comment saying it was handled.
   Moving it down here is the whole fix. */
@media (max-width:359px){
  .pk-header__row{gap:10px}
  .pk-header__tools{gap:0}
  .pk-icon{width:34px}
  /* the gap is 0 here, so the pitch IS the width: a 44px target would reach
     10px into its neighbour and take the taps meant for it. The height still
     lifts to 44, which is free, because the row is 64 tall. */
  .pk-header__tools .pk-icon::after{width:34px}
}
@media (min-width:1024px){
  .pk-nav{display:block}
  .pk-burger{display:none}
}
@media (hover:hover) and (pointer:fine){
  .pk-nav a:hover,.pk-icon:hover{color:var(--pk-primary)}
}

/* the mobile panel the reference opens under the bar */
.pk-mnav{border-top:1px solid var(--pk-border);background:var(--pk-surface)}
.pk-mnav ul{display:flex;flex-direction:column;padding-block:6px}
.pk-mnav a{display:block;padding:12px 0;font-size:14px;font-weight:500;color:rgba(43,43,43,.8)}
.pk-mnav li+li{border-top:1px solid var(--pk-border)}
/* Shop's children (WordPress' own .sub-menu). Before this they rendered at the
   SAME indent and weight as Shop itself, reading as three equal top-level tabs
   with no sign Cakes/Baking Supplies belonged to Shop. Indenting and quieting
   them is the fix, not hiding them - a menu the client edits herself must never
   go unreachable at any width. */
.pk-mnav .sub-menu{padding-block:0 4px}
.pk-mnav .sub-menu li{border-top:none}
.pk-mnav .sub-menu a{padding:8px 0 8px 20px;font-size:13px;font-weight:400;color:var(--pk-muted)}
/* Nothing floats on top of the drawer while it is open.

   The LOAD-BEARING rule is the z-index one, and it names no vendor. The header
   is already position:sticky at z-index 50, so lifting it only while the
   drawer is open has the same blast radius as hiding one launcher, and it
   covers ANY fixed widget this site picks up later. The chat bubble that
   prompted this sits at 997.

   Hiding .chaty-widget as well is cosmetic, not structural: a launcher
   floating over an open menu looks wrong even when it is behind it. If Chaty
   ever renames that class the rule matches nothing and the menu stays usable
   anyway, which is the whole reason the vendor name is on the cosmetic half
   and not the safety half. See [[l-245]]. */
.pk-mnav-open .pk-header{z-index:998}
.pk-mnav-open .chaty-widget{display:none !important}
@media (min-width:1024px){ .pk-mnav{display:none !important} }

/* ============================================================================
   3. HERO   cream band, 2 columns, staggered rise
   ========================================================================= */
.pk-hero{
  position:relative;overflow:hidden;background:var(--pk-cream);
  display:flex;align-items:stretch;
  /* One screen, whatever the screen is. 100dvh is the DYNAMIC viewport unit:
     it tracks the phone browser's address bar as it collapses, where plain
     100vh measures the tallest state and pushes the bottom of the hero out of
     sight on every iPhone. min-height, never height, so a short landscape
     screen lets the content grow instead of clipping it. */
  min-height:calc(100vh - var(--pk-chrome));
  min-height:calc(100dvh - var(--pk-chrome));
}
.pk-hero__grid{
  position:relative;display:grid;width:100%;flex:1;
  grid-template-columns:1fr;
  /* the copy takes what it needs; the photograph takes whatever is left */
  grid-template-rows:auto minmax(0,1fr);
  align-items:stretch;
  gap:clamp(10px,2.4vh,32px);padding-block:clamp(14px,3vh,40px);
}
.pk-hero__copy{position:relative;z-index:10;max-width:32rem;align-self:center}
.pk-hero__chip{
  display:inline-flex;align-items:center;gap:8px;
  padding:8px 16px;border-radius:var(--pk-pill);background:var(--pk-surface);
  box-shadow:0 1px 2px rgba(0,0,0,.06);
}
.pk-hero__chip i{
  display:grid;place-items:center;width:20px;height:20px;flex:none;
  border-radius:var(--pk-pill);background:var(--pk-pink);
}
.pk-hero__chip i svg{width:10px;height:10px;color:#fff}
.pk-hero__chip span{font-size:12px;font-weight:500}
.pk-hero h1{
  margin-top:clamp(12px,2.2vh,24px);
  /* The reference's h1 is text-4xl/sm:text-5xl with a hard <br>. That is small
     for a hero and the break fights the wrap. Sized up, and it wraps freely. */
  font-size:clamp(34px,5.6vw,58px);line-height:1.1;letter-spacing:-.025em;
  text-wrap:balance;
}
.pk-hero h1 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-hero__sub{margin-top:clamp(8px,1.6vh,16px);max-width:30rem;font-size:14px;line-height:1.75;color:var(--pk-muted)}
.pk-hero__cta{margin-top:clamp(14px,2.6vh,28px);display:flex;flex-wrap:wrap;align-items:center;gap:12px 24px}
/* The gallery IS the second grid row now. The floor lives here, not on the
   card: a card is absolutely positioned and reports nothing back to the row,
   so a floor on the card would overlap the controls while every layout signal
   stayed green. On this the row grows and the hero grows with it. */
.pk-hero__art{position:relative}

/* ----------------------------------------------------------------------------
   THE GALLERY. A 3D carousel of her own cakes, in the hero's right column.

   Tony asked for this after finding it on kexsio.com. Its reference is React,
   Tailwind and a fixed card size; none of that survives the port.

   HOW IT WORKS, because it looks more complicated than it is. Each card carries
   ONE inline custom property, --pos: its signed distance from the active card,
   so -2 -1 0 1 2. Every visual consequence below is computed from that single
   number. The script writes --pos and nothing else, which is why there is no
   layout code in the JavaScript at all.

   WHY IT IS NOT SIZED IN PIXELS. The hero fills exactly one screen and hands
   this column whatever height is left. The reference's clamp(370px,42vw,535px)
   card would break that at the first awkward viewport, so the stage fills the
   cell and the card is a share of the STAGE height with aspect-ratio giving the
   width. The carousel gets smaller on a short screen instead of overflowing it.

   Warning: no card may ever carry .pk-rise. That animation ends on
   transform:none with fill-mode forwards and would flatten these transforms the
   moment it finished.
   -------------------------------------------------------------------------- */
.pk-gal{
  position:relative;height:100%;
  /* the floor the card used to carry, on the thing the grid can measure */
  min-height:250px;
  /* two rows: the cakes, then the controls. Nothing is ever laid over the
     card, which is what was hiding its own label on a phone. */
  display:grid;grid-template-rows:minmax(0,1fr) auto;gap:10px;
  align-content:stretch;
  touch-action:pan-y;                 /* a vertical page scroll still works */
  user-select:none;-webkit-user-select:none;
}
/* the dots inside it are built by the script, so an empty pill and two dead
   arrows is what a no-JS visitor would otherwise get */
.pk-gal__bar{display:none}
.js .pk-gal__bar{
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding-bottom:2px;
}
.pk-gal__stage{
  position:relative;width:100%;height:100%;
  display:flex;align-items:center;justify-content:center;
  perspective:1400px;transform-style:preserve-3d;
  /* the card is measured against THIS box, in both directions */
  container-type:size;
}

.pk-gal__card:nth-child(1){--pos:0}
.pk-gal__card:nth-child(2){--pos:1}
.pk-gal__card:nth-child(3){--pos:2}
.pk-gal__card:nth-child(4){--pos:-2}
.pk-gal__card:nth-child(5){--pos:-1}
.pk-gal__card{
  --pos:0;
  --d:max(var(--pos), -1 * var(--pos));   /* absolute value, in CSS */
  position:absolute;
  /* The card must fit the stage in BOTH directions and stay 4:5. Setting a
     height and a max-width instead lets max-width clamp the width after
     aspect-ratio has derived it, which silently produces a tall thin card:
     measured 202x454 at 768px, an 0.44 ratio, not 0.8. Taking the smaller of
     the height the stage allows and the height an 80%-wide card implies keeps
     the ratio true at every size. */
  height:min(100cqh, calc(80cqw * 1.25), 520px);
  aspect-ratio:4/5;
  /* the caption below keys its own cqi off THIS box, not the stage. Card
     width is not itself content-derived (height above + aspect-ratio give
     it a definite size), so it can be a nested query container without a
     circularity problem. Without this the caption was scaling with the
     full-width stage while the card stayed height-bound and narrow in the
     500-767px band, so a wide caption sat over a narrow card. */
  container-type:size;
  overflow:hidden;border-radius:var(--pk-r-xl);
  background:var(--pk-cream-deep);
  box-shadow:0 30px 60px -18px rgba(31,10,20,.34), 0 6px 16px -8px rgba(31,10,20,.2);
  transform:
    translate3d(calc(var(--pos) * 58%), 0, calc(var(--d) * -90px))
    rotateY(calc(var(--pos) * -11deg))
    scale(calc(1 - var(--d) * 0.13));
  opacity:calc(1 - var(--d) * 0.48);
  filter:blur(calc(var(--d) * 2.5px));
  z-index:calc(10 - var(--d));
  /* emil-design-eng caps UI transitions at 300ms and calls anything longer
     marketing. A hero gallery IS marketing, so it may be longer, but the
     reference's 760ms is longer than it needs. 620ms lands just inside
     motion-house's 640ms Arrival budget, so the thing that arrives matches the
     house standard while the page keeps the reference's curve. */
  transition:transform 620ms var(--pk-ease),
             opacity 480ms var(--pk-ease),
             filter 480ms var(--pk-ease),
             visibility 0s linear 480ms;
}
/* past the near neighbours the cards are gone, not merely faint */
.pk-gal__card[data-far]{opacity:0;visibility:hidden;pointer-events:none}
.pk-gal__card:not([data-far]){visibility:visible;transition-delay:0s}
.pk-gal__card[data-active]{cursor:default}
.pk-gal__card:not([data-active]){cursor:pointer}

.pk-gal__card img{
  height:100%;width:100%;object-fit:cover;
  /* only hero-cake.jpg is portrait; the rest are 4:3 or square, so they crop */
  transform:scale(1.05);
  transition:transform 1.1s var(--pk-ease);
}
.pk-gal__card[data-active] img{transform:scale(1)}

.pk-gal__scrim{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top, rgba(31,10,20,.94) 0%, rgba(31,10,20,.62) 22%,
    rgba(31,10,20,.24) 45%, rgba(31,10,20,0) 70%);
}

/* the shine only crosses the card that is being looked at */
/* .pk-gal__shine removed with its animation; see the note below. */
/* The shine is gone. Its only job was to mark the active card, and the card
   already says that four other ways: larger, centred, un-rotated, and the
   only one with a visible caption. A gloss sweeping over a cake photograph
   every six seconds is the idiom of a discount banner. Stilling it after one
   pass would have been worse than either extreme -- a shimmer that stops
   reads as something that finished loading. */
/* transform, not `left`: `left` is a layout property and this loops for ever */

.pk-gal__cap{
  /* cqi here is the CARD's own inline size (container-type:size is on
     .pk-gal__card, above), not the stage's. A card at 320-767px viewport
     widths is height-bound (100cqh) and stays narrow even while the stage
     itself is wide, so keying off the stage sized the caption for a card
     that wasn't there and still clipped "Buttercream"/"Celebration" in
     that band. Keying off the card tracks its ACTUAL rendered width at
     every breakpoint, mobile and desktop alike. */
  position:absolute;
  left:clamp(8px,6cqi,18px);right:clamp(8px,6cqi,18px);bottom:clamp(8px,5cqi,16px);
  z-index:2;
  display:flex;align-items:flex-end;justify-content:space-between;gap:clamp(4px,4cqi,12px);
  color:#fff;pointer-events:none;
}
/* hidden until its card is the active one, but ONLY once the script is running.
   Without it no card is ever active, and every label would be invisible. */
.js .pk-gal__cap{
  opacity:0;transform:translateY(12px);
  transition:opacity 420ms var(--pk-ease) 160ms, transform 480ms var(--pk-ease) 160ms;
}
.js .pk-gal__card[data-active] .pk-gal__cap{opacity:1;transform:none}
.pk-gal__cap p{
  margin:0;font-family:var(--pk-display);
  font-size:clamp(11px,9cqi,28px);font-weight:700;
  letter-spacing:-.03em;color:#fff;
  /* the fallback, not the fix: the clamp above is sized against real
     content ("Buttercream", "Celebration") down to the smallest card this
     page renders, so this should never actually engage. */
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.pk-gal__cap .ph{border-bottom-color:rgba(255,255,255,.45)}
.pk-gal__cap span{
  display:grid;place-items:center;width:clamp(20px,13cqi,38px);height:clamp(20px,13cqi,38px);flex:none;
  border-radius:var(--pk-pill);border:1px solid rgba(255,255,255,.45);
  /* Measured: a translucent WHITE fill lightens the ground under white text,
     and over a pale cake the 11px number fell to 2.51 against a need of 4.5.
     A translucent INK fill does the opposite and reads as glass just as well. */
  background:rgba(31,10,20,.90);
  font-size:clamp(8px,4cqi,11px);font-weight:700;
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
}

.pk-gal__arrow{
  display:grid;place-items:center;flex:none;
  /* Tony: bar was too big, 19% of the whole gallery at 360px. 40px matches
     .pk-icon in the header (line ~294) rather than inventing a new size,
     and still clears the 24px WCAG 2.2 tap-target floor with room to spare. */
  width:40px;height:40px;border-radius:var(--pk-pill);
  border:1px solid var(--pk-border);background:var(--pk-surface);
  color:var(--pk-fg);box-shadow:0 8px 20px -12px rgba(31,10,20,.35);
  transition:background 220ms var(--pk-ease), transform 220ms var(--pk-ease);
}
.pk-gal__arrow svg{width:17px;height:17px}
.pk-gal__arrow:active{transform:scale(.92)}

.pk-gal__dots{
  display:flex;align-items:center;gap:4px;padding:0 10px;
  border-radius:var(--pk-pill);border:1px solid var(--pk-border);
  background:var(--pk-surface);
}
/* the dot is small; the thing you press is not. 40px of height, drawn as 8px. */
.pk-gal__dots button{
  position:relative;width:26px;height:40px;padding:0;border:0;background:none;
  transition:transform 140ms var(--pk-ease);
}
.pk-gal__dots button:active{transform:scale(.88)}
.pk-gal__dots button::after{
  content:"";position:absolute;left:50%;top:50%;width:8px;height:8px;
  border-radius:var(--pk-pill);background:rgba(43,43,43,.3);
  /* Tony: no stretch. Active dot stays the same size as the rest, colour
     is the only thing that changes, so the row's spacing never goes
     uneven around it. */
  transform:translate(-50%,-50%) scale(1);
  transform-origin:center;
  transition:transform 220ms var(--pk-ease), background 220ms var(--pk-ease);
}
.pk-gal__dots button[aria-current="true"]::after{transform:translate(-50%,-50%) scale(1.35);background:var(--pk-pink-ink)}

@media (hover:hover) and (pointer:fine){
  .pk-gal__arrow:hover{background:#fff;transform:scale(1.07)}
  .pk-gal__card:not([data-active]):hover{opacity:calc(1 - var(--d) * 0.3)}
}

@media (min-width:768px){
  .pk-hero__grid{grid-template-columns:1fr 1fr;grid-template-rows:minmax(0,1fr);padding-block:0}
  .pk-hero__copy{padding-block:clamp(16px,4vh,56px)}
  /* Tony: too big, wants air above and especially below. Card cap and the
     width factor both come down ~15% (was 520px/66cqw); the stage keeps
     more of the row than the card does, and that spare room is pushed to
     the bottom via padding-block below, not split evenly. */
  .pk-gal{min-height:clamp(180px,42vh,420px);padding-block:clamp(8px,2vh,20px) clamp(24px,6vh,56px)}
  .pk-gal__card{height:min(100cqh, calc(56cqw * 1.25), 420px)}
  .pk-gal__bar{gap:12px}
}

/* ============================================================================
   4. MARQUEE   green strip, her four lines, gold leaf between
   ========================================================================= */
.pk-marquee{width:100%;overflow:hidden;background:var(--pk-primary);padding-block:14px}
.pk-marquee__row{display:flex;width:max-content;align-items:center;animation:pk-marquee 26s linear infinite}
.pk-marquee__row > span{display:flex;flex:none;align-items:center}
.pk-marquee b{
  padding-inline:32px;font-size:14px;font-weight:500;letter-spacing:.02em;
  color:var(--pk-primary-fg);
}
.pk-leaf{width:20px;height:14px;flex:none;color:var(--pk-gold-soft)}

/* ============================================================================
   5. CATEGORIES   the main categories, as circular plates in one centred row
   ========================================================================= */
/* Tony, 1 Sep 2026: circles were too large to read as a clean grid.
   Tony, 1 Sep 2026 (again): still too big, down another 40% (120px to 72px),
   plus the whole row tightened to match.

   TONY, 9 SEP 2026: A CENTRED ROW, NOT A FIXED GRID, AND THE PLATES BACK UP
   TO 132px. This was `repeat(6,1fr)` above 1024px, written when six product
   categories were top-level. The 9 September re-parenting made two of them
   parents and the other fourteen their children, so the row became two cards
   sitting in the first two of six columns, hard against the left edge of the
   page, with four empty columns to their right. Flex with `justify-content:
   center` has no column count to go stale: two cards centre, and so would
   three or five the day Palesa adds one. The two `repeat(3,1fr)` /
   `repeat(6,1fr)` media queries that used to live at the bottom of this block
   are gone with it - `flex-wrap` does that job at every width, so re-adding
   them would only re-introduce the bug.

   72px was the right size for six plates in a row. It is far too small for
   two, which read as leftovers rather than as the shop's front door, so they
   match the shop page's own tiles now (132px, gold ring). One visual language
   across the two pages that show the same two categories. */
.pk-cats{
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:32px 48px;margin-top:34px;
}
.pk-cat{display:block;text-align:center;flex:0 1 200px;min-width:140px}
.pk-cat__img{
  display:block;margin-inline:auto;aspect-ratio:1/1;width:100%;max-width:132px;
  overflow:hidden;border-radius:var(--pk-pill);background:var(--pk-cream);
  border:2px solid var(--pk-gold-soft);padding:5px;
  transition:border-color .3s var(--pk-ease),box-shadow .3s var(--pk-ease);
}
.pk-cat__img img{
  height:100%;width:100%;object-fit:cover;border-radius:var(--pk-pill);
  transition:transform .5s var(--pk-ease);
}
.pk-cat h3{margin-top:14px;font-size:15px;font-weight:600}
.pk-cat p{margin-top:3px;font-size:11px;color:var(--pk-muted)}
/* Tony, 7 Sep 2026: the live product count, beside the name. Cream pill
   so it reads as a quantity rather than part of the title. --pk-muted on
   --pk-cream is 4.76:1, which clears AA at this size; --pk-cream-deep
   would be 4.14 and would not. The pill is allowed to wrap under a long
   name rather than squeeze it. */
.pk-cat__n{
  display:inline-block;margin-left:5px;padding:0 8px;
  border-radius:var(--pk-pill);background:var(--pk-cream);
  color:var(--pk-muted);font-size:11px;font-weight:600;line-height:19px;
  vertical-align:1px;
}
@media (hover:hover) and (pointer:fine){
  .pk-cat:hover .pk-cat__img img{transform:scale(1.05)}
  .pk-cat:hover .pk-cat__img{border-color:var(--pk-gold-ink);box-shadow:var(--pk-shadow)}
}
/* Tony, 9 Sep 2026: "the categories on mobile can they sit side by side
   horizontal not vertical, cakes left, baking supplies right". The centred
   flex row above wraps them onto two lines once the phone is narrower than
   two 200px cards plus the gap, which on his screen it was. Two explicit
   columns cannot wrap, so below 768px the row is a grid and the answer does
   not depend on how wide the cards happen to be. `justify-items:center`
   keeps a third category centred in its own column the day one is added,
   rather than hugging the left. */
@media (max-width:767px){
  .pk-cats{display:grid;grid-template-columns:repeat(2,1fr);gap:24px 16px;justify-items:center}
  .pk-cat{flex:none;width:100%;max-width:200px}
}

/* ============================================================================
   6. PROMO BANNERS   two cards: custom cakes (cream) and classes (green)
   ========================================================================= */
.pk-promos{display:grid;grid-template-columns:1fr;gap:24px}
.pk-promo{
  position:relative;display:flex;flex-direction:column-reverse;overflow:hidden;
  border-radius:var(--pk-r-xl);background:var(--pk-cream);
}
/* Tony, 1 Sep 2026: this card ate most of a phone screen for one line of
   copy and a button. Photo and every margin brought down together. */
.pk-promo__body{position:relative;z-index:10;padding:24px}
.pk-promo h3{
  margin-top:12px;font-size:clamp(20px,2.4vw,24px);line-height:1.2;
}
.pk-promo h3 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-promo p{margin-top:8px;font-size:12px;line-height:1.7;color:var(--pk-muted)}
.pk-promo .pk-btn{margin-top:16px}
/* Tony, 7 Sep 2026: on a phone these two photos are hidden. Stacked above the
   card they added height without adding meaning, and the cake shot read as a
   second hero. From 640px up the photo returns as the side panel of the card. */
.pk-promo img{display:none;width:100%;height:140px;object-fit:cover;object-position:center}
.pk-promo--dark{background:var(--pk-primary)}
.pk-promo--dark h3{color:var(--pk-primary-fg)}
.pk-promo--dark h3 em{color:var(--pk-gold-soft)}
.pk-promo--dark p{color:var(--pk-primary-soft)}
@media (min-width:640px){
  .pk-promo{flex-direction:row;min-height:200px}
  .pk-promo__body{max-width:58%}
  .pk-promo img{display:block;position:absolute;bottom:0;right:0;height:100%;width:46%;object-position:top}
}
@media (min-width:768px){ .pk-promos{grid-template-columns:repeat(2,1fr)} }

/* ============================================================================
   ABOUT   four-photo collage with corner blocks and seal, copy + stats.
   Was home-page section 7; lives on page-about.php since 14 Sep 2026.
   ========================================================================= */
.pk-about{display:grid;grid-template-columns:1fr;align-items:center;gap:48px}
.pk-collage{position:relative;margin-inline:auto;width:100%;max-width:430px}
.pk-collage__tl{
  position:absolute;left:-12px;top:-12px;width:112px;height:112px;
  border-top-left-radius:70px;background:var(--pk-primary);
}
.pk-collage__br{
  position:absolute;right:-12px;bottom:-12px;width:112px;height:112px;
  border-bottom-right-radius:70px;background:var(--pk-pink);
}
.pk-collage__grid{position:relative;display:grid;grid-template-columns:1.35fr 1fr;gap:12px}
.pk-collage__grid img{height:100%;width:100%;object-fit:cover}
.pk-collage__grid img:nth-child(1){border-top-left-radius:60px}
.pk-collage__grid img:nth-child(3),
.pk-collage__grid img:nth-child(4){height:160px}
.pk-collage__grid img:nth-child(3){border-bottom-left-radius:60px}
.pk-collage__grid img:nth-child(4){border-bottom-right-radius:60px}
/* Tony, 8 September 2026: this seal was 64px carrying 8px type, and 8px was
   the ONLY use of that size in the whole stylesheet - everything else on the
   site bottoms out at 11px. At that size the words were a smudge and the gold
   hairline ring inside it read as dirt rather than as a detail. It is bigger
   now and the type is 11px, the same floor the rest of the site uses.

   Still centred, deliberately: a medallion on the crossing of a four-photo
   collage is the composition, not an accident. What was wrong was the craft.

   The colour pair is unchanged and already measured - --pk-gold-soft on
   --pk-primary is 4.89:1, recorded on the token itself, which clears AA for
   normal text and now does so at a size somebody can actually read. */
.pk-collage__seal{
  position:absolute;left:50%;top:50%;z-index:10;transform:translate(-50%,-50%);
  display:grid;place-items:center;width:84px;height:84px;
  border-radius:var(--pk-pill);background:var(--pk-primary);
  box-shadow:0 10px 15px -3px rgba(0,0,0,.2);
}
.pk-collage__seal div{
  display:grid;place-items:center;width:68px;height:68px;text-align:center;
  border-radius:var(--pk-pill);border:1px solid rgba(211,163,63,.55);
}
.pk-collage__seal svg{width:13px;height:13px;color:var(--pk-gold-soft)}
.pk-collage__seal span{
  margin-top:3px;font-size:11px;font-weight:600;line-height:1.25;
  letter-spacing:.06em;text-transform:uppercase;color:var(--pk-gold-soft);
}
@media (min-width:768px){
  .pk-collage__seal{width:96px;height:96px}
  .pk-collage__seal div{width:78px;height:78px}
}
.pk-about__copy h2{
  margin-top:8px;font-size:clamp(26px,3.4vw,33.6px);line-height:1.2;letter-spacing:-.02em;
}
.pk-about__copy h2 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-about__copy > p:first-child{font-size:14px;font-weight:500;color:var(--pk-muted)}
.pk-about__body{margin-top:16px;max-width:30rem;font-size:14px;line-height:1.75;color:var(--pk-muted)}
.pk-about__body + .pk-about__body{margin-top:12px}
.pk-stats{
  margin-top:24px;display:grid;grid-template-columns:repeat(3,1fr);
  max-width:30rem;border-radius:var(--pk-r);background:var(--pk-primary);
  padding:20px 24px;
}
.pk-stats b{display:block;font-family:var(--pk-display);font-size:20px;font-weight:700;color:var(--pk-gold-soft)}
.pk-stats span{display:block;margin-top:4px;font-size:11px;color:var(--pk-primary-soft)}
@media (min-width:768px){ .pk-about{grid-template-columns:1fr 1fr} }

/* ============================================================================
   8/12. PRODUCT GRIDS   card = square plate, hover rail, slide-up button
   ========================================================================= */
.pk-filters{
  margin-top:28px;display:flex;justify-content:center;gap:10px;
  overflow-x:auto;padding-bottom:4px;scrollbar-width:none;
}
.pk-filters::-webkit-scrollbar{display:none}
.pk-filters button{
  flex:none;min-height:38px;padding:8px 16px;border-radius:var(--pk-pill);
  border:1px solid var(--pk-border);background:var(--pk-surface);
  font-size:12px;font-weight:500;color:var(--pk-muted);
  transition:border-color .2s var(--pk-ease),background .2s var(--pk-ease),color .2s var(--pk-ease);
}
.pk-filters button[aria-pressed="true"]{
  border-color:var(--pk-primary);background:var(--pk-primary);color:var(--pk-primary-fg);
}
.pk-grid{margin-top:36px;display:grid;grid-template-columns:repeat(2,1fr);gap:18px}
/* The elevation tokens were added on 2 September because the 29 merged pages
   had no elevation. The fix lifted those 29 and left the homepage flat, so
   the source of truth became the only page whose cards sat on the paper.
   Same token, same card, everywhere. */
.pk-prod,.pk-post,.pk-deal{box-shadow:var(--pk-shadow-sm)}
.pk-prod{position:relative;overflow:hidden;border-radius:var(--pk-r-xl);background:var(--pk-surface)}
/* the title is the only link and it covers the card, so the tap target is
   the card and not eighteen pixels of text */
.pk-prod h3 a::after{content:"";position:absolute;inset:0;z-index:1}
.pk-prod__add{z-index:3}
.pk-prod__plate{position:relative;aspect-ratio:1/1;overflow:hidden;background:var(--pk-cream)}
.pk-prod__plate img{height:100%;width:100%;object-fit:cover;transition:transform .5s var(--pk-ease)}
.pk-prod__tag{
  position:absolute;left:12px;top:12px;z-index:10;padding:5px 11px;
  border-radius:var(--pk-pill);background:var(--pk-gold-ink);color:#fff;
  font-size:11px;font-weight:500;
}
/* Tony, 8 September 2026: the save button, and it is the only control on the
   photo now. It used to sit in a rail (class pk-prod__rail, now deleted) with
   a dead Compare button and a magnifier that just re-linked the product, all
   three revealed on hover.

   ALWAYS VISIBLE, and that is the fix rather than a preference. The rail was
   opacity:0 until the card was hovered, so the one piece of state a shopper
   needs to keep - "I saved this" - was the one piece that existed only under
   the cursor. It also meant a keyboard user could focus a button that was
   invisible, and that three transparent hit targets sat over every photo,
   because the rail faded with opacity and never set pointer-events.

   These rules are `.pk-prod .product-wish` (0,2,0) on purpose. The base
   `.product-wish` styling lives in assets/css/shop.css (0,1,0), which loads
   after this file, so anything weaker here would silently lose. */
.pk-prod .product-wish{
  position:absolute;right:12px;top:12px;z-index:10;
  display:grid;place-items:center;width:36px;height:36px;
  border-radius:var(--pk-pill);border:1px solid var(--pk-border);
  background:rgba(255,255,255,.95);color:var(--pk-muted);
  box-shadow:0 1px 3px rgba(0,0,0,.15);
  transition:color .2s var(--pk-ease),border-color .2s var(--pk-ease),background .2s var(--pk-ease);
}
.pk-prod .product-wish svg{width:18px;height:18px}
/* Saved: a solid pink heart and a ring in the same colour. --pk-pink-ink is
   4.62:1 on white, measured, and an icon needs 3:1. */
.pk-prod .product-wish.is-saved{
  color:var(--pk-pink-ink);border-color:var(--pk-pink-ink);background:var(--pk-surface);
}
.pk-prod .product-wish.is-saved svg{fill:currentColor}
.pk-prod__add{
  position:absolute;left:12px;right:12px;bottom:12px;z-index:10;
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:10px;border-radius:var(--pk-pill);
  background:var(--pk-primary);color:var(--pk-primary-fg);
  font-size:11px;font-weight:500;
  opacity:0;transform:translateY(12px);
  transition:opacity .3s var(--pk-ease),transform .3s var(--pk-ease);
}
.pk-prod__add svg{width:12px;height:12px}
/* Tony, 1 Sep 2026: text under the photo read cramped and uneven between
   cards. The size line was an inline style in js/palesas.js (font-size,
   colour and margin hand-set per card, outside this file entirely) instead
   of a class here, which is exactly what let it drift out of the same
   rhythm as its siblings. Moved into .pk-prod__sizes below; every line in
   this block now shares one vertical rhythm (top:12px→8px→8px→8px) instead
   of the old, uneven 10px/6px/4px/6px. */
.pk-prod__meta{padding:12px 6px 4px}
.pk-prod__top{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:var(--pk-muted)}
.pk-prod__stars{display:flex;align-items:center;gap:4px}
.pk-prod__stars svg{width:12px;height:12px;fill:var(--pk-star);color:var(--pk-star)}
.pk-prod h3{margin-top:8px;font-size:15px;font-weight:600;line-height:1.3}
.pk-prod__sizes{margin-top:8px;font-size:12px;line-height:1.5;color:var(--pk-muted)}
.pk-prod__price{margin-top:8px;display:flex;align-items:baseline;gap:8px;font-size:15px}
.pk-prod__price b{font-weight:700;color:var(--pk-primary)}
.pk-prod__price span{font-size:12px;color:var(--pk-muted)}
@media (min-width:768px){ .pk-grid{grid-template-columns:repeat(3,1fr)} }
@media (min-width:1024px){ .pk-grid--6{grid-template-columns:repeat(6,1fr)} }
/* Homepage grid holding 4 or 8 products (only what can be bought today, e.g.
   the eight priced cakes): four across, so 8 sits as two even rows of four
   instead of six over two. Twelve still gets six across. Tony, 25 Sep 2026. */
@media (min-width:768px){
  #pk-products:has(> .pk-prod:nth-child(4n):last-child):not(:has(> .pk-prod:nth-child(6n):last-child)){grid-template-columns:repeat(4,1fr)}
}
@media (hover:hover) and (pointer:fine){
  .pk-prod:hover .pk-prod__plate img{transform:scale(1.05)}
  .pk-prod:hover .pk-prod__add{opacity:1;transform:translateY(0)}
  /* :not(.is-saved) is load-bearing. The old rule repainted the button on
     hover unconditionally, so it overwrote the pink saved colour under the
     user's own cursor - at the exact moment of clicking, there was no
     confirmation at all. */
  .pk-prod .product-wish:not(.is-saved):hover{color:var(--pk-primary);border-color:var(--pk-primary)}
  .pk-filters button:hover{border-color:rgba(31,69,41,.4)}
}
/* A touch device never hovers, so the Add bar must simply be there. The save
   button needs no entry here any more: it is visible at every width now. */
@media (hover:none){
  .pk-prod__add{opacity:1;transform:none;position:static;margin-top:8px}
}

/* ============================================================================
   9. EDIBLE PRINTING   the reference's green panel with the countdown
   ========================================================================= */
/* Plain CTA panel now, no image, no plate, no countdown, matching
   .pk-signup's shape further down the page (leaves + centered content +
   one action) rather than the reference's 3-column layout it came from. */
.pk-panel{position:relative;overflow:hidden;border-radius:var(--pk-r-xl);background:var(--pk-primary)}
.pk-panel__leaf{position:absolute;pointer-events:none;opacity:.3;color:var(--pk-gold-soft)}
.pk-panel__leaf--a{left:-30px;top:-26px;width:200px;transform:rotate(18deg)}
.pk-panel__leaf--b{right:-24px;bottom:-30px;width:200px;transform:rotate(-160deg)}
.pk-panel__mid{position:relative;z-index:10;padding:48px 32px;text-align:center}
.pk-panel__mid h2{
  margin-top:16px;font-size:clamp(24px,3vw,30px);line-height:1.2;color:var(--pk-primary-fg);
}
.pk-panel__mid h2 em{font-style:normal;color:var(--pk-gold-soft)}
.pk-panel__mid p{
  margin-inline:auto;margin-top:12px;max-width:22rem;
  font-size:12px;line-height:1.7;color:var(--pk-primary-soft);
}
.pk-panel__mid .pk-btn{margin-top:28px}

/* ============================================================================
   10. CLASSES   horizontal cards
   ========================================================================= */
.pk-deals{margin-top:36px;display:grid;grid-template-columns:1fr;gap:24px}
.pk-deal{display:flex;align-items:center;gap:20px;border-radius:var(--pk-r-xl);background:var(--pk-surface);padding:16px}
.pk-deal__img{position:relative;height:144px;width:144px;flex:none;overflow:hidden;border-radius:var(--pk-r);background:var(--pk-cream)}
.pk-deal__img img{height:100%;width:100%;object-fit:cover}
.pk-deal__img .pk-prod__tag{left:8px;top:8px}
.pk-deal__body{min-width:0}
.pk-deal__top{display:flex;align-items:center;gap:12px;font-size:11px;color:var(--pk-muted)}
.pk-deal h3{margin-top:6px;font-size:16px;font-weight:600}
.pk-deal__price{margin-top:6px;font-size:14px}
.pk-deal__price b{font-weight:700;color:var(--pk-primary)}
.pk-deal__note{margin-top:8px;font-size:11px;line-height:1.7;color:var(--pk-muted)}
.pk-deal .pk-btn{margin-top:12px}
@media (min-width:768px){ .pk-deals{grid-template-columns:repeat(2,1fr)} }

/* ============================================================================
   14. BLOG   three cards
   ========================================================================= */
/* Tony, 8 September 2026: on a phone one of these cards was 407px tall and
   263px of that was the picture, so three of them ran 1,492px - 1.77 phone
   screens, 14% of the whole page, for three cards that say "Coming soon".
   Worse, all three pictures are the class photographs already shown higher
   up the same page, so the largest element in the section was a repeat.

   The phone card is a row now: an 88px thumbnail, the title beside it. The
   stacked picture-on-top card is still the design at 768px and up, where
   the three-up grid gives it the room and there is no height to save. */
.pk-posts{margin-top:20px;display:grid;grid-template-columns:1fr;gap:12px}
.pk-post{
  position:relative;display:flex;align-items:center;gap:12px;padding:12px;
  overflow:hidden;border-radius:var(--pk-r-xl);background:var(--pk-surface);
}
.pk-post__img{flex:0 0 auto;width:88px;height:88px;overflow:hidden;border-radius:var(--pk-r)}
.pk-post__img img{height:100%;width:100%;object-fit:cover;transition:transform .5s var(--pk-ease)}
/* min-width:0 or a long title refuses to wrap and pushes the row wide. */
.pk-post__body{min-width:0;padding:0}
.pk-post__meta{display:flex;align-items:center;gap:16px;font-size:11px;color:var(--pk-muted)}
.pk-post__meta span{display:flex;align-items:center;gap:6px}
.pk-post__meta svg{width:12px;height:12px;color:var(--pk-gold-ink)}
.pk-post h3{margin-top:6px;font-size:15px;font-weight:600;line-height:1.3}
.pk-post a.pk-read{
  margin-top:6px;display:inline-flex;align-items:center;gap:6px;min-height:24px;
  font-size:12px;font-weight:500;color:var(--pk-primary);
}
.pk-post a.pk-read svg{width:14px;height:14px}
/* The whole card is the tap target, not the 12px words at the bottom of it.
   One real link still, in one place in the markup - this only stretches its
   hit area over the card, which is why the read link can afford to be 24px
   high on a phone instead of 36px. */
.pk-post a.pk-read::after{content:"";position:absolute;inset:0}
@media (min-width:768px){
  .pk-posts{margin-top:36px;grid-template-columns:repeat(3,1fr);gap:24px}
  .pk-post{display:block;padding:0}
  .pk-post__img{width:auto;height:auto;aspect-ratio:4/3;border-radius:0}
  .pk-post__body{padding:20px}
  .pk-post h3{margin-top:12px;font-size:16px;line-height:1.35}
  .pk-post a.pk-read{margin-top:16px;min-height:36px}
}
@media (hover:hover) and (pointer:fine){
  .pk-post:hover .pk-post__img img{transform:scale(1.05)}
}

/* The blog section's one way through to the archive. Centred under the grid,
   at every width. Added 8 September 2026 with the section becoming real: the
   three cards are the newest three posts, and this is the only route to the
   rest of them. */
.pk-posts__more{margin-top:20px;display:flex;justify-content:center}
@media (min-width:768px){
  .pk-posts__more{margin-top:32px}
}

/* ============================================================================
   15. SOCIAL MOSAIC
   ========================================================================= */
.pk-mosaic{margin-top:36px;display:grid;grid-template-columns:repeat(2,1fr);gap:16px}
.pk-tile{position:relative;display:block;overflow:hidden;border-radius:var(--pk-r);aspect-ratio:1/1}
.pk-tile img{height:100%;width:100%;object-fit:cover;transition:transform .5s var(--pk-ease)}
.pk-tile__over{
  position:absolute;inset:0;display:grid;place-items:center;
  background:rgba(31,69,41,.6);opacity:0;transition:opacity .3s var(--pk-ease);
}
.pk-tile__over svg{width:24px;height:24px;color:var(--pk-primary-fg)}
@media (min-width:640px){
  .pk-mosaic{grid-template-columns:repeat(4,1fr);grid-auto-rows:190px}
  .pk-tile{aspect-ratio:auto}
  .pk-tile--c{grid-column:2 / span 2;grid-row:1 / span 2}
  .pk-tile--w{grid-column:span 2}
}
@media (hover:hover) and (pointer:fine){
  .pk-tile:hover img{transform:scale(1.05)}
  .pk-tile:hover .pk-tile__over{opacity:1}
}

/* ============================================================================
   16. FAQ   accordion, one open at a time
   ========================================================================= */
.pk-faq{margin-inline:auto;margin-top:36px;max-width:48rem;display:grid;gap:12px}
.pk-faq__item{
  overflow:hidden;border-radius:var(--pk-r);
  border:1px solid var(--pk-border);background:var(--pk-surface);
  transition:border-color .2s var(--pk-ease);
}
.pk-faq__item[data-open]{border-color:rgba(31,69,41,.3)}
.pk-faq__q{
  display:flex;width:100%;align-items:center;justify-content:space-between;gap:16px;
  padding:16px 20px;text-align:left;
}
.pk-faq__q span:first-child{font-size:14px;font-weight:600}
.pk-faq__q i{
  display:grid;place-items:center;width:24px;height:24px;flex:none;
  border-radius:var(--pk-pill);background:var(--pk-cream);color:var(--pk-primary);
  transition:background .2s var(--pk-ease),color .2s var(--pk-ease);
}
.pk-faq__q i svg{width:12px;height:12px}
.pk-faq__item[data-open] .pk-faq__q i{background:var(--pk-primary);color:var(--pk-primary-fg)}
.pk-faq__a{padding:0 20px 20px;font-size:12px;line-height:1.75;color:var(--pk-muted)}

/* ============================================================================
   17. TRUST ROW   four real facts, cream block
   ========================================================================= */
.pk-trust{
  display:grid;grid-template-columns:1fr;gap:24px;
  border-radius:var(--pk-r-xl);background:var(--pk-cream);padding:36px 32px;
}
.pk-trust > div{display:flex;align-items:center;gap:16px}
.pk-trust i{
  display:grid;place-items:center;width:44px;height:44px;flex:none;
  border-radius:var(--pk-pill);background:var(--pk-primary);
}
.pk-trust i svg{width:16px;height:16px;color:var(--pk-gold-soft)}
.pk-trust h3{font-size:14px;font-weight:600}
.pk-trust p{margin-top:2px;font-size:11px;color:var(--pk-muted)}
@media (min-width:640px){ .pk-trust{grid-template-columns:repeat(2,1fr)} }
@media (min-width:1024px){ .pk-trust{grid-template-columns:repeat(4,1fr)} }

/* ============================================================================
   18. SIGN-UP   green band
   ========================================================================= */
.pk-signup{
  position:relative;overflow:hidden;border-radius:var(--pk-r-xl);
  background:var(--pk-primary);padding:48px 32px;text-align:center;
}
.pk-signup h2{font-size:clamp(24px,3vw,30px);color:var(--pk-primary-fg)}
.pk-signup h2 em{font-style:normal;color:var(--pk-gold-soft)}
.pk-signup > div{position:relative;z-index:10}
.pk-signup p{margin-inline:auto;margin-top:12px;max-width:28rem;font-size:12px;line-height:1.7;color:var(--pk-primary-soft)}
.pk-signup form{
  margin-inline:auto;margin-top:28px;display:flex;align-items:center;gap:8px;
  max-width:28rem;border-radius:var(--pk-pill);background:var(--pk-surface);padding:6px;
}
.pk-signup input{
  min-width:0;flex:1;border:0;background:transparent;padding:10px 16px;
  font-size:12px;color:var(--pk-fg);
}
.pk-signup input:focus{outline:0}
.pk-signup form:focus-within{box-shadow:0 0 0 2px var(--pk-gold-soft)}

/* ============================================================================
   19. FOOTER   green
   ========================================================================= */
.pk-footer{background:var(--pk-primary);color:var(--pk-primary-fg);padding-top:56px}
.pk-footer__grid{display:grid;grid-template-columns:repeat(2,1fr);gap:40px;padding-bottom:48px}
/* 1fr is minmax(auto,1fr), so a track cannot shrink below its min-content, and
   palesasbakesandtreats@gmail.com is one 197px unbreakable token. That blew the
   footer 22px past a 320px screen. min-width:0 lets the track shrink; the wrap
   rule gives the address somewhere to break. */
.pk-footer__grid > *{min-width:0}
.pk-footer__contact span{overflow-wrap:anywhere}
.pk-footer__brand{grid-column:span 2}
.pk-footer__brand img{height:34px;width:auto;background:var(--pk-surface);padding:5px 9px;border-radius:6px}
.pk-footer__brand p{margin-top:16px;max-width:22rem;font-size:12px;line-height:1.7;color:var(--pk-primary-soft)}
.pk-footer__social{margin-top:20px;display:flex;gap:10px}
.pk-footer__social a{
  display:grid;place-items:center;width:32px;height:32px;border-radius:var(--pk-pill);
  background:rgba(255,255,255,.1);transition:background .2s var(--pk-ease);
}
.pk-footer__social svg{width:14px;height:14px}
.pk-footer h3{font-size:14px;font-weight:600}
.pk-footer__grid ul{margin-top:16px;display:grid;gap:10px}
.pk-footer__grid ul a{
  display:inline-flex;align-items:center;min-height:26px;
  font-size:12px;color:var(--pk-primary-soft);transition:color .2s var(--pk-ease);
}
.pk-footer__contact li{display:flex;gap:8px;font-size:12px;line-height:1.6;color:var(--pk-primary-soft)}
.pk-footer__contact svg{width:14px;height:14px;flex:none;margin-top:3px;color:var(--pk-gold-soft)}
.pk-footer__bar{
  display:flex;flex-direction:column;align-items:center;justify-content:space-between;gap:12px;
  border-top:1px solid var(--pk-primary-line);padding-block:24px;
  font-size:11px;color:var(--pk-primary-soft);
}
.pk-footer__bar div{display:flex;gap:20px}
.pk-footer__bar a{display:inline-flex;align-items:center;min-height:26px}
@media (min-width:768px){
  .pk-footer__grid{grid-template-columns:repeat(4,1fr)}
  .pk-footer__bar{flex-direction:row}
}
@media (min-width:1024px){ .pk-footer__grid{grid-template-columns:repeat(6,1fr)} }
@media (hover:hover) and (pointer:fine){
  .pk-footer__grid ul a:hover{color:var(--pk-gold-soft)}
  .pk-footer__social a:hover{background:var(--pk-gold)}
}

/* ============================================================================
   CONTACT PAGE   the exact reference design Tony approved: centered hero (no
   photo, half height), boxed four-card info strip, form + dark store card with
   a decorative gradient swirl, cream promo band. 2 September 2026.

   Everything here is scoped under `.pk-contact-page` (set on <main>) so the
   contact-specific button/eyebrow treatment (uppercase, smaller, tighter)
   never leaks onto the shared .pk-btn/.pk-eyebrow used on other pages.
   Header, topbar and footer are the real shared components, untouched.
   ========================================================================= */

/* --- THE SUB-PAGE HERO. partials/hero.html. -------------------------------
   Every page except the homepage carries this one; index.html keeps .pk-hero
   and its carousel. Tony, 2 September 2026: two hero designs on this site and
   no more, so a page designed outside can be stitched onto ours without a
   third shape appearing.

   It was .pk-chero, and its eyebrow was scoped as ".pk-contact-page
   .pk-eyebrow" -- a class on the Contact page's <main>. Reused unchanged on
   another page the markup would have been byte-identical and the eyebrow would
   have rendered as plain grey 14px with no gold star, while shell.mjs check,
   check-seams.mjs and the icon-resolution check all called the page clean. A
   markup checker cannot see a stylesheet. So these are scoped to the COMPONENT
   now, never to a page. ------------------------------------------------- */
.pk-shero__in{max-width:680px;margin-inline:auto;text-align:center}
.pk-shero .pk-eyebrow{
  text-align:center;color:var(--pk-primary);font-size:12px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
}
.pk-shero .pk-eyebrow:before{content:"✦";color:var(--pk-gold);font-size:17px;margin-right:10px;vertical-align:-1px}
.pk-shero h1{
  margin-top:16px;font-family:var(--pk-display);font-weight:700;
  font-size:clamp(40px,5.2vw,64px);line-height:1.03;letter-spacing:-.035em;
  color:var(--pk-primary);
}
.pk-shero h1 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-shero__script{
  margin-top:5px;display:flex;align-items:center;justify-content:center;gap:10px;
  font-family:var(--pk-script);font-weight:400;font-size:clamp(37px,4.8vw,59px);line-height:.8;
  color:var(--pk-gold-ink);
}
.pk-shero__script svg{width:20px;height:20px;color:var(--pk-pink-ink)}
.pk-shero__sub{margin:22px auto 0;max-width:500px;font-size:15px;line-height:1.7;color:var(--pk-fg)}

/* --- info strip: five boxed cards, not bare columns ------------------------ */
.pk-cstrip{display:grid;grid-template-columns:1fr;gap:12px}
@media (min-width:560px){ .pk-cstrip{grid-template-columns:repeat(2,1fr)} }
@media (min-width:1024px){ .pk-cstrip{grid-template-columns:repeat(4,1fr)} }
/* Flex column + margin-top:auto on the last line (note/link) so all four cards
   land their bottom line on ONE baseline. Without it the content is plain block
   flow: "Visit us" wraps its address onto a second line and drops its
   "Get directions" below the other three, which reads as broken. Measured
   after the fix: all four last lines land on one baseline at 1440 (681px),
   at 1024, and two-up at 768/900. */
.pk-cstrip__item{
  display:flex;flex-direction:column;
  color:inherit;text-decoration:none;
  transition:transform 140ms var(--pk-ease),border-color 140ms var(--pk-ease);
  text-align:left;background:linear-gradient(145deg,var(--pk-surface) 0%,var(--pk-cream) 100%);
  border:1px solid var(--pk-cream-deep);border-radius:var(--pk-r);
  padding:20px 19px;min-height:160px;
}
/* the whole card is an <a> now: on a phone it was a 350x182 object of which
   only the 310x26 link did anything. No hover exists on touch, so the press
   state is the only feedback there is. */
.pk-cstrip__item:active{transform:scale(.985);border-color:var(--pk-pink)}
.pk-cstrip__icon{
  display:grid;place-items:center;flex:none;width:43px;height:43px;margin-bottom:13px;
  border-radius:var(--pk-pill);background:var(--pk-cream);color:var(--pk-primary);
}
.pk-cstrip__icon svg{width:21px;height:21px}
.pk-cstrip__item h3{font-family:var(--pk-display);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px}
.pk-cstrip__item p{margin:0;font-size:11.5px;line-height:1.55;color:var(--pk-muted)}
.pk-cstrip__item p span,.pk-cstrip__item p a{display:block;color:var(--pk-fg);font-weight:500;margin-bottom:6px;text-decoration:none}
.pk-cstrip__item p strong{display:block;color:var(--pk-fg);font-weight:500;margin-bottom:6px}
.pk-cstrip__note{display:block;margin-top:auto;padding-top:12px;font-size:11.5px;color:var(--pk-muted)}
.pk-cstrip__link{display:block;margin-top:auto;padding-top:12px;align-self:start;font-size:11.5px;font-weight:600;color:var(--pk-primary);text-decoration:underline;text-underline-offset:2px}
@media (hover:hover) and (pointer:fine){
  .pk-cstrip__link:hover,.pk-cstrip__item p a:hover{color:var(--pk-pink-ink)}
}

/* MOBILE: a contact method is a ROW, not a card. The stacked shape above (icon
   puck on its own line, then label, then value, then note) is a 4-across
   desktop composition; used vertically on a phone it made each card 174-192px
   and the four of them 750px, so they could never be seen together. Icon left,
   three lines right: 84-102px each, the whole section 504px, inside one screen.
   Everything above 559px is untouched - min-height and the margin-top:auto
   baseline trick still do their job in a real row. */
@media (max-width:559px){
  .pk-cstrip{gap:10px}
  .pk-cstrip__item{
    display:grid;grid-template-columns:40px 1fr;column-gap:14px;row-gap:1px;
    min-height:0;padding:14px 16px;align-items:center;
  }
  .pk-cstrip__icon{grid-row:1/span 3;width:40px;height:40px;margin-bottom:0;align-self:center}
  .pk-cstrip__icon svg{width:19px;height:19px}
  .pk-cstrip__item h3{margin-bottom:0;font-size:11px}
  /* min-width:0 lets the track shrink. Without it palesasbakesandtreats@gmail.com
     is one unbreakable 32-character token setting a min-content floor, and the
     card blew 23px past a 320px screen. This pair is load-bearing. */
  .pk-cstrip__item p{min-width:0}
  .pk-cstrip__item p span,.pk-cstrip__item p strong{
    margin-bottom:0;font-size:14px;overflow-wrap:anywhere;
  }
  .pk-cstrip__note,.pk-cstrip__link{margin-top:0;padding-top:0;font-size:11px}
}

/* --- the contact-page button: pill, uppercase, tight, scoped here only ----- */
.pk-contact-page .pk-btn{
  padding:12px 22px;font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
}
@media (hover:hover) and (pointer:fine){
  .pk-contact-page .pk-btn:hover{background:#54132b;opacity:1;box-shadow:none}
}

/* --- the split: form card + dark store card -------------------------------
   The only other form in this system is .pk-signup's single email pill.
   This is the first multi-field form, styled as soft filled pills. */
.pk-csplit{display:grid;grid-template-columns:1fr;gap:14px;align-items:stretch}
@media (min-width:900px){ .pk-csplit{grid-template-columns:1.25fr 1fr;gap:14px} }
.pk-ccard{background:var(--pk-surface);border:1px solid var(--pk-border);border-radius:var(--pk-r);padding:30px}
.pk-ccard h2{font-family:var(--pk-display);font-size:28px;line-height:1.15;color:var(--pk-primary)}
.pk-ccard>p{margin-top:7px;font-size:12px;color:var(--pk-muted)}

.pk-cform{margin-top:24px;display:grid;grid-template-columns:1fr;gap:15px 16px}
@media (min-width:640px){ .pk-cform{grid-template-columns:1fr 1fr} }
.pk-cform__row{display:contents}
.pk-cfield{display:grid;gap:6px}
.pk-cfield:has(textarea){grid-column:1/-1}
.pk-cfield label{font-size:12px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
.pk-cfield input,.pk-cfield select,.pk-cfield textarea{
  width:100%;border:1px solid var(--pk-cream-deep);border-radius:5px;background:var(--pk-surface);
  color:var(--pk-fg);padding:11px 12px;font:inherit;font-size:13.5px;outline:none;
  transition:border-color .25s var(--pk-ease),box-shadow .25s var(--pk-ease);
}
/* 16px is not a taste choice: iOS Safari zooms the viewport on focus for any
   control under 16px and does not zoom back out, so the visitor has to pinch
   to reach Send. Desktop keeps the intended 13.5px. */
@media (max-width:479px){
  .pk-cfield input,.pk-cfield select,.pk-cfield textarea{font-size:16px}
}
.pk-cfield input,.pk-cfield select{height:41px}
.pk-cfield textarea{min-height:105px;resize:vertical}
.pk-cfield input::placeholder,.pk-cfield textarea::placeholder{color:var(--pk-muted)}
.pk-cfield select{
  appearance:none;padding-right:34px;
  background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6" fill="none" stroke="%236a6a6a" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-repeat:no-repeat;background-position:right 14px center;background-size:16px;
}
.pk-cfield select:invalid{color:var(--pk-muted)}
.pk-cfield input:focus,.pk-cfield select:focus,.pk-cfield textarea:focus{
  border-color:var(--pk-primary);box-shadow:0 0 0 3px rgba(110,25,55,.08);
}
.pk-cfield input:focus-visible,.pk-cfield select:focus-visible,.pk-cfield textarea:focus-visible{
  outline:2px solid var(--pk-primary);outline-offset:1px;
}
.pk-cform > button{grid-column:1/-1;width:100%;height:43px}
.pk-cform__status{
  grid-column:1/-1;margin-top:2px;min-height:16px;text-align:center;
  font-size:11px;color:var(--pk-muted);
}
.pk-cform__status:before{content:"✦";color:var(--pk-gold);margin-right:8px}
/* The star is part of the MESSAGE, not decoration. With no message the rule
   above still painted a lone gold glyph floating under the Send button.
   Suppress the GLYPH, not the element: the element keeps its reserved
   min-height (so the card does not jump on submit) and stays in the
   accessibility tree (it is role="status"; display:none would have pulled a
   live region out of it). JS sets textContent on submit, so :empty stops
   matching and the star returns with the message. */
.pk-cform__status:empty:before{content:none}

.pk-cstore{
  position:relative;overflow:hidden;border-radius:var(--pk-r);
  background:var(--pk-primary);color:var(--pk-primary-fg);padding:31px 32px;
}
.pk-cstore:after{
  content:"";position:absolute;right:-20px;bottom:-35px;width:230px;height:280px;opacity:.26;
  background:
    radial-gradient(ellipse at 50% 85%,transparent 0 42%,var(--pk-gold-soft) 43% 44%,transparent 45%),
    radial-gradient(ellipse at 22% 58%,transparent 0 40%,var(--pk-gold-soft) 41% 42%,transparent 43%),
    radial-gradient(ellipse at 78% 45%,transparent 0 38%,var(--pk-gold-soft) 39% 40%,transparent 41%);
  transform:rotate(-18deg);
}
.pk-cstore__badge{display:none}
.pk-cstore h2{position:relative;z-index:2;font-family:var(--pk-display);font-size:30px;color:var(--pk-primary-fg)}
.pk-cstore h2:after{content:"✦";font-size:14px;color:var(--pk-gold-soft);margin-left:13px;vertical-align:7px}
.pk-cstore>p{position:relative;z-index:2;margin-top:7px;max-width:300px;font-size:12px;line-height:1.7;color:var(--pk-primary-soft)}
.pk-cstore__divider{height:1px;width:180px;margin-top:24px;margin-bottom:23px;background:rgba(255,255,255,.2)}
.pk-cstore__list{position:relative;z-index:2;display:grid;gap:19px}
/* stretch matches this card to the form beside it; the CTA is bottom-anchored
   in the .pk-cstore .pk-btn--light rule below, NOT here: an earlier rule of the
   same specificity loses to it on order. */
.pk-cstore{display:flex;flex-direction:column}
.pk-cstore__row{display:grid;grid-template-columns:25px 1fr;gap:10px;align-items:start}
.pk-cstore__row i{display:block;width:20px;height:20px;color:#fff}
.pk-cstore__row i svg{width:20px;height:20px}
.pk-cstore__row p{font-size:11.5px;line-height:1.55;color:var(--pk-primary-soft)}
/* inline-block + padding: as a plain inline link this measured 15px tall,
   under the 24px tap minimum. Inline boxes ignore vertical padding for hit
   area, so the display change is the load-bearing half. */
.pk-cstore__row p a{display:inline-block;padding-block:5px}
.pk-cstore .pk-btn--light{
  position:relative;z-index:2;margin-top:auto;align-self:start;background:transparent;
  border:1px solid rgba(255,255,255,.65);color:#fff;
}
/* auto only distributes leftover space; when the card is NOT stretched (one
   column, under 900px) there is none, so keep the original 26px as the floor. */
.pk-cstore__list + .pk-btn--light{margin-top:26px}
@media (min-width:900px){ .pk-cstore__list + .pk-btn--light{margin-top:auto} }
@media (hover:hover) and (pointer:fine){
  .pk-cstore .pk-btn--light:hover{background:#fff;color:var(--pk-primary)}
}

/* --- bottom promo band: compact 3-part banner, not a padded card ---------- */
.pk-cpromo{
  background:var(--pk-cream);border-radius:var(--pk-r);min-height:92px;
  display:grid;grid-template-columns:1fr;gap:20px;align-items:center;padding:22px;
}
@media (min-width:1024px){
  .pk-cpromo{grid-template-columns:auto 1fr auto;padding:18px 42px}
}
.pk-cpromo__lead{display:flex;align-items:flex-start;gap:16px}
.pk-cpromo__icon{
  display:grid;place-items:center;flex:none;width:54px;height:54px;border-radius:var(--pk-pill);
  background:var(--pk-surface);color:var(--pk-primary);
}
.pk-cpromo__icon svg{width:25px;height:25px}
.pk-cpromo h2{font-family:var(--pk-display);color:var(--pk-primary);font-size:15px;margin-bottom:3px}
.pk-cpromo h2 em{font-style:normal}
.pk-cpromo p{margin:0;font-size:11.5px;color:var(--pk-muted)}
.pk-contact-page .pk-cpromo .pk-btn{background:var(--pk-surface);color:var(--pk-primary);border:1px solid var(--pk-primary);min-width:270px;justify-content:center}
@media (hover:hover) and (pointer:fine){
  .pk-contact-page .pk-cpromo .pk-btn:hover{background:var(--pk-primary);color:#fff}
}
@media (max-width:639px){ .pk-contact-page .pk-cpromo .pk-btn{min-width:0;width:100%} }


/* ============================================================================
   ABOUT PAGE   no photographs, and no editorial-minimalism costume either

   Second rebuild, 1 September 2026. The first version reused homepage photos.
   The second replaced them with a full-screen 86px statement, a monogram
   watermark, Sec. 01/02/03 numbering and a big-number strip, and Tony's
   verdict was that it looked AI-generated and not real. He was right, and
   craft-floor.md names three of those four devices in its refuse list: the
   hero-metric template, an uppercase eyebrow over every section, and section
   numbering where the sequence carries no information. Flavours are not
   step one of anything.

   So this version stays inside the warm world the homepage already has,
   cream and berry and gold, and spends the page on the one true and unusual
   fact in PRODUCT.md: four things a home baker normally gets from four
   different places happen in one small shop, run by one person who used to
   teach for a living. No new copy is written: every line is either hers
   verbatim, already approved on the homepage, or a real price.
   ========================================================================= */

/* --- opening. A banner, not a screen. The build is already 17 phone screens
       tall, so this earns its height or it does not get any. ------------- */
.pk-abtop{background:var(--pk-cream)}
.pk-abtop__in{padding-block:clamp(30px,5vw,52px);max-width:680px}
.pk-abtop h1{font-size:clamp(27px,3.6vw,38px);line-height:1.18;letter-spacing:-.02em}
.pk-abtop h1 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-abtop p{margin-top:14px;max-width:56ch;font-size:14px;line-height:1.75;color:var(--pk-muted)}
.pk-abtop__meta{
  margin-top:20px;display:flex;flex-wrap:wrap;gap:8px 22px;
  font-size:12px;color:var(--pk-gold-ink);font-weight:500;
}

/* --- her letter ------------------------------------------------------------
   Kept from the last version because it is the only part that was right:
   her own words, at a size someone actually reads, signed. */
/* Sits in a cream card rather than floating in white space. Centred text in
   an empty white band read as stranded, which is the same "nothing was
   decided here" look the whole page was rejected for. */
/* Left-aligned, not centred. Every other band on this page starts at the
   container's left edge, and one centred object among them read as a stray
   rather than as emphasis. */
.pk-letter{
  max-width:760px;
  background:var(--pk-cream);border-radius:var(--pk-r-xl);
  padding:clamp(28px,4vw,52px) clamp(22px,4vw,56px);
}
.pk-letter__from{
  font-family:var(--pk-script);font-size:clamp(32px,3.6vw,44px);line-height:1.1;
  color:var(--pk-pink-ink);
}
.pk-letter p{
  margin-top:18px;font-size:clamp(14.5px,1.4vw,16.5px);line-height:1.85;color:var(--pk-fg);
}
.pk-letter__sign{
  margin-top:26px;padding-top:20px;border-top:1px solid var(--pk-cream-deep);
  font-size:12.5px;line-height:1.7;color:var(--pk-gold-ink);font-weight:500;
}

/* --- the four things. Rows, not cards, not icons in circles, not numbered.
       Each carries a real fact and a real price, because specifics are what
       a chain cannot copy and what stops a page reading as filler. ------- */
.pk-four{border-top:1px solid var(--pk-border)}
.pk-thing{
  display:grid;grid-template-columns:1fr;gap:6px;
  padding-block:clamp(20px,2.6vw,28px);border-bottom:1px solid var(--pk-border);
}
@media (min-width:768px){
  /* the description gets a real measure instead of stretching the full
     width, and the fact column is wide enough that a price never sits
     jammed against the container edge */
  .pk-thing{grid-template-columns:minmax(0,13rem) minmax(0,34rem) minmax(0,11rem);gap:36px;align-items:baseline}
  .pk-thing__fact{text-align:right}
}
.pk-thing h3{font-size:clamp(17px,1.8vw,20px);line-height:1.25;letter-spacing:-.01em}
.pk-thing p{font-size:13.5px;line-height:1.7;color:var(--pk-muted)}
.pk-thing__fact{
  font-size:13px;font-weight:600;color:var(--pk-primary);white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
@media (max-width:767px){ .pk-thing__fact{margin-top:4px} }

/* --- the classes. Her six, with what you take home. The one place on this
       page that goes deep, because teaching is what she did before this. -- */
/* One column, full width, like a price list on a wall. Two columns put the
   dividers at different heights on the left and right because the rows are
   not the same depth, and a rule that does not line up with the rule beside
   it reads as broken rather than as a grid. */
.pk-classes{display:grid;grid-template-columns:1fr;gap:0}
.pk-class{
  display:flex;align-items:baseline;justify-content:space-between;gap:24px;
  padding-block:14px;border-bottom:1px solid rgba(110,25,55,.13);
}
.pk-class h3{font-size:14.5px;font-weight:600;line-height:1.35}
.pk-class p{margin-top:3px;font-size:12.5px;line-height:1.6;color:var(--pk-muted)}
.pk-class b{
  font-size:14px;color:var(--pk-primary);white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.pk-terms{
  margin-top:22px;padding:18px 20px;border-radius:var(--pk-r);
  background:var(--pk-cream);font-size:12.5px;line-height:1.75;color:var(--pk-fg);
  max-width:62ch;
}

/* --- her sentence, on the berry. The page's one loud moment. ------------- */
.pk-pull{background:var(--pk-primary);color:var(--pk-primary-fg)}
.pk-pull__in{padding-block:clamp(44px,6vw,72px);max-width:820px;margin-inline:auto;text-align:center}
.pk-pull blockquote{
  margin:0;font-family:var(--pk-display);font-weight:700;
  font-size:clamp(21px,3.1vw,38px);line-height:1.28;letter-spacing:-.02em;
}
.pk-pull blockquote em{font-style:normal;color:var(--pk-gold-soft)}
.pk-pull cite{
  display:block;margin-top:20px;font-style:normal;font-size:12px;color:var(--pk-primary-soft);
}

/* --- closing ------------------------------------------------------------- */
.pk-close{max-width:560px}
.pk-close h2{font-size:clamp(22px,3vw,30px);line-height:1.22;letter-spacing:-.02em}
.pk-close h2 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-close p{margin-top:12px;font-size:13.5px;line-height:1.75;color:var(--pk-muted)}
.pk-close__cta{margin-top:22px;display:flex;flex-wrap:wrap;gap:12px 22px;align-items:center}

/* .pk-btn--link is a MODIFIER and was used bare twice on the homepage. The
   icon rule lives on the base class, so the arrow rendered at its intrinsic
   556px: a near-black square half a viewport tall, on the page that is the
   source of truth for every other page. Sizing the icon on the modifier too
   means the mistake cannot render that way again. */
.pk-btn--link svg{width:14px;height:14px;flex:none}

/* ============================================================================
   THE NAV DROPDOWN. Added 2 September 2026, when the site went from 5 pages to
   31 and Tony asked for every one of them to be reachable from the top menu.

   There was no dropdown, submenu, popover or <details> anywhere in this
   project, so this is modelled on the two disclosure patterns that already
   exist here and are proven: the burger and the FAQ accordion. Both are a
   CLICK toggle driven by the `hidden` attribute plus aria-expanded, both close
   on Escape and return focus. Hover-to-open was rejected: it cannot be used
   from a keyboard or a touch screen, and this menu has to work on a phone.

   The panel is `position:absolute` inside `.pk-hassub`, which is the only
   positioned ancestor between it and the sticky header. Without that
   position:relative it would anchor to `.pk-header` and sit under the logo.
   ========================================================================= */
.pk-hassub{position:relative}

.pk-subbtn{
  display:inline-flex;align-items:center;gap:6px;
  border:0;background:none;padding:0;cursor:pointer;
  font-family:var(--pk-sans);font-size:13px;font-weight:500;
  color:rgba(43,43,43,.8);
  transition:color .2s var(--pk-ease);
}
.pk-subbtn[aria-expanded="true"]{color:var(--pk-primary)}
.pk-pm{width:13px;height:13px;flex:none;overflow:visible}
.pk-pm__v{
  transform-box:view-box;transform-origin:50% 50%;
  transition:transform 220ms var(--pk-ease);
}
.pk-subbtn[aria-expanded="true"] .pk-pm__v{transform:rotate(90deg)}

.pk-sub{
  position:absolute;top:calc(100% + 14px);right:0;z-index:60;
  min-width:220px;padding:10px;
  background:var(--pk-surface);
  border:1px solid var(--pk-border);border-radius:var(--pk-r-lg);
  box-shadow:0 20px 45px -22px rgba(80,35,45,.5);
}
/* The panel would otherwise open with its top edge hard against the header and
   no way for the pointer to travel between them without closing it. */
.pk-sub:before{content:"";position:absolute;left:0;right:0;top:-14px;height:14px}

.pk-sub ul{list-style:none;margin:0;padding:0;display:block}
.pk-sub a{
  display:block;padding:8px 12px;border-radius:var(--pk-r);
  font-size:13px;color:var(--pk-fg);text-decoration:none;
  transition:background .2s var(--pk-ease),color .2s var(--pk-ease);
}
.pk-sub a[aria-current="page"]{color:var(--pk-primary);font-weight:600}

/* Four groups side by side. Fifteen links in one column is the mess this was
   asked to avoid; four short columns is scannable. */
.pk-sub--wide{
  display:grid;grid-template-columns:repeat(4,minmax(150px,1fr));gap:4px 10px;
  right:0;padding:16px;
  /* Copywriting is parked; today's labels are not final. 1fr under
     shrink-to-fit sizing grows to a column's full unbroken content width, so
     a longer future label could balloon one column and drag the panel wide.
     760px is a ceiling above the 664px this measures today, not a value
     tuned to today's exact copy; the calc() half is the narrow-viewport
     safety net. */
  max-width:min(760px, calc(100vw - 40px));
}
.pk-sub__group h3{
  margin:0 0 6px;padding:0 12px;
  font-family:var(--pk-sans);font-size:10px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--pk-muted);
}

/* The mobile menu carries the same links under plain headings. A disclosure
   inside a disclosure on a phone is worse than a list you can scroll. */
.pk-mnav__h{
  /* no padding-inline: this already sits inside a .pk-wrap, which applies
     --pk-pad, so setting it again indented the heading 40px while every link
     it labels started at 20px. Visible in the 7 Sep screenshot. */
  margin:14px 0 2px;padding:8px 0 0;
  font-family:var(--pk-sans);font-size:10px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--pk-muted);
  border-top:1px solid var(--pk-border);
}
/* Sized by --pk-mnav-top (the header row alone), NEVER by --pk-chrome.
   --pk-chrome includes .pk-header, and this drawer lives inside .pk-header, so
   it was a value computed from itself: open the menu, let anything re-measure
   (a rotation, a phone address bar hiding, a scroll), and the drawer collapsed
   to an 11px sliver. It is also simply the right number, because by the time
   this is open the top bar and the marquee have scrolled away. The :root
   default is 110px, the at-rest value, so the drawer is usable even before the
   footer script has measured. See [[l-245]]. */
.pk-mnav{max-height:calc(100dvh - var(--pk-mnav-top));overflow-y:auto;overscroll-behavior:contain}

@media (hover:hover) and (pointer:fine){
  .pk-subbtn:hover{color:var(--pk-primary)}
  .pk-sub a:hover{background:var(--pk-cream);color:var(--pk-primary)}
}

/* ========================================================================
   THE HEADER OVERLAY SYSTEM (7 Sept 2026)

   One shared visual vocabulary for the four new header panels (search
   overlay, wishlist drawer, cart drawer, account popover), all opened by
   PBT.disclosure() in pbt-overlays.js. Same [hidden] + [data-open] two-step
   as .pk-sub above, same motion-house tokens (--ease-move / --dur-panel)
   on all four so none of them feels like it belongs to a different site.

   .pk-scroll-lock is applied to <body> by every panel that is full-viewport
   at rest (the search overlay, the wishlist drawer, the cart drawer -
   pbt-*.js all pass lockScroll:true) so the page cannot scroll behind them;
   the account popover is the one exception, since it is a small menu
   anchored to its own trigger, not a full-viewport surface, and does not
   request it. (An earlier version of this comment said the search overlay
   was also an exception - it was not; caught by the Reviewer, 7 Sept.)
   ======================================================================== */
.pk-scroll-lock{overflow:hidden}

/* A fixed third-party widget sits above every panel in this theme. The four
   panels are z-index 80-85; the chat bubble this site runs is at 997 today.

   THIS IS THE SAME DEFECT AS [[l-245]]'s finding 1, on a different panel.
   There it covered the mobile menu's last link. Here, measured live on
   8 Sept 2026 at 390px with a four-item basket, it covered the LEFT 41%
   (143px of 350px) of the cart drawer's Checkout button:
   document.elementFromPoint() 30px in from that button's left edge returned
   the widget, not the Checkout link. A shopper tapping that half opened
   WhatsApp instead of paying. It reaches the "View cart" link too. At 1440px
   it is 1%, so this is a phone-only failure and invisible on a desktop pass.

   The split below is deliberate and is L-245's conclusion applied, not
   reinvented: the z-index rule is LOAD-BEARING and NAMES NO VENDOR, so it
   covers any fixed widget this site picks up later; hiding .chaty-widget is
   COSMETIC, so if Chaty renames that class the checkout is still tappable.
   L-245 recorded that putting the vendor's class on the load-bearing half was
   the first attempt and the wrong way round - this file made that same first
   attempt again on 8 Sept before that entry was re-read.

   CORRECTED 8 Sept 2026, Reviewer finding F2: the load-bearing number was
   first written as 998 - one more than Chaty's CURRENT 997, a site-owner
   setting in the Chaty plugin that can be changed at any time, and other
   chat widgets ship far higher (Tawk.to defaults near 2 billion). One more
   than a number we do not control is not "names no vendor", it is the same
   fragile coupling with an extra digit. Set to 9999 instead: comfortably
   above anything a chat/cookie/back-to-top widget plausibly ships at,
   without being tied to any one vendor's current value.

   Scoped to .pk-scroll-lock, which every full-viewport panel sets while open
   (search overlay, wishlist drawer, cart drawer), so it lifts only for as
   long as a modal surface is actually up. The mobile menu keeps its own
   .pk-mnav-open pair above, lifting .pk-header rather than the panel.
   CORRECTED 8 Sept 2026, Reviewer finding F4: this used to say that pair
   works "because that drawer lives inside the header" - it does not, every
   panel in this theme is a body-level sibling of .pk-header (see
   header.php's own comment above the search overlay markup), and the
   .pk-mnav-open fix only works BECAUSE of that: a sticky ancestor's z-index
   cannot reach outside its own stacking context, so if the drawer really
   were nested inside .pk-header, lifting the header would never have raised
   it above a body-level widget in the first place. Full correction, with
   the stacking-context reasoning, in palesas.js next to the original
   comment. */
.pk-scroll-lock .pk-search-overlay,
.pk-scroll-lock .pk-wish-drawer,
.pk-scroll-lock .pk-cart-drawer{z-index:9999}
.pk-scroll-lock .chaty-widget{display:none !important}

/* Reviewer finding F1, 8 Sept 2026: the failure-state toast (PBT.toast(),
   defined further down this file) is a FIFTH surface the same widget can
   cover, and neither mechanism above reaches it. It fires from a search or
   wishlist failure with NO panel open - .pk-scroll-lock is not set, and
   .pk-mnav-open is unrelated - so it was invisible to both fixes. At 390px
   it sits left:50%, bottom:24px, spanning roughly x16-374: the same bottom
   corner as the chat launcher. Given its own z-index, independent of any
   panel state, rather than folded into the .pk-scroll-lock rule above. */
.pk-toast{z-index:9999}

/* The shop's mobile filter panel is the FOURTH surface that sets
   .pk-scroll-lock (pbt-shop-filters.js), and it was nearly missed here: it
   lives in shop.css rather than with the other panels, and it is only a
   fixed drawer below 768px - above that it is an ordinary grid column in the
   sidebar. So the lift is scoped to the width where it is actually a drawer;
   applying z-index:998 to the desktop sidebar would float it over the page
   for no reason. Grep lockScroll before adding a fifth panel. */
@media (max-width:767px){
  .pk-scroll-lock .shop-filters{z-index:9999}
}

/* Account popover. position:fixed so it escapes .pk-header's own stacking
   and can never be clipped by an ancestor's overflow, per the "overlays
   escape their container" rule - it does not live inside .pk-header at
   all in the markup, for the same reason.

   `top` and `right` are set by JS (assets/js/pbt-account.js's position()),
   not here. A first version anchored off --pk-headerrow - the row's
   constant HEIGHT (64px) - as if it were a vertical screen position, and
   `right:var(--pk-pad)` anchored to the viewport edge rather than the
   button, so above 1240px wide (once .pk-wrap hits its 1200px max-width)
   the popover drifted ~112px right of the icon that opened it. Both were
   real, found by the Reviewer's design pass. There is no static value that
   is correct at every scroll position and viewport width against a button
   sitting inside a `max-width` wrapper - it has to be measured against the
   button's own getBoundingClientRect(), the same live-measurement
   discipline palesas.js already uses for the header's chrome variables. */
.pk-popover{
  position:fixed;z-index:70;
  width:min(320px, calc(100vw - 2 * var(--pk-pad)));
}
.pk-popover__panel{
  position:relative;background:var(--pk-surface);
  border:1px solid var(--pk-border);border-radius:var(--pk-r-lg);
  box-shadow:0 24px 48px -16px rgba(31,10,20,.32);
  padding:20px;
  opacity:0;transform:translateY(-8px) scale(.98);transform-origin:top right;
  transition:opacity var(--dur-panel) var(--ease-move),transform var(--dur-panel) var(--ease-move);
}
.pk-popover[data-open] .pk-popover__panel{opacity:1;transform:none}
.pk-popover__close{
  position:absolute;top:12px;right:12px;width:28px;height:28px;
  display:flex;align-items:center;justify-content:center;
  border:0;background:none;padding:0;cursor:pointer;color:var(--pk-muted);
  transition:color var(--dur-tap) var(--ease-ui);
}
.pk-popover__close svg{width:16px;height:16px}
.pk-popover__close:hover{color:var(--pk-fg)}
.pk-popover__greet{margin:0 0 12px;padding-right:24px;font-family:var(--pk-display);font-weight:600}
.pk-popover__links{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.pk-popover__links a{
  display:block;padding:10px 8px;border-radius:var(--pk-r);
  color:var(--pk-fg);text-decoration:none;font-size:14px;
  transition:background var(--dur-tap) var(--ease-ui);
}
.pk-popover__title{margin:0 0 14px;padding-right:24px;font-family:var(--pk-display);font-weight:600;font-size:1.1rem}
.pk-popover__error{
  /* --pk-pink-ink is documented in this file's own :root comment as
     "large display type" only (4.62-4.07:1, below AA's 4.5:1 floor for
     normal text) - this is the single most important string in the
     component, at .85rem, so it needs the token that actually clears AA
     at that size. --pk-primary clears 11.31:1 on white. Found by the
     Reviewer's verification pass. */
  margin:0 0 12px;padding:10px 12px;border-radius:var(--pk-r);
  background:rgba(217,46,104,.08);color:var(--pk-primary);font-size:.85rem;
}
.pk-popover__login label{display:block;font-size:.8rem;color:var(--pk-muted);margin:10px 0 4px}
/* Scoped to text/password only - a bare `input` selector here also matched
   the "Remember me" checkbox below, stretching it to `width:100%` and, via
   `font:inherit`, colliding with .pk-popover__remember's own layout (next
   rule). Found by the Reviewer's design pass: the checkbox rendered
   centred alone with its "Remember me" label orphaned on the line below. */
.pk-popover__login input[type="text"],
.pk-popover__login input[type="password"]{
  width:100%;padding:10px 12px;border:1px solid var(--pk-border);border-radius:var(--pk-r);
  background:var(--pk-bg);color:var(--pk-fg);
  /* 16px, not inherited: iOS Safari zooms the viewport when a focused text
     control is under 16px and does not zoom back out. This popover's own
     onOpen() auto-focuses the first field, so this floor applies the
     moment the popover opens, not just on manual tap - see the site-wide
     16px rule lower in this file for the general form of this problem. */
  font:inherit;font-size:16px;
}
.pk-popover__login input:focus-visible{outline:2px solid var(--pk-primary);outline-offset:1px}
/* Two classes, so this beats .pk-popover__login label's (0,1,1) instead of
   losing to it - the exact collision described above. */
.pk-popover__login .pk-popover__remember{
  display:flex;align-items:center;gap:8px;margin:14px 0 0;font-size:.85rem;color:var(--pk-muted);
}
.pk-popover__login .pk-btn{width:100%;margin-top:16px;justify-content:center}
.pk-popover__forgot{display:block;margin-top:12px;font-size:.85rem;text-align:center;color:var(--pk-muted)}

@media (hover:hover) and (pointer:fine){
  .pk-popover__links a:hover{background:var(--pk-cream);color:var(--pk-primary)}
}

/* THE MOBILE DRAWER OPENS (D19, to-check.md, closed 7 Sept 2026). It used to
   snap open with zero transition, next to a desktop dropdown that animates -
   the same drawer, two different manners, on the same site. Per the
   motion-house rule against ever animating height/top/left on an element
   that is live-reflowing the page, [hidden] itself stays an instant
   attribute toggle (assets/js/palesas.js still sets it directly); only the
   drawer's own CONTENT fades and rises in, using the same tokens as the
   other four new panels above, so the whole header now reads as one system. */
.pk-mnav .pk-wrap{
  opacity:0;transform:translateY(-8px);
  transition:opacity var(--dur-panel) var(--ease-move),transform var(--dur-panel) var(--ease-move);
}
.pk-mnav[data-open] .pk-wrap{opacity:1;transform:none}

@media (prefers-reduced-motion: reduce){
  .pk-popover__panel,.pk-mnav .pk-wrap{transition-duration:1ms}
}

/* Search overlay. position:fixed, inset:0 - escapes .pk-header entirely,
   same reasoning as .pk-popover above. The scrim and panel fade/rise on
   [data-open] using the same house tokens as every other new panel. */
.pk-search-overlay{position:fixed;inset:0;z-index:80}
.pk-search-overlay__scrim{
  position:absolute;inset:0;border:0;padding:0;cursor:pointer;
  background:rgba(43,43,43,.5);opacity:0;transition:opacity var(--dur-ui) var(--ease-ui);
}
.pk-search-overlay[data-open] .pk-search-overlay__scrim{opacity:1}
.pk-search-overlay__panel{
  position:relative;max-width:640px;margin:80px auto 0;padding:0 var(--pk-pad);
  opacity:0;transform:translateY(-16px);
  transition:opacity var(--dur-panel) var(--ease-move),transform var(--dur-panel) var(--ease-move);
}
.pk-search-overlay[data-open] .pk-search-overlay__panel{opacity:1;transform:none}
.pk-search-overlay form{
  display:flex;align-items:center;gap:10px;
  background:var(--pk-surface);border-radius:var(--pk-r-lg);
  box-shadow:0 24px 64px -16px rgba(31,10,20,.4);
  padding:16px 20px;
}
.pk-search-overlay form svg{width:20px;height:20px;color:var(--pk-muted);flex:none}
.pk-search-overlay input{
  flex:1;border:0;background:none;font:inherit;font-size:16px;color:var(--pk-fg);
}
.pk-search-overlay input:focus{outline:none}
.pk-search-overlay__close{
  border:0;background:none;padding:4px;cursor:pointer;color:var(--pk-muted);flex:none;
  transition:color var(--dur-tap) var(--ease-ui);
}
.pk-search-overlay__close svg{width:18px;height:18px}
.pk-search-overlay__close:hover{color:var(--pk-fg)}

.pk-search__results{
  margin-top:8px;background:var(--pk-surface);border-radius:var(--pk-r-lg);
  box-shadow:0 24px 64px -16px rgba(31,10,20,.4);
  max-height:60vh;overflow-y:auto;
}
.pk-search__results:empty{display:none;box-shadow:none}
.pk-search__list{list-style:none;margin:0;padding:8px}
.pk-search__result{
  display:flex;align-items:center;gap:12px;padding:10px;border-radius:var(--pk-r);
  text-decoration:none;color:var(--pk-fg);
  transition:background var(--dur-tap) var(--ease-ui);
}
.pk-search__result img{width:48px;height:48px;border-radius:var(--pk-r);object-fit:cover;flex:none;background:var(--pk-bg)}
.pk-search__meta{display:flex;flex-direction:column;gap:2px;min-width:0}
.pk-search__cat{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--pk-muted)}
.pk-search__title{font-weight:600;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pk-search__price{font-size:13px;color:var(--pk-primary)}
.pk-search__seeall{border-top:1px solid var(--pk-border);padding:4px;list-style:none}
.pk-search__seeall a{
  display:block;padding:10px;border-radius:var(--pk-r);text-align:center;
  font-size:13px;font-weight:600;color:var(--pk-primary);text-decoration:none;
  transition:background var(--dur-tap) var(--ease-ui);
}
.pk-search__empty{margin:0;padding:16px 20px;font-size:14px;color:var(--pk-muted)}
.pk-search__empty a{color:var(--pk-primary)}
.pk-search__loading{margin:0;padding:16px 20px;font-size:14px;color:var(--pk-muted)}

@media (hover:hover) and (pointer:fine){
  .pk-search__result:hover{background:var(--pk-cream)}
  .pk-search__seeall a:hover{background:var(--pk-cream)}
}
.pk-search__result:focus-visible,.pk-search__seeall a:focus-visible{outline:2px solid var(--pk-primary);outline-offset:-2px;background:var(--pk-cream)}

@media (max-width:680px){
  .pk-search-overlay__panel{margin-top:24px}
}

@media (prefers-reduced-motion: reduce){
  .pk-search-overlay__scrim,.pk-search-overlay__panel{transition-duration:1ms}
}

/* Shared modal chrome, added for the class-enrol and waitlist/notify-me
   popups (baking-classes.css / single-class.css). Same [hidden]+[data-open]
   two-step, PBT.disclosure(), fade/rise as .pk-search-overlay above - kept
   here rather than in either page stylesheet because both templates need
   identical modal chrome and neither one "owns" it more than the other. */
.pk-modal{position:fixed;inset:0;z-index:90}
.pk-modal__backdrop{
  position:absolute;inset:0;border:0;padding:0;cursor:pointer;
  background:rgba(43,43,43,.5);opacity:0;transition:opacity var(--dur-ui) var(--ease-ui);
}
.pk-modal[data-open] .pk-modal__backdrop{opacity:1}
.pk-modal__panel{
  position:relative;max-width:520px;margin:60px auto;padding:0 var(--pk-pad);
  max-height:calc(100vh - 120px);overflow-y:auto;
  opacity:0;transform:translateY(-16px);
  transition:opacity var(--dur-panel) var(--ease-move),transform var(--dur-panel) var(--ease-move);
}
.pk-modal[data-open] .pk-modal__panel{opacity:1;transform:none}
.pk-modal__card{
  position:relative;background:var(--pk-surface);border-radius:var(--pk-r-xl);
  box-shadow:0 24px 64px -16px rgba(31,10,20,.4);padding:32px 28px;
}
.pk-modal__close{
  position:absolute;top:14px;right:14px;border:0;background:none;padding:6px;cursor:pointer;
  color:var(--pk-muted);transition:color var(--dur-tap) var(--ease-ui);
}
.pk-modal__close svg{width:20px;height:20px}
.pk-modal__close:hover{color:var(--pk-fg)}
.pk-modal__card h2{margin:0 0 6px;font-family:var(--pk-display)}
.pk-modal__card > p{margin:0 0 20px;color:var(--pk-muted)}
/* The enrol/waitlist forms moved out of .enrol (single-class.css) into this
   shared modal card, and single-class.css's own ".enrol form" grid no
   longer wraps them, so the field layout is redeclared here rather than
   left unstyled. */
.pk-modal__card form{display:grid;grid-template-columns:1fr 1fr;gap:13px}
.pk-modal__card form input,.pk-modal__card form select,.pk-modal__card form textarea{
  border:1px solid var(--pk-border);border-radius:var(--pk-r);padding:12px;
  font:13px "DM Sans",sans-serif;width:100%;max-width:100%;box-sizing:border-box;
}
.pk-modal__card form select,.pk-modal__card form .pk-btn{grid-column:1/-1}
.pk-modal__card #message,.pk-modal__card #waitlist-message{margin:0;font-size:13px;color:var(--pk-muted)}
@media (max-width:480px){
  .pk-modal__card form{grid-template-columns:1fr}
}

@media (max-width:680px){
  .pk-modal__panel{margin:24px auto}
}
@media (prefers-reduced-motion: reduce){
  .pk-modal__backdrop,.pk-modal__panel{transition-duration:1ms}
}

/* Header icon badges (wishlist count today, cart count when that area
   ships). One rule, both icons: any .pk-icon that carries a count badge
   needs position:relative so the badge can anchor to its own corner. */
.pk-header__tools .pk-icon{position:relative}
.pk-wish-count,.pk-cart-count{
  position:absolute;top:2px;right:2px;min-width:15px;height:15px;padding:0 3px;
  display:flex;align-items:center;justify-content:center;border-radius:var(--pk-pill);
  background:var(--pk-pink);color:#fff;font-size:10px;font-weight:700;line-height:1;
}
.pk-wish-count:empty,.pk-cart-count:empty{display:none}

/* Wishlist drawer. Same fixed/inset:0 escape-the-header pattern as the
   search overlay above, sliding in from the right edge instead of
   dropping from the top - the cart drawer (next area) will share this
   exact pattern, so a fix here benefits both once cart ships. */
.pk-wish-drawer{position:fixed;inset:0;z-index:85}
.pk-wish-drawer__scrim{
  position:absolute;inset:0;border:0;padding:0;cursor:pointer;
  background:rgba(43,43,43,.5);opacity:0;transition:opacity var(--dur-ui) var(--ease-ui);
}
.pk-wish-drawer[data-open] .pk-wish-drawer__scrim{opacity:1}
.pk-wish-drawer__panel{
  position:absolute;top:0;right:0;height:100%;width:min(420px,100vw);
  background:var(--pk-surface);box-shadow:-24px 0 64px -16px rgba(31,10,20,.4);
  display:flex;flex-direction:column;
  transform:translateX(100%);
  transition:transform var(--dur-panel) var(--ease-move);
}
.pk-wish-drawer[data-open] .pk-wish-drawer__panel{transform:translateX(0)}
.pk-wish-drawer__head{
  display:flex;align-items:center;justify-content:space-between;
  padding:20px;border-bottom:1px solid var(--pk-border);flex:none;
}
.pk-wish-drawer__head h2{margin:0;font-family:var(--pk-display);font-size:1.1rem}
.pk-wish-drawer__close{border:0;background:none;padding:4px;cursor:pointer;color:var(--pk-muted)}
.pk-wish-drawer__close svg{width:20px;height:20px}
.pk-wish-drawer__close:hover{color:var(--pk-fg)}
.pk-wish-drawer__body{flex:1;overflow-y:auto;padding:8px 20px}
.pk-wish-item{display:flex;gap:12px;padding:14px 0;border-bottom:1px solid var(--pk-border)}
.pk-wish-item img{width:64px;height:64px;border-radius:var(--pk-r);object-fit:cover;flex:none;background:var(--pk-bg)}
.pk-wish-item__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;justify-content:center}
.pk-wish-item__title{font-weight:600;font-size:14px;color:var(--pk-fg);text-decoration:none}
.pk-wish-item__price{font-size:13px;color:var(--pk-primary)}
.pk-wish-item__remove{align-self:flex-start;border:0;background:none;padding:6px;cursor:pointer;color:var(--pk-muted);flex:none}
.pk-wish-item__remove svg{width:16px;height:16px}
.pk-wish-empty{padding:60px 0;text-align:center;color:var(--pk-muted)}
.pk-wish-empty svg{width:40px;height:40px;margin-bottom:12px;color:var(--pk-border)}
.pk-wish-empty p{margin:0;font-size:14px}
.pk-wish-loading{padding:60px 0;text-align:center;color:var(--pk-muted);font-size:14px}

@media (hover:hover) and (pointer:fine){
  .pk-wish-item__remove:hover{color:var(--pk-fg)}
}

@media (prefers-reduced-motion: reduce){
  .pk-wish-drawer__scrim,.pk-wish-drawer__panel{transition-duration:1ms}
}

/* Cart drawer. Same shell as the wishlist drawer just above (deliberately -
   see inc/cart-drawer.php's own docblock for why this one has no bespoke
   AJAX endpoint), with its own item/foot styling for quantity + subtotal. */
.pk-cart-drawer{position:fixed;inset:0;z-index:85}
.pk-cart-drawer__scrim{
  position:absolute;inset:0;border:0;padding:0;cursor:pointer;
  background:rgba(43,43,43,.5);opacity:0;transition:opacity var(--dur-ui) var(--ease-ui);
}
.pk-cart-drawer[data-open] .pk-cart-drawer__scrim{opacity:1}
.pk-cart-drawer__panel{
  position:absolute;top:0;right:0;height:100%;width:min(420px,100vw);
  background:var(--pk-surface);box-shadow:-24px 0 64px -16px rgba(31,10,20,.4);
  display:flex;flex-direction:column;
  transform:translateX(100%);
  transition:transform var(--dur-panel) var(--ease-move);
}
.pk-cart-drawer[data-open] .pk-cart-drawer__panel{transform:translateX(0)}
.pk-cart-drawer__head{
  display:flex;align-items:center;justify-content:space-between;
  padding:20px;border-bottom:1px solid var(--pk-border);flex:none;
}
.pk-cart-drawer__head h2{margin:0;font-family:var(--pk-display);font-size:1.1rem}
.pk-cart-drawer__close{border:0;background:none;padding:4px;cursor:pointer;color:var(--pk-muted)}
.pk-cart-drawer__close svg{width:20px;height:20px}
.pk-cart-drawer__close:hover{color:var(--pk-fg)}
.pk-cart-drawer__body{flex:1;overflow-y:auto;padding:8px 20px;display:flex;flex-direction:column}
.pk-cart-item{display:flex;gap:12px;padding:14px 0;border-bottom:1px solid var(--pk-border)}
.pk-cart-item img{width:64px;height:64px;border-radius:var(--pk-r);object-fit:cover;flex:none;background:var(--pk-bg)}
.pk-cart-item__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;justify-content:center}
.pk-cart-item__title{font-weight:600;font-size:14px;color:var(--pk-fg);text-decoration:none}
.pk-cart-item__meta .variation{margin:2px 0 0;font-size:12px;color:var(--pk-muted)}
.pk-cart-item__meta .variation-Size,.pk-cart-item__meta .variation p{margin:0;display:inline}
.pk-cart-item__qty{font-size:12px;color:var(--pk-muted)}
.pk-cart-item__price{font-size:13px;color:var(--pk-primary)}
.pk-cart-item__remove{align-self:flex-start;border:0;background:none;padding:6px;cursor:pointer;color:var(--pk-muted);flex:none}
.pk-cart-item__remove svg{width:16px;height:16px}
.pk-cart-empty{padding:60px 0;text-align:center;color:var(--pk-muted);margin-top:auto;margin-bottom:auto}
.pk-cart-empty svg{width:40px;height:40px;margin-bottom:12px;color:var(--pk-border)}
.pk-cart-empty p{margin:0 0 16px;font-size:14px}
.pk-cart-drawer__foot{flex:none;padding:16px 20px 20px;border-top:1px solid var(--pk-border);display:flex;flex-direction:column;gap:10px}
.pk-cart-drawer__subtotal{display:flex;justify-content:space-between;align-items:center;margin:0 0 4px;font-size:14px}
.pk-cart-drawer__subtotal b{font-family:var(--pk-display);font-size:1.05rem}

@media (hover:hover) and (pointer:fine){
  .pk-cart-item__remove:hover{color:var(--pk-fg)}
}

@media (prefers-reduced-motion: reduce){
  .pk-cart-drawer__scrim,.pk-cart-drawer__panel{transition-duration:1ms}
}

/* The shared failure-state toast (pbt-overlays.js's PBT.toast()). Added
   7 Sept 2026, Reviewer finding H3: neither the search overlay nor the
   wishlist drawer had any visible way to say "that didn't work" - an
   expired nonce or a dropped request both failed completely silently. */
.pk-toast{
  position:fixed;left:50%;bottom:24px;z-index:200;
  max-width:min(360px,calc(100vw - 32px));padding:12px 18px;
  border-radius:var(--pk-r);background:var(--pk-fg);color:#fff;
  font-size:13px;text-align:center;box-shadow:var(--pk-shadow-sm);
  opacity:0;pointer-events:none;
  transform:translate(-50%,12px);
  transition:opacity var(--dur-ui) var(--ease-ui),transform var(--dur-ui) var(--ease-ui);
}
.pk-toast.is-shown{opacity:1;transform:translate(-50%,0)}

@media (prefers-reduced-motion: reduce){
  .pk-toast{transition-duration:1ms}
}


/* Depth on the merged pages card surfaces, from the shared tokens. Before
   this, nine of the twelve merged pages had no box-shadow on any element,
   so leaving the homepage flattened the whole site. */
.ec-card,
.ec-panel,
.occasion-grid article,
.service-grid article,
.details-grid,
.value-grid article,
.post,
.cards>div,
.cards article,
.product-card,
.shop-benefits__grid,
.ec-side,
.gallery-grid, .grid > figure, .grid > a, .benefits > *, .overview,
.syllabus, .tutor, .related article, .enrol, .ec-tabs, .ec-price { box-shadow:var(--pk-shadow-sm) }

/* The account sidebar marks where you are, the way the nav does. */
.ec-side a[aria-current="page"]{color:var(--pk-primary);font-weight:600}
.ec-side li:has(a[aria-current="page"]){background:var(--pk-cream);border-radius:var(--pk-r);margin-inline:-8px;padding-inline:8px}

/* ============================================================================
   STAGGER. Measured 2 September 2026: on the merged pages the reveal was
   landing on SECTIONS up to 2896px tall. At threshold 0.12 a block that size
   fires when 347px of it is on screen, translates 44px as one slab, and
   finishes before you have scrolled a third of it. You then scroll 2500px of
   content that has already arrived and will never move. The homepage does not
   have this problem because its reveal targets are 42 to 566px and fit the
   viewport.

   So a container can now stagger its own children. The child durations are
   SHORTER than the parent tokens (.55s/.7s against .9s/1s) on purpose: a
   stagger multiplies duration, and six items at 1s each with a 280ms tail
   starts reading as a slideshow rather than an arrival.
   ========================================================================= */
.js .reveal[data-stagger] > *{
  opacity:0;transform:translate3d(0,28px,0);
  transition:opacity .55s var(--pk-ease),transform .7s var(--pk-ease);
}
.js .reveal[data-stagger][data-shown="true"] > *{opacity:1;transform:none}
.js .reveal[data-stagger][data-shown="true"] > :nth-child(1){transition-delay:0ms}
.js .reveal[data-stagger][data-shown="true"] > :nth-child(2){transition-delay:70ms}
.js .reveal[data-stagger][data-shown="true"] > :nth-child(3){transition-delay:140ms}
.js .reveal[data-stagger][data-shown="true"] > :nth-child(4){transition-delay:210ms}
.js .reveal[data-stagger][data-shown="true"] > :nth-child(n+5){transition-delay:280ms}

/* Arrival is over. Hand the children back to the press transition, at (0,4,0)
   so it outranks the (0,3,0) rule above -- which is the whole reason the press
   block further down was inert on .product-card, .ec-card and three others.
   The delay goes too: a press must never wait 280ms for its own feedback. */
.js .reveal[data-stagger][data-settled] > *{
  transition:transform 140ms var(--pk-ease),opacity 140ms var(--pk-ease);
  transition-delay:0s;
}

/* ============================================================================
   PRESS FEEDBACK. Every hover state on this site sits behind
   @media (hover:hover), correctly, and a touch counterpart was written for the
   product rail and the add button. It stopped at making them VISIBLE and never
   gave them a pressed state, so on a phone, tapping a product card, a category
   tile, a mosaic tile or a FAQ row returned nothing at all. 140ms matches the
   press already on .pk-btn and .pk-icon.

   Deliberately NOT in the reduced-motion block: a press response is
   confirmation that a tap landed, not decoration.
   ========================================================================= */
/* The merged pages' cards are on this list too. Press feedback existed only
   for `.pk-*`, so tapping a product on the HOMEPAGE responded and tapping the
   same object on the SHOP page was dead -- one behaviour or the other decided
   by which design system that page happened to arrive from. */
.pk-prod,.pk-cat,.pk-tile,.pk-prod__add,.pk-filters button,.pk-subbtn,
.pk-prod .product-wish,.pk-faq__q,.pk-read,
.product-card,.ec-card,.cakes-gallery figure,.occasion-grid article,.related a{
  transition:transform 140ms var(--pk-ease),opacity 140ms var(--pk-ease);
}
.pk-prod:active,.pk-cat:active,.pk-tile:active,
.product-card:active,.ec-card:active,.cakes-gallery figure:active,
.occasion-grid article:active,.related a:active{transform:scale(.985)}
.pk-prod__add:active,.pk-filters button:active,.pk-subbtn:active{transform:scale(.97)}
.pk-prod .product-wish:active{transform:scale(.9)}
.pk-faq__q:active,.pk-read:active{opacity:.7}

/* THE DROPDOWN OPENS. It used to appear in a single frame while its own caret
   spent 200ms rotating, on the same button. `hidden` cannot transition, so the
   visual state is a data-open attribute and `hidden` is removed one frame
   earlier and restored after the exit. 160ms in, 120ms out: this menu is used
   constantly across 32 pages and has to feel instant. The icon comes DOWN to
   160ms to meet the panel rather than the panel going up to meet it. */
.pk-sub{
  opacity:0;transform:scale(.97) translateY(-4px);transform-origin:top right;
  transition:opacity 160ms var(--pk-ease),transform 180ms var(--pk-ease);
}
.pk-sub[data-open]{opacity:1;transform:none}
.pk-pm__v{transition:transform 160ms var(--pk-ease)}

/* The filter used to cut four products out in the same frame with no
   acknowledgement that a filter had done it. Out is the deliberate part and
   fades; in is the response and is immediate. */
.pk-prod[data-out]{opacity:0;transform:scale(.97);pointer-events:none}

/* `height` is a layout property and this animated on every scroll past 8px,
   which also drove ten writes to the root --pk-chrome variable per scroll.
   A transform does the same thing on the compositor. 29/34 = .853. */
.pk-logo img{transition:transform 300ms var(--pk-ease)}
.pk-header[data-stuck] .pk-logo img{height:34px;transform:scale(.853)}

/* The pager lives here, not in ecommerce.css, because shop.html carries one
   and does not load that stylesheet: its page links rendered as 4px, 8px
   and 17px of bare text. A component used by more than one family belongs
   in the shared file. */
/* The shop said "Showing 1-12 of 289 results" and offered no way to reach the
   other 277. In WordPress this becomes the_posts_pagination(). */
.ec-pager{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:center;
  margin:28px auto 0;max-width:var(--pk-wrap);padding-inline:var(--pk-pad)}
.ec-pager a:not(.pk-btn){display:grid;place-items:center;min-width:40px;min-height:40px;
  border-radius:var(--pk-pill);color:var(--pk-fg);text-decoration:none;font-size:13px}
.ec-pager a[aria-current="page"]{background:var(--pk-primary);color:var(--pk-primary-fg);
  font-weight:600}
/* What a disabled control looks like, in ONE place. It had two homes: this
   rule at .45, and `.pk-btn[disabled],.pk-btn[aria-disabled="true"]` at .5 in
   ecommerce.css. Same specificity, so the answer depended on which stylesheet
   the page happened to load, and the one page in the whole site that actually
   uses it -- shop.html's "Previous" -- loads shop.css and not ecommerce.css, so
   the rule written for that button never reached it. This is the same mistake
   the pager comment directly above already describes and fixed once. */
.pk-btn[disabled],.pk-btn[aria-disabled="true"],
.ec-pager [aria-disabled="true"]{
  opacity:.45;cursor:not-allowed;transform:none;box-shadow:none;pointer-events:none;
}
@media (hover:hover) and (pointer:fine){ .ec-pager a:not(.pk-btn):hover{background:var(--pk-cream)} }

/* ============================================================================
   SITE-WIDE FLOORS. Added 2 September 2026, when 29 pages designed outside
   were merged in. Every one brought its own form and its own small print, and
   measuring 31 pages found the same two faults over and over. These are
   floors, not styling: they change nothing that already cleared them.

   They sit here, immediately BEFORE the reduced-motion block, because that
   block has to stay last in the file. See the warning on it below.
   ========================================================================= */

/* An <img> carries width and height ATTRIBUTES so the browser can reserve its
   box before the file arrives. Those attributes are also a presentational
   hint: the moment CSS sets `width:100%` and says nothing about height, the
   attribute's height applies literally, and a 1400px-tall attribute makes a
   1400px-tall box. `aspect-ratio` never gets a say, and object-fit falls back
   to `fill`, which STRETCHES the photograph rather than cropping it.

   That was live on 32 images across 8 pages. gallery.html rendered its images
   376x1400 from a 1120x1400 source -- a 3x horizontal squash of real cakes --
   and custom-cakes.html rendered 183x1400 slivers. Nothing reported it:
   the markup was valid, the files loaded, every string was present, no console
   error, and the width ladder saw no overflow because the images were the
   right WIDTH. Only a person looking at a screenshot could see it, and one
   did. The homepage was never affected, which is exactly why those pages read
   as belonging to a different site.

   `height:auto` hands the decision back to CSS: an aspect-ratio applies if one
   is set, and the file's own proportions apply if not. It is a bare element
   selector, so every rule that genuinely sets a height -- .pk-logo img,
   .tutor img, .intro>img -- still wins on specificity. */
img{height:auto}

/* 16px is not a taste choice. iOS Safari zooms the viewport when a focused
   text control is under 16px and does not zoom back, so the visitor has to
   pinch to reach the button. This file already did it for .pk-cfield; the
   merged pages brought 12, 13 and 14px controls on nine more pages, so the
   rule is now the site's rather than one component's. 480 is this project's
   own phone line, unchanged. */
@media (max-width:479px){
  /* !important is load-bearing here and is not laziness. Six merged stylesheets
     set their control sizes with the `font:` shorthand on a CLASS
     (`.cakes-form select{font:400 13px ...}`), which beats a bare element
     selector, so the first version of this floor lost silently on the pages
     that needed it most. A floor that can be outranked is not a floor. */
  input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]),
  select, textarea { font-size:16px !important }
}

/* A tap target under 24px fails on a phone whoever wrote it. The merged pages
   brought 17, 18 and 19px links, almost all of them in navigation, footers and
   list rows. This lifts the HIT AREA only: type size is untouched, so nothing
   looks different, it is only reachable.

   The first attempt named a hand-written list of container classes and missed
   most of them, because 31 pages have more containers than anyone will list.
   This selects by ROLE instead: anything in a nav, a footer or an aside, and
   any link that is the direct child of a list item. Links inside running prose
   (`p a`) are deliberately not selected, because padding them would break the
   line spacing of the paragraph they sit in. */
nav a, footer a, aside a, li > a,
.pk-subbtn, .pk-read, .pk-footer__bar a,
.service-grid a, .details-grid a, .cakes-notes a, .ec-table a, .ec-form a {
  display:inline-flex; align-items:center; min-height:24px;
}

/* The dropdown buttons are 21px of text in a 64px bar. They have room. */
.pk-subbtn{min-height:24px}


/* A phone held sideways is about 500px tall and the two-column hero has to
   give up something to still fit one screen. It gives up padding and a little
   headline size, never the button. */
@media (min-width:768px) and (max-height:620px){
  .pk-hero__copy{padding-block:10px}
  .pk-gal{min-height:150px;padding-block:8px 16px}
  .pk-gal__card{height:min(100cqh, calc(52cqw * 1.25), 420px)}
  .pk-hero h1{font-size:clamp(28px,4.2vw,40px)}
  .pk-hero__sub{font-size:13px}
}


/* ============================================================================
   CONTACT PAGE (page-contact.php), 25 Sep 2026. A berry details card beside
   the message form. Existing tokens only; the form itself is .pk-cform /
   .pk-cfield from the Custom Cakes page. Kept above REDUCED MOTION on purpose.
   ========================================================================= */
.pk-contact{background:var(--pk-surface)}
.pk-contact__grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:24px;align-items:start}
.pk-contact__grid>*{min-width:0}
.pk-contact h2{font-family:var(--pk-display);font-size:clamp(24px,3vw,30px);line-height:1.15;margin:0}
.pk-contact h2 em{font-style:normal;color:var(--pk-pink-ink)}
.pk-contact__intro{margin:10px 0 0;font-size:14px;line-height:1.6;color:var(--pk-muted)}

.pk-contact__card{
  position:relative;overflow:hidden;background:var(--pk-primary);color:var(--pk-primary-fg);
  border-radius:var(--pk-r-xl);padding:36px;
}
.pk-contact__card:after{
  content:"";position:absolute;right:-70px;top:-70px;width:200px;height:200px;border-radius:50%;
  border:1px solid rgba(211,163,63,.35);pointer-events:none;
}
.pk-contact__card h2{color:var(--pk-primary-fg)}
.pk-contact__card h2 em{color:var(--pk-gold-soft)}
.pk-contact__card .pk-contact__intro{color:var(--pk-primary-soft)}

.pk-contact__list{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:4px}
.pk-contact__list a{
  display:flex;align-items:center;gap:14px;min-height:56px;padding:8px 10px;margin-inline:-10px;
  border-radius:var(--pk-r);color:var(--pk-primary-fg);text-decoration:none;
  font-size:15px;font-weight:600;line-height:1.35;overflow-wrap:anywhere;
  transition:background-color .25s var(--pk-ease);
}
.pk-contact__list small{
  display:block;font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--pk-primary-soft);margin-bottom:2px;
}
@media (hover:hover) and (pointer:fine){ .pk-contact__list a:hover{background:rgba(255,255,255,.08)} }
.pk-contact__list a:focus-visible,.pk-contact__wa:focus-visible{outline:2px solid var(--pk-gold-soft);outline-offset:2px}

.pk-contact__ic{
  flex:none;display:grid;place-items:center;width:42px;height:42px;border-radius:var(--pk-pill);
  background:rgba(255,255,255,.1);color:var(--pk-gold-soft);
}
.pk-contact__ic svg{width:18px;height:18px}

.pk-contact__hours{margin-top:24px;padding-top:22px;border-top:1px solid rgba(255,255,255,.16)}
.pk-contact__kicker{
  display:flex;align-items:center;gap:8px;margin:0 0 12px;font-size:11px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--pk-gold-soft);
}
.pk-contact__kicker svg{width:15px;height:15px}
.pk-contact__hours dl{margin:0;display:grid;gap:8px}
.pk-contact__hours dl div{display:flex;justify-content:space-between;gap:16px;font-size:14px}
.pk-contact__hours dt{color:var(--pk-primary-soft)}
.pk-contact__hours dd{margin:0;font-weight:600;font-variant-numeric:tabular-nums}
.pk-contact__note{margin:12px 0 0;font-size:12px;color:var(--pk-primary-soft)}
.pk-contact__wa{margin-top:26px;width:100%;justify-content:center;gap:8px}

.pk-contact__form{
  background:var(--pk-surface);border:1px solid var(--pk-border);border-radius:var(--pk-r-xl);
  padding:36px;box-shadow:0 18px 40px -28px rgba(110,25,55,.25);
}
.pk-contact__form .pk-cform{margin-top:26px}
.pk-contact__full{grid-column:1/-1}
.pk-contact__form .pk-cfield input{height:46px}
.pk-contact__form .pk-cfield textarea{min-height:150px}
.pk-contact__form .pk-cform>button{height:48px;margin-top:4px}
.pk-contact__err{
  margin:18px 0 0;padding:12px 14px;border-radius:var(--pk-r);font-size:13px;
  background:var(--pk-cream);color:var(--pk-primary);border:1px solid var(--pk-cream-deep);
}
.pk-contact__done{text-align:center;padding:36px 0}
.pk-contact__done .pk-contact__ic{margin:0 auto 18px;width:56px;height:56px;background:var(--pk-cream);color:var(--pk-primary)}
.pk-contact__done .pk-contact__ic svg{width:24px;height:24px}
.pk-contact__done p{margin:10px 0 24px;color:var(--pk-muted);font-size:14px}

@media (max-width:860px){
  .pk-contact__grid{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:560px){
  .pk-contact__card,.pk-contact__form{padding:24px}
  .pk-contact__card:after{display:none}
  .pk-contact__list a{font-size:14px;overflow-wrap:break-word}
}


/* ============================================================================
   REDUCED MOTION. Last in the file on purpose: it has to outrank every
   component rule above it, and source order is what decides that at equal
   specificity. The reference ships a real block and it is kept.
   Reduce means reduce, not remove: nothing is hidden, only stilled.
   ========================================================================= */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .js .pk-rise{opacity:1;animation:none}
  .pk-marquee__row{animation:none}
  .pk-gal__card,.pk-gal__card img{transition:none}
  /* .js-prefixed, because the rule it has to beat is `.js .pk-gal__cap`.
     Source order only settles a fight between equal specificities, and
     this one was (0,1,0) against (0,2,0). Measured still animating. */
  .js .pk-gal__cap{transition:none}
  /* reduce means still the movement, not remove the element */
  .pk-prod__add{transform:none}
  .pk-gal__arrow:hover{transform:none}
  .js .reveal{opacity:1;filter:none;transform:none;transition:none}
  /* [data-shown] included on purpose: the delay rules this has to beat are
     (0,4,1) and without it this is (0,2,0). Harmless while the duration is
     0s, and it is the same specificity mistake as the .pk-gal__cap one
     directly above, so it does not get to survive twice. */
  .js .reveal[data-stagger] > *,
  .js .reveal[data-stagger][data-shown="true"] > *{
    opacity:1;transform:none;transition:none;transition-delay:0s
  }
  .pk-btn:hover{transform:none;box-shadow:none}
  .pk-cat:hover .pk-cat__img img,
  .pk-prod:hover .pk-prod__plate img,
  .pk-post:hover .pk-post__img img,
  .pk-tile:hover img{transform:none}
  .pk-prod__plate img,.pk-post__img img,.pk-cat__img img,.pk-tile img{transition:none}
  .pk-header,.pk-logo img{transition:none}
}

/* The same media query again, and this one is deliberately blunt.
   Everything above it names `.pk-*` selectors and relies on source order, which
   settles a fight inside ONE file. It does not settle anything against the
   twelve page stylesheets, every one of which is linked BELOW palesas.css and
   therefore cascades after this. Two infinite animations were running for a
   reduced-motion visitor because of it: `pk-spin` and `pk-shimmer` in
   ecommerce.css, on seventeen pages.

   !important is what makes this independent of load order, which matters more
   after the WordPress conversion than it does now: wp_enqueue_style ordering
   and any concatenation plugin will reshuffle these files.

   Durations are near-zero rather than `none` on purpose. An animation that is
   removed never reaches its end state, so anything animating INTO view would
   stay invisible; one that completes in .01ms lands on its final frame at once.
   Reduce means stilled, not hidden -- the same rule as the block above. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-delay:0s!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    transition-delay:0s!important;
    scroll-behavior:auto!important;
  }
}


/* An empty product category is named in the footer but never linked - the
   same rule the shop sidebar, the category tiles and the homepage grid all
   read out of pbt_wc_categories_with_counts(). This one rule lives here
   rather than in shop.css because the footer is on every page and shop.css
   is not. Added 8 September 2026. */
.pk-footer .is-empty { opacity: .45; cursor: not-allowed; }

/* =====================================================================
   THE HOMEPAGE PRODUCT GRID: THE SWAP, AND EVEN CARD HEIGHTS
   Added 10 September 2026, with the twelve-product grid and its two AJAX
   filters. Tony, on the first version of that swap: "it looks not so smooth
   and premium, it also looks slow, that white flash between the switch."

   The white flash itself was a JavaScript fault and is fixed there (the grid
   was emptied BEFORE the request was sent, so it sat blank for the whole
   round trip). What lives here is the motion that replaced it, and it uses
   --pk-ease, the reference's own curve, NOT the --ease-* set defined near the
   top of this file: that set is scoped by its own comment to the header
   overlay system, and the two vocabularies are kept apart on purpose.
   ===================================================================== */

/* The grid is never taken below 55%. There is no state in which this section
   is blank, which is the whole point. `height` is animated because the swap
   pins it either side of the change; it is a layout property, but it moves
   only for the ~380ms of a deliberate press, never on scroll. */
/* WARNING: THE TRANSITION IS ON THE TWO SWAP STATES, NEVER ON `#pk-products`
   ITSELF, AND THAT IS NOT A STYLE CHOICE. `#pk-products` is an id, (1,0,0),
   and this element also carries `.reveal .reveal--up`, whose transition is
   declared at `.js .reveal`, (0,2,0). A bare `#pk-products{transition:...}`
   therefore OVERRIDES the scroll reveal outright: the section's arrival
   crossfades in 140ms instead of 900ms, and its 44px rise and 6px blur lose
   their transition entirely and snap. The products band became the one band
   on the homepage that pops while the heading and the filter row directly
   above it still glide. Nothing errors, every count still measures correctly,
   and a screenshot cannot see it. Caught by the Reviewer, 10 Sep 2026. */
#pk-products[data-swapping],
#pk-products[data-entering]{transition:height 300ms var(--pk-ease),opacity 140ms var(--pk-ease)}
#pk-products[data-swapping]{opacity:.55}

/* The live region the filter row announces through. Visually gone, still read
   aloud: `display:none` and `visibility:hidden` are both skipped by screen
   readers, which is the whole trap this clip pattern exists to avoid. */
.pk-sr-only{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* Only while the height is actually moving, so a second row that is on its
   way out cannot spill past the new height. Card shadows are inside the
   padding, so nothing visible is clipped. */
#pk-products[data-entering]{overflow:hidden}

@keyframes pk-prod-in{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:none}
}

/* 70ms is the section's own stagger, the same cadence the scroll reveal uses
   above. Capped at the sixth card: on a twelve-card grid a per-card delay
   would put the last one most of a second behind the first. */
#pk-products[data-entering] > .pk-prod{animation:pk-prod-in 380ms var(--pk-ease) both}
#pk-products[data-entering] > .pk-prod:nth-child(2){animation-delay:70ms}
#pk-products[data-entering] > .pk-prod:nth-child(3){animation-delay:140ms}
#pk-products[data-entering] > .pk-prod:nth-child(4){animation-delay:210ms}
#pk-products[data-entering] > .pk-prod:nth-child(5){animation-delay:280ms}
#pk-products[data-entering] > .pk-prod:nth-child(n+6){animation-delay:350ms}

/* EVEN CARD HEIGHTS. Before this, a three-line cake name pushed its own price
   down and left the prices in a row sitting at four different heights. It was
   survivable on one row of six short ingredient names; it is not on two rows
   that include "Navy Cap and Gold Balloon Graduation Cake".

   The name is NOT truncated - a shopper needs the whole cake name - it is
   given the height of two lines and allowed to take three. The price is
   pushed to the bottom of the card instead, so the baseline is common however
   the name wraps. Same approach the shop archive card already takes with
   `min-height` on its title (assets/css/shop.css). */
.pk-prod{display:flex;flex-direction:column}
.pk-prod__meta{display:flex;flex-direction:column;flex:1 1 auto}
/* Two lines, expressed as two lines. `.pk-prod h3` is font-size:15px and
   line-height:1.3, so this is 2 x 1.3em and it follows a type change instead
   of quietly becoming one-and-a-half lines. It was a hard 39px for an hour,
   which is the same number and none of the meaning. */
.pk-prod h3{min-height:2.6em}
.pk-prod__price{margin-top:auto;padding-top:8px}

/* The button under the grid. Centred, and far enough from the last row that
   it reads as belonging to the section rather than to the cards. */
.pk-staples-more{margin-top:28px;display:flex;justify-content:center}

@media (prefers-reduced-motion:reduce){
  #pk-products[data-swapping],
  #pk-products[data-entering]{transition:none}
  #pk-products[data-entering] > .pk-prod{animation:none}
}
