/*
  MyPetsFiles -- CMS rendering + Website Builder editor styles
  (Version 24, Website Builder Phase 2: Homepage Visual Builder).

  Two independent concerns share this one file:

  1. PUBLIC CMS RENDERING (.mpf-cms-*) -- classes emitted by
     templates/design_system/_cms_components.html when rendering a
     CMS-enabled page (or an admin preview). Every class here maps to
     one of website_builder_service.py's controlled/validated tokens
     (ALLOWED_BACKGROUND_COLORS, ALLOWED_GRADIENTS, ALLOWED_ALIGNMENTS,
     ALLOWED_WIDTHS, ALLOWED_SPACING, ALLOWED_BORDER_RADIUS,
     ALLOWED_ANIMATIONS, ALLOWED_TYPOGRAPHY_SIZES) -- an admin never
     writes CSS, only picks a token from a <select>, and that token
     resolves to exactly one class below.
  2. ADMIN EDITOR (.mpf-wbe-*) -- the three-pane Homepage Visual
     Builder shell (left: section list, center: preview iframe, right:
     property panel), loaded only on
     /admin/website/builder/pages/<page_key>/edit.

  Uses the SAME custom properties already defined in
  mpf-design-system.css (--mpf-space-*, --mpf-radius-*, --mpf-navy,
  --mpf-accent-2, --mpf-surface-*, --mpf-border) -- no new color/
  spacing system introduced.
*/

/* ==================================================
   1. CMS SECTION/ELEMENT RENDERING
   ================================================== */

.mpf-cms-section{ position:relative; overflow:hidden; }
.mpf-cms-section-inner{ position:relative; z-index:2; padding:var(--mpf-space-6) 0; }

/* Phase 6C Hotfix -- vertical content position (top/middle/bottom)
   within a section, independent of any background image/video's own
   Position X/Y (that remains entirely separate -- see the
   background-image/video rules below and _clean_media_display() in
   website_builder_service.py; this feature never touches those
   values). "top" is the default and predates this feature: it emits
   NO class and NO rule here at all, so a section left at "top" (every
   section before this hotfix, and any left at the default since)
   renders through exactly the same .mpf-cms-section (block) /
   .mpf-cms-section-inner (padded block) rules above, byte-for-byte --
   this is what makes the feature purely additive. "middle"/"bottom"
   switch ONLY the outer .mpf-cms-section to a column flexbox (its
   .mpf-cms-bg-video/.mpf-cms-overlay children stay position:absolute,
   entirely unaffected by their parent's display value -- see those
   rules above) and use the standard flexbox auto-margin technique on
   .mpf-cms-section-inner to position it within however tall the
   section actually ends up being (from a Height preset like
   .mpf-cms-bg--h-large, a background image/video's natural size, or
   its own content) -- .mpf-cms-section-inner's own padding is
   completely untouched either way, and this needs no media query:
   flexbox vertical alignment behaves identically at every viewport
   width, so it is responsive on desktop/tablet/mobile with zero extra
   rules. */
.mpf-cms-section.mpf-cms-vpos--middle,
.mpf-cms-section.mpf-cms-vpos--bottom{ display:flex; flex-direction:column; }
.mpf-cms-section.mpf-cms-vpos--middle > .mpf-cms-section-inner{ margin-top:auto; margin-bottom:auto; }
.mpf-cms-section.mpf-cms-vpos--bottom > .mpf-cms-section-inner{ margin-top:auto; }

