/* =========================================================
   PIZZA HUB — Frontend Ordering UI
   All classes prefixed .ph- to avoid theme/page-builder clashes.
   ========================================================= */

:root, .ph-root, .ph-app {
	/* Design tokens matched to the Pizza Hub brand source (Lovable design):
	   Deep Brown / Terracotta / Warm Off White / Soft Cream / Warm Orange.
	   Defined on :root (not just .ph-root/.ph-app) because the item-picker
	   sheet, and other overlays, are appended directly to <body> — outside
	   the .ph-root subtree — so var(--ph-orange) etc. were resolving to
	   nothing there. That's why the selected radio/checkbox indicator never
	   showed color: every rule using var(--ph-orange) silently failed
	   (falls back to initial: transparent bg, currentColor border) while a
	   literal color like the white box-shadow kept working, which is
	   exactly the "looks selected but has no color" bug reported. Root-level
	   custom properties fix every current and future var(--ph-*) use
	   anywhere in the DOM, not just this one sheet. */
	--ph-charcoal: #2B1B17;      /* Deep Brown */
	--ph-charcoal-2: #1c110d;    /* darker brown, for hero gradient */
	--ph-orange: #C94827;        /* Terracotta — primary accent */
	--ph-orange-dark: #a83a1f;   /* darker terracotta, hover/shadow */
	--ph-red-orange: #E86632;    /* Warm Orange — gradient second stop */
	--ph-cream: #FAF8F4;         /* Warm Off White — page background */
	--ph-beige: #F3EFE8;         /* Soft Cream — secondary/muted surfaces */
	--ph-white: #ffffff;
	--ph-gold: #d4ab53;
	--ph-text: #2B1B17;          /* Deep Brown as body text, matches source */
	/* Was #8a7768 — a warm gray that's only ~3:1 contrast against white/cream
	   card backgrounds (below the ~4.5:1 normally recommended for body text),
	   which is exactly what reads as faint/washed-out on a white section.
	   This is the color used for every description, price label, meta line,
	   and secondary caption across the site. Darkened so it's clearly
	   legible while still reading as visually secondary to --ph-text. */
	--ph-muted: #6b5847;
	--ph-radius: 18px;
	--ph-shadow: 0 8px 24px rgba(43,27,23,.12);
	/* Shared easing curves (see docs/skills/animate, docs/skills/review-
	   animations) — built-in CSS easings like "ease" are noticeably weaker
	   than a real curve. --ph-ease-out is for anything entering/exiting or
	   responding to a press (cards, sheets, buttons); --ph-ease-in-out is
	   for something moving/morphing while already on screen (carousel
	   tracks). Applied to the highest-traffic elements below rather than
	   swapped in everywhere at once — plain "ease" elsewhere still works
	   fine, just not with quite as much snap. */
	--ph-ease-out: cubic-bezier(.23, 1, .32, 1);
	--ph-ease-in-out: cubic-bezier(.77, 0, .175, 1);
	font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; /* v265: was Barlow */
	color: var(--ph-text);
	box-sizing: border-box;
	/* Crisper text rendering — especially noticeable on a bold, chunky
	   display font like the current heading font: without
	   this, some displays (Mac/retina in particular) render heavy strokes
	   slightly fuzzy rather than clean-edged. */
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}
.ph-root *, .ph-app *, .ph-root *::before, .ph-app *::before, .ph-root *::after, .ph-app *::after { box-sizing: inherit; }
/* Smooth in-page jumps (Enroll Now -> Register Now, View full menu, Order
   Now, Track Order, header nav links, etc.) instead of the browser's
   default instant/jarring anchor jump — makes clicking those buttons feel
   like something actually opened rather than the page silently snapping
   to a new spot. Only affects same-page "#anchor" links; normal page
   navigation is unaffected. */
html { scroll-behavior: smooth; scroll-padding-top: 86px; }
body { -webkit-overflow-scrolling: touch; }
@media (max-width: 767px) {
	html { scroll-padding-top: 72px; }
	body { touch-action: pan-y; }
	.ph-root, .ph-app, .ph-sheet, .ph-cart-drawer, .ph-cart-body {
		-webkit-overflow-scrolling: touch;
		scroll-behavior: smooth;
	}
	/* Keep touch scrolling native and fluid; avoid accidental horizontal page drag. */
	.ph-root, .ph-app { max-width: 100%; overflow-x: clip; }
}
@media (prefers-reduced-motion: reduce) {
	html, .ph-root, .ph-app, .ph-sheet, .ph-cart-drawer, .ph-cart-body { scroll-behavior: auto !important; }
}

/* Mobile-native fixes (see docs/skills/mobile-native) — the small platform-
   layer details that separate "a website on a phone" from something that
   feels installed, found by auditing this file against that checklist.
   - Kills the gray/blue flash Android/iOS paint on any tap — was only set
     on the bottom nav before, everywhere else still flashed on tap.
   - Stops pull-to-refresh from hijacking a scroll inside the cart drawer/
     checkout — a downward swipe at the top of that panel used to risk
     reloading the whole page instead of just scrolling the panel.
   - Every :hover rule in this file (~38 of them, below) is now wrapped in
     @media (hover:hover) and (pointer:fine) — without that, tapping a
     button/card on a touch screen applies its :hover style (lift, shadow,
     color change) and leaves it stuck there until the next tap somewhere
     else, since phones fake :hover on tap with no way to "un-hover". Each
     one was wrapped in place (not moved) to keep the exact original
     cascade order; the one exception is the ":-webkit-autofill:hover"
     rule near the checkout form fields, which is a Chrome autofill-styling
     hack rather than a real interactive hover and must stay ungated. */
html { -webkit-tap-highlight-color: transparent; }
html, body { overscroll-behavior: none; }
.ph-cart-drawer, .ph-cart-body { overscroll-behavior: contain; }

/* Every checkout/search input was 14-15px — under the 16px iOS Safari uses
   to decide whether to zoom the whole page in on focus, so tapping into
   Name/Phone/Address/Coupon/Search on an iPhone used to zoom the page in
   every time. 16px only on touch-capable screens (small phones/tablets,
   via max-width rather than a touch media query since desktop-with-
   touchscreen users still get the roomier desktop sizing they're on a
   large-enough display to want) — desktop typography above is untouched. */
@media (max-width: 782px) {
	.ph-search-input, .ph-note-input, .ph-coupon-row input[type="text"],
	.ph-field input, .ph-field textarea, .ph-field select, .ph-track-form input {
		font-size: 16px;
	}
}
/* Anchored to #ph-app-root (not just .ph-heading alone) for extra defense
   against Divi's own site-wide "Header Font" setting (Divi -> Theme
   Customizer -> General Settings -> Typography, confirmed set on this
   site) — an ID-anchored selector out-ranks whatever plain class/tag
   selector Divi's own dynamic CSS generates, regardless of load order.
   (The actual font-flashing-then-reverting bug turned out to be a
   different, unrelated rule entirely — see class-ph-frontend-assets.php's
   print_late_overrides(), which must be kept in sync with this one any
   time the font changes here.)
   Trial #8: Baloo 2 — a rounded, friendly, playful display font, unlike
   every prior trial a normal variable-weight family rather than a single
   fixed-weight novelty font, so it needs an explicit font-weight here to
   read as bold/chunky rather than falling back to the browser's plain
   default bold; 800 (ExtraBold) is Baloo 2's heaviest weight, closest to
   the high-impact feel the prior display-font trials all had. */
#ph-app-root .ph-heading { font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; font-weight: 800 !important; letter-spacing: -0.01em; } /* v265: was Baloo 2 — brand spots below keep it */
.ph-script { font-family: 'Caveat', cursive; }

