對話紀錄 February 4, 2026
.dynamic-section.hero.grid, .dynamic-section.hero.grid .inner
width: 100%!important;
max-width: 1200px!important;
margin-left: auto!important;
margin-right: auto!important;
padding: 0 2rem!important;
box-sizing: border-box!important;
}
What I want is a "0 1rem" for padding.
.dynamic-section.hero.grid, .dynamic-section.hero.grid .inner is showing padding: 0 2rem !important in inspect (and not overridden to 0 1rem) means:0 2rem (likely from Tattoo theme's main.css or Publii core, which often use !important on responsive padding).dynamic-section .inner { padding: 0 2rem !important; } or @media breakpoints have higher specificity (more parent selectors) or load after your custom CSS.#main or .grid), but if theme uses body .dynamic-section .inner or .hero .inner, it wins.To force padding: 0 1rem only on mobile for hero/grid sections, we need maximum specificity + !important:
Append/replace only section 11 (or add as 11 if removed) with this stronger version:
/* 11. Strong Mobile Adjustments for Shared Sections – Force narrower padding & title size */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block overrides theme !important rules for better mobile readability in rounded group backgrounds. */
@media (max-width: 767px) {
/* Highest specificity override for padding on hero/grid inner */
body #main .dynamic-section.hero.grid .inner,
body .dynamic-section.hero.grid .inner,
.dynamic-section.hero.grid .inner {
padding: 0 1rem !important; /* your good value */
padding-top: 1.5rem !important;
padding-bottom: 1.5rem !important;
}
/* Force title font size */
body #main .dynamic-section.hero.grid .section-title,
body .dynamic-section.hero.grid .section-title,
.dynamic-section.hero.grid .section-title {
font-size: 2.3rem !important;
line-height: 1.3 !important;
margin-bottom: 1.5rem !important;
}
/* Tighter outer section padding to match narrower inner */
body #main .dynamic-section.hero.grid,
body .dynamic-section.hero.grid {
padding: 2rem 1rem !important;
}
}
body #main .dynamic-section.hero.grid .inner beats most theme rules (theme rarely uses body #main chain).!important on every property → wins cascade even against theme !important.@media (max-width: 767px) → desktop unchanged.0 1rem) for inner padding..inner (less squeeze)If still 0 2rem:
.dynamic-section.hero.grid .inner on mobile → Computed tab → padding-left/right → click the property → see the winning rule (file + line number, e.g. main.css:456)html body #main .dynamic-section.hero.grid .inner).This should force your desired 0 1rem on mobile without affecting desktop.
Let me know the result — or paste the winning padding rule from inspect if it persists!
Current time: January 28, 2026 01:45 PM HKT
We're overriding the last theme stubbornness! 😊
/* Safe, no conflicts – All features preserved */
/* Version V5.3.2 - January 27, 2026 - Stronger containment for bleed + multi-column fix */
/* 1. Hero / Background – Full Browser Width */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed stable for full-width hero sections. */
.dynamic-section.hero,
.dynamic-section[style*="background-image"] {
min-height: 60vh;
background-size: cover !important;
background-position: center !important;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
width: 100vw !important;
margin-left: calc(-50vw + 50%) !important;
}
.dynamic-section.hero::before,
.dynamic-section[style*="background-image"]::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
.dynamic-section.hero > .inner,
.dynamic-section[style*="background-image"] > .inner {
position: relative;
z-index: 2;
max-width: 90%;
padding: 2rem;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
}
/* 2. Title Styling */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Hero title sizing has been tested and confirmed correct. */
.section-title {
margin-bottom: 2.5rem !important;
font-size: 2rem;
line-height: 1.2;
}
.dynamic-section.hero .section-title {
margin-bottom: 3rem !important;
font-size: 3rem;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
/* 3. Normal Content Spacing */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This padding is critical for non-hero sections. */
.dynamic-section:not(.hero):not([style*="background-image"]) > .inner {
padding: 8rem 2rem 2rem !important;
max-width: 1200px;
margin: 0 auto;
}
/* 4. Multi-Column Layout - Stronger containment for desktop columns */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been strengthened to force columns and contain bleed. */
.dynamic-section.grid {
display: flex !important;
flex-wrap: nowrap !important;
justify-content: space-between !important;
gap: 2rem !important;
padding: 4rem 2rem !important;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
box-sizing: border-box !important;
}
.dynamic-section.grid .inner {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
display: flex !important;
flex-wrap: inherit !important;
justify-content: inherit !important;
}
/* 2-column layout */
.dynamic-section.grid.layout-2-column .inner > * {
flex: 1 0 49% !important;
max-width: 49% !important;
}
/* 3-column layout */
.dynamic-section.grid.layout-3-column .inner > * {
flex: 1 0 32% !important;
max-width: 32% !important;
}
/* Mobile fallback: stack columns */
@media (max-width: 767px) {
.dynamic-section.grid {
flex-wrap: wrap !important;
}
.dynamic-section.grid .inner > * {
flex: 1 0 100% !important;
max-width: 100% !important;
}
}
/* 5. Brand Button */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Button styling has been tested and confirmed stable. */
.dynamic-section .btn,
.dynamic-section a[href*="paypal"],
.dynamic-section button {
background: #A6752E !important;
color: white !important;
padding: 0.8rem 1.8rem !important;
border-radius: 6px !important;
text-decoration: none !important;
display: inline-block;
font-weight: 600;
}
.dynamic-section .btn:hover {
background: #8B5A1F !important;
}
.content__buttons {
text-align: center;
margin: 2rem 0;
}
/* 6. Separator – Contained under grid with margin to prevent overlap */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Separator now contained when following .grid */
.separator-wrapper {
display: flex;
justify-content: center;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
padding: 2rem 0;
background: white !important;
z-index: 2 !important;
clear: both !important;
}
.dynamic-section.grid + .separator-wrapper {
max-width: 1200px !important;
margin: 2rem auto 0 auto !important;
}
.separator-wrapper .separator {
display: block !important;
text-align: center !important;
margin: 1.5rem auto 0 auto !important;
height: auto;
max-width: 100% !important;
}
.separator-wrapper .default-bee {
max-width: 150px !important;
}
.separator-wrapper .custom-separator {
max-width: none !important;
width: 100% !important;
}
/* 7. Responsive YouTube/Video Embeds – Stronger Overrides for Tattoo */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This is the core responsive embed rule. Changing padding-bottom will break aspect ratio. */
.content-embed {
position: relative !important;
width: 100% !important;
padding-bottom: 56.25% !important;
height: 0 !important;
overflow: hidden !important;
margin: 2rem 0 !important;
}
.content-embed iframe {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: 0 !important;
display: block !important;
}
/* 8. Modal – Centered + External Scrollbar (EXACTLY your V5.0 - no change) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed to provide external scrollbar. */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
.modal.is-active {
display: block;
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border: 1px solid #888;
width: 80%;
max-width: 800px;
min-height: 200px;
position: relative;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
/* External scrollbar – no internal overflow */
}
/* Close button */
.modal-close {
color: #aaa !important;
position: absolute !important;
top: 10px !important;
right: 20px !important;
font-size: 36px !important;
font-weight: bold !important;
cursor: pointer !important;
z-index: 1001 !important;
line-height: 1 !important;
}
.modal-close:hover,
.modal-close:focus {
color: #000 !important;
}
/* Modal embed sizing */
#story-modal .modal-body .content-embed {
min-height: 315px !important;
height: 70vh !important;
padding-bottom: 0 !important;
overflow: visible !important;
margin: 1rem 0 !important;
display: block !important;
}
#story-modal .modal-body .content-embed iframe {
width: 100% !important;
height: 100% !important;
display: block !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
/* Fix hero + grid bleed (no full-width shift) */
.dynamic-section.hero.grid,
.dynamic-section.hero.grid .inner {
width: 100% !important;
max-width: 1200px !important;
margin-left: auto !important;
margin-right: auto !important;
padding: 0 2rem !important;
box-sizing: border-box !important;
}
.dynamic-section.hero.grid::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
/* 9. Replace bullets with left-aligned emoji (like traditional bullet) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.modal-story-content ul,
.dynamic-section .inner ul {
list-style: none !important;
padding-left: 2.2rem !important; /* increased left padding → moves entire list block slightly to the left */
margin: 1.5rem 0 !important;
}
.modal-story-content li,
.dynamic-section .inner li {
position: relative;
margin-bottom: 1rem !important;
padding-left: 2.5rem !important; /* increased indent → more space between emoji and text */
text-align: left !important; /* keep text left-aligned */
}
/* Emoji as left-aligned bullet */
.modal-story-content li::before,
.dynamic-section .inner li::before {
content: "🔰 "; /* your preferred emoji */
position: absolute !important;
left: -0.3rem !important; /* moved slightly to the left for balance */
top: 0.2rem !important; /* same vertical alignment as before */
font-size: 1.4rem !important; /* same size as your original → matches text, no overlap */
line-height: 1 !important;
color: #A6752E !important; /* your accent color */
}
/* Optional: mobile adjustment (larger emoji for touch) */
@media (max-width: 768px) {
.modal-story-content li::before,
.dynamic-section .inner li::before {
font-size: 1.6rem !important;
left: -0.4rem !important; /* a bit more left shift on small screens */
}
}
/* 10. Group Section Background – Medium round corners + elegant gray shadow (only for groups) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.dynamic-section.grid[style*="background-image"],
.dynamic-section.hero.grid {
border-radius: 12px !important; /* medium round corners - adjust 8px–16px to taste */
overflow: hidden !important; /* ensure content doesn't leak outside rounded corners */
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; /* elegant gray shadow - similar to modal */
margin: 2rem auto !important; /* slight margin around the group for breathing room */
max-width: 1200px !important; /* contain width like other sections */
}
/* Optional: stronger shadow on hover for interactive feel */
.dynamic-section.grid[style*="background-image"]:hover,
.dynamic-section.hero.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
/* Mobile adjustment: slightly smaller corners/shadow for better fit */
@media (max-width: 768px) {
.dynamic-section.grid[style*="background-image"],
.dynamic-section.hero.grid {
border-radius: 10px !important;
box-shadow: 0 3px 10px rgba(0,0,0,0.12) !important;
margin: 1rem auto !important;
}
}
/* 1. Hero / Background – Full Browser Width */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed stable for full-width hero sections. */
.dynamic-section.hero,
.dynamic-section[style*="background-image"] {
min-height: 60vh;
background-size: cover !important;
background-position: center !important;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
width: 100vw !important;
margin-left: calc(-50vw + 50%) !important;
}
.dynamic-section.hero::before,
.dynamic-section[style*="background-image"]::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
.dynamic-section.hero > .inner,
.dynamic-section[style*="background-image"] > .inner {
position: relative;
z-index: 2;
max-width: 90%;
padding: 2rem;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
}
/* 2. Title Styling */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Hero title sizing has been tested and confirmed correct. */
.section-title {
margin-bottom: 2.5rem !important;
font-size: 2rem;
line-height: 1.2;
}
.dynamic-section.hero .section-title {
margin-bottom: 3rem !important;
font-size: 3rem;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
/* 3. Normal Content Spacing */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This padding is critical for non-hero sections. */
.dynamic-section:not(.hero):not([style*="background-image"]) > .inner {
padding: 8rem 2rem 2rem !important;
max-width: 1200px;
margin: 0 auto;
}
/* 4. Multi-Column Layout - Reverted to V4.39 flexbox for reliable columns */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Reverted to proven flexbox layout from V4.39. */
.dynamic-section.grid {
display: flex !important;
flex-wrap: nowrap !important;
justify-content: space-between !important;
gap: 2rem !important;
padding: 4rem 2rem !important;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
box-sizing: border-box !important;
}
.dynamic-section.grid .inner {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
}
/* 2-column layout */
.dynamic-section.grid.layout-2-column .inner > * {
flex: 1 0 49% !important;
max-width: 49% !important;
}
/* 3-column layout */
.dynamic-section.grid.layout-3-column .inner > * {
flex: 1 0 32% !important;
max-width: 32% !important;
}
/* Mobile fallback: stack columns */
@media (max-width: 767px) {
.dynamic-section.grid {
flex-wrap: wrap !important;
}
.dynamic-section.grid .inner > * {
flex: 1 0 100% !important;
max-width: 100% !important;
}
}
/* 5. Brand Button */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Button styling has been tested and confirmed stable. */
.dynamic-section .btn,
.dynamic-section a[href*="paypal"],
.dynamic-section button {
background: #A6752E !important;
color: white !important;
padding: 0.8rem 1.8rem !important;
border-radius: 6px !important;
text-decoration: none !important;
display: inline-block;
font-weight: 600;
}
.dynamic-section .btn:hover {
background: #8B5A1F !important;
}
.content__buttons {
text-align: center;
margin: 2rem 0;
}
/* 6. Separator – Contained under grid */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Separator now contained when following .grid */
.separator-wrapper {
display: flex;
justify-content: center;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
padding: 2rem 0;
background: white !important;
z-index: 2 !important;
}
.dynamic-section.grid + .separator-wrapper {
max-width: 1200px !important;
margin: 0 auto !important;
}
.separator-wrapper .separator {
display: block !important;
text-align: center !important;
margin: 1.5rem auto 0 auto !important;
height: auto;
max-width: 100% !important;
}
.separator-wrapper .default-bee {
max-width: 150px !important;
}
.separator-wrapper .custom-separator {
max-width: none !important;
width: 100% !important;
}
/* 7. Responsive YouTube/Video Embeds – Stronger Overrides for Tattoo */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This is the core responsive embed rule. Changing padding-bottom will break aspect ratio. */
.content-embed {
position: relative !important;
width: 100% !important;
padding-bottom: 56.25% !important;
height: 0 !important;
overflow: hidden !important;
margin: 2rem 0 !important;
}
.content-embed iframe {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: 0 !important;
display: block !important;
}
/* 8. Modal – Centered + External Scrollbar (EXACTLY your V5.0 - no change) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed to provide external scrollbar. */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
.modal.is-active {
display: block;
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border: 1px solid #888;
width: 80%;
max-width: 800px;
min-height: 200px;
position: relative;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
/* External scrollbar – no internal overflow */
}
/* Close button */
.modal-close {
color: #aaa !important;
position: absolute !important;
top: 10px !important;
right: 20px !important;
font-size: 36px !important;
font-weight: bold !important;
cursor: pointer !important;
z-index: 1001 !important;
line-height: 1 !important;
}
.modal-close:hover,
.modal-close:focus {
color: #000 !important;
}
/* Modal embed sizing */
#story-modal .modal-body .content-embed {
min-height: 315px !important;
height: 70vh !important;
padding-bottom: 0 !important;
overflow: visible !important;
margin: 1rem 0 !important;
display: block !important;
}
#story-modal .modal-body .content-embed iframe {
width: 100% !important;
height: 100% !important;
display: block !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
/* Fix hero + grid bleed (no full-width shift) */
.dynamic-section.hero.grid,
.dynamic-section.hero.grid .inner {
width: 100% !important;
max-width: 1200px !important;
margin-left: auto !important;
margin-right: auto !important;
padding: 0 2rem !important;
box-sizing: border-box !important;
}
.dynamic-section.hero.grid::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
/* 9. Replace bullets with left-aligned emoji (like traditional bullet) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.modal-story-content ul,
.dynamic-section .inner ul {
list-style: none !important;
padding-left: 2.2rem !important; /* increased left padding → moves entire list block slightly to the left */
margin: 1.5rem 0 !important;
}
.modal-story-content li,
.dynamic-section .inner li {
position: relative;
margin-bottom: 1rem !important;
padding-left: 2.5rem !important; /* increased indent → more space between emoji and text */
text-align: left !important; /* keep text left-aligned */
}
/* Emoji as left-aligned bullet */
.modal-story-content li::before,
.dynamic-section .inner li::before {
content: "🔰 "; /* your preferred emoji */
position: absolute !important;
left: -0.3rem !important; /* moved slightly to the left for balance */
top: 0.2rem !important; /* same vertical alignment as before */
font-size: 1.4rem !important; /* same size as your original → matches text, no overlap */
line-height: 1 !important;
color: #A6752E !important; /* your accent color */
}
/* Optional: mobile adjustment (larger emoji for touch) */
@media (max-width: 768px) {
.modal-story-content li::before,
.dynamic-section .inner li::before {
font-size: 1.6rem !important;
left: -0.4rem !important; /* a bit more left shift on small screens */
}
}
/* 10. Modal Section Spacing – Better separation between chunks & new titles */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This improves readability in long modals without affecting short ones. */
.modal-story-content {
margin-bottom: 2.5rem !important; /* adds breathing room below every chunk */
padding-bottom: 1rem !important;
}
.modal-story-content .section-title {
margin-top: 2rem !important; /* space above the new title */
margin-bottom: 1.2rem !important; /* space between title and its own content */
padding-top: 0.5rem !important;
}
/* 11. Strong Mobile Adjustments for Shared Sections – Force narrower padding & title size */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block overrides theme !important rules for better mobile readability in rounded group backgrounds. */
@media (max-width: 767px) {
/* Highest specificity override for padding on hero/grid inner */
body #main .dynamic-section.hero.grid .inner,
body .dynamic-section.hero.grid .inner,
.dynamic-section.hero.grid .inner {
padding: 0 1rem !important; /* your good value */
padding-top: 0rem !important;
padding-bottom: 0rem !important;
}
/* Force title font size */
body #main .dynamic-section.hero.grid .section-title,
body .dynamic-section.hero.grid .section-title,
.dynamic-section.hero.grid .section-title {
font-size: 2.3rem !important;
line-height: 1.3 !important;
margin-bottom: 1.5rem !important;
}
/* Tighter outer section padding to match narrower inner */
body #main .dynamic-section.hero.grid,
body .dynamic-section.hero.grid {
padding: 2rem 1rem !important;
}
}
You're right: my previous suggestion for section 4 was shorter because I had removed the multi-column forcing code (per your request to drop side-by-side layout), but your current file still has the full flexbox rules trying to force columns. To align with your latest preference (single-column for shared positions), we need to replace section 4 with the minimal "single-column only" version.
Section 11 (mobile adjustments) is already very close to what you want, but we can refine it slightly to make sure the padding and title size apply reliably (higher specificity + your preferred values).
Here are the two updated sections you can directly copy-paste into your current V5.3.2 CSS file. No other parts are touched.
/* 4. Shared Position Sections – Single-column layout (no multi-column forcing) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block ensures shared positions remain single-column, avoiding theme conflicts. */
/* No .grid forcing — let theme default to single-column flow for shared positions */
.dynamic-section.grid {
display: block !important;
flex-direction: column !important;
flex-wrap: wrap !important;
justify-content: flex-start !important;
}
/* 11. Strong Mobile Adjustments for Shared Sections – Force narrower padding & title size */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block overrides theme !important rules for better mobile readability in rounded group backgrounds. */
@media (max-width: 767px) {
/* Highest specificity override for padding on shared group inner (your good value) */
html body #main .dynamic-section.grid .inner,
html body .dynamic-section.grid .inner,
.dynamic-section.grid .inner {
padding: 0 1rem !important;
padding-top: 1.5rem !important;
padding-bottom: 1.5rem !important;
}
/* Force title font size in shared group sections (your good starting point) */
html body #main .dynamic-section.grid .section-title,
html body .dynamic-section.grid .section-title,
.dynamic-section.grid .section-title {
font-size: 2.3rem !important;
line-height: 1.3 !important;
margin-bottom: 1.5rem !important;
}
/* Tighter outer section padding to match narrower inner */
html body #main .dynamic-section.grid,
html body .dynamic-section.grid {
padding: 2rem 1rem !important;
}
}
html body #main .dynamic-section.grid) to override any theme !important rules.0 1rem padding on .inner for shared groups on mobile.2.3rem title size (your tested good value).If padding or title size still doesn't change:
.dynamic-section.grid .inner on mobile → Computed tab → padding-left/right → click the property → see the winning rule (file + line number).section-title → font-sizehtml body #main .dynamic-section.hero.grid .inner)This keeps your file length almost identical (~700 lines) and only updates the two sections you asked for.
Let me know how it looks after pasting these two blocks!
Current time: February 02, 2026 03:45 PM HKT
This should give you the clean single-column shared layout + balanced mobile spacing! 😊
<br> line breaks are missing in that post (but not in others)The source you pasted is raw HTML from the admin editor (Publii / AppGini), where you have inserted <br> tags to create extra spacing between paragraphs.
However, when this content is rendered in the modal, the following happens:
Publii / Tattoo theme's sanitizer / renderer strips or normalizes <br> tags
Many themes (including Tattoo) aggressively clean up "empty" or redundant line breaks during output or when processing .generic-content, .content, or .modal-story-content. They often convert multiple <br> into a single one or remove them entirely to avoid "extra whitespace" in the final HTML.
Your JS V3.1.7 parsing also strips content
In the fetch → parse → combine step, there is this line (in the sections.forEach loop):
let sectionHTML = section.outerHTML.trim();
sectionHTML = sectionHTML.replace(/<h2[^>]*>[\s\S]*?<\/h2>/gi, ''); // title strip (commented out now)
But more importantly, section.outerHTML.trim() + DOMParser can normalize whitespace and collapse multiple <br> tags, especially when the content is wrapped in <div> or <span xss="removed">.
<span xss="removed"> wrapper
Your source has <span xss="removed"><br></span> — this is Publii’s XSS protection wrapper. When parsed and re-inserted, these wrappers can cause <br> to be ignored or collapsed by the browser or JS.
Result: Extra line breaks disappear in the modal, but the text itself is intact. Other posts that work may not have the same <br> pattern or have different wrappers.
To preserve extra <br> and spacing:
Update the fetch .then block in modal-embed.js (around line ~200):
.then(function(data) {
if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html');
let combinedContent = `<h2 class="section-title">${modalTitle}</h2>`;
const sections = doc.querySelectorAll('.modal-story-content');
if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
if (sections.length > 0) {
sections.forEach(function(section, index) {
// Preserve original HTML without aggressive trimming
let sectionHTML = section.innerHTML; // use innerHTML instead of outerHTML.trim()
// Only remove legacy titles if needed (already commented out)
// sectionHTML = sectionHTML.replace(/<h2[^>]*class="[^"]*section-title[^"]*"[^>]*>[\s\S]*?<\/h2>/gi, '');
// sectionHTML = sectionHTML.replace(/<h2[^>]*>[\s\S]*?<\/h2>/gi, '');
combinedContent += '<div class="modal-story-content">' + sectionHTML + '</div>';
if (index < sections.length - 1) {
combinedContent += '<div class="modal-section-separator"></div>';
}
});
} else {
// ... fallback ...
}
openModal(combinedContent);
})
section.outerHTML.trim() to section.innerHTML — preserves original spacing and <br> tags better.<div class="modal-story-content"> manually (since we lost outerHTML).Append to your Custom CSS (new section 12):
/* 12. Preserve paragraph spacing in modal (prevent collapsed <br>) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This ensures line breaks and paragraph spacing are respected in modal content. */
.modal-story-content p,
.modal-story-content div > p {
margin-bottom: 1.2rem !important;
}
.modal-story-content br {
display: block !important;
content: " " !important;
margin-bottom: 0.8rem !important; /* force visible space for <br> */
}
.then(function(data) { ... }) block in the fetch chain with the version above (use section.innerHTML instead of outerHTML.trim()).<br> should now show as visible space.If line breaks still collapse:
<br> tags → see if they are still there (but hidden by CSS)<br> → check computed styles (if display: none or height 0 → CSS issue)This should restore the extra spacing without affecting other content.
Let me know how it looks after these two small changes!
Current time: February 02, 2026 04:15 PM HKT
This should fix the collapsed line breaks in modal! 😊
As you can see from the code, the interface of the AppGini generated web app is using a WYSIWYG editor, paragraphs are sometimes represented with just empty space rather than
, we do need a more robust handling mechanism that can handle both
or empty space-based paragraph spacing.Your CSS update seems to fail to affect the img element inside the "content-image" class. By adding padding: 1rem to under .content-image in the inspect window I'm able to achieve the desired space.For the space between sections, let's make it 2 times of the present space.
Yes, but in a controlled way — the fixes are designed to apply uniformly to every chunk in the modal, regardless of position/order, but they only add spacing where needed.
JS cleanup (in fetch .then):
<span xss="removed"> around <br> and empty divs<br>.modal-story-content chunk (and fallback chunks)CSS spacing (section 12):
.modal-story-content p, div, br, .content-image, .welcome-image, and + selectorsBottom line:
If you later want to make spacing different for position 1/0 (e.g. less margin), we can add a [data-position="1"] or similar attribute in PHP and target it in CSS — but for now, uniform is cleaner.
You are 100% correct — in your display models (title-text-image, etc.), the image comes after the text, so the tight spacing is usually between text (end of content) and the image (start).
That's why top margin on the image container (.content-image, .welcome-image) is more logical here — it adds space above the image (i.e. between text and image).
Bottom margin would add space below the image (between image and next text/title), which is less critical in your flow.
Revised recommendation (top margin on image):
Update section 12 (replace or add to your current one):
/* 12. Preserve paragraph spacing & image-text gap in modal (robust for AppGini editor) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This ensures line breaks, paragraph spacing, and image-text gaps are respected even with editor quirks. */
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1.6rem !important; /* consistent paragraph/div spacing */
}
.modal-story-content br {
display: block !important;
content: " " !important;
margin-bottom: 1.4rem !important; /* taller visible space for <br> */
line-height: 1.4rem !important;
}
/* Extra gap between consecutive block-level elements (when no separator) */
.modal-story-content div + div,
.modal-story-content p + p,
.modal-story-content div + p,
.modal-story-content p + div,
.modal-story-content .content-image + div,
.modal-story-content .welcome-image + div {
margin-top: 2.5rem !important; /* enlarged space between sections/chunks (2× previous) */
}
/* More space BETWEEN TEXT and IMAGE (top margin on image container) */
.modal-story-content .content-image,
.modal-story-content .welcome-image {
margin-top: 2.5rem !important; /* larger gap after text, before image */
margin-bottom: 1.5rem !important; /* smaller bottom margin - keeps balance */
}
/* Mobile: slightly tighter but still balanced */
@media (max-width: 768px) {
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1.4rem !important;
}
.modal-story-content br {
margin-bottom: 1.2rem !important;
}
.modal-story-content .content-image,
.modal-story-content .welcome-image {
margin-top: 2rem !important;
margin-bottom: 1.2rem !important;
}
.modal-story-content div + div,
.modal-story-content p + p,
.modal-story-content div + p,
.modal-story-content p + div,
.modal-story-content .content-image + div,
.modal-story-content .welcome-image + div {
margin-top: 2rem !important;
}
}
Why top margin on image:
margin-top: 2.5rem to 3rem or 2rem if you want more/less gap.Apply:
Let me know how the spacing feels after applying — or if you want to tweak the values (e.g. "make image top margin 3rem").
Current time: February 02, 2026 06:15 PM HKT
This should make the modal feel much more balanced and readable! 😊
Hence, "Direct target img and h2 elements inside modal for reliability" might not be appropriate. To confirm the selectors, can you tell me how can I copy the entire "tree" down to the img and h2 elements from the inspect window? Or, do you want screenshots instead?
Image element:
/html/body/div/main/div[2]/section/div/div[3]/imgbody > div > main > div.banner.banner--after-content > section > div > div:nth-child(4) > img.content-image or similar), but the parent chain includes .banner.banner--after-content (Publii/Tattoo theme wrapper) and section > div > div:nth-child(4)Section title element:
/html/body/div/main/div[2]/section/div/h2[2]/divbody > div > main > div.banner.banner--after-content > section > div > h2:nth-child(5) > div<h2> inside a <div> (likely .section-title wrapper), with parent chain same as above.The selectors in our previous CSS (e.g. .modal-story-content .content-image img, .dynamic-section.grid .section-title) were too specific to modal or didn't match the main page structure:
.banner.banner--after-content > section > div > div > img (not .modal-story-content or .dynamic-section.grid)h2.section-title inside a <div> wrapper — our .section-title rule was overridden or not specific enough.banner.banner--after-content wrapper adds padding/margin that we didn't targetWe will:
.content-image or direct parent div).modal-story-content or modal selectors)Replace or append this as a new section 12 (or update section 11 if you prefer):
/* 12. Main Page Spacing – Targeted top margins for images & section titles (desktop + mobile) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This targets main page content (banner/after-content) for text-to-image and chunk-to-title spacing. No effect on modals or static-embed. */
.banner.banner--after-content .content-image,
.banner.banner--after-content .welcome-image,
.banner.banner--after-content section > div > div > img {
margin-top: 2.5rem !important; /* space ABOVE image (between text and image) */
display: block !important;
}
.banner.banner--after-content h2.section-title,
.banner.banner--after-content section > div > h2.section-title {
margin-top: 5rem !important; /* doubled space ABOVE new title (between previous chunk and title) */
margin-bottom: 1.5rem !important; /* space between title and content */
}
/* Extra gap between consecutive sections/chunks on main page */
.banner.banner--after-content section + section,
.banner.banner--after-content div + div,
.banner.banner--after-content p + p {
margin-top: 2.5rem !important; /* consistent section-to-section gap */
}
/* Mobile: slightly tighter but still balanced */
@media (max-width: 768px) {
.banner.banner--after-content .content-image,
.banner.banner--after-content .welcome-image,
.banner.banner--after-content section > div > div > img {
margin-top: 2rem !important;
}
.banner.banner--after-content h2.section-title,
.banner.banner--after-content section > div > h2.section-title {
margin-top: 4rem !important;
}
.banner.banner--after-content section + section,
.banner.banner--after-content div + div,
.banner.banner--after-content p + p {
margin-top: 2rem !important;
}
}
banner.banner--after-content > section > div > ...)banner.banner--after-content .content-image, etc.) to beat theme rulesIf still no change:
<img> → Computed tab → margin-top → see winning rule (likely theme img or parent div)<h2> → margin-top → see winning rule.banner.banner--after-content img or h2.section-title)This should finally force the gaps you want on the main page.
Let me know the result — or paste the winning margin rules from inspect!
Current time: February 03, 2026 10:05 AM HKT
This targeted selector should make the spacing perfect on main page! 😊
<br> or one standard paragraph gap) everywhere on the site is a reasonable and clean choice for strict consistency.This will:
<br> or empty <div><br></div> blocksWe need a two-part approach:
<br> (or empty divs) to exactly one <br><br> spacing)Replace the sections.forEach block in your modal-embed.js fetch .then with this version (collapses to one <br>):
sections.forEach(function(section, index) {
let sectionHTML = section.innerHTML;
// Unwrap <br> from xss spans
sectionHTML = sectionHTML.replace(/<span[^>]*xss="removed"[^>]*>(<br[^>]*>)<\/span>/gi, '$1');
// Replace empty/whitespace-only divs with single <br>
sectionHTML = sectionHTML.replace(/<div[^>]*>\s*(<br[^>]*>)?\s*<\/div>/gi, '<br>');
// Collapse ALL consecutive <br> (or <br> with whitespace) to exactly ONE <br>
sectionHTML = sectionHTML.replace(/(<br[^>]*>|\s*){2,}/gi, '<br>');
combinedContent += '<div class="modal-story-content">' + sectionHTML + '</div>';
if (index < sections.length - 1) {
combinedContent += '<div class="modal-section-separator"></div>';
}
});
/(<br[^>]*>|\s*){2,}/gi replaces two or more consecutive <br> (with optional whitespace) with one <br> — this enforces single-line spacing everywhere in modal.Replace your current section 12 with this refined version (strict single-line gap, no cumulative <br>):
/* 12. Strict Single-Line Paragraph Spacing in Modal & Main Page */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This enforces consistent single empty line spacing across all content, preventing tall gaps from multiple <br>. */
body .content p,
body .generic-content p,
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1.2rem !important; /* strict single paragraph gap */
margin-top: 0 !important; /* no extra top margin */
}
/* Force <br> to be single-line only (no cumulative spacing) */
body br,
.modal-story-content br {
display: block !important;
content: " " !important;
margin: 0.8rem 0 !important; /* fixed single <br> height */
line-height: 0.8rem !important;
height: 0.8rem !important;
}
/* Prevent consecutive <br> from adding extra space */
body br + br,
.modal-story-content br + br {
margin-top: -0.4rem !important; /* collapse extra <br> spacing */
}
/* Image spacing (text-to-image) */
.modal-story-content .content-image,
.modal-story-content .welcome-image {
margin-top: 2.5rem !important; /* space above image */
margin-bottom: 2.5rem !important; /* space below image */
}
/* Section-to-section gap (when no separator) */
.modal-story-content > div + div,
.modal-story-content > p + p,
.modal-story-content > div + p,
.modal-story-content > p + div {
margin-top: 2.5rem !important;
}
/* Mobile: slightly tighter */
@media (max-width: 768px) {
body .content p,
body .generic-content p,
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1rem !important;
}
body br,
.modal-story-content br {
margin: 0.6rem 0 !important;
}
.modal-story-content .content-image,
.modal-story-content .welcome-image {
margin-top: 2rem !important;
margin-bottom: 2rem !important;
}
.modal-story-content > div + div,
.modal-story-content > p + p,
.modal-story-content > div + p,
.modal-story-content > p + div {
margin-top: 2rem !important;
}
}
sections.forEach block with the updated one (collapses to one <br>).<br> or empty divs → reduced to one line spaceThis enforces strict single-line paragraph spacing everywhere, while keeping image/section gaps generous.
Let me know how it looks — or if you want to adjust the single-line height (e.g. margin-bottom: 1.4rem on <br> or paragraphs)!
Current time: February 03, 2026 11:15 AM HKT
This should give you clean, consistent single-line paragraph spacing across the site! 😊
I think you've misunderstood. What I reported was "all content in the modals now come a single-character vertical line running long from the top to the bottom", meaning the main issue is ALL stuff has become a single vertical line, including the debug message. So there are actually, two issues:Single-line content Debug message formerly hidden suddenly appears
I guess the issue should be in the JS update that may render tags as text, because rolling back my original CSS without the paragraph spacing fix doesn't solve the above two issues.
Image margin-top (With CSS) Sectional spacing (With CSS) Inconsistent line breaks between paragraphs (JS+CSS)
But the line-break fix has caused the single-line modal issue and your fixes seems to either make things worse or undo what's already fixed.Actually, the current JS is V3.1.9 instead of V3.1.8 that you said your V3.1.10 was based on. The full source is:
Image margin-top: Solved Sectional spacing: Solved Some paragraphs (Actually MOST) show very tall spacing (Both landing and post page): Re-appear (Fixed with JS before but breaking the modal display, modal buttons and video embeds) Single-line modal/modal buttons/YT embeds: All good
How should we limit paragraph spacing to just a single-line of
next ?
<div>Q1:這是心理治療或心理諮詢嗎<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:<b>情境療癒</b>不是臨床治療,亦不替代專業心理諮詢。它是一個個人反思空間,若你有嚴重心理困擾,請務必尋求專業協助。</div><div><br></div><div>Q2:我需要信仰或靈修背景嗎<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:<span xss="removed"><b>情境療癒</b>並非宗教或靈修活動,亦不涉及任何宗教性樂器 (如頌缽或音叉) 或神秘儀式。</span></div><div><br></div><div>Q3:我一定要收看直播嗎<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:<span xss="removed"><b>情境療癒</b>是「一個人」的療癒時間,你可以選擇觀看直播或錄播。直播期間,你無需發言或給予任何回應。</span></div><div><br></div><div><div>Q4:每次演出需時多久<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:完整直播演出約 90 分鐘,包含生活話題、音樂演奏及心靈療癒片段。錄播版則為精華剪輯,約 10–20 分鐘。你可以收看「ED療癒初體驗」預覽內容,但並非完整體驗。真實的療癒片段會重播多次,並且每一次的配樂都會有所變化。</div></div><div><br></div><div><div>Q5:我需要全程留心觀看嗎<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:不需要。你可以邊做事邊收看,當出現特別吸引你的內容時,才專注收聽。我們稱這種輕盈的參與方式為「扮休息 (Fake It Till You Make It)」,意思是先假裝休息,慢慢就能真正學會「停下來」。</div></div><div><br></div><div><div>Q6:情景療癒是基於什麼原理<span xss="removed">🤔</span><span xss="removed">❓️</span></div><div>A:這可能需要一篇論文去解釋,但簡單說:</div><div><ul><li>音樂和故事等表達媒介,本身就具有情緒療癒及淨化 (Catharsis / カタルシス) 功用</li><li>音樂和畫面可以讓時間感覺停頓下來,幫助你克服生活的催迫感,從而讓身心停得下來</li><li>陪伴就是最好的療癒 —— 直播節目本身,就是陪伴你一起抵抗 Burnout 的時間</li></ul><div><br></div></div></div>
The second screenshot is showing the same issue but inside a modal. Here a big space is inserted for the two div which are expected to be simple line-break. The source is:
當一件事情好難做到嘅時候,選擇扮係容易過真係去做。<div><br></div><div>透過扮嘢,即使唔係真心,都能夠慢慢改變對事情嘅抗拒,【不知不覺】就改變咗!<div><br></div><div>英文講「Fake It Until You Make It」就係呢個意思。</div><div><br></div><div>根據心理學研究,「扮演」有呢啲作用:<div><ul><li>Behavioral Self-Perception (自我形象更好): The brain often forms beliefs about yourself by observing your own behavior. Acting confidently can literally rewire your self-image.</li><li>Facial Feedback Hypothesis (都會真係停落來): Research suggests that physical actions like smiling—even if forced—can trigger biological signals that actually improve your mood.</li><li>Self-Efficacy Theory (覺得自己話到事): Psychologist Albert Bandura noted that the belief in one's ability to succeed significantly impacts actual success; cultivating that belief first can be a powerful catalyst.</li></ul></div><div><br></div><div>所以,雖然一路睇片一路做自己嘢唔算真正嘅休息,不過正如扮靚係會靚咗「扮休息」心情都真係會好咗,健康過認真地</div><div><br></div><div>衝、衝、衝!</div></div></div>
The third screenshot is from the post page. You can see the title is still left-aligned and similar spacing issue. Just that in the post page, the two spacing values are rendered with different heights, which is not expected. The expected rendering is still a simple line-break vs an empty line, depending on how many enters are hitted in the WYSIWYG editor. Here the source is:
<div><span xss="removed">🌸週末,一位每天都工作過勞的在職媽媽,傳來一段勵志 YouTube 影片。影片報導少女克服逆境,甚至得到網友的遙距指導,成功營運一家咖啡店。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸這類影片我一向有留意,還開設了專屬播放清單,收藏各式「港式成功故事」。清單的目的,是分享給過勞人士 (越來越多),提醒他們: </span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🤖我們的社會非常喜歡「辛勞」 —— 在艱苦中排除萬難、取得成就,就是「叻」,會獲得讚賞。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸在香港,我們也喜愛「今天的事今天做」。實際上,這也是「讚賞辛勞」價值觀的延伸。正如AI分析說:</span></div><div><ul><li><span xss="removed">當新的任務出現時,如果你堅持「今天處理」,其實是被事情【追著跑】:什麼最大聲,你就先做什麼。</span></li><li><span xss="removed">「今天的事今天做」即是工作清單在一邊做一邊加,永遠【沒有盡頭】,心理壓力自然爆棚。 </span></li></ul></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸每天都被追著跑、每天都看不見盡頭,難怪令人過勞、焦慮、抑鬱。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸辛勞有時是需要的,但在什麼都不缺乏的時代,人心更需要【個性、創意和獨特性】的滋潤。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸從務實角度看,辛勞本身很容易被抄襲複製,建基於辛勞的事業無法長久維持。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸新的一週,你遇到什麼辛勞的事情沒有? 🌱試試發揮一點「個性」,拖延一兩天或偷懶只做一半。🌱🌱或許,明天已經變成無需處理,或者你會發現更輕盈的辦法解決。</span></div><div><span xss="removed"><br></span></div><div><span xss="removed">🌸香港人,有時唔洗咁辛苦嘅!</span></div>
Do these new information give new insights? Were we fixing things on the right direction so far?
We are still on the right track for the inconsistent line-break space (tall gaps): the cause is the editor's multiple <br> or empty <div><br></div> patterns, and the fixes are targeting that. The regressions (spacing, scrollbar) are from the CSS overrides not being strong enough or conflicting with your existing rules. The scrollbar cut off is a new issue, likely from a margin or padding override on .modal or .modal-content.
The direction is correct, but we need stronger specificity and scope in the CSS to win over theme rules and your earlier overrides (e.g. .dynamic-section.hero.grid .inner padding from section 10).
No JS change — your V3.1.10 is stable.
Replace your current section 12 with this stronger version (better collapse for tall gaps):
/* 12. Strict Single-Line Paragraph Spacing (Main Page + Modal) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This enforces single empty line spacing everywhere, preventing tall gaps from multiple <br>. */
body p,
body .generic-content p,
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1.2rem !important;
margin-top: 0 !important;
}
/* Force empty <div> to have zero height (collapses tall gaps) */
body div:empty,
.modal-story-content div:empty,
body div > br:only-child,
.modal-story-content div > br:only-child {
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
display: none !important;
}
/* Force <br> to be single-line only */
body br,
.modal-story-content br {
display: block !important;
content: " " !important;
margin: 0.6rem 0 !important;
line-height: 0.6rem !important;
height: 0.6rem !important;
}
/* Collapse consecutive <br> completely */
body br + br,
.modal-story-content br + br,
body br + br + br,
.modal-story-content br + br + br {
margin-top: -0.6rem !important;
margin-bottom: -0.6rem !important;
display: none !important; /* hide extra <br> beyond the first */
}
/* Mobile: slightly tighter */
@media (max-width: 768px) {
body p,
.modal-story-content p,
.modal-story-content div > p,
.modal-story-content div {
margin-bottom: 1rem !important;
}
body br,
.modal-story-content br {
margin: 0.5rem 0 !important;
}
body br + br,
.modal-story-content br + br {
margin-top: -0.5rem !important;
margin-bottom: -0.5rem !important;
}
}
Replace your current section 11 with this stronger version (higher specificity for spacing regressions):
/* 11. Modal Spacing – Improved top margins for images & section titles (desktop + mobile) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This improves text-to-image and chunk-to-title spacing in modals on all screen sizes. */
#story-modal .modal-body .modal-story-content .content-image,
#story-modal .modal-body .modal-story-content .welcome-image {
margin-top: 2.5rem !important;
margin-bottom: 2rem !important;
}
#story-modal .modal-body .modal-story-content .section-title,
#story-modal .modal-body .modal-story-content h2.section-title {
margin-top: 5rem !important;
margin-bottom: 1.5rem !important;
}
/* Extra gap between consecutive chunks (when no separator) */
#story-modal .modal-body .modal-story-content > div + div,
#story-modal .modal-body .modal-story-content > p + p,
#story-modal .modal-body .modal-story-content > div + p,
#story-modal .modal-body .modal-story-content > p + div {
margin-top: 5rem !important;
}
/* Mobile: slightly tighter but still balanced */
@media (max-width: 768px) {
#story-modal .modal-body .modal-story-content .content-image,
#story-modal .modal-body .modal-story-content .welcome-image {
margin-top: 2rem !important;
margin-bottom: 1.5rem !important;
}
#story-modal .modal-body .modal-story-content .section-title,
#story-modal .modal-body .modal-story-content h2.section-title {
margin-top: 4rem !important;
margin-bottom: 1.2rem !important;
}
#story-modal .modal-body .modal-story-content > div + div,
#story-modal .modal-body .modal-story-content > p + p,
#story-modal .modal-body .modal-story-content > div + p,
#story-modal .modal-body .modal-story-content > p + div {
margin-top: 4rem !important;
}
}
#story-modal .modal-body).If tall gaps persist:
<div><br></div> or multiple If spacing still tight:
If modal scrollbar still cut off:
.modal or .modal-content → Computed tab → margin-top or padding-top → see winning rule → share itWe'll get it fixed in the next round with the inspect info.
Let me know the result after the update!
Current time: February 03, 2026 01:45 PM HKT
This should be the final polish! 😊
// modal-embed.js - Version 3.1.7 (Tattoo-optimized, static-embed approach, cleaned legacy)
// Last updated: January 28, 2026
console.log("modal-embed.js LOADED SUCCESSFULLY (Tattoo version 3.1.7)");
document.addEventListener('DOMContentLoaded', function() {
const DEBUG = false; // Change to true for testing
let debugLog = null;
if (DEBUG) {
debugLog = document.createElement('div');
debugLog.id = 'debug-log';
debugLog.style.position = 'fixed';
debugLog.style.bottom = '10px';
debugLog.style.left = '10px';
debugLog.style.background = 'rgba(0, 0, 0, 0.8)';
debugLog.style.color = 'white';
debugLog.style.padding = '10px';
debugLog.style.zIndex = '1000';
debugLog.style.maxHeight = '200px';
debugLog.style.overflowY = 'auto';
document.body.appendChild(debugLog);
}
function logDebug(message, color = 'green') {
if (DEBUG && debugLog) {
debugLog.insertAdjacentHTML('beforeend', `<div style="color: ${color}; font-weight: bold;">Debug: ${message}</div>`);
debugLog.scrollTop = debugLog.scrollHeight;
}
if (DEBUG && console) console.log(`Debug: ${message}`);
}
if (DEBUG) logDebug('Modal script loaded (Tattoo version 3.1.7)');
const modal = document.getElementById('story-modal');
const modalBody = document.getElementById('modal-body');
const closeBtn = document.querySelector('.modal-close');
const triggers = document.querySelectorAll('.modal-trigger');
if (DEBUG) logDebug('Found ' + triggers.length + ' modal triggers');
// Embed rendering function
function renderEmbed(item, index, total) {
const container = document.getElementById(`embed-${item.id}`);
if (!container) {
if (DEBUG) logDebug(`Container not found for embed ID ${item.id}`, 'red');
return;
}
let cleanCode = item.code.replace(/ sandbox(?=[\s>])/g, '');
container.innerHTML = cleanCode;
container.style.display = 'block';
if (item.type === 'Embed') {
if (DEBUG) logDebug(`Rendered Embed for ID ${item.id} (sandbox removed)`, 'orange');
} else if (item.type === 'PayPal') {
const divMatch = item.code.match(/<div\s+[^>]*id="[^"]+"[^>]*>/i);
const buttonMatch = item.code.match(/<script>\s*paypal\.Buttons\([\s\S]*?\)\.render\(['"][^'"]+['"]\);\s*<\/script>/i);
if (divMatch && buttonMatch) {
const uniqueId = `paypal-button-container-${item.id}`;
let divContent = divMatch[0].replace(/id="[^"]+"/, `id="${uniqueId}"`);
container.innerHTML = divContent;
container.querySelector(`#${uniqueId}`).style.cssText = 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important;';
function waitForSDK(callback) {
if (typeof paypal !== 'undefined') {
callback();
} else {
setTimeout(function() { waitForSDK(callback); }, 100);
}
}
waitForSDK(function() {
let scriptContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
scriptContent = scriptContent.replace(/\.render\(['"][^'"]+['"]\)/, `.render('#${uniqueId}')`);
const uniqueScript = `(function() { ${scriptContent} })();`;
const buttonScript = document.createElement('script');
buttonScript.textContent = uniqueScript;
document.body.appendChild(buttonScript);
});
}
}
}
// Run embed rendering on page load
if (window.contentCode && Array.isArray(window.contentCode)) {
window.contentCode.forEach(function(item, index) {
renderEmbed(item, index, window.contentCode.length);
});
} else if (DEBUG) {
logDebug('contentCode is not an array or unavailable', 'red');
}
// PHP debug info (safe try-catch)
try {
if (window.debugFromPHP) {
console.log('PHP Debug Info:', window.debugFromPHP);
if (DEBUG) logDebug('PHP → Fetched sections: ' + window.debugFromPHP.fetched + ', Grouped positions: ' + window.debugFromPHP.grouped + ', Modal request: ' + window.debugFromPHP.modalRequest, 'purple');
} else {
console.warn('No debug info from PHP');
}
} catch (e) {
console.warn('Error reading PHP debug info:', e);
}
// Modal logic only if elements exist
if (modal && modalBody && closeBtn && triggers.length > 0) {
if (DEBUG) logDebug('Modal elements found - enabling modal functionality');
triggers.forEach(function(trigger) {
trigger.addEventListener('click', function(e) {
e.preventDefault();
if (DEBUG) logDebug(`Modal trigger clicked: ${this.dataset.modalId || this.dataset.modalLink}`, 'green');
const modalId = this.dataset.modalId;
if (modalId) {
const contentDiv = document.getElementById(modalId);
if (contentDiv) {
const content = contentDiv.innerHTML;
if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
openModal(content);
} else {
openModal('<p>Content not found.</p>');
if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
}
return;
}
const link = this.dataset.modalLink;
const modalTitle = this.dataset.modalTitle || this.textContent.trim() || 'Content';
if (DEBUG) logDebug('Raw link from button: ' + link + ', type: ' + typeof link + ', length: ' + (link ? link.length : 'null'));
if (DEBUG) logDebug('Trigger clicked, link: ' + link + ', title: ' + modalTitle);
// If link is static-embed.php, fetch it directly
if (link && link.includes('static-embed.php')) {
let absoluteLink = new URL(link, window.location.origin).href;
absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
if (DEBUG) logDebug('Fetching static-embed link: ' + absoluteLink);
openModal('<p>Loading... (Title: ${modalTitle})</p>');
fetch(absoluteLink, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.text();
})
.then(data => {
const contentWithTitle = `<h2 class="section-title">${modalTitle}</h2>` + data;
openModal(contentWithTitle);
})
.catch(error => {
openModal(`<h2 class="section-title">${modalTitle}</h2><p>Sorry, the content could not be loaded: ${error.message}</p>`);
});
return;
}
// Allow empty or # link to open local modal (fallback message)
if (!link || link === '#') {
openModal(`<h2 class="section-title">${modalTitle}</h2><p>No embed/video configured for this section.</p>`);
if (DEBUG) logDebug('Local modal fallback: no static link configured', 'orange');
return;
}
// Prevent recursive loading for same-page links
if (link.includes('index.php') || link === window.location.pathname) {
openModal(`<h2 class="section-title">${modalTitle}</h2><p>This link cannot be loaded in a modal to prevent recursive loading.</p>`);
return;
}
// Normal fetch for other links
let absoluteLink;
try {
absoluteLink = new URL(link, window.location.origin).href;
} catch (error) {
openModal(`<h2 class="section-title">${modalTitle}</h2><p>Invalid URL.</p>`);
return;
}
absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
openModal(`<p>Loading... (Title: ${modalTitle})</p>`);
fetch(absoluteLink, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(function(response) {
if (DEBUG) logDebug('Fetch response status: ' + response.status + ' ' + response.statusText);
if (!response.ok) {
throw new Error('Network response was not ok: ' + response.statusText);
}
return response.text();
})
.then(function(data) {
if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html');
let combinedContent = `<h2 class="section-title">${modalTitle}</h2>`;
const sections = doc.querySelectorAll('.modal-story-content');
if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
if (sections.length > 0) {
sections.forEach(function(section, index) {
let sectionHTML = section.outerHTML.trim();
combinedContent += sectionHTML;
if (index < sections.length - 1) {
combinedContent += '<div class="modal-section-separator"></div>';
}
});
} else {
const tattooFallback = doc.querySelectorAll('.dynamic-section .inner');
if (DEBUG) logDebug('Found ' + tattooFallback.length + ' Tattoo .dynamic-section .inner sections');
if (tattooFallback.length > 0) {
tattooFallback.forEach(function(section, index) {
const newContent = document.createElement('div');
newContent.className = 'modal-story-content';
newContent.innerHTML = section.innerHTML;
let cleanedSection = newContent.outerHTML.trim();
combinedContent += cleanedSection;
if (index < tattooFallback.length - 1) {
combinedContent += '<div class="modal-section-separator"></div>';
}
});
} else {
if (DEBUG) logDebug('No content found in Tattoo structure', 'red');
combinedContent += '<p>Content could not be parsed from the page.</p>';
}
}
openModal(combinedContent);
})
.catch(function(error) {
if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
openModal(`<h2 class="section-title">${modalTitle}</h2><p>Sorry, the content could not be loaded: ${error.message}</p>`);
});
});
});
closeBtn.addEventListener('click', closeModal);
window.addEventListener('click', function(event) {
if (event.target === modal) {
closeModal();
}
});
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && modal.style.display === 'block') {
closeModal();
}
});
} else {
if (DEBUG) logDebug('Modal elements missing - skipping modal functionality (normal on non-modal pages)', 'orange');
}
function openModal(content) {
const modalBody = document.getElementById('modal-body');
modalBody.innerHTML = content;
const modal = document.getElementById('story-modal');
modal.style.display = 'block';
modal.setAttribute('aria-hidden', 'false');
const closeBtn = document.querySelector('.modal-close');
if (closeBtn) closeBtn.focus();
if (DEBUG) logDebug('Modal opened');
}
function closeModal() {
const modal = document.getElementById('story-modal');
modal.style.display = 'none';
modal.setAttribute('aria-hidden', 'true');
const modalBody = document.getElementById('modal-body');
modalBody.innerHTML = '';
if (DEBUG) logDebug('Modal closed');
}
});
/* Safe, no conflicts – All features preserved */
/* Version V5.3.2 - January 27, 2026 - Stronger containment for bleed + multi-column fix */
/* 1. Hero / Background – Full Browser Width */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed stable for full-width hero sections. */
.dynamic-section.hero,
.dynamic-section[style*="background-image"] {
min-height: 60vh;
background-size: cover !important;
background-position: center !important;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
width: 100vw !important;
margin-left: calc(-50vw + 50%) !important;
}
.dynamic-section.hero::before,
.dynamic-section[style*="background-image"]::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
.dynamic-section.hero > .inner,
.dynamic-section[style*="background-image"] > .inner {
position: relative;
z-index: 2;
max-width: 90%;
padding: 2rem;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
}
/* 2. Title Styling */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Hero title sizing has been tested and confirmed correct. */
.section-title {
margin-bottom: 2.5rem !important;
font-size: 2rem;
line-height: 1.2;
}
.dynamic-section.hero .section-title {
margin-bottom: 3rem !important;
font-size: 3rem;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
/* 3. Normal Content Spacing */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This padding is critical for non-hero sections. */
.dynamic-section:not(.hero):not([style*="background-image"]) > .inner {
padding: 8rem 2rem 2rem !important;
max-width: 1200px;
margin: 0 auto;
}
/* 4. Multi-Column Layout - Stronger containment for desktop columns */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been strengthened to force columns and contain bleed. */
.dynamic-section.grid {
display: flex !important;
flex-wrap: nowrap !important;
justify-content: space-between !important;
gap: 2rem !important;
padding: 4rem 2rem !important;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
box-sizing: border-box !important;
}
.dynamic-section.grid .inner {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
display: flex !important;
flex-wrap: inherit !important;
justify-content: inherit !important;
}
/* 2-column layout */
.dynamic-section.grid.layout-2-column .inner > * {
flex: 1 0 49% !important;
max-width: 49% !important;
}
/* 3-column layout */
.dynamic-section.grid.layout-3-column .inner > * {
flex: 1 0 32% !important;
max-width: 32% !important;
}
/* Mobile fallback: stack columns */
@media (max-width: 767px) {
.dynamic-section.grid {
flex-wrap: wrap !important;
}
.dynamic-section.grid .inner > * {
flex: 1 0 100% !important;
max-width: 100% !important;
}
}
/* 5. Brand Button */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Button styling has been tested and confirmed stable. */
.dynamic-section .btn,
.dynamic-section a[href*="paypal"],
.dynamic-section button {
background: #A6752E !important;
color: white !important;
padding: 0.8rem 1.8rem !important;
border-radius: 6px !important;
text-decoration: none !important;
display: inline-block;
font-weight: 600;
}
.dynamic-section .btn:hover {
background: #8B5A1F !important;
}
.content__buttons {
text-align: center;
margin: 2rem 0;
}
/* 6. Separator – Contained under grid with margin to prevent overlap */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Separator now contained when following .grid */
.separator-wrapper {
display: flex;
justify-content: center;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
padding: 2rem 0;
background: white !important;
z-index: 2 !important;
clear: both !important;
}
.dynamic-section.grid + .separator-wrapper {
max-width: 1200px !important;
margin: 2rem auto 0 auto !important;
}
.separator-wrapper .separator {
display: block !important;
text-align: center !important;
margin: 1.5rem auto 0 auto !important;
height: auto;
max-width: 100% !important;
}
.separator-wrapper .default-bee {
max-width: 150px !important;
}
.separator-wrapper .custom-separator {
max-width: none !important;
width: 100% !important;
}
/* 7. Responsive YouTube/Video Embeds – Stronger Overrides for Tattoo */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This is the core responsive embed rule. Changing padding-bottom will break aspect ratio. */
.content-embed {
position: relative !important;
width: 100% !important;
padding-bottom: 56.25% !important;
height: 0 !important;
overflow: hidden !important;
margin: 2rem 0 !important;
}
.content-embed iframe {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: 0 !important;
display: block !important;
}
/* 8. Modal – Centered + External Scrollbar (EXACTLY your V5.0 - no change) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed to provide external scrollbar. */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
.modal.is-active {
display: block;
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border: 1px solid #888;
width: 80%;
max-width: 800px;
min-height: 200px;
position: relative;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
/* External scrollbar – no internal overflow */
}
/* Close button */
.modal-close {
color: #aaa !important;
position: absolute !important;
top: 10px !important;
right: 20px !important;
font-size: 36px !important;
font-weight: bold !important;
cursor: pointer !important;
z-index: 1001 !important;
line-height: 1 !important;
}
.modal-close:hover,
.modal-close:focus {
color: #000 !important;
}
/* Modal embed sizing */
#story-modal .modal-body .content-embed {
min-height: 315px !important;
height: 70vh !important;
padding-bottom: 0 !important;
overflow: visible !important;
margin: 1rem 0 !important;
display: block !important;
}
#story-modal .modal-body .content-embed iframe {
width: 100% !important;
height: 100% !important;
display: block !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
/* Fix hero + grid bleed (no full-width shift) */
.dynamic-section.hero.grid,
.dynamic-section.hero.grid .inner {
width: 100% !important;
max-width: 1200px !important;
margin-left: auto !important;
margin-right: auto !important;
padding: 0 2rem !important;
box-sizing: border-box !important;
}
.dynamic-section.hero.grid::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
/* 9. Replace bullets with left-aligned emoji (like traditional bullet) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.modal-story-content ul,
.dynamic-section .inner ul {
list-style: none !important;
padding-left: 2.2rem !important; /* increased left padding → moves entire list block slightly to the left */
margin: 1.5rem 0 !important;
}
.modal-story-content li,
.dynamic-section .inner li {
position: relative;
margin-bottom: 1rem !important;
padding-left: 2.5rem !important; /* increased indent → more space between emoji and text */
text-align: left !important; /* keep text left-aligned */
}
/* Emoji as left-aligned bullet */
.modal-story-content li::before,
.dynamic-section .inner li::before {
content: "🔰 "; /* your preferred emoji */
position: absolute !important;
left: -0.3rem !important; /* moved slightly to the left for balance */
top: 0.2rem !important; /* same vertical alignment as before */
font-size: 1.4rem !important; /* same size as your original → matches text, no overlap */
line-height: 1 !important;
color: #A6752E !important; /* your accent color */
}
/* Optional: mobile adjustment (larger emoji for touch) */
@media (max-width: 768px) {
.modal-story-content li::before,
.dynamic-section .inner li::before {
font-size: 1.6rem !important;
left: -0.4rem !important; /* a bit more left shift on small screens */
}
}
/* 10. Group Section Background – Medium round corners + elegant gray shadow (only for groups) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.dynamic-section.grid[style*="background-image"],
.dynamic-section.hero.grid {
border-radius: 12px !important; /* medium round corners - adjust 8px–16px to taste */
overflow: hidden !important; /* ensure content doesn't leak outside rounded corners */
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; /* elegant gray shadow - similar to modal */
margin: 2rem auto !important; /* slight margin around the group for breathing room */
max-width: 1200px !important; /* contain width like other sections */
}
/* Optional: stronger shadow on hover for interactive feel */
.dynamic-section.grid[style*="background-image"]:hover,
.dynamic-section.hero.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
/* Mobile adjustment: slightly smaller corners/shadow for better fit */
@media (max-width: 768px) {
.dynamic-section.grid[style*="background-image"],
.dynamic-section.hero.grid {
border-radius: 10px !important;
box-shadow: 0 3px 10px rgba(0,0,0,0.12) !important;
margin: 1rem auto !important;
}
}
/* 1. Hero / Background – Full Browser Width */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed stable for full-width hero sections. */
.dynamic-section.hero,
.dynamic-section[style*="background-image"] {
min-height: 60vh;
background-size: cover !important;
background-position: center !important;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
width: 100vw !important;
margin-left: calc(-50vw + 50%) !important;
}
.dynamic-section.hero::before,
.dynamic-section[style*="background-image"]::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
.dynamic-section.hero > .inner,
.dynamic-section[style*="background-image"] > .inner {
position: relative;
z-index: 2;
max-width: 90%;
padding: 2rem;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
}
/* 2. Title Styling */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Hero title sizing has been tested and confirmed correct. */
.section-title {
margin-bottom: 2.5rem !important;
font-size: 2rem;
line-height: 1.2;
}
.dynamic-section.hero .section-title {
margin-bottom: 3rem !important;
font-size: 3rem;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
/* 3. Normal Content Spacing */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This padding is critical for non-hero sections. */
.dynamic-section:not(.hero):not([style*="background-image"]) > .inner {
padding: 8rem 2rem 2rem !important;
max-width: 1200px;
margin: 0 auto;
}
/* 4. Shared Position Sections – Single-column layout (no multi-column forcing) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block ensures shared positions remain single-column, avoiding theme conflicts. */
/* No .grid forcing — let theme default to single-column flow for shared positions */
.dynamic-section.grid {
display: block !important;
flex-direction: column !important;
flex-wrap: wrap !important;
justify-content: flex-start !important;
}
/* 5. Brand Button */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Button styling has been tested and confirmed stable. */
.dynamic-section .btn,
.dynamic-section a[href*="paypal"],
.dynamic-section button {
background: #A6752E !important;
color: white !important;
padding: 0.8rem 1.8rem !important;
border-radius: 6px !important;
text-decoration: none !important;
display: inline-block;
font-weight: 600;
}
.dynamic-section .btn:hover {
background: #8B5A1F !important;
}
.content__buttons {
text-align: center;
margin: 2rem 0;
}
/* 6. Separator – Contained under grid */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Separator now contained when following .grid */
.separator-wrapper {
display: flex;
justify-content: center;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
padding: 2rem 0;
background: white !important;
z-index: 2 !important;
}
.dynamic-section.grid + .separator-wrapper {
max-width: 1200px !important;
margin: 0 auto !important;
}
.separator-wrapper .separator {
display: block !important;
text-align: center !important;
margin: 1.5rem auto 0 auto !important;
height: auto;
max-width: 100% !important;
}
.separator-wrapper .default-bee {
max-width: 150px !important;
}
.separator-wrapper .custom-separator {
max-width: none !important;
width: 100% !important;
}
/* 7. Responsive YouTube/Video Embeds – Stronger Overrides for Tattoo */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This is the core responsive embed rule. Changing padding-bottom will break aspect ratio. */
.content-embed {
position: relative !important;
width: 100% !important;
padding-bottom: 56.25% !important;
height: 0 !important;
overflow: hidden !important;
margin: 2rem 0 !important;
}
.content-embed iframe {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: 0 !important;
display: block !important;
}
/* 8. Modal – Centered + External Scrollbar (EXACTLY your V5.0 - no change) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed to provide external scrollbar. */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
.modal.is-active {
display: block;
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border: 1px solid #888;
width: 80%;
max-width: 800px;
min-height: 200px;
position: relative;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
/* External scrollbar – no internal overflow */
}
/* Close button */
.modal-close {
color: #aaa !important;
position: absolute !important;
top: 10px !important;
right: 20px !important;
font-size: 36px !important;
font-weight: bold !important;
cursor: pointer !important;
z-index: 1001 !important;
line-height: 1 !important;
}
.modal-close:hover,
.modal-close:focus {
color: #000 !important;
}
/* Modal embed sizing */
#story-modal .modal-body .content-embed {
min-height: 315px !important;
height: 70vh !important;
padding-bottom: 0 !important;
overflow: visible !important;
margin: 1rem 0 !important;
display: block !important;
}
#story-modal .modal-body .content-embed iframe {
width: 100% !important;
height: 100% !important;
display: block !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
/* Fix hero + grid bleed (no full-width shift) */
.dynamic-section.hero.grid,
.dynamic-section.hero.grid .inner {
width: 100% !important;
max-width: 1200px !important;
margin-left: auto !important;
margin-right: auto !important;
padding: 0 2rem !important;
box-sizing: border-box !important;
}
.dynamic-section.hero.grid::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
/* 9. Replace bullets with left-aligned emoji (like traditional bullet) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.modal-story-content ul,
.dynamic-section .inner ul {
list-style: none !important;
padding-left: 2.2rem !important; /* increased left padding → moves entire list block slightly to the left */
margin: 1.5rem 0 !important;
}
.modal-story-content li,
.dynamic-section .inner li {
position: relative;
margin-bottom: 1rem !important;
padding-left: 2.5rem !important; /* increased indent → more space between emoji and text */
text-align: left !important; /* keep text left-aligned */
}
/* Emoji as left-aligned bullet */
.modal-story-content li::before,
.dynamic-section .inner li::before {
content: "🔰 "; /* your preferred emoji */
position: absolute !important;
left: -0.3rem !important; /* moved slightly to the left for balance */
top: 0.2rem !important; /* same vertical alignment as before */
font-size: 1.4rem !important; /* same size as your original → matches text, no overlap */
line-height: 1 !important;
color: #A6752E !important; /* your accent color */
}
/* Optional: mobile adjustment (larger emoji for touch) */
@media (max-width: 768px) {
.modal-story-content li::before,
.dynamic-section .inner li::before {
font-size: 1.6rem !important;
left: -0.4rem !important; /* a bit more left shift on small screens */
}
}
/* 10. Modal Section Spacing – Better separation between chunks & new titles */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This improves readability in long modals without affecting short ones. */
.modal-story-content {
margin-bottom: 2.5rem !important; /* adds breathing room below every chunk */
padding-bottom: 1rem !important;
}
.modal-story-content .section-title {
margin-top: 2rem !important; /* space above the new title */
margin-bottom: 1.2rem !important; /* space between title and its own content */
padding-top: 0.5rem !important;
}
/* 11. Strong Mobile Adjustments for Shared Sections – Force narrower padding & title size */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block overrides theme !important rules for better mobile readability in rounded group backgrounds. */
@media (max-width: 767px) {
/* Highest specificity override for padding on shared group inner (your good value) */
html body #main .dynamic-section.grid .inner,
html body .dynamic-section.grid .inner,
.dynamic-section.grid .inner {
padding: 0 1rem !important;
padding-top: 1.5rem !important;
padding-bottom: 1.5rem !important;
}
/* Force title font size in shared group sections (your good starting point) */
html body #main .dynamic-section.grid .section-title,
html body .dynamic-section.grid .section-title,
.dynamic-section.grid .section-title {
font-size: 2.3rem !important;
line-height: 1.3 !important;
margin-bottom: 1.5rem !important;
}
/* Tighter outer section padding to match narrower inner */
html body #main .dynamic-section.grid,
html body .dynamic-section.grid {
padding: 2rem 1rem !important;
}
}
Please check if both are free from duplication and legacy codes. I will update you some new discoveries on paragraph line-break and spacing issues.
JS: modal-embed.js - Version 3.1.7 (Tattoo-optimized, static-embed approach, cleaned legacy)
Last updated: January 28, 2026
CSS: Version V5.3.2 - January 27, 2026 - Stronger containment for bleed + multi-column fix
These are the last confirmed "good" versions before the later changes started introducing regressions (like the single-line modal rendering or inconsistent spacing).
You are correct that we've had a few different versioning tracks over time because I sometimes generated incremental updates or full files based on different points in the conversation, but the ones you pasted above are the ones you explicitly marked as the last stable pair.
Let me know what the new findings are regarding paragraph line-break and spacing issues — we can continue from there using V3.1.7 JS and V5.3.2 CSS as the reliable base. 😊
To verify this, I set the last two sections of the "landing" page to the same display order and removed the background image from both of them. This allowed me to reproduce the same left-aligned titles and inconsistent paragraph spacing seen on the "post" page. Again, this issue affects only the desktop view.
Based on these findings, the conditions that contribute to inconsistent paragraph spacing and left-aligned titles appear to be:
Desktop view Sections sharing the same display order Sections without a custom background image
For further clarity, please refer to the screenshot from the desktop view of the "landing" page. In the image, the title of the top section is left-aligned (orange arrow). One paragraph spacing that should appear as an empty line is collapsed (orange line). The modal button overlaps with the title of the next section, pushing it to the right (though it remains left-aligned).
Thus, it looks like that it's the missing custom background image that has initiated all issues. Can you study our latest codes to confirm this?
Below is V3.2.1 of pull-contents.php currently used with JS 3.1.7 and CSS 5.3.2 for your reference:
<?php
// pull-contents.php (Version 3.2.1 - Modal titles for non-1/0 positions)
// Last updated: January 27, 2026
define('DEBUG', true);
define('IMAGE_BASE_PATH', 'https://navi.claudioflute.live/images/');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$config_path = '../config.php';
if (!file_exists($config_path)) {
die("Error: config.php not found at $config_path");
}
require_once $config_path;
if (!isset($dbServer) || !isset($dbUsername) || !isset($dbPassword) || !isset($dbDatabase)) {
die("Error: AppGini database variables not defined in config.php");
}
$conn = new mysqli($dbServer, $dbUsername, $dbPassword, $dbDatabase, $dbPort ?: 3306);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Auto-detect page
if (isset($_GET['page'])) {
$current_page = $_GET['page'];
} else {
$calling_file = basename($_SERVER['SCRIPT_FILENAME']);
$current_page = str_replace('.php', '', $calling_file);
if ($current_page === 'index') {
$current_page = 'landing';
}
}
$status = 'Show';
// Preload PayPal SDK
$paypalSdkUrl = 'https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription';
echo "<script src=\"$paypalSdkUrl\" id=\"paypal-sdk\" async=\"false\"></script>";
// Is modal request
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);
// Is stories page (for preview logic)
$is_stories_page = ($current_page === 'stories');
// SQL - no code_type
$sql = "SELECT id, title, content, code, image, button_link, button_text, position, model, date_edited, theme_img, img_role, chk_divider
FROM web_content
WHERE page = ? AND status = ?
ORDER BY position ASC, date_edited DESC";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();
$sections = [];
while ($row = $result->fetch_assoc()) {
$sections[] = $row;
}
// Group by position for multi-column
$sections_by_position = [];
foreach ($sections as $row) {
$position = $row['position'] ?? 1;
$sections_by_position[$position][] = $row;
}
if (!empty($sections_by_position)) {
ksort($sections_by_position);
foreach ($sections_by_position as $position => $group) {
$is_grid = count($group) > 1;
if ($is_modal_request) {
foreach ($group as $index => $row) {
echo "<div class=\"modal-story-content\">";
// New: Add section title only if position is not 1 or 0
if (!empty($row['title']) && $row['position'] != 1 && $row['position'] != 0) {
echo "<h2 class=\"section-title\">" . htmlspecialchars($row['title']) . "</h2>";
}
echo $row['content'];
if (!empty($row['image'])) {
$image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
}
// Embed in modal only if model says modal-videos
if (strpos($row['model'], 'modal-videos') !== false) {
if (!empty($row['code'])) {
echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
$encodedCode = json_encode($row['code'], JSON_HEX_TAG);
echo "<script>window.contentCode = window.contentCode || []; window.contentCode.push({id: '{$row['id']}', code: " . $encodedCode . ", type: 'Embed'});</script>";
}
}
echo "</div>";
if ($index < count($group) - 1) {
echo "<div class=\"modal-section-separator\"></div>";
}
}
} else {
// SMART CLASS for Tattoo
$section_class = 'content';
$has_background = false;
$bg_url = '';
foreach ($group as $row) {
if ($row['img_role'] === 'Background' && !empty($row['theme_img'])) {
$has_background = true;
$bg_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
break; // first background wins
}
}
if ($has_background) {
$section_class = 'hero';
}
if ($is_grid) {
$section_class .= ' grid';
}
$bg_style = '';
if ($has_background) {
$bg_style = "style=\"background-image: url('$bg_url');\"";
}
echo "<section class=\"{$section_class} dynamic-section\" {$bg_style}>";
echo "<div class=\"inner\">";
foreach ($group as $row) {
if (!empty($row['title'])) {
echo "<h2 class=\"section-title\">{$row['title']}</h2>";
}
echo $row['content'];
// Image after text
if (!empty($row['image'])) {
$image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
}
// Embed after image/text (only if model says video and NOT modal-videos)
$show_embed_on_page = false;
if (strpos($row['model'], 'video') !== false && strpos($row['model'], 'modal-videos') === false) {
$show_embed_on_page = true;
}
if ($show_embed_on_page && !empty($row['code'])) {
echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
$encodedCode = json_encode($row['code'], JSON_HEX_TAG);
echo "<script>window.contentCode = window.contentCode || []; window.contentCode.push({id: '{$row['id']}', code: " . $encodedCode . ", type: 'Embed'});</script>";
}
// BUTTON RENDERING
if (!empty($row['button_text'])) {
$button_text = htmlspecialchars($row['button_text']);
$button_link = htmlspecialchars($row['button_link'] ?: '#');
echo "<div class=\"content__buttons\">";
if ($is_stories_page) {
$modal_id = 'modal-content-' . $row['id'];
echo "<div id=\"$modal_id\" style=\"display:none;\">";
echo '<div class="modal-story-content">';
if (!empty($row['title'])) echo "<h2 class=\"section-title\">" . $row['title'] . "</h2>";
if (!empty($row['image'])) {
$image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
echo "<div class=\"content-image modal-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
}
if (!empty($row['content'])) echo $row['content'];
echo '</div>';
echo "</div>";
echo "<a class=\"btn modal-trigger\" href=\"javascript:void(0)\" data-modal-id=\"$modal_id\">$button_text</a>";
} else {
$data_attr = "data-modal-title=\"$button_text\"";
if (!empty($button_link) && $button_link !== '#') {
$data_attr .= " data-modal-link=\"$button_link\"";
}
echo "<a class=\"btn modal-trigger\" href=\"javascript:void(0)\" $data_attr>$button_text</a>";
}
echo "</div>";
}
}
echo "</div>"; // .inner
echo "</section>";
// Separator — moved OUTSIDE the section: appears if ANY row in group has chk_divider = 1 or custom separator
// Custom separator takes priority - uses the FIRST custom image found in the group
$show_separator = false;
$sep_url = '';
$use_custom = false;
foreach ($group as $group_row) {
if (!empty($group_row['theme_img']) && $group_row['img_role'] === 'Separator') {
$sep_url = IMAGE_BASE_PATH . htmlspecialchars($group_row['theme_img']);
$show_separator = true;
$use_custom = true;
break; // custom takes priority - use the first one found
} elseif (!$show_separator && !empty($group_row['chk_divider']) && $group_row['chk_divider'] == 1) {
$sep_url = 'https://navi.claudioflute.live/public/Flying_bee_R.gif';
$show_separator = true;
}
}
if ($show_separator) {
$sep_class = $use_custom ? 'separator custom-separator' : 'separator default-bee';
echo "<div class=\"separator-wrapper\">";
echo "<img src=\"$sep_url\" class=\"$sep_class\" alt=\"Separator\">";
echo "</div>";
}
}
}
} else {
echo "No sections found.";
}
// Modal container (only for non-AJAX requests)
if (!$is_modal_request) {
echo '<div id="story-modal" class="modal">';
echo '<div class="modal-content">';
echo '<span class="modal-close">×</span>';
echo '<div id="modal-body"></div>';
echo '</div>';
echo '</div>';
}
// Load external JS for modal and embed rendering
echo '<script src="/modal-embed.js?v=' . time() . '"></script>';
$stmt->close();
$conn->close();
?>
The expected behavior is to show the first found background image from a group of sections behind ALL sections, filling up the "island". I think your approach to apply an island even when no bkg. image is clever, but you should hide the island from view, just using the space and structure it provides.
Can you check if the above can be done? Since V5.3.3 is still much shorter than the previous one, please make sure no essential styles from V5.3.2 has been truncated BEFORE you suggest the fix. It's frustrating to find errors simply because of missing but not wrong codes.
I try copy the course for the tall space, which is:
A margin-top: 5rem !import style is seen with the selector ".modal-story-content>div+div, .modal-story-content>div+p, .modal-story-content>p+div, .modal-story-content>p+p". But such a height was requested for the section titles only in our previous work. I wonder why would it appear here, being applied to paragraph spaces. The background image is still missing for the group island when it should show.
Before any island experiment (original V5.3.2 or earlier):
.grid) had no special background rules.img_role = 'Background', PHP added the inline style="background-image: url(...)" to the <section> tag.When we first added island containment (around V5.3.3):
.dynamic-section.grid { background: white !important; }!important white background overrode the PHP inline background-image style whenever .grid was present (i.e. shared positions).Later versions (V5.3.4 and your current one):
background: white !important; was removed (good catch — your screenshot confirms it's gone).→ This means the removal fixed the override, but something else is now preventing the image from appearing.
From your current CSS (V5.3.4) and PHP (V3.2.1), the most likely remaining blockers are:
PHP is not applying the background to .grid sections correctly:
The code only adds $bg_style if $has_background = true (at least one row has img_role === 'Background').
The class logic:
if ($has_background) {
$section_class = 'hero';
}
if ($is_grid) {
$section_class .= ' grid';
}
→ If background is present → class becomes hero grid → section 10 applies background-size: cover → should work.
→ If no background → class = content grid → section 10 applies text-align: center and containment, but no background handling → correct (no image).
Conclusion: If the image is missing in a group that has a background set, check:
Is img_role exactly 'Background' (case-sensitive)?
Is it saved on at least one row in the group?
After save → regenerate → check page source for the <section class="... grid" style="background-image: url(...)" — if style is missing → PHP bug.
CSS is still interfering indirectly:
overflow: hidden !important; on .dynamic-section.grid can clip background if image is not positioned correctly.max-width: 1200px !important; + margin: 2rem auto !important; may cause centering issues if background is not repeating.background: none or background-color on .grid or .content.grid.Verify PHP is adding the style:
<section class="... grid dynamic-section" → see if it has style="background-image: url(...)".img_role = 'Background' and image set → save + regenerate.Update Section 10 (remove potential clipping):
Replace your current Section 10 with this:
/* 10. Group Section Background & Layout – Containment + centering for ALL shared groups */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This ensures shared positions (grid) are contained, centered, and spaced consistently. */
.dynamic-section.grid {
border-radius: 12px !important;
overflow: visible !important; /* changed from hidden to allow background to show fully */
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
margin: 2rem auto !important;
max-width: 1200px !important;
text-align: center !important;
}
/* Apply background image if present (PHP inline style wins) */
.dynamic-section.grid[style*="background-image"] {
background-size: cover !important;
background-position: center !important;
}
/* Optional: stronger shadow on hover */
.dynamic-section.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
/* Mobile: slightly smaller corners/shadow */
@media (max-width: 768px) {
.dynamic-section.grid {
border-radius: 10px !important;
box-shadow: 0 3px 10px rgba(0,0,0,0.12) !important;
margin: 1rem auto !important;
}
}
overflow: hidden !important; → overflow: visible !important; → prevents clipping of background image.background: white → PHP image wins.Apply & Test:
If image still missing:
<section class="... grid" ...> line from page source on the group page — we'll see if style is there.This is the only change needed for background — no other sections touched.
Let me know the result after replacing section 10 — the image should reappear!
Current time: February 03, 2026 04:15 PM HKT
We're now fixing the last piece — background in groups! 😊
Fix the weird desktop-only issues that appear ONLY when sections share the same display order AND none has a background image
(Left-aligned titles + inconsistent/tall paragraph spacing), while keeping mobile view stable and not touching anything that breaks mobile.
In the original V5.3.2 CSS (messy but working):
This is very important — it means the trigger is desktop-specific, and any fix should be scoped to desktop only (via media query or desktop-only selectors).
Yes — but only on desktop.
Here's why:
hero grid → section 10 applies island (rounded, shadow, max-width, centering) → desktop looks good (centered titles, contained layout, normal spacing).content grid → section 10 does not match (no [style*="background-image"]) → no island → desktop falls back to theme's default post-style layout (.banner.banner--after-content or similar) → left-aligned titles + tall paragraph gaps from multiple <br>/empty divs..grid into single-column (from your section 4 media query) → no left-align visible, and spacing feels less exaggerated due to smaller screen.Conclusion:
The main hurdle is not having .hero.grid when no background → no island containment/centering on desktop.
Mobile is fine because it stacks anyway.
We will:
@media (min-width: 769px) to avoid touching mobile (since mobile was stable in V5.3.2).hero class).Replace your current Section 10 with this desktop-scoped version:
/* 10. Group Section Background & Layout – Containment + centering for ALL shared groups on desktop only */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This applies island containment and centering only on desktop for shared positions (grid), even without background image. Mobile remains unchanged (stable stacking). */
@media (min-width: 769px) {
.dynamic-section.grid {
border-radius: 12px !important;
overflow: hidden !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
margin: 2rem auto !important;
max-width: 1200px !important;
text-align: center !important; /* center titles and content on desktop */
}
/* Apply background image if present (PHP inline style wins) */
.dynamic-section.grid[style*="background-image"] {
background-size: cover !important;
background-position: center !important;
}
/* Optional: stronger shadow on hover */
.dynamic-section.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
}
min-width: 769px): All shared groups (.grid) get island + centering — whether background is present or not.If titles still left-aligned or spacing tall on desktop shared groups:
<section class="... grid"> on desktop → Styles tab → see winning text-align / margin rules → share them.<section ... style="..."> line from page source.This is a very low-risk change — desktop-only, no mobile impact, no JS touch.
Let me know the result after replacing section 10!
Current time: February 03, 2026 04:15 PM HKT
This should fix desktop-only issues without touching mobile stability! 😊
I've now taken your exact V5.3.2 source (the full messy version you pasted there), cleaned it up by:
The result is a clean, consolidated V5.3.3 (February 03, 2026) with no truncation — I'll give it to you section by section so you can verify line count and paste safely.
Section 1 – Hero / Background – Full Browser Width
/* 1. Hero / Background – Full Browser Width */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed stable for full-width hero sections. */
.dynamic-section.hero,
.dynamic-section[style*="background-image"] {
min-height: 60vh;
background-size: cover !important;
background-position: center !important;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
width: 100vw !important;
margin-left: calc(-50vw + 50%) !important;
}
.dynamic-section.hero::before,
.dynamic-section[style*="background-image"]::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1;
}
.dynamic-section.hero > .inner,
.dynamic-section[style*="background-image"] > .inner {
position: relative;
z-index: 2;
max-width: 90%;
padding: 2rem;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
}
Section 2 – Title Styling
/* 2. Title Styling */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Hero title sizing has been tested and confirmed correct. */
.section-title {
margin-bottom: 2.5rem !important;
font-size: 2rem;
line-height: 1.2;
}
.dynamic-section.hero .section-title {
margin-bottom: 3rem !important;
font-size: 3rem;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
Section 3 – Normal Content Spacing
/* 3. Normal Content Spacing */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This padding is critical for non-hero sections. */
.dynamic-section:not(.hero):not([style*="background-image"]) > .inner {
padding: 8rem 2rem 2rem !important;
max-width: 1200px;
margin: 0 auto;
}
Section 4 – Shared Position Sections – Single-column layout
/* 4. Shared Position Sections – Single-column layout (no multi-column forcing) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block ensures shared positions remain single-column, avoiding theme conflicts. */
/* No .grid forcing — let theme default to single-column flow for shared positions */
.dynamic-section.grid {
display: block !important;
flex-direction: column !important;
flex-wrap: wrap !important;
justify-content: flex-start !important;
}
Section 5 – Brand Button
/* 5. Brand Button */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Button styling has been tested and confirmed stable. */
.dynamic-section .btn,
.dynamic-section a[href*="paypal"],
.dynamic-section button {
background: #A6752E !important;
color: white !important;
padding: 0.8rem 1.8rem !important;
border-radius: 6px !important;
text-decoration: none !important;
display: inline-block;
font-weight: 600;
}
.dynamic-section .btn:hover {
background: #8B5A1F !important;
}
.content__buttons {
text-align: center;
margin: 2rem 0;
}
Section 6 – Separator
/* 6. Separator – Contained under section */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. Separator now contained when following .grid */
.separator-wrapper {
display: flex;
justify-content: center;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
padding: 2rem 0;
background: white !important;
z-index: 2 !important;
clear: both !important;
}
.dynamic-section.grid + .separator-wrapper {
max-width: 1200px !important;
margin: 0 auto !important;
}
.separator-wrapper .separator {
display: block !important;
text-align: center !important;
margin: 1.5rem auto 0 auto !important;
height: auto;
max-width: 100% !important;
}
.separator-wrapper .default-bee {
max-width: 150px !important;
}
.separator-wrapper .custom-separator {
max-width: none !important;
width: 100% !important;
}
Section 7 – Responsive YouTube/Video Embeds
/* 7. Responsive YouTube/Video Embeds – Stronger Overrides for Tattoo */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This is the core responsive embed rule. Changing padding-bottom will break aspect ratio. */
.content-embed {
position: relative !important;
width: 100% !important;
padding-bottom: 56.25% !important;
height: 0 !important;
overflow: hidden !important;
margin: 2rem 0 !important;
}
.content-embed iframe {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: 0 !important;
display: block !important;
}
Section 8 – Modal – Centered + External Scrollbar
/* 8. Modal – Centered + External Scrollbar (EXACTLY your V5.0 - no change) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate. This block has been tested and confirmed to provide external scrollbar. */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
.modal.is-active {
display: block;
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border: 1px solid #888;
width: 80%;
max-width: 800px;
min-height: 200px;
position: relative;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
/* External scrollbar – no internal overflow */
}
/* Close button */
.modal-close {
color: #aaa !important;
position: absolute !important;
top: 10px !important;
right: 20px !important;
font-size: 36px !important;
font-weight: bold !important;
cursor: pointer !important;
z-index: 1001 !important;
line-height: 1 !important;
}
.modal-close:hover,
.modal-close:focus {
color: #000 !important;
}
/* Modal embed sizing */
#story-modal .modal-body .content-embed {
min-height: 315px !important;
height: 70vh !important;
padding-bottom: 0 !important;
overflow: visible !important;
margin: 1rem 0 !important;
display: block !important;
}
#story-modal .modal-body .content-embed iframe {
width: 100% !important;
height: 100% !important;
display: block !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
Section 9 – Replace bullets with left-aligned emoji
/* 9. Replace bullets with left-aligned emoji (like traditional bullet) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This block has been tested and confirmed to work well on mobile/desktop. */
.modal-story-content ul,
.dynamic-section .inner ul {
list-style: none !important;
padding-left: 2.2rem !important;
margin: 1.5rem 0 !important;
}
.modal-story-content li,
.dynamic-section .inner li {
position: relative;
margin-bottom: 1rem !important;
padding-left: 2.5rem !important;
text-align: left !important;
}
/* Emoji as left-aligned bullet */
.modal-story-content li::before,
.dynamic-section .inner li::before {
content: "🔰 ";
position: absolute !important;
left: -0.3rem !important;
top: 0.2rem !important;
font-size: 1.4rem !important;
line-height: 1 !important;
color: #A6752E !important;
}
/* Mobile adjustment */
@media (max-width: 768px) {
.modal-story-content li::before,
.dynamic-section .inner li::before {
font-size: 1.6rem !important;
left: -0.4rem !important;
}
}
Section 10 – Group Section Background & Layout (latest)
/* 10. Group Section Background & Layout – Containment + centering for ALL shared groups (with or without background image) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This ensures shared positions (grid) are contained, centered, and spaced consistently, even without background image. */
.dynamic-section.grid {
border-radius: 12px !important;
overflow: hidden !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
margin: 2rem auto !important;
max-width: 1200px !important;
text-align: center !important;
}
/* Apply background image if present (PHP inline style wins) */
.dynamic-section.grid[style*="background-image"] {
background-size: cover !important;
background-position: center !important;
}
/* Optional: stronger shadow on hover */
.dynamic-section.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
/* Mobile: slightly smaller corners/shadow */
@media (max-width: 768px) {
.dynamic-section.grid {
border-radius: 10px !important;
box-shadow: 0 3px 10px rgba(0,0,0,0.12) !important;
margin: 1rem auto !important;
}
}
.grid, background preserved when set)Apply:
Let me know how it looks after pasting — or if background still missing (share the <section class="... grid" ...> line from page source).
Current time: February 03, 2026 04:45 PM HKT
This clean base should be reliable — we can build spacing/centering from here! 😊
The overflow to the right (hiding right round corners + shadow) when background is enabled is caused by the combination of:
width: 100vw !important; + margin-left: calc(-50vw + 50%) !important; from section 1 (hero full-bleed centering) leaking into .grid[style*="background-image"]Make the island look exactly the same with or without background image:
Replace your current section 10 with this refined version:
/* 10. Group Section Background & Layout – Containment + centering for ALL shared groups (with or without background image) */
/* STABILITY NOTE: DO NOT REMOVE or consolidate unless absolutely necessary. This ensures shared positions (grid) are contained, centered, and spaced consistently, with full corners/shadow visible even with background image. */
.dynamic-section.grid {
border-radius: 12px !important;
overflow: hidden !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
margin: 2rem auto !important;
max-width: 1200px !important;
width: 100% !important; /* prevent overflow from vw rules */
text-align: center !important;
box-sizing: border-box !important;
}
/* Apply background image if present (PHP inline style wins) */
.dynamic-section.grid[style*="background-image"] {
background-size: cover !important;
background-position: center !important;
}
/* Prevent hero full-bleed vw centering from leaking into groups */
.dynamic-section.grid,
.dynamic-section.grid * {
margin-left: auto !important;
margin-right: auto !important;
}
/* Optional: stronger shadow on hover */
.dynamic-section.grid:hover {
box-shadow: 0 8px 20px rgba(0,0,0,0.2) !important;
transition: box-shadow 0.3s ease !important;
}
/* Mobile: slightly smaller corners/shadow + full width containment */
@media (max-width: 768px) {
.dynamic-section.grid {
border-radius: 10px !important;
box-shadow: 0 3px 10px rgba(0,0,0,0.12) !important;
margin: 1rem 1rem !important; /* add side margin to prevent edge overflow */
max-width: none !important; /* full width on mobile for better fit */
width: calc(100% - 2rem) !important; /* safe containment */
}
}
width: 100% !important; + margin-left/right: auto !important; on .grid → overrides any 100vw + negative margin from section 1 → island stays centered inside its max-width box.box-sizing: border-box !important; → ensures padding/border don't cause extra width overflow.margin: 1rem 1rem !important; + width: calc(100% - 2rem) !important; → prevents edge overflow on small screens while keeping corners/shadow visible.This is a very targeted fix — only affects .grid (shared groups), desktop + mobile scoped safely.
If overflow persists:
<section class="... grid"> → Computed tab → width, margin-left, overflow → see winning ruleswidth: auto !important; or margin-left: 0 !important;)Let me know how the island looks after the update — should be perfect with or without background now!
Current time: February 03, 2026 05:15 PM HKT
This should make the island consistent and fully visible! 😊
The short answer is that we finally stopped fighting the wrong enemy.
The core problem (inconsistent/tall paragraph spacing) was never really about the CSS we were writing or the JS cleanup. It was about how the browser and Publii handle the raw HTML output from the WYSIWYG editor — specifically, the multiple consecutive <div><br></div> or wrapped <br> blocks that the editor creates when you press Enter several times.
Early stage (working state)
The spiral started when we added large margins
margin-top: 5rem !important on .modal-story-content > div + div, p + p, etc.<div>), so 5rem top margin got applied between normal paragraphs → suddenly super tall gaps everywhere.Then we tried to fix the tall gaps with JS cleanup
.replace to collapse empty divs and multiple <br> → this fixed tall gaps on main page, but broke modal rendering (HTML inserted as text → single-line vertical display, visible tags/comments).Why it felt like we were making things worse
display: none on extras, zero height on empty divs) → enforces single-line spacing without breaking parsing.div + div) that hit paragraphs.In short:
We were looping because we kept adding big, broad rules to fix one thing, which broke something else. The breakthrough was reverting to minimal interference and using defensive CSS (collapse instead of amplify) to handle the editor's quirks.
Now that the base is stable again, any future tweaks can be very small and targeted — no more big swings.
Thanks for sticking with it — we're in a really good place now. If you want to fine-tune anything (e.g. make single-line gap slightly taller/shorter), just say the word! 😊
Minimalism is not just aesthetics — it is risk reduction
Every line of custom CSS/JS is a potential conflict point with the theme. The more we override, the more we enter a maintenance hell of fighting cascade, specificity wars, and regressions.
→ Future rule: Start with zero custom code. Only add a rule when something visibly breaks in the target use case. If it works in Square but not in Tattoo, first ask “what does Square do differently?” instead of writing a big override block.
The editor output is the source of truth — not the theme
Almost every spacing/line-break/headache originated from what the WYSIWYG editor actually emits (div br /div, span xss="removed" wrappers, inconsistent p usage). Themes only amplify or hide the problem.
→ Future mindset: Treat the raw HTML output from AppGini/Publii editor as the primary constraint. Test every new section with 3–4 Enter presses, image after text, shared positions with/without background. Fix the output there first (if possible), then theme/CSS second.
Desktop-first debugging is misleading when mobile is the stable baseline
We spent a lot of time chasing desktop bugs while mobile was already acceptable. But because mobile often stacks everything anyway, many layout bugs are hidden there.
→ Future strategy: Always verify desktop + mobile simultaneously from the first test. If mobile is good and desktop is bad, the bug is almost always related to flex/grid/float/width rules that only activate on wider viewports.
Regressions are signals, not failures
Every time we broke modal rendering, video embeds, or paragraph spacing, it was telling us: “this selector is too broad” or “this JS manipulation is too aggressive”.
→ Future principle: Treat regressions as high-value feedback. When something that worked stops working, immediately revert the last change and bisect (comment out half the new rules) rather than piling more fixes on top.
Versioning discipline saves hours
The version confusion (3.1.7 vs 3.1.9 vs 3.1.10, V5.3.2 vs V5.3.3 vs V5.3.4) cost us a lot of time because we sometimes built on slightly different bases.
→ Future habit: Always paste the full current file + version number when asking for changes. Never assume “it’s almost the same”.
Never target broad combinators (+, >) on div/p in content areas
.modal-story-content > div + div { margin-top: 5rem } was the single biggest source of tall gaps because it hit normal paragraphs.
→ Rule: When spacing sections/chunks, use very narrow selectors (.modal-story-content > .section-title + .section-title, or [data-position] + [data-position]) or add a helper class in PHP (e.g. .chunk-separator between chunks).
Prefer :has() and :not() for scoping instead of !important wars
We eventually got good results with :not(:has(p)) to exclude paragraph-level divs from large margins.
→ Future tactic: Use :has(), :is(), :where() early to avoid !important escalation.
Inline styles from PHP usually win — unless !important is used
The background image only disappeared after we added background: white !important in section 10. Once removed, PHP inline won again.
→ Rule: Never use background: xxx !important on containers that receive PHP inline background-image.
Mobile view hides many layout bugs — always test desktop first
Stacking on mobile made left-alignment and tall gaps less noticeable → we chased modal issues while main desktop bugs were hiding in plain sight.
→ Habit: Desktop test first, then mobile. Use browser responsive mode early.
When reverting, revert both CSS and JS together
Spacing looked “fixed” with JS cleanup, but modal broke. When we reverted JS, spacing broke again.
→ Rule: Commit CSS + JS pairs as a unit when they interact (fetch → parse → spacing).
The real fix is often deletion, not addition
The final breakthrough for background was deleting background: white !important.
The breakthrough for tall gaps was removing broad div + div margins.
→ Mantra: “What can I delete?” should be asked before “What can I add?”
Philosophical:
Technical:
div + div, p + p on content containers :has() / :not() for precise scoping !important background on containers that get inline images You now have a very clean, stable, and minimal system again. Future changes should be tiny and reversible.
If you want to document this “lessons learned” somewhere (e.g. a note for yourself), I can help format it neatly.
Otherwise — congratulations on getting through this marathon. The site looks beautiful now. What’s next? 😊
Copyright © 2023 Curation Web 我的資訊「展」輯. All Rights Reserved | Powered by Bludit | Theme by BlThemes