/* Background type: color */
.mpf-cms-bg-color--dark{ background:var(--mpf-navy); }
.mpf-cms-bg-color--light{ background:#f4f7fb; color:#0a1428; }
.mpf-cms-bg-color--brand{ background:var(--mpf-accent-2); }
.mpf-cms-bg-color--surface{ background:var(--mpf-surface-2); }

/* Background type: gradient */
.mpf-cms-bg-gradient--brand_blue{ background:linear-gradient(135deg,var(--mpf-navy),var(--mpf-accent-2)); }
.mpf-cms-bg-gradient--dark_fade{ background:linear-gradient(180deg,rgba(10,20,40,.15),rgba(10,20,40,.92)); }
.mpf-cms-bg-gradient--sunset{ background:linear-gradient(135deg,#ff7e5f,#feb47b); }

/* Background type: image */
/* Phase 5E bugfix: background-repeat was never set here, so it fell
   back to the browser default ("repeat"). Cover always fills 100% of
   both axes so the bug was invisible there, but Contain leaves empty
   space on one axis whenever the image and section don't share the
   same aspect ratio -- and the browser was tiling the image into that
   empty space. no-repeat is now the explicit, permanent default; see
   ALLOWED_MEDIA_REPEAT in website_builder_service.py for the (rarely
   needed) opt-in Repeat/Repeat X/Repeat Y controls, applied inline by
   cms_render_section() alongside this rule (inline style wins over
   this class for the small minority of sections that opt into tiling
   on purpose -- e.g. a small repeating texture/pattern background). */
.mpf-cms-bg--image{ background-size:cover; background-position:center; background-repeat:no-repeat; }

/* Background type: video */
.mpf-cms-bg-video{ position:absolute; inset:0; z-index:0; overflow:hidden; }
.mpf-cms-bg-video video,
.mpf-cms-bg-video img{ width:100%; height:100%; object-fit:cover; display:block; }
.mpf-cms-bg-video iframe{ position:absolute; top:50%; left:50%; width:177.78vh; min-width:100%; height:56.25vw; min-height:100%; transform:translate(-50%,-50%); border:0; }

.mpf-cms-overlay{ position:absolute; inset:0; z-index:1; pointer-events:none; }

/* Layout: predefined column systems (Phase 2 scope -- see report) */
.mpf-cms-layout--single{ display:block; }
.mpf-cms-layout--two_column{ display:grid; grid-template-columns:1fr 1fr; gap:var(--mpf-space-5); align-items:center; }
.mpf-cms-layout--three_column{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--mpf-space-5); align-items:start; }
@media (max-width:760px){
    .mpf-cms-layout--two_column,
    .mpf-cms-layout--three_column{ grid-template-columns:1fr; }
}
.mpf-cms-column{ min-width:0; }

/*
  Free-Position Layered Canvas (audited feature -- see
  MyPetsFiles_LayeredFreePosition_ArchitectureAudit.docx points 6-8).
  position:relative turns this into the positioning context every
  .mpf-cms-el--positioned child's position:absolute left/top percentage
  (set inline per-element in _cms_components.html) resolves against.
  min-height is the zero-schema-migration stand-in for a real "canvas
  height" concept the audit confirmed doesn't exist anywhere in this
  project today -- no section has a stored height. This is deliberately
  a plain CSS floor, not a stored value: a canvas with no elements (or
  only elements positioned in its top portion) still has a usable drop
  target instead of collapsing to zero height, and the canvas still
  grows normally if an element's own content pushes past this floor
  (position:absolute elements don't contribute to auto height, so an
  element placed near the bottom edge can still overflow -- acceptable
  for this phase; revisit only if real use proves it awkward, per the
  audit's point 8 recommendation not to add a stored height field until
  that's actually shown to be necessary).

  Round 7 -- ONE CANONICAL COORDINATE SURFACE (real-browser-reported
  WYSIWYG mismatch: identical saved position_x/position_y rendered a
  Button in a substantially different spot in the standalone Draft
  Preview than in the embedded builder). Root cause traced from this
  file and _cms_components.html: this canvas previously had NO width of
  its own -- it filled whatever ancestor width happened to be available,
  which genuinely differs between the embedded builder iframe (squeezed
  into a pane, often well under 1000px) and a real browser viewport
  (frequently 1400-2000px+). Since left/top are percentages of that
  variable width/fixed height box, and Image/Video elements scale their
  own rendered size with the canvas (max-width:NN% below) while Button/
  Heading/Text/Spacer's "auto" width does not (shrink-to-fit, a fixed
  pixel size regardless of canvas width), the same stored coordinates
  could only ever look consistent at ONE specific canvas width -- there
  was no single canonical layout, only as many different ones as there
  were viewport widths.

  Fix: a fixed, canonical logical width (1180px -- reusing this
  project's own established content-width convention, see
  .mpf-marketing-hero-inner/.mpf-feature-row-inner in mpf-public.css,
  both width:min(1180px,94%) -- not a new arbitrary number), so every
  percentage inside always resolves against the exact same reference
  frame, everywhere. The .mpf-cms-free-canvas-viewport wrapper
  (_cms_components.html) reserves whatever ACTUAL space is available and
  the un-gated script near the end of templates/public/cms_page.html
  applies transform:scale() to fit this fixed-width canvas into it --
  uniformly, both axes together, which by construction preserves every
  relative position and size no matter how much it's scaled down. This
  is what makes the "workspace can visually scale the canvas... but that
  scaling must not change the saved layout" requirement true: the
  builder iframe, standalone Draft Preview, and published page all now
  render the exact same 1180px-wide layout, just visually shrunk to fit
  when the real available width is narrower (embedded pane, tablet,
  mobile) and unshrunk (scale capped at 1, centered) whenever it isn't.
*/
.mpf-cms-free-canvas-viewport{
    position:relative; overflow-y:hidden;
    /* No-JS / pre-hydration fallback only: if the scaling script hasn't
       run yet (or JS is unavailable), don't let the fixed 1180px canvas
       below silently clip on a narrow screen -- let it scroll instead,
       so the content stays reachable rather than invisible. Once the
       script runs, this wrapper's own height/width are effectively
       matched to the scaled canvas and no scrollbar appears in normal
       use. */
    overflow-x:auto;
}
.mpf-cms-layout--free_canvas{
    position:relative; width:1180px; min-height:420px; max-width:none;
    transform-origin:top left;
}
.mpf-cms-el--positioned{
    position:absolute; margin:0;
    /* Overridden per-element by the inline left/top/z-index style
       _cms_components.html emits from that element's own stored
       position_x/position_y/stack-order -- these are just safe
       fallbacks so a positioned element is never fully unstyled if the
       inline style is ever missing for any reason. */
    left:50%; top:50%;
}

/*
  Free Canvas real-browser fix round -- CASCADE COLLISION (root cause of
  reported Failures 1 "image clipped/doesn't resemble prior layout" and
  2 "Button cannot overlap Image").

  .mpf-wbe-el-hoverable (further down this file, pre-existing since the
  Phase D hotfix, is_preview-only) sets `position:relative` on EVERY
  element inside a "layered" section -- its own comment there explains
  this exists purely so the absolutely-positioned drag-handle chip
  (.mpf-wbe-el-drag-handle-chip) has a non-static ancestor to anchor
  against. cms_page.html's is_preview script (the ONLY place D.12
  dragging happens, i.e. the exact context the user's real-browser test
  used) adds that class to every element in a layered section,
  INCLUDING free_canvas elements that already carry
  .mpf-cms-el--positioned from the server-rendered markup above. Both
  selectors are single classes of equal specificity (0,0,1,0); CSS
  cascade order breaks the tie in favor of whichever rule appears LATER
  in this file, and .mpf-wbe-el-hoverable is defined after this rule --
  so in the actual admin preview, `position` silently resolved to
  `relative`, not `absolute`, for every Free Canvas element. A
  position:relative element still reserves its normal document-flow
  space and only offsets FROM that reserved spot, which is exactly why
  Button could never truly render on top of Image (both were still
  laid out in flow, one after the other) and why moved/never-moved
  elements produced an unpredictable, flow-driven arrangement instead
  of true X/Y placement.

  Fix: this compound selector (0,0,2,0 specificity) beats either single-
  class rule regardless of source order, so `position:absolute` always
  wins whenever both classes are present. This changes nothing for
  every OTHER element the hover chip touches (every Structured-layout
  element, which never carries .mpf-cms-el--positioned in the first
  place) and nothing for the chip itself -- position:absolute is an
  equally valid non-static anchor for an absolutely-positioned child as
  position:relative is, so .mpf-wbe-el-drag-handle-chip still anchors
  correctly.
*/
.mpf-cms-el--positioned.mpf-wbe-el-hoverable{ position:absolute; }

/*
  Free Canvas real-browser fix round -- DIMENSION-AWARE SIZING (residual
  cause of Failure 1). A position:absolute element with only left/top
  set (no explicit width) sizes itself "shrink-to-fit", which for an
  <img> child relying solely on `max-width:100%; height:auto`
  (.mpf-cms-image, unchanged) can resolve toward the image's own large
  intrinsic pixel size rather than any sensible on-canvas size, which
  then gets clipped by the pre-existing, unrelated
  .mpf-cms-section{ overflow:hidden } rule -- exactly the reported
  "image clipped/extends beyond the visible area" symptom. Giving the
  positioned wrapper a REAL width (not max-width) removes the
  ambiguity; this reuses the EXISTING width field/values every element
  already has (ALLOWED_WIDTHS in website_builder_service.py -- no new
  database field), and only takes effect for elements that are actually
  in free_canvas mode (the compound selector requires
  .mpf-cms-el--positioned), so structured-layout width behavior
  (max-width based) is completely unchanged. The default "auto" width
  is left as true shrink-to-fit for text-like elements (heading/text/
  button/spacer -- safe, bounded by their own short content) but is
  given a sensible bounded default specifically for image/video, whose
  large intrinsic pixel dimensions are what actually caused the
  clipping bug; an admin can still pick 25/50/75/100 explicitly on any
  element type for a bigger or smaller box. This also directly supplies
  the element-width data the dimension-aware clamping fix (cms_page.html)
  needs, without requiring any new stored field.

  Real-browser fix round 2 -- the "auto" rule below originally forced a
  FIXED width:35% on every Image/Video, which the real-browser test
  confirmed over-corrected the original bug: every such image rendered
  as a small, uniform thumbnail regardless of its actual size, because
  a fixed `width` always wins over the image's own natural dimensions,
  the exact "arbitrarily collapse the image" behavior explicitly not
  wanted. Changed to `max-width` instead of `width`: max-width on this
  wrapper resolves against the free_canvas container's own real,
  non-circular width (so it is NOT subject to the shrink-to-fit
  circularity that caused the original oversized/clipped bug -- see
  above), but unlike a fixed `width`, it only ever CAPS the wrapper,
  never forces it. A naturally small image (e.g. a logo) keeps
  rendering at its own natural size instead of being stretched or
  shrunk to a fixed box. 25/50/75/100 are deliberately left as fixed
  `width` values (unchanged) -- those are an admin's explicit,
  deterministic choice ("75 should render approximately 75% width"),
  which a fixed width is exactly the correct tool for; only the
  *default*, un-chosen "auto" case needed to stop being forced into a
  fixed size.

  Real-browser fix round 3 -- max-width:50% (round 2's value) was still
  reported as visually too small. Root-cause audit of the FULL image
  rendering chain (every rule in this file touching .mpf-cms-image,
  .mpf-cms-media--fit-N, .mpf-cms-media--h-N, and
  .mpf-cms-el--positioned -- see the fix-round-3 report for the
  complete trace) confirms there is
  no other hard-coded width/height competing with this rule: Media
  Width="full" and Media Height="auto" (this project's defaults) both
  print NO extra class at all (see cms_media_classes()'s own docstring
  in _cms_components.html), so this max-width rule is genuinely the
  ONLY thing governing the wrapper's size for the un-chosen "auto"
  case -- confirming Element Width really is the sole source of truth
  here, exactly as intended. Bumped from 50% to 75%, deliberately
  REUSING the existing, already-tested "75" tier's own percentage
  (mpf-cms-width--75 immediately below) rather than inventing a third
  arbitrary number -- a meaningfully larger, more useful default,
  while keeping `max-width` (not a fixed `width`) so a genuinely small
  source image still renders at its own natural size instead of being
  stretched up to fill 75% of the canvas.

  Also confirmed by that same audit (relevant to the separately-
  reported "cropped/blurred" appearance): object-fit:cover and
  object-position only ever have a visible cropping effect when the
  <img> has an explicit, non-auto height (i.e. one of the
  .mpf-cms-media--h-* rules below, height:Npx) creating a box whose
  aspect ratio can genuinely differ from the source photo's own. With
  Media Height="auto" (this project's default, and what every element
  in this bug report was actually set to), the <img> always keeps
  .mpf-cms-image's height:auto -- its rendered box is always exactly
  the source photo's own aspect ratio, scaled down, so Fit/Position
  have nothing to crop and are correctly inert. This is pre-existing
  behavior, shared by every structured (non-free_canvas) layout too --
  not a Free Canvas defect, and not touched by this fix. The
  "cropped/blurred" look is the visual effect of rendering a detailed
  source photo at a small on-screen size (worsened by the admin
  preview iframe's own reduced width, since these are percentage-based
  wrapper sizes); it should read as substantially clearer and larger
  once the wrapper itself renders at its new, bigger default size.
*/
.mpf-cms-el--positioned.mpf-cms-width--25{ width:25%; }
.mpf-cms-el--positioned.mpf-cms-width--50{ width:50%; }
.mpf-cms-el--positioned.mpf-cms-width--75{ width:75%; }
.mpf-cms-el--positioned.mpf-cms-width--100{ width:100%; }
.mpf-cms-el--positioned.mpf-cms-width--auto[data-cms-element-type="image"],
.mpf-cms-el--positioned.mpf-cms-width--auto[data-cms-element-type="video"]{ max-width:75%; }

/* Element common style tokens */
.mpf-cms-el{ margin-bottom:var(--mpf-space-4); }
.mpf-cms-align--left{ text-align:left; }
.mpf-cms-align--center{ text-align:center; margin-left:auto; margin-right:auto; }
.mpf-cms-align--right{ text-align:right; margin-left:auto; }

.mpf-cms-width--auto{ max-width:none; }
.mpf-cms-width--25{ max-width:25%; }
.mpf-cms-width--50{ max-width:50%; }
.mpf-cms-width--75{ max-width:75%; }
.mpf-cms-width--100{ max-width:100%; width:100%; }

.mpf-cms-space--none{ margin-bottom:0; }
.mpf-cms-space--sm{ margin-bottom:var(--mpf-space-2); }
.mpf-cms-space--md{ margin-bottom:var(--mpf-space-4); }
.mpf-cms-space--lg{ margin-bottom:var(--mpf-space-5); }
.mpf-cms-space--xl{ margin-bottom:var(--mpf-space-6); }

.mpf-cms-radius--none{ border-radius:0; }
.mpf-cms-radius--sm{ border-radius:var(--mpf-radius-sm); }
.mpf-cms-radius--md{ border-radius:var(--mpf-radius-md); }
.mpf-cms-radius--lg{ border-radius:var(--mpf-radius-lg); }
.mpf-cms-radius--full{ border-radius:var(--mpf-radius-pill); }

.mpf-cms-type--sm{ font-size:14px; }
.mpf-cms-type--md{ font-size:var(--mpf-text-body); }
.mpf-cms-type--lg{ font-size:var(--mpf-text-h3); }
.mpf-cms-type--xl{ font-size:var(--mpf-text-h2); }
.mpf-cms-type--2xl{ font-size:var(--mpf-text-h1); }
/* Text Styling Controls (Phase 3): larger presets for Free Canvas
   display/hero text -- 2xl (32px, --mpf-text-h1) was confirmed too
   small for that use case. Purely additive; every existing class above
   is untouched. .mpf-cms-type--custom intentionally sets no font-size
   of its own -- the element's inline style (typography_size_custom_px,
   see cms_text_style() in _cms_components.html) always wins over a
   class of equal-or-lower specificity, so this class only needs to
   exist so the element still has a stable, targetable class name. */
.mpf-cms-type--3xl{ font-size:40px; }
.mpf-cms-type--4xl{ font-size:56px; }
.mpf-cms-type--5xl{ font-size:72px; }
.mpf-cms-type--custom{ }

@keyframes mpfCmsFadeIn{ from{ opacity:0; } to{ opacity:1; } }
@keyframes mpfCmsSlideUp{ from{ opacity:0; transform:translateY(18px); } to{ opacity:1; transform:translateY(0); } }
.mpf-cms-anim--fade_in{ animation:mpfCmsFadeIn .6s ease both; }
.mpf-cms-anim--slide_up{ animation:mpfCmsSlideUp .6s ease both; }

.mpf-cms-image{ max-width:100%; height:auto; display:block; }
.mpf-cms-video-el{ max-width:100%; display:block; }

/* Phase 5D: Responsive Media Controls -- generic, additive modifier
   classes. None of the rules above this block are modified. Every
   class below is purely additive: applied alongside (never instead
   of) an element's existing class (.mpf-cms-image, .mpf-cms-video-el,
   a gallery <img>, or the <section>/<video> a background renders on).
   !important is used deliberately and uniformly here -- this is a
   small, closed set of admin-selected, whitelisted presentation
   tokens (not arbitrary CSS), and reliably overriding higher-
   specificity compound selectors already in this file (e.g.
   ".mpf-cms-bg-video video, .mpf-cms-bg-video img{object-fit:cover;}"
   above) matters more than avoiding !important in this narrow,
   contained context. Values come only from ALLOWED_MEDIA_FIT /
   ALLOWED_MEDIA_POSITION / ALLOWED_MEDIA_HEIGHT / ALLOWED_MEDIA_WIDTH
   in website_builder_service.py, so the class names below are the
   complete, exhaustive set -- nothing else can ever be emitted. */
.mpf-cms-media--fit-cover{ object-fit:cover !important; background-size:cover !important; }
.mpf-cms-media--fit-contain{ object-fit:contain !important; background-size:contain !important; }
.mpf-cms-media--pos-center{ object-position:center !important; background-position:center !important; }
.mpf-cms-media--pos-top{ object-position:top !important; background-position:top !important; }
.mpf-cms-media--pos-bottom{ object-position:bottom !important; background-position:bottom !important; }
.mpf-cms-media--pos-left{ object-position:left !important; background-position:left !important; }
.mpf-cms-media--pos-right{ object-position:right !important; background-position:right !important; }
.mpf-cms-media--pos-top_left{ object-position:left top !important; background-position:left top !important; }
.mpf-cms-media--pos-top_right{ object-position:right top !important; background-position:right top !important; }
.mpf-cms-media--pos-bottom_left{ object-position:left bottom !important; background-position:left bottom !important; }
.mpf-cms-media--pos-bottom_right{ object-position:right bottom !important; background-position:right bottom !important; }
/* height="auto" (the default) intentionally has NO rule here -- that
   is what makes it a true no-op. Omitting a class name for it, and
   simply not printing a "--h-auto" class in the macro at all, means
   every existing section keeps its current natural sizing behavior
   (hero/layered image's unconstrained height:auto, or the gallery
   grid's own fixed 1:1 aspect-ratio box) by construction, with zero
   special-casing. */
.mpf-cms-media--h-small{ height:200px; width:100%; }
.mpf-cms-media--h-medium{ height:360px; width:100%; }
.mpf-cms-media--h-large{ height:520px; width:100%; }
.mpf-cms-media--h-full{ height:80vh; width:100%; }
/* width="full" (the default) intentionally has no rule either, for
   the same reason. width="contained" reuses the exact 760px
   convention already established by cms_render_rich_text() /
   cms_render_faq() elsewhere in this file, for visual consistency. */
.mpf-cms-media--w-contained{ max-width:760px; margin-left:auto; margin-right:auto; }

/* Phase 5E: Advanced Media & Background Controls -- additive only,
   nothing above this block is modified. New Fit options ("natural" =
   object-fit:none/background-size:auto, showing the media at its
   real/unscaled size cropped to its box; "stretch" = object-fit:fill/
   background-size:100% 100%, the one case that deliberately does NOT
   preserve aspect ratio, exactly because the admin explicitly asked
   for it). Precise Position X/Y and Zoom are NOT expressed as classes
   here -- they're numeric (0-100 / 50-300), not a small closed token
   set, so cms_media_style()/cms_bg_style() in _cms_components.html
   compute them as inline style instead (same already-established
   pattern this file's overlay rendering has used since Phase 2 for
   overlay_opacity). "auto"/"full" (the defaults for height/width) and
   "no-repeat" (the default for repeat, now the *only* way a Phase-5D-
   or-earlier row can render -- old rows have no "repeat" key at all,
   and _clean_media_display() defaults it to "no-repeat") still print
   no class/no inline override, so nothing already tested changes. */
.mpf-cms-media--fit-natural{ object-fit:none !important; background-size:auto !important; }
.mpf-cms-media--fit-stretch{ object-fit:fill !important; background-size:100% 100% !important; }

/* Background-context height presets (min-height, NOT height -- a
   background must never clip a section's actual content the way a
   fixed height:Npx could). Deliberately separate from the existing
   .mpf-cms-media--h-* rules above (height:Npx), which remain
   untouched and still govern the <img>/<video> element cases exactly
   as tested in Phase 5D. "custom" height for a background section is
   handled via inline min-height (see cms_bg_style()), not a class. */
.mpf-cms-bg--h-small{ min-height:200px; }
.mpf-cms-bg--h-medium{ min-height:360px; }
.mpf-cms-bg--h-large{ min-height:520px; }
.mpf-cms-bg--h-full{ min-height:80vh; }

/* Clips a Zoom transform:scale() on an <img>/<video> to its
   surrounding box so a zoomed-in image can never visually overflow
   its section/card -- applied to the SAME existing wrapper elements
   Phase 5D already rendered (.mpf-cms-hero-image, gallery <figure>,
   a layered element's own wrapper div), never a new wrapper element. */
.mpf-cms-media-clip{ overflow:hidden; }

.mpf-cms-spacer--sm{ height:var(--mpf-space-3); }
.mpf-cms-spacer--md{ height:var(--mpf-space-4); }
.mpf-cms-spacer--lg{ height:var(--mpf-space-6); }
.mpf-cms-spacer--xl{ height:calc(var(--mpf-space-6) * 2); }

.mpf-cms-gallery-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:var(--mpf-space-3); }
.mpf-cms-gallery-grid img{ width:100%; aspect-ratio:1/1; object-fit:cover; border-radius:var(--mpf-radius-md); }

.mpf-cms-testimonial{ padding:var(--mpf-space-4); background:var(--mpf-surface-1); border:1px solid var(--mpf-border); border-radius:var(--mpf-radius-md); }
.mpf-cms-testimonial-byline{ display:flex; align-items:center; gap:var(--mpf-space-2); margin-top:var(--mpf-space-2); }
.mpf-cms-testimonial-avatar{ width:36px; height:36px; border-radius:50%; object-fit:cover; flex-shrink:0; }
.mpf-cms-faq-item{ padding:var(--mpf-space-3) 0; border-bottom:1px solid var(--mpf-border); }

/* Phase 5B -- stats_strip section type */
.mpf-cms-stats-strip{ display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:var(--mpf-space-4); text-align:center; }
.mpf-cms-stats-strip-item-value{ font-size:2rem; font-weight:700; line-height:1.1; }
.mpf-cms-stats-strip-item-label{ margin-top:var(--mpf-space-1); font-weight:600; }
.mpf-cms-stats-strip-item-caption{ margin-top:2px; }

/* True Nested Containers -- Round N.3. Public-render layout for a
   container element (element_type="container", added N.1 -- see
   templates/design_system/_cms_components.html's cms_render_element()
   container branch). N.1 explicitly left this CSS for N.3 since no
   admin UI could create a container until now. A container itself has
   no visible chrome of its own (no border/background/padding) -- it is
   purely a layout wrapper around its children, each of which already
   carries its own .mpf-cms-el spacing/alignment/width via the same
   rules every other element already uses, container child or not.
   "stack" needs no extra rule beyond the default block flow every
   .mpf-cms-el already gets (each child's own margin-bottom already
   spaces them vertically) -- flex column is used anyway so a stack
   container's children align consistently even when a child's own
   width/alignment style makes it narrower than the container. "row"
   places children side by side, wrapping rather than overflowing on a
   narrow container. An empty container (no children) collapses to
   zero visible height either way -- exactly the same "nothing to
   render" behavior every other empty state in this stylesheet already
   has, not a special case. */
.mpf-cms-container{ margin-bottom:var(--mpf-space-4); }
.mpf-cms-container:last-child{ margin-bottom:0; }
.mpf-cms-container-layout--stack{ display:flex; flex-direction:column; }
.mpf-cms-container-layout--row{ display:flex; flex-direction:row; flex-wrap:wrap; align-items:flex-start; gap:var(--mpf-space-4); }
.mpf-cms-container-layout--row > .mpf-cms-el{ margin-bottom:0; }
/* A container nested inside another container (depth 2) shouldn't
   double up on the depth-1 container's own bottom margin -- its
   parent's flex gap (row layout) or child margin-bottom (stack
   layout) already provides the spacing between siblings. */
.mpf-cms-container .mpf-cms-container{ margin-bottom:0; }
@media (max-width:760px){
    /* Matches this file's existing 760px column-collapse breakpoint
       (see the layered-column rules above) -- a "row" container's
       children stack vertically on a narrow viewport, the same way a
       multi-column layered section already does, rather than
       shrinking side-by-side children to an unusably narrow width. */
    .mpf-cms-container-layout--row{ flex-direction:column; }
}

/* Responsive visibility -- Phase 2's one exposed responsive control */
@media (max-width:600px){
    [data-cms-hide-mobile="1"]{ display:none !important; }
}
@media (min-width:601px) and (max-width:1024px){
    [data-cms-hide-tablet="1"]{ display:none !important; }
}

/* ==================================================
   1B. PHASE 6B -- GENERIC VISUAL POLISH
   ================================================== */
/* Every rule in this block is scoped to .mpf-cms-section-inner (or a
   class already only ever emitted inside it) -- i.e. it applies to
   ANY page rendered through cms_render_section(), on ANY page_key,
   not just Home. Nothing here is keyed to a section id, an anchor
   slug, or any Homepage-specific selector. This is deliberate: the
   Phase 6B request was to polish shared Website Builder presentation,
   not to hand-tune the Homepage with one-off CSS. */

/* --------------------------------------------------
   Section vertical rhythm.
   Previously a flat 40px top/bottom on every section regardless of
   viewport (var(--mpf-space-6) 0) -- functional, but tight for a
   marketing page meant to read as a sequence of deliberate visual
   "beats" rather than a stack of admin panels. Bumped to a more
   generous 72px on tablet/desktop; the existing 40px is kept
   UNCHANGED on mobile (<=760px, matching the layout system's existing
   breakpoint) specifically so this doesn't eat already-scarce small-
   screen vertical space. Nothing else about .mpf-cms-section-inner
   (position, z-index) changes. */
/* Phase C: Global Styles indirection -- --mpf-cms-section-padding-y(-mobile)
   are only ever set inside cms_page.html's Global Styles <style> block,
   so this remains a byte-identical 72px/40px default (var(--mpf-space-6)
   is 40px) until an admin picks a non-default Spacing option. */
.mpf-cms-section-inner{ padding:var(--mpf-cms-section-padding-y, 72px) 0; }
@media (max-width:760px){
    .mpf-cms-section-inner{ padding:var(--mpf-cms-section-padding-y-mobile, var(--mpf-space-6)) 0; }
}

/* --------------------------------------------------
   Heading hierarchy inside CMS content.
   Audit finding: body[data-mpf-root] h1,h2,h3 (mpf-design-system.css)
   sets weight/color/margin globally but NEVER font-size -- every
   CMS-rendered heading that isn't individually hand-sized elsewhere
   (mpf-public.css's .mpf-marketing-hero-text h1 / .mpf-final-cta h2)
   was falling through to the browser's unstyled UA default instead of
   this project's own --mpf-text-h1/h2/h3 scale. Scoped to
   .mpf-cms-section-inner so it fixes every CMS section type
   (cta_banner's h2, feature_grid/faq/testimonials' h3, any future
   type) without touching the two pages that already set their own
   explicit size, and without touching non-CMS surfaces (admin portal,
   pet dashboard) at all. h1/h2 also get a touch of letter-spacing and
   tighter line-height, which is what actually makes a heading read as
   "designed" rather than default body copy at a bigger size. */
.mpf-cms-section-inner h1{ font-size:var(--mpf-text-h1); line-height:1.15; letter-spacing:-.01em; }
.mpf-cms-section-inner h2{ font-size:var(--mpf-text-h2); line-height:1.2; letter-spacing:-.005em; }
.mpf-cms-section-inner h3{ font-size:var(--mpf-text-h3); line-height:1.3; }
.mpf-cms-section-inner h4{ font-size:var(--mpf-text-body); font-weight:var(--mpf-weight-medium); line-height:1.4; margin:0 0 var(--mpf-space-2); }

/* --------------------------------------------------
   Legibility on light-toned backgrounds (real bug fix, not a style
   preference). --mpf-text-secondary / --mpf-text-muted are calibrated
   for light-text-on-dark-navy (the whole site's default surface).
   .mpf-cms-bg-color--light and .mpf-cms-bg-gradient--sunset are both
   light/warm-toned backgrounds -- any plain text sitting directly on
   them (not inside a .mpf-card/.mpf-widget/.mpf-cms-testimonial,
   which always carry their OWN dark surface background regardless of
   the section, so stay correct automatically) inherits those same
   light-on-dark colors and becomes low-contrast/hard to read. FAQ
   items (.mpf-cms-faq-item, no background of its own) are the
   clearest example. Scoped custom-property overrides fix every
   current and future plain-text element in one place, the same way
   .mpf-cms-bg-color--light already overrides the base `color` --
   this simply extends that existing pattern to the two secondary/
   muted tokens it didn't cover. */
.mpf-cms-bg-color--light .mpf-cms-section-inner,
.mpf-cms-bg-gradient--sunset .mpf-cms-section-inner{
    --mpf-text-secondary:#33415c;
    --mpf-text-muted:#5a6b8c;
}

/* --------------------------------------------------
   Card / feature-card consistency.
   .mpf-feature-card already gets .mpf-card's dark surface, border,
   radius, shadow, and padding (see mpf-design-system.css) -- it was
   only ever missing the same hover-lift interaction .mpf-card
   already offers via the OPT-IN .mpf-card--interactive modifier
   (cms_render_feature_grid() doesn't add that modifier class). Rather
   than touch the template, the exact same transform/shadow/border
   treatment is applied directly here so every feature card gets it
   automatically, with zero markup change. The icon block becomes a
   true circle (was a small rounded square) with its color centered
   inside a fixed-size ring, which reads more like a deliberate icon
   treatment and less like a stray color swatch. */
.mpf-feature-card{
    transition:transform .15s ease, box-shadow .15s ease, border-color .15s ease;
    /* Final Visual Polish round: tightened from the Icon Layout &
       Positioning round's 28px/var(--mpf-radius-lg) hardcode -- 24px
       reads less bulky while staying spacious, and border-radius now
       references --mpf-card-radius (falling back to the same
       --mpf-radius-lg default) so an admin's Global Styles card-radius
       choice actually reaches Feature Grid cards too, exactly like it
       already does for every other var(--mpf-card-radius) consumer.
       The prior round's hardcoded var(--mpf-radius-lg, 20px) quietly
       bypassed that Global Styles control -- same visual default,
       correct behavior when Global Styles is customized. */
    padding:24px;
    border-radius:var(--mpf-card-radius, var(--mpf-radius-lg));
    /* Final Visual Polish round: a very subtle inset top highlight
       layered ON TOP of the existing Global-Styles-driven shadow
       (var(--mpf-card-shadow, var(--mpf-shadow-sm)) is preserved
       exactly, never replaced) -- a faint 1px light edge along the
       card's top, the kind of restrained glass/depth cue common to
       sleek, modern dark-UI cards. Barely visible at rest, intentional
       rather than decorative. */
    box-shadow:var(--mpf-card-shadow, var(--mpf-shadow-sm)), inset 0 1px 0 rgba(255,255,255,.05);
}
.mpf-feature-card:hover{
    transform:translateY(-3px);
    box-shadow:var(--mpf-shadow-md), inset 0 1px 0 rgba(255,255,255,.06);
    border-color:rgba(59,130,246,.35);
}
.mpf-feature-icon{
    /* Feature Grid Icon Layout & Positioning round: width/height are now
       ALSO set inline per-item (cms_render_feature_grid() -- scaled to
       the item's own icon_size token), so these are just the safe
       fallback if that inline style is ever absent for any reason.
       40px matches "md", the default/pre-this-round size. */
    width:40px;
    height:40px;
    border-radius:50%;
    /* Final Visual Polish round: widened from var(--mpf-space-3)=12px
       to 16px -- a clearer visual gap between the icon "zone" and the
       title/caption "text zone" below it, one half of the asymmetric
       icon-to-title / title-to-caption spacing this round introduces
       for a stronger sense of hierarchy (see .mpf-feature-card-title/
       -caption below for the other half). Purely a spacing change --
       the icon's own box model, sizing, and offset-transform mechanics
       (both inline, from cms_render_feature_grid()) are untouched. */
    margin-bottom:16px;
    display:flex;
    align-items:center;
    justify-content:center;
    /* Icon Layout & Positioning round: an icon box's height/width must
       never get squeezed by anything above/below it in the card's
       normal block flow -- flex-shrink only matters inside a flex
       container, but .mpf-card is plain block, so this is a defensive
       no-op today and a safeguard against a future layout change. */
    flex-shrink:0;
}
/* Icon Completion round: Feature Grid's per-item SVG icon (mpf_content_icon,
   rendered inside .mpf-feature-icon) sits on top of the colored circle
   background, so it needs to read as white/light against any accent color
   rather than inheriting page text color. The circle itself keeps the
   feature's accent color unchanged. */
.mpf-feature-icon-svg{
    color:#fff;
}
/* Feature Grid Icon Visual Fix round: the "Show icon circle" OFF case
   (the default appearance for a newly selected Feature Grid icon) -- a
   standalone outline SVG, no colored circle/background behind it,
   matching the reference look.
   Feature Grid Icon Layout & Positioning round FIX: width/height are
   now set INLINE per-item (cms_render_feature_grid(), scaled to the
   item's icon_size), matching the icon's own real pixel size exactly.
   Previously this rule had no width at all, so as a block-level flex
   container it silently stretched to the card's full width and
   justify-content:center then centered the SVG horizontally -- that's
   why production showed the icon centered at the top of the card
   instead of sitting upper-left next to the title. width:max-content
   below is a defensive fallback (same reasoning as .mpf-feature-icon's
   fallback width above) for the rare case the inline style is ever
   missing -- it keeps the box exactly as wide as its SVG rather than
   ever silently stretching full-width again. */
.mpf-feature-icon-standalone{
    width:max-content;
    /* Final Visual Polish round: matches .mpf-feature-icon's margin-bottom
       bump (12px -> 16px) so the standalone (no-circle) icon path gets the
       identical icon-to-title spacing improvement as the circle-badge path. */
    margin-bottom:16px;
    display:flex;
    align-items:center;
    justify-content:center;
    flex-shrink:0;
}
/* Feature Grid Icon Visual Fix round: the standalone icon's own color
   comes from the item's existing icon_color choice (the same
   var(--mpf-{accent}) tokens .mpf-feature-icon's circle background
   already uses) rather than being hardcoded white -- there's no colored
   circle behind it to provide contrast, so the SVG itself needs to carry
   the accent color via stroke="currentColor" picking up this `color`. */
.mpf-feature-icon-standalone-svg{
    color:inherit;
}
/* Feature Grid Icon Layout & Positioning round: Feature Grid's title and
   caption get their own small, narrowly-scoped classes instead of the
   shared .mpf-widget-title/.mpf-widget-caption (used broadly across
   pet-profile widgets/dashboards elsewhere in the project -- changing
   those would risk regressing unrelated screens). Slightly larger/
   bolder title for a clearer hierarchy under the icon, softer/muted
   caption tone underneath -- the "sleek, modern" polish this round
   asked for, without touching any shared component. */
.mpf-feature-card-title{
    margin:0;
    /* Final Visual Polish round: 16px -> 17px plus a touch of negative
       letter-spacing -- a small, modern-feeling type refinement. Weight,
       color, and line-height are unchanged. */
    font-size:17px;
    font-weight:var(--mpf-weight-medium, 600);
    letter-spacing:-0.01em;
    color:var(--mpf-text-primary);
    line-height:1.3;
}
.mpf-feature-card-caption{
    /* Final Visual Polish round: tightened from 6px to 4px so the caption
       visually groups with the title right above it, reinforcing the
       icon-zone / text-zone hierarchy created by the larger icon
       margin-bottom above. Font-size/color/line-height unchanged. */
    margin:4px 0 0;
    font-size:var(--mpf-text-caption, 13px);
    color:var(--mpf-text-muted, var(--mpf-text-secondary));
    line-height:1.55;
}
/* Final Visual Polish round: on tablet/desktop widths (where
   .mpf-feature-row-inner in mpf-public.css switches to a 2- or 4-column
   grid), give the cards a bit more breathing room between them --
   var(--mpf-space-4)=20px instead of the base var(--mpf-space-3)=12px
   gap -- so the multi-column layout feels intentional rather than
   cramped. Mobile's single-column gap (defined in mpf-public.css) is
   untouched. Scoped here (rather than edited in mpf-public.css) to keep
   this round's changed-file footprint to this one CSS file. */
@media (min-width:640px){
    .mpf-feature-row-inner{
        gap:var(--mpf-space-4, 20px);
    }
}
/* Icon Completion round: standalone "icon" Layered-section element. No
   background/circle -- just the inline SVG at its chosen size, colored via
   the same var(--mpf-{accent}) custom properties Feature Grid already uses.
   Sizing comes from the SVG's own width/height (see _mpf_icon_element_px()
   in _cms_components.html), so this wrapper only needs to avoid collapsing
   to zero height/width as an inline element. */
.mpf-cms-icon-el{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    line-height:0;
}

/* --------------------------------------------------
   FAQ presentation.
   Was padding + a bottom border only -- functional, but visually
   indistinguishable from any other stacked plain-text block. Slightly
   more breathing room per item and a tighter question/answer pairing
   (question keeps the global h3 margin-bottom already set above; this
   just increases the item's own vertical padding so a list of FAQs
   reads as a sequence of distinct rows rather than one dense block of
   text) is the whole change -- no accordion/JS behavior is introduced,
   since this system has none today and adding one is out of scope for
   a CSS-only pass. */
.mpf-cms-faq-item{ padding:var(--mpf-space-4) 0; }

/* --------------------------------------------------
   Testimonials byline spacing tightened slightly to sit closer to the
   quote it belongs to (was using the same var(--mpf-space-2) as every
   other unrelated gap in the file; a hair tighter reads as more
   intentionally grouped). Small, low-risk, purely visual. */
.mpf-cms-testimonial-byline{ margin-top:6px; }

/* ==================================================
   2. HOMEPAGE VISUAL BUILDER -- three-pane editor shell
   ================================================== */

.mpf-wbe-shell{
    display:grid;
    grid-template-columns:260px 1fr 320px;
    gap:var(--mpf-space-3);
    height:calc(100vh - 220px);
    min-height:520px;
}
@media (max-width:1100px){
    .mpf-wbe-shell{ grid-template-columns:1fr; height:auto; }
}

/*
  Round 4 -- large editing workspace (Sections/Properties panels are
  tools, the canvas is the workspace, per the requested Illustrator/
  Canva/Figma-style direction). Two independent, additive mechanisms:

  1. WIDEN THE OUTER ADMIN SHELL, but ONLY on this page. The shared
     .mpf-admin-shell{ width:min(1400px,96%); } rule in
     mpf-design-system.css is used by every admin page (Dashboard,
     Users, Pets, ...); editing that rule directly would widen every
     one of those pages too, which is out of scope and unnecessary
     risk. Instead this uses a :has() parent selector scoped to
     "an .mpf-admin-shell that actually contains a .mpf-wbe-shell" --
     true only on this builder page -- so mpf-design-system.css itself
     is never touched and every other admin page keeps its existing
     1400px cap byte-for-byte. :has() has been supported in every
     evergreen browser since 2023 (Chrome/Edge/Safari 105+, Firefox
     121+); if an admin's browser somehow lacks it, this selector
     simply never matches and the page falls back to today's existing
     1400px cap -- a safe, non-breaking degradation, not an error.
  2. COLLAPSIBLE SECTIONS/PROPERTIES PANELS + A COMBINED "FOCUS CANVAS"
     TOGGLE. Pure CSS attribute-selector states on .mpf-wbe-shell
     itself (data-wbe-left-collapsed/data-wbe-right-collapsed, toggled
     by plain class/attribute JS in mpf-website-builder.js -- no
     server round-trip, no iframe reload, so this can never touch
     position_x/position_y or any other saved element/section data;
     see that file's own comment on this block for the full
     guarantee). Each collapsed panel keeps its title bar (with the
     button that reopens it) but hides its body, per the requested
     "[<][ CANVAS ][ Properties ]" / "[ Sections ][ CANVAS ][>]" /
     "[<][ LARGE CANVAS ][>]" concept.
*/
.mpf-admin-shell:has(.mpf-wbe-shell){ width:min(1800px, 98%); }

.mpf-wbe-shell[data-wbe-left-collapsed="1"]{ grid-template-columns:44px 1fr 320px; }
.mpf-wbe-shell[data-wbe-right-collapsed="1"]{ grid-template-columns:260px 1fr 44px; }
.mpf-wbe-shell[data-wbe-left-collapsed="1"][data-wbe-right-collapsed="1"]{ grid-template-columns:44px 1fr 44px; }
@media (max-width:1100px){
    /* Stacked single-column layout already ignores grid-template-columns
       entirely (see the existing @media rule above) -- collapse state
       is irrelevant there, so no extra override is needed here. */
    .mpf-wbe-pane--collapsed{ display:block !important; }
    .mpf-wbe-pane--collapsed .mpf-wbe-pane-body{ display:block !important; }
}

.mpf-wbe-pane{
    background:var(--mpf-surface-1);
    border:1px solid var(--mpf-border);
    border-radius:var(--mpf-radius-md);
    overflow-y:auto;
    padding:var(--mpf-space-3);
}

.mpf-wbe-pane-title{ font-size:13px; font-weight:600; text-transform:uppercase; letter-spacing:.04em; color:var(--mpf-text-secondary); margin:0 0 var(--mpf-space-3); }

.mpf-wbe-pane-title--row{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.mpf-wbe-pane-collapse-btn{
    flex:0 0 auto; width:24px; height:24px; display:inline-flex; align-items:center; justify-content:center;
    background:transparent; border:1px solid var(--mpf-border); border-radius:6px; color:var(--mpf-text-secondary);
    cursor:pointer; font-size:13px; line-height:1; text-transform:none; letter-spacing:normal; font-weight:400;
}
.mpf-wbe-pane-collapse-btn:hover{ color:var(--mpf-text-primary); border-color:var(--mpf-border-strong); }
.mpf-wbe-pane-collapse-btn:focus-visible{ outline:2px solid var(--mpf-accent-cyan); outline-offset:1px; }

/*
  Collapsed rail: the pane keeps its real width from the grid (44px,
  set above) and stays a flex column so its lone visible child (the
  collapse/expand button) centers cleanly; the title label and the
  entire body are hidden, but remain in the DOM completely unchanged
  -- nothing here removes or re-creates any form field, so no
  in-progress edit is lost by collapsing a panel mid-edit.
*/
.mpf-wbe-pane--collapsed{ display:flex; flex-direction:column; align-items:center; padding:10px 4px; overflow:visible; }
.mpf-wbe-pane--collapsed .mpf-wbe-pane-body{ display:none; }
.mpf-wbe-pane--collapsed .mpf-wbe-pane-title--row{ flex-direction:column; gap:6px; }
.mpf-wbe-pane--collapsed .mpf-wbe-pane-title--row > span{ display:none; }
.mpf-wbe-pane-collapse-btn[data-wbe-collapse-toggle="right"]{ transform:none; }
.mpf-wbe-pane--collapsed .mpf-wbe-pane-collapse-btn{ transform:rotate(180deg); }

.mpf-wbe-focus-btn{
    display:inline-flex; align-items:center; gap:5px; padding:5px 12px; font-size:12px; font-weight:600;
    color:var(--mpf-text-secondary); cursor:pointer; border-radius:999px; border:1px solid var(--mpf-border);
    background:transparent; user-select:none;
}
.mpf-wbe-focus-btn:hover{ color:var(--mpf-text-primary); border-color:var(--mpf-border-strong); }
.mpf-wbe-focus-btn[aria-pressed="true"]{ color:#ffffff; background:var(--mpf-accent-2); border-color:var(--mpf-accent-2); }

.mpf-wbe-section-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--mpf-space-2); }
/*
  Phase D.1 -- section cards redesigned to a two-row layout so a full
  section title is readable instead of being ellipsis-truncated to
  fit alongside 3-4 action icons on one line (the original single-row
  flex layout this replaced). Row 1 = drag handle + title (wraps to a
  second line when needed) + Visible/Hidden status. Row 2 = the exact
  same 4 action forms/routes this project already had -- nothing about
  what an icon DOES changed, only where it sits. The <li> itself keeps
  draggable="true" (see mpf-website-builder.js's section drag-and-drop,
  unchanged) -- there was never a dedicated drag handle in the original
  implementation to preserve, so the whole card remains the drag
  surface; the small handle glyph added below is a purely visual/
  aria-hidden affordance, not a new restriction on where a drag can
  start from.
*/
.mpf-wbe-section-row{
    display:flex; flex-direction:column; gap:6px;
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border); border-radius:var(--mpf-radius-sm);
    padding:9px 10px; cursor:grab;
}
.mpf-wbe-section-row[data-wbe-selected="1"]{ border-color:var(--mpf-border-strong); box-shadow:0 0 0 1px var(--mpf-border-strong); }
.mpf-wbe-section-row.mpf-wbe-dragging{ opacity:.4; }
.mpf-wbe-section-row-top{ display:flex; align-items:flex-start; gap:7px; }
.mpf-wbe-section-row-handle{
    flex:0 0 auto; color:var(--mpf-text-muted); font-size:13px; line-height:1.4;
    padding-top:1px; user-select:none;
}
.mpf-wbe-section-row-identity{ flex:1; min-width:0; display:block; text-decoration:none; }
.mpf-wbe-section-row-label{
    display:block; font-size:13.5px; font-weight:600; line-height:1.32;
    color:var(--mpf-text-primary); overflow-wrap:anywhere; white-space:normal;
}
.mpf-wbe-section-row-type{
    display:inline-flex; align-items:center; gap:5px; margin-top:4px;
    font-size:10.5px; text-transform:uppercase; letter-spacing:.04em; color:var(--mpf-text-muted);
}
.mpf-wbe-section-row-type::before{
    content:""; width:6px; height:6px; border-radius:50%; background:var(--mpf-success, #22c55e); flex:0 0 auto;
}
.mpf-wbe-section-row[data-wbe-hidden="1"] .mpf-wbe-section-row-type::before{ background:var(--mpf-text-muted); }
.mpf-wbe-section-row-actions{
    display:flex; align-items:center; gap:6px; flex-wrap:wrap;
    padding-top:6px; margin-top:1px; border-top:1px solid var(--mpf-border);
}
.mpf-wbe-icon-btn{
    width:27px; height:27px; display:inline-flex; align-items:center; justify-content:center;
    background:transparent; border:1px solid var(--mpf-border); border-radius:6px; color:var(--mpf-text-secondary);
    cursor:pointer; font-size:12.5px; line-height:1; flex:0 0 auto;
}
.mpf-wbe-icon-btn:hover{ color:var(--mpf-text-primary); border-color:var(--mpf-border-strong); background:var(--mpf-surface-1); }
.mpf-wbe-icon-btn:focus-visible{ outline:2px solid var(--mpf-accent-cyan); outline-offset:1px; }
.mpf-wbe-section-row[data-wbe-hidden="1"] .mpf-wbe-section-row-label{ color:var(--mpf-text-muted); font-style:italic; }
.mpf-wbe-icon-svg{ width:15px; height:15px; display:block; flex:0 0 auto; }
.mpf-wbe-toolbar .mpf-btn .mpf-wbe-icon-svg{ width:16px; height:16px; margin-right:2px; vertical-align:-3px; }
.mpf-wbe-section-row-handle .mpf-wbe-icon-svg{ width:14px; height:14px; }

.mpf-wbe-canvas-frame-wrap{ height:100%; min-height:480px; background:#fff; border-radius:var(--mpf-radius-sm); overflow:hidden; }
.mpf-wbe-canvas-frame{ width:100%; height:100%; border:0; display:block; }

.mpf-wbe-field{ margin-bottom:var(--mpf-space-3); }
.mpf-wbe-field label{ display:block; font-size:12.5px; color:var(--mpf-text-secondary); margin-bottom:4px; }
.mpf-wbe-field input[type="text"],
.mpf-wbe-field input[type="url"],
.mpf-wbe-field input[type="number"],
.mpf-wbe-field select,
.mpf-wbe-field textarea{
    width:100%; padding:8px 10px; border-radius:8px; border:1px solid var(--mpf-border);
    background:var(--mpf-surface-2); color:var(--mpf-text-primary); font:inherit;
}
.mpf-wbe-field textarea{ resize:vertical; min-height:70px; }

.mpf-wbe-add-menu{ display:flex; flex-wrap:wrap; gap:6px; margin-bottom:var(--mpf-space-3); }
.mpf-wbe-toolbar{ display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--mpf-space-3); gap:8px; flex-wrap:wrap; }
.mpf-wbe-breakpoint-toggle{ display:flex; gap:4px; }
.mpf-wbe-breakpoint-toggle button{
    padding:6px 10px; border-radius:8px; border:1px solid var(--mpf-border); background:var(--mpf-surface-2);
    color:var(--mpf-text-secondary); cursor:pointer; font-size:12.5px;
}
.mpf-wbe-breakpoint-toggle button[data-wbe-active="1"]{ color:var(--mpf-text-primary); border-color:var(--mpf-border-strong); background:var(--mpf-surface-3); }

/* Phase 3 -- Media Library visual picker (progressive enhancement over
   the plain <select data-mpf-media-type> in admin/_website_section_form.html;
   see static/js/mpf-website-builder.js's IIFE at the bottom of the file). */
.mpf-wbe-media-field{ margin-bottom:var(--mpf-space-3); }
.mpf-wbe-media-select--hidden{ display:none !important; }
.mpf-wbe-media-preview{
    display:flex; align-items:center; gap:8px; padding:6px 8px; margin-bottom:6px;
    border:1px solid var(--mpf-border); border-radius:8px; background:var(--mpf-surface-2); min-height:40px;
}
.mpf-wbe-media-preview img{ width:32px; height:32px; object-fit:cover; border-radius:6px; flex-shrink:0; }
.mpf-wbe-media-preview-icon{
    width:32px; height:32px; display:flex; align-items:center; justify-content:center; flex-shrink:0;
    border-radius:6px; background:var(--mpf-surface-3); font-size:13px;
}
.mpf-wbe-media-preview-label{ font-size:12.5px; color:var(--mpf-text-secondary); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mpf-wbe-media-preview-empty{ font-size:12.5px; color:var(--mpf-text-muted); font-style:italic; }

.mpf-wbe-media-modal{
    display:none; position:fixed; inset:0; z-index:1000; background:rgba(6,10,20,.6);
    align-items:center; justify-content:center; padding:24px;
}
.mpf-wbe-media-modal-card{
    width:100%; max-width:720px; max-height:80vh; display:flex; flex-direction:column;
    background:var(--mpf-surface-1); border:1px solid var(--mpf-border); border-radius:var(--mpf-radius-md);
    overflow:hidden;
}
.mpf-wbe-media-modal-header{
    display:flex; align-items:center; justify-content:space-between; padding:14px 16px;
    border-bottom:1px solid var(--mpf-border); font-weight:600; color:var(--mpf-text-primary);
}
.mpf-wbe-media-modal-close{
    background:transparent; border:0; color:var(--mpf-text-secondary); font-size:20px; line-height:1; cursor:pointer;
}
.mpf-wbe-media-modal-close:hover{ color:var(--mpf-text-primary); }
.mpf-wbe-media-modal-grid{
    padding:14px; overflow-y:auto; display:grid; grid-template-columns:repeat(auto-fill, minmax(96px, 1fr)); gap:10px;
}
.mpf-wbe-media-tile{
    display:flex; flex-direction:column; align-items:center; gap:6px; padding:8px;
    border:1px solid var(--mpf-border); border-radius:8px; background:var(--mpf-surface-2); cursor:pointer;
    color:var(--mpf-text-secondary); font-size:11px; text-align:center;
}
.mpf-wbe-media-tile:hover{ border-color:var(--mpf-border-strong); color:var(--mpf-text-primary); }
.mpf-wbe-media-tile img{ width:64px; height:64px; object-fit:cover; border-radius:6px; }
.mpf-wbe-media-tile-icon{
    width:64px; height:64px; display:flex; align-items:center; justify-content:center;
    border-radius:6px; background:var(--mpf-surface-3); font-size:20px;
}
.mpf-wbe-media-tile span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
.mpf-wbe-media-tile--none{ justify-content:center; min-height:96px; font-weight:600; }

.mpf-wbe-empty{ color:var(--mpf-text-muted); font-size:13px; text-align:center; padding:var(--mpf-space-4) 0; }

.mpf-wbe-version-row{ display:flex; align-items:center; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--mpf-border); font-size:13px; }

/* ==================================================
   Founders System Component (Founders Visual-Equivalence Hotfix)
   Purely additive -- new class names only, nothing above this block is
   modified. Reproduces the reference static page's dark hero (via the
   existing, unmodified "brand_blue" gradient background token applied
   at the section level) with white floating cards for stats/progress/
   claim panel, same visual language as .founder-hero/.stat-card/
   .info-card in templates/founders.html.
   ================================================== */
.mpf-cms-founder-program{ text-align:center; padding:6px 4px 4px; }
.mpf-cms-founder-eyebrow{
    margin-bottom:14px; color:#bfe0ff; font-size:15px; font-weight:800;
    letter-spacing:2px; text-transform:uppercase;
}
.mpf-cms-founder-headline{
    max-width:860px; margin:0 auto; color:#fff; font-size:44px; line-height:1.1;
}
.mpf-cms-founder-description{
    max-width:760px; margin:18px auto 0; color:#e9f4ff; font-size:18px; line-height:1.6;
}

.mpf-cms-founder-stats{
    display:grid; grid-template-columns:repeat(3,1fr); gap:18px;
    max-width:1080px; margin:34px auto 18px; text-align:center;
}
.mpf-cms-founder-stat-card{
    background:#fff; border:1px solid #dce5f1; border-radius:18px;
    box-shadow:0 8px 28px rgba(19,48,91,.18); padding:25px;
}
.mpf-cms-founder-stat-number{ color:#0a397c; font-size:38px; font-weight:900; }
.mpf-cms-founder-stat-label{ margin-top:5px; color:#65748b; font-weight:700; font-size:14px; }

.mpf-cms-founder-progress-panel{
    max-width:1080px; margin:0 auto 18px; background:#fff; border:1px solid #dce5f1;
    border-radius:18px; box-shadow:0 8px 28px rgba(19,48,91,.18); padding:22px 26px; text-align:left;
}
.mpf-cms-founder-status-pill{
    display:inline-block; padding:8px 12px; background:#eaf8ee; color:#1f6533;
    border:1px solid #abd8b7; border-radius:999px; font-size:14px; font-weight:800;
}

.mpf-cms-founder-claim-panel{
    max-width:1080px; margin:0 auto; background:#fff; border:1px solid #dce5f1;
    border-radius:18px; box-shadow:0 8px 28px rgba(19,48,91,.18); padding:26px; text-align:left;
}
.mpf-cms-founder-claim-heading{ margin:0 0 14px; color:#0a2d66; font-size:26px; }
.mpf-cms-founder-note{
    padding:17px; background:#eef6ff; border:1px solid #c7ddf4; border-radius:12px;
    color:#24476e; line-height:1.55; margin-bottom:16px;
}
.mpf-cms-founder-claim-panel .mpf-btn{ margin:0 8px 8px 0; }

@media(max-width:800px){
    .mpf-cms-founder-stats{ grid-template-columns:1fr; }
    .mpf-cms-founder-headline{ font-size:32px; }
}
.mpf-wbe-version-row:last-child{ border-bottom:0; }

/* ==================================================
   Phase 5E -- Advanced Media & Background Controls (admin UI only).
   Every rule below styles the property panel's new collapsible Media/
   Overlay groups, sliders, and preset buttons in
   templates/admin/_website_section_form.html -- purely cosmetic, no
   interaction with the public-facing CMS render CSS earlier in this
   file. Nothing above this block is modified.
   ================================================== */
.mpf-wbe-collapsible{
    border:1px solid var(--mpf-border); border-radius:8px; margin-bottom:var(--mpf-space-3);
    background:var(--mpf-surface-2); overflow:hidden;
}
.mpf-wbe-collapsible > summary{
    cursor:pointer; padding:8px 10px; font-size:12.5px; font-weight:700;
    color:var(--mpf-text-primary); list-style:none; display:flex; align-items:center; gap:6px;
}
.mpf-wbe-collapsible > summary::-webkit-details-marker{ display:none; }
.mpf-wbe-collapsible > summary::before{ content:"\25B8"; font-size:11px; color:var(--mpf-text-secondary); transition:transform .15s ease; }
.mpf-wbe-collapsible[open] > summary::before{ transform:rotate(90deg); }
.mpf-wbe-collapsible > .mpf-wbe-collapsible-body{ padding:4px 10px 10px; }
.mpf-wbe-collapsible .mpf-wbe-collapsible{ background:var(--mpf-surface-3); }

/* ==================================================
   Multi-Item Content Controls (Gallery/Testimonials/FAQ, also Feature
   Grid and Stats Strip -- every array-based section type shares this
   one item-list UI in templates/admin/_website_section_form.html).
   Purely additive to the layout: the fields inside .mpf-wbe-array-item-
   body are the exact same inputs/selects/textareas this file already
   styled via .mpf-wbe-field rules above -- only the wrapper/header/
   controls around them are new. Nothing above this block is modified.
   ================================================== */
.mpf-wbe-array-wrap{ margin-bottom:var(--mpf-space-3); }
.mpf-wbe-array-group{ display:flex; flex-direction:column; gap:8px; margin-bottom:8px; }
.mpf-wbe-array-item{
    border:1px solid var(--mpf-border); border-radius:8px; overflow:hidden; background:var(--mpf-surface-2);
}
.mpf-wbe-array-item-header{
    display:flex; align-items:center; justify-content:space-between; gap:8px;
    padding:7px 10px; background:var(--mpf-surface-3); border-bottom:1px solid var(--mpf-border);
}
.mpf-wbe-array-item-label{ font-size:12.5px; font-weight:700; color:var(--mpf-text-primary); }
.mpf-wbe-array-item-controls{ display:flex; align-items:center; gap:4px; }
.mpf-wbe-array-item-controls .mpf-btn{ min-height:28px; padding:2px 10px; line-height:1.4; }
.mpf-wbe-array-item-controls .mpf-btn:disabled{ opacity:.35; cursor:not-allowed; }
.mpf-wbe-array-item-body{ padding:8px 10px; }
.mpf-wbe-array-add-btn{ width:100%; justify-content:center; }

/* A slider + its live numeric readout, reused for Position X/Y, Zoom,
   and every overlay opacity control. The <input type=range> keeps the
   real submitted name; the trailing number is decoration only, kept in
   sync entirely client-side (see mpf-website-builder.js's generic
   delegated "input" listener). */
.mpf-wbe-range-row{ display:flex; align-items:center; gap:10px; }
.mpf-wbe-range-row input[type="range"]{ flex:1; accent-color:var(--mpf-accent-cyan); }
.mpf-wbe-range-value{
    min-width:38px; text-align:right; font-size:12px; font-weight:600;
    color:var(--mpf-text-secondary); font-variant-numeric:tabular-nums;
}

/* The 9-point focal-position quick-select grid -- still a convenience
   shortcut only (see ALLOWED_MEDIA_POSITION's Phase 5E docstring in
   website_builder_service.py); clicking one just fills in the real
   Position X/Y sliders above via mpfWbeSetPosition(), it writes no
   field of its own. */
.mpf-wbe-position-presets{
    display:grid; grid-template-columns:repeat(3, 1fr); gap:4px; margin:6px 0 10px;
}
.mpf-wbe-position-presets button{
    padding:6px 0; font-size:11px; border-radius:6px; border:1px solid var(--mpf-border);
    background:var(--mpf-surface-1); color:var(--mpf-text-secondary); cursor:pointer;
}
.mpf-wbe-position-presets button:hover{ border-color:var(--mpf-border-strong); color:var(--mpf-text-primary); }

/* Enable Overlay toggle row + the gradient stop pair beneath it. */
.mpf-wbe-overlay-toggle{ display:flex; align-items:center; gap:8px; margin-bottom:var(--mpf-space-3); font-size:13px; }
.mpf-wbe-overlay-toggle input{ width:auto; }
.mpf-wbe-gradient-stop{
    border:1px dashed var(--mpf-border); border-radius:8px; padding:8px; margin-bottom:8px;
}
.mpf-wbe-gradient-stop-label{ font-size:11.5px; font-weight:700; color:var(--mpf-text-secondary); margin-bottom:6px; text-transform:uppercase; letter-spacing:.03em; }

/* ==========================================================================
   3. PHASE A -- BUILDER UX UPGRADE (Visual Builder 2.0, Phase A)
   ========================================================================== */

/*
  A.1 -- Properties panel tabs (Content/Style/Layout/Responsive/Advanced).
  Pure-CSS tab switching: 5 hidden radio inputs + <label for="..."> tab
  buttons + a matching data-tab panel, all direct children of
  .mpf-wbe-tabs. No JS is involved in switching tabs, and no field is
  ever removed from the DOM -- every panel's inputs are always present
  and always submitted, regardless of which tab is currently visible.
  See templates/admin/_website_section_form.html for the markup this
  styles and the grouping rationale.
*/
.mpf-wbe-tab-radio{
    position:absolute; opacity:0; width:0; height:0; pointer-events:none;
}
.mpf-wbe-tab-bar{
    display:flex; flex-wrap:wrap; gap:2px; margin-bottom:var(--mpf-space-3);
    border-bottom:1px solid var(--mpf-border); padding-bottom:0;
}
.mpf-wbe-tab-btn{
    display:inline-block; padding:6px 10px; font-size:12px; font-weight:600;
    color:var(--mpf-text-secondary); cursor:pointer; border-radius:6px 6px 0 0;
    border:1px solid transparent; border-bottom:none; margin-bottom:-1px;
    user-select:none;
}
.mpf-wbe-tab-btn:hover{ color:var(--mpf-text-primary); background:var(--mpf-surface-2); }
.mpf-wbe-tab-panel{ display:none; }
.mpf-wbe-tab-panel > *:first-child{ margin-top:0; }

#mpfWbeTabContent:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeTabContent"],
#mpfWbeTabStyle:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeTabStyle"],
#mpfWbeTabLayout:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeTabLayout"],
#mpfWbeTabResponsive:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeTabResponsive"],
#mpfWbeTabAdvanced:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeTabAdvanced"]{
    color:var(--mpf-accent-cyan); background:var(--mpf-surface-2);
    border-color:var(--mpf-border); border-bottom:1px solid var(--mpf-surface-2);
}

#mpfWbeTabContent:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="content"],
#mpfWbeTabStyle:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="style"],
#mpfWbeTabLayout:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="layout"],
#mpfWbeTabResponsive:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="responsive"],
#mpfWbeTabAdvanced:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="advanced"]{
    display:block;
}

/*
  A.2 -- Click-to-select highlight, applied INSIDE the preview iframe by
  the is_preview-gated script in templates/public/cms_page.html. This
  class is only ever added by that script, which only ever exists in
  the admin preview response -- a live public page never has any script
  that could add this class, so this rule is dead weight (never
  matched) on every real page, not merely invisible.
  outline (not border) so it never shifts layout/reflows content.
*/
.mpf-wbe-preview-selected{
    outline:2px solid var(--mpf-accent-cyan);
    outline-offset:-2px;
    position:relative;
}

/*
  A.4 -- Navigator/Layers panel: nested element rows under a section
  row, and the hidden-section/hidden-element visual treatment. Reuses
  the existing .mpf-wbe-section-row/-list look rather than inventing a
  parallel style language.
*/
.mpf-wbe-navigator-elements{
    list-style:none; margin:2px 0 4px 0; padding:0 0 0 18px;
    border-left:1px dashed var(--mpf-border);
}
.mpf-wbe-navigator-element-row{ font-size:12px; }
.mpf-wbe-navigator-element-row a{
    display:flex; align-items:center; justify-content:space-between; gap:6px;
    padding:4px 8px; border-radius:6px; color:var(--mpf-text-secondary);
    text-decoration:none; cursor:pointer;
}
.mpf-wbe-navigator-element-row a:hover{ background:var(--mpf-surface-2); color:var(--mpf-text-primary); }
.mpf-wbe-navigator-element-row[data-wbe-el-selected="1"] a{
    background:var(--mpf-surface-2); color:var(--mpf-accent-cyan); font-weight:600;
}
.mpf-wbe-navigator-element-row[data-wbe-el-hidden="1"] a{ color:var(--mpf-text-tertiary, var(--mpf-text-secondary)); font-style:italic; }
.mpf-wbe-navigator-toggle{
    background:none; border:none; color:inherit; cursor:pointer; font-size:11px;
    padding:2px 4px; margin-right:2px;
}
.mpf-wbe-navigator-badge{
    font-size:9.5px; text-transform:uppercase; letter-spacing:.03em; color:var(--mpf-text-secondary);
    border:1px solid var(--mpf-border); border-radius:4px; padding:1px 4px;
}

/*
  A.3 -- Element drag-and-drop. Reuses the exact opacity treatment
  .mpf-wbe-section-row.mpf-wbe-dragging already uses for section rows,
  scoped to .mpf-wbe-element-form instead, so the two drag interactions
  look/feel consistent.
*/
.mpf-wbe-element-form.mpf-wbe-dragging{ opacity:.4; }
.mpf-wbe-element-drag-handle{
    cursor:grab; user-select:none; -webkit-user-select:none;
}
.mpf-wbe-element-drag-handle:active{ cursor:grabbing; }
.mpf-wbe-element-form{ transition:box-shadow .12s ease; }
.mpf-wbe-element-form.mpf-wbe-element-highlight{
    box-shadow:0 0 0 2px var(--mpf-accent-cyan);
}

/*
  True Nested Containers -- Round N.3. Admin "Elements in This Section"
  list -- N.1 explicitly left this CSS for N.3, since no admin UI could
  create a container until now.

  Depth indentation: mpf-wbe-element-depth-N is added by flatten_
  elements_for_editor() (website_builder_service.py) via each element
  row's class list -- 0 for every top-level element (every page that
  existed before this round only ever has depth 0, so this is visually
  a no-op for them). Uses margin-left rather than the pre-existing
  inline `style="border:...;padding:...;"` on .mpf-wbe-element-form
  (see _website_section_form.html) so nothing here has to fight that
  inline style's specificity.
*/
.mpf-wbe-element-depth-1{ margin-left:22px; }
.mpf-wbe-element-depth-2{ margin-left:44px; }

/* Container badge/row distinguishing style. box-shadow (not `border`)
   deliberately -- .mpf-wbe-element-form already carries an inline
   `border:1px solid var(--mpf-border)` on every row (container or not),
   and an inline style attribute always wins over an external stylesheet
   rule for the same `border` property, so an inset box-shadow is the
   one way to add a colored accent stripe here without that fight. */
.mpf-wbe-element-form--container{
    box-shadow:inset 3px 0 0 var(--mpf-accent-cyan);
    background:var(--mpf-surface-2);
}
.mpf-wbe-container-badge{
    display:inline-flex; align-items:center; gap:4px;
    font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.03em;
    color:var(--mpf-accent-cyan);
    border:1px solid var(--mpf-border); border-radius:999px;
    padding:1px 8px 1px 6px; margin-left:8px; vertical-align:middle;
}
.mpf-wbe-container-badge .mpf-wbe-icon-svg{ width:12px; height:12px; }
.mpf-wbe-container-empty-hint{
    color:var(--mpf-text-secondary); font-size:12.5px; font-style:italic;
    border:1px dashed var(--mpf-border); border-radius:6px;
    padding:6px 8px; margin:6px 0 0;
}

/*
  True Nested Containers -- Round N.4. The component-library drag
  source (_website_section_form.html) -- one chip per element type plus
  one per System Component, dragged onto the live preview iframe to
  create a new element (see that route's "use_type_defaults" branch,
  routes/website_builder.py, and cms_page.html's new library-item
  dragover/drop handling). Same cyan/border/pill language as the
  container badge above, laid out as a wrapping row of small pills
  rather than a list -- this is a palette to grab from, not data to
  read. cursor:grab (not pointer) signals "drag me", matching
  .mpf-wbe-el-hoverable's own convention in the preview.
*/
.mpf-wbe-library-chip-row{ display:flex; flex-wrap:wrap; gap:6px; }
.mpf-wbe-library-chip{
    display:inline-flex; align-items:center; gap:5px;
    font-size:12px; font-weight:600; color:var(--mpf-text-primary, #1a2430);
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border); border-radius:999px;
    padding:5px 10px 5px 8px; cursor:grab; user-select:none;
    transition:border-color .15s ease, color .15s ease, background .15s ease;
}
.mpf-wbe-library-chip:hover{ border-color:var(--mpf-accent-cyan); color:var(--mpf-accent-cyan); }
.mpf-wbe-library-chip:active{ cursor:grabbing; }
.mpf-wbe-library-chip .mpf-wbe-icon-svg{ width:14px; height:14px; flex:none; }

/*
  Round 1 -- Real Visual Component Palette. A persistent, always-
  rendered pane (website_page_editor.html, above the Sections pane, NOT
  gated behind selecting an existing Layered section) so the admin sees
  "these are the things I can add to my page" the moment the builder
  opens, per the request for Elementor/Wix-style discoverability.

  Architecture note (why this is additive, not a rewrite): the tiles
  below reuse the exact same mechanisms Round N.4 already built --
  Section tiles submit to the SAME admin_website_section_add_default
  route the plain <select> dropdown already posts to (just one small
  form per tile instead of one shared form), and Element tiles carry
  the SAME data-mpf-library-* attributes the old properties-panel chip
  row used, so the existing document-level "dragstart" delegate in
  mpf-website-builder.js (which matches on the .mpf-wbe-library-chip
  class, not on where that element lives in the DOM) picks them up
  with no JS changes at all. Nothing here is a second component-
  creation or drag-and-drop system.

  .mpf-wbe-left-stack wraps the new palette pane + the existing
  #mpfWbeSectionPane so both still occupy the SAME single grid cell
  (column 1) .mpf-wbe-shell's grid-template-columns already reserves --
  see that rule below. This means the existing collapse mechanism
  (data-wbe-left-collapsed on .mpf-wbe-shell, toggled by the Sections
  pane's own collapse button) continues to control the whole left
  column's width exactly as before; the palette simply hides itself in
  that collapsed (44px) state rather than trying to render inside it --
  see the .mpf-wbe-shell[data-wbe-left-collapsed="1"] rule below.
*/
/*
  Round 2 -- UI/UX refinement. The palette pane from Round 1 is renamed
  from a generic .mpf-wbe-palette-pane class to the specific
  #mpfWbeLibraryPane id (website_page_editor.html) now that it houses a
  single unified "Component Library" (Sections | Elements tabs) instead
  of two stacked groups -- same flex-sizing/collapse behavior as Round
  1, just re-targeted at the new id.
*/
.mpf-wbe-left-stack{ display:flex; flex-direction:column; gap:var(--mpf-space-3); min-height:0; height:100%; }
.mpf-wbe-left-stack > .mpf-wbe-pane{ min-height:0; }
#mpfWbeLibraryPane{ flex:0 0 auto; max-height:55%; }
.mpf-wbe-left-stack > #mpfWbeSectionPane{ flex:1 1 auto; }
.mpf-wbe-shell[data-wbe-left-collapsed="1"] #mpfWbeLibraryPane{ display:none; }
@media (max-width:1100px){
    .mpf-wbe-left-stack{ height:auto; }
    #mpfWbeLibraryPane{ max-height:none; }
    .mpf-wbe-left-stack > #mpfWbeSectionPane{ flex:none; }
}

.mpf-wbe-palette-group{ margin-bottom:var(--mpf-space-3); }
.mpf-wbe-palette-group:last-child{ margin-bottom:0; }
.mpf-wbe-palette-group-title{
    font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
    color:var(--mpf-text-secondary); margin:0 0 6px;
}
.mpf-wbe-palette-hint{ color:var(--mpf-text-secondary); font-size:11.5px; margin:0 0 8px; }
.mpf-wbe-palette-grid{
    display:grid; grid-template-columns:repeat(auto-fill, minmax(72px, 1fr)); gap:8px;
}
.mpf-wbe-palette-tile{
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
    padding:10px 6px; min-height:68px;
    font-size:11px; font-weight:600; text-align:center; line-height:1.2;
    color:var(--mpf-text-primary, #1a2430);
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border); border-radius:10px;
    cursor:pointer; user-select:none; width:100%;
    transition:border-color .15s ease, color .15s ease, background .15s ease, transform .1s ease;
}
.mpf-wbe-palette-tile .mpf-wbe-icon-svg{ width:20px; height:20px; flex:none; }
.mpf-wbe-palette-tile:hover{ border-color:var(--mpf-accent-cyan); color:var(--mpf-accent-cyan); background:var(--mpf-surface-1); }
.mpf-wbe-palette-tile:active{ transform:scale(.97); }
/* Element tiles are dragged, not clicked -- cursor:grab signals that,
   matching .mpf-wbe-library-chip's existing convention above. */
.mpf-wbe-palette-tile--draggable{ cursor:grab; }
.mpf-wbe-palette-tile--draggable:active{ cursor:grabbing; transform:none; }
/* Disabled state: Element tiles render even with no Layered section
   selected (so the admin always sees the full palette), but nothing is
   draggable until there's a valid section to drop into -- see the
   Jinja condition in website_page_editor.html. Dimmed + not-allowed
   cursor + no hover reaction communicates that plainly. */
.mpf-wbe-palette-tile--disabled{
    opacity:.45; cursor:not-allowed; pointer-events:none;
}
.mpf-wbe-palette-tile-form{ display:contents; }

/*
  Round 2 -- Component Library tabs (Sections | Elements). Reuses the
  exact same hidden-radio + label + data-tab panel technique as A.1's
  properties-panel tabs above (#mpfWbeTabContent etc.) -- new radio
  name/ids so this tab group is fully independent of that one, but the
  shared .mpf-wbe-tabs/.mpf-wbe-tab-radio/.mpf-wbe-tab-bar/.mpf-wbe-tab-
  btn/.mpf-wbe-tab-panel(s) classes are unchanged, so no new selectors
  were needed for the base look -- only the id-specific :checked rules
  below, mirroring A.1's pattern.
*/
#mpfWbeLibraryTabSections:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeLibraryTabSections"],
#mpfWbeLibraryTabElements:checked ~ .mpf-wbe-tab-bar label[for="mpfWbeLibraryTabElements"]{
    color:var(--mpf-accent-cyan); background:var(--mpf-surface-2);
    border-color:var(--mpf-border); border-bottom:1px solid var(--mpf-surface-2);
}
#mpfWbeLibraryTabSections:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="library-sections"],
#mpfWbeLibraryTabElements:checked ~ .mpf-wbe-tab-panels .mpf-wbe-tab-panel[data-tab="library-elements"]{
    display:block;
}
.mpf-wbe-library-tabs .mpf-wbe-tab-bar{ margin-bottom:var(--mpf-space-2); }