/* ---------------- Scroll-reveal (paired with mountScrollReveal() in frontend.js) ---------------- */
.ph-reveal { opacity: 0; transform: translateY(20px); transition: opacity .6s ease, transform .6s ease; }
.ph-reveal.ph-in-view { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
	.ph-reveal, .ph-reveal.ph-in-view { opacity: 1 !important; transform: none !important; transition: none !important; }
	.ph-root *, .ph-app * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ---------------- Buttons ---------------- */
.ph-btn.ph-btn { display: inline-flex !important; align-items: center; justify-content: center; gap: 8px; padding: 13px 26px; border-radius: 999px; font-weight: 600; font-size: 16px; border: none !important; cursor: pointer; text-decoration: none; background-clip: padding-box; transition: transform .15s var(--ph-ease-out), box-shadow .15s var(--ph-ease-out), background .15s var(--ph-ease-out); }
/* Press feedback (find-animation-opportunities) — :hover alone gives a
   touch device (most of this checkout's traffic) zero confirmation a tap
   registered, since touch has no hover. :active works identically for
   touch and mouse, so no media-query gating needed here. */
.ph-btn.ph-btn:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) { .ph-btn:hover { transform: translateY(-1px); } }
.ph-btn-primary.ph-btn-primary { background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)) !important; color: #fff !important; box-shadow: 0 6px 18px rgba(201,72,39,.35); }
@media (hover: hover) and (pointer: fine) { .ph-btn-primary.ph-btn-primary:hover { box-shadow: 0 10px 24px rgba(201,72,39,.45); color: #fff !important; } }
/* Product card "Choose"/"Add" button — plain solid white, not the
   orange gradient every other primary button site-wide uses, per direct
   request to remove the dark/muddy mixed color and use one flat white
   instead. Scoped to just .ph-food-card so this doesn't touch "Order
   Now" or any other primary button elsewhere on the site. Needs its own
   border and dark text since white-on-white would otherwise disappear
   against the card's own white background. */
.ph-food-card .ph-btn-primary.ph-btn-primary { background: #fff !important; color: #000 !important; border: 1px solid #000 !important; box-shadow: none; }
@media (hover: hover) and (pointer: fine) { .ph-food-card .ph-btn-primary.ph-btn-primary:hover { background: #f2f2f2 !important; color: #000 !important; box-shadow: none; } }
.ph-btn-secondary { background: transparent; color: var(--ph-cream) !important; border: 2px solid rgba(255,255,255,.5) !important; }
@media (hover: hover) and (pointer: fine) { .ph-btn-secondary:hover { background: rgba(255,255,255,.12); color: #fff !important; } }
.ph-btn-outline { background: #fff !important; color: var(--ph-charcoal) !important; border: 1.5px solid #e6ded3 !important; }
@media (hover: hover) and (pointer: fine) { .ph-btn-outline:hover { border-color: var(--ph-orange) !important; color: var(--ph-orange) !important; } }
.ph-btn-block { width: 100%; }
.ph-btn-sm { padding: 8px 16px; font-size: 14px; }
.ph-btn-whatsapp { background: #25D366; color: #fff; }
@media (hover: hover) and (pointer: fine) { .ph-btn-whatsapp:hover { background: #1ebc59; color: #fff; } }
.ph-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* ---------------- Bento Hero (matches brand source: real photo tile + dark headline tile + stats) ---------------- */
.ph-app .ph-bento-hero.ph-bento-hero { display: grid; gap: 14px; margin-top: 28px !important; grid-template-columns: repeat(12, 1fr); }
.ph-bento-tile { position: relative; overflow: hidden; border-radius: 28px; border: 1px solid rgba(43,27,23,.08); background: #fff; box-shadow: var(--ph-shadow); transition: transform .18s var(--ph-ease-out), box-shadow .18s var(--ph-ease-out); }
@media (hover: hover) and (pointer: fine) { .ph-bento-tile:hover { transform: translateY(-3px); box-shadow: 0 20px 40px -18px rgba(43,27,23,.28); } }
.ph-bento-tile-dark { background: linear-gradient(150deg, #3a2620 0%, var(--ph-charcoal) 100%); color: #fff; border-color: rgba(255,255,255,.1); }
.ph-bento-tile-ember { background: linear-gradient(140deg, var(--ph-red-orange) 0%, var(--ph-orange-dark) 100%); color: #fff; border-color: rgba(255,255,255,.14); }
/* Relying on inherited `color` for headings inside these dark/colored tiles is
   fragile: a directly-matched rule on the child (e.g. a theme's own
   `h1 { color: ... }`, which Divi and most themes set) always wins over an
   inherited value regardless of that rule's specificity — inheritance loses to
   ANY direct match. Force it explicitly instead, the same lesson already
   learned for font-family. (Body text like .ph-bento-sub already sets its own
   explicit color and isn't affected by this bug — only headings were.) */
.ph-bento-tile-dark h1, .ph-bento-tile-dark h2, .ph-bento-tile-dark h3, .ph-bento-tile-dark .ph-heading,
.ph-bento-tile-ember h1, .ph-bento-tile-ember h2, .ph-bento-tile-ember h3, .ph-bento-tile-ember .ph-heading {
	color: #fff !important;
}
.ph-bento-headline-tile { grid-column: span 12; padding: 40px 28px; display: flex; flex-direction: column; justify-content: center; }
@media (min-width: 900px) { .ph-bento-headline-tile { grid-column: span 7; padding: 60px 52px; } }
.ph-eyebrow-pill { display: inline-flex; width: fit-content; border: 1px solid rgba(255,255,255,.2); background: rgba(255,255,255,.1); color: var(--ph-red-orange); border-radius: 999px; padding: 6px 16px; font-size: 11px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase; }
.ph-eyebrow-text { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .24em; text-transform: uppercase; color: var(--ph-orange); }
.ph-bento-headline { font-size: clamp(32px, 5.4vw, 58px); line-height: .95; margin: 24px 0 0; }
.ph-bento-sub { margin: 22px 0 0; max-width: 420px; font-size: 16px; line-height: 1.6; color: rgba(255,255,255,.7); }
.ph-hero-ctas { display: flex; gap: 14px; flex-wrap: wrap; }
.ph-bento-headline-tile .ph-hero-ctas { margin-top: 32px; justify-content: flex-start; }
.ph-bento-photo-tile { grid-column: span 12; min-height: 260px; background-size: cover; background-position: center; }
@media (min-width: 900px) { .ph-bento-photo-tile { grid-column: span 5; } }
/* Homepage hero photo tile slideshow (Pizza Hub → Settings → "Homepage Hero
   Slider"): each slide is a full-bleed absolutely-positioned layer,
   cross-fading via opacity — .ph-bento-tile already provides the
   position:relative + overflow:hidden this depends on. With only one slide
   (the no-custom-photos fallback) this just renders as a plain static
   photo, since there's nothing else to fade to. */
.ph-hero-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1.1s ease; z-index: 1; overflow: hidden; }
.ph-hero-slide.is-active { opacity: 1; }
/* Auto-fit any uploaded photo — portrait, landscape, tiny or huge — without
   ever cropping it: background-size:cover (the old rule, above) always
   filled the tile by cutting off whatever part of the image didn't match
   its fixed aspect ratio, which is exactly what customers complained cut
   heads/edges off products.
   BUG FIX: this used to blank the element's own background with
   `background: none !important` (to stop it painting a raw cover-cropped
   copy underneath) and have the two pseudo-elements below re-pull the same
   photo via `background-image: inherit`. That doesn't work: an `!important`
   declaration wins the cascade for the property's actual COMPUTED value, so
   the element's real, resolved background-image was already "none" by the
   time `inherit` read it on the children — not the URL the template set
   inline. Every slide has been rendering fully blank because of this since
   this slider was introduced. Fixed by having the template hand the URL
   down through the "--ph-slide-img" custom property instead (see app.php) —
   custom properties inherit normally and nothing here overrides them, so
   there's no equivalent conflict, and the element itself simply never has a
   background-image to blank in the first place. */
/* UPDATE (after the fix above finally made the photo visible for the first
   time): direct live feedback was "why is this blurry" — this used to be
   two layers, a sharp `background-size:contain` photo on top of a
   `blur(28px)` scaled-up copy of itself filling in the gaps around it so a
   tile/photo aspect-ratio mismatch never showed stark empty bars. That's a
   real, deliberate pattern ("blurred matte", the same thing big photo/video
   apps do for oddly-shaped media) — but on this tile the photo's proportions
   left a large enough sliver of that blurred backdrop visible that it read
   as "the photo itself is out of focus" rather than an artful frame, which
   is a worse first impression than the rare edge-crop a plain full-bleed
   photo can cause. Simplified to one sharp `cover` layer — always fills the
   tile edge-to-edge, never blurry, at the cost of very minor edge cropping
   on an unusually-shaped upload (the same trade every restaurant site's
   hero banner already makes). The blurred backdrop layer is gone; nothing
   else referenced it. */
.ph-hero-slide::after {
	content: ""; position: absolute; inset: 0;
	background-image: var(--ph-slide-img); background-size: cover; background-position: center;
}
.ph-bento-photo-overlay { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 12px; background: rgba(255,255,255,.9); backdrop-filter: blur(6px); border-radius: 18px; padding: 12px 16px; }
/* Hero slider caption card ("Signature" / "Cheese Blast Pizza" / the
   Call Now or Order button) hidden below the same 900px breakpoint the
   hero grid itself already uses to go single-column (see the
   min-width:900px rules above) — was previously only hidden below 640px,
   leaving a real gap between 640-900px (tablets, some phones in
   landscape) where the layout had already gone single-column/stacked but
   this card was still showing on top of a now-narrower photo. Desktop
   keeps it exactly as-is — this is CSS-only (display:none), so the slide
   image, the PHP markup, and the underlying data are all untouched and
   the card reappears instantly if this rule is ever removed. */
@media (max-width: 900px) {
	.ph-bento-photo-overlay { display: none; }
}
.ph-bento-stat-tile { grid-column: span 6; padding: 24px; display: flex; flex-direction: column; justify-content: center; }
@media (min-width: 900px) { .ph-bento-stat-tile { grid-column: span 3; } }
.ph-bento-stat-num { font-size: 34px; margin: 0; line-height: 1; }
.ph-bento-stat-label { margin: 8px 0 0; font-size: 14px; font-weight: 600; }
.ph-bento-stars { color: var(--ph-orange); letter-spacing: 2px; }
.ph-bento-desc-tile { grid-column: span 12; padding: 24px; display: flex; flex-direction: column; justify-content: center; gap: 4px; }
@media (min-width: 900px) { .ph-bento-desc-tile { grid-column: span 6; } }
.ph-bento-desc { margin: 6px 0 0; font-size: 15px; color: var(--ph-muted); line-height: 1.5; }

/* ---------------- Benefits ---------------- */
/* !important: without it, Divi's own section/element styling wins this
   specific margin-top on the live site (a plain, non-important declaration
   here was measured at 0px in the rendered page — same conflict already
   documented below for Story, and the same fix already used two rules down
   for .ph-benefits-grid). */
.ph-benefits-section { margin-top: 40px !important; }
/* !important throughout: Divi (and most themes) apply their own default
   list styling to bare <ul>/<li> elements with enough specificity to fight
   plain rules — the same class of conflict already fixed for fonts/colors
   elsewhere in this file. Without it, the grid gap here was collapsing to
   zero on Divi, making the 4 benefit tiles appear to touch with no spacing. */
.ph-app .ph-benefits-grid.ph-benefits-grid { list-style: none !important; margin: 0 !important; padding: 0 !important; display: grid !important; gap: 14px !important; grid-template-columns: 1fr !important; }
@media (min-width: 640px) { .ph-app .ph-benefits-grid.ph-benefits-grid { grid-template-columns: 1fr 1fr !important; } }
@media (min-width: 1024px) { .ph-app .ph-benefits-grid.ph-benefits-grid { grid-template-columns: repeat(4, 1fr) !important; } }
.ph-benefit-item { display: flex !important; align-items: flex-start; gap: 12px; padding: 20px; }
.ph-benefit-icon { flex: 0 0 auto; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 16px; background: rgba(201,72,39,.1); font-size: 20px; }
.ph-benefit-title { display: block; font-size: 19px; line-height: 1.1; }
.ph-benefit-text { display: block; margin-top: 4px; font-size: 13px; color: var(--ph-muted); }

/* ---------------- Story ---------------- */
/* margin-top was 32px — same as the Benefits section above it uses for ITS
   own top gap (against the hero). Adjacent sibling margins collapse to
   whichever is larger rather than adding together, so Benefits→Story only
   ever had a single 32px gap in practice, and combined with both sections'
   .ph-bento-tile box-shadow (0 8px 24px) bleeding into that gap from both
   sides, the two rows read as crowded/touching rather than as separate
   sections. Bumped to give this specific boundary clear breathing room.
   !important for the same reason as .ph-benefits-section just above: a
   plain declaration here measured 0px on the live site (Divi's own section
   styling was winning), so this needs the same override every other
   section-spacing rule in this file already uses. Slightly larger on
   tablet/desktop (see the min-width:900px rule below) since the two-column
   layout there reads visually heavier and wants more separation from the
   compact benefits row above it. */
.ph-story-section { margin-top: 64px !important; display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 900px) { .ph-story-section { margin-top: 88px !important; grid-template-columns: 1.35fr 1fr; } }
.ph-story-photo { min-height: 260px; position: relative; overflow: hidden; background: linear-gradient(135deg,#F3EFE8,#e8dcc9); }
.ph-bento-tile-placeholder {
	position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
	gap: 10px; font-size: 44px; color: rgba(43,27,23,.28);
}
.ph-bento-tile-placeholder span {
	font-size: 13px; font-weight: 600; color: rgba(43,27,23,.45); max-width: 220px; text-align: center;
}
/* Real <img loading="lazy"> instead of a background-image — this tile sits
   well below the hero fold, so deferring its fetch until it's about to
   scroll into view (rather than the browser downloading it immediately,
   which is all a CSS background-image ever does) saves real bytes on
   first load. Absolutely positioned + object-fit:cover reproduces the old
   background-size:cover/background-position:center look exactly. */
.ph-story-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.ph-story-copy { padding: 32px; display: flex; flex-direction: column; justify-content: center; }
.ph-story-title { font-size: clamp(30px, 4vw, 42px); margin: 10px 0 0; line-height: .95; }
.ph-story-desc { margin: 16px 0 0; font-size: 16px; color: var(--ph-muted); line-height: 1.6; }

/* "Meet the Team" — same bento two-tile layout as "Our Story" above it, just
   with the copy tile placed first in markup so the photo lands on the
   opposite side on desktop, purely for visual rhythm between the two
   back-to-back sections (both stack to one column on phones either way). */
.ph-team-section { margin-top: 40px !important; display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 900px) { .ph-team-section { grid-template-columns: 1fr 1.35fr; } }
.ph-team-photo { min-height: 260px; position: relative; overflow: hidden; background: linear-gradient(135deg,#F3EFE8,#e8dcc9); }
.ph-team-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 15%; display: block; }
.ph-team-copy { padding: 32px; display: flex; flex-direction: column; justify-content: center; }
.ph-team-title { font-size: clamp(30px, 4vw, 42px); margin: 10px 0 0; line-height: .95; }
.ph-team-desc { margin: 16px 0 0; font-size: 16px; color: var(--ph-muted); line-height: 1.6; }

/* ---------------- Offers (admin-editable, Settings → Offers Section) ---------------- */
/* !important for the same reason as Benefits/Story above (Divi section override). */
/* Per direct instruction: every section on the site should be exactly 90%
   width, consistently — this section no longer gets special wider
   treatment (was 98%/1800px across several rounds trying to make 4 offer
   cards look as big as the old 3 did). Real consequence worth knowing: at
   the standard 90%/1280px width, 4 cards will be more modestly sized than
   3 were — that's the direct, unavoidable result of prioritizing width
   consistency across the site over this one section's card size. */
.ph-offers-section { margin-top: 40px !important; }
/* Wider than the standard .ph-container-wide (90% / 1280px cap) specifically
   for this section — with 4 cards per row (see .ph-offers-slide below) that
   standard width made each card noticeably smaller than the 3-per-row
   layout before it, per direct feedback marking exactly that on a
   screenshot. NOTE: max-width alone wasn't enough on its own first attempt
   at this — .ph-container-wide's width:90% was still often the SMALLER,
   binding constraint on ordinary laptop screens (e.g. 90% of 1440px is
   1296px, barely above the old 1280px cap and nowhere near the new 1520px
   one), so the section barely grew at all in practice. Bumping the % too
   (96%) is what actually lets it reach the new max-width on real screens
   instead of just on unusually wide monitors. */
/* More compact per direct feedback ("too big/long") — padding, icon and
   text sizes all reduced a step so this reads as a slim promotional strip
   rather than a large card. */
.ph-offers-card { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); color: #fff; border-radius: var(--ph-radius); padding: 16px clamp(16px, 3vw, 28px); box-shadow: 0 14px 34px rgba(201,72,39,.3); }
.ph-offers-icon { font-size: 26px; flex: 0 0 auto; }
.ph-offers-card > div { flex: 1; min-width: 200px; }
.ph-offers-title { font-size: clamp(18px, 2.2vw, 22px); margin: 0 0 4px; color: #fff !important; }
.ph-offers-text { margin: 0; font-size: 14px; color: rgba(255,255,255,.9); line-height: 1.4; }
.ph-offers-cta.ph-offers-cta { background: #fff !important; color: var(--ph-orange) !important; box-shadow: none; flex: 0 0 auto; }

@media (hover: hover) and (pointer: fine) { .ph-offers-cta.ph-offers-cta:hover { background: var(--ph-cream) !important; } }
@media (max-width: 560px) { .ph-offers-card { text-align: center; justify-content: center; } .ph-offers-card > div { flex: 1 1 100%; } }

/* ---------------- Offers gallery (now a live carousel over real Offer records — see PH_Offers / app.php) ---------------- */
.ph-offers-gallery-section { margin-top: 48px !important; }
.ph-offers-gallery-header { text-align: center; margin-bottom: 22px; }
.ph-offers-gallery-header h2 { margin: 4px 0 0; }
/* UPDATE: this used to show one offer at a time, full width — direct
   feedback was that a single small poster made the whole section look
   sparse/unfinished ("i need at the same time show 3 offer image so that
   this section will look good with content"). Now shows 3 offer cards
   side by side (2 on tablet, 1 on phones — see the media queries below);
   the arrow buttons still slide the row over by one card at a time for
   sites with more than fit on screen, and app.php's JS hides them
   automatically once every offer already fits with nothing left to reveal,
   so they're never shown pointing at nothing. */
/* THE actual reason every previous "make Latest Offers bigger" attempt
   barely moved the needle: this rule had its own hardcoded max-width:980px,
   completely separate from .ph-offers-section's own width above (which kept
   getting widened — 1280px -> 1520px -> 1640px -> 1800px — across several
   rounds with almost no visible effect, because THIS 980px cap was the
   actual binding constraint the whole time and was never touched). Now
   max-width:100% so the carousel actually uses whatever room the section
   above it provides. */
.ph-offers-carousel { display: flex; align-items: center; gap: 14px; max-width: 100%; margin: 0 auto; }
.ph-offers-viewport { overflow: hidden; flex: 1 1 auto; min-width: 0; }
.ph-offers-track { display: flex; gap: 18px; transition: transform .5s ease; }
.ph-offers-slide { flex: 0 0 calc((100% - 54px) / 4); max-width: calc((100% - 54px) / 4); box-sizing: border-box; }
@media (max-width: 900px) {
	.ph-offers-slide { flex: 0 0 calc((100% - 18px) / 2); max-width: calc((100% - 18px) / 2); }
}
.ph-offers-gallery-card { position: relative; display: block; width: 100%; border-radius: 20px; overflow: hidden; box-shadow: 0 14px 30px rgba(43,27,23,.14); border: 1px solid rgba(43,27,23,.06); transition: transform .18s ease, box-shadow .18s ease; }
@media (hover: hover) and (pointer: fine) { .ph-offers-gallery-card:hover { transform: translateY(-4px); box-shadow: 0 18px 38px rgba(43,27,23,.2); } }
/* aspect-ratio instead of a fixed pixel height: offer photos are uploaded at
   all sorts of sizes, and a fixed height combined with object-fit:cover was
   cropping the interesting part off on narrow mobile widths — aspect-ratio
   scales the crop window with the card itself so the same framing holds at
   every width instead of an increasingly tight sliver of it. */
.ph-offers-gallery-card img { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
/* Title/price/delivery note overlay, same "frosted panel over the photo"
   idiom as .ph-bento-photo-overlay elsewhere on this page, so an offer with
   no baked-in poster text (a plain product photo) still reads clearly. */
.ph-offers-gallery-caption { position: absolute; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; gap: 2px; padding: 14px 16px 12px; background: linear-gradient(180deg, rgba(43,27,23,0) 0%, rgba(43,27,23,.82) 55%, rgba(43,27,23,.92) 100%); }
.ph-offers-gallery-title { font-weight: 700; font-size: 16px; color: #fff; }
.ph-offers-gallery-price { font-weight: 700; font-size: 15px; color: var(--ph-gold); }
.ph-offers-gallery-delivery { font-size: 12.5px; color: rgba(255,255,255,.85); }
/* UPDATE: this used to be a plain white circle (white fill, a barely-15%-
   opacity border, a thin dark glyph) that only picked up color on hover —
   next to this section's own near-white/beige background that read as an
   empty pale box rather than a button, and got reported as exactly that
   ("put here also latest offer", pointing at the nav arrows, mistaking them
   for blank ad slots). Now solid-colored and bold at rest, not just on
   hover, so it's unmistakably a clickable control instead of empty space. */
.ph-offers-arrow {
	flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%;
	border: none; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange));
	color: #fff; box-shadow: 0 6px 16px rgba(201,72,39,.35); cursor: pointer;
	font-size: 17px; font-weight: 700; line-height: 1; display: flex; align-items: center;
	justify-content: center; transition: transform .18s ease, box-shadow .18s ease;
}
@media (hover: hover) and (pointer: fine) { .ph-offers-arrow:hover { transform: translateY(-2px) scale(1.06); box-shadow: 0 9px 20px rgba(201,72,39,.45); } }
@media (max-width: 560px) {
	.ph-offers-carousel { max-width: 340px; gap: 6px; }
	.ph-offers-track { gap: 12px; }
	.ph-offers-slide { flex: 0 0 100%; max-width: 100%; }
	.ph-offers-arrow { width: 34px; height: 34px; font-size: 13px; }
	.ph-offers-gallery-card { border-radius: 14px; }
}

/* ---------------- Course graduates ("Success Stories") ---------------- */
.ph-graduates-section { margin-top: 48px !important; text-align: center; }
/* Distinct background from Reviews/Track Your Order (which still share the
   plain .ph-section-panel warm-beige treatment below) — a dark charcoal
   panel instead, per direct request to change this section's look on its
   own rather than matching its siblings. Text/scrollbar/arrow colors
   adjusted for contrast against the darker background. */
.ph-graduates-section.ph-section-panel {
	background: radial-gradient(circle 260px at 4% 8%, rgba(212,171,83,.18), transparent 72%),
		radial-gradient(circle 280px at 96% 92%, rgba(201,72,39,.22), transparent 72%),
		linear-gradient(160deg, #3a2620 0%, var(--ph-charcoal) 100%);
}
.ph-graduates-section .ph-heading,
.ph-graduates-section .ph-eyebrow-text { color: #fff; }
.ph-graduates-section .ph-graduates-grid::-webkit-scrollbar-thumb { background: rgba(255,255,255,.2); }
.ph-graduates-section .ph-graduate-name { color: #fff; }
.ph-graduates-section .ph-graduate-course { color: rgba(255,255,255,.65); }
.ph-graduates-section .ph-empty-teaser { color: rgba(255,255,255,.8); }
/* Requested more breathing room top and bottom — .ph-section-panel (shared
   by several sections on this page) only gives 44px/48px; bumped specifically
   for this section via the two-class selector below so it doesn't drag every
   other panel along with it. */
.ph-graduates-section.ph-section-panel { padding-top: 60px; padding-bottom: 64px; }
.ph-graduates-title { font-size: clamp(26px, 3.6vw, 36px); margin: 8px 0 34px; }
.ph-graduates-grid { display: flex; gap: 26px; overflow-x: auto; padding: 6px 4px 20px; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; justify-content: safe center; }
.ph-graduates-grid::-webkit-scrollbar { height: 6px; }
.ph-graduates-grid::-webkit-scrollbar-thumb { background: rgba(43,27,23,.15); border-radius: 999px; }
/* Auto-scroll marquee (Task: "should auto scroll and show all the student
   face... with nice animation") — only kicks in once there are enough
   graduates for a loop to feel like a marquee rather than a jitter; with
   3 or fewer it stays the plain scrollable row above. The row's own
   content width is animated with a CSS transform instead of JS so it
   costs nothing on the main thread and never drifts out of sync. */
.ph-graduates-viewport { overflow: hidden; padding: 6px 0 20px; }
.ph-graduates-track.is-auto-scrolling {
	overflow-x: visible; scroll-snap-type: none; padding: 0 4px;
	width: max-content;
	animation: ph-graduates-scroll 32s linear infinite;
}
/* Split from a single combined ":hover, :focus-within" rule — wrapping the
   whole thing in @media (hover:hover) would have also silently disabled
   the :focus-within pause for keyboard/screen-reader users on touch
   devices, who have no hover but still deserve the same "stop scrolling
   while I'm interacting with this" behavior. */
@media (hover: hover) and (pointer: fine) {
	.ph-graduates-track.is-auto-scrolling:hover { animation-play-state: paused; }
}
.ph-graduates-track.is-auto-scrolling:focus-within { animation-play-state: paused; }
@keyframes ph-graduates-scroll {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); } /* exactly one full (unduplicated) copy's width */
}
@media (prefers-reduced-motion: reduce) {
	.ph-graduates-track.is-auto-scrolling { animation: none; overflow-x: auto; width: auto; }
}
.ph-graduate-card { flex: 0 0 auto; width: 168px; scroll-snap-align: start; display: flex; flex-direction: column; align-items: center; gap: 6px; }
/* Was 96px with the browser's default center-center crop — small enough,
   combined with most submitted photos being closer to head-and-shoulders
   or half-body shots than tight headshots, that the circle regularly cut
   heads off at the top. Sized up and biased the crop toward the top of
   the photo (object-position) instead of dead-center, same fix already
   applied to the certificate's own photo circles elsewhere in this file. */
.ph-graduate-photo { width: 128px; height: 128px; border-radius: 50%; object-fit: cover; object-position: center 18%; border: 3px solid var(--ph-orange); box-shadow: var(--ph-shadow); transition: transform .25s ease; }
@media (hover: hover) and (pointer: fine) { .ph-graduate-card:hover .ph-graduate-photo { transform: scale(1.06); } }
.ph-graduate-name { font-weight: 700; margin: 6px 0 0; }
.ph-graduate-course { font-size: 13px; color: var(--ph-muted); margin: 0; }
.ph-graduate-badge { display: inline-block; font-size: 11px; font-weight: 700; color: #1e8e4f; background: #e7f6ee; padding: 3px 10px; border-radius: 999px; margin-top: 2px; }

/* ---------------- Reviews ---------------- */
/* !important for the same reason as Benefits/Story above. */
.ph-reviews-section { margin-top: 40px !important; padding-bottom: 8px; }
/* Requested more room above this section's content (same "feels cramped"
   feedback as the graduates section above) — extra top padding via the
   two-class override so only this panel gets it, not every .ph-section-panel
   on the page. Applies whether the content below is the built-in carousel or
   the Trustindex shortcode widget — both sit right under the same heading. */
.ph-reviews-section.ph-section-panel { padding-top: 58px; }
/* Eyebrow+icon heading, same idiom as Market's hero icon on the admin side
   and this same page's own Offers/Graduates headings — see the comment in
   app.php for why this was added. */
.ph-reviews-header { display: flex; align-items: center; gap: 16px; margin: 0 0 30px; }
.ph-reviews-header-icon {
	flex: 0 0 auto; width: 54px; height: 54px; border-radius: 50%; font-size: 24px;
	display: flex; align-items: center; justify-content: center;
	background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange));
	box-shadow: 0 8px 18px rgba(201,72,39,.3);
}
.ph-reviews-title { margin: 4px 0 0; font-size: clamp(26px, 3.6vw, 36px); }
/* Distinct look from Track Your Order (still the plain shared beige
   .ph-section-panel) and from Trained In Our Kitchen (dark charcoal) —
   a warm terracotta panel instead, per direct request to change this
   section's background/layout on its own. Header icon flips to a white
   circle with an orange glyph so it still reads clearly against the new
   background instead of blending into it (it was already an orange
   gradient circle, which would've disappeared on an orange section). */
.ph-reviews-section.ph-section-panel {
	background: radial-gradient(circle 240px at 4% 10%, rgba(255,255,255,.12), transparent 70%),
		radial-gradient(circle 260px at 96% 90%, rgba(43,27,23,.18), transparent 70%),
		linear-gradient(135deg, var(--ph-orange) 0%, var(--ph-red-orange) 100%);
}
.ph-reviews-section .ph-heading,
.ph-reviews-section .ph-eyebrow-text { color: #fff; }
.ph-reviews-section .ph-reviews-header-icon { background: #fff; color: var(--ph-orange); box-shadow: 0 8px 18px rgba(43,27,23,.2); }
.ph-reviews-section .ph-empty-teaser { color: rgba(255,255,255,.85); }
@media (max-width: 480px) {
	.ph-reviews-header { gap: 12px; }
	.ph-reviews-header-icon { width: 44px; height: 44px; font-size: 20px; }
}
.ph-reviews-grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 768px) { .ph-reviews-grid { grid-template-columns: repeat(3, 1fr); } }
.ph-review-card { padding: 24px; margin: 0; }
.ph-review-card blockquote { margin: 14px 0 0; font-size: 15px; line-height: 1.6; }
.ph-review-card figcaption { margin-top: 16px; font-size: 19px; }
.ph-review-card:not(.ph-bento-tile-dark) blockquote { color: var(--ph-muted); }

/* Shared "soft panel" backdrop for homepage sections that used to sit
   flush against the page's own plain background with no visual frame at
   all (Reviews and Course graduates below; Order tracking above layers
   this on top of its own distinct gradient). Two blurred color blobs give
   it a little life without ever competing with the real content sitting
   on top — z-index:-1 plus isolation:isolate on the panel itself keeps
   them pinned behind everything inside this one element specifically,
   they can never end up on top of anything. */
/* UPDATE: this used to decorate only two corners (top-right + bottom-left,
   via ::before/::after — a plain element only gets two pseudo-elements, so
   that was the most this technique could ever add) — direct feedback was
   that the OTHER two corners of these panels (Track Your Order, Trained In
   Our Kitchen, Loved Locally) still read as flat/empty side margins ("add
   some style here", pointing at exactly those blank corners). Rebuilt as
   four soft radial-gradient blobs layered directly into the panel's own
   background instead of pseudo-elements — a `background` can stack as many
   gradient layers as needed, so all four corners get the same soft color
   wash now, and there's no pseudo-element budget left over for anything
   else that might need one later. rgba() values below are var(--ph-orange)
   / var(--ph-gold) spelled out as literal RGB — a CSS custom property
   holding a plain hex color can't be interpolated into rgba() for the alpha
   channel the way these blobs need. */
.ph-section-panel {
	position: relative; isolation: isolate; overflow: hidden; padding: 44px 20px 48px; border-radius: 28px;
	background:
		radial-gradient(circle 220px at 2% 6%, rgba(201,72,39,.28), transparent 72%),
		radial-gradient(circle 240px at 98% 10%, rgba(201,72,39,.28), transparent 72%),
		radial-gradient(circle 220px at 6% 96%, rgba(212,171,83,.3), transparent 72%),
		radial-gradient(circle 240px at 96% 94%, rgba(212,171,83,.3), transparent 72%),
		linear-gradient(160deg, var(--ph-beige) 0%, #f0d9b8 55%, var(--ph-beige) 100%);
}

/* Built-in reviews auto-sliding carousel (one review per slide, arrows +
   autoplay — see frontend/templates/app.php). The Trustindex-shortcode
   branch above is untouched by any of this. */
.ph-reviews-carousel { display: flex; align-items: center; gap: 12px; max-width: 760px; margin: 0 auto; }
.ph-reviews-viewport { overflow: hidden; flex: 1 1 auto; min-width: 0; }
.ph-reviews-track { display: flex; transition: transform .5s ease; }
.ph-reviews-slide { flex: 0 0 100%; max-width: 100%; box-sizing: border-box; }
.ph-reviews-carousel .ph-review-card { text-align: center; }
.ph-reviews-arrow {
	flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%;
	border: 1px solid rgba(43,27,23,.15); background: var(--ph-white);
	color: var(--ph-text); box-shadow: var(--ph-shadow); cursor: pointer;
	font-size: 15px; line-height: 1; display: flex; align-items: center;
	justify-content: center; transition: background .18s ease, color .18s ease, transform .18s ease;
}
@media (hover: hover) and (pointer: fine) { .ph-reviews-arrow:hover { background: var(--ph-orange); color: var(--ph-white); transform: translateY(-2px); } }
@media (max-width: 640px) {
	.ph-reviews-carousel { gap: 6px; }
	.ph-reviews-arrow { width: 34px; height: 34px; font-size: 13px; }
}
.ph-review-google-badge { font-size: 12px; font-weight: 500; opacity: .75; }

/* ---------------- Admin-only "this section is empty" hints ---------------- */
/* The Graduates and Reviews sections are hidden from real visitors entirely
   when there's nothing to show (no featured students / no reviews yet) —
   that's deliberate, an empty section looks unfinished. But that also made
   both sections look plain "missing" to the site owner, who couldn't tell
   whether it was a bug or just needed content. Shown only to logged-in
   admins (current_user_can('manage_options') in app.php), so real visitors
   never see it. */
.ph-admin-empty-hint { max-width: 640px; margin: 0 auto; padding: 22px 26px; border: 2px dashed #d7c9b3; border-radius: var(--ph-radius); background: #fffaf0; text-align: center; }
.ph-admin-empty-hint-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: #a15c00; background: #ffe9c2; padding: 3px 12px; border-radius: 999px; margin-bottom: 10px; }
.ph-admin-empty-hint p { margin: 6px 0; font-size: 14.5px; color: var(--ph-text); line-height: 1.55; }
.ph-admin-empty-hint strong { color: var(--ph-orange); }

/* Public "coming soon" teaser — shown to everyone (not just admins) in the
   same spot when the Graduates/Reviews sections have no content yet, so
   the section is never mistaken for missing/broken. Softer and shorter
   than the admin hint above, since it's the version real visitors see. */
.ph-empty-teaser { max-width: 480px; margin: 0 auto; padding: 20px 24px; text-align: center; }
.ph-empty-teaser p { margin: 0; font-size: 15px; color: var(--ph-muted); }

/* ---------------- Category nav & Search ---------------- */
/* Redesigned: this sticky filter bar used to be plain text-only buttons on
   a flat background that was the exact same color as the page itself, so
   it visually disappeared — now each chip carries a food icon (matched to
   the category name dynamically, see categoryIcon() in frontend.js, so
   this works for whatever categories are configured in the admin, not
   just the ones that exist today) and the bar itself gets a soft shadow
   line so it reads as a distinct control, not blank space. Still sticky —
   this is a functional filter, not a decorative showcase. */
.ph-category-nav { display: flex; gap: 10px; overflow-x: auto; padding: 14px 6px; scrollbar-width: none; position: sticky; top: 96px; background: var(--ph-cream); z-index: 20; box-shadow: 0 2px 12px rgba(43,27,23,.06); }
.ph-category-nav::-webkit-scrollbar { display: none; }
.ph-category-chip { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 7px; padding: 10px 20px; border-radius: 999px; background: #fff; border: 1.5px solid #ece3d6; font-weight: 600; font-size: 15px; cursor: pointer; color: var(--ph-text); white-space: nowrap; transition: transform .18s var(--ph-ease-out), box-shadow .18s var(--ph-ease-out), background .18s var(--ph-ease-out), border-color .18s var(--ph-ease-out); box-shadow: 0 2px 6px rgba(43,27,23,.05); }
.ph-category-chip:active { transform: scale(.96); }
.ph-category-chip-icon { font-size: 17px; line-height: 1; }
@media (hover: hover) and (pointer: fine) { .ph-category-chip:hover { transform: translateY(-2px); box-shadow: 0 6px 14px rgba(43,27,23,.14); border-color: var(--ph-orange); } }
.ph-category-chip.active { background: linear-gradient(135deg, var(--ph-orange) 0%, var(--ph-red-orange) 100%); color: #fff; border-color: transparent; box-shadow: 0 6px 16px rgba(201,72,39,.35); transform: translateY(-1px); }
.ph-search-bar { display: flex; align-items: center; gap: 10px; background: #fff; border: 1.5px solid #ece3d6; border-radius: 14px; padding: 12px 16px; margin: 4px 0 14px; }
.ph-search-icon { display: flex; align-items: center; color: var(--ph-muted); flex: 0 0 auto; }
.ph-search-icon svg { width: 19px; height: 19px; }
.ph-search-input { flex: 1; border: none; outline: none; font-family: inherit; font-size: 15px; background: transparent; color: var(--ph-text); }

/* ---------------- Customer Favorites ---------------- */
/* !important for the same reason as Benefits/Story/Reviews above. */
.ph-favorites-section { margin-top: 40px !important; }
.ph-favorites-header { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 22px; }
.ph-favorites-title { font-size: clamp(28px, 4vw, 40px); margin: 8px 0 0; line-height: 1; }

/* ---------------- Menu grid ---------------- */
.ph-menu-section-title { position: relative; font-size: 22px; font-weight: 700; margin: 36px 4px 16px; padding-left: 16px; }
.ph-menu-section-title::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 6px; height: 24px; border-radius: 4px; background: linear-gradient(180deg, var(--ph-orange), var(--ph-red-orange)); }
/* This was CSS Grid with `repeat(auto-fit, minmax(260px, 1fr))`, which only
   avoids blank space in ONE specific case: when the grid has fewer items
   total than it has columns (then the unused columns collapse and the few
   items stretch). It does NOT help when there are enough items to fill
   several full rows plus a partial last row — e.g. 8 items at 6 columns
   wide leaves 2 items in row 2 and 4 blank grid cells beside them, because
   columns 3-6 aren't "empty" overall (row 1 uses them), so auto-fit can't
   collapse them. That's exactly the "blank space, fill it with items"
   report on a wide desktop window with the full menu list, not just the
   2-card Customer Favorites case a narrower fix already covered.
   Flexbox with wrapping fixes both cases at once: each wrapped LINE
   distributes its own leftover space among the items actually on that
   line (flex-grow), independently of every other line — so a trailing
   partial row always stretches its own cards to fill the row, no matter
   how many full rows came before it or how many items are left over. */
/* row-gap (32px) > column-gap (14px) — a real bug seen live: with a plain
   uniform 14px gap, each card's own drop-shadow (8px vertical offset, 24px
   blur — see --ph-shadow) bled straight down through that small a gap and
   landed visibly on top of whatever card sat in the next row underneath
   it, looking exactly like an unwanted shadow smudged onto that lower
   card. More vertical room between rows (not columns — cards in the same
   row still sit exactly as close together as before) gives the shadow
   room to fade out before it ever reaches the row below. */
/* Converted from flex-wrap to CSS Grid (auto-fit/minmax) with
   justify-content: center — same fix as the Best-Sellers grid above,
   applied here too per direct feedback that an incomplete last row (e.g.
   3 cards left over from a 5-per-row layout, common now that individual
   category tabs like Premium Pizza can have far fewer than a full grid's
   worth of items) sat left-aligned with empty space to its right instead
   of sitting centered. Grid's auto-fit also handles the responsive
   column count on its own based on available width, which is what let
   the old @media (max-width: 860px) "force every card to 100% width"
   override below be removed entirely — auto-fit with a 260px minimum
   already collapses to one column on a narrow phone screen without
   needing a separate rule to force it. */
/* Minimum lowered from 260px to 240px — on the narrowest common phone
   width (320px, e.g. an older iPhone SE), after the container's own
   padding the actual content area is closer to ~256px, which is
   slightly less than 260px was asking for. A few pixels of overflow
   risk on the smallest real devices, closed off here rather than left
   as an edge case; doesn't meaningfully change how this looks on a
   normal phone or on desktop; the max-width cap on the card itself is
   still what limits how large it grows). */
.ph-menu-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); justify-content: center; gap: 32px 14px; padding: 0 4px 20px; }
.ph-menu-grid > .ph-food-card { max-width: 340px; width: 100%; }
/* Sized to match .ph-menu-grid's own cards (240px/340px) instead of a
   smaller 220px/280px — the Featured & Popular section (often just 1-3
   cards) used to render visibly narrower/smaller cards than the regular
   menu grid right below it, which read as inconsistent/misaligned even
   though each was centered correctly on its own. Same card size across
   both sections now. */
.ph-bestsellers-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); justify-content: center; gap: 32px 14px; }
.ph-bestsellers-grid > .ph-food-card { max-width: 340px; }
.ph-food-card {
	background: #fff; border-radius: 8px; box-shadow: var(--ph-shadow); border: 1px solid #000; overflow: hidden; display: flex; flex-direction: column; transition: transform .15s var(--ph-ease-out), box-shadow .15s var(--ph-ease-out);
}
@media (hover: hover) and (pointer: fine) { .ph-food-card:hover { transform: translateY(-3px); box-shadow: 0 14px 30px rgba(43,27,23,.16); } }
.ph-food-card:active { transform: scale(.98); }
.ph-food-card-img {
	width: 100%; aspect-ratio: 4/3; background: linear-gradient(135deg,#F3EFE8,#e8dcc9); position: relative;
	/* flex-shrink: 0 — without this, a card whose sibling in the same row
	   needs extra height (e.g. a longer description wrapping to 2 lines)
	   can pull height away from the image via the flexbox shrink algorithm
	   once `.ph-menu-grid`'s default align-items:stretch equalizes row
	   heights. Locking the image's height to its aspect-ratio and letting
	   `.ph-food-body` (flex:1) absorb all extra space instead keeps a fixed
	   gap above the title always. */
	flex-shrink: 0;
	/* Product photos are now real <img loading="lazy"> elements (for
	   deferred off-screen loading) instead of a CSS background-image, so
	   the hover zoom moved to the <img> below. This div itself is given
	   its own overflow:hidden and never changes size on hover — it's the
	   fixed-size clipping box. That's different from the old bug (fixed
	   elsewhere) where the box being clipped by the *card's* rounded
	   overflow:hidden was itself the element being transform-scaled, which
	   caused cross-browser subpixel clipping glitches. Here the img scales
	   inside a same-size, non-scaling container, so there's no box for the
	   ancestor to fail to clip.
	*/
	overflow: hidden;
}
.ph-food-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .45s ease; }
@media (hover: hover) and (pointer: fine) { .ph-food-card:hover .ph-food-card-img img { transform: scale(1.08); } }
.ph-food-card-img.ph-food-photo-missing { background: linear-gradient(135deg,#F3EFE8,#e8dcc9); display: flex; align-items: center; justify-content: center; }
.ph-food-card-img.ph-food-photo-missing::after { content: '🍕'; font-size: 40px; opacity: .35; }
/* Fallback for the rare case a photo-less product also has a badge
   (Popular/Featured/Premium/Combo) — just enough of a positioning
   context for that absolutely-positioned badge to anchor correctly,
   without reserving a full image-sized box the way .ph-food-card-img
   itself does. */
.ph-food-card-badge-only { position: relative; height: 34px; }
.ph-food-badge { position: absolute; top: 10px; left: 10px; background: var(--ph-orange); color: #fff; font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }
.ph-food-badge-combo { background: #3f6b46; }
.ph-food-badge-premium { background: linear-gradient(135deg, #b8860b, #d4af37); color: #fff; }
.ph-combo-includes { font-size: 12.5px; color: var(--ph-muted); background: var(--ph-beige); border-radius: 8px; padding: 6px 10px; margin: 2px 0; line-height: 1.4; }
.ph-food-body { padding: 18px 16px 16px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.ph-food-name { font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: 0.01em; font-size: 21px; font-weight: 700; margin: 0; }
.ph-food-desc { font-size: 15.5px; color: var(--ph-muted); line-height: 1.45; min-height: 18px; }
.ph-food-sizes { display: flex; flex-direction: column; gap: 6px; margin: 6px 0; }
.ph-food-size-row { display: flex; justify-content: space-between; font-size: 14px; padding: 5px 10px; background: #f2f2f2; border-radius: 8px; color: #000; }
/* UPDATE: items with no size list (burgers, sandwiches, single-price pizzas)
   go straight from the name to this price line — when there's also no
   description (see foodCardHtml() in frontend.js, which now skips that div
   entirely rather than rendering it empty), that's very little content
   under a comparatively large photo, and margin-top:auto pushes this line
   down to wherever the card ends up being tall, leaving plain blank space
   above it. Reported directly on the Burger/Sandwich cards ("add some style
   ... so it will not look blank"). A dashed divider plus turning the
   price+button strip into its own light "shelf" (.ph-food-footer below)
   gives that lower zone a deliberate look regardless of how much space
   lands above it, instead of the price just floating in empty white. */
.ph-food-price {
	font-size: 20px; font-weight: 800; color: #000;
	margin-top: 10px; padding: 12px 14px; background: #f2f2f2;
	border-radius: 10px; text-align: center;
}
/* Small fixed top margin, not auto — history worth keeping in the code:
   v154 tried margin-top:auto to push this flush to the bottom of a
   stretched card, fixing buttons sitting at different heights across a
   row of similarly-sized cards. That worked for that narrow case, but
   anywhere a single-price item (a burger, sandwich, or single-price
   pizza like Rooster's Delight) shares a row with a multi-size pizza —
   Best Sellers, or the main menu grid once Premium duplicates are mixed
   in — the size gap between "one price line" and "three size rows" made
   auto push the price box a long way down, leaving a large, disorienting
   blank gap above it and, worse, sometimes enough extra card height to
   throw off the row's wrapping entirely. A small fixed value trades away
   perfect button alignment across mismatched card heights (a minor,
   easy-to-miss cosmetic difference) for never creating that void again,
   which is the more disruptive failure mode of the two. A prior pass
   wrote this reasoning down as a fix without actually changing the value
   below it — fixed for real this time; the rule above now reads 10px. */
.ph-food-footer { display: flex; justify-content: space-between; align-items: center; margin: 12px -16px -16px; padding: 10px 16px 14px; background: linear-gradient(180deg, transparent, #f2f2f2 45%); }

/* ---------------- Item Customize / size-select modal ---------------- */
.ph-modal-overlay-fe { position: fixed; inset: 0; background: rgba(20,17,15,.6); z-index: 99999; display: flex; align-items: flex-end; justify-content: center; }
@media (min-width: 768px) { .ph-modal-overlay-fe { align-items: center; } }
.ph-sheet { background: #fff; width: 100%; max-width: 480px; max-height: 90vh; overflow-y: auto; border-radius: 20px 20px 0 0; padding: 22px 20px 26px; position: relative; animation: ph-slide-up .25s var(--ph-ease-out); }
@media (min-width: 768px) { .ph-sheet { border-radius: 20px; } }
@keyframes ph-slide-up { from { transform: translateY(30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.ph-sheet-close {
	position: absolute; top: 12px; right: 12px;
	background: linear-gradient(135deg, #e63a22, #c92f1d); color: #fff;
	border: 2px solid rgba(255,255,255,.9); width: 44px; height: 44px;
	min-width: 44px; min-height: 44px; padding: 0; border-radius: 50%;
	font-size: 30px; font-weight: 400; line-height: 1; cursor: pointer;
	display: inline-flex; align-items: center; justify-content: center;
	box-shadow: 0 7px 18px rgba(155,38,24,.24);
	transition: transform .16s var(--ph-ease-out), box-shadow .16s ease, background .16s ease;
	touch-action: manipulation; -webkit-tap-highlight-color: transparent;
	z-index: 8;
}
.ph-sheet-close:focus-visible { outline: 3px solid rgba(201,72,39,.28); outline-offset: 3px; }
.ph-sheet-close:active { transform: scale(.92); box-shadow: 0 3px 10px rgba(155,38,24,.20); }
@media (hover:hover) and (pointer:fine) {
	.ph-sheet-close:hover { transform: scale(1.06); background: linear-gradient(135deg, #f04a2f, #d6321f); box-shadow: 0 9px 22px rgba(155,38,24,.30); }
}
.ph-sheet h3 { font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: 0.01em; margin: 0 0 4px; font-size: 20px; }
.ph-sheet-sub { color: var(--ph-muted); font-size: 14px; margin-bottom: 16px; }
.ph-option-group { margin-bottom: 20px; }
.ph-option-group-title { font-weight: 700; font-size: 15px; margin-bottom: 10px; text-transform: uppercase; letter-spacing: .03em; color: var(--ph-charcoal); }
/* Size / extras choice rows — redesigned in 5.11.1: was a bare border that
   only changed color on selection, giving no clue these rows were
   radio/checkbox controls. A leading indicator now makes that explicit — a
   filled circle for "Choose Size" (single-select, one item picked at a
   time) and a checkmarked square for "Extras" (multi-select, each item
   toggles independently) — plus a hover state and a bit more breathing
   room so the list reads as a proper form instead of a flat block of text. */
.ph-option-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border: 1.5px solid #ece3d6; border-radius: 12px; margin-bottom: 8px; cursor: pointer; background: #fff; transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease, transform .12s var(--ph-ease-out); }
.ph-option-row:active { transform: scale(.98); }
@media (hover: hover) and (pointer: fine) { .ph-option-row:hover { border-color: #e2cdb2; } }
.ph-option-row.selected { border-color: var(--ph-orange); background: #fff6ef; box-shadow: 0 3px 10px rgba(201,72,39,.10); }
.ph-option-row > span { flex: 1; font-size: 14.5px; font-weight: 600; }
.ph-option-row > strong { flex: 0 0 auto; font-size: 14.5px; color: var(--ph-orange-dark); }
.ph-option-row[data-size]::before, .ph-option-row[data-extra]::before { content: ''; flex: 0 0 18px; width: 18px; height: 18px; border: 2px solid #d8cbb5; background-color: #fff; background-repeat: no-repeat; background-position: center; transition: border-color .15s ease, background-color .15s ease; }
.ph-option-row[data-size]::before { border-radius: 50%; }
.ph-option-row[data-extra]::before { border-radius: 5px; }
.ph-option-row[data-size].selected::before { border-color: var(--ph-orange); background-color: var(--ph-orange); box-shadow: inset 0 0 0 3px #fff; }
.ph-option-row[data-extra].selected::before { border-color: var(--ph-orange); background-color: var(--ph-orange); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E"); background-size: 11px 11px; }
.ph-qty-control { display: flex; align-items: center; gap: 12px; }
.ph-qty-btn { width: 30px; height: 30px; border-radius: 50%; border: 1.5px solid #e0d5c4; background: #fff; font-size: 17px; cursor: pointer; }
/* Extras got their own +/- stepper (was click-anywhere-to-toggle, one of
   each only) so a customer can order e.g. 2x Extra Cheese, not just yes/no.
   The row itself is no longer a click target — only the stepper buttons
   are — so it drops the pointer cursor; flex-wrap keeps the stepper from
   getting crushed against the name+price on very narrow phones. */
.ph-option-row[data-extra] { cursor: default; flex-wrap: wrap; }
.ph-extra-price { min-width: 50px; text-align: right; }
.ph-qty-control-extra { flex: 0 0 auto; gap: 6px; margin-left: auto; }
.ph-qty-control-extra .ph-qty-btn { width: 26px; height: 26px; font-size: 15px; }
.ph-qty-control-extra span { min-width: 14px; text-align: center; font-weight: 700; font-size: 14px; }
@media (max-width: 380px) {
	.ph-extra-price { min-width: 42px; font-size: 13px; }
}
.ph-note-input { width: 100%; padding: 10px 12px; border: 1.5px solid #ece3d6; border-radius: 10px; font-family: inherit; font-size: 15px; }
.ph-sheet-total { display: flex; justify-content: space-between; align-items: center; margin: 0 0 12px; font-weight: 700; font-size: 17px; }
/* .ph-sheet itself is the scrolling container (overflow-y:auto, see its
   own rule above) -- position:sticky on this footer pins it to the
   bottom of THAT scroll viewport, not the browser window, which is
   exactly what's needed here: Add to Cart stays visible the whole time
   a customer is scrolling through sizes/extras, instead of being
   whatever-many-hundred-px below the fold on a small phone. The negative
   margin + matching bottom offset cancel out .ph-sheet's own bottom
   padding so the footer sits flush with the true bottom edge. */
.ph-sheet-footer {
	position: sticky; bottom: -26px; margin: 16px -20px -26px;
	padding: 14px 20px calc(20px + env(safe-area-inset-bottom));
	background: #fff; box-shadow: 0 -8px 20px rgba(43,27,23,.08);
	border-radius: 0 0 20px 20px;
}

/* ---------------- Sticky mobile order bar ---------------- */
.ph-sticky-cart-bar { position: fixed; bottom: 12px; left: 12px; right: 12px; background: var(--ph-charcoal); color: #fff; padding: 14px 18px; border-radius: 16px; display: none; align-items: center; justify-content: space-between; z-index: 500; box-shadow: 0 10px 30px rgba(43,27,23,.35); opacity: 0; transform: translateY(12px); transition: opacity .25s var(--ph-ease-out), transform .25s var(--ph-ease-out), display .25s allow-discrete; }
.ph-sticky-cart-bar.visible { display: flex; opacity: 1; transform: translateY(0); }
@starting-style { .ph-sticky-cart-bar.visible { opacity: 0; transform: translateY(12px); } }
.ph-sticky-cart-bar .ph-btn { padding: 10px 20px; font-size: 15px; }

/* ---------------- Cart drawer ---------------- */
.ph-cart-drawer { position: fixed; top: 0; right: 0; height: 100%; width: 420px; max-width: 94vw; background: #fff; box-shadow: -12px 0 40px rgba(43,27,23,.25); z-index: 100000; transform: translateX(100%); transition: transform .25s var(--ph-ease-out); display: flex; flex-direction: column; }
.ph-cart-drawer.open { transform: translateX(0); }
.ph-cart-header { padding: 20px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
.ph-cart-body { flex: 1; overflow-y: auto; padding: 12px 20px; }
/* Entrance is intentionally subtle (140ms, 6px) rather than a dramatic
   slide — this list is a full re-render on every cart change (see
   frontend.js's renderCart()), not an incremental diff, so every row
   technically replays this on every change, not just a freshly-added one.
   At this size/speed it reads as "the list refreshing smoothly" rather
   than a distracting flash each time; a bigger effect would over-animate
   quantity changes on items already in the cart. */
.ph-cart-item { display: flex; gap: 12px; padding: 14px 0; border-bottom: 1px solid #f1ece4; opacity: 1; transform: translateY(0); transition: opacity .14s var(--ph-ease-out), transform .14s var(--ph-ease-out); }
@starting-style { .ph-cart-item { opacity: 0; transform: translateY(6px); } }
.ph-cart-item-img { width: 64px; height: 64px; border-radius: 10px; background-size: cover; background-position: center; background-color: #f1e7da; flex: 0 0 auto; }
.ph-cart-item-info { flex: 1; }
.ph-cart-item-name { font-weight: 700; font-size: 15px; }
.ph-cart-item-offer-badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; background: var(--ph-orange-tint, #fdeadb); color: var(--ph-orange-dark, #b5590b); vertical-align: middle; }
.ph-cart-item-meta { font-size: 13px; color: var(--ph-muted); margin: 3px 0; }
.ph-cart-item-info .ph-combo-includes { margin: 3px 0; }
.ph-cart-item-controls { display: flex; align-items: center; justify-content: space-between; margin-top: 6px; }
.ph-summary-row { display: flex; justify-content: space-between; font-size: 15px; margin-bottom: 8px; }
.ph-summary-row.total { font-weight: 800; font-size: 18px; margin-top: 10px; }
.ph-summary-row.ph-summary-discount { color: #1e8e4f; font-weight: 600; }
.ph-summary-combo { font-size: 12px; color: var(--ph-muted); font-weight: 400; margin-top: 2px; }
.ph-combo-includes-list { display: flex; flex-direction: column; gap: 4px; font-size: 14px; padding: 8px 10px; background: var(--ph-beige); border-radius: 8px; }
.ph-combo-item-note { color: var(--ph-muted); font-style: italic; }
.ph-coupon-row { display: flex; gap: 8px; }
.ph-coupon-row input[type="text"] { flex: 1; padding: 10px 12px; border: 1px solid #ddd; border-radius: 10px; font-size: 14px; text-transform: uppercase; }
.ph-coupon-message { font-size: 13px; margin-top: 8px; min-height: 1.2em; }
.ph-coupon-message.success { color: #1e8e4f; font-weight: 600; }
.ph-coupon-message.error { color: #C94827; font-weight: 600; }
/* display:none can't itself transition, so opacity/transform changes on the
   SAME class toggle as display:block would otherwise jump instantly with
   no visible fade — @starting-style + allow-discrete is the standard fix
   for exactly this "also toggling display" case. Where it's unsupported
   (older browsers), the element still opens/closes correctly, just
   without the fade — safe, graceful degradation, not a broken state. */
.ph-cart-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 99998; display: none; opacity: 0; transition: opacity .2s ease, display .2s allow-discrete; }
.ph-cart-overlay.open { display: block; opacity: 1; }
@starting-style { .ph-cart-overlay.open { opacity: 0; } }
.ph-empty-state { text-align: center; padding: 40px 10px; color: var(--ph-muted); }

/* Order form now lives inline in the cart drawer (no separate "Proceed to
   Checkout" step) — the item list scrolls straight into the form below it,
   both inside the drawer's own .ph-cart-body (already flex:1/overflow-y:auto
   above), so this works the same on mobile, tablet and desktop without any
   extra breakpoints of its own. */
.ph-cart-subtotal-strip { display: flex; justify-content: space-between; align-items: center; font-weight: 700; font-size: 16px; padding: 14px 0 4px; }
.ph-cart-checkout-mount { margin-top: 6px; border-top: 2px dashed #ece3d6; padding-top: 18px; }
.ph-cart-checkout-mount:empty { margin-top: 0; border-top: none; padding-top: 0; }
/* checkoutTemplate()'s own wrapper is styled for a full page (see
   .ph-checkout-wrap below) — inside the narrower drawer it needs no outer
   max-width/centering and no reserved bottom clearance for the mobile bottom
   nav / sticky bar, which never overlap this drawer. */
.ph-cart-checkout-mount .ph-checkout-wrap { max-width: none; margin: 0; padding: 0 0 16px; }
.ph-cart-checkout-mount .ph-checkout-step { padding: 16px; }
.ph-toast { position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%); background: var(--ph-charcoal); color: #fff; padding: 10px 20px; border-radius: 999px; font-size: 14px; z-index: 100001; opacity: 0; transition: opacity .2s; pointer-events: none; }
.ph-toast.visible { opacity: 1; }

/* ---------------- Checkout ---------------- */
.ph-checkout-wrap { max-width: 640px; margin: 0 auto; padding: 20px 16px 100px; }
.ph-checkout-step { margin-bottom: 26px; background: #fff; border-radius: var(--ph-radius); padding: 20px; box-shadow: var(--ph-shadow); }
.ph-checkout-empty { text-align: center; padding: 56px 20px; background: linear-gradient(180deg, #fff 0%, var(--ph-beige) 160%); border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); border: 1.5px dashed #e2cdb2; }
.ph-checkout-empty-icon { width: 64px; height: 64px; margin: 0 auto 16px; border-radius: 50%; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); display: flex; align-items: center; justify-content: center; box-shadow: 0 10px 24px rgba(201,72,39,.28); }
.ph-checkout-empty-icon svg { width: 30px; height: 30px; color: #fff; }
.ph-checkout-empty h3 { margin: 0 0 8px; font-size: 22px; }
.ph-checkout-empty p { margin: 0 0 20px; color: var(--ph-muted); font-size: 15px; }
.ph-checkout-step h4 { margin: 0 0 14px; font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: 0.01em; font-size: 17px; display:flex; align-items:center; gap:8px; }
.ph-field { margin-bottom: 14px; }
/* Within a side-by-side row specifically, each field becomes a flex
   column with its actual control (select/input) pushed to the bottom —
   grid's default stretch already makes both fields in a row the same
   total height, so this then aligns the two controls to that same
   bottom edge regardless of how many lines either label wraps to.
   Without this, a longer label (e.g. "Ward (Sylhet City Corporation) *"
   wrapping to two lines next to a short one-line "Area *") pushes its
   own select down further than its neighbor's, visibly misaligning two
   controls that are meant to sit side by side at the same height. */
.ph-row-2 > .ph-field { display: flex; flex-direction: column; justify-content: flex-end; }
.ph-field label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
.ph-field input, .ph-field textarea, .ph-field select { width: 100%; padding: 11px 13px; border-radius: 10px; border: 1.5px solid #ece3d6; font-family: inherit; font-size: 15px; background: #fff; }
/* .ph-field is the field wrapper for every checkout and course
   registration input across the storefront — its focus state used to be
   only a border-color change, which is a weak signal on its own (thin
   1.5px border, easy to miss, and relies on color alone to show what's
   focused). Adding the same soft ring the order-tracking form's inputs
   already use makes "this is the active field" unambiguous for anyone
   tabbing through the checkout with a keyboard, not just for people who
   can clearly see a color shift. */
.ph-field input:focus, .ph-field textarea:focus, .ph-field select:focus { outline: none; border-color: var(--ph-orange); box-shadow: 0 0 0 3px rgba(201,72,39,.14); }
/* Chrome (and some other browsers) paint their own pale blue/yellow tint
   behind a field once they've autofilled it — reported as "color not
   professionally fit" on the course registration form's NID/Passport and
   Password fields, both of which are exactly the kind of field Chrome
   guesses at (and sometimes guesses wrong, e.g. dropping a saved email
   into the NID field). That tint is the browser overriding our own white
   background, not anything this stylesheet sets — so it's forced back to
   match every other field via the standard "inset box-shadow" override
   (there's no direct background-color property for autofilled fields;
   browsers only respect box-shadow/text-fill-color here). Scoped to
   .ph-field so it only touches inputs this plugin renders. */
.ph-field input:-webkit-autofill, .ph-field input:-webkit-autofill:hover, .ph-field input:-webkit-autofill:focus, .ph-field input:-webkit-autofill:active {
	-webkit-text-fill-color: var(--ph-text);
	-webkit-box-shadow: 0 0 0 1000px #fff inset;
	box-shadow: 0 0 0 1000px #fff inset;
	border-color: #ece3d6;
	transition: background-color 9999s ease-in-out 0s, color 9999s ease-in-out 0s;
}
.ph-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 480px) { .ph-row-2 { grid-template-columns: 1fr; } }
/* Course registration form: stacked to fully vertical (one field per row)
   per direct request — this is the one place in the form a "row" pairing
   was used at all (Mobile Number + Email), and it read as inconsistent
   next to every other field in the same form being full-width. Scoped to
   .ph-course-reg-form only; the course-status lookup form and the main
   checkout form still use the normal side-by-side .ph-row-2 layout, since
   neither was part of this request. */
.ph-course-reg-form .ph-row-2 { grid-template-columns: 1fr; }

/* ---------------- Course registration: passport photo upload ---------------- */
.ph-photo-upload { display: flex; align-items: center; gap: 16px; padding: 14px; border: 1.5px dashed #e0d5c4; border-radius: 14px; background: var(--ph-beige); }
.ph-photo-upload-preview { width: 84px; height: 108px; flex: 0 0 auto; border-radius: 10px; background: #fff; background-size: cover; background-position: center; display: flex; align-items: center; justify-content: center; border: 1.5px solid #e0d5c4; cursor: pointer; transition: border-color .15s ease, transform .15s ease; }
@media (hover: hover) and (pointer: fine) { .ph-photo-upload-preview:hover { border-color: var(--ph-orange); transform: translateY(-1px); } }
.ph-photo-upload-preview.has-image { border-style: solid; border-color: var(--ph-orange); }
.ph-photo-upload-icon { font-size: 28px; opacity: .5; }
.ph-photo-upload-info { flex: 1; min-width: 0; }
.ph-photo-upload-hint { margin: 8px 0 0; font-size: 13px; color: var(--ph-muted); }
.ph-photo-upload-filename { margin: 4px 0 0; font-size: 13px; font-weight: 600; color: var(--ph-charcoal); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ph-photo-upload-error.ph-form-error { margin: 6px 0 0; padding: 0; background: none; font-size: 13px; }
.ph-photo-upload-error.ph-form-error:empty { display: none; }
@media (max-width: 480px) {
	.ph-photo-upload { padding: 12px; gap: 12px; }
	.ph-photo-upload-preview { width: 68px; height: 88px; }
}

/* ---------------- Course page (hero + course cards + registration) ---------------- */
.ph-course-hero { background: linear-gradient(135deg, var(--ph-charcoal), var(--ph-charcoal-2)); padding: 64px 0 56px; text-align: center; color: #fff; }
.ph-course-hero-row { display: flex; align-items: center; justify-content: center; gap: 48px; text-align: left; }
.ph-course-hero-copy { max-width: 560px; }
.ph-course-hero-eyebrow { display: inline-block; background: rgba(212,171,83,.16); color: var(--ph-gold); font-size: 14px; font-weight: 700; letter-spacing: .06em; padding: 7px 18px; border-radius: 999px; margin-bottom: 18px; border: 1px solid rgba(212,171,83,.4); }
.ph-course-hero-title { font-size: clamp(32px, 5.4vw, 52px); margin: 0 0 14px; color: #fff; text-shadow: 0 2px 18px rgba(0,0,0,.25); }
.ph-course-hero-sub { max-width: 560px; margin: 0; color: rgba(255,255,255,.85); font-size: 17px; line-height: 1.65; }
.ph-course-hero-photo { flex: 0 0 auto; width: 280px; border-radius: 24px; overflow: hidden; box-shadow: 0 20px 44px rgba(0,0,0,.35); border: 1px solid rgba(212,171,83,.35); }
.ph-course-hero-photo img { display: block; width: 100%; height: auto; }
.ph-course-hero-photo-missing { width: 100%; aspect-ratio: 4/5; display: flex; align-items: center; justify-content: center; font-size: 64px; background: linear-gradient(135deg,#3a2620,#2B1B17); opacity: .5; }
@media (max-width: 860px) {
	.ph-course-hero-row { flex-direction: column; text-align: center; gap: 28px; }
	.ph-course-hero-copy { max-width: 100%; }
	.ph-course-hero-sub { margin: 0 auto; }
	.ph-course-hero-photo { width: 220px; }
}

/* This section previously pulled the cards UP to overlap the hero's bottom
   edge (margin-top: -34px, before that -56px) for a "floating" depth
   effect. Reported twice as "no space between the hero and the cards" —
   because that's exactly what an overlap looks like: the white cards sit
   flush against or over the dark hero with no visible break, no matter how
   the overlap amount is tuned. Fixed decisively by dropping the overlap
   entirely and adding a real, unambiguous top gap instead.

   That "real gap" turned out to still not exist in practice: this rule was
   only ever a single class (`.ph-course-page-body`), and the markup also
   carries `.ph-container` (`padding: 0 16px`, declared LATER in this file
   — see the Utility section below). Equal specificity + later source order
   means `.ph-container` was silently winning the cascade for the padding
   property, zeroing out this rule's `56px`/`60px` top/bottom padding on
   the live page (confirmed via computed style: paddingTop/Bottom were both
   0px). That's also most of why "make more nice mobile version" feedback
   kept coming back to this page even after this comment was written — the
   fix never actually took effect. Selector bumped to the two-class
   `.ph-container.ph-course-page-body` (matches the exact same fix already
   applied to `.ph-container.ph-tracking-section`) so this rule's own
   padding always wins regardless of where either rule sits in the file.
   A soft cream→beige→cream background was added at the same time — with
   the padding bug fixed, the previously-invisible gap is now a real block
   of space, and on this page's plain-white theme body a bare gap still
   read as "white on white"; the gradient gives the whole content block
   (cards + register form) a tinted backdrop so the white cards/form read
   as raised surfaces instead of blending into the page. */
.ph-container.ph-course-page-body { padding: 56px 0 60px; margin-top: 0; background: #fff; }

/* v2.9.7: Basic / Standard / Premium cards use a clean flat page background and neutral shadows only; removed the coloured/glowy cast behind the plan cards. */
/* v2.9.8: remove the remaining cream/beige parent band around the pricing cards and dashboard CTA. */
.ph-course-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 22px; margin: 0 0 60px; }
/* border-radius forced with !important, same reason as the Offers/Story
   sections elsewhere in this file: some themes/page builders (Divi
   included) ship their own more specific "card"/"box" rules that can
   otherwise win the cascade and flatten these corners back to square,
   which is exactly what made the featured "Most Popular" plan look
   inconsistent with the rest here despite this rule always having a
   radius set. overflow:hidden guarantees the corners actually clip
   whatever's inside, rather than just clipping the border itself. */
.ph-course-card { position: relative; background: #fff; border-radius: var(--ph-radius) !important; overflow: hidden; box-shadow: 0 8px 24px rgba(43,27,23,.08); padding: 30px 24px 26px; display: flex; flex-direction: column; border: 1px solid #eee5dc; transition: transform .18s var(--ph-ease-out), box-shadow .18s var(--ph-ease-out), border-color .18s var(--ph-ease-out); }
@media (hover: hover) and (pointer: fine) { .ph-course-card:hover { transform: translateY(-3px); box-shadow: 0 12px 28px rgba(43,27,23,.11); border-color: #e6d8cc; } }
.ph-course-card.featured { border-color: rgba(201,72,39,.55); border-radius: var(--ph-radius) !important; box-shadow: 0 8px 24px rgba(43,27,23,.08); }
.ph-course-card-badge { position: absolute; top: -13px; left: 24px; background: var(--ph-orange); color: #fff; font-size: 11px; font-weight: 700; letter-spacing: .03em; padding: 5px 14px; border-radius: 999px; box-shadow: 0 3px 8px rgba(43,27,23,.12); }
.ph-course-card-icon { font-size: 28px; width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; border-radius: 16px; margin-bottom: 14px; }
.ph-course-card-name { font-size: 20px; margin: 0 0 12px; }
.ph-course-card-list { list-style: none; margin: 0 0 18px; padding: 0; flex: 1; }
.ph-course-card-list li { font-size: 14.5px; color: var(--ph-text); padding: 5px 0 5px 22px; position: relative; line-height: 1.4; }
.ph-course-card-list li::before { content: '✓'; position: absolute; left: 0; top: 5px; color: var(--ph-orange); font-weight: 700; }
.ph-course-card-meta { font-size: 14px; color: var(--ph-muted); margin-bottom: 4px; }
.ph-course-card-price { font-family: 'Plus Jakarta Sans', sans-serif !important; font-size: 30px; color: var(--ph-orange); margin-bottom: 18px; }

/* Three tiers, three distinct looks — icon badge color + price color each
   cycle through green/gold/orange by card position (1st/2nd/3rd course),
   independently of "featured" (which is about longest-duration, not tier
   position, so a featured card keeps its own tier color plus the orange
   featured border). Was previously identical cards with only text
   differing, per owner feedback ("make some different between 3 course"). */
.ph-course-card-tier-1 .ph-course-card-icon { background: rgba(30,142,79,.14); color: #1e8e4f; }
.ph-course-card-tier-1 .ph-course-card-price { color: #1e8e4f; }
.ph-course-card-tier-2 .ph-course-card-icon { background: rgba(212,171,83,.2); color: #a97e1f; }
.ph-course-card-tier-2 .ph-course-card-price { color: #a97e1f; }
.ph-course-card-tier-3 .ph-course-card-icon { background: rgba(201,72,39,.14); color: var(--ph-orange); }
.ph-course-card-tier-3 .ph-course-card-price { color: var(--ph-orange); }

/* "Our Speciality" — Pizza Hub -> Settings -> Course Page. A photo with a
   small icon badge overlapping its bottom-right corner, then a colored
   heading and a short paragraph underneath; a card whose photo was left
   empty falls back to a plain standalone icon tile instead (see PH_Settings
   defaults()/course_page() for how a card gets skipped entirely). */
.ph-speciality-section, .ph-spotlights-section { margin: 56px auto !important; }
/* Shared by both "Our Speciality" and "See It For Yourself" (Spotlights)
   below — one small eyebrow + centered heading treatment for either
   section's title block. */
.ph-section-heading { text-align: center; margin-bottom: 32px; }
.ph-section-eyebrow { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--ph-orange); margin-bottom: 8px; }
.ph-speciality-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
.ph-speciality-card { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); overflow: hidden; padding-bottom: 24px; transition: transform .15s ease, box-shadow .15s ease; }
@media (hover: hover) and (pointer: fine) { .ph-speciality-card:hover { transform: translateY(-4px); box-shadow: 0 16px 34px rgba(43,27,23,.18); } }
.ph-speciality-icon-panel { position: relative; height: 140px; overflow: hidden; display: flex; align-items: center; justify-content: center; }
.ph-speciality-icon-panel-bg {
	position: absolute; inset: -20%; border-radius: 50%;
	background: conic-gradient(from 0deg, var(--ph-orange), var(--ph-red-orange), #d4ab53, var(--ph-orange));
	opacity: .16; animation: ph-speciality-spin 12s linear infinite;
}
@media (hover: hover) and (pointer: fine) { .ph-speciality-card:hover .ph-speciality-icon-panel-bg { animation-duration: 4s; } }
.ph-speciality-icon-panel-icon {
	position: relative; width: 64px; height: 64px; border-radius: 18px;
	background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange));
	color: #fff; font-size: 28px; display: flex; align-items: center; justify-content: center;
	box-shadow: 0 10px 22px rgba(201,72,39,.35); transition: transform .25s ease;
}
@media (hover: hover) and (pointer: fine) { .ph-speciality-card:hover .ph-speciality-icon-panel-icon { transform: scale(1.08) rotate(-4deg); } }
@keyframes ph-speciality-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
	.ph-speciality-icon-panel-bg { animation: none; }
}
.ph-speciality-card-title { font-size: 19px; color: var(--ph-orange); margin: 6px 22px 10px; }
.ph-speciality-card-text { font-size: 14.5px; color: var(--ph-muted); line-height: 1.6; margin: 0 22px; }
@media (max-width: 860px) { .ph-speciality-grid { grid-template-columns: 1fr; } }

/* Student & Influencer Spotlights ("See It For Yourself") — a gallery of
   admin-uploaded video cards, each with a plain HTML5 <video controls>
   player (no autoplay, no third-party embed) and an optional quote/name
   underneath. Renders only when at least one spotlight has been added —
   see PH_Settings::get_course_spotlights()/course_page(). */
.ph-spotlights-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 300px)); justify-content: center; gap: 28px; }
.ph-spotlight-card { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); overflow: hidden; }
.ph-spotlight-card video { width: 100%; aspect-ratio: 9 / 16; max-height: 420px; object-fit: cover; display: block; background: #000; }
.ph-spotlight-card-body { padding: 16px 20px 20px; }
.ph-spotlight-card-quote { font-size: 14.5px; color: var(--ph-text); line-height: 1.6; font-style: italic; margin: 0 0 8px; }
.ph-spotlight-card-name { font-size: 13.5px; font-weight: 700; color: var(--ph-orange); margin: 0; }
@media (max-width: 860px) { .ph-spotlights-grid { grid-template-columns: 1fr; } .ph-spotlight-card video { aspect-ratio: 16 / 9; max-height: 260px; } }

/* Homepage video Reels (Pizza Hub -> Reels) — the general-purpose sibling of
   the Spotlights grid above (that one is course marketing with a name/quote
   and a normal play button; this one is a growing gallery of short brand and
   customer clips, no controls, tap-to-unmute instead). Deliberately mirrors
   .ph-spotlight-card's own card treatment (white card, var(--ph-shadow),
   var(--ph-radius), 9:16 video via object-fit: cover) so both video
   galleries read as the same design system, and uses a FIXED column count
   per breakpoint (not auto-fit/minmax like Speciality/Spotlights above) so
   rows always line up evenly — a 6th, 11th, etc. video starts a new full
   row rather than ever leaving a lone short row.
   Sized at 5 columns wide on desktop (two even rows of 5 for a 10-video
   gallery) and stretched to fill almost the full site width, matching how
   wide the rest of the page reads, rather than sitting in the narrower
   ~980px column this section used at launch. */
.ph-reels-section { margin: 56px auto !important; }
.ph-reels-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 22px; width: 88%; max-width: 1280px; margin: 0 auto; }
/* Caption removed (was .ph-reel-caption below each video) per direct
   request — just the video card now, so the border below carries more of
   the "framed" look on its own: a real visible border (not just the
   shadow this had before) plus a touch more radius/shadow so each card
   reads as a distinct framed clip rather than a plain floating video. */
.ph-reel-card { position: relative; background: #fff; border: 1px solid rgba(43,27,23,.10); border-radius: var(--ph-radius); box-shadow: 0 6px 18px rgba(43,27,23,.10); overflow: hidden; }
.ph-reel-card video { width: 100%; aspect-ratio: 9 / 16; max-height: 340px; object-fit: cover; display: block; background: #000; }
.ph-reel-mute-toggle { position: absolute; right: 10px; bottom: 10px; width: 34px; height: 34px; border-radius: 50%; border: none; background: rgba(0,0,0,.55); color: #fff; font-size: 15px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
@media (max-width: 1100px) { .ph-reels-grid { grid-template-columns: repeat(3, 1fr); width: 90%; } }
@media (max-width: 700px) { .ph-reels-grid { grid-template-columns: repeat(2, 1fr); width: 92%; } }
@media (max-width: 460px) { .ph-reels-grid { grid-template-columns: 1fr; width: 92%; } .ph-reel-card video { aspect-ratio: 16 / 9; max-height: 260px; } }

.ph-course-register-section { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 36px clamp(18px, 4vw, 44px); scroll-margin-top: 90px; margin-top: 14px; }
/* Clicking "Enroll Now" jumps to this exact section (its id is the
   anchor's target, #ph-course-register-anchor) — this brief highlight on
   arrival, on top of the smooth scroll above, is what makes that read as
   "a form just opened for you" instead of the page quietly jumping.
   Fades back to the section's normal resting shadow on its own. */
.ph-course-register-section:target { animation: phRegisterHighlight 1.8s ease-out; }
@keyframes phRegisterHighlight {
	0% { box-shadow: 0 0 0 5px rgba(201,72,39,.45), var(--ph-shadow); }
	100% { box-shadow: var(--ph-shadow); }
}
.ph-course-register-heading { text-align: center; max-width: 480px; margin: 0 auto 28px; }
.ph-course-register-heading h2 { font-size: clamp(24px, 3vw, 32px); margin: 0 0 8px; }
.ph-course-register-heading p { color: var(--ph-muted); font-size: 15px; margin: 0; }
.ph-course-register-section .ph-checkout-step { box-shadow: none; padding: 0; }
.ph-course-register-section .ph-checkout-step h4 { display: none; } /* "Course Registration" heading is now the section heading above */

/* Enroll modal — replaces the old always-open .ph-course-register-section.
   Reuses the site's existing bottom-sheet-on-mobile / centered-on-desktop
   pattern (.ph-modal-overlay-fe + .ph-sheet, same one the cart/checkout
   uses) rather than a new one-off dialog component. Widened past the
   cart sheet's 480px default since this form carries four full sections
   (course, info, ID verification, account) — 480px made every field feel
   cramped in testing. */
.ph-course-enroll-sheet { max-width: 560px; }
.ph-course-enroll-sheet .ph-course-register-heading { margin-top: 4px; }
.ph-course-enroll-sheet .ph-checkout-step { box-shadow: none; padding: 0; }
.ph-course-enroll-sheet .ph-checkout-step h4 { display: none; } /* heading above already says "Register Now" */
.ph-course-enroll-overlay[hidden] { display: none; }

.ph-course-dashboard-cta { display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; margin-top: 32px; padding: 20px; background: #fff; border: 1px solid #eee5dc; border-radius: var(--ph-radius); font-weight: 600; box-shadow: 0 4px 14px rgba(43,27,23,.05); }

@media (max-width: 640px) {
	.ph-course-hero { padding: 44px 0 36px; }
	.ph-container.ph-course-page-body { padding-top: 36px; margin-top: 0; }
	.ph-course-register-section { padding: 26px 18px; }
	.ph-course-dashboard-cta { flex-direction: column; text-align: center; }
}

/* ---------------- Course registration & lookup ---------------- */
/* Registration form fields are grouped into labeled sections (Course, Your
   Information, Identity Verification, Dashboard Account) rather than one
   long unbroken list of fields — same field names/behavior as before, just
   organized so it reads as a guided flow instead of a wall of inputs. */
.ph-course-reg-form .ph-course-form-section + .ph-course-form-section { border-top: 1px solid #f1ece4; margin-top: 22px; padding-top: 22px; }
.ph-course-form-section-label { font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: .03em; font-size: 16px; color: var(--ph-orange); margin: 0 0 14px; display: flex; align-items: center; gap: 8px; }
.ph-course-submit-btn { margin-top: 6px; }
.ph-course-fee-note { font-size: 14px; color: var(--ph-muted); background: var(--ph-beige); border-radius: 10px; padding: 10px 14px; margin: 4px 0 18px; }
.ph-form-error { color: #b3261e; background: #fdecea; border-radius: 10px; padding: 10px 14px; margin-top: 12px; font-size: 15px; }
.ph-course-success { text-align: center; padding: 12px 0; }
.ph-course-success h3 { margin: 0 0 10px; font-size: 26px; }
.ph-course-regid { font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: .06em; font-size: 22px; color: var(--ph-orange); background: var(--ph-beige); border-radius: 10px; padding: 10px 16px; display: inline-block; margin: 6px 0 14px; }
.ph-course-status p { margin: 0 0 8px; font-size: 15px; }
.ph-course-pending-note { color: var(--ph-muted); font-size: 14px; margin-top: 10px !important; }

/* ---------------- Certificate result card (student lookup) ---------------- */
.ph-cert-card { max-width: 380px; margin: 20px auto 0; background: #fff; border-radius: 18px; padding: 28px 24px; box-shadow: 0 12px 28px rgba(43,27,23,.14); text-align: center; border: 1px solid #ece3d6; }
/* The student's own passport-style photo (collected at registration —
   see PH_Course_Registrations::register()), shown here so whoever is
   checking a certificate can match the name to a face. Blank/omitted
   entirely for the ~230 legacy pre-2026 certificates that never had a
   photo on file — see verify_student()'s doc comment. */
/* object-position biased toward the top (rather than the default centered
   crop) because these are passport-style photos — usually taller than they
   are wide, with the face/hair near the top of the frame — so a plain
   center-crop into this 88x88 circle was cutting off the top of the
   subject's hair. Nudging the focal point up keeps the whole head in
   frame for the vast majority of uploaded photos. */
.ph-cert-card-photo { display: block; width: 88px; height: 88px; margin: 0 auto 14px; border-radius: 50%; object-fit: cover; object-position: center 20%; border: 3px solid var(--ph-beige); box-shadow: 0 6px 16px rgba(43,27,23,.2); background: #fff; }
/* Was a plain beige pill with a "✓" text character — now a real green
   scalloped "verified seal" icon (built in courses.js) next to the label,
   the same visual idiom as a verified-account checkmark, just in this
   plugin's own green rather than any particular app's blue. */
.ph-cert-card-badge { display: inline-flex; align-items: center; gap: 6px; background: transparent; color: #1e8e4f; font-size: 13.5px; font-weight: 700; letter-spacing: .02em; padding: 0; margin-bottom: 14px; }
.ph-verified-badge-icon { flex: 0 0 auto; }
/* Verify Certificate result card: the badge used to sit next to a full sentence
   ("Certificate verification successful. This credential is officially issued by
   PizzaHub Sylhet.") every time, with no motion -- per feedback, shortened to a
   plain "Verified by Pizza Hub Sylhet" and given a small entrance animation so
   the green seal visibly 'arrives' the moment the Verifying... loading state
   resolves into a result, rather than just appearing pre-drawn. */
@keyframes ph-verified-pop { 0% { opacity: 0; transform: scale(.4) rotate(-8deg); } 60% { opacity: 1; transform: scale(1.18) rotate(2deg); } 100% { opacity: 1; transform: scale(1) rotate(0); } }
@keyframes ph-verified-text-in { 0% { opacity: 0; transform: translateX(-4px); } 100% { opacity: 1; transform: translateX(0); } }
.ph-cert-card-badge .ph-verified-badge-icon { animation: ph-verified-pop .55s cubic-bezier(.34,1.56,.64,1) both; transform-origin: center; }
/* Hover zoom for the verification seal: a clear but restrained scale-up,
   with a soft green shadow. Target the seal when the pointer is over either
   the seal or its label so the small icon is easy to interact with. */
@keyframes ph-verified-hover-zoom { from { transform: scale(1); } to { transform: scale(1.38); } }
@media (hover: hover) and (pointer: fine) {
  .ph-cert-card-badge .ph-verified-badge-icon:hover,
  .ph-cert-card-badge:hover .ph-verified-badge-icon {
    animation: ph-verified-hover-zoom .18s ease-out both;
    filter: drop-shadow(0 2px 4px rgba(30,142,79,.35));
    cursor: zoom-in;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ph-cert-card-badge .ph-verified-badge-icon,
  .ph-cert-card-badge span { animation: none; }
  .ph-cert-card-badge .ph-verified-badge-icon:hover,
  .ph-cert-card-badge:hover .ph-verified-badge-icon { animation: none; transform: scale(1.12); }
}
.ph-cert-card-badge span { animation: ph-verified-text-in .35s ease-out .25s both; }
.ph-cert-card-name { font-family: 'Plus Jakarta Sans', sans-serif !important; font-size: 26px; letter-spacing: .02em; }
.ph-cert-card-course { font-size: 16px; color: var(--ph-muted); margin-top: 2px; }
.ph-cert-card-divider { height: 1px; background: linear-gradient(to right, transparent, var(--ph-orange), transparent); margin: 18px 0 20px; }
.ph-cert-card-btn { display: inline-block; padding: 14px 34px; font-size: 16px; font-weight: 700; color: #fff; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); border-radius: 999px; text-decoration: none; box-shadow: 0 10px 22px rgba(201,72,39,.35); }
.ph-cert-card-footer { margin-top: 16px; font-size: 13px; color: var(--ph-muted); }
.ph-cert-card + .ph-cert-card { margin-top: 16px; }
.ph-course-lookup-altlink { text-align: center; margin-top: 18px; font-size: 14px; }
.ph-course-lookup-altlink a { color: var(--ph-orange); font-weight: 600; text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .ph-course-lookup-altlink a:hover { text-decoration: underline; } }

/* ---------------- Verify Certificate page ----------------
   Previously this page's shortcode returned a single bare, unstyled
   `<div class="ph-mount-verify-certificate">` with no `.ph-container`
   wrapper at all (every other full-bleed page — course, course-dashboard —
   wraps its content in `.ph-container.ph-course-page-body` for a centered,
   max-width, padded layout). With no wrapper, the plain form's text inputs
   and button had nothing constraining their width, so they stretched
   edge-to-edge across the full browser viewport (confirmed live: a 1521px
   wide form on a 1536px-wide window) — reported as a big empty/unfinished-
   looking area on the page. Fixed by giving this page the same dark
   hero-band + centered-container treatment as the Course page (reusing
   `.ph-course-hero*` classes directly for visual consistency) and adding a
   short "how it works" strip plus a properly max-width, centered card
   around the actual form, so the page reads as a real section instead of
   a form dropped on a blank page. */
.ph-verify-cert-badge { flex: 0 0 auto; width: 200px; height: 200px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(212,171,83,.12); border: 1px solid rgba(212,171,83,.4); box-shadow: 0 20px 44px rgba(0,0,0,.35); }
.ph-verify-cert-badge svg { width: 84px; height: 84px; color: var(--ph-gold); }
@media (max-width: 860px) { .ph-verify-cert-badge { width: 150px; height: 150px; } .ph-verify-cert-badge svg { width: 64px; height: 64px; } }

.ph-container.ph-verify-cert-body { padding: 56px 0 60px; margin-top: 0; background: linear-gradient(180deg, var(--ph-cream) 0%, var(--ph-beige) 45%, var(--ph-cream) 100%); }

.ph-verify-cert-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; margin: 0 0 40px; }
.ph-verify-cert-step { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 26px 22px; text-align: center; }
.ph-verify-cert-step-num { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 50%; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); color: #fff; font-family: 'Plus Jakarta Sans', sans-serif !important; font-size: 17px; margin-bottom: 12px; }
.ph-verify-cert-step h3 { font-size: 17px; margin: 0 0 6px; }
.ph-verify-cert-step p { font-size: 14px; color: var(--ph-muted); margin: 0; line-height: 1.5; }

.ph-verify-cert-card { max-width: 620px; margin: 0 auto; background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 36px clamp(18px, 4vw, 44px); }
.ph-verify-cert-card .ph-checkout-step { box-shadow: none; padding: 0; margin-bottom: 0; }
.ph-verify-cert-card .ph-checkout-step h4 { display: none; } /* page heading above already says "Verify a Certificate" */

.ph-verify-cert-altlink { text-align: center; margin: 26px 0 0; font-size: 14px; color: var(--ph-muted); }
.ph-verify-cert-altlink a { color: var(--ph-orange); font-weight: 600; text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .ph-verify-cert-altlink a:hover { text-decoration: underline; } }

@media (max-width: 860px) { .ph-verify-cert-steps { grid-template-columns: 1fr; } }
@media (max-width: 640px) {
	.ph-container.ph-verify-cert-body { padding-top: 36px; margin-top: 0; }
	.ph-verify-cert-card { padding: 26px 18px; }
}

/* ---------------- Contact Us page ---------------- */
.ph-container.ph-contact-body { padding: 56px 0 60px; margin-top: 0; }
.ph-contact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin: 0 0 40px; }
.ph-contact-card { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 26px 22px; text-align: center; }
.ph-contact-card-icon { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: 50%; background: var(--ph-beige); font-size: 22px; margin-bottom: 12px; }
.ph-contact-card h3 { font-size: 17px; margin: 0 0 8px; }
.ph-contact-card p { font-size: 14.5px; color: var(--ph-muted); margin: 0 0 2px; line-height: 1.5; }
.ph-contact-card p a { color: var(--ph-orange); font-weight: 600; text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .ph-contact-card p a:hover { text-decoration: underline; } }
.ph-contact-cta { text-align: center; }
.ph-contact-socials { display: flex; justify-content: center; gap: 14px; margin-top: 18px; }
.ph-contact-socials a { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%; background: var(--ph-beige); color: var(--ph-text); transition: background .2s ease, color .2s ease, transform .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-contact-socials a:hover { background: var(--ph-orange); color: #fff; transform: translateY(-2px); } }
@media (max-width: 640px) { .ph-container.ph-contact-body { padding-top: 36px; } }

/* ---------------- Terms & Privacy pages ---------------- */
/* Same dark hero band as the rest of these standalone pages, then the
   prose itself in one centered card — long legal text on a bare
   background reads as an afterthought, this makes it read as a real page. */
.ph-container.ph-legal-body { padding: 48px 0 60px; margin-top: 0; }
.ph-legal-card { max-width: 760px; margin: 0 auto; background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: clamp(26px, 4vw, 48px); }
.ph-legal-card h2 { font-size: 19px; margin: 30px 0 10px; }
.ph-legal-card h2:first-of-type { margin-top: 6px; }
.ph-legal-card p { font-size: 15px; line-height: 1.7; color: var(--ph-text); margin: 0 0 10px; }
.ph-legal-updated { color: var(--ph-muted) !important; font-size: 13.5px !important; font-style: italic; }
@media (max-width: 640px) { .ph-container.ph-legal-body { padding-top: 32px; } .ph-legal-card { padding: 24px 18px; } }

/* ---------------- Offer landing page (the "Offer" page — see PH_Shortcodes::offer_landing()) ---------------- */
/* This is the page a Facebook/Instagram ad actually sends a click to, so it
   gets its own full-width banner photo (when the offer has one) instead of
   living inside the usual narrow content card — the goal is "this looks
   like a real ad landing page", not just another info page. */
/* v2 redesign — this page exists purely to convert someone who just
   clicked a Facebook/Instagram ad, so it's deliberately built to read
   like a scroll-stopping promo card (dark gradient scrim behind the
   photo, a glowing CTA) rather than a plain content page. Every class
   name is unchanged from before — offer_landing() in
   class-ph-shortcodes.php never changed, only the CSS did. */
.ph-offer-landing-image { width: min(1120px, calc(100% - 32px)); aspect-ratio: 16 / 7; max-height: 500px; overflow: hidden; position: relative; margin: 28px auto 0; border-radius: 26px; background: #f3eee8; border: 1px solid rgba(43,27,23,.08); box-shadow: 0 18px 48px rgba(43,27,23,.16); }
.ph-offer-landing-image img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.ph-offer-landing-image::after {
	content: ""; position: absolute; inset: 0;
	background: linear-gradient(180deg, rgba(20,14,11,0) 40%, rgba(20,14,11,.65) 100%);
	pointer-events: none;
}
.ph-offer-landing-ribbon { position: absolute; top: 18px; left: 18px; z-index: 1; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); color: #fff; font-size: 13px; font-weight: 800; padding: 7px 16px; border-radius: 999px; box-shadow: 0 6px 16px rgba(0,0,0,.25); }
.ph-container.ph-offer-landing-body { padding: 40px 0 60px; margin-top: 0; }
.ph-offer-landing-card { max-width: 540px; margin: -76px auto 0; position: relative; z-index: 2; background: #fff; border-radius: 22px; box-shadow: 0 24px 54px rgba(43,27,23,.28); padding: clamp(28px, 4vw, 44px); text-align: center; }
/* No banner photo at all (offer has no image) — nothing to overlap, so the
   card just sits with normal top spacing instead of floating in mid-air. */
.ph-offer-landing-body > .ph-offer-landing-card:first-child { margin-top: 0; }
.ph-offer-landing-brand { display: flex; align-items: center; justify-content: center; gap: 8px; margin-bottom: 14px; }
.ph-offer-landing-brand img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; }
.ph-offer-landing-brand span { font-size: 13px; font-weight: 700; letter-spacing: .03em; color: var(--ph-muted); text-transform: uppercase; }
.ph-offer-landing-ended { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #b3401f; background: #fde3d8; padding: 5px 14px; border-radius: 999px; margin-bottom: 14px; }
.ph-offer-landing-title { font-size: clamp(24px, 4vw, 34px); margin: 0 0 14px; }
/* The price is the whole reason someone tapped the ad — it's now a bold
   badge of its own instead of plain colored text, so it reads as the
   headline number on the card, not a caption under the title. */
.ph-offer-landing-price { display: inline-block; font-size: clamp(24px, 4.4vw, 34px); font-weight: 800; color: #fff; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); padding: 8px 22px; border-radius: 999px; margin-bottom: 12px; box-shadow: 0 8px 20px rgba(201,72,39,.3); }
.ph-offer-landing-delivery { font-size: 15px; color: var(--ph-muted); margin: 0 0 10px; }
/* The offer's own free-text description (Pizza Hub -> Offers -> Description)
   — what's actually in the deal beyond just the price/delivery line, e.g.
   portion size or how to redeem it. Left-aligned against the otherwise
   centered card since a paragraph of real sentences reads better ragged-
   right than centered, with a soft divider above so it doesn't run
   straight into the price/delivery line above it. */
.ph-offer-landing-description { font-size: 14.5px; line-height: 1.6; color: var(--ph-ink, #3a2a24); text-align: left; margin: 4px 0 18px; padding-top: 14px; border-top: 1px solid rgba(43,27,23,.09); }
.ph-offer-landing-countdown { font-size: 13.5px; font-weight: 700; color: #a5281c; background: #fdf1ee; display: inline-block; padding: 5px 14px; border-radius: 999px; margin: 0 0 20px; }
/* A soft glow that draws the eye to the primary CTA on load — the one
   action this whole page exists to get clicked, so it's allowed a little
   delight. Runs 3 times then stops (see review-animations): an endless
   glow on something a visitor might sit and read next to for a while
   stops being "drawing attention" and starts being visual noise with no
   ongoing purpose — unlike the live-order pulse dot elsewhere, which
   loops forever because it's indicating actual ongoing state. */
.ph-offer-landing-cta { display: inline-flex; margin-top: 4px; animation: ph-offer-cta-glow 2.2s var(--ph-ease-in-out) 3; }
@keyframes ph-offer-cta-glow {
	0%, 100% { box-shadow: 0 6px 18px rgba(201,72,39,.35); }
	50% { box-shadow: 0 6px 26px rgba(201,72,39,.6); }
}
.ph-offer-landing-secondary { display: block; margin-top: 12px; font-size: 13.5px; color: var(--ph-muted); text-decoration: underline; }
.ph-offer-landing-missing { padding: 70px 0; text-align: center; }
.ph-offer-landing-missing p { color: var(--ph-muted); font-size: 15px; margin: 10px 0 26px; }
@media (max-width: 640px) {
	.ph-offer-landing-image { width: calc(100% - 20px); aspect-ratio: 4 / 3; max-height: none; margin-top: 12px; border-radius: 18px; }
	.ph-offer-landing-card { margin-top: -40px; padding: 24px 18px; }
}

/* ---------------- Student login ---------------- */
.ph-student-auth-wrap { display: flex; justify-content: center; padding: 12px 0; }
.ph-student-auth-card { width: 100%; max-width: 440px; background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: clamp(28px, 5vw, 44px); text-align: center; }
.ph-student-auth-icon { width: 64px; height: 64px; margin: 0 auto 16px; border-radius: 50%; background: var(--ph-beige); display: flex; align-items: center; justify-content: center; font-size: 28px; }
.ph-student-auth-title { font-size: 26px; margin: 0 0 8px; }
.ph-student-auth-sub { color: var(--ph-muted); font-size: 15px; margin: 0 0 22px; }
.ph-student-auth-form { text-align: left; }
.ph-student-auth-divider { display: flex; align-items: center; gap: 12px; margin: 22px 0 16px; color: var(--ph-muted); font-size: 13px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.ph-student-auth-divider::before, .ph-student-auth-divider::after { content: ''; flex: 1; height: 1px; background: #ece3d6; }

/* ---------------- Student dashboard ---------------- */
.ph-student-dashboard-header { display: flex; align-items: center; gap: 16px; background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 22px 26px; margin-bottom: 24px; }
.ph-student-dashboard-avatar { width: 56px; height: 56px; flex: 0 0 auto; border-radius: 50%; background: linear-gradient(135deg, var(--ph-orange), var(--ph-red-orange)); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 26px; }
.ph-student-dashboard-header-text { flex: 1; min-width: 0; }
.ph-student-dashboard-eyebrow { margin: 0; font-size: 13px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ph-muted); }
.ph-student-dashboard-header-text h2 { margin: 2px 0 0; font-size: 24px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ph-dashboard-logout { flex: 0 0 auto; font-size: 13px; font-weight: 600; color: var(--ph-orange); text-decoration: none; padding: 8px 16px; border-radius: 999px; border: 1.5px solid #ece3d6; transition: border-color .15s ease, background .15s ease; }
@media (hover: hover) and (pointer: fine) { .ph-dashboard-logout:hover { border-color: var(--ph-orange); background: var(--ph-beige); } }

.ph-student-dashboard-empty { text-align: center; background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 48px 24px; }
.ph-student-dashboard-empty-icon { font-size: 40px; margin-bottom: 12px; }
.ph-student-dashboard-empty h3 { margin: 0 0 8px; font-size: 22px; }
.ph-student-dashboard-empty p { margin: 0 0 20px; color: var(--ph-muted); font-size: 15px; }

.ph-dashboard-reg-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; }
.ph-dashboard-reg-card { background: #fff; border-radius: var(--ph-radius); box-shadow: var(--ph-shadow); padding: 22px; display: flex; flex-direction: column; }
.ph-dashboard-reg-card-top { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 10px; }
.ph-dashboard-reg-card-icon { font-size: 26px; }
.ph-dashboard-status-pill { font-size: 11px; font-weight: 700; padding: 5px 12px; border-radius: 999px; white-space: nowrap; }
.ph-dashboard-status-pill-completed { background: #e6f4ea; color: #1e7a3d; }
.ph-dashboard-status-pill-progress { background: var(--ph-beige); color: var(--ph-muted); }
.ph-dashboard-status-pill-pending { background: #fff3cd; color: #8a5a00; }
.ph-dashboard-reg-card-pending { opacity: .9; }
.ph-dashboard-pending-note { font-size: 13px; color: var(--ph-muted); line-height: 1.5; margin: 4px 0 0; }
.ph-dashboard-reg-card-name { font-family: 'Plus Jakarta Sans', sans-serif !important; letter-spacing: .01em; font-size: 19px; }
.ph-dashboard-reg-meta { margin: 6px 0 12px; font-size: 13.5px; color: var(--ph-muted); }
.ph-dashboard-attendance { margin: 0 0 14px; }
.ph-dashboard-attendance-bar { height: 8px; border-radius: 5px; background: rgba(43,27,23,.08); overflow: hidden; }
.ph-dashboard-attendance-bar span { display: block; height: 100%; background: linear-gradient(90deg, var(--ph-orange), var(--ph-red-orange)); border-radius: 5px; transition: width .3s ease; }
.ph-dashboard-attendance-label { margin: 6px 0 0; font-size: 12.5px; font-weight: 600; color: var(--ph-muted); }
.ph-dashboard-attendance-done { font-size: 14px; }
.ph-dashboard-checkin-form { margin-top: 10px; }
.ph-dashboard-checkin-form .ph-btn { width: 100%; }
.ph-dashboard-checkin-done { font-size: 13px; font-weight: 600; color: #1e7a3d; margin: 10px 0 0; }
.ph-dashboard-checkin-banner { border-radius: 12px; padding: 12px 16px; font-size: 14px; font-weight: 600; margin: 0 0 16px; }
.ph-dashboard-checkin-banner-ok { background: #e6f4ea; color: #1e7a3d; }
.ph-dashboard-checkin-banner-error { background: #fdecea; color: #b3261e; }
.ph-dashboard-payment-pill { display: inline-block; align-self: flex-start; font-size: 11.5px; font-weight: 700; padding: 5px 12px; border-radius: 999px; margin-bottom: 16px; }
.ph-dashboard-payment-pill-paid { background: #e6f4ea; color: #1e7a3d; }
.ph-dashboard-payment-pill-unpaid { background: #fdecea; color: #b3261e; }

/* Course Video Lessons section on the student dashboard — see
   PH_Course_Lessons/PH_Student_Dashboard on the backend for the actual
   paid+approved+drip-day gating; this is purely presentational. */
.ph-dashboard-videos { margin: 4px 0 14px; }
.ph-dashboard-videos-title { font-weight: 700; font-size: 13px; margin: 0 0 8px; }
.ph-dashboard-videos-locked-note { font-size: 12.5px; color: var(--ph-muted); background: var(--ph-beige); border-radius: 10px; padding: 10px 12px; margin: 0; }
.ph-dashboard-video-list { display: flex; flex-direction: column; gap: 10px; }
.ph-dashboard-video-item { border: 1px solid #e6ded3; border-radius: 12px; padding: 10px; }
.ph-dashboard-video-item video { width: 100%; border-radius: 8px; display: block; background: #000; }
.ph-dashboard-video-item .ph-dashboard-video-embed { width: 100%; aspect-ratio: 16/9; border-radius: 8px; display: block; background: #000; }
.ph-dashboard-video-item-title { font-weight: 600; font-size: 13px; margin: 8px 0 2px; }
.ph-dashboard-video-item-desc { font-size: 12px; color: var(--ph-muted); margin: 0; }
.ph-dashboard-video-item.is-locked { background: var(--ph-beige); opacity: .85; }
.ph-dashboard-materials { margin: 4px 0 14px; }
.ph-dashboard-material-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ph-dashboard-material-list li a { display: block; font-size: 13px; font-weight: 600; padding: 10px 12px; border: 1px solid #e6ded3; border-radius: 10px; color: var(--ph-text); text-decoration: none; }
@media (hover: hover) and (pointer: fine) {
	.ph-dashboard-material-list li a:hover { border-color: var(--ph-orange); }
}
.ph-dashboard-video-locked-icon { font-size: 26px; text-align: center; padding: 18px 0; }
.ph-dashboard-reg-cta { margin-top: auto; }
.ph-dashboard-reg-pending { margin: auto 0 0; font-size: 13px; color: var(--ph-muted); font-style: italic; }

@media (max-width: 480px) {
	.ph-student-dashboard-header { padding: 18px; }
	.ph-student-dashboard-avatar { width: 48px; height: 48px; font-size: 22px; }
}

.ph-choice-card { border: 1.5px solid #ece3d6; border-radius: 12px; padding: 14px 16px; margin-bottom: 10px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; }
.ph-choice-card.selected { border-color: var(--ph-orange); background: #fff6ef; }
.ph-choice-title { font-weight: 700; font-size: 15px; }
.ph-choice-sub { font-size: 13px; color: var(--ph-muted); }
.ph-choice-price { font-weight: 700; color: var(--ph-orange-dark); }

/* "Share my location" toggle (checkout step 2) — on by default (the
   suggested/recommended path) but the customer can switch it off entirely,
   which hides the button/map below it rather than just leaving them there
   unused. Matches the plain checkbox styling already used for "Active"
   toggles elsewhere in this plugin's admin, brought to the public frontend. */
.ph-toggle-row { display: flex; align-items: center; gap: 9px; font-size: 14px; margin-bottom: 12px; cursor: pointer; }
.ph-toggle-row input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--ph-orange); flex-shrink: 0; }
.ph-toggle-row em { color: var(--ph-muted); font-style: normal; }

/* Checkout's Ward -> Area delivery-price disclaimer (step 3) — the ৳60–100
   range note plus the "we'll confirm on WhatsApp before any change" promise
   the site owner specifically asked be shown instead of an automated
   distance-based calculator. */
.ph-delivery-fee-note { font-size: 13px; line-height: 1.55; color: var(--ph-muted); background: var(--ph-beige); border-radius: 10px; padding: 10px 13px; margin: 4px 0 0; }
/* "My area isn't listed" / "Outside Sylhet City Corporation" toggle links
   (checkout step 3) — a plain button styled to read as a text link, since
   it's an alternate path out of the normal ward/area picker, not a
   primary action that should compete visually with the real form fields. */
.ph-link-button { background: none; border: none; padding: 0; font-size: 13.5px; font-weight: 600; color: var(--ph-orange); cursor: pointer; text-decoration: underline; }
.ph-outside-city-link { margin: 10px 0 0; }
.ph-custom-area-note { font-size: 12.5px; line-height: 1.5; color: var(--ph-muted); margin: 6px 0 0; }
/* v238: stands in for the ward/area picker once a location pin is set (see
   syncAreaPicker() in frontend.js), so step 3 explains why it looks empty
   instead of appearing to have quietly lost its dropdowns. Green to read as
   a confirmation that something was captured, not a warning. */
.ph-area-auto-note { font-size: 13px; line-height: 1.5; color: #1c7d1c; background: #eef7ee; border: 1px solid #d3e8d3; border-radius: 10px; padding: 10px 12px; margin: 0 0 12px; }
.ph-checkout-email-note { font-size: 12.5px; line-height: 1.5; color: var(--ph-muted); margin: 6px 0 0; }

.ph-location-box { border: 1.5px dashed #d8cbb5; border-radius: 12px; padding: 16px; text-align: center; margin-bottom: 12px; }
.ph-location-status { font-size: 14px; color: var(--ph-muted); margin-top: 8px; }
.ph-location-status.ok { color: #1c7d1c; }
.ph-location-status.error { color: #a12525; }
/* Blocked-permission instructions (see locationPermissionHelpHtml() in
   frontend.js) — only shown after a deliberate button tap gets denied, so it
   reads as "here's exactly how to fix that" rather than a wall of text
   nobody asked for. */
.ph-location-help { font-size: 13px; line-height: 1.5; color: var(--ph-text); background: #fdf1ee; border: 1px solid #f3d9d2; border-radius: 10px; padding: 10px 12px; margin-top: 10px; text-align: left; }
.ph-location-help strong { color: #a12525; }
/* Pre-prompt explainer (v237, see #ph-location-why in frontend.js). Shown
   BEFORE the browser's own location prompt exists — that prompt explains
   nothing, so this is what a first-time visitor actually gets to read before
   deciding. Deliberately calm and neutral, not red: the reddish treatment
   above is reserved for "here's how to unblock the block you already hit". */
.ph-location-why { font-size: 13px; line-height: 1.55; color: var(--ph-text); background: var(--ph-beige); border: 1px solid #e8dfd0; border-radius: 10px; padding: 10px 12px; margin-top: 10px; text-align: left; }
.ph-location-why strong { color: #a83a1f; }
/* The draggable-pin map (see initLocationMap()) — always rendered, GPS
   permission or not, so there's always a precise way to share a delivery
   spot. Rounded to match the rest of this card's inputs, tall enough to
   drag comfortably on a phone without needing to zoom the whole page. */
.ph-location-map { height: 220px; border-radius: 10px; margin-top: 12px; border: 1px solid #e4d9c8; }
.ph-location-map .leaflet-marker-icon { cursor: grab; }
.ph-location-map-hint { font-size: 12.5px; color: var(--ph-muted); margin-top: 6px; }

.ph-payment-details { background: var(--ph-beige); border-radius: 12px; padding: 14px 16px; margin-top: 10px; font-size: 14px; line-height: 1.6; }
.ph-error-msg { color: #a12525; font-size: 14px; margin-top: 6px; }
.ph-success-box { text-align: center; padding: 40px 20px; }
.ph-success-icon { width: 70px; height: 70px; border-radius: 50%; background: #e5f7e5; color: #1c7d1c; display: flex; align-items: center; justify-content: center; font-size: 34px; margin: 0 auto 16px; animation: ph-verified-pop .55s cubic-bezier(.34,1.56,.64,1) both; }

/* ---------------- Order tracking ---------------- */
/* Redesigned in 5.11.1: this section previously sat flush against the
   page's own white background with only a faint box-shadow to separate
   the white .ph-tracking-card from it — on a white/near-white page this
   read as flat and unclear. It also had a bug: `.ph-tracking-section`'s
   own `padding-bottom` was silently overridden by the later `.ph-container`
   rule (both are single-class selectors, so equal specificity means
   whichever is declared LAST in this file wins — and .ph-container's
   `padding: 0 16px` shorthand comes after this block and resets
   padding-bottom to 0). Selector below is bumped to
   `.ph-container.ph-tracking-section` (two classes = higher specificity)
   so this section's own padding always wins regardless of source order. */
/* This block used to also re-declare `background` here with the exact same
   value `.ph-section-panel` (below) already sets — copied in at the time
   rather than actually needing the override, since only `padding` was ever
   at risk from `.ph-container`'s later rule. That leftover duplicate is
   removed now: it was silently freezing this section one background update
   behind `.ph-section-panel` (its four-corner decorative blobs, added after
   feedback that this section's own side margins looked bare, weren't
   reaching this element because this more-specific rule was still painting
   over them with the old plain gradient). Background now comes from
   `.ph-section-panel` alone, same as every other panel section on the page. */
.ph-container.ph-tracking-section { margin-top: 56px !important; padding: 40px 20px 48px; border-radius: 28px; }
.ph-tracking-card { max-width: 620px; margin: 0 auto; background: #fff; border: 1px solid rgba(43,27,23,.07); border-radius: var(--ph-radius); box-shadow: 0 14px 34px rgba(43,27,23,.10); padding: 40px clamp(20px, 4vw, 44px) 44px; text-align: center; }
.ph-tracking-eyebrow { margin: 0 auto 14px; }
.ph-tracking-title { font-size: clamp(24px, 3.4vw, 34px); margin: 0 0 8px; }
.ph-tracking-sub { color: var(--ph-muted); font-size: 15px; margin: 0 0 26px; }

/* Pizza Blogs — listing page for the site's own native WordPress posts
   (see PH_Shortcodes::blog_page()). Card look deliberately echoes the
   menu's own .ph-food-card (rounded corners, soft shadow, image on top)
   so this reads as part of the same site rather than a bolted-on blog
   theme. */
.ph-blog-section { margin-top: 56px; padding: 0 20px 64px; }
.ph-blog-title { font-size: clamp(28px, 4vw, 40px); margin: 10px 0 8px; }
.ph-blog-sub { color: var(--ph-muted); font-size: 15px; max-width: 620px; margin: 0 0 32px; }
.ph-blog-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 28px; }
.ph-blog-card { display: block; background: #fff; border-radius: var(--ph-radius); overflow: hidden; box-shadow: 0 10px 26px rgba(43,27,23,.08); text-decoration: none; color: inherit; transition: transform .2s ease, box-shadow .2s ease; }
@media (hover: hover) and (pointer: fine) {
	.ph-blog-card:hover { transform: translateY(-4px); box-shadow: 0 16px 34px rgba(43,27,23,.14); }
}
.ph-blog-card-img { width: 100%; aspect-ratio: 16/10; overflow: hidden; }
.ph-blog-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ph-blog-card-img-missing { display: flex; align-items: center; justify-content: center; font-size: 40px; opacity: .35; background: linear-gradient(135deg,#F3EFE8,#e8dcc9); }
.ph-blog-card-body { padding: 18px 18px 20px; }
.ph-blog-card-date { font-size: 12.5px; color: var(--ph-muted); text-transform: uppercase; letter-spacing: .03em; }
.ph-blog-card-title { font-size: 19px; margin: 6px 0 8px; line-height: 1.3; color: var(--ph-text); }
.ph-blog-card-excerpt { font-size: 14px; color: var(--ph-muted); line-height: 1.5; margin: 0; }
.ph-blog-pagination { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 40px; }
.ph-blog-pagination .page-numbers { display: inline-flex; align-items: center; justify-content: center; min-width: 38px; height: 38px; padding: 0 10px; border-radius: 999px; background: var(--ph-beige); color: var(--ph-text); text-decoration: none; font-weight: 600; font-size: 14px; }
.ph-blog-pagination .page-numbers.current { background: var(--ph-orange); color: #fff; }
@media (hover: hover) and (pointer: fine) {
	.ph-blog-pagination .page-numbers:not(.current):hover { background: var(--ph-orange-light, #f3d9c8); }
}
.ph-track-form { display: flex; gap: 10px; flex-wrap: wrap; max-width: 480px; margin: 0 auto 28px; }
.ph-track-input-wrap { position: relative; flex: 1; min-width: 160px; }
.ph-track-input-icon { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); font-size: 15px; opacity: .65; pointer-events: none; }
.ph-track-form input { width: 100%; padding: 12px 13px 12px 38px; border-radius: 10px; border: 1.5px solid #ece3d6; font-family: inherit; font-size: 15px; transition: border-color .15s ease, box-shadow .15s ease; }
.ph-track-form input:focus { outline: none; border-color: var(--ph-orange); box-shadow: 0 0 0 3px rgba(201,72,39,.12); }
.ph-track-btn.ph-track-btn { width: 100%; }
@media (min-width: 480px) { .ph-track-btn.ph-track-btn { width: auto; } }
.ph-track-hint { margin: -18px auto 20px; max-width: 480px; font-size: 13px; color: var(--ph-muted); }

/* Phone-only lookup: when the customer leaves the Order ID blank and just
   searches by phone number, several orders can come back — shown as a
   simple clickable list rather than a single timeline, so they can pick
   the one they meant. */
.ph-track-order-list { max-width: 480px; margin: 0 auto; text-align: left; display: flex; flex-direction: column; gap: 10px; }
.ph-track-order-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 16px; border: 1.5px solid #ece3d6; border-radius: 12px; background: #fff; cursor: pointer; transition: border-color .15s ease, transform .15s ease; text-align: left; width: 100%; font-family: inherit; }
.ph-track-order-item:active { transform: scale(.98); }
@media (hover: hover) and (pointer: fine) { .ph-track-order-item:hover { border-color: var(--ph-orange); transform: translateY(-1px); } }
.ph-track-order-item-id { font-weight: 700; }
.ph-track-order-item-date { font-size: 12.5px; color: var(--ph-muted); margin-top: 2px; }
.ph-track-order-item-status { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 4px 10px; border-radius: 999px; background: var(--ph-cream); color: var(--ph-orange); white-space: nowrap; }
.ph-track-back-btn { background: none; border: none; color: var(--ph-orange); font-weight: 700; font-size: 14px; cursor: pointer; margin-bottom: 16px; padding: 4px; }
.ph-timeline { max-width: 440px; margin: 0 auto; text-align: left; }
.ph-review-mine { max-width: 440px; margin: 18px auto 0; background: var(--ph-cream); border-radius: var(--ph-radius); padding: 18px 20px; text-align: center; }
.ph-review-mine-title { font-weight: 700; margin: 0 0 8px; }
.ph-review-mine-stars { color: var(--ph-orange); font-size: 18px; margin-bottom: 6px; }
.ph-review-mine-text { font-style: italic; color: var(--ph-charcoal); margin: 0 0 8px; }
.ph-review-mine-note { font-size: 13px; color: var(--ph-muted); margin: 0; }
.ph-review-name-hint { font-weight: 400; font-size: 12px; color: var(--ph-muted); }
.ph-review-gmb-prompt { margin-top: 16px; padding-top: 16px; border-top: 1px solid #e6ded3; }
.ph-review-gmb-text { font-size: 13.5px; color: var(--ph-charcoal); margin: 0 0 12px; }
.ph-review-gmb-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.ph-review-gmb-actions .ph-btn { padding: 9px 16px; font-size: 13.5px; }
.ph-review-gmb-hint { font-size: 12px; color: var(--ph-success, #2e8b57); margin: 10px 0 0; }
.ph-timeline-step { display: flex; gap: 14px; padding-bottom: 24px; position: relative; }
.ph-timeline-step::before { content:''; position: absolute; left: 13px; top: 30px; bottom: 0; width: 2px; background: #e6ded3; transition: background .3s ease; }
.ph-timeline-step:last-child::before { display: none; }
.ph-timeline-dot { width: 28px; height: 28px; border-radius: 50%; background: #e6ded3; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 14px; font-weight: 700; transition: background .3s ease, transform .3s ease; }
.ph-timeline-step.done .ph-timeline-dot { background: var(--ph-orange); box-shadow: 0 0 0 4px rgba(201,72,39,.15); transform: scale(1.05); }
.ph-timeline-step.done::before { background: var(--ph-orange); }
.ph-timeline-label { font-weight: 700; font-size: 16px; }
.ph-timeline-time { font-size: 14px; color: var(--ph-muted); }

.ph-track-live-note { max-width: 440px; margin: 4px auto 0; text-align: center; font-size: 12.5px; color: var(--ph-muted); display: flex; align-items: center; justify-content: center; gap: 6px; }
.ph-track-live-dot { width: 8px; height: 8px; border-radius: 50%; background: #46a758; display: inline-block; animation: ph-track-live-pulse 1.6s infinite; }
@keyframes ph-track-live-pulse { 0%,100% { box-shadow: 0 0 0 0 rgba(70,167,88,.55); } 50% { box-shadow: 0 0 0 4px rgba(70,167,88,0); } }

/* ---------------- Customer self-cancel (order tracking page) ----------------
   A quiet text link rather than a big red button up front — cancelling is a
   deliberate, occasional action, not something that should visually compete
   with the live status timeline above it. Expands into a small confirm panel
   in place; see PH_Orders::cancel_by_customer() for the eligibility rule this
   mirrors (only shown at all when the kitchen hasn't started the order yet). */
.ph-track-cancel-zone { max-width: 440px; margin: 18px auto 0; text-align: center; }
.ph-track-cancel-link { background: none; border: none; padding: 6px 10px; font: inherit; font-size: 13px; color: var(--ph-muted); text-decoration: underline; cursor: pointer; }
@media (hover: hover) and (pointer: fine) { .ph-track-cancel-link:hover { color: #a12525; } }
.ph-track-cancel-panel { margin-top: 10px; padding: 16px; border: 1.5px solid #f0d7d7; background: #fff8f7; border-radius: 12px; text-align: left; }
.ph-track-cancel-panel-title { margin: 0 0 10px; font-weight: 700; color: var(--ph-charcoal); }
.ph-track-cancel-label { display: block; font-size: 13px; font-weight: 600; color: var(--ph-charcoal); margin-bottom: 10px; }
.ph-track-cancel-label select { display: block; width: 100%; margin-top: 5px; padding: 9px 10px; border-radius: 8px; border: 1.5px solid #ece3d6; font-family: inherit; font-size: 14px; background: #fff; }
.ph-track-cancel-note { display: block; width: 100%; min-height: 60px; margin-bottom: 12px; padding: 9px 10px; border-radius: 8px; border: 1.5px solid #ece3d6; font-family: inherit; font-size: 14px; resize: vertical; }
.ph-track-cancel-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.ph-track-cancel-actions .ph-btn { flex: 1; min-width: 140px; padding: 10px 14px; font-size: 14px; }
.ph-btn-danger.ph-btn-danger { background: #c0392b !important; color: #fff !important; border: none !important; }
@media (hover: hover) and (pointer: fine) { .ph-btn-danger.ph-btn-danger:hover { background: #a5281c !important; color: #fff !important; } }
.ph-track-cancelled { max-width: 440px; margin: 0 auto; text-align: center; }
.ph-track-cancelled-icon { width: 52px; height: 52px; line-height: 52px; margin: 0 auto 12px; border-radius: 50%; background: #fbeaea; color: #c0392b; font-size: 24px; font-weight: 800; }
.ph-track-cancelled-meta { color: var(--ph-muted); font-size: 13.5px; margin: -6px 0 14px; }

/* ---------------- Bottom mobile nav ----------------
   Redesigned in 5.11.0: hand-drawn inline SVG line icons (replacing raw
   emoji, which render inconsistently across Android/iOS/browser combos),
   a pill highlight behind the active icon, a polished badge, and subtle
   elevation so the bar reads as a real app tab-bar rather than a flat
   strip. Height is intentionally NOT fixed — the safe-area inset is added
   as extra padding on top of the content's natural height, so a device
   with a large home-indicator inset (e.g. notched iPhones) gets a taller
   bar instead of squeezed icons. --ph-bottom-nav-h below is therefore an
   ESTIMATE of the bar's typical rendered height (content + a modest
   safe-area allowance), not a live measurement — it exists only so
   .ph-sticky-cart-bar's mobile override (further below) has a single
   number to sit above, the same way the floating WhatsApp button's own
   inline styles (class-ph-frontend-assets.php) already estimate this
   bar's height in plain pixels rather than measuring it. */
:root { --ph-bottom-nav-h: 64px; }
.ph-bottom-nav { position: fixed; bottom: 0; left: 0; right: 0; background: rgba(255,255,255,.97); -webkit-backdrop-filter: saturate(180%) blur(14px); backdrop-filter: saturate(180%) blur(14px); border-top: 1px solid rgba(43,27,23,.08); box-shadow: 0 -6px 22px rgba(43,27,23,.10); display: none; justify-content: space-around; align-items: center; padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); z-index: 400; }
@media (max-width: 782px) { .ph-bottom-nav.enabled { display: flex; } }
.ph-bottom-nav-item { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; flex: 1 1 0; min-width: 0; max-width: 88px; padding: 2px 4px; font-size: 10.5px; font-weight: 600; color: var(--ph-muted); text-decoration: none; position: relative; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.ph-bottom-nav-icon { display: flex; align-items: center; justify-content: center; position: relative; width: 44px; height: 30px; border-radius: 12px; transition: background-color .18s ease, transform .18s ease; }
.ph-bottom-nav-icon svg { width: 22px; height: 22px; }
.ph-bottom-nav-label { line-height: 1.15; letter-spacing: .01em; transition: color .15s ease; }
.ph-bottom-nav-item.active { color: var(--ph-orange); }
.ph-bottom-nav-item.active .ph-bottom-nav-icon { background: rgba(201,72,39,.13); transform: translateY(-1px); }
/* v244 — native tab-bar press feedback: an immediate tint + scale on
   :active so every tap is acknowledged in milliseconds, pairing with the
   instant jump the JS performs. Short transitions so the release snap
   never lags the next tap; -webkit-tap-highlight is suppressed above so
   this is the only (consistent) press visual. */
.ph-bottom-nav-item:active { color: var(--ph-orange); }
.ph-bottom-nav-item:active .ph-bottom-nav-icon { background: rgba(201,72,39,.24); transform: scale(.92); }
.ph-bottom-nav-badge { position: absolute; top: 1px; right: 3px; background: var(--ph-orange); color: #fff; font-size: 9.5px; font-weight: 700; line-height: 1; border-radius: 999px; padding: 3px 5px; min-width: 15px; text-align: center; box-shadow: 0 0 0 2px rgba(255,255,255,.97); }
@media (max-width: 782px) { .ph-sticky-cart-bar { bottom: calc(var(--ph-bottom-nav-h) + env(safe-area-inset-bottom) + 10px); } }

/* Utility */
.ph-container { width: 90%; max-width: 1080px; margin: 0 auto; padding: 0 16px; }
/* Always capped at 1280px, deliberately NOT affected by the layout_width=full
   breakout above — matches the design source, where the hero/benefits/story/
   reviews sections stay centered with a max-width even at very wide
   viewports (only the plain menu grid was meant to go edge-to-edge). */
.ph-container-wide { width: 90%; max-width: 1280px; margin: 0 auto; padding: 0 16px; }
@media (max-width: 768px) {
	.ph-container, .ph-container-wide { width: 98%; }
}
.ph-loading { text-align: center; padding: 40px; color: var(--ph-muted); }

/* ---------------- Full-width layout (Settings → layout_width = 'full', default) ---------------- */
/* Only affects the [pizza_hub_app] all-in-one shortcode's .ph-container wrapper
   (menu grid + tracking section). Checkout/cart/modals intentionally stay at
   their existing narrower max-widths regardless — a form or drawer stretched
   to fill a 27" monitor is a usability regression, not an improvement.

   PREVIOUSLY this used a `100vw` + negative-margin "breakout" trick to escape
   the active theme's own content wrapper and go truly edge-to-edge across the
   whole browser viewport. That broke in two different ways on two different
   real themes (Twenty Twenty-Five, then Divi) — each theme's own content
   wrapper (margin/padding/box structure) interacts with the trick differently,
   and there's no way to predict that from CSS alone across arbitrary themes.
   Both breakages produced a shifted layout with a stray horizontal scrollbar,
   which is a far worse outcome than "not quite edge-to-edge". Removed that
   technique entirely: this class now simply removes the plugin's OWN internal
   1080px cap so content fills whatever width the active theme's content area
   already provides. It will not force anything wider than the viewport, so it
   cannot reproduce this class of bug — the trade-off is that on a theme with a
   narrow content column, this won't reach true edge-to-edge (only as wide as
   that theme allows), which is a safe, honest limitation rather than a bug. */
.ph-app.ph-layout-full .ph-container { max-width: none !important; }
/* .ph-menu-grid is CSS Grid now (see "Menu grid" above) — on the wider,
   theme-full-width layout this class enables, a smaller grid-template
   minimum fits a bit more per row on the extra width; the max-width cap
   is unchanged so cards don't grow oversized either way. */
.ph-app.ph-layout-full .ph-menu-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* ---------------- Footer (brand-matched: dark brown + terracotta CTA) ---------------- */
.ph-footer { margin-top: 48px; background: var(--ph-charcoal); color: #fff; border-radius: 32px 32px 0 0; overflow: hidden; }
/* Same inheritance issue as the bento dark/ember tiles above — force heading
   color explicitly rather than relying on inheritance from .ph-footer. */
.ph-footer h1, .ph-footer h2, .ph-footer h3, .ph-footer .ph-heading { color: #fff !important; }
.ph-footer-cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px; padding: 32px 24px; border-bottom: 1px solid rgba(255,255,255,.1); }
.ph-footer-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .28em; text-transform: uppercase; color: var(--ph-red-orange); margin: 0 0 8px; }
.ph-footer-cta h2 { margin: 0; font-size: clamp(22px, 3.4vw, 32px); line-height: 1.15; max-width: 480px; }
.ph-footer-cta-btn { flex-shrink: 0; }
/* 4-column footer: Brand (wider) / Quick Links / Course & Students / Contact.
   Was a plain 2-column brand+contact split — now carries real site
   navigation and course/student links so the footer works as a second way
   to get around the site, not just a contact card. */
.ph-footer .ph-footer-grid.ph-footer-grid-4 { display: grid !important; gap: 32px !important; padding: 44px 24px 36px !important; grid-template-columns: 1.3fr 0.9fr 0.9fr 1.6fr !important; align-items: start !important; }
@media (max-width: 900px) { .ph-footer .ph-footer-grid.ph-footer-grid-4 { grid-template-columns: 1fr 1fr !important; } .ph-footer-col-brand { grid-column: 1 / -1; } }
@media (max-width: 560px) { .ph-footer .ph-footer-grid.ph-footer-grid-4 { grid-template-columns: 1fr !important; } .ph-footer-col-brand { grid-column: auto; } }
.ph-footer-col h3 { margin: 0 0 16px; font-size: 11px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.45); }
.ph-footer-brand { display: flex; align-items: center; flex-wrap: nowrap; gap: 12px; }
.ph-footer-brand > img, .ph-footer-brand > div { align-self: center; margin: 0; }
/* The logo file itself is a solid white square with the wordmark/tagline
   baked directly into it (no transparency) — dropped straight onto the dark
   footer background at a flush 52px, that white square read as a stray,
   broken box rather than a logo (reported live, with a screenshot pointing
   right at it). Framed as a small white rounded "badge" with padding and a
   soft shadow instead, it reads as a deliberate mark sitting on the dark
   footer — the same treatment real logos-on-dark-footers commonly use when
   the mark itself isn't available in a light/transparent variant. Sized up
   slightly and padded (box-sizing: border-box keeps the overall footprint
   the same as before) so the artwork isn't shrunk by the new padding. */
.ph-footer-logo { width: 60px; height: 60px; object-fit: contain; border-radius: 12px; background: #fff; padding: 6px; box-sizing: border-box; box-shadow: 0 6px 16px rgba(0,0,0,.28); }
.ph-footer-tagline { margin: 3px 0 0; line-height: 1.3; font-size: 10px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase; color: rgba(255,255,255,.55); }
.ph-footer-desc { margin: 16px 0 0; max-width: 380px; font-size: 15px; line-height: 1.6; color: rgba(255,255,255,.65); }
.ph-footer-socials { display: flex; gap: 8px; margin-top: 20px; }
.ph-footer-socials a { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; border: 1px solid rgba(255,255,255,.15); color: rgba(255,255,255,.7); text-decoration: none; transition: border-color .2s ease, color .2s ease, background .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-footer-socials a:hover { border-color: var(--ph-red-orange); color: #fff; background: var(--ph-red-orange); } }
.ph-footer-links { list-style: none; margin: 0; padding: 0; }
.ph-footer-links li { margin-bottom: 12px; }
.ph-footer-links a { color: rgba(255,255,255,.75); text-decoration: none; font-size: 15px; transition: color .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-footer-links a:hover { color: var(--ph-red-orange); } }
/* Slightly smaller + nowrap on the address/hours lines specifically —
   direct feedback that these two long lines should each fit on one line
   rather than wrapping to two; combined with the wider contact column
   above (was 1.2fr, now 1.6fr) so this doesn't just overflow instead. */
.ph-footer-contact p { margin: 0 0 14px; font-size: 15px; color: rgba(255,255,255,.75); }
.ph-footer-contact p:nth-of-type(1),
.ph-footer-contact p:nth-of-type(2) { font-size: 13.5px; white-space: nowrap; }
@media (max-width: 560px) {
	.ph-footer-contact p:nth-of-type(1),
	.ph-footer-contact p:nth-of-type(2) { white-space: normal; }
}
.ph-footer-contact a { color: inherit; text-decoration: none; transition: color .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-footer-contact a:hover { color: var(--ph-red-orange); } }
.ph-footer-bottom { display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: space-between; align-items: center; padding: 18px 24px; border-top: 1px solid rgba(255,255,255,.1); font-size: 13px; color: rgba(255,255,255,.45); }
.ph-footer-bottom-centered { justify-content: center; flex-direction: column; text-align: center; gap: 4px; }
.ph-footer-bottom p { margin: 0; }
.ph-footer-legal-links { display: flex; gap: 18px; }
.ph-footer-legal-links a { color: rgba(255,255,255,.6); text-decoration: none; transition: color .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-footer-legal-links a:hover { color: var(--ph-red-orange); } }
.ph-footer-credit-link { color: #fff; text-decoration: none; transition: color .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-footer-credit-link:hover { color: var(--ph-red-orange); } }
@media (max-width: 480px) { .ph-footer-cta { flex-direction: column; align-items: flex-start; } .ph-footer-bottom { justify-content: center; text-align: center; } }

/* ---------------- Site header (logo, nav, cart, login, Order Now) ---------------- */
.ph-site-header { position: sticky; top: 0; z-index: 100; background: rgba(250,248,244,.92); backdrop-filter: blur(10px); border-bottom: 1px solid #ece3d6; }
.ph-site-header .ph-site-header-inner.ph-site-header-inner { display: flex !important; align-items: center; justify-content: space-between; gap: 18px; padding: 5px 16px !important; }
.ph-site-header-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ph-text); flex-shrink: 0; }
.ph-site-header-brand img { width: 92px; height: 92px; object-fit: contain; }
.ph-site-header-brand span { display: none; }
.ph-site-header-nav { display: flex; gap: 20px; }
.ph-site-header-nav a { position: relative; font-size: 17px; font-weight: 600; color: var(--ph-text); text-decoration: none; opacity: .8; transition: opacity .15s, color .15s; }
.ph-site-header-nav a::after { content: ''; position: absolute; left: 0; right: 100%; bottom: -4px; height: 2px; background: var(--ph-orange); border-radius: 2px; transition: right .2s ease; }
@media (hover: hover) and (pointer: fine) { .ph-site-header-nav a:hover { opacity: 1; color: var(--ph-orange); } }
@media (hover: hover) and (pointer: fine) { .ph-site-header-nav a:hover::after { right: 0; } }
.ph-site-header-actions { display: flex; align-items: center; gap: 14px; }
.ph-site-header-login { font-size: 16px; font-weight: 600; color: var(--ph-text); text-decoration: none; white-space: nowrap; }
@media (hover: hover) and (pointer: fine) { .ph-site-header-login:hover { color: var(--ph-orange); } }
/* Was plain text at 80% opacity — easy to miss next to the cart icon and
   the login link either side of it. Now a proper bordered pill button
   with an icon, same weight as a real call-to-action instead of an
   afterthought link. */
.ph-site-header-track { display: inline-flex; align-items: center; gap: 6px; font-size: 14.5px; font-weight: 700; color: var(--ph-orange); text-decoration: none; white-space: nowrap; padding: 8px 16px; border-radius: 999px; border: 1.5px solid var(--ph-orange); transition: background .18s ease, color .18s ease, transform .18s ease, box-shadow .18s ease; }
.ph-site-header-track span { font-size: 15px; }
@media (hover: hover) and (pointer: fine) { .ph-site-header-track:hover { background: var(--ph-orange); color: #fff; transform: translateY(-1px); box-shadow: 0 6px 14px rgba(201,72,39,.3); } }
.ph-site-header-cart { position: relative; background: none; border: none; font-size: 23px; cursor: pointer; line-height: 1; padding: 4px; }
.ph-site-header-cart-badge { position: absolute; top: -4px; right: -6px; background: var(--ph-orange); color: #fff; font-size: 10px; font-weight: 700; min-width: 16px; height: 16px; border-radius: 999px; display: flex; align-items: center; justify-content: center; padding: 0 3px; }
.ph-site-header-burger { display: none; background: none; border: none; font-size: 22px; cursor: pointer; padding: 4px 8px; }
.ph-site-header-mobile-nav { display: none; flex-direction: column; gap: 2px; padding: 6px 16px 14px; }
.ph-site-header-mobile-nav a { padding: 13px 6px; font-size: 17px; font-weight: 600; color: var(--ph-text); text-decoration: none; border-top: 1px solid #ece3d6; }
.ph-site-header-mobile-nav.open { display: flex; }
/* Bumped from 860px to 980px in 5.8.0: the desktop nav gained a 6th item
   ("Verify Certificate") and the old breakpoint left a squeezed zone
   (~861-960px) where the wider nav could crowd or overflow past the
   header actions before switching to the burger menu. */
@media (max-width: 980px) {
	.ph-site-header-nav { display: none; }
	.ph-site-header-burger { display: block; }
	/* ORDER NOW no longer exists in the header markup at all (removed
	   entirely, every width — see render_site_header()), so there's nothing
	   left to hide here. This selector is kept only as a harmless backstop
	   in case any older cached copy of the header markup is still being
	   served somewhere. */
	.ph-site-header-actions .ph-btn.ph-btn.ph-btn { display: none !important; }
	/* Already reachable from the burger menu (.ph-site-header-mobile-nav) —
	   hiding it here keeps the mobile actions row (login/dashboard + cart +
	   burger) from getting cramped next to the brand logo. */
	.ph-site-header-track { display: none !important; }
	/* Requested: "i have already student login in humberber manu so remove
	   it from mobile version top header just logo and humber manu" — both
	   the Student Login/My Dashboard link and the cart button are already
	   reachable elsewhere on mobile (the burger menu's last item, and the
	   bottom nav's Cart tab, respectively), so the mobile top header is now
	   just the brand logo and the burger button, nothing else. */
	.ph-site-header-login { display: none !important; }
	.ph-site-header-cart { display: none !important; }
}

/* Smallest phones (iPhone SE-class ~375px and below, plus older 320-360px
   Android devices): with the login link and cart button now hidden on
   mobile (see the 980px block above), only the brand logo+name and burger
   button remain in the top header, so there's no overflow risk left to
   guard against here — kept as a lean, comment-only breakpoint rather than
   deleted outright, in case a future header addition needs it again. */
@media (max-width: 400px) {
	.ph-site-header .ph-site-header-inner.ph-site-header-inner { gap: 8px; padding: 8px 12px !important; }
	.ph-site-header-brand span { font-size: 16px; }
	.ph-site-header-brand img { width: 56px; height: 56px; }
	.ph-category-nav { top: 68px; } /* header shrinks to ~72px tall here (56px logo + padding) — matches .ph-category-nav's top offset above */
}

/* ---------------- Page-builder wrapper cleanup ---------------- */
/* The app is placed inside a builder's own section/row (a Divi Section >
   Row > Column > Text module here — confirmed via the live site's DOM).
   Builders give those wrappers generous default top/bottom padding (Divi:
   ~50px on the section plus ~25px on the row, so ~75px combined), which was
   never wanted here: .ph-site-header is already sticky with its own
   padding, and .ph-footer already supplies its own visual edge, so that
   default padding only showed up as unwanted blank space above the header
   and below the footer. :has() lets this stay scoped to wrappers that
   actually contain the app, so it can't affect any other Divi section
   elsewhere on the site. Left/right padding is untouched — .ph-container /
   .ph-container-wide already handle horizontal centering. */
.et_pb_section:has(.ph-app),
.et_pb_row:has(.ph-app) {
	padding-top: 0 !important;
	padding-bottom: 0 !important;
}

/* Note: the floating WhatsApp chat button's CSS lives inline in
   PH_Frontend_Assets::print_floating_whatsapp_button() instead of here,
   deliberately — that button is meant to appear on EVERY front-end page,
   including ones with no Pizza Hub shortcode at all, and this stylesheet is
   only enqueued on pages that have one (see PH_Frontend_Assets::enqueue()).
   Keeping the button's styling self-contained means it renders correctly
   regardless of whether this file happens to be loaded. */

/* ---------------- Keyboard focus — a consistent, branded focus ring for
   buttons/links/checkboxes/radios across the storefront, using
   :focus-visible so it only appears for keyboard navigation (Tab), never
   on an ordinary tap/click. Deliberately unscoped (not ".ph-root X" /
   ".ph-app X") since several interactive surfaces — the cart drawer, the
   item customization sheet, toasts — are appended directly to <body>,
   outside that subtree (see the :root token comment at the top of this
   file for why that same "appended outside .ph-root" pattern already
   mattered once before, for CSS custom properties). */
.ph-root a:focus-visible, .ph-app a:focus-visible,
.ph-root button:focus-visible, .ph-app button:focus-visible,
.ph-root input[type=checkbox]:focus-visible, .ph-app input[type=checkbox]:focus-visible,
.ph-root input[type=radio]:focus-visible, .ph-app input[type=radio]:focus-visible,
.ph-cart-drawer a:focus-visible, .ph-cart-drawer button:focus-visible,
.ph-sheet a:focus-visible, .ph-sheet button:focus-visible,
.ph-sheet input[type=checkbox]:focus-visible, .ph-sheet input[type=radio]:focus-visible {
	outline: 2px solid var(--ph-gold);
	outline-offset: 2px;
	border-radius: 6px;
}

/* ---------------- "Build a Custom Pizza" CTA banner — sits at the top of
   the "Everything we make" menu section, only rendered when Settings ->
   General -> Builder Product is set (see renderCustomPizzaCta() in
   frontend.js). Its button reuses the existing [data-add] click handler
   every other product card's "Choose"/"Add" button already goes through,
   so clicking it opens the normal size/extras sheet — this banner is
   purely a shortcut into that same flow, not a separate feature. */
.ph-builder-cta {
	display: flex; align-items: center; justify-content: space-between; gap: var(--ph-space-4, 16px);
	flex-wrap: wrap; margin: 0 0 20px; padding: 18px 22px;
	border-radius: var(--ph-radius); color: var(--ph-white);
	background: linear-gradient(120deg, var(--ph-charcoal), var(--ph-charcoal-2));
	box-shadow: var(--ph-shadow);
	position: relative; overflow: hidden;
}
.ph-builder-cta::before {
	content: ''; position: absolute; inset: 0;
	background: radial-gradient(circle at 85% 30%, rgba(212,171,83,.28), transparent 60%);
	pointer-events: none;
}
.ph-builder-cta-text { display: flex; flex-direction: column; gap: 4px; position: relative; z-index: 1; }
.ph-builder-cta-text strong { font-size: 17px; font-weight: 800; }
.ph-builder-cta-text span { font-size: 13.5px; color: rgba(255,255,255,.78); }
.ph-builder-cta .ph-btn { position: relative; z-index: 1; flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 480px) {
	.ph-builder-cta { padding: 16px; }
	.ph-builder-cta .ph-btn { width: 100%; text-align: center; }
}

/* ---------------- Live "pizza preview" circle + round topping chips in
   the item customize sheet (v222). Purely visual — the extras/[data-extra]
   attributes and add-to-cart logic are unchanged, this only restyles how
   each extra ROW looks and adds one decorative circle above them that
   fills up with the toppings actually picked (see updatePizzaPreview() in
   frontend.js). Scoped under .ph-sheet so it can never affect any other
   part of the storefront. */
.ph-pizza-preview { display: flex; flex-direction: column; align-items: center; gap: 8px; margin: 4px 0 26px; }
/* A real 3D tilt — the pizza is viewed at a slight downward angle, like a
   photo of an actual pizza on a table, instead of a flat top-down circle.
   Split across two static wrapper layers (perspective, then rotateX) so
   they never fight with the JS-driven resize/bounce transform that stays
   on .ph-pizza-preview-base itself (nested transforms compose correctly:
   tilted + sized + bounced, all at once, each layer only touching its own
   property). */
.ph-pizza-preview-stage { position: relative; padding-bottom: 14px; perspective: 700px; }
.ph-pizza-preview-stage::after {
	/* The pizza's own shadow cast onto the "table" beneath it, grounding
	   the tilt instead of leaving the disc looking like it's floating. */
	content: ''; position: absolute; left: 50%; bottom: 2px; width: 72%; height: 16px;
	background: radial-gradient(ellipse at center, rgba(43,27,23,.4), transparent 72%);
	transform: translateX(-50%); filter: blur(1px); z-index: 0;
}
.ph-pizza-preview-tilt { transform: rotateX(22deg); transform-style: preserve-3d; position: relative; z-index: 1; }
.ph-pizza-preview-base {
	position: relative; width: 130px; height: 130px; border-radius: 50%;
	transition: width .3s var(--ph-ease-out), height .3s var(--ph-ease-out);
	/* Crust ring (outer) -> sauce (mid) -> cheese (center). The extra
	   highlight/shadow bands on the crust (the two radial-gradients right
	   before the base dough color) bevel its edge — light catching the
	   near side, shadow pooling the far side — so it reads as a raised,
	   puffy 3D ring rather than a flat colored circle. */
	background:
		radial-gradient(circle at 35% 22%, rgba(255,255,255,.65), transparent 40%),
		radial-gradient(circle at 32% 30%, rgba(255,255,255,.35), transparent 46%),
		radial-gradient(circle at 68% 76%, rgba(43,27,23,.28), transparent 46%),
		radial-gradient(circle, #f6d78a 0%, #f6d78a 63%, #e2ad4e 64%, #e2ad4e 78%, #d99a3d 79%, #d99a3d 100%);
	box-shadow: 0 14px 22px rgba(43,27,23,.28), 0 2px 0 rgba(43,27,23,.15), inset 0 0 0 6px rgba(255,255,255,.35), inset 0 -6px 10px rgba(43,27,23,.18), inset 0 4px 8px rgba(255,255,255,.4);
	overflow: hidden;
}
.ph-pizza-preview-base::before {
	/* Sauce + melted-cheese texture inside the crust ring — a scatter of
	   soft blobs via repeating radial-gradients rather than a real photo,
	   plus a diagonal sheen and a pooled-shadow edge so the cheese layer
	   itself looks domed rather than flat. */
	content: ''; position: absolute; inset: 10%; border-radius: 50%;
	box-shadow: inset 0 -5px 9px rgba(43,27,23,.35), inset 0 3px 6px rgba(255,255,255,.3);
	background:
		radial-gradient(circle at 20% 25%, rgba(255,255,255,.5) 0 6%, transparent 7%),
		radial-gradient(circle at 70% 15%, rgba(255,255,255,.4) 0 5%, transparent 6%),
		radial-gradient(circle at 50% 55%, rgba(255,255,255,.45) 0 7%, transparent 8%),
		radial-gradient(circle at 80% 70%, rgba(255,255,255,.4) 0 5%, transparent 6%),
		radial-gradient(circle at 25% 75%, rgba(255,255,255,.4) 0 6%, transparent 7%),
		radial-gradient(circle at 30% 25%, rgba(255,255,255,.25), transparent 55%),
		#e0642f;
}
.ph-pizza-preview-toppings { position: absolute; inset: 0; z-index: 1; }
.ph-pizza-topping {
	position: absolute; font-size: 18px; line-height: 1;
	filter: drop-shadow(0 3px 2px rgba(0,0,0,.4)) drop-shadow(0 1px 0 rgba(255,255,255,.25));
	animation: ph-topping-pop .22s var(--ph-ease-out);
}
@keyframes ph-topping-pop { from { transform: translate(-50%,-50%) scale(.3); opacity: 0; } to { opacity: 1; } }
.ph-pizza-preview-caption { font-size: 12px; font-weight: 700; color: var(--ph-muted); text-transform: uppercase; letter-spacing: .04em; }


/* The temporary clone that flies from a chip to the pizza when a topping
   is picked (see flyToPizza() in frontend.js) — fixed-positioned so it can
   travel across the whole sheet regardless of scroll position, removed
   from the DOM the moment it lands. */
.ph-pizza-fly {
	position: fixed; z-index: 99999; font-size: 22px; line-height: 1; pointer-events: none;
	transform: translate(-50%,-50%) scale(1.3); filter: drop-shadow(0 2px 4px rgba(0,0,0,.3));
	transition: left .45s cubic-bezier(.2,.8,.2,1), top .45s cubic-bezier(.2,.8,.2,1), transform .45s ease;
}

/* Round icon "chips" replacing the old plain list rows for extras — one
   per sauce/cheese/topping, laid out in a wrapping grid. */
.ph-extra-chip-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(82px, 1fr)); gap: 12px 8px; }
.ph-option-row.ph-extra-chip {
	flex-direction: column; align-items: center; text-align: center; gap: 6px;
	padding: 12px 6px 10px; margin-bottom: 0; cursor: pointer;
}
.ph-option-row.ph-extra-chip:active { transform: scale(.96); }
.ph-option-row.ph-extra-chip::before { content: none; } /* the avatar circle below is the selection indicator now */
.ph-extra-chip-avatar {
	position: relative; width: 50px; height: 50px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center; font-size: 24px;
	background: linear-gradient(160deg, #fff8ef, #f3e6d2); border: 2px solid #ecd9bd;
	transition: border-color .15s ease, background .15s ease, transform .15s var(--ph-ease-out);
}
.ph-extra-chip.selected .ph-extra-chip-avatar {
	border-color: var(--ph-orange); background: linear-gradient(160deg, #fff1e4, #ffe1c9);
	transform: scale(1.08); box-shadow: 0 4px 12px rgba(201,72,39,.2);
}
.ph-extra-chip-badge {
	position: absolute; top: -3px; right: -3px; min-width: 18px; height: 18px; padding: 0 4px;
	border-radius: 999px; background: var(--ph-orange); color: #fff; font-size: 10.5px; font-weight: 800;
	display: flex; align-items: center; justify-content: center; opacity: 0; transform: scale(.5);
	transition: opacity .15s ease, transform .15s ease;
}
.ph-extra-chip.selected .ph-extra-chip-badge { opacity: 1; transform: scale(1); }
.ph-extra-chip-name { font-size: 11.5px; font-weight: 700; line-height: 1.25; }
.ph-extra-chip .ph-extra-price { font-size: 11px; font-weight: 600; color: var(--ph-muted); min-width: 0; text-align: center; }
.ph-extra-chip .ph-qty-control-extra { margin-left: 0; gap: 5px; }
.ph-extra-chip .ph-qty-control-extra .ph-qty-btn { width: 22px; height: 22px; font-size: 13px; }
@media (max-width: 380px) {
	.ph-extra-chip-grid { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); }
}

/* ---------------- More animation/polish for the pizza builder (v227):
   a bounce when the pizza resizes, a pulse on the price whenever it
   changes, ambient "fresh from the oven" steam, a fade-out for removed
   toppings, a small hover wiggle on the chip icons, and a mini "🍕" that
   flies to the cart icon when the item is confirmed. All purely visual —
   none of this changes what actually gets added to the cart. */
@keyframes ph-pizza-bounce {
	0% { transform: scale(1); }
	35% { transform: scale(1.07); }
	60% { transform: scale(.97); }
	100% { transform: scale(1); }
}
.ph-pizza-preview-base.ph-pizza-bounce { animation: ph-pizza-bounce .38s var(--ph-ease-out); }

@keyframes ph-price-pulse {
	0% { transform: scale(1); color: var(--ph-text); }
	40% { transform: scale(1.14); color: var(--ph-orange); }
	100% { transform: scale(1); color: var(--ph-text); }
}
.ph-sheet-total span[data-sheet-total] { display: inline-block; }
.ph-sheet-total span[data-sheet-total].ph-price-pulse { animation: ph-price-pulse .35s ease; }

/* Steam — three soft wisps rising and fading above the pizza, looped,
   offset so they don't all move in lockstep. */
.ph-pizza-preview-steam { position: relative; width: 60px; height: 22px; margin-bottom: -6px; }
.ph-pizza-preview-steam span {
	position: absolute; bottom: 0; width: 7px; height: 16px; border-radius: 50%;
	background: rgba(255,255,255,.65); filter: blur(2px);
	animation: ph-steam-rise 2.6s ease-in-out infinite;
}
.ph-pizza-preview-steam span:nth-child(1) { left: 12px; animation-delay: 0s; }
.ph-pizza-preview-steam span:nth-child(2) { left: 27px; animation-delay: .7s; }
.ph-pizza-preview-steam span:nth-child(3) { left: 42px; animation-delay: 1.4s; }
@keyframes ph-steam-rise {
	0% { transform: translateY(4px) scale(.7); opacity: 0; }
	30% { opacity: .8; }
	100% { transform: translateY(-20px) scale(1.15); opacity: 0; }
}

/* Removed topping fades + shrinks out of the pizza instead of just
   disappearing (see shrinkFromPizza() in frontend.js, which adds this
   class and waits briefly before actually re-rendering). */
.ph-pizza-topping.leaving { transition: transform .2s ease, opacity .2s ease; opacity: 0 !important; transform: translate(-50%,-50%) scale(0) !important; }

/* The moving contact shadow beneath a flying topping (flyToPizza in
   frontend.js positions/sizes this every frame) and the brief squash it
   does on landing, plus the "thump" ripple left where it lands. */
.ph-pizza-fly-shadow {
	position: fixed; z-index: 99998; width: 22px; height: 22px; border-radius: 50%;
	background: radial-gradient(ellipse at center, rgba(43,27,23,.55), transparent 72%);
	pointer-events: none;
}
@keyframes ph-fly-land { 0% { transform: translate(-50%,-50%) scale(1); } 45% { transform: translate(-50%,-50%) scale(.68) rotate(0deg); } 100% { transform: translate(-50%,-50%) scale(.95); } }
.ph-pizza-fly.ph-pizza-fly-land { animation: ph-fly-land .18s ease-out; }
.ph-pizza-impact {
	position: absolute; width: 20px; height: 20px; margin: -10px 0 0 -10px; border-radius: 50%;
	background: radial-gradient(circle, rgba(255,255,255,.9), transparent 70%);
	pointer-events: none; animation: ph-impact-pulse .42s ease-out forwards;
}
@keyframes ph-impact-pulse { 0% { transform: scale(.2); opacity: .95; } 100% { transform: scale(2.4); opacity: 0; } }

/* A small idle wiggle on hover — cursor-only, so it never fires from a
   tap on touch devices. */
@keyframes ph-chip-wiggle {
	0%, 100% { transform: rotate(0deg); }
	25% { transform: rotate(-8deg); }
	75% { transform: rotate(8deg); }
}
@media (hover: hover) and (pointer: fine) {
	.ph-extra-chip:not(.selected):hover .ph-extra-chip-avatar span:first-child { display: inline-block; animation: ph-chip-wiggle .4s ease; }
}

/* The mini pizza that flies from the sheet to the cart icon on confirm —
   same fixed-position flying-clone technique as flyToPizza(), just a
   longer, spinning trip. */
.ph-pizza-fly-cart { font-size: 26px; transition: left .55s cubic-bezier(.3,.7,.4,1), top .55s cubic-bezier(.3,.7,.4,1), transform .55s ease, opacity .3s ease .25s; }

/* ---------------- Mini pizza thumbnail in the cart (v230) — the same
   crust/cheese/topping look as the sheet's live preview, shrunk down and
   static (no tilt/steam/animation, just the finished look) so a cart line
   keeps showing the actual pizza the customer built instead of reverting
   to plain text or a generic photo. */
.ph-cart-item-img-pizza { background: #f1e7da; display: flex; align-items: center; justify-content: center; }
.ph-mini-pizza { width: 52px; height: 52px; }
.ph-mini-pizza-base {
	position: relative; width: 100%; height: 100%; border-radius: 50%;
	background:
		radial-gradient(circle at 35% 25%, rgba(255,255,255,.6), transparent 42%),
		radial-gradient(circle, #f6d78a 0%, #f6d78a 63%, #e2ad4e 64%, #e2ad4e 78%, #d99a3d 79%, #d99a3d 100%);
	box-shadow: 0 2px 5px rgba(43,27,23,.25), inset 0 0 0 3px rgba(255,255,255,.35);
	overflow: hidden;
}
.ph-mini-pizza-base::before {
	content: ''; position: absolute; inset: 12%; border-radius: 50%; background: #e0642f;
	box-shadow: inset 0 -2px 4px rgba(43,27,23,.3);
}
.ph-mini-pizza-toppings { position: absolute; inset: 0; }
.ph-mini-pizza-toppings span { position: absolute; font-size: 9px; line-height: 1; filter: drop-shadow(0 1px 1px rgba(0,0,0,.35)); }

/* The finished-pizza "ta-da" moment right before it flies to the cart
   (see the data-confirm-add handler in openItemSheet). */
@keyframes ph-pizza-reveal-glow {
	0% { transform: scale(1); box-shadow: 0 14px 22px rgba(43,27,23,.28), 0 0 0 0 rgba(212,171,83,0); }
	40% { transform: scale(1.1); box-shadow: 0 14px 22px rgba(43,27,23,.28), 0 0 0 14px rgba(212,171,83,.35); }
	100% { transform: scale(1); box-shadow: 0 14px 22px rgba(43,27,23,.28), 0 0 0 0 rgba(212,171,83,0); }
}
.ph-pizza-preview-base.ph-pizza-reveal { animation: ph-pizza-reveal-glow .55s var(--ph-ease-out); }
.ph-pizza-preview-caption { transition: color .2s ease; }

/* ---------------- Real 3D pizza (Three.js, lazy-loaded — v231). Sits in
   the same spot the 2D CSS pizza occupies, hidden until the library
   finishes loading and a scene is actually created (see the
   loadThreeJS()/createPizza3D() wiring in openItemSheet); the 2D version
   underneath is left fully intact as the permanent fallback. */
.ph-pizza-3d { display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 4px 0 22px; }
.ph-pizza-3d-canvas {
	width: 100%; max-width: 260px; height: 190px; border-radius: 16px;
	background: linear-gradient(160deg, #fdf3e2, #f3e6d2);
	box-shadow: inset 0 0 0 1px rgba(43,27,23,.08);
	touch-action: none; /* the orbit-drag lives on this canvas, not page scroll */
	overflow: hidden;
}
.ph-pizza-3d-canvas canvas { display: block; width: 100% !important; height: 100% !important; cursor: grab; }
.ph-pizza-3d-canvas canvas:active { cursor: grabbing; }
.ph-pizza-3d-hint { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--ph-muted); font-size: 11px; }

/* Keeps the pizza preview (2D or 3D, whichever is active) pinned to the
   top of the sheet while the toppings list underneath scrolls normally —
   the sheet itself (.ph-sheet) is the scrolling container, so "top: 0"
   here means "the top of the sheet's own scroll area," right where its
   top padding ends. The shadow only becomes visible once something has
   actually scrolled under it, as a subtle "this is floating above
   content" cue rather than a permanent hard line. */
.ph-pizza-sticky-wrap {
	position: sticky; top: 0; z-index: 5; background: #fff;
	padding-bottom: 8px; box-shadow: 0 10px 12px -10px rgba(43,27,23,.18);
}

/* ---------------- Verify Certificate page — 2-colour background (v248)
   Per direct feedback that this page showed too many background colours.
   Before: a charcoal->darker-charcoal GRADIENT header, a gold-tinted pill
   and gold-tinted badge circle inside it, then a cream->beige->cream
   GRADIENT body band that only covered the centered container, so the
   theme's own page colour showed as different-coloured strips down both
   sides and around the footer. Now exactly two backgrounds: one solid
   dark header, and one flat warm-cream colour for the rest of the page,
   edge to edge. Content cards stay white on top of it.
   Everything is scoped under .ph-verify-cert-page, so the Course page —
   which reuses the same .ph-course-hero header classes — is untouched. */
body:has(.ph-verify-cert-page),
body:has(.ph-verify-cert-page) #page-container,
body:has(.ph-verify-cert-page) #main-content,
body:has(.ph-verify-cert-page) .ph-root.ph-app { background: var(--ph-cream) !important; }

.ph-verify-cert-page { background: var(--ph-cream); }

/* Colour 1: solid header, no gradient */
.ph-verify-cert-page .ph-course-hero { background: var(--ph-charcoal) !important; background-image: none !important; }
.ph-verify-cert-page .ph-course-hero-eyebrow { background: transparent; border-color: rgba(255,255,255,.28); color: var(--ph-gold); }
.ph-verify-cert-page .ph-verify-cert-badge { background: transparent; border: 2px solid rgba(255,255,255,.18); box-shadow: none; }

/* Colour 2: flat cream body, full width (no gradient band) */
.ph-verify-cert-page .ph-container.ph-verify-cert-body { background: transparent !important; }

/* Cards on the cream: plain white, soft neutral shadow, one accent colour */
.ph-verify-cert-page .ph-verify-cert-step,
.ph-verify-cert-page .ph-verify-cert-card { background: #fff; border: 1px solid #eee6da; box-shadow: 0 4px 14px rgba(43,27,23,.06); }
.ph-verify-cert-page .ph-verify-cert-step-num { background: var(--ph-orange); }
.ph-verify-cert-page .ph-cert-card { border-color: #eee6da; box-shadow: 0 4px 14px rgba(43,27,23,.06); }
.ph-verify-cert-page .ph-cert-card-photo { border-color: #eee6da; }

/* ---------------- Our Speciality cards with photos (v249) — Course page.
   Each card opens with a photo (4:3, full card width); the card's old
   icon sits as a small round white chip in the photo's bottom-left
   corner, same position on all three cards. Card 3 is the real
   certificate — a document — so it's shown whole ("contain") on a cream
   mat instead of being cropped like the two kitchen photos. */
.ph-speciality-photo { position: relative; aspect-ratio: 4 / 3; overflow: hidden; margin-bottom: 18px; background: var(--ph-beige); }
.ph-speciality-photo img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 30%; transition: transform .5s var(--ph-ease-out); }
.ph-speciality-photo--document { background: var(--ph-cream); padding: 14px 14px 20px; box-sizing: border-box; }
.ph-speciality-photo--document img { object-fit: contain; object-position: center; filter: drop-shadow(0 4px 10px rgba(43,27,23,.14)); }
.ph-speciality-photo-chip {
	position: absolute; left: 14px; bottom: 14px; width: 46px; height: 46px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center; font-size: 21px; line-height: 1;
	background: #fff; box-shadow: 0 6px 16px rgba(43,27,23,.22); border: 2px solid var(--ph-orange);
}
.ph-speciality-card--photo .ph-speciality-card-title { margin-top: 0; }
@media (hover: hover) and (pointer: fine) {
	.ph-speciality-card--photo:hover .ph-speciality-photo:not(.ph-speciality-photo--document) img { transform: scale(1.05); }
}

/* ---------------- Course page "See It For Yourself" videos (v250) —
   video only (no name/quote caption) inside a framed border: a warm
   orange->gold gradient ring, a thin white inner gap, rounded corners
   and a soft shadow, so each video reads as a deliberate "phone screen"
   tile rather than a bare black box. The ring is the card's own
   background showing through 5px of padding; the white gap is a border
   on the inner .ph-spotlight-frame. */
.ph-spotlight-card--framed {
	background: linear-gradient(145deg, var(--ph-orange), var(--ph-gold)) !important;
	padding: 5px; border-radius: 26px; overflow: visible;
	box-shadow: 0 14px 32px rgba(43,27,23,.18);
	transition: transform .2s var(--ph-ease-out), box-shadow .2s var(--ph-ease-out);
}
.ph-spotlight-frame {
	border-radius: 21px; overflow: hidden; border: 4px solid #fff; background: #000;
}
.ph-spotlight-card--framed video { border-radius: 0; aspect-ratio: 9 / 16; max-height: 460px; }
@media (hover: hover) and (pointer: fine) {
	.ph-spotlight-card--framed:hover { transform: translateY(-5px); box-shadow: 0 20px 40px rgba(201,72,39,.25); }
}
/* Phones: keep the videos upright (they're portrait clips) instead of the
   old 16:9 crop, centered at a comfortable width. */
@media (max-width: 860px) {
	.ph-spotlights-grid { grid-template-columns: minmax(0, 300px); }
	.ph-spotlight-card--framed video { aspect-ratio: 9 / 16; max-height: 480px; }
}

/* ---------------- Course plan names (v254) — "Basic / Standard / Premium"
   is the card's big title; the course name ("Professional Pizza Course")
   sits under it as a smaller line. */
.ph-course-card-name.ph-course-card-plan { font-size: 28px; margin: 0 0 2px; letter-spacing: .01em; }
.ph-course-card-course { margin: 0 0 14px; font-size: 14px; font-weight: 600; color: var(--ph-muted); }

/* =====================================================================
   v265 — FONTS: 90% Plus Jakarta Sans, 10% our brand.
   Pizza Hut's site uses Sharp Sans Display (a paid, licensed font — not
   copied). Plus Jakarta Sans is a free Google Font with the same bold,
   round, geometric feel, used for everything: body, menu, food names,
   prices, buttons, checkout, section titles. Our BRAND keeps Baloo 2 only
   where it's the identity: the restaurant name (header/footer), the
   homepage hero headline and the offer page's headline/brand line.
   Kept in sync with the Google Fonts link and print_late_overrides() in
   includes/class-ph-frontend-assets.php.
   Bebas Neue (tall, condensed) was replaced — Jakarta is wider, so those
   spots get slightly smaller sizes and tighter spacing to fit the same room.
   ===================================================================== */
#ph-app-root .ph-heading.ph-bento-headline, #ph-app-root .ph-heading.ph-offer-landing-title,
.ph-site-header-brand, .ph-footer-brand, .ph-offer-landing-brand { font-family: 'Baloo 2', cursive !important; font-weight: 800 !important; letter-spacing: 0; }
.ph-food-name { font-size: 17px; font-weight: 800; letter-spacing: 0; line-height: 1.25; }
.ph-sheet h3 { font-size: 19px; font-weight: 800; letter-spacing: 0; }
.ph-checkout-step h4 { font-size: 15.5px; font-weight: 800; letter-spacing: 0; }
.ph-course-card-price { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }
.ph-course-form-section-label { font-size: 14px; font-weight: 800; letter-spacing: 0; }
.ph-course-regid { font-size: 18px; font-weight: 800; letter-spacing: .02em; }
.ph-cert-card-name { font-size: 22px; font-weight: 800; letter-spacing: 0; }
.ph-dashboard-reg-card-name { font-size: 17px; font-weight: 800; letter-spacing: 0; }
/* Buttons, prices and form fields use the same family (inherit). */
.ph-root button, .ph-app button, .ph-root input, .ph-app input, .ph-root select, .ph-app select, .ph-root textarea, .ph-app textarea { font-family: inherit; }

/* =====================================================================
   v266 — mobile tab bar: standard pizza-site layout
   Home · Menu · [Cart, raised centre button] · Deals · Track.
   Idle tabs: outline icon in muted brown. Active tab: the icon FILLS in
   (.ph-nav-fill) in brand orange + a small dot under the label.
   Scroll: .is-hidden slides the bar down out of view while scrolling down
   (frontend.js mountBottomNavScroll()); the floating "View cart" bar
   follows it down so there's never an empty gap.
   ===================================================================== */
.ph-bottom-nav {
	transform: translateY(0); transition: transform .32s var(--ph-ease-out, ease), box-shadow .2s;
	align-items: flex-end; padding-top: 8px; overflow: visible;
}
.ph-bottom-nav.is-hidden { transform: translateY(calc(100% + 24px)); box-shadow: none; }
.ph-bottom-nav-item { font-size: 10.5px; font-weight: 700; gap: 3px; padding-bottom: 2px; }
.ph-bottom-nav-icon { width: 46px; height: 30px; border-radius: 12px; background: transparent !important; }
.ph-bottom-nav-icon svg { width: 24px; height: 24px; overflow: visible; }
.ph-bottom-nav .ph-nav-fill { fill: currentColor; stroke: none; opacity: 0; transition: opacity .18s ease; }
.ph-bottom-nav .ph-nav-dot { transition: fill .18s ease; }
.ph-bottom-nav-item.active { color: var(--ph-orange); }
.ph-bottom-nav-item.active .ph-bottom-nav-icon { transform: translateY(-1px); }
.ph-bottom-nav-item.active .ph-nav-fill { opacity: 1; }
.ph-bottom-nav-item.active .ph-nav-dot { fill: #fff; }          /* toppings / % dots show white on the filled shape */
.ph-bottom-nav-item.active .ph-nav-mark { stroke: #fff; }
.ph-bottom-nav-item.active .ph-bottom-nav-label::after {
	content: ''; display: block; width: 4px; height: 4px; border-radius: 50%; background: currentColor; margin: 2px auto 0;
}
.ph-bottom-nav-item:active .ph-bottom-nav-icon { transform: scale(.9); }

/* Raised centre Cart button (the most important tap on a pizza site). */
.ph-bottom-nav-item.ph-bottom-nav-cart { color: var(--ph-text); }
.ph-bottom-nav-cart .ph-bottom-nav-icon {
	width: 56px; height: 56px; margin-top: -26px; border-radius: 50%;
	background: linear-gradient(135deg, #E86632, #C94827) !important; color: #fff;
	box-shadow: 0 8px 18px rgba(201,72,39,.38), 0 0 0 5px rgba(255,255,255,.97);
}
.ph-bottom-nav-cart .ph-bottom-nav-icon svg { width: 26px; height: 26px; }
.ph-bottom-nav-cart:active .ph-bottom-nav-icon { transform: scale(.92); }
.ph-bottom-nav-cart .ph-bottom-nav-badge { top: -2px; right: -2px; background: #2B1B17; box-shadow: 0 0 0 2px #fff; }
/* A little bounce on the cart when an item lands in it (class set by the
   cart-badge update in frontend.js; harmless if never set). */
@keyframes ph-cart-pop { 0% { transform: scale(1); } 35% { transform: scale(1.18); } 100% { transform: scale(1); } }
.ph-bottom-nav-cart.ph-pop .ph-bottom-nav-icon { animation: ph-cart-pop .45s var(--ph-ease-out, ease); }

/* The floating "View cart" bar sits above the tab bar — when the tab bar
   slides away, it moves down with it instead of leaving a gap. */
@media (max-width: 782px) {
	.ph-sticky-cart-bar { transition: bottom .32s var(--ph-ease-out, ease), opacity .25s, transform .25s; }
	/* Sit clear of the raised centre Cart button (it pokes ~26px above the bar). */
	html:not(.ph-nav-hidden) .ph-sticky-cart-bar { bottom: calc(var(--ph-bottom-nav-h) + env(safe-area-inset-bottom) + 34px); }
	html.ph-nav-hidden .ph-sticky-cart-bar { bottom: calc(env(safe-area-inset-bottom) + 12px); }
}
@media (prefers-reduced-motion: reduce) {
	.ph-bottom-nav, .ph-sticky-cart-bar { transition: none; }
	.ph-bottom-nav-cart.ph-pop .ph-bottom-nav-icon { animation: none; }
}

/* =====================================================================
   v267 — "Choose any N pizzas" offer picker (offer landing page).
   Grid of pizza cards -> "Your pizzas" list with per-pizza toppings ->
   a bar with the fixed price + Add to Cart / Order Now that sticks to the
   bottom of the screen on phones so it's always one tap away.
   ===================================================================== */
.ph-offer-landing-was { margin-left: 10px; font-size: .55em; font-weight: 600; color: var(--ph-muted); }
.ph-offer-landing-includes { margin: 4px 0 14px; font-weight: 700; color: var(--ph-text); }
.ph-offer-picker { margin-top: 18px; text-align: left; }
.ph-offer-picker-loading, .ph-offer-picker-empty { color: var(--ph-muted); text-align: center; padding: 20px 0; }
.ph-op-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.ph-op-title { margin: 0; font-size: 18px; font-weight: 800; color: var(--ph-text); }
.ph-op-counter { padding: 5px 12px; border-radius: 999px; background: #f3ece2; font-size: 13px; font-weight: 800; color: var(--ph-text); white-space: nowrap; }
.ph-op-counter.is-done { background: #e3f4ea; color: #1e7a45; }
.ph-op-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.ph-op-card {
	display: flex; flex-direction: column; gap: 6px; padding: 8px 8px 10px; border: 2px solid transparent; border-radius: 16px;
	background: #fff; box-shadow: 0 2px 8px rgba(43,27,23,.07); cursor: pointer; text-align: left; font: inherit; color: var(--ph-text);
	transition: border-color .15s, transform .12s, opacity .15s;
}
.ph-op-card:active { transform: scale(.97); }
.ph-op-card.is-selected { border-color: var(--ph-orange); }
.ph-op-card.is-disabled { opacity: .45; cursor: not-allowed; }
.ph-op-img { display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 3; border-radius: 12px; background: #f1e3cf center / cover no-repeat; font-size: 32px; }
.ph-op-name { font-size: 14px; font-weight: 800; line-height: 1.25; }
.ph-op-add { font-size: 12.5px; font-weight: 700; color: var(--ph-orange); }
.ph-op-card.is-disabled .ph-op-add { color: var(--ph-muted); }
.ph-op-picks { margin-top: 18px; }
.ph-op-sub { margin: 0 0 8px; font-size: 15px; font-weight: 800; }
.ph-op-pick { background: #fff; border-radius: 14px; padding: 10px 12px; box-shadow: 0 1px 4px rgba(43,27,23,.06); margin-bottom: 8px; }
.ph-op-pick-row { display: flex; align-items: center; gap: 10px; }
.ph-op-pick-no { flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; background: var(--ph-text); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; }
.ph-op-pick-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.ph-op-pick-body strong { font-size: 14.5px; }
.ph-op-pick-body span { font-size: 12.5px; color: var(--ph-muted); }
.ph-op-link { border: 0; background: #f3ece2; color: var(--ph-text); border-radius: 999px; padding: 6px 12px; font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
.ph-op-remove { border: 0; background: transparent; color: var(--ph-muted); font-size: 16px; cursor: pointer; padding: 4px 6px; }
.ph-op-extras { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ph-op-extra { border: 1.5px solid #e6dccd; background: #fff; border-radius: 999px; padding: 6px 11px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; color: var(--ph-text); }
.ph-op-extra.is-on { border-color: var(--ph-orange); background: #fff1e8; color: var(--ph-orange-dark, #a83a1f); }
.ph-op-extra em { font-style: normal; color: var(--ph-muted); }
.ph-op-bar {
	display: flex; align-items: center; gap: 10px; margin-top: 18px; padding: 12px; border-radius: 16px;
	background: #fff; box-shadow: 0 6px 22px rgba(43,27,23,.12);
}
.ph-op-bar-price { flex: 1 1 auto; display: flex; flex-direction: column; line-height: 1.15; }
.ph-op-bar-price strong { font-size: 20px; font-weight: 800; }
.ph-op-bar-price s { font-size: 12.5px; color: var(--ph-muted); }
.ph-op-bar-price span { font-size: 12px; color: var(--ph-muted); }
.ph-op-bar .ph-btn { padding: 11px 14px; font-size: 14px; white-space: nowrap; }
.ph-op-btn-cart { background: #f3ece2; color: var(--ph-text); border: 0; }
.ph-op-bar .ph-btn:disabled { opacity: .45; cursor: not-allowed; }
@media (max-width: 782px) {
	/* Always reachable: stick to the bottom, above the mobile tab bar. */
	.ph-op-bar { position: sticky; bottom: calc(var(--ph-bottom-nav-h, 64px) + env(safe-area-inset-bottom) + 34px); z-index: 30; }
	html.ph-nav-hidden .ph-op-bar { bottom: calc(env(safe-area-inset-bottom) + 10px); }
	.ph-op-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.ph-op-bar .ph-btn { padding: 10px 12px; font-size: 13.5px; }
}

/* =========================================================
   v2.9.4 — MOBILE-FIRST POLISH PASS
   Premium phone/tablet rhythm, larger touch targets, cleaner cards,
   smoother horizontal browsing, safer sticky spacing and stronger hero
   hierarchy. Desktop remains unchanged.
   ========================================================= */
@media (max-width: 782px) {
	:root { --ph-mobile-gutter: 14px; }

	/* Global mobile rhythm */
	.ph-app { overflow-x: clip; padding-bottom: calc(var(--ph-bottom-nav-h, 64px) + env(safe-area-inset-bottom) + 18px); }
	.ph-container, .ph-container-wide {
		width: 100% !important;
		max-width: none !important;
		padding-left: var(--ph-mobile-gutter) !important;
		padding-right: var(--ph-mobile-gutter) !important;
	}
	.ph-heading { text-wrap: balance; }
	.ph-btn.ph-btn { min-height: 48px; padding: 12px 20px; font-size: 15px; }

	/* Header — compact, premium, thumb-friendly */
	.ph-site-header {
		background: rgba(250,248,244,.96);
		-webkit-backdrop-filter: blur(16px) saturate(150%);
		backdrop-filter: blur(16px) saturate(150%);
		box-shadow: 0 8px 28px rgba(43,27,23,.07);
	}
	.ph-site-header .ph-site-header-inner.ph-site-header-inner {
		min-height: 68px;
		padding: 6px 14px !important;
	}
	.ph-site-header-brand img { width: 58px; height: 58px; }
	.ph-site-header-burger {
		display: grid;
		place-items: center;
		width: 46px;
		height: 46px;
		padding: 0;
		border-radius: 15px;
		background: #fff;
		border: 1px solid rgba(43,27,23,.09);
		box-shadow: 0 7px 20px rgba(43,27,23,.08);
	}
	.ph-site-header-mobile-nav {
		padding: 8px 14px 16px;
		background: rgba(250,248,244,.98);
		box-shadow: 0 18px 36px rgba(43,27,23,.09);
	}
	.ph-site-header-mobile-nav a {
		min-height: 50px;
		display: flex;
		align-items: center;
		padding: 12px 10px;
		border-radius: 12px;
		border-top: 0;
	}
	.ph-site-header-mobile-nav a + a { border-top: 1px solid rgba(43,27,23,.07); }

	/* Hero — strong first-screen impact without crowding */
	.ph-app .ph-bento-hero.ph-bento-hero { gap: 12px; margin-top: 14px !important; }
	.ph-bento-tile { border-radius: 22px; }
	.ph-bento-headline-tile { padding: 30px 22px 26px; min-height: auto; }
	.ph-bento-headline { font-size: clamp(34px, 10vw, 46px); line-height: .98; margin-top: 18px; letter-spacing: -.025em; }
	.ph-bento-sub { margin-top: 16px; font-size: 15px; line-height: 1.65; max-width: none; }
	.ph-bento-headline-tile .ph-hero-ctas { margin-top: 22px; gap: 10px; }
	.ph-hero-ctas .ph-btn { flex: 1 1 145px; }
	.ph-bento-photo-tile { min-height: 235px; aspect-ratio: 16 / 10; }
	.ph-bento-stat-tile { padding: 20px 18px; min-height: 118px; }
	.ph-bento-stat-num { font-size: 29px; }
	.ph-bento-desc-tile { padding: 21px 18px; }

	/* Section spacing — consistent, breathable scroll rhythm */
	.ph-benefits-section,
	.ph-story-section,
	.ph-team-section,
	.ph-offers-section,
	.ph-offers-gallery-section,
	.ph-reviews-section,
	.ph-menu-section,
	.ph-course-section,
	.ph-speciality-section { scroll-margin-top: 82px; }
	.ph-benefits-section { margin-top: 28px !important; }
	.ph-story-section, .ph-team-section { gap: 22px; }

	/* Cards */
	.ph-benefit-card,
	.ph-story-card,
	.ph-team-card,
	.ph-review-card,
	.ph-food-card,
	.ph-speciality-card,
	.ph-offers-card { border-radius: 20px; box-shadow: 0 10px 28px rgba(43,27,23,.09); }
	.ph-course-card { border-radius: 20px; box-shadow: 0 7px 20px rgba(43,27,23,.07); border-color: #eee5dc; }
	.ph-food-card { overflow: hidden; }
	.ph-menu-grid { grid-template-columns: 1fr !important; gap: 16px; padding-left: 0; padding-right: 0; }
	.ph-menu-grid > .ph-food-card { max-width: none; }
	.ph-menu-section-title { margin-top: 28px; margin-bottom: 13px; font-size: 21px; }

	/* Search + category chips feel native */
	.ph-category-nav {
		top: 68px;
		margin-left: calc(var(--ph-mobile-gutter) * -1);
		margin-right: calc(var(--ph-mobile-gutter) * -1);
		padding: 11px var(--ph-mobile-gutter);
		gap: 8px;
		scroll-snap-type: x proximity;
		-webkit-overflow-scrolling: touch;
	}
	.ph-category-chip { scroll-snap-align: start; min-height: 42px; white-space: nowrap; }
	.ph-search-input { min-height: 48px; border-radius: 14px; }

	/* Carousels — one confident card at a time on phones */
	.ph-offers-viewport, .ph-reviews-viewport { overflow: hidden; }
	.ph-offers-track, .ph-reviews-track { will-change: transform; }
	.ph-offers-gallery-card, .ph-review-card { border-radius: 20px; }
	.ph-offers-arrow, .ph-reviews-arrow { width: 42px; height: 42px; }

	/* Forms / sheets */
	.ph-sheet { border-radius: 24px 24px 0 0; max-height: 92dvh; }
	.ph-field { margin-bottom: 14px; }
	.ph-field input, .ph-field textarea, .ph-field select, .ph-track-form input {
		min-height: 48px;
		border-radius: 13px;
	}
	.ph-field textarea { min-height: 104px; }
	.ph-row-2 { gap: 12px; }

	/* Course page */
	.ph-course-hero { padding: 42px 0 38px; }
	.ph-course-hero-row { gap: 22px; }
	.ph-course-hero-title { font-size: clamp(31px, 9vw, 42px); line-height: 1.05; }
	.ph-course-hero-sub { font-size: 15px; line-height: 1.65; }
	.ph-course-hero-photo { width: min(72vw, 230px); border-radius: 20px; }

	/* Footer — cleaner stack and more comfortable tap targets */
	.ph-footer { margin-top: 36px; border-radius: 26px 26px 0 0; }
	.ph-footer-cta { padding: 26px 18px; gap: 16px; }
	.ph-footer-cta-btn { width: 100%; }
	.ph-footer-cta-btn .ph-btn, .ph-footer-cta > .ph-btn { width: 100%; }
	.ph-footer .ph-footer-grid.ph-footer-grid-4 { padding: 30px 18px 26px !important; gap: 26px !important; }
	.ph-footer-brand { align-items: center; }
	.ph-footer-logo { width: 66px; height: 66px; }
	.ph-footer-links a, .ph-footer-contact a { display: inline-flex; min-height: 40px; align-items: center; }
	.ph-footer-socials a { width: 44px; height: 44px; }
	.ph-footer-bottom { padding: 18px 16px calc(18px + env(safe-area-inset-bottom)); }

	/* Bottom navigation — slightly more premium / tactile */
	:root { --ph-bottom-nav-h: 70px; }
	.ph-bottom-nav {
		padding: 7px 5px calc(7px + env(safe-area-inset-bottom));
		background: rgba(255,255,255,.94);
		-webkit-backdrop-filter: blur(18px) saturate(180%);
		backdrop-filter: blur(18px) saturate(180%);
		box-shadow: 0 -10px 30px rgba(43,27,23,.12);
	}
	.ph-bottom-nav-item { min-height: 52px; }
	.ph-bottom-nav-icon { width: 46px; height: 31px; }
}

@media (max-width: 420px) {
	:root { --ph-mobile-gutter: 12px; }
	.ph-bento-headline-tile { padding: 26px 18px 22px; }
	.ph-bento-headline { font-size: clamp(32px, 10.5vw, 42px); }
	.ph-hero-ctas { flex-direction: column; }
	.ph-hero-ctas .ph-btn { width: 100%; flex-basis: auto; }
	.ph-bento-photo-tile { min-height: 210px; }
	.ph-bento-stat-tile { grid-column: span 12; min-height: 100px; }
	.ph-footer-logo { width: 60px; height: 60px; }
}

/* v2.9.5 — Contact form + built-in arithmetic CAPTCHA. */
.ph-contact-form-wrap {
	max-width: 820px;
	margin: 0 auto 38px;
	padding: 30px;
	background: #fff;
	border: 1px solid rgba(43,27,23,.08);
	border-radius: 24px;
	box-shadow: 0 16px 40px rgba(43,27,23,.09);
}
.ph-contact-form-copy { text-align: center; margin-bottom: 22px; }
.ph-contact-form-kicker { display: inline-block; font-size: 13px; font-weight: 800; color: var(--ph-orange); text-transform: uppercase; letter-spacing: .08em; margin-bottom: 6px; }
.ph-contact-form-copy h2 { margin: 0 0 7px; font-size: clamp(24px,3vw,34px); color: var(--ph-text); }
.ph-contact-form-copy p { margin: 0; color: var(--ph-muted); }
.ph-contact-form-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 16px; }
.ph-contact-form label { display: flex; flex-direction: column; gap: 7px; color: var(--ph-text); font-size: 13px; font-weight: 700; }
.ph-contact-form-wide { grid-column: 1 / -1; }
.ph-contact-form input,
.ph-contact-form textarea {
	width: 100%;
	border: 1px solid rgba(43,27,23,.16);
	border-radius: 14px;
	background: #fffdfa;
	color: var(--ph-text);
	font: inherit;
	font-weight: 500;
	font-size: 15px;
	padding: 13px 14px;
	outline: none;
	transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
}
.ph-contact-form textarea { min-height: 130px; resize: vertical; line-height: 1.55; }
.ph-contact-form input:focus,
.ph-contact-form textarea:focus { border-color: var(--ph-orange); box-shadow: 0 0 0 4px rgba(201,72,39,.10); background: #fff; }
.ph-contact-captcha { padding: 15px; border-radius: 16px; background: var(--ph-beige); border: 1px dashed rgba(201,72,39,.35); }
.ph-contact-captcha input { max-width: 220px; background: #fff; }
.ph-contact-submit { margin-top: 18px; min-width: 180px; justify-content: center; }
.ph-contact-alert { margin: 0 0 18px; padding: 13px 15px; border-radius: 12px; font-size: 14px; font-weight: 650; }
.ph-contact-alert-success { background: #e8f7ee; color: #17683a; border: 1px solid #b9e5c9; }
.ph-contact-alert-error { background: #fff0ed; color: #9d2f1e; border: 1px solid #f3c1b9; }
.ph-contact-hp { position: absolute !important; left: -10000px !important; width: 1px !important; height: 1px !important; overflow: hidden !important; }
@media (max-width: 640px) {
	.ph-contact-form-wrap { padding: 20px 16px; border-radius: 20px; margin-bottom: 28px; }
	.ph-contact-form-grid { grid-template-columns: 1fr; gap: 13px; }
	.ph-contact-form-wide { grid-column: auto; }
	.ph-contact-form input, .ph-contact-form textarea { font-size: 16px; min-height: 48px; }
	.ph-contact-captcha input { max-width: none; }
	.ph-contact-submit { width: 100%; min-height: 50px; }
}

/* v2.9.6 — Course page Certificate On Completion demo artwork.
   The new demo is a wide chef + certificate composition, so preserve the
   full artwork instead of forcing it into the older 4:3 document crop. */
.ph-speciality-photo--document { aspect-ratio: 3 / 2; padding: 0; background: #fff; }
.ph-speciality-photo--document img { object-fit: cover; object-position: center; filter: none; }
@media (max-width: 767px) {
  .ph-speciality-photo--document { aspect-ratio: 3 / 2; }
}


/* v2.9.9 — approved Moin Monju certificate demo crop. */
.ph-speciality-photo--document img { object-position: center center; }
/* v3.0.4 ad landing conversion details */
.ph-offer-facts{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:18px 0 12px}.ph-offer-facts span{padding:10px 8px;border-radius:12px;background:#fff7f1;border:1px solid #f1dfd2;font-size:12px;font-weight:700;color:#51362b}.ph-offer-how{text-align:left;background:#f8f5f1;border-radius:14px;padding:14px 16px;margin:10px 0 18px;border-left:4px solid var(--ph-orange)}.ph-offer-how strong{display:block;margin-bottom:4px}.ph-offer-how p{margin:0;color:var(--ph-muted);font-size:13px;line-height:1.55}@media(max-width:560px){.ph-offer-facts{grid-template-columns:1fr}.ph-offer-landing-cta{position:sticky;bottom:74px;z-index:5;box-shadow:0 10px 26px rgba(0,0,0,.2)}}

/* v3.0.6: offer image belongs INSIDE the white offer card. */
.ph-offer-landing .ph-offer-landing-card {max-width:640px;margin:0 auto;padding:clamp(18px,3vw,34px);overflow:hidden;}
.ph-offer-landing .ph-offer-landing-image {width:100%;aspect-ratio:16/9;max-height:360px;margin:0 0 24px;border-radius:18px;box-shadow:none;}
.ph-offer-landing .ph-offer-landing-image img {object-fit:cover;}
.ph-offer-landing .ph-container.ph-offer-landing-body {padding-top:30px;}
@media(max-width:600px){.ph-offer-landing .ph-offer-landing-card{padding:16px;border-radius:20px}.ph-offer-landing .ph-offer-landing-image{aspect-ratio:4/3;margin-bottom:20px;border-radius:14px;}}

/* v5.0.2: Compact mobile BOTTOM navigation (not the header hamburger).
   Five equally sized tap targets, restrained icons and labels, safe-area aware. */
@media (max-width: 782px) {
  :root { --ph-bottom-nav-h: 54px; }
  .ph-bottom-nav.enabled {
    display: flex;
    min-height: 54px;
    padding: 3px 6px calc(3px + env(safe-area-inset-bottom));
    align-items: stretch;
    gap: 2px;
    background: rgba(255,255,255,.97);
    border-top: 1px solid rgba(43,27,23,.10);
    box-shadow: 0 -3px 14px rgba(43,27,23,.07);
  }
  .ph-bottom-nav .ph-bottom-nav-item {
    flex: 1 1 0;
    min-height: 46px;
    max-width: none;
    gap: 1px;
    padding: 2px 0;
    font-size: 9px;
    font-weight: 650;
    line-height: 1.1;
  }
  .ph-bottom-nav .ph-bottom-nav-icon,
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-icon {
    width: 34px;
    height: 25px;
    border-radius: 9px;
    background: transparent;
  }
  .ph-bottom-nav .ph-bottom-nav-icon svg,
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-icon svg {
    width: 20px;
    height: 20px;
  }
  .ph-bottom-nav .ph-bottom-nav-label { font-size: 9px; line-height: 1.2; }
  .ph-bottom-nav .ph-bottom-nav-badge { top: -3px; right: -5px; }
  .ph-bottom-nav-item.active .ph-bottom-nav-icon { transform: none; }
  .ph-bottom-nav-item.active .ph-bottom-nav-label::after { bottom: -3px; }
}
@media (max-width: 360px) {
  .ph-bottom-nav.enabled { padding-left: 2px; padding-right: 2px; }
  .ph-bottom-nav .ph-bottom-nav-label { font-size: 8.5px; }
}

/* v5.0.3: Always-visible compact mobile bottom navigation. */
@media (max-width: 782px) {
  .ph-bottom-nav.enabled,
  .ph-bottom-nav.enabled.is-hidden {
    position: fixed !important;
    inset: auto 0 0 0 !important;
    transform: none !important;
    translate: none !important;
    visibility: visible !important;
    opacity: 1 !important;
    z-index: 9998 !important;
    transition: none !important;
  }
  .ph-app { padding-bottom: calc(70px + env(safe-area-inset-bottom)) !important; }
  .ph-sticky-cart-bar { bottom: calc(62px + env(safe-area-inset-bottom)) !important; }
}

/* v5.0.6: Cart icon clarity and true centre alignment in compact bottom navigation.
   Override the older raised 56px cart button and the 5.0.2 transparency rule. */
@media (max-width: 782px) {
  .ph-bottom-nav.enabled { justify-content: space-between; }
  .ph-bottom-nav .ph-bottom-nav-item.ph-bottom-nav-cart {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-width: 0; padding: 2px 0; margin: 0; transform: none;
  }
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-icon {
    display: flex; align-items: center; justify-content: center;
    box-sizing: border-box; width: 34px; height: 25px; min-width: 34px;
    margin: 0 auto; padding: 0; border-radius: 9px;
    background: transparent !important; color: #c94827; box-shadow: none;
    transform: none; line-height: 0;
  }
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-icon svg {
    display: block; width: 23px; height: 23px; min-width: 23px;
    margin: 0; padding: 0; fill: none; stroke: currentColor;
    stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round;
    overflow: visible; flex: none;
  }
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-label {
    width: 100%; text-align: center; line-height: 1.2; margin: 0;
  }
  .ph-bottom-nav .ph-bottom-nav-cart .ph-bottom-nav-badge {
    top: -5px; right: -7px; z-index: 2;
    box-shadow: 0 0 0 2px #fff;
  }
  .ph-bottom-nav .ph-bottom-nav-cart:active .ph-bottom-nav-icon { transform: scale(.94); }
}

/* v5.1.4: resilient food photos, including cart and the item chooser. */
.ph-food-card-img:has(> img[data-ph-food-image])::after { content: none !important; }
.ph-food-card-img > img[data-ph-food-image] { display: block !important; opacity: 1 !important; visibility: visible !important; }
.ph-food-card-img:has(> img[data-ph-demo-active='1'])::before { content: 'Demo image'; position: absolute; left: 9px; bottom: 9px; z-index: 2; padding: 3px 7px; border-radius: 4px; background: rgba(0,0,0,.64); color: #fff; font: 11px/1.4 Arial, sans-serif; pointer-events: none; }
.ph-cart-item-img > img[data-ph-food-image], .ph-op-img > img[data-ph-food-image] { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.ph-item-photo { height: 170px; margin: 0 42px 16px 0; border-radius: 10px; overflow: hidden; }
.ph-item-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 480px) { .ph-item-photo { height: 140px; } }

/* v3.5.4: curated food photos do not display a demo badge. */
.ph-food-card-img:has(> img[data-ph-demo-active="1"])::before { content: none !important; display: none !important; }

/* v3.6.9: balanced footer navigation and contact columns (desktop/tablet/mobile). */
@media (min-width: 901px) {
  .ph-footer .ph-footer-grid.ph-footer-grid-4 {
    grid-template-columns: minmax(0, 1.22fr) repeat(3, minmax(0, 1fr)) !important;
    column-gap: clamp(24px, 2.4vw, 44px) !important;
    padding-inline: clamp(28px, 3vw, 54px) !important;
  }
  .ph-footer-grid-4 > .ph-footer-col { min-width: 0; }
  .ph-footer-grid-4 > .ph-footer-col:not(.ph-footer-col-brand) {
    display: flex; flex-direction: column;
  }
}
.ph-footer-grid-4 .ph-footer-col h3 { min-height: 22px; margin-bottom: 20px; }
.ph-footer-grid-4 .ph-footer-links li { margin: 0 0 15px; line-height: 1.55; }
.ph-footer-grid-4 .ph-footer-contact p { margin: 0 0 15px; line-height: 1.55; overflow-wrap: anywhere; }
.ph-footer-grid-4 .ph-footer-contact p:nth-of-type(1),
.ph-footer-grid-4 .ph-footer-contact p:nth-of-type(2) { white-space: normal; font-size: 15px; }
@media (min-width: 901px) and (max-width: 1200px) {
  .ph-footer .ph-footer-grid.ph-footer-grid-4 { grid-template-columns: minmax(0, 1.05fr) repeat(3, minmax(0, 1fr)) !important; column-gap: 20px !important; }
  .ph-footer-grid-4 .ph-footer-links a, .ph-footer-grid-4 .ph-footer-contact p { font-size: 14px; }
}

/* v3.6.12 — consistent 96%-wide content across PizzaHub public sections.
   Keep section backgrounds full-bleed, and preserve deliberately narrow
   forms, modal windows, certificate print pages, and mobile gutters. */
@media (min-width: 783px) {
  .ph-app .ph-container,
  .ph-app .ph-container-wide,
  .ph-app.ph-layout-full .ph-container,
  .ph-app .ph-site-header-inner,
  .ph-app .ph-footer-cta,
  .ph-app .ph-footer-grid,
  .ph-app .ph-footer-bottom,
  .ph-app .ph-reels-grid {
    box-sizing: border-box;
    width: 96% !important;
    max-width: none !important;
    margin-left: auto !important;
    margin-right: auto !important;
  }
}

/* v3.6.28: 90% desktop width; certificate print styles untouched. */
@media (min-width: 783px) {
  .ph-app .ph-container,
  .ph-app .ph-container-wide,
  .ph-app.ph-layout-full .ph-container,
  .ph-app .ph-site-header-inner,
  .ph-app .ph-footer-cta,
  .ph-app .ph-footer-grid,
  .ph-app .ph-footer-bottom,
  .ph-app .ph-reels-grid { width: 90% !important; }
}

/* v3.6.31: narrow-screen ordering and tap-target refinements; scoped to storefront. */
@media (max-width: 767px) {
  .ph-menu-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .ph-menu-grid > * { min-width: 0; }
  .ph-food-card { min-width: 0; overflow: hidden; }
  .ph-food-card img { max-width: 100%; }
  .ph-food-name { overflow-wrap: anywhere; }
  .ph-search-input { font-size: 16px; }
  .ph-food-card button, .ph-food-card a { touch-action: manipulation; }
}
@media (max-width: 380px) {
  .ph-menu-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  .ph-food-card, .ph-food-card *, .ph-menu-grid * { scroll-behavior: auto !important; }
}

/* v3.6.32: guided, single-column manual delivery selection on phones/tablets. */
.ph-ward-area-hint { margin: 8px 0 14px; padding: 10px 12px; background: #fff8eb; border-left: 3px solid #c48b31; border-radius: 5px; color: #60461f; font-size: 13px; line-height: 1.5; }
@media (max-width: 900px) {
 .ph-checkout-wrap .ph-ward-area-fields { grid-template-columns: minmax(0,1fr); gap: 10px; }
 .ph-checkout-wrap .ph-ward-area-fields select { width: 100%; max-width: 100%; min-height: 46px; font-size: 16px; }
 .ph-checkout-wrap .ph-checkout-step { min-width: 0; }
}


/* Certificate verification student-photo zoom viewer (v4.1.8). */
.ph-cert-photo-open{display:block;border:0;padding:0;margin:0 auto 14px;background:transparent;border-radius:50%;cursor:zoom-in;line-height:0}
.ph-cert-photo-open:focus-visible{outline:3px solid #1e8e4f;outline-offset:4px}
.ph-cert-photo-open .ph-cert-card-photo{margin:0}
.ph-cert-photo-viewer{display:none;position:fixed;inset:0;z-index:999999;background:rgba(13,20,17,.88);align-items:center;justify-content:center;padding:20px;box-sizing:border-box}
.ph-cert-photo-viewer.is-open{display:flex}
.ph-cert-photo-viewer-panel{position:relative;display:flex;flex-direction:column;align-items:center;gap:14px;width:min(900px,100%);max-height:95vh;background:#fff;border-radius:16px;padding:54px 18px 18px;box-sizing:border-box;overflow:hidden}
.ph-cert-photo-close{position:absolute;right:12px;top:8px;width:42px;height:42px;border:0;border-radius:50%;background:#f2f4f3;color:#18352a;font-size:30px;line-height:1;cursor:pointer}
.ph-cert-photo-stage{display:flex;align-items:center;justify-content:center;width:100%;height:min(65vh,650px);overflow:auto;touch-action:pan-x pan-y pinch-zoom;background:#f5f7f6;border-radius:10px}
.ph-cert-photo-large{display:block;max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;transform-origin:center;transition:transform .12s ease;}
.ph-cert-photo-controls{display:flex;gap:10px;align-items:center;justify-content:center}
.ph-cert-photo-controls button{min-width:44px;min-height:42px;padding:8px 14px;border:1px solid #d7e0da;border-radius:10px;background:#f7faf8;color:#173d2d;font-size:18px;font-weight:700;cursor:pointer}
.ph-cert-photo-hint{margin:0;color:#65736b;font-size:12px;text-align:center}
body.ph-cert-viewer-open{overflow:hidden}
@media(max-width:600px){.ph-cert-photo-viewer{padding:10px}.ph-cert-photo-viewer-panel{padding:50px 10px 14px}.ph-cert-photo-stage{height:60vh}}