/*
  Round 2 -- classic fallback controls (old dropdown + Reusable
  Sections form), demoted from always-visible to a collapsed <details>
  disclosure inside the Sections tab so the tile grid is what the eye
  lands on first, per requirement 4 ("de-emphasize, don't remove").
  No JS: <details>/<summary> is native, so the old form markup inside
  it is completely unchanged and still works identically whether open
  or collapsed.
*/
.mpf-wbe-classic-controls{
    margin-top:var(--mpf-space-2); padding-top:var(--mpf-space-2);
    border-top:1px dashed var(--mpf-border);
}
.mpf-wbe-classic-controls > summary{
    font-size:11px; font-weight:600; color:var(--mpf-text-secondary);
    cursor:pointer; user-select:none; list-style:none;
}
.mpf-wbe-classic-controls > summary::-webkit-details-marker{ display:none; }
.mpf-wbe-classic-controls > summary::before{ content:"\25B8"; display:inline-block; margin-right:5px; transition:transform .12s ease; }
.mpf-wbe-classic-controls[open] > summary::before{ transform:rotate(90deg); }
.mpf-wbe-classic-controls > summary:hover{ color:var(--mpf-text-primary); }

/*
  Round 2 -- drag-and-drop discoverability banner shown above the
  Element tiles (requirement 5). --active = a Layered section is
  selected, so dragging works right now; --inactive = nothing
  compatible is selected, so the banner explains what to do instead
  (select a Layered section, or one-click add one via the button
  below) rather than just leaving the tiles dimmed with no context.
*/
.mpf-wbe-library-context{
    display:flex; align-items:flex-start; gap:8px;
    font-size:11.5px; line-height:1.4;
    border-radius:8px; padding:8px 10px; margin:0 0 10px;
}
.mpf-wbe-library-context .mpf-wbe-icon-svg{ width:15px; height:15px; flex:none; margin-top:1px; }
.mpf-wbe-library-context--active{
    color:var(--mpf-accent-cyan);
    background:color-mix(in srgb, var(--mpf-accent-cyan) 10%, var(--mpf-surface-2));
    border:1px solid color-mix(in srgb, var(--mpf-accent-cyan) 35%, var(--mpf-border));
}
.mpf-wbe-library-context--inactive{
    color:var(--mpf-text-secondary);
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border);
    flex-direction:column; align-items:stretch;
}
.mpf-wbe-library-context-btn{
    display:inline-block; align-self:flex-start;
    font-size:11px; font-weight:700; color:var(--mpf-accent-cyan);
    background:var(--mpf-surface-1); border:1px solid var(--mpf-accent-cyan);
    border-radius:999px; padding:4px 12px; cursor:pointer;
    transition:background .15s ease, color .15s ease;
}
.mpf-wbe-library-context-btn:hover{ background:var(--mpf-accent-cyan); color:var(--mpf-surface-1); }

/* ==========================================================================
   5. PHASE D -- BUILDER TOOLS DRAWER, FIND SECTION, CONTEXTUAL TOOLS
      (Visual Builder 2.0, Phase D)
   ========================================================================== */

/*
  D.2 -- Builder Tools drawer. Fixed-position slide-in panel, closed by
  default (display:none via the [hidden]-equivalent aria-hidden gate
  below) so it never reserves permanent layout space away from the
  3-pane editor -- it overlays on top when open, same pattern as the
  existing media picker modal (.mpf-wbe-media-modal) already uses.
*/
.mpf-wbe-tools-backdrop{
    position:fixed; inset:0; background:rgba(4,10,24,.45); z-index:40;
}
.mpf-wbe-tools-drawer{
    position:fixed; top:0; right:0; height:100vh; width:340px; max-width:92vw;
    background:var(--mpf-surface-1); border-left:1px solid var(--mpf-border);
    box-shadow:var(--mpf-shadow-md, 0 8px 30px rgba(0,0,0,.35));
    z-index:41; overflow-y:auto; padding:16px;
    transform:translateX(100%); transition:transform .16s ease;
}
.mpf-wbe-tools-drawer[data-wbe-open="1"]{ transform:translateX(0); }
.mpf-wbe-tools-drawer-header{
    display:flex; align-items:center; justify-content:space-between;
    margin-bottom:12px; padding-bottom:10px; border-bottom:1px solid var(--mpf-border);
}
.mpf-wbe-tools-drawer-title{ font-size:15px; font-weight:700; color:var(--mpf-text-primary); }
.mpf-wbe-tools-group{ margin-bottom:16px; }
.mpf-wbe-tools-group[data-wbe-tools-contextual="1"]{
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border); border-radius:var(--mpf-radius-sm);
    padding:10px;
}
.mpf-wbe-tools-group-title{
    font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
    color:var(--mpf-text-secondary); margin-bottom:8px;
}
.mpf-wbe-tools-btn-row{ display:flex; gap:6px; flex-wrap:wrap; }
.mpf-wbe-tools-find-results{
    list-style:none; margin:6px 0 0; padding:0; max-height:220px; overflow-y:auto;
}
.mpf-wbe-tools-find-results li{ margin:0; }
.mpf-wbe-tools-find-results a{
    display:block; padding:6px 8px; border-radius:6px; font-size:13px;
    color:var(--mpf-text-primary); text-decoration:none;
}
.mpf-wbe-tools-find-results a:hover,
.mpf-wbe-tools-find-results a:focus-visible{ background:var(--mpf-surface-2); }
.mpf-wbe-tools-find-empty{ font-size:12.5px; color:var(--mpf-text-muted); padding:6px 8px; }
.mpf-wbe-tools-shortcut-list{ list-style:none; margin:0; padding:0; font-size:12.5px; color:var(--mpf-text-secondary); }
.mpf-wbe-tools-shortcut-list li{ display:flex; align-items:center; gap:6px; padding:3px 0; }
.mpf-wbe-tools-shortcut-list kbd{
    display:inline-block; min-width:18px; text-align:center; font-size:11px; font-family:inherit;
    background:var(--mpf-surface-2); border:1px solid var(--mpf-border); border-radius:4px; padding:1px 5px;
}
.mpf-wbe-tools-shortcut-note{ font-size:11px; color:var(--mpf-text-muted); margin:8px 0 0; font-style:italic; }

/*
  Reuses .mpf-wbe-preview-selected's outline treatment for a row in the
  left Sections list that Find Section (D.3) or a keyboard jump has
  just scrolled to, purely as a momentary visual confirmation -- this
  class is only ever toggled by JS below, never server-rendered.
*/
.mpf-wbe-section-row.mpf-wbe-just-found{
    outline:2px solid var(--mpf-accent-cyan); outline-offset:1px;
}

/* ==========================================================================
   6. PHASE D.12 -- IN-PREVIEW ELEMENT SELECTION + REORDER (LAYERED SECTIONS)
   ========================================================================== */

/*
  These classes are added/removed ONLY by the is_preview-gated script
  in templates/public/cms_page.html (see that file) -- exactly the same
  guarantee .mpf-wbe-preview-selected already relies on (A.2 above): a
  live public page never runs that script, so these rules are dead
  weight there, never matched. outline/box-shadow only, so nothing here
  ever reflows layout -- same discipline as every other preview-only
  rule in this file.
*/
.mpf-wbe-el-hoverable{ cursor:grab; position:relative; }
.mpf-wbe-el-hoverable:hover{ outline:1px dashed var(--mpf-accent-cyan); outline-offset:2px; }
.mpf-wbe-preview-el-selected{ outline:2px solid var(--mpf-accent-cyan); outline-offset:2px; position:relative; }
.mpf-wbe-el-dragging{ opacity:.35; cursor:grabbing; }
.mpf-wbe-el-drop-indicator{
    height:3px; background:var(--mpf-accent-cyan); border-radius:2px; margin:4px 0;
}
.mpf-wbe-column-drop-target{ outline:2px dashed var(--mpf-accent-cyan); outline-offset:4px; }

/*
  True Nested Containers -- Round N.4. Same on/off discipline as
  .mpf-wbe-column-drop-target above (added/removed only by
  cms_page.html's is_preview-gated D.12 script, dead weight on a real
  public page). Two distinct states, matching the "show clear
  invalid-drop feedback instead of silently failing" requirement:
  -target (cyan, matches every other valid-drop affordance in this
  file) for a container that would legally accept the current drag,
  and -invalid (--mpf-danger red, this file's one existing error/danger
  token -- see mpf-design-system.css) for a container the drag engine's
  client-side check has already rejected (self/cycle or depth-3) before
  ever asking the server. outline (not border/box-shadow) so nothing
  here can shift the container's own box-shadow accent from N.3's
  .mpf-wbe-element-form--container, or reflow layout, matching every
  other drop-target rule's own convention.
*/
.mpf-wbe-container-drop-target{ outline:2px dashed var(--mpf-accent-cyan); outline-offset:2px; background:rgba(6,182,212,.06); }
.mpf-wbe-container-drop-invalid{ outline:2px dashed var(--mpf-danger); outline-offset:2px; background:var(--mpf-danger-bg); cursor:not-allowed; }

/*
  Free-Position Layered Canvas (audited feature -- see
  MyPetsFiles_LayeredFreePosition_ArchitectureAudit.docx points 6, 10,
  15). The free_canvas equivalent of .mpf-wbe-el-drop-indicator above --
  a live preview of where an X/Y drag would land, sized (via inline
  width/height set in cms_page.html) to roughly match the element being
  dragged. pointer-events:none is set here from the start (unlike the
  gap identified in .mpf-wbe-el-drop-indicator, which was missing it) --
  this element must never be able to become dragover's own ev.target,
  which is exactly the bug class that made the before/after indicator's
  target calculation flip-flop. position:absolute + left/top (set
  inline, as a percentage, on every dragover tick) places it within its
  [data-cms-free-canvas] parent, which is already position:relative
  (see .mpf-cms-layout--free_canvas above).
*/
.mpf-wbe-el-free-canvas-ghost{
    position:absolute; pointer-events:none; box-sizing:border-box;
    border:2px dashed var(--mpf-accent-cyan); border-radius:6px;
    background:rgba(47,208,255,.14); z-index:9999;
}

/*
  Hotfix (post-Phase-D) -- D.12's drag-and-drop was functionally
  correct but had NO visible affordance: cursor:pointer plus a
  hover-only dashed outline gave no indication an element was
  draggable at all unless you were already hovering it, and there was
  no drag handle -- exactly the discoverability gap reported after
  testing Phase D in production. This chip is injected once per
  hoverable element by the D.12 script in cms_page.html (not by this
  CSS alone) and is purely visual/decorative -- dragging itself still
  works by grabbing the element itself, unchanged from before. Shown
  only on hover or when the element is the currently-selected one, so
  it never clutters the page, and (like every other .mpf-wbe-* rule in
  this file) is only ever attached to an element by is_preview-gated
  JS, so it can never appear on a real public page.
*/
.mpf-wbe-el-drag-handle-chip{
    position:absolute; top:-22px; left:-1px; z-index:6;
    display:none; align-items:center; gap:4px;
    background:var(--mpf-accent-cyan); color:#04202a;
    font-size:11px; font-weight:600; line-height:1; letter-spacing:.02em;
    padding:3px 7px; border-radius:4px 4px 4px 0; text-transform:capitalize;
    pointer-events:none; white-space:nowrap;
}
.mpf-wbe-el-hoverable:hover > .mpf-wbe-el-drag-handle-chip,
.mpf-wbe-preview-el-selected > .mpf-wbe-el-drag-handle-chip{ display:flex; }

/*
  Empty Layered section canvas (builder preview only). Without this, an
  empty Layered section rendered as a bare, unlabeled rectangle with no
  indication anything could be added there. Injected by the same
  is_preview-gated D.12 script, never present in public output.
*/
.mpf-wbe-empty-column-cta{
    display:block; text-align:center; padding:18px 12px; margin:4px 0;
    border:1px dashed var(--mpf-border); border-radius:8px;
    color:var(--mpf-text-secondary, #6b7785); font-size:13px; text-decoration:none;
    transition:border-color .15s ease, color .15s ease;
}
.mpf-wbe-empty-column-cta:hover{
    border-color:var(--mpf-accent-cyan); color:var(--mpf-accent-cyan);
}

/* ==========================================================================
   4. PHASE B -- RESPONSIVE EDITING WORKFLOW (Visual Builder 2.0, Phase B)
   ========================================================================== */

/*
  B.1 -- Device preview toolbar. Same hidden-radio + label technique as
  Phase A's tab bar (see section 3 above). Selecting a device only ever
  changes .mpf-wbe-canvas-frame-wrap's own max-width (the iframe inside
  it is width:100%/height:100%, so it always fills whatever width its
  wrapper currently has) -- never the iframe's src.
*/
.mpf-wbe-device-radio{
    position:absolute; opacity:0; width:0; height:0; pointer-events:none;
}
.mpf-wbe-device-toolbar{ display:flex; gap:2px; }
.mpf-wbe-device-btn{
    display:inline-block; padding:5px 12px; font-size:12px; font-weight:600;
    color:var(--mpf-text-secondary); cursor:pointer; border-radius:999px;
    border:1px solid var(--mpf-border); user-select:none;
}
.mpf-wbe-device-btn:hover{ color:var(--mpf-text-primary); }

#mpfWbeDeviceDesktop:checked ~ .mpf-wbe-pane label[for="mpfWbeDeviceDesktop"],
#mpfWbeDeviceTablet:checked ~ .mpf-wbe-pane label[for="mpfWbeDeviceTablet"],
#mpfWbeDeviceMobile:checked ~ .mpf-wbe-pane label[for="mpfWbeDeviceMobile"]{
    color:#ffffff; background:var(--mpf-accent-2); border-color:var(--mpf-accent-2);
}

/*
  B.1 continued -- the actual resize. .mpf-wbe-canvas-frame-wrap already
  exists (Phase 2); this only adds a max-width transition and the two
  device-specific caps. Desktop (the default, radio unchecked state
  included) gets no rule here at all, so it keeps today's full-width
  behavior byte-for-byte when neither Tablet nor Mobile is selected.
  768px / 390px are representative tablet/phone viewport widths, and
  intentionally land inside the SAME max-width:1024px / max-width:600px
  breakpoints hide_mobile/hide_tablet and every Phase B responsive
  override already key off of -- so what the toolbar previews is
  exactly the width the real CSS below will actually apply at.
*/
.mpf-wbe-canvas-frame-wrap{ margin:0 auto; transition:max-width .15s ease; }
#mpfWbeDeviceTablet:checked ~ .mpf-wbe-pane .mpf-wbe-canvas-frame-wrap{
    max-width:768px; border:1px solid var(--mpf-border); box-shadow:var(--mpf-shadow-sm);
}
#mpfWbeDeviceMobile:checked ~ .mpf-wbe-pane .mpf-wbe-canvas-frame-wrap{
    max-width:390px; border:1px solid var(--mpf-border); box-shadow:var(--mpf-shadow-sm);
}

/*
  B.4/B.5 -- device-gated Responsive tab blocks (per-section AND
  per-element), in _website_section_form.html. Hidden by default so a
  block only appears while its own device is the one currently selected
  in the toolbar above -- Desktop shows the existing base fields
  instead (those are untouched, plain always-visible fields, not gated
  by this rule at all).
*/
.mpf-wbe-responsive-device-block{ display:none; }
#mpfWbeDeviceTablet:checked ~ .mpf-wbe-pane .mpf-wbe-responsive-device-block[data-responsive-device="tablet"],
#mpfWbeDeviceMobile:checked ~ .mpf-wbe-pane .mpf-wbe-responsive-device-block[data-responsive-device="mobile"]{
    display:block;
}
.mpf-wbe-responsive-desktop-hint{ display:block; }
#mpfWbeDeviceTablet:checked ~ .mpf-wbe-pane .mpf-wbe-responsive-desktop-hint,
#mpfWbeDeviceMobile:checked ~ .mpf-wbe-pane .mpf-wbe-responsive-desktop-hint{
    display:none;
}
.mpf-wbe-responsive-inherit-hint{
    font-size:11.5px; color:var(--mpf-text-muted); margin:2px 0 8px; font-style:italic;
}

/* ==================================================================
   PET PROFILE SYSTEM COMPONENT STYLING FOUNDATION
   ==================================================================
   Baseline, additive-only presentation for the 4 Dynamic Widgets'
   render output (design_system/_cms_components.html:
   cms_render_pet_name/cms_render_pet_photo/cms_render_qr_code/
   cms_render_owner_contact). Before this block, none of the 7 classes
   below (.mpf-cms-system-component, .mpf-cms-pet-photo,
   .mpf-cms-pet-name, .mpf-cms-pet-qr, .mpf-cms-owner-contact,
   .mpf-cms-system-heading, .mpf-cms-system-subtitle) had a single CSS
   rule anywhere in this project -- confirmed by grepping every
   stylesheet before writing this block. A real Pet Photo would have
   rendered as a raw, unconstrained <img> at its native pixel size.

   Every selector here is scoped to exactly these 7 classes (or, for
   .mpf-cms-owner-contact's tel: link, one child selector off it) --
   nothing broader (no bare `img`, no bare heading tag, no section- or
   page-level selector). SYSTEM_COMPONENT_REGISTRY restricts
   component_key="pet_name"/"pet_photo"/"qr_code"/"owner_contact" to
   PET_PROFILE_TEMPLATE_PAGE_KEY alone (enforced at the registry AND
   the route layer, re-verified in this project's own System Health
   Test) -- so these classes can only ever render on the Pet Profile
   Template page. No other Website Builder page, and no other section
   type, is reachable by any rule below.

   THESE ARE BASELINE DEFAULTS, NOT A FINISHED DESIGN. This block
   deliberately does not: set a background or border color on
   .mpf-cms-owner-contact (the section's own Background/Overlay
   controls, already generic to every section type, are what create
   "strong visual separation" for that band -- see
   cms_render_section()); force Pet Photo into a circular/avatar crop;
   or fix a container width/alignment beyond simple responsive
   centering. All 4 components remain exactly as adjustable via the
   existing section-level Background/Overlay/Vertical Position/
   Responsive controls as any other section type -- nothing here
   competes with or overrides those.
   ================================================================== */

/*
  .mpf-cms-system-component: the shared outer <div> every one of the 4
  widgets renders into. A plain flex column, centered, with a small
  token-based gap between whatever children happen to be present
  (optional heading, the live value, optional caption/fallback text).
  Deliberately no background/border/padding/max-width here -- the
  section's own .mpf-cms-section-inner wrapper (generic, untouched)
  already provides section-level spacing; adding a second, competing
  padding/width rule here would make it harder, not easier, to use the
  existing section controls around it, which is exactly what this
  round was asked not to do.
*/
.mpf-cms-system-component{
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:var(--mpf-space-2);
    text-align:center;
}

/*
  .mpf-cms-system-heading / .mpf-cms-system-subtitle: the optional
  heading/subtitle content_json fields (pet_name, qr_code, and
  owner_contact all support "heading"; owner_contact alone also
  supports "subtitle" -- see SYSTEM_COMPONENT_REGISTRY's
  presentation_fields). Both render as plain <h2>/<p> tags, which
  already inherit this project's real heading/paragraph rules by
  element type -- .mpf-cms-section-inner h2 (font-size:
  var(--mpf-text-h2), this same file, PHASE 6B block above) and
  body[data-mpf-root] p (color:var(--mpf-text-secondary),
  mpf-design-system.css) already apply here with zero new rules
  needed. These two class-scoped rules add only spacing/alignment on
  top of that inherited typography -- no font-size, weight, or color
  is redeclared here, per this round's instruction to reuse existing
  design-system/Global Style tokens rather than introduce unrelated
  hard-coded typography.
*/
.mpf-cms-system-heading{
    margin:0;
}
.mpf-cms-system-subtitle{
    margin:0;
}

/*
  .mpf-cms-pet-name: the pet's nickname/animal_name line -- the one
  piece of text on the whole page that most needs to read as the
  clear, primary identity of who this profile belongs to. Sized and
  weighted like this project's own existing "big stat value" pattern
  (.mpf-widget-value, mpf-design-system.css: font-size:
  var(--mpf-text-h1), weight var(--mpf-weight-bold),
  color var(--mpf-text-primary), line-height 1.1) rather than
  inventing a new scale -- same tokens, same visual weight class
  already established elsewhere in this design system. The secondary
  designation/breed caption line the same macro renders right below it
  (cms_render_pet_name(), _cms_components.html) already uses
  .mpf-widget-caption, which mpf-design-system.css already styles
  (font-size:var(--mpf-text-caption), color:var(--mpf-text-muted)) --
  that existing rule is what actually produces the "clear hierarchy
  between the pet's name and its secondary information": nothing new
  needed for the secondary line at all, only the primary name line
  needed a rule here.
*/
.mpf-cms-pet-name{
    font-size:var(--mpf-text-h1);
    font-weight:var(--mpf-weight-bold);
    line-height:1.15;
    color:var(--mpf-text-primary);
    margin:0;
}

/*
  .mpf-cms-pet-photo: responsive by construction (width:100% of
  whatever container it's in, capped by max-width so it never renders
  at a real uploaded photo's raw native size) and cannot overflow its
  container at any viewport, including mobile, because it is never
  wider than 100% of its parent. height:auto preserves the photo's own
  natural aspect ratio in the ordinary case (no forced crop), while
  max-height is a safety bound for an unusually tall/narrow source
  photo; object-fit:contain is what makes that bound safe -- if
  max-height ever actually constrains a photo before max-width does,
  contain guarantees the full photo still displays, never cropped and
  never stretched/distorted, just letterboxed. This is deliberately
  NOT object-fit:cover with a fixed aspect-ratio box (which would
  crop part of every photo to force one exact shape) and deliberately
  NOT border-radius:50% (no circular/avatar crop, per this round's
  explicit instruction) -- var(--mpf-radius-lg) gives a tasteful
  rounded-rectangle corner treatment instead, the same radius token
  scale (not a new arbitrary value) already used for cards/panels
  elsewhere in this design system.
*/
.mpf-cms-pet-photo{
    display:block;
    width:100%;
    max-width:420px;
    height:auto;
    max-height:420px;
    margin:0 auto;
    object-fit:contain;
    border-radius:var(--mpf-radius-lg);
}

/*
  .mpf-cms-pet-qr: unlike a photo, a QR code's shape is not a design
  choice -- it must stay square or it may not scan correctly.
  aspect-ratio:1/1 enforces that unconditionally regardless of the
  underlying asset's own dimensions (both the real recovery_qr_file()
  output and the synthetic_preview_qr_sample.png asset are already
  square PNGs, so this is a safety guarantee, not a visible change to
  either today). width:100% with a max-width keeps it responsive and
  non-overflowing at mobile widths, same pattern as the photo above;
  object-fit:contain guarantees the whole code is always visible,
  centered, and never stretched/distorted -- distortion on a QR code
  is a functional risk (scan failure), not just a visual one, so
  "never distort" matters even more here than for the photo.
  border-radius uses the smaller --mpf-radius-sm token -- enough to
  soften the corners without rounding into the code's own finder
  patterns.
*/
.mpf-cms-pet-qr{
    display:block;
    width:100%;
    max-width:220px;
    aspect-ratio:1/1;
    margin:0 auto;
    object-fit:contain;
    border-radius:var(--mpf-radius-sm);
}

/*
  .mpf-cms-owner-contact: the emergency-contact-name/phone text line.
  Deliberately no background, border, or accent color declared here --
  per this round's explicit instruction, "urgent/different" visual
  treatment for this band is the section's own Background/Overlay
  controls' job (already generic to every section type), not something
  this component should hard-code into itself. What this rule DOES do
  is make the text itself clean and readable: token-based body-size
  typography (matching this project's existing body[data-mpf-root] p
  rule's color, explicitly restated here only because this text sits
  inside a <p> that could be given a component-specific background by
  an admin later, so it should stay legible against either the default
  surface or a future accent background rather than silently
  inheriting whatever .mpf-cms-bg-color--light's text-color override
  happens to produce) and a small margin-top so it doesn't crowd
  directly against an optional heading/subtitle above it. The phone
  number's existing tel: link (cms_render_owner_contact() already
  emits <a href="tel:...">) gets the same accent-cyan color already
  used for the rest of this project's inline links/focus states
  (mpf-design-system.css's --mpf-accent-cyan), so it visibly reads as
  a tappable link -- important for the mobile "one-tap call the owner"
  use case -- without introducing a new color token.
*/
.mpf-cms-owner-contact{
    font-size:var(--mpf-text-body);
    line-height:1.5;
    margin:var(--mpf-space-2) 0 0;
}
.mpf-cms-owner-contact a{
    color:var(--mpf-accent-cyan);
    font-weight:var(--mpf-weight-medium);
    text-decoration:none;
}
.mpf-cms-owner-contact a:hover,
.mpf-cms-owner-contact a:focus-visible{
    text-decoration:underline;
}

/*
  .mpf-cms-pet-id-card: Pet ID Card integration round. The generated
  card image (card_templates/make_pet_card.py's real templates are
  ~1537x1023px, a landscape ~3:2 shape) is styled with the exact same
  responsive/non-distorting pattern as .mpf-cms-pet-photo above --
  width:100% of its container capped by max-width, height:auto,
  object-fit:contain as a safety bound, var(--mpf-radius-lg) for the
  same tasteful rounded-corner treatment already used for the photo.
  A slightly wider max-width than the photo (560px vs 420px) reflects
  the card's own landscape proportions so it doesn't render tiny next
  to Pet Photo when the two sit in adjacent columns; this is a
  cosmetic default only -- the existing generic element Width control
  (see cms_render_element()'s style classes) still overrides it exactly
  like it already does for Pet Photo, System Component styling was
  never meant to be the final word on sizing.
*/
.mpf-cms-pet-id-card{
    display:block;
    width:100%;
    max-width:560px;
    height:auto;
    max-height:420px;
    margin:0 auto;
    object-fit:contain;
    border-radius:var(--mpf-radius-lg);
}

