AI 對話紀錄  September 24, 2025

【AG-29】Chaplaincy Admin CMS Content Frame Sizing & PayPal Buttons / Server CSP Settings

【AG-29】Chaplaincy Admin CMS Content Frame Sizing & PayPal Buttons / Server CSP Settings

Q: An issue I found is when the browser window is narrowed down but before it reaches the width of mobile view, the right content area is not centered properly, as shown in image 2 with two red arrows of different lengths. However, I'm unable to find which class contribute to that.

Image description

A: I’m thrilled to hear that Version 3.4 successfully resolved the gap—great progress! Thanks for the new details about the line on the right top being removed by disabling the circled styles of the yellow-highlighted class in Image 1, and for pointing out the centering issue in Image 2 when the browser window is narrowed but before it reaches the mobile view (between PC and mobile breakpoints). The red arrows of different lengths suggest the right content area (likely .main__right) isn’t aligning properly in this intermediate width range. Since you couldn’t identify the contributing class, I’ll analyze the issue based on the page-empty.hbs template and previous CSS, propose a fix, and provide an updated custom.css (Version 3.5) with version numbering.

Updated Issue Analysis

  • Line Removal: The yellow-highlighted class in Image 1 (likely .banner--after-content or a related style) had circled properties (e.g., border, margin, or padding) that, when disabled, removed the line. This suggests the line is a border or spacing artifact we can target.
  • Centering Issue: The intermediate width range (between PC and mobile, likely 768px to a smaller threshold) shows uneven red arrows, indicating .main__right or its content isn’t centering. This could stem from:
    • Flexbox or Float Behavior: The "Square" theme might use flexbox or floats that break centering in this range.
    • Max-Width Mismatch: .main__right or .content__entry might not adjust its max-width or margin correctly.
    • Responsive Breakpoint: The transition between PC (max-width: 1200px) and mobile (width: 100vw) lacks a smooth adjustment.
  • Template Context: .main__right contains .content__entry--full and conditionally .main__right--bottom, which might shift alignment without proper constraints.

Fix Strategy

  1. Remove the Line:
    • Target the yellow-highlighted class (assumed .banner--after-content) and set border: none !important and outline: none !important to remove the line, based on the circled styles.
  2. Fix Centering:
    • Add a media query for the intermediate range (e.g., 768px to 1024px) to enforce centering on .main__right using margin: 0 auto and a dynamic max-width (e.g., 90% or calculated value).
    • Ensure .content__entry--full and .main__right--bottom inherit this centering.
  3. Preserve Layout:
    • Maintain the two-column (PC) and single-column (mobile) layouts.
  4. Add Version Comment: Include /* Custom CSS for NLT Chaplaincy CMS - Version 3.5 */.

Updated custom.css (Version 3.5)

Below is the updated custom.css (Version 3.5) addressing the line and centering.

/* Custom CSS for NLT Chaplaincy CMS - Version 3.5 */

/* Ensure myButton overrides theme styles */
.content__buttons a.myButton {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.myButtonBig {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Set default font for the entire site */
body {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Test class to verify font loading */
.font-test {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 24px;
    color: red;
}

/* Dynamic Section Styles */
.content__entry {
    position: relative;
    padding: 20px;
    min-height: 200px;
    margin: 0 auto;
    max-width: 1200px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--full {
    width: auto;
    position: relative;
    left: 0;
    transform: none;
    margin: 0 auto;
    max-width: 1200px;
    min-height: 400px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

@media (max-width: 768px) {
    .section-hero--full {
        width: 100vw;
        left: 50%;
        transform: translateX(-50%);
        margin: 0;
        padding: 0;
        min-height: 300px;
    }
    .section-hero__content {
        padding: 10px;
    }
    .section-hero__content h1 {
        font-size: 1.5em;
        margin-bottom: 0.75em;
        padding-top: 0.75em;
    }
    .generic-content {
        font-size: 0.9em;
        max-width: 100%;
        padding: 0 10px;
    }
}

.section-hero__content {
    position: relative;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    z-index: 1;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--full[style*="background-image"] h1,
.section-hero--full[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.section-hero--full h1,
.section-hero--full .generic-content {
    color: #333;
}

/* Override theme's heading font variable */
h1, h2, h3, h4, h5, h6 {
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
    margin-bottom: 1.5em !important; /* Increased margin for taller spacing */
}

/* Ensure specific heading styles inherit the font */
.content__entry h1 {
    font-size: 2em;
    font-weight: 700;
    line-height: 1.2;
    color: #333;
    margin-bottom: 1.5em !important; /* Taller margin */
    padding-top: 1em;
    text-align: center;
    text-shadow: none;
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
}

.generic-content {
    text-align: center;
    margin: 0 auto;
    max-width: 800px;
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    margin-bottom: 1em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry[style*="background-image"] h1,
.content__entry[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.generic-content p {
    margin: 0 0 1em 0;
}

.generic-content ul,
.generic-content ol {
    margin: 0 0 1em 0;
    padding: 0 !important;
    list-style: none !important;
}

.generic-content li {
    margin-bottom: 0.5em;
    text-align: center;
    list-style: none !important;
}

.generic-content ul[style*="list-style"],
.generic-content ol[style*="list-style"],
.generic-content li[style*="list-style"] {
    list-style: none !important;
}

.content__buttons {
    text-align: center !important;
    margin: 2em 0 1em 0;
}

.content__buttons a.myButton {
    display: inline-block;
    margin: 0 auto;
}

.content__entry .content__buttons {
    text-align: center !important;
}

.welcome-image {
    max-width: 100%;
    height: auto;
    margin: 1em auto;
    display: block;
    border-radius: 8px; /* Added for rounded corners */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Added for light shadow */
}

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
    background: none;
}

.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.separator {
    text-align: center;
    margin: 60px 0 20px 0;
}

/* Remove min-height and top spacing from the first content__entry--full */
.content__entry.content__entry--full:first-child {
    min-height: 0 !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
}

/* Adjust margin-top to close gap on PC view with maximum specificity */
.main.page .main__right:first-child {
    margin-top: -3.8rem !important;
    margin-top: -3.8rem !important; /* Redundant !important for priority */
}

/* Remove extra padding on right scrolling frame with maximum specificity */
.main.page .main__right {
    margin: 0% !important;
    margin: 0% !important; /* Redundant !important for priority */
}

/* Target the gap-causing class (.banner--after-content) and remove the line */
.banner--after-content {
    margin: 0 !important;
    margin: 0 !important; /* Redundant !important for priority */
    padding: 0 !important;
    padding: 0 !important; /* Redundant !important for priority */
    border: none !important;
    border: none !important; /* Redundant !important for priority */
    outline: none !important;
    outline: none !important; /* Redundant !important for priority */
}

/* Reset and adjust spacing for mobile view with maximum specificity */
@media (max-width: 768px) {
    .main.page .main__right:first-child {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    /* Target potential parent containers in mobile view */
    .main.page .container:first-child,
    .main.page .post__entry:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .main__right .main__right--bottom:first-child {
        margin-top: -3.8rem !important;
        margin-top: -3.8rem !important; /* Redundant !important for priority */
        margin-bottom: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .container > .main__right > .main__right--bottom:first-child,
    .main.page .post__entry > .main__right > .main__right--bottom:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    /* Target the gap-causing class (.banner--after-content) in mobile view */
    .banner--after-content {
        margin: 0 !important;
        margin: 0 !important; /* Redundant !important for priority */
        padding: 0 !important;
        padding: 0 !important; /* Redundant !important for priority */
        border: none !important;
        border: none !important; /* Redundant !important for priority */
        outline: none !important;
        outline: none !important; /* Redundant !important for priority */
    }
}

/* Intermediate width range (e.g., 768px to 1024px) to fix centering */
@media (min-width: 769px) and (max-width: 1023px) {
    .main.page .main__right {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 90% !important;
        max-width: 90% !important; /* Redundant !important for priority */
        width: auto !important;
        width: auto !important; /* Redundant !important for priority */
    }
    .main.page .main__right .content__entry--full {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 100% !important;
        max-width: 100% !important; /* Redundant !important for priority */
    }
    .main.page .main__right .main__right--bottom {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 100% !important;
        max-width: 100% !important; /* Redundant !important for priority */
    }
}

/* Ensure first section-hero--full has no extra top margin */
.main.page .main__right .main__right--bottom .section-hero--full:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* Remove top margin from the first separator within content__entry--full */
.content__entry.content__entry--full .separator:first-child {
    margin-top: 0;
}

.section-hero--full:not([style*="background-image"]) + .separator {
    margin-top: 40px;
}

.separator-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

.separator img[src$="down_arrow.png"] {
    max-width: 256px;
}

hr {
    display: none !important;
    visibility: hidden !important;
}

.post__content hr,
.container hr,
.post__content hr[style],
.container hr[style] {
    display: none !important;
    visibility: hidden !important;
}

.post__content::before,
.post__content::after,
.container::before,
.container::after,
.post__entry::before,
.post__entry::after,
.content__entry::before,
.content__entry::after,
.generic-content::before,
.generic-content::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

.separator::before,
.separator::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

body,
body p,
body h2,
body h3,
body h4,
body h5,
body h6,
.post__content,
.post__content p,
.post__content h2,
.post__content h3,
.post__content h4,
.post__content h5,
.post__content h6,
.container,
.container p,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6 {
    color: #333 !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

h1.post__title,
h1.hero__title,
h1.site__title {
    color: #fff !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.button,
a.btn,
a.post__button,
.post__content a.button,
.post__content a.btn,
.post__content a.post__button,
.container a.button,
.container a.btn,
.container a.post__button {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Custom Modal Styles */
.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-content {
    background-color: #fff;
    margin: 5% auto;
    padding: 20px;
    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;
}

.modal-close {
    color: #aaa;
    position: absolute;
    top: 10px;
    right: 20px;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.3s ease;
    z-index: 1001;
}

.modal-close:hover,
.modal-close:focus {
    color: #000;
    text-decoration: none;
}

#modal-body {
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    max-width: 100%;
    box-sizing: border-box;
    padding-top: 40px;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body h1 {
    font-size: 1.5em;
    margin-bottom: 0.5em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body p {
    margin-bottom: 1em;
}

#modal-body * {
    max-width: 100%;
    box-sizing: border-box;
}

#modal-body img,
#modal-body div,
#modal-body table {
    max-width: 100%;
    height: auto;
}

#modal-body .welcome-image {
    border-radius: 8px; /* Ensure rounded corners in mobile */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Ensure light shadow in mobile */
}

.modal[aria-hidden="false"] {
    display: block;
}

.modal-close:focus {
    outline: 2px solid #44c767;
    outline-offset: 2px;
}

/* Custom separator for modal sections */
.modal-section-separator {
    text-align: center;
    margin: 20px 0;
    font-size: 24px;
    line-height: 1;
    color: #666;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}
.modal-section-separator::before {
    content: "···";
}

/* Chaplaincy Admin specific overrides */
.report-container {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry table.report-table {
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

Changes in custom.css (Version 3.5)

  • Remove the Line:
    • Added border: none !important and outline: none !important to .banner--after-content with redundant !important to match the circled styles from Image 1.
  • Fix Centering:
    • Added @media (min-width: 769px) and (max-width: 1023px) to target the intermediate range.
    • Set .main.page .main__right { margin: 0 auto !important; max-width: 90% !important; width: auto !important; } to enforce centering.
    • Applied similar rules to .content__entry--full and .main__right--bottom to align their content.
  • Version Comment: Updated to /* Custom CSS for NLT Chaplaincy CMS - Version 3.5 */.

Troubleshooting Steps

  1. Verify File Deployment:
    • Ensure custom.css is in /Web/nlhcm/public/assets/css/custom.css.
    • Check the file’s modification time (should be after 07:40 PM HKT on August 11, 2025).
  2. Clear All Caches:
    • Hard refresh (Ctrl+Shift+R or Cmd+Shift+R).
    • Clear browser and Publii caches, then regenerate and publish.
    • Clear server cache if applicable.
  3. Check CSS Load:
    • Use DevTools Network tab to confirm custom.css loads with a 200 status.
    • In Elements tab, verify .banner--after-content shows border: none !important and .main__right shows margin: 0 auto !important in the intermediate range.

Testing Instructions for custom.css (Version 3.5)

  1. Deploy File:
    • Replace the existing custom.css and follow troubleshooting steps.
  2. Test Gap and Line Removal:
    • Visit https://www.nlt-chaplaincy.org/index.php and https://www.nlt-chaplaincy.org/stories.php at 08:00 PM HKT on Monday, August 11, 2025.
    • Check PC and mobile views to confirm the gap and line are gone, with .banner--after-content showing border: none !important.
  3. Test Centering:
    • Narrow the browser window to the intermediate range (e.g., 769px to 1023px) and confirm .main__right is centered, with red arrows (if re-added) showing equal lengths.
    • Verify .content__entry--full and .main__right--bottom align properly.
  4. Test Layout:
    • Confirm the two-column (PC) and single-column (mobile) layouts.
    • Check alignment with .section-hero--full.
  5. Test with Content:
    • Add the paragraph on index.php and test all views.
  6. Inspect Styling:
    • Confirm .welcome-image, .myButton, and .myButtonBig styles.
  7. Fallback Check:
    • If centering issues persist, share a screenshot of the CSS cascade for .main__right in the intermediate range, noting the window width.

Publii Context

  • Template Insight: .banner--after-content’s border caused the line, and the intermediate range needs explicit centering rules.
  • Next Issue: Let me know if further adjustments are needed!

The file management plan remains available for your review.


Q: With this version the centering issue is solved but the top line is not removed. I wonder if the correct class ".top__item--right" was addressed in your fix?


A: I’m glad to hear the centering issue is resolved with Version 3.5—great progress! However, I apologize that the top line persists, and thank you for suggesting .top__item--right as a potential class to address. Upon reviewing, it seems there was a misunderstanding: the previous fix targeted .banner--after-content for the line removal based on your earlier input, but your latest message indicates .top__item--right might be the correct class causing the line. Since .top__item--right isn’t explicitly mentioned in the page-empty.hbs template you provided, it could be part of the theme’s broader layout (e.g., a header or sidebar element) or dynamically injected. I’ll adjust the custom.css to target .top__item--right for the line removal, while preserving the centering fix, and provide an updated custom.css (Version 3.6) with version numbering.

Updated Issue Analysis

  • Centering Resolved: The @media (min-width: 769px) and (max-width: 1023px) rules successfully centered .main__right, addressing the uneven red arrows.
  • Top Line Persistence: The line not being removed suggests .banner--after-content’s border: none !important and outline: none !important didn’t target the correct element. Your mention of .top__item--right implies this class, possibly in the header or a right-aligned top section, is responsible for the line (e.g., a border or divider).
  • Class Context: .top__item--right might be part of the navbar partial (included via {{> navbar}} in page-empty.hbs) or a theme-specific style, contributing to the top layout.

Fix Strategy

  1. Remove the Top Line:
    • Target .top__item--right with border: none !important, outline: none !important, and box-shadow: none !important to eliminate the line.
    • Apply this globally, as it may affect both PC and mobile views.
  2. Preserve Centering Fix:
    • Retain the intermediate range media query for .main__right, .content__entry--full, and .main__right--bottom.
  3. Preserve Gap Fix:
    • Keep .banner--after-content rules and margin-top: -3.8rem adjustments.
  4. Preserve Layout:
    • Ensure the two-column (PC) and single-column (mobile) layouts remain intact.
  5. Add Version Comment: Include /* Custom CSS for NLT Chaplaincy CMS - Version 3.6 */.

Updated custom.css (Version 3.6)

Below is the updated custom.css (Version 3.6) targeting .top__item--right for the line.

/* Custom CSS for NLT Chaplaincy CMS - Version 3.6 */

/* Ensure myButton overrides theme styles */
.content__buttons a.myButton {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.myButtonBig {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Set default font for the entire site */
body {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Test class to verify font loading */
.font-test {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 24px;
    color: red;
}

/* Dynamic Section Styles */
.content__entry {
    position: relative;
    padding: 20px;
    min-height: 200px;
    margin: 0 auto;
    max-width: 1200px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--full {
    width: auto;
    position: relative;
    left: 0;
    transform: none;
    margin: 0 auto;
    max-width: 1200px;
    min-height: 400px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

@media (max-width: 768px) {
    .section-hero--full {
        width: 100vw;
        left: 50%;
        transform: translateX(-50%);
        margin: 0;
        padding: 0;
        min-height: 300px;
    }
    .section-hero__content {
        padding: 10px;
    }
    .section-hero__content h1 {
        font-size: 1.5em;
        margin-bottom: 0.75em;
        padding-top: 0.75em;
    }
    .generic-content {
        font-size: 0.9em;
        max-width: 100%;
        padding: 0 10px;
    }
}

.section-hero__content {
    position: relative;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    z-index: 1;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--full[style*="background-image"] h1,
.section-hero--full[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.section-hero--full h1,
.section-hero--full .generic-content {
    color: #333;
}

/* Override theme's heading font variable */
h1, h2, h3, h4, h5, h6 {
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
    margin-bottom: 1.5em !important; /* Increased margin for taller spacing */
}

/* Ensure specific heading styles inherit the font */
.content__entry h1 {
    font-size: 2em;
    font-weight: 700;
    line-height: 1.2;
    color: #333;
    margin-bottom: 1.5em !important; /* Taller margin */
    padding-top: 1em;
    text-align: center;
    text-shadow: none;
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
}

.generic-content {
    text-align: center;
    margin: 0 auto;
    max-width: 800px;
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    margin-bottom: 1em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry[style*="background-image"] h1,
.content__entry[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.generic-content p {
    margin: 0 0 1em 0;
}

.generic-content ul,
.generic-content ol {
    margin: 0 0 1em 0;
    padding: 0 !important;
    list-style: none !important;
}

.generic-content li {
    margin-bottom: 0.5em;
    text-align: center;
    list-style: none !important;
}

.generic-content ul[style*="list-style"],
.generic-content ol[style*="list-style"],
.generic-content li[style*="list-style"] {
    list-style: none !important;
}

.content__buttons {
    text-align: center !important;
    margin: 2em 0 1em 0;
}

.content__buttons a.myButton {
    display: inline-block;
    margin: 0 auto;
}

.content__entry .content__buttons {
    text-align: center !important;
}

.welcome-image {
    max-width: 100%;
    height: auto;
    margin: 1em auto;
    display: block;
    border-radius: 8px; /* Added for rounded corners */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Added for light shadow */
}

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
    background: none;
}

.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.separator {
    text-align: center;
    margin: 60px 0 20px 0;
}

/* Remove min-height and top spacing from the first content__entry--full */
.content__entry.content__entry--full:first-child {
    min-height: 0 !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
}

/* Adjust margin-top to close gap on PC view with maximum specificity */
.main.page .main__right:first-child {
    margin-top: -3.8rem !important;
    margin-top: -3.8rem !important; /* Redundant !important for priority */
}

/* Remove extra padding on right scrolling frame with maximum specificity */
.main.page .main__right {
    margin: 0% !important;
    margin: 0% !important; /* Redundant !important for priority */
}

/* Target the gap-causing class (.banner--after-content) */
.banner--after-content {
    margin: 0 !important;
    margin: 0 !important; /* Redundant !important for priority */
    padding: 0 !important;
    padding: 0 !important; /* Redundant !important for priority */
    border: none !important;
    border: none !important; /* Redundant !important for priority */
    outline: none !important;
    outline: none !important; /* Redundant !important for priority */
}

/* Target .top__item--right to remove the top line */
.top__item--right {
    border: none !important;
    border: none !important; /* Redundant !important for priority */
    outline: none !important;
    outline: none !important; /* Redundant !important for priority */
    box-shadow: none !important;
    box-shadow: none !important; /* Redundant !important for priority */
}

/* Reset and adjust spacing for mobile view with maximum specificity */
@media (max-width: 768px) {
    .main.page .main__right:first-child {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    /* Target potential parent containers in mobile view */
    .main.page .container:first-child,
    .main.page .post__entry:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .main__right .main__right--bottom:first-child {
        margin-top: -3.8rem !important;
        margin-top: -3.8rem !important; /* Redundant !important for priority */
        margin-bottom: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .container > .main__right > .main__right--bottom:first-child,
    .main.page .post__entry > .main__right > .main__right--bottom:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    /* Target the gap-causing class (.banner--after-content) in mobile view */
    .banner--after-content {
        margin: 0 !important;
        margin: 0 !important; /* Redundant !important for priority */
        padding: 0 !important;
        padding: 0 !important; /* Redundant !important for priority */
        border: none !important;
        border: none !important; /* Redundant !important for priority */
        outline: none !important;
        outline: none !important; /* Redundant !important for priority */
    }
    /* Target .top__item--right in mobile view */
    .top__item--right {
        border: none !important;
        border: none !important; /* Redundant !important for priority */
        outline: none !important;
        outline: none !important; /* Redundant !important for priority */
        box-shadow: none !important;
        box-shadow: none !important; /* Redundant !important for priority */
    }
}

/* Intermediate width range (e.g., 768px to 1023px) to fix centering */
@media (min-width: 769px) and (max-width: 1023px) {
    .main.page .main__right {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 90% !important;
        max-width: 90% !important; /* Redundant !important for priority */
        width: auto !important;
        width: auto !important; /* Redundant !important for priority */
    }
    .main.page .main__right .content__entry--full {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 100% !important;
        max-width: 100% !important; /* Redundant !important for priority */
    }
    .main.page .main__right .main__right--bottom {
        margin: 0 auto !important;
        margin: 0 auto !important; /* Redundant !important for priority */
        max-width: 100% !important;
        max-width: 100% !important; /* Redundant !important for priority */
    }
    /* Target .top__item--right in intermediate range */
    .top__item--right {
        border: none !important;
        border: none !important; /* Redundant !important for priority */
        outline: none !important;
        outline: none !important; /* Redundant !important for priority */
        box-shadow: none !important;
        box-shadow: none !important; /* Redundant !important for priority */
    }
}

/* Ensure first section-hero--full has no extra top margin */
.main.page .main__right .main__right--bottom .section-hero--full:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* Remove top margin from the first separator within content__entry--full */
.content__entry.content__entry--full .separator:first-child {
    margin-top: 0;
}

.section-hero--full:not([style*="background-image"]) + .separator {
    margin-top: 40px;
}

.separator-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

.separator img[src$="down_arrow.png"] {
    max-width: 256px;
}

hr {
    display: none !important;
    visibility: hidden !important;
}

.post__content hr,
.container hr,
.post__content hr[style],
.container hr[style] {
    display: none !important;
    visibility: hidden !important;
}

.post__content::before,
.post__content::after,
.container::before,
.container::after,
.post__entry::before,
.post__entry::after,
.content__entry::before,
.content__entry::after,
.generic-content::before,
.generic-content::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

.separator::before,
.separator::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

body,
body p,
body h2,
body h3,
body h4,
body h5,
body h6,
.post__content,
.post__content p,
.post__content h2,
.post__content h3,
.post__content h4,
.post__content h5,
.post__content h6,
.container,
.container p,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6 {
    color: #333 !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

h1.post__title,
h1.hero__title,
h1.site__title {
    color: #fff !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.button,
a.btn,
a.post__button,
.post__content a.button,
.post__content a.btn,
.post__content a.post__button,
.container a.button,
.container a.btn,
.container a.post__button {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Custom Modal Styles */
.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-content {
    background-color: #fff;
    margin: 5% auto;
    padding: 20px;
    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;
}

.modal-close {
    color: #aaa;
    position: absolute;
    top: 10px;
    right: 20px;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.3s ease;
    z-index: 1001;
}

.modal-close:hover,
.modal-close:focus {
    color: #000;
    text-decoration: none;
}

#modal-body {
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    max-width: 100%;
    box-sizing: border-box;
    padding-top: 40px;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body h1 {
    font-size: 1.5em;
    margin-bottom: 0.5em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body p {
    margin-bottom: 1em;
}

#modal-body * {
    max-width: 100%;
    box-sizing: border-box;
}

#modal-body img,
#modal-body div,
#modal-body table {
    max-width: 100%;
    height: auto;
}

#modal-body .welcome-image {
    border-radius: 8px; /* Ensure rounded corners in mobile */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Ensure light shadow in mobile */
}

.modal[aria-hidden="false"] {
    display: block;
}

.modal-close:focus {
    outline: 2px solid #44c767;
    outline-offset: 2px;
}

/* Custom separator for modal sections */
.modal-section-separator {
    text-align: center;
    margin: 20px 0;
    font-size: 24px;
    line-height: 1;
    color: #666;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}
.modal-section-separator::before {
    content: "···";
}

/* Chaplaincy Admin specific overrides */
.report-container {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry table.report-table {
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

Changes in custom.css (Version 3.6)

  • Remove the Top Line:
    • Added .top__item--right { border: none !important; outline: none !important; box-shadow: none !important; } with redundant !important to target the line, applied globally and in @media (max-width: 768px) and @media (min-width: 769px) and (max-width: 1023px).
  • Preserve Centering Fix:
    • Retained the intermediate range media query for .main__right, .content__entry--full, and .main__right--bottom.
  • Preserve Gap Fix:
    • Kept .banner--after-content rules and margin-top: -3.8rem adjustments.
  • Version Comment: Updated to /* Custom CSS for NLT Chaplaincy CMS - Version 3.6 */.

Troubleshooting Steps

  1. Verify File Deployment:
    • Ensure custom.css is in /Web/nlhcm/public/assets/css/custom.css.
    • Check the file’s modification time (should be after 07:45 PM HKT on August 11, 2025).
  2. Clear All Caches:
    • Hard refresh (Ctrl+Shift+R or Cmd+Shift+R).
    • Clear browser and Publii caches, then regenerate and publish.
    • Clear server cache if applicable.
  3. Check CSS Load:
    • Use DevTools Network tab to confirm custom.css loads with a 200 status.
    • In Elements tab, verify .top__item--right shows border: none !important and check if the line disappears.

Testing Instructions for custom.css (Version 3.6)

  1. Deploy File:
    • Replace the existing custom.css and follow troubleshooting steps.
  2. Test Line Removal:
    • Visit https://www.nlt-chaplaincy.org/index.php and https://www.nlt-chaplaincy.org/stories.php at 08:00 PM HKT on Monday, August 11, 2025.
    • Check PC view and confirm the top line is gone, with .top__item--right showing border: none !important.
    • Switch to mobile view and verify the line is gone.
  3. Test Centering:
    • Narrow the browser to the intermediate range (769px to 1023px) and confirm .main__right remains centered.
  4. Test Layout:
    • Confirm the two-column (PC) and single-column (mobile) layouts.
    • Verify alignment with .section-hero--full.
  5. Test with Content:
    • Add the paragraph on index.php and test all views.
  6. Inspect Styling:
    • Confirm .welcome-image, .myButton, and .myButtonBig styles.
  7. Fallback Check:
    • If the line persists, share a screenshot of the CSS cascade for .top__item--right or the element with the line, and let me know its exact location (e.g., header, top-right).

Publii Context

  • Class Insight: .top__item--right likely resides in the navbar partial or theme header, contributing the line.
  • Next Issue: We’ll refine further if needed!

The file management plan remains available for your review.

Original Chat


Q: As shown in the attached screenshots, theme image is leaving white spaces (Orange arrows) with the edge of the browser when set as "Background" in both desktop and mobile views. Can we enforce it to take up the entire width of the right frame (In desktop-view language)? To cater for smaller images I think we should enlarge any image to fill up the width as well!


A: Thanks for the feedback and the attached screenshot! I understand that with the current update (V4.8 of custom.css), the background image is being compressed (outside the red box), and you’d prefer to maintain its original aspect ratio. Additionally, the illustration image (inside the red box) appears too wide, and you’d like more margin on the sides, with conservative adjustments to also affect modal images. Let’s adjust the CSS to address these concerns while keeping changes balanced.

Analysis

1. Background Image Compression

  • The current background-size: 100% 100% stretches the image to fill both width and height, distorting the aspect ratio and causing compression (as seen outside the red box).
  • To preserve the aspect ratio, we should use background-size: cover (which maintains the aspect ratio while covering the container) or contain (which fits the image within the container without cropping), but cover is better for full-width coverage without white spaces.

2. Illustration Image Width

  • The illustration image (red box) is likely a .welcome-image within a .content-image or .section-hero__content div, constrained by max-width: 100% but possibly stretched due to the container’s width.
  • Adding more margin requires reducing the effective width, which can be achieved by setting a max-width less than 100% and adjusting padding or margins.

3. Modal Impact

  • Since changes should be conservative and affect modals (e.g., .modal-story-content .content-image), we’ll limit the width reduction and ensure it applies consistently across contexts.

4. Desired Outcome

  • Background image: Maintain aspect ratio, fill the width, and minimize white spaces.
  • Illustration image: Reduce width with more side margins, keeping adjustments subtle for modals.

Proposed Solution

We’ll update custom.css to:

  • Switch background-size: 100% 100% to cover for background images to preserve aspect ratio.
  • Adjust the illustration image’s max-width to 80% (conservative) and add padding to the container for side margins.
  • Apply these changes consistently to modal images.

Updated custom.css (V4.9)

/* Custom CSS for Lifenavi (navi.claudioflute.live) and Chaplaincy Admin (admin.nlt-chaplaincy.org) - Version 4.9 */
/* Shared styles for both systems, with domain-specific overrides as noted */

/* Ensure myButton overrides theme styles */
.content__buttons a.myButton {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.myButtonBig {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Set default font for the entire site */
body {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Test class to verify font loading */
.font-test {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 24px;
    color: red;
}

/* Dynamic Section Styles */
.content__entry {
    position: relative;
    padding: 20px;
    min-height: 200px;
    margin: 0 auto;
    max-width: 1200px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Hide empty content__entry div from Publii theme */
div.content__entry:empty {
    display: none;
}

/* Alternative: Hide content__entry div on dynamic pages (uncomment and add data-page-type="dynamic" to <body> if needed) */
/* body[data-page-type="dynamic"] div.content__entry {
    display: none;
} */

/* Enhanced Background Image Styles */
.section-hero--full[style*="background-image"],
.section-group[style*="background-image"] {
    width: 100% !important; /* Enforce full width of the container */
    max-width: none !important; /* Remove max-width constraint */
    margin-left: 0 !important;
    margin-right: 0 !important;
    background-size: cover !important; /* Preserve aspect ratio, cover the area */
    background-position: center !important;
    background-repeat: no-repeat !important;
}

.section-hero--full[style*="background-image"] .section-hero__content,
.section-group[style*="background-image"] .section-hero__content {
    background: none !important; /* Ensure content area doesn’t obscure background */
}

/* Responsive Adjustments */
@media (max-width: 768px) {
    .section-hero--full[style*="background-image"] {
        width: 100vw !important; /* Full viewport width on mobile */
        left: 50% !important;
        transform: translateX(-50%) !important;
        margin: 0 !important;
        padding: 0 !important;
        min-height: 300px;
        background-size: cover !important; /* Preserve aspect ratio on mobile */
    }
    .section-group[style*="background-image"] {
        width: 100vw !important; /* Full viewport width on mobile */
        margin: 0 !important;
        padding: 0 !important;
        background-size: cover !important; /* Preserve aspect ratio on mobile */
    }
}

/* Illustration Image Adjustments */
.content-image,
.modal-story-content .content-image {
    padding: 0 10px; /* Add conservative side margins */
    max-width: 80%; /* Reduce width to leave more margin, conservative adjustment */
    margin: 1em auto; /* Center with margins */
}

.welcome-image {
    max-width: 100%; /* Ensure image respects container width */
    height: auto;
    margin: 0 auto; /* Center within content-image */
    display: block;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* Ensure content is visible over background */
.section-hero--full[style*="background-image"] h1,
.section-hero--full[style*="background-image"] .generic-content,
.section-group[style*="background-image"] h1,
.section-group[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.section-hero--full h1,
.section-hero--column h1,
.section-hero--full .generic-content,
.section-hero--column .generic-content {
    color: #333;
}

.section-hero--full {
    width: auto;
    position: relative;
    left: 0;
    transform: none;
    margin: 0 auto;
    max-width: 1200px;
    min-height: 400px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--column {
    flex: 1;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    padding: 10px;
}

.section-group {
    display: flex;
    flex-wrap: nowrap; /* Prevent wrapping to new rows */
    gap: 20px;
    max-width: 1200px;
    margin: 0 auto;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.layout-2-column {
    display: flex;
    justify-content: space-between;
}
.layout-2-column .section-hero--column {
    flex: 1 0 49%; /* Slightly less than 50% to account for gap */
    max-width: 49%;
}

.layout-3-column {
    display: flex;
    justify-content: space-between;
}
.layout-3-column .section-hero--column {
    flex: 1 0 32%; /* Slightly less than 33.33% to account for gap */
    max-width: 32%;
}

@media (max-width: 768px) {
    .section-hero--full {
        width: 100vw;
        left: 50%;
        transform: translateX(-50%);
        margin: 0;
        padding: 0;
        min-height: 300px;
    }
    .section-hero--column {
        flex: 1 0 100%;
        max-width: 100%;
        margin-bottom: 20px;
    }
    .section-group {
        flex-wrap: wrap; /* Allow wrapping on mobile */
    }
    .section-hero__content {
        padding: 10px;
    }
    .section-hero__content h1 {
        font-size: 1.5em;
        margin-bottom: 0.75em;
        padding-top: 0.75em;
    }
    .generic-content {
        font-size: 0.9em;
        max-width: 100%;
        padding: 0 10px;
    }
}

.section-hero__content {
    position: relative;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    z-index: 1;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Alignment Classes */
.align-left {
    text-align: left;
}
.content-image.align-left, .video-embed.align-left {
    margin: 1em 0;
}
.align-center {
    text-align: center;
}

/* Override theme's heading font variable */
h1, h2, h3, h4, h5, h6 {
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
    margin-bottom: 1.5em !important;
}

/* Ensure specific heading styles inherit the font */
.content__entry h1 {
    font-size: 2em;
    font-weight: 700;
    line-height: 1.2;
    color: #333;
    margin-bottom: 1.5em !important;
    padding-top: 1em;
    text-align: center;
    text-shadow: none;
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
}

.generic-content {
    margin: 0 auto;
    max-width: 800px;
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    margin-bottom: 1em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry[style*="background-image"] h1,
.content__entry[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.generic-content p {
    margin: 0 0 1em 0;
}

.generic-content ul,
.generic-content ol {
    margin: 0 0 1em 0;
    padding: 0 !important;
    list-style: none !important;
}

.generic-content li {
    margin-bottom: 0.5em;
    text-align: inherit;
    list-style: none !important;
}

.generic-content ul[style*="list-style"],
.generic-content ol[style*="list-style"],
.generic-content li[style*="list-style"] {
    list-style: none !important;
}

.content__buttons {
    text-align: center !important;
    margin: 2em 0 1em 0;
}

.content__buttons a.myButton {
    display: inline-block;
    margin: 0 auto;
}

.content__entry .content__buttons {
    text-align: center !important;
}

.welcome-image {
    max-width: 100%; /* Ensure image respects container width */
    height: auto;
    margin: 0 auto; /* Center within content-image */
    display: block;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
    background: none;
}

.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.separator {
    text-align: center;
    margin: 60px 0 20px 0;
}

.content__entry.content__entry--full:first-child {
    min-height: 0 !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
}

.main.page .main__right:first-child {
    margin-top: -3.8rem !important;
}

.main.page .main__right {
    margin: 0% !important;
}

.banner--after-content {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    outline: none !important;
}

.top__item--right {
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
}

@media (max-width: 768px) {
    .main.page .main__right:first-child {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    .main.page .container:first-child,
    .main.page .post__entry:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .main__right .main__right--bottom:first-child {
        margin-top: -3.8rem !important;
        margin-bottom: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .container > .main__right > .main__right--bottom:first-child,
    .main.page .post__entry > .main__right > .main__right--bottom:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .banner--after-content {
        margin: 0 !important;
        padding: 0 !important;
        border: none !important;
        outline: none !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

@media (min-width: 769px) and (max-width: 1023px) {
    .main.page .main__right {
        margin: 0 auto !important;
        max-width: 90% !important;
        width: auto !important;
    }
    .main.page .main__right .content__entry--full {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .main.page .main__right .main__right--bottom {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

.main.page .main__right .main__right--bottom .section-hero--full:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

.content__entry.content__entry--full .separator:first-child {
    margin-top: 0;
}

.section-hero--full:not([style*="background-image"]) + .separator {
    margin-top: 40px;
}

.separator-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

.separator img[src$="down_arrow.png"] {
    max-width: 256px;
}

hr {
    display: none !important;
    visibility: hidden !important;
}

.post__content hr,
.container hr,
.post__content hr[style],
.container hr[style] {
    display: none !important;
    visibility: hidden !important;
}

.post__content::before,
.post__content::after,
.container::before,
.container::after,
.post__entry::before,
.post__entry::after,
.content__entry::before,
.content__entry::after,
.generic-content::before,
.generic-content::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

.separator::before,
.separator::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

body,
body p,
body h2,
body h3,
body h4,
body h5,
body h6,
.post__content,
.post__content p,
.post__content h2,
.post__content h3,
.post__content h4,
.post__content h5,
.post__content h6,
.container,
.container p,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6 {
    color: #333 !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

h1.post__title,
h1.hero__title,
h1.site__title {
    color: #fff !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.button,
a.btn,
a.post__button,
.post__content a.button,
.post__content a.btn,
.post__content a.post__button,
.container a.button,
.container a.btn,
.container a.post__button {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Custom Modal Styles */
.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-content {
    background-color: #fff;
    margin: 5% auto;
    padding: 20px;
    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;
}

.modal-close {
    color: #aaa;
    position: absolute;
    top: 10px;
    right: 20px;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.3s ease;
    z-index: 1001;
}

.modal-close:hover,
.modal-close:focus {
    color: #000;
    text-decoration: none;
}

#modal-body {
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    max-width: 100%;
    box-sizing: border-box;
    padding-top: 40px;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    overflow-x: hidden;
}

#modal-body h1 {
    font-size: 1.5em;
    margin-bottom: 0.5em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body p {
    margin-bottom: 1em;
}

#modal-body * {
    max-width: 100%;
    height: auto;
    box-sizing: border-box;
}

#modal-body img,
#modal-body div,
#modal-body table {
    max-width: 100%;
    height: auto;
}

#modal-body .welcome-image {
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.modal[aria-hidden="false"] {
    display: block;
}

.modal-close:focus {
    outline: 2px solid #44c767;
    outline-offset: 2px;
}

/* Custom separator for modal sections */
.modal-section-separator {
    text-align: center;
    margin: 20px 0;
    font-size: 24px;
    line-height: 1;
    color: #666;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}
.modal-section-separator::before {
    content: "···";
}

/* Modal-specific content container */
.modal-story-content {
    max-width: 100%;
    margin: 0 auto;
    padding: 20px;
    box-sizing: border-box;
    overflow-x: hidden;
}

.modal-story-content h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
    text-align: center;
}

.modal-story-content .content-image {
    padding: 0 10px; /* Add conservative side margins */
    max-width: 80%; /* Reduce width to leave more margin, conservative adjustment */
    margin: 1em auto; /* Center with margins */
}

.modal-story-content .generic-content {
    font-size: 1rem;
    line-height: 1.6;
}

/* Chaplaincy Admin specific overrides */
.report-container {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry table.report-table {
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Ensure background image applies to the section */
.section-hero--full[style*="background-image"] .section-hero__content,
.section-group[style*="background-image"] .section-hero__content {
    background: none;
}

Explanation of Changes

custom.css V4.9

  • Versioning: Updated to V4.9.
  • Background Image Aspect Ratio:
    • Changed background-size: 100% 100% to cover in .section-hero--full[style*="background-image"] and .section-group[style*="background-image"] to preserve the original aspect ratio while ensuring the image covers the container, minimizing white spaces.
    • Kept width: 100% !important and max-width: none !important to maintain full width coverage.
  • Illustration Image Adjustments:
    • Updated .content-image and .modal-story-content .content-image with:
    • max-width: 80% (down from 100%) to reduce width and leave more side margins.
    • padding: 0 10px to add conservative side spacing.
    • margin: 1em auto to center the image with consistent margins.
    • Ensured .welcome-image retains max-width: 100% to respect the container’s new width, avoiding further stretching.
  • Modal Consistency: Applied the same max-width: 80% and padding: 0 10px to .modal-story-content .content-image for conservative alignment with main content.
  • No Changes to pull-contents.php: The image application logic remains unaffected; only CSS is modified.

Testing Instructions

  1. Deploy Updated File:
    • Replace custom.css with V4.9.
    • Keep pull-contents.php as V2.39 (no changes required).
  2. Test Background Image:
    • Load a page with a theme image set as "Background" (e.g., https://navi.claudioflute.live/public/?page=post-list).
    • Verify the image maintains its aspect ratio, fills the width, and no longer appears compressed (outside the red box). Check for minimal white spaces.
  3. Test Illustration Image:
    • Confirm the illustration image (red box) has more side margins, with a width reduced to ~80% of its container, and looks balanced.
  4. Test Mobile View:
    • Use a mobile device or responsive mode (e.g., 320px width).
    • Ensure the background image covers the viewport with preserved aspect ratio, and the illustration image has appropriate margins.
  5. Test Modal:
    • Open a modal (via a .modal-trigger button) with an image.
    • Verify the image width is reduced to ~80% with side margins, maintaining a conservative look.
  6. Report Issues:
    • If the background image shows white spaces or the illustration/modal image looks off, share a new screenshot with annotations.

Additional Notes

  • Aspect Ratio Trade-Off: cover may crop the image vertically if the aspect ratio doesn’t match the container. If white spaces reappear, we can try contain with width: 100vw and adjust positioning—let me know if needed.
  • Margin Flexibility: The 10px padding is conservative; if more margin is desired, we can increase it (e.g., padding: 0 20px)—please specify.
  • Time: It’s 12:45 PM HKT on Wednesday, August 20, 2025, giving you the afternoon to test.
  • Next Steps: Once resolved, we can return to the emoji issue or other urgent tasks.

Please deploy and test, and let me know the results!


Q: With this update, the background image looks like being compressed, as shown in the attached screenshot (Area OUTSIDE the red box). Can we change to keep the original aspect ratio of the background image? Also, the illustration image (Red box) is a bit too wide, I want it to leave more margin on the sides. Since this will also affect the image size in modals, so please keep the adjustment conservative.

Image description


A: Thank you for the feedback and the attached screenshot! I apologize for the confusion—your request seems to be a repeat, and it appears the previous update (V4.9 of custom.css) didn’t fully resolve the issue. The background image is still being compressed (outside the red box), and the illustration image (inside the red box) remains too wide with insufficient side margins. Let’s refine the solution to ensure the background image preserves its original aspect ratio while filling the width, and adjust the illustration image with more conservative margins that also apply to modals.

Analysis

1. Background Image Compression

  • The current background-size: cover in V4.9 maintains the aspect ratio but may still cause compression or cropping if the image’s dimensions don’t align with the container’s aspect ratio. This is evident outside the red box.
  • To avoid compression, we can use background-size: contain to fit the image within the container while preserving its aspect ratio, though this might reintroduce white spaces. Alternatively, we can combine cover with adjustments to ensure minimal distortion.

2. Illustration Image Width

  • The illustration image (red box), governed by .welcome-image within .content-image, is set to max-width: 80% with padding: 0 10px. This reduction from 100% is still too wide for your preference, suggesting a need for a slightly narrower width (e.g., 70%) and increased padding (e.g., 20px) for more side margins.
  • The conservative adjustment request implies avoiding drastic changes, so we’ll limit the width to 70% and padding to 20px.

3. Modal Impact

  • Changes must apply to .modal-story-content .content-image to maintain consistency, keeping the adjustment subtle to avoid disrupting modal layouts.

4. Desired Outcome

  • Background image: Preserve aspect ratio, fill the width as much as possible, and minimize compression or white spaces.
  • Illustration image: Reduce width to ~70% with ~20px side margins, applied conservatively to modals.

Proposed Solution

We’ll update custom.css to:

  • Switch background-size: cover to contain for background images to avoid compression while preserving aspect ratio, with width: 100vw to maximize width coverage.
  • Adjust the illustration image’s max-width to 70% and increase padding to 20px for more side margins.
  • Apply these changes consistently to modal images with a conservative approach.

Updated custom.css (V4.10)

/* Custom CSS for Lifenavi (navi.claudioflute.live) and Chaplaincy Admin (admin.nlt-chaplaincy.org) - Version 4.10 */
/* Shared styles for both systems, with domain-specific overrides as noted */

/* Ensure myButton overrides theme styles */
.content__buttons a.myButton {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.myButtonBig {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Set default font for the entire site */
body {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Test class to verify font loading */
.font-test {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 24px;
    color: red;
}

/* Dynamic Section Styles */
.content__entry {
    position: relative;
    padding: 20px;
    min-height: 200px;
    margin: 0 auto;
    max-width: 1200px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Hide empty content__entry div from Publii theme */
div.content__entry:empty {
    display: none;
}

/* Alternative: Hide content__entry div on dynamic pages (uncomment and add data-page-type="dynamic" to <body> if needed) */
/* body[data-page-type="dynamic"] div.content__entry {
    display: none;
} */

/* Enhanced Background Image Styles */
.section-hero--full[style*="background-image"],
.section-group[style*="background-image"] {
    width: 100vw !important; /* Use viewport width to maximize coverage */
    max-width: none !important; /* Remove max-width constraint */
    margin-left: 50% !important;
    margin-right: -50vw !important;
    transform: translateX(-50%) !important; /* Center the extended width */
    background-size: contain !important; /* Preserve aspect ratio, fit within container */
    background-position: center !important;
    background-repeat: no-repeat !important;
}

.section-hero--full[style*="background-image"] .section-hero__content,
.section-group[style*="background-image"] .section-hero__content {
    background: none !important; /* Ensure content area doesn’t obscure background */
}

/* Responsive Adjustments */
@media (max-width: 768px) {
    .section-hero--full[style*="background-image"] {
        width: 100vw !important; /* Full viewport width on mobile */
        left: 50% !important;
        transform: translateX(-50%) !important;
        margin: 0 !important;
        padding: 0 !important;
        min-height: 300px;
        background-size: contain !important; /* Preserve aspect ratio on mobile */
    }
    .section-group[style*="background-image"] {
        width: 100vw !important; /* Full viewport width on mobile */
        margin: 0 !important;
        padding: 0 !important;
        background-size: contain !important; /* Preserve aspect ratio on mobile */
    }
}

/* Illustration Image Adjustments */
.content-image,
.modal-story-content .content-image {
    padding: 0 20px; /* Increase side margins to 20px for more space */
    max-width: 70%; /* Reduce width to 70% for more margin, conservative adjustment */
    margin: 1em auto; /* Center with margins */
}

.welcome-image {
    max-width: 100%; /* Ensure image respects container width */
    height: auto;
    margin: 0 auto; /* Center within content-image */
    display: block;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* Ensure content is visible over background */
.section-hero--full[style*="background-image"] h1,
.section-hero--full[style*="background-image"] .generic-content,
.section-group[style*="background-image"] h1,
.section-group[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.section-hero--full h1,
.section-hero--column h1,
.section-hero--full .generic-content,
.section-hero--column .generic-content {
    color: #333;
}

.section-hero--full {
    width: auto;
    position: relative;
    left: 0;
    transform: none;
    margin: 0 auto;
    max-width: 1200px;
    min-height: 400px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--column {
    flex: 1;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    padding: 10px;
}

.section-group {
    display: flex;
    flex-wrap: nowrap; /* Prevent wrapping to new rows */
    gap: 20px;
    max-width: 1200px;
    margin: 0 auto;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.layout-2-column {
    display: flex;
    justify-content: space-between;
}
.layout-2-column .section-hero--column {
    flex: 1 0 49%; /* Slightly less than 50% to account for gap */
    max-width: 49%;
}

.layout-3-column {
    display: flex;
    justify-content: space-between;
}
.layout-3-column .section-hero--column {
    flex: 1 0 32%; /* Slightly less than 33.33% to account for gap */
    max-width: 32%;
}

@media (max-width: 768px) {
    .section-hero--full {
        width: 100vw;
        left: 50%;
        transform: translateX(-50%);
        margin: 0;
        padding: 0;
        min-height: 300px;
    }
    .section-hero--column {
        flex: 1 0 100%;
        max-width: 100%;
        margin-bottom: 20px;
    }
    .section-group {
        flex-wrap: wrap; /* Allow wrapping on mobile */
    }
    .section-hero__content {
        padding: 10px;
    }
    .section-hero__content h1 {
        font-size: 1.5em;
        margin-bottom: 0.75em;
        padding-top: 0.75em;
    }
    .generic-content {
        font-size: 0.9em;
        max-width: 100%;
        padding: 0 10px;
    }
}

.section-hero__content {
    position: relative;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    z-index: 1;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Alignment Classes */
.align-left {
    text-align: left;
}
.content-image.align-left, .video-embed.align-left {
    margin: 1em 0;
}
.align-center {
    text-align: center;
}

/* Override theme's heading font variable */
h1, h2, h3, h4, h5, h6 {
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
    margin-bottom: 1.5em !important;
}

/* Ensure specific heading styles inherit the font */
.content__entry h1 {
    font-size: 2em;
    font-weight: 700;
    line-height: 1.2;
    color: #333;
    margin-bottom: 1.5em !important;
    padding-top: 1em;
    text-align: center;
    text-shadow: none;
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
}

.generic-content {
    margin: 0 auto;
    max-width: 800px;
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    margin-bottom: 1em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry[style*="background-image"] h1,
.content__entry[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.generic-content p {
    margin: 0 0 1em 0;
}

.generic-content ul,
.generic-content ol {
    margin: 0 0 1em 0;
    padding: 0 !important;
    list-style: none !important;
}

.generic-content li {
    margin-bottom: 0.5em;
    text-align: inherit;
    list-style: none !important;
}

.generic-content ul[style*="list-style"],
.generic-content ol[style*="list-style"],
.generic-content li[style*="list-style"] {
    list-style: none !important;
}

.content__buttons {
    text-align: center !important;
    margin: 2em 0 1em 0;
}

.content__buttons a.myButton {
    display: inline-block;
    margin: 0 auto;
}

.content__entry .content__buttons {
    text-align: center !important;
}

.welcome-image {
    max-width: 100%; /* Ensure image respects container width */
    height: auto;
    margin: 0 auto; /* Center within content-image */
    display: block;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
    background: none;
}

.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.separator {
    text-align: center;
    margin: 60px 0 20px 0;
}

.content__entry.content__entry--full:first-child {
    min-height: 0 !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
}

.main.page .main__right:first-child {
    margin-top: -3.8rem !important;
}

.main.page .main__right {
    margin: 0% !important;
}

.banner--after-content {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    outline: none !important;
}

.top__item--right {
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
}

@media (max-width: 768px) {
    .main.page .main__right:first-child {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    .main.page .container:first-child,
    .main.page .post__entry:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .main__right .main__right--bottom:first-child {
        margin-top: -3.8rem !important;
        margin-bottom: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .container > .main__right > .main__right--bottom:first-child,
    .main.page .post__entry > .main__right > .main__right--bottom:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .banner--after-content {
        margin: 0 !important;
        padding: 0 !important;
        border: none !important;
        outline: none !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

@media (min-width: 769px) and (max-width: 1023px) {
    .main.page .main__right {
        margin: 0 auto !important;
        max-width: 90% !important;
        width: auto !important;
    }
    .main.page .main__right .content__entry--full {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .main.page .main__right .main__right--bottom {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

.main.page .main__right .main__right--bottom .section-hero--full:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

.content__entry.content__entry--full .separator:first-child {
    margin-top: 0;
}

.section-hero--full:not([style*="background-image"]) + .separator {
    margin-top: 40px;
}

.separator-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

.separator img[src$="down_arrow.png"] {
    max-width: 256px;
}

hr {
    display: none !important;
    visibility: hidden !important;
}

.post__content hr,
.container hr,
.post__content hr[style],
.container hr[style] {
    display: none !important;
    visibility: hidden !important;
}

.post__content::before,
.post__content::after,
.container::before,
.container::after,
.post__entry::before,
.post__entry::after,
.content__entry::before,
.content__entry::after,
.generic-content::before,
.generic-content::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

.separator::before,
.separator::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

body,
body p,
body h2,
body h3,
body h4,
body h5,
body h6,
.post__content,
.post__content p,
.post__content h2,
.post__content h3,
.post__content h4,
.post__content h5,
.post__content h6,
.container,
.container p,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6 {
    color: #333 !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

h1.post__title,
h1.hero__title,
h1.site__title {
    color: #fff !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.button,
a.btn,
a.post__button,
.post__content a.button,
.post__content a.btn,
.post__content a.post__button,
.container a.button,
.container a.btn,
.container a.post__button {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Custom Modal Styles */
.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-content {
    background-color: #fff;
    margin: 5% auto;
    padding: 20px;
    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;
}

.modal-close {
    color: #aaa;
    position: absolute;
    top: 10px;
    right: 20px;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.3s ease;
    z-index: 1001;
}

.modal-close:hover,
.modal-close:focus {
    color: #000;
    text-decoration: none;
}

#modal-body {
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    max-width: 100%;
    box-sizing: border-box;
    padding-top: 40px;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    overflow-x: hidden;
}

#modal-body h1 {
    font-size: 1.5em;
    margin-bottom: 0.5em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body p {
    margin-bottom: 1em;
}

#modal-body * {
    max-width: 100%;
    height: auto;
    box-sizing: border-box;
}

#modal-body img,
#modal-body div,
#modal-body table {
    max-width: 100%;
    height: auto;
}

#modal-body .welcome-image {
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.modal[aria-hidden="false"] {
    display: block;
}

.modal-close:focus {
    outline: 2px solid #44c767;
    outline-offset: 2px;
}

/* Custom separator for modal sections */
.modal-section-separator {
    text-align: center;
    margin: 20px 0;
    font-size: 24px;
    line-height: 1;
    color: #666;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}
.modal-section-separator::before {
    content: "···";
}

/* Modal-specific content container */
.modal-story-content {
    max-width: 100%;
    margin: 0 auto;
    padding: 20px;
    box-sizing: border-box;
    overflow-x: hidden;
}

.modal-story-content h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
    text-align: center;
}

.modal-story-content .content-image {
    padding: 0 20px; /* Increase side margins to 20px for more space */
    max-width: 70%; /* Reduce width to 70% for more margin, conservative adjustment */
    margin: 1em auto; /* Center with margins */
}

.modal-story-content .generic-content {
    font-size: 1rem;
    line-height: 1.6;
}

/* Chaplaincy Admin specific overrides */
.report-container {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry table.report-table {
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Ensure background image applies to the section */
.section-hero--full[style*="background-image"] .section-hero__content,
.section-group[style*="background-image"] .section-hero__content {
    background: none;
}

Explanation of Changes

custom.css V4.10

  • Versioning: Updated to V4.10.
  • Background Image Aspect Ratio:
    • Changed background-size: cover to contain in .section-hero--full[style*="background-image"] and .section-group[style*="background-image"] to preserve the original aspect ratio and avoid compression.
    • Set width: 100vw !important with margin-left: 50% !important, margin-right: -50vw !important, and transform: translateX(-50%) !important to extend the container to the viewport width and center it, minimizing white spaces while fitting the image.
  • Illustration Image Adjustments:
    • Updated .content-image and .modal-story-content .content-image with:
    • max-width: 70% (down from 80%) to further reduce width for more side margins.
    • padding: 0 20px (up from 10px) to increase side spacing conservatively.
    • margin: 1em auto to center the image with consistent margins.
    • Ensured .welcome-image retains max-width: 100% to respect the container’s new width.
  • Modal Consistency: Applied the same max-width: 70% and padding: 0 20px to .modal-story-content .content-image for a balanced effect.
  • No Changes to pull-contents.php: The image application logic remains unchanged; only CSS is adjusted.

Testing Instructions

  1. Deploy Updated File:
    • Replace custom.css with V4.10.
    • Keep pull-contents.php as V2.39 (no changes required).
  2. Test Background Image:
    • Load a page with a theme image set as "Background" (e.g., https://navi.claudioflute.live/public/?page=post-list).
    • Verify the image maintains its original aspect ratio, fills the width as much as possible, and no longer appears compressed (outside the red box). Check for minimal white spaces (note that contain may leave small gaps if the aspect ratio differs significantly).
  3. Test Illustration Image:
    • Confirm the illustration image (red box) has more side margins (20px each side) with a width reduced to ~70% of its container, looking balanced and not too wide.
  4. Test Mobile View:
    • Use a mobile device or responsive mode (e.g., 320px width).
    • Ensure the background image fits the viewport with preserved aspect ratio, and the illustration image has appropriate margins.
  5. Test Modal:
    • Open a modal (via a .modal-trigger button) with an image.
    • Verify the image width is reduced to ~70% with 20px side margins, maintaining a conservative and balanced look.
  6. Report Issues:
    • If the background image shows significant white spaces or the illustration/modal image looks off, share a new screenshot with annotations.

Additional Notes

  • Aspect Ratio Trade-Off: contain preserves the aspect ratio but may leave white spaces if the image is narrower or taller than the container. If this is unacceptable, we can revert to cover with object-fit: cover (via inline styles if needed)—let me know your preference.
  • Margin Flexibility: The 20px padding is conservative; if more margin is desired (e.g., 30px), we can increase it—please specify.
  • Time: It’s 12:50 PM HKT on Wednesday, August 20, 2025, giving you the afternoon to test.
  • Next Steps: Once resolved, we can return to the emoji issue or other urgent tasks.

Please deploy and test, and let me know the results!


Q: Um...I think V4.7 is closer to my intended effect. For the time being, I'll reverse to V4.7 of the CSS. There is no need to generate again as it's available in our earlier chat.


A: Understood! It’s great that V4.7 of custom.css aligns more closely with your intended effect. Since it’s already available in our earlier chat, I won’t regenerate it, and you can revert to that version as needed. I’ll respect your decision to pause further adjustments for now. If you’d like to revisit the background image compression, illustration margins, or the earlier emoji issue later, or if any new urgent tasks arise, feel free to let me know. It’s currently 12:54 PM HKT on Wednesday, August 20, 2025, so you have the afternoon to test or address other priorities. I’m here to assist whenever you’re ready!

Original Chat


Q: Today, I noticed a horizontal scrollbar on the Chaplaincy website on my computer when I made the browser window very narrow to imitate a phone screen. But when I visit my Lifenavi website sharing the same custom CSS V4.7, no horizontal scrollbar was seen. What do you think could be the cause? Attached is a screenshot of the horizontal scrollbar captured on my PC. When viewed on a phone there is no horizontal scrollbar.


A: Thank you for bringing this to my attention! It’s interesting that you’ve noticed a horizontal scrollbar on the Chaplaincy Admin website when narrowing the browser window to imitate a phone screen, while the Lifenavi website, which shares the same custom.css (V4.7), does not exhibit this issue. The fact that the scrollbar disappears on an actual phone screen for Chaplaincy Admin adds another layer to investigate. Let’s analyze the potential causes based on the information provided, the attached screenshot, and the shared CSS, and propose a solution.

Analysis

1. Observed Behavior

  • Chaplaincy Admin: A horizontal scrollbar appears when the browser window is narrowed on your PC (simulating a phone screen), but not on an actual phone.
  • Lifenavi: No horizontal scrollbar appears under the same narrow-window simulation on your PC.
  • Shared CSS: Both sites use custom.css V4.7, suggesting the issue might stem from differences in HTML structure, base theme CSS, or content rather than the custom CSS alone.

2. Potential Causes

  • Base Theme Differences: Chaplaincy Admin and Lifenavi might use different base themes (e.g., Publii themes) or versions, which could introduce default styles (e.g., min-width, overflow-x) that trigger the scrollbar. The custom CSS overrides might not fully align across both sites.
  • Content Overflow: Specific content on Chaplaincy Admin (e.g., wide tables, images, or unadjusted containers) might exceed the viewport width when scaled down, causing overflow. The screenshot could help identify this if it shows the offending element.
  • Media Query Inconsistency: The @media (max-width: 768px) rules in V4.7 might not fully account for all elements on Chaplaincy Admin, especially if Lifenavi has fewer or differently structured components.
  • Viewport Meta Tag: If Chaplaincy Admin’s HTML lacks or has a misconfigured <meta name="viewport"> tag (e.g., missing width=device-width), it might not scale properly in narrow desktop windows, unlike on a phone where the browser handles it natively.
  • Dynamic Content: Elements generated by pull-contents.php or other scripts (e.g., .section-hero--full, .section-group) might render differently due to varying data or styling overrides in Chaplaincy Admin.
  • Browser Rendering: The simulation on your PC might trigger a desktop-specific rendering bug or CSS miscalculation that doesn’t occur on an actual phone.

3. Key Observations

  • The absence of a scrollbar on Lifenavi suggests its layout or content adapts better to narrow widths.
  • The disappearance on a phone indicates the issue is likely tied to the desktop simulation rather than the mobile design itself.
  • The screenshot should reveal the element causing the overflow (e.g., a wide image, table, or misaligned div).

Proposed Solution

Since both sites share custom.css V4.7, the issue likely lies in Chaplaincy Admin’s unique HTML, theme, or content. Let’s address this by:

  • Ensuring proper viewport scaling.
  • Adding a global overflow control to prevent horizontal scrolling.
  • Inspecting and adjusting specific elements causing overflow.

1. Check and Update Viewport Meta Tag

  • Verify Chaplaincy Admin’s <head> section includes:
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  • If missing or incomplete, add it to the theme’s header.php or equivalent template file. This ensures the page scales correctly in narrow desktop simulations.

2. Add Global Overflow Control

  • Update custom.css V4.7 (or create a Chaplaincy-specific override) to prevent horizontal overflow:
     /* Add to custom.css or a Chaplaincy-specific file */
     html, body {
         overflow-x: hidden;
         width: 100%;
         margin: 0;
         padding: 0;
     }
     .main.page, .container, .post__entry {
         width: 100%;
         max-width: 100%;
         overflow-x: hidden;
     }
  • This forces the layout to hide any horizontal overflow, mimicking Lifenavi’s behavior.

3. Inspect and Adjust Overflowing Elements

  • Based on the screenshot, identify the element causing the scrollbar (e.g., a wide .welcome-image, .video-embed, or .section-group).
  • Add targeted CSS. For example, if it’s an image:
     .welcome-image {
         max-width: 100%;
         height: auto;
     }
     .content-image {
         width: 100%;
         overflow-x: hidden;
     }
  • If it’s a table or group, ensure flex items or tables are constrained:
     .section-group {
         flex-wrap: wrap;
         width: 100%;
     }
     .section-hero--column {
         flex: 1 0 100%;
         max-width: 100%;
     }

4. Test Media Queries

  • Enhance the existing @media (max-width: 768px) block in V4.7 to enforce wrapping and width limits:
     @media (max-width: 768px) {
         .section-hero--full, .section-group {
             width: 100vw !important;
             left: 50% !important;
             transform: translateX(-50%) !important;
             margin: 0 !important;
             padding: 0 !important;
             overflow-x: hidden;
         }
         .content__entry, .generic-content {
             max-width: 100% !important;
             padding: 0 10px;
         }
     }

Testing Instructions

  1. Verify Viewport Tag:
    • Check Chaplaincy Admin’s HTML source (right-click > View Page Source) for the <meta name="viewport"> tag. Add or correct it if needed, then save the theme file.
  2. Update custom.css:
    • Append the global overflow control and media query adjustments to V4.7 (or create a custom-chaplaincy.css file and link it after the shared CSS).
  3. Inspect Screenshot:
    • Analyze the attached screenshot to identify the overflowing element (e.g., image, table). Share the element’s class or ID if unsure.
  4. Test on PC:
    • Narrow the browser window on Chaplaincy Admin and confirm the horizontal scrollbar is gone.
  5. Test on Phone:
    • Verify the layout remains functional on an actual phone, with no unintended clipping.
  6. Compare with Lifenavi:
    • Repeat the narrow-window test on Lifenavi to ensure consistency.
  7. Report Issues:
    • If the scrollbar persists, provide the screenshot analysis or the HTML snippet of the offending element.

Additional Notes

  • Time: It’s 06:51 PM HKT on Wednesday, August 27, 2025, so you have the evening to test.
  • Theme Difference: If the issue persists, check the Publii theme version or settings for Chaplaincy Admin vs. Lifenavi. Share the theme names if possible.
  • Next Steps: Once resolved, we can return to the web_content library idea or address other tasks.

Please implement these changes and let me know the results or any specific elements from the screenshot that need targeting!


Q: I've appended the following lines at the end of CSS V4.7 and the issue is solved.

However, adding " " to the < head > alone didn't help.

/* Add to custom.css or a Chaplaincy-specific file */
html, body {
    overflow-x: hidden;
    width: 100%;
    margin: 0;
    padding: 0;
}
.main.page, .container, .post__entry {
    width: 100%;
    max-width: 100%;
    overflow-x: hidden;
}

Another issue to bring up is we are currently adding PayPal subscription buttons to the website site. However, with the following code provided by PayPal, the button can be viewed from the Publii preview page but not from the dynamic index.php.

<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
<script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
<script>
  paypal.Buttons({
      style: {
          shape: 'pill',
          color: 'gold',
          layout: 'vertical',
          label: 'subscribe'
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          /* Creates the subscription */
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert(data.subscriptionID); // You can add optional success message for the subscriber here
      }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button
</script>

I think it's because we've hidden the Publii theme content area when we were working to reduce the white gap on the top of the page in our previous discussions. Please see how we can show static content created in Publii and hide it when there is no static content.


Thank you for your inputs on the updated design. Below are my preferences. Please proceed with coding if you think they're good.I've added positions 100-104 to the "position" field to accommodate the PP sections. I think using three-digit numbers consistently looks better. For the "model" field, I've added "title-text-paypal" which looks clearer than my original proposal as user may confuse "button" with existing CTA buttons. For what you said "Rendering PP buttons on all pages", what I meant was if the PP sections are set to "landing" they should be shown on index.php. If they are set for "finance", then finance.php should display them when both inside a modal or direct-accessed. I was emphasizing that this is the existing logic for any dynamic sections so no change is needed.

Please treat other behaviors of PP sections the same as other sessions, such as CTA button rendering logic (hide in modal), "theme_img" handing, "chk_divider" handling etc. Attached is the source of the current V2.13 of pull-contents.php for your reference.


The code is now 697 lines long and is about right! There is one issue I've missed. Since the "content" field is using a HTML editor it will sanitize the PP button script. Do you think we can put the button code inside the "video_embed" field for it doesn't have a HTML editor? For "model", we can keep "title-text-paypal" unchanged but the rendering logic is basically the same as "title-text-video". We can also add a PayPal button field ("paypal_button") if it's necessary, for you may want to handle the codes differently for YouTube embed code and PP button code. What's your recommendation? Let's review the design first and leave coding for later.


Yup! Let's proceed with coding! Please pay attention to any special character handling in the formerly "video_embed" field (Now renamed to "code") and preserve it. I will do the schema change ("code", "code_type", new "model" option "title-text-code") via AppGini so no DB script is needed.


While the length is still 597 lines long the basic display of the website has been restored. However, all images have been missing in modals and PayPal dynamic sessions are not displayed (I think this is intentional). The page-top debug message is now:Debug: Calling file: 'index.php' Debug: REQUEST_URI: '/' Debug: Current page after str_replace: 'index' Debug: Current page set to 'landing' Debug: Final current_page: 'landing' Debug: Image Base Path: 'https://admin.nlt-chaplaincy.org/images/' Debug: Is modal request: No Debug: Fetched 9 sections for page 'landing'The modal debug message (Missing "image") Debug: Modal opened Debug: Fetch response status: 200 OK Debug: Fetched HTML length: 23478 Debug: Found 3 modal-story-content sections in fetched content Debug: Found 3 modal-story-content sections Debug: Modal openedPlease check if the image is not processed or the image path has been wrongly changed.


This fix has restored image display. However, the PP section is still missing. I've checked the section has chosen "landing" as the page, "PayPal" as code type, "100" as position and is set to "Show". The code put into the "code" field is:

<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
<script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
<script>
  paypal.Buttons({
      style: {
          shape: 'pill',
          color: 'gold',
          layout: 'vertical',
          label: 'subscribe'
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          /* Creates the subscription */
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert(data.subscriptionID); // You can add optional success message for the subscriber here
      }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button
</script>

This has been tested in Publii as static content and verified working in the preview.


This fix doesn't solve the PP button display issue. I've tried re-generated the site using Publii. What's working in the preview now fails. PayPal button from the static block is not shown but the title of the static block is shown. I'm not sure if this is just a CSS issue that hide static button.


Thanks for the fix. Please remember to update the version number as we've been staying at 2.14 for very long. Let's also assume the number of lines is correct at 638 lines with the latest V2.14 fix. To help find out the issue, I've set the PP section to "title-text" temporarily. When position is set to "10" the section is displayed but when set to "100" the entire section is missing. While there are 11 "landing" sections in the database the debug message says "Debug: Fetched 10 sections for page 'landing'. Does this give more hints, like we've set the max. number of sections to 10 (Please take a look at our historical chats for reference)? Static PP buttons are still missing. For title, I was referring to the title I added in Publii, not title from the web_content dynamic section. A screenshot has been attached for clarity. The circled title is added in Publii, not from any dynamic section.


Q: The PP section positioned at "100" is finally shown with this version! However, with the "title-text-button" model, I see an orange inline debug message that says:

Debug: PayPal code for ID 58: <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ /* Creates the subscription */ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); // You can add optional success message for the subscriber here } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button//

The floating debug message layer says:

Debug: Modal script loaded
Debug: Found 7 modal triggers
Debug: Re-rendered PayPal button for ID 58

Thanks for the update. It was my typo to say "title-text-button". The option chosen was "title-text-code" as our design intended. The script I obtained from PP was:

···

···

As you can see it contain two pairs of < script > tag.With these additional info., does V2.15 (Actually it should be V2.16. According to our past protocol, you'll update the version number every time changes are introduced) still apply?


While you said "Since the SDK is already in the < head >", the PP-provided head code is actually:

<script src="https://www.paypal.com/sdk/js?client-id=BAA3CoIb4JoqOOk3pcQTP3qNktdZtHH6XivPxUlzpsk17UqgPBTbLfowLMyjT6OaRCP39w2Tkv6cEguw3I&components=hosted-buttons&disable-funding=venmo&currency=HKD">
</script>

This is not the same as this script in the button code:

<script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>

Does V2.16 correctly handle this?Please remember to confirm the fix strategy before generating code.

A: Thank you for pointing out the discrepancy between the PayPal SDK scripts! You're absolutely correct, and this mismatch between the <head> script and the script embedded in the button code is a critical issue that needs to be addressed. Let’s confirm the fix strategy before generating the updated code.

Analysis

  • Current <head> SDK Script:

    <script src="https://www.paypal.com/sdk/js?client-id=BAA3CoIb4JoqOOk3pcQTP3qNktdZtHH6XivPxUlzpsk17UqgPBTbLfowLMyjT6OaRCP39w2Tkv6cEguw3I&components=hosted-buttons&disable-funding=venmo&currency=HKD">
    </script>
    • Uses client-id=BAA3CoIb4... with components=hosted-buttons, disable-funding=venmo, and currency=HKD.
  • Button Code SDK Script:

    <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
    • Uses client-id=AUNZIE2pdhV... with vault=true and intent=subscription.
  • Discrepancy:

    • Different client-id values indicate these are likely for different PayPal accounts or applications.
    • The <head> script is configured for hosted buttons with specific funding and currency options, while the button code requires subscription functionality (vault=true and intent=subscription).
    • Loading both SDKs could cause conflicts, as the PayPal SDK should be loaded once with parameters matching the intended use case.

Fix Strategy Confirmation

To ensure V2.16 correctly handles this, let’s confirm the following strategy:

  1. Unify SDK Loading:

    • Remove the SDK <script> from the button code’s code field since it’s already in the <head>.
    • Update the <head> script to use the client-id=AUNZIE2pdhV... with vault=true and intent=subscription to match the button code’s requirements, as subscriptions are the intended functionality.
    • Instruct you to replace the current <head> script with:
      <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
  2. Adjust PHP Logic:

    • Modify the PHP to extract only the button initialization script (e.g., paypal.Buttons(...)) from the code field, ignoring the SDK <script>.
    • Store the cleaned button script in window.paypalCode for dynamic rendering.
  3. JavaScript Re-Rendering:

    • Ensure the JavaScript re-renders only the button initialization script, relying on the single <head> SDK load.
    • Add a check to avoid re-rendering if the SDK isn’t loaded, with enhanced debug logging.
  4. Version Update:

    • Increment to V2.16 to reflect this significant change.

Confirmation

  • Do you agree with updating the <head> script to match the button code’s SDK parameters (client-id=AUNZIE2pdhV..., vault=true, intent=subscription)? This assumes the button code’s configuration is the intended use case (subscriptions).
  • Are you okay with removing the SDK <script> from the code field in the database, keeping only the <div> and button initialization <script>? This simplifies the dynamic rendering process.

Please confirm or provide feedback on these steps. Once confirmed, I’ll generate the updated V2.16 code accordingly.


Q: Answering your questions:

Do you agree with updating the < head > script...? With further checking, you're right that the two SDK scripts are conflicting with each other. The correct code to put in the < body> of the web page is:

<!-- Load PayPal SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription"></script>

<!-- PayPal Button Container -->
<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>

<!-- Render the Subscription Button -->
<script>
  paypal.Buttons({
    style: {
      shape: 'pill',
      color: 'gold',
      layout: 'vertical',
      label: 'subscribe'
    },
    createSubscription: function(data, actions) {
      return actions.subscription.create({
        plan_id: 'P-3FU48207V4555173AM5STYUA'
      });
    },
    onApprove: function(data, actions) {
      alert("Subscription successful! ID: " + data.subscriptionID);
    }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA');
</script>

Are you okay with removing the SDK < script > from the code?

  • Per my above response, should we actually leave the SDK script from the above in the "code" field so that the entire block will be loaded in the < body >?

Q: Thank you for the detailed update and clarification! Based on your new information, I’ll adjust the fix strategy accordingly. Let’s refine our approach and confirm the next steps.

Updated Analysis

  • Provided Code:

    <!-- Load PayPal SDK -->
    <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription"></script>
    
    <!-- PayPal Button Container -->
    <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
    
    <!-- Render the Subscription Button -->
    <script>
    paypal.Buttons({
      style: {
        shape: 'pill',
        color: 'gold',
        layout: 'vertical',
        label: 'subscribe'
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert("Subscription successful! ID: " + data.subscriptionID);
      }
    }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA');
    </script>
    • This confirms the entire block (SDK load, container, and button script) is intended to be placed in the <body>, and no <head> script is needed.
  • Previous Conflict:

    • The mismatch between the <head> script (client-id=BAA3CoIb4...) and the button code’s SDK (client-id=AUNZIE2pdhV...) caused the issue. Removing the <head> script aligns with PayPal’s recommendation to load the SDK where the button is rendered.
  • Current Issue:

    • V2.16 attempted to split the SDK and button scripts, but since the entire block should be in the <body>, we need to handle the full code field as a single unit, ensuring the SDK loads before the button script executes.

Revised Fix Strategy Confirmation

Based on your input, let’s adjust the strategy:

  1. Remove <head> Script:

    • Confirm you’ll remove the existing <head> script (client-id=BAA3CoIb4...) from your HTML template, as it’s no longer needed.
  2. Handle Full <body> Block:

    • Update the PHP to store the entire code field (including the SDK <script>, container, and button <script>) in window.paypalCode.
    • Modify the JavaScript to append the full block as a new <div> with the inner HTML, ensuring the SDK loads and the button renders in sequence.
  3. Dynamic Rendering:

    • Use document.body.appendChild to insert a <div> containing the full code content, allowing the browser to execute the scripts naturally.
    • Add a delay or check to ensure the SDK is loaded before rendering, though the inline nature should handle this.
  4. Version Update:

    • Increment to V2.16 to reflect this change.

Confirmation Questions

  • Are you okay with removing the <head> script entirely? This aligns with your updated code, placing all PayPal logic in the <body>.
  • Should we keep the full block (SDK + container + button script) in the code field? Based on your suggestion, yes, we’ll leave the SDK <script> in the code field to ensure the entire block is loaded dynamically in the <body>.

Please confirm these adjustments. Once confirmed, I’ll generate the updated V2.16 code.


Q: Yes for both questions. I will remove the head code in Publii and re-gen. the web page. Pls. proceed with coding!


Before testing, I just want to check if the current handling of code block compatible with YouTube embed code?


Yes, please extend dynamic insertion. For YT embeds, we usually use sth. like this:

<iframe width="560" height="315" src="https://www.youtube.com/embed/1_7xSwEC71A?si=pUd70OsjPWunYFXe" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

This version is still not showing the button but a block of debug message:

`});
Debug: Embed code for ID 58: <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script> <script> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ /* Creates the subscription */ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); // You can add optional success message for the subscriber here } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button </script>

The floating layer shows: Debug: contentCode unavailable

And, I find the button code, which is like:

<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
<script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
<script>
  paypal.Buttons({
      style: {
          shape: 'pill',
          color: 'gold',
          layout: 'vertical',
          label: 'subscribe' 
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          /* Creates the subscription */
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert(data.subscriptionID); // You can add optional success message for the subscriber here
      }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button
</script>

Has been changed to:

<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
[removed][removed]
[removed]
  paypal.Buttons({
      style: {
          shape: 'pill',
          color: 'gold',
          layout: 'vertical',
          label: 'subscribe' 
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          /* Creates the subscription */
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert(data.subscriptionID); // You can add optional success message for the subscriber here
      }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); // Renders the PayPal button
[removed]

The code field is being modified by AppGini generated app upon saving, not by AppGini itself as it's a desktop app which won't deal with my data. I'm okay with reinserting the PP block directly in the database.


I've manually updated the database and confirm the "content" is not changed. However, this version still render the following in orange:

Debug: Embed code for ID 58 (PayPal): <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script> <script> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); </script>

This version still fails. The floating debug message says: "Debug: Unsupported embed type or invalid code for ID 58" and the content is still:

Debug: Embed code for ID 58 (PayPal): <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> <script src="https://www.paypal.com/sdk/js?client-id=AUNZIE2pdhVLgVgUFGEQyyYAJVMuxwkXmCwvslu3c1eR-TMLSvCIBhDZxhhteIoQCTAzJ-50qNcVpuoa&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script> <script> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); </script>

The on-page content remains unchanged with direct-rendered text only. The floating debug messages have changed to:Debug: Inserted div for PayPal ID 58 Debug: Inserted PayPal SDK script for ID 58 Debug: PayPal SDK failed to load for ID 58 after 5s


Before confirmation, I want to know if the PP code is intended to be inserted into CMS, why it seems that it's difficult to get the button displayed? I think most CMS will be similar to ours to use database and save content in database fields.


A: That’s a great question! Let’s address why it’s proving challenging to display the PayPal button, especially in the context of a CMS that uses a database to store content like yours (built with AppGini). This will help clarify the intent and the difficulties, which can guide our next steps.

Intent of PayPal Code Insertion

  • Purpose: The PayPal code block (containing a <div>, an SDK <script>, and a button <script>) is intended to be dynamically inserted into the webpage to render an interactive "Subscribe" button. This block is typically provided by PayPal for integration and relies on:
    • The SDK <script> loading the PayPal JavaScript library.
    • The button <script> initializing and rendering the button within the specified <div> once the SDK is available.
  • CMS Integration: In a CMS environment, the goal is to store this code in a database field (e.g., web_content.code) and have the PHP/JavaScript logic render it on the frontend. This is a common approach for embedding third-party scripts (e.g., PayPal, YouTube) in CMS platforms like WordPress, Joomla, or custom AppGini-based systems.

Why It’s Difficult to Display the Button

Several factors are contributing to the difficulty, which are not uncommon in CMS/database-driven setups:

  1. Database Storage and Sanitization:

    • Many CMSs, including AppGini-generated apps, apply HTML sanitization or security filters to prevent XSS (cross-site scripting) attacks. This can strip or modify <script> tags when saving to the database, as you’ve observed with the [removed] placeholders. Even though you’ve manually restored the content, the initial save process highlights a limitation.
    • Impact: The code field might need to be configured as a "raw HTML" or "unsafe" field in the CMS to preserve scripts, which isn’t always the default.
  2. Dynamic Script Execution:

    • Inserting HTML with <script> tags via innerHTML or insertAdjacentHTML doesn’t automatically execute the scripts. This is a browser security feature. Our previous attempts used insertAdjacentHTML, which worked for the <div> but not the scripts, requiring separate <script> element creation.
    • Impact: The button script relies on the SDK, and if the SDK doesn’t load, the button won’t render, making the execution process more complex than static HTML (e.g., YouTube <iframe>).
  3. SDK Loading Dependency:

    • The PayPal SDK is an external resource (https://www.paypal.com/sdk/js?...) that must load before the button script can run. Network delays, CSP restrictions, or an invalid client-id can cause failures. The 5-second timeout in V2.19 might be too short, and the debug message confirms the SDK didn’t load.
    • Impact: Unlike static embeds, this dependency introduces a timing issue that static CMS content (e.g., text or images) doesn’t face.
  4. CMS Rendering Limitations:

    • Most CMSs (e.g., WordPress with shortcodes, or AppGini with hooks) expect static or pre-rendered content in database fields. Dynamically executing third-party scripts requires custom JavaScript, which adds complexity. Your setup with pull-contents.php is a custom solution, but it must handle this dynamic nature explicitly.
    • Impact: The CMS might not be designed to handle live script execution from database fields without additional frontend logic, unlike pre-loaded scripts in <head>.
  5. Debug and Testing Challenges:

    • The debug output helps, but the fallback behavior and parsing issues have masked the root cause. The SDK load failure is now the critical blocker, which is a common hurdle in CMS integrations with external APIs.
    • Impact: Iterative testing is needed to isolate network, CSP, or configuration issues.

Comparison to Other CMSs

  • Similarities: Most CMSs (e.g., WordPress, Drupal, or custom AppGini apps) use databases to store content and face similar challenges with third-party scripts:
    • WordPress uses shortcodes or widgets, often requiring plugins (e.g., PayPal plugins) to handle SDK loading.
    • Drupal uses custom blocks with raw HTML fields, but CSP or module settings might block external scripts.
    • AppGini, being a database-driven framework, relies on custom PHP/JavaScript, which is what we’re building here.
  • Differences: Some CMSs pre-load SDKs in the <head> (e.g., via a theme or plugin), avoiding the timing issue. Your setup avoids this by keeping the code in the database, which is flexible but requires robust dynamic rendering.

Why It’s Harder Than Expected

  • The combination of database storage, script execution, and external dependency (PayPal SDK) creates a unique challenge. Static content (e.g., text, images) or self-contained embeds (e.g., YouTube <iframe>) don’t require this level of coordination. The difficulty stems from:
    • Ensuring the SDK loads before the button script.
    • Bypassing CMS sanitization to preserve the code.
    • Managing DOM manipulation to execute scripts correctly.

Next Steps

  • Verify SDK Load: Check the browser console (F12) for 404 errors, CSP violations, or network issues with the PayPal SDK URL. Ensure the client-id is valid and the domain allows the script.
  • Extend Timeout: Increase to 10 seconds to give the SDK more time.
  • Adjust Fallback: Ensure the fallback only triggers on parsing failure, not SDK load failure.
  • Test: Deploy the updated code to confirm if the SDK loads with the new timeout.

Confirmation

  • Are you okay with testing V2.20 with an extended timeout (10s) and enhanced debug to check the SDK load? This will help isolate the issue.
  • Can you check the browser console (F12) for errors after testing? This is crucial to diagnose the SDK failure.

Please confirm, and I’ll generate the V2.20 code.

Note: It’s 01:50 PM HKT on Friday, August 29, 2025—plenty of time to test today.


Q: Okay! Let's proceed with V2.20 and see how things will go!


The debug message says:Debug: Inserted div for PayPal ID 58 Debug: Inserted PayPal SDK script for ID 58 Debug: PayPal SDK load error for ID 58: undefined Debug: PayPal SDK failed to load for ID 58 after 10sNo change in the content display!


Opening your PP link in the browser returns (too long to include all):

(function({__FIRST_RENDER_EXPERIMENTS__,__FUNDING_ELIGIBILITY__,__MESSAGING_GLOBALS__}){
    /*! For license information please see output.js.LICENSE.txt */
!function(n,e){for(var t in e)n[t]=e[t]}(window,function(n){var e={};function t(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(r,o,function(e){return n[e]}.bind(null,o));return r},t.n=function(n){var e=n&&n.__esModule?.....

I just tried to upload the static HTML file, which is showing the button in the preview, to the server and the buttons become missing when accessed from the public URL https://www.nlt-chaplaincy.org/index.html.I also wonder if separate the SDK script and the button script into two fields (Say we add a pre-populated "sdk" field and hide it from the users) would help to simplify the issue. is not present on the generated HTML files. I do not own the login of the PP account. However, the code is taken from a former website prototype created in Wix. Let's assume the PP client-id is right unless the browser message above indicates something else.


V2.21 still fails to render the button script. The debug message is:Debug: Inserted div for PayPal ID 58 Debug: PayPal SDK failed to load for ID 58 after 10s


Before testing this new version, I want to clarify that if I'm running the website on my own QNAP NAS server (A Synology NAS is doing the reverse proxy role) would there be still CSP? I confirm that the "content" field is now containing just the button script and the container div as:

<div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
<script>
  paypal.Buttons({
    style: {
      shape: 'pill',
      color: 'gold',
      layout: 'vertical',
      label: 'subscribe'
    },
    createSubscription: function(data, actions) {
      return actions.subscription.create({
        plan_id: 'P-3FU48207V4555173AM5STYUA'
      });
    },
    onApprove: function(data, actions) {
      alert(data.subscriptionID);
    }
  }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA');
</script>

A: Thank you for the clarification! The current date and time is 07:34 PM HKT on Friday, August 29, 2025. Let’s address your question about CSP with your QNAP NAS server setup and confirm the updated content field, then decide on the next steps.

Clarification on CSP with QNAP NAS and Synology Reverse Proxy

  • Content Security Policy (CSP) Overview:

    • CSP is a security feature that restricts the sources from which resources (e.g., scripts, styles) can be loaded. It can be enforced at three levels:
      1. Browser Default: Browsers apply a basic CSP if none is specified.
      2. Server Configuration: Web servers (e.g., Apache, Nginx) or reverse proxies can enforce CSP via headers (e.g., Content-Security-Policy in .htaccess or server settings).
      3. HTML Meta Tag: A <meta http-equiv="Content-Security-Policy"> tag can define CSP client-side.
  • QNAP NAS Server:

    • QNAP NAS typically runs a lightweight web server (e.g., Apache or Nginx) as part of its QTS operating system. By default, QNAP does not enforce a strict CSP unless you’ve configured it manually in the web server settings or via a custom configuration file (e.g., .htaccess for Apache).
    • If you’re serving the site directly from the QNAP NAS without additional security tweaks, there’s likely no CSP unless you’ve enabled it (e.g., via the QNAP Control Panel under "Web Server" or "Security" settings).
  • Synology NAS as Reverse Proxy:

    • A Synology NAS acting as a reverse proxy (using DSM’s Reverse Proxy feature) can enforce CSP at the proxy level. Synology allows you to configure HTTP headers, including Content-Security-Policy, in the Reverse Proxy settings:
    • Check the Reverse Proxy rules in DSM under "Control Panel > Application Portal > Reverse Proxy."
    • Look for custom headers or security policies that might block https://www.paypal.com.
    • If no custom CSP is set, the proxy will pass the request to the QNAP NAS, and the behavior depends on the NAS’s configuration.
  • Likelihood of CSP:

    • Given no <meta> tag is present in your generated HTML and you haven’t mentioned configuring CSP on the QNAP or Synology, it’s possible there’s no explicit CSP. However, a default or inherited policy (e.g., from Synology’s security defaults or a browser extension) could still block the PayPal SDK.
    • The fact that the SDK loads in a standalone browser tab but not on the live site (https://www.nlt-chaplaincy.org/index.html) suggests a server-side or proxy-level restriction, such as an implicit CSP or CORS issue.
  • Action Steps:

    • Check QNAP Settings: Log into your QNAP NAS admin interface and review the web server settings (e.g., under "Control Panel > Applications > Web Server") for any security policies or custom headers.
    • Check Synology Reverse Proxy: In DSM, go to "Control Panel > Application Portal > Reverse Proxy," edit the rule for www.nlt-chaplaincy.org, and check the "Custom Header" section for a Content-Security-Policy header. If present, note its value or temporarily remove it for testing.
    • Test Without Proxy: If possible, bypass the Synology reverse proxy temporarily (e.g., access the QNAP NAS directly via its IP or hostname) to isolate the issue.

Content Field Update

  • New Content:
    • The content field now contains only the button script and container <div>:
      <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div>
      <script>
      paypal.Buttons({
      style: {
        shape: 'pill',
        color: 'gold',
        layout: 'vertical',
        label: 'subscribe'
      },
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          plan_id: 'P-3FU48207V4555173AM5STYUA'
        });
      },
      onApprove: function(data, actions) {
        alert(data.subscriptionID);
      }
      }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA');
      </script>
    • This is a positive change, as it aligns with the preloaded SDK approach in V2.21 and V2.22, removing the SDK script from the code field. The code_type should still be PayPal to trigger the correct rendering logic.

Next Steps

Since the SDK load failure persists, and your setup involves a QNAP NAS with a Synology reverse proxy, the issue is likely server- or proxy-related rather than the code itself. Before testing V2.22, let’s ensure the environment is configured correctly:

  1. Verify Environment:

    • Check the QNAP web server and Synology reverse proxy settings for CSP or security headers as outlined above.
    • If you find a CSP, temporarily disable it or add https://www.paypal.com to the script-src directive (e.g., script-src 'self' https://www.paypal.com;).
  2. Proceed with V2.22:

    • The updated code in V2.22 includes enhanced SDK detection and a synchronous load attempt. Testing it will help confirm if the issue is timing-related or a persistent block.
    • Given the content field update, the renderEmbed function should now parse the new code correctly, focusing on the <div> and button script.

Confirmation

  • Are you okay with testing V2.22 after checking the QNAP and Synology settings for CSP?
  • Can you:
    • Share the results of the CSP check (e.g., any headers found in QNAP or Synology settings)?
    • Test the site after bypassing the reverse proxy (if feasible) and report the behavior?
    • Confirm the code_type in the database for ID 58 is still PayPal?

Please confirm, and I’ll proceed with any adjustments based on your findings. If CSP or a domain mismatch is confirmed, we can refine the approach (e.g., splitting fields or contacting the Wix owner for the client-id).

Note: It’s 07:40 PM HKT on Friday, August 29, 2025—still time to test today, but we can continue tomorrow if needed.


Q: V2.22 shows the following debug message during my initial testing before checking for server CSP settings:

Debug: Inserted div for PayPal ID 58
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: Checking SDK for ID 58, status: undefined
Debug: PayPal SDK failed to load for ID 58 after 10s, Error: No specific error

I found the "Enable CSP HTTP header" option was checked on my QNAP server. I have disabled it and tested again. The debug message changed to:

Debug: Div match for ID 58: Success
Debug: Button match for ID 58: Success
Debug: Inserted div for PayPal ID 58
Debug: Rendered PayPal button for ID 58

The content area is still showing:

Debug: Embed code for ID 58 (PayPal): <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> <script> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); </script>

I have checked my Synology NAS and there is no custom header set.


A: Thank you for the detailed update! The current date and time is 07:50 PM HKT on Friday, August 29, 2025. The new debug messages and your server configuration changes provide significant progress. Let’s analyze the situation and resolve the remaining issue.

Analysis

Initial Debug Messages (With CSP Enabled)

  • The repeated Debug: Checking SDK for ID 58, status: undefined followed by Debug: PayPal SDK failed to load for ID 58 after 10s, Error: No specific error confirms the PayPal SDK wasn’t loading, likely due to the QNAP server’s CSP setting blocking https://www.paypal.com.

Updated Debug Messages (After Disabling CSP)

  • Positive Changes:
    • Debug: Div match for ID 58: Success and Debug: Button match for ID 58: Success indicate the parsing of the updated content field (containing the <div> and button script) is working correctly.
    • Debug: Inserted div for PayPal ID 58 confirms the <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"> is inserted into the DOM.
    • Debug: Rendered PayPal button for ID 58 suggests the button script was executed, which is a significant improvement since the SDK load succeeded after disabling CSP.
  • Remaining Issue:
    • The content area still displays the raw Debug: Embed code for ID 58 (PayPal): ... text, including the <div> and <script> block. This indicates the fallback or original content isn’t being replaced or hidden properly after the button is rendered.

Root Cause

  • Content Display Issue:
    • The renderEmbed function inserts the <div> and executes the button script, but the original code text (logged as debug output) remains visible. This happens because:
    • The debug output (<div style='color: orange;'>Debug: Embed code for ID 58...) is being rendered as part of the page content and not removed after the button is inserted.
    • The container.style.display = 'none' only hides the initial <div class="content-embed">, not the debug output or the original text if it’s part of the content flow.
  • CSP Resolution:
    • Disabling the "Enable CSP HTTP header" on the QNAP server resolved the SDK load issue, confirming the CSP was the primary blocker. The absence of custom headers on the Synology reverse proxy supports this.

Fix Strategy

  1. Remove Debug Output from Content:
    • Move the debug logging of the embed code to the JavaScript console instead of the page DOM to prevent it from being rendered as content.
  2. Ensure Content Replacement:
    • After inserting the <div> and rendering the button, explicitly clear or replace the original content-embed div to avoid displaying the raw code.
  3. Version Update:
    • Increment to V2.23 to reflect these changes.

Updated V2.23 Code (638 Lines)

<?php
// pull-contents.php (Version 2.23)

// Debug toggle: Set to true to enable debug messages, false to disable
define('DEBUG', true);

// Define base path for images
define('IMAGE_BASE_PATH', 'https://admin.nlt-chaplaincy.org/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 from calling file if $_GET['page'] not set
if (isset($_GET['page'])) {
    $current_page = $_GET['page'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Page set from GET: '$current_page'</div>";
    }
} else {
    $calling_file = basename($_SERVER['SCRIPT_FILENAME']);
    $request_uri = $_SERVER['REQUEST_URI'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Calling file: '$calling_file'</div>";
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: REQUEST_URI: '$request_uri'</div>";
    }
    $current_page = str_replace('.php', '', $calling_file);
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page after str_replace: '$current_page'</div>";
    }
    if ($current_page === 'index') {
        $current_page = 'landing';
        if (DEBUG) {
            echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page set to 'landing'</div>";
        }
        if (preg_match('/\/(task-map)\.php/', $request_uri, $matches)) {
            $current_page = $matches[1];
            if (DEBUG) {
                echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page updated from REQUEST_URI: '$current_page'</div>";
            }
        }
    }
}
$status = 'Show';

// Preload PayPal SDK with synchronous load
$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\" onload=\"console.log('PayPal SDK loaded')\" onerror=\"console.error('PayPal SDK load error:', event)\"></script>";

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Final current_page: '$current_page'</div>";
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Image Base Path: '" . IMAGE_BASE_PATH . "'</div>";
}

// Determine if we're on the stories page
$is_stories_page = ($current_page === 'stories');

// Check if the request is for modal content
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Is modal request: " . ($is_modal_request ? 'Yes' : 'No') . "</div>";
}

$sql = "SELECT id, title, content, code, code_type, 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);
if (!$stmt) {
    die("Prepare failed: " . $conn->connect_error);
}

$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();

$sections = [];
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $sections[] = $row;
    }
}

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Fetched " . count($sections) . " sections for page '$current_page'</div>";
}

// Group sections by position
$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);
    $current_index = 0;

    foreach ($sections_by_position as $position => $group) {
        $current_index++;
        $use_default_divider = true;
        $has_background_image = false;
        foreach ($group as $row) {
            if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                $has_background_image = true;
                $use_default_divider = false;
                break;
            }
        }
        if (isset($group[0]['chk_divider'])) {
            $use_default_divider = $group[0]['chk_divider'] == 1;
        }

        if ($is_modal_request) {
            foreach ($group as $index => $row) {
                if (DEBUG) {
                    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Rendering modal section ID: {$row['id']} at position: $position</div>";
                }
                echo "<div class=\"modal-story-content\">";
                if (!empty($row['title'])) {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                }
                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>";
                } elseif ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                }
                if (!empty($row['content'])) {
                    echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                }
                if (DEBUG) {
                    echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                }
                echo "</div>";
                if ($index < count($group) - 1 || $current_index < count($sections_by_position)) {
                    echo "<div class=\"modal-section-separator\"></div>";
                }
            }
        } else {
            if (count($group) > 1) {
                $column_count = min(count($group), 3);
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                echo "<section class=\"section-hero section-hero--full layout-$column_count-column$extra_class\">";
                echo "<div class=\"section-hero__content\">";
                foreach ($group as $index => $row) {
                    echo "<div class=\"column column-$index\">";
                    if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                        $background_image = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                        echo "<div class=\"background-image-wrapper\" style=\"background-image: url('$background_image');\"></div>";
                    }
                    if ($current_page === 'faq') {
                        echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                    } else {
                        echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center\">";
                        if ($is_stories_page) {
                            $content = strip_tags($row['content']);
                            $preview = mb_substr($content, 0, 60, 'UTF-8');
                            if (mb_strlen($content, 'UTF-8') > 60) {
                                $preview .= '...';
                            }
                            echo htmlspecialchars($preview);
                        } else {
                            echo $row['content'];
                        }
                        echo "</div>";
                    }
                    if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                        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: '{$row['code_type']}'});</script>";
                        if (DEBUG) {
                            console.log(`Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}`);
                        }
                    }
                    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>";
                    }
                    if (!empty($row['button_text'])) {
                        $button_text = htmlspecialchars($row['button_text']);
                        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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                            }
                            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>";
                            } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                                echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                            }
                            if (!empty($row['content'])) {
                                echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                            }
                            if (DEBUG) {
                                echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                            }
                            echo '</div>';
                            echo "</div>";
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                            if (DEBUG) {
                                echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                            }
                        } else {
                            $button_link = htmlspecialchars($row['button_link'] ?: '');
                            if (!empty($button_link) && $row['button_link'] !== '#') {
                                echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                            } else {
                                echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                            }
                        }
                        echo "</div>";
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</section>";
            } else {
                $row = $group[0];
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                $background_style = $has_background_image ? " style=\"background-image: url('" . IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']) . "');\"" : '';
                echo "<div class=\"section-hero section-hero--full$extra_class\"$background_style>";
                echo "<div class=\"section-hero__content\">";
                if ($current_page === 'faq') {
                    echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                } else {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center\">";
                    if ($is_stories_page) {
                        $content = strip_tags($row['content']);
                        $preview = mb_substr($content, 0, 60, 'UTF-8');
                        if (mb_strlen($content, 'UTF-8') > 60) {
                            $preview .= '...';
                        }
                        echo htmlspecialchars($preview);
                    } else {
                        echo $row['content'];
                    }
                    echo "</div>";
                }
                if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    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: '{$row['code_type']}'});</script>";
                    if (DEBUG) {
                        console.log(`Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}`);
                    }
                }
                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>";
                }
                if (!empty($row['button_text'])) {
                    $button_text = htmlspecialchars($row['button_text']);
                    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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        }
                        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>";
                        } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                            echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                        }
                        if (!empty($row['content'])) {
                            echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                        }
                        if (DEBUG) {
                            echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                        }
                        echo '</div>';
                        echo "</div>";
                        echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                        if (DEBUG) {
                            echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                        }
                    } else {
                        $button_link = htmlspecialchars($row['button_link'] ?: '');
                        if (!empty($button_link) && $row['button_link'] !== '#') {
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                        } else {
                            echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                        }
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</div>";
            }
        }

        if ($current_index < count($sections_by_position) && !$is_modal_request) {
            if ($use_default_divider) {
                echo "<div class=\"separator\"><img src=\"https://admin.nlt-chaplaincy.org/public/down_arrow.png\" alt=\"Default Separator\" class=\"separator-image\"></div>";
            } elseif (!empty($row['theme_img']) && $row['img_role'] === 'Separator') {
                $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                echo "<div class=\"separator\"><img src=\"$image_url\" alt=\"Custom Separator\" class=\"separator-image\"></div>";
            }
        }

        if (DEBUG) {
            echo "<div style=\"color: green; font-weight: bold; text-align: center;\">Debug: Section with position $position rendered successfully with " . count($group) . " items.</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>';
}

$stmt->close();
$conn->close();
?>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Synchronize with PHP DEBUG setting
    const DEBUG = <?php echo DEBUG ? 'true' : 'false'; ?>;

    // Debug log setup
    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}`, { color });
    }

    if (DEBUG) logDebug('Modal script loaded');

    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');

    // Function to render embed blocks with script separation
    function renderEmbed(item) {
        const container = document.getElementById(`embed-${item.id}`);
        if (!container) {
            if (DEBUG) logDebug(`Container not found for embed ID ${item.id}`, 'red');
            return;
        }

        // Extract components with flexible matching
        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 (DEBUG) {
            logDebug(`Div match for ID ${item.id}: ${divMatch ? 'Success' : 'Failed'}`, divMatch ? 'green' : 'red');
            logDebug(`Button match for ID ${item.id}: ${buttonMatch ? 'Success' : 'Failed'}`, buttonMatch ? 'green' : 'red');
        }

        if (item.type === 'PayPal' && divMatch && buttonMatch) {
            // Insert div and clear container
            container.innerHTML = divMatch[0];
            if (DEBUG) logDebug(`Inserted div for PayPal ID ${item.id}`, 'orange');

            // Check SDK availability with retry
            const checkSDK = setInterval(() => {
                if (typeof paypal !== 'undefined') {
                    clearInterval(checkSDK);
                    const buttonScript = document.createElement('script');
                    buttonScript.textContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
                    document.body.appendChild(buttonScript);
                    if (DEBUG) logDebug(`Rendered PayPal button for ID ${item.id}`, 'orange');
                    container.style.display = 'block'; // Ensure visibility
                } else if (DEBUG) {
                    logDebug(`Checking SDK for ID ${item.id}, status: ${typeof paypal}`, 'yellow');
                }
            }, 100);

            // Extended timeout with detailed log
            setTimeout(() => {
                if (typeof paypal === 'undefined') {
                    clearInterval(checkSDK);
                    const sdkScript = document.getElementById('paypal-sdk');
                    const errorMsg = sdkScript ? (sdkScript.error || 'No specific error') : 'Script element not found';
                    if (DEBUG) logDebug(`PayPal SDK failed to load for ID ${item.id} after 10s, Error: ${errorMsg}`, 'red');
                }
            }, 10000);
        } else if (item.type === 'YouTube') {
            // Insert YouTube iframe directly
            container.innerHTML = item.code;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Rendered YouTube embed for ID ${item.id}`, 'orange');
        } else {
            // Fallback only on parsing failure
            container.innerHTML = `<div class="embed-fallback" id="fallback-${item.id}">${item.code}</div>`;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Fallback rendered for ID ${item.id} (parsing failed, scripts not executed)`, 'red');
        }
    }

    // Process embed blocks
    if (window.contentCode && Array.isArray(window.contentCode)) {
        window.contentCode.forEach(item => {
            renderEmbed(item);
        });
    } else if (DEBUG) {
        logDebug('contentCode is not an array or unavailable', 'red');
    }

    function openModal(content) {
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = content;

        const buttons = tempDiv.querySelectorAll('.content__buttons');
        buttons.forEach(button => button.remove());

        const debugMessages = tempDiv.querySelectorAll('div[style*="color: purple"], div[style*="color: blue"], div[style*="color: red"], div[style*="color: green"]');
        debugMessages.forEach(msg => msg.remove());

        let combinedContent = '';
        const sections = tempDiv.querySelectorAll('.modal-story-content');
        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
        if (sections.length > 0) {
            sections.forEach((section, index) => {
                combinedContent += section.outerHTML.trim();
                if (index < sections.length - 1) {
                    combinedContent += '<div class="modal-section-separator"></div>';
                }
            });
        } else {
            const fallbackSections = tempDiv.querySelectorAll('.section-hero__content');
            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
            if (fallbackSections.length > 0) {
                fallbackSections.forEach((section, index) => {
                    const title = section.querySelector('h1');
                    const image = section.querySelector('.content-image');
                    const content = section.querySelector('.generic-content');

                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    if (title) newContent.appendChild(title.cloneNode(true));
                    if (image) newContent.appendChild(image.cloneNode(true));
                    if (content) newContent.appendChild(content.cloneNode(true));

                    combinedContent += newContent.outerHTML.trim();
                    if (index < fallbackSections.length - 1) {
                        combinedContent += '<div class="modal-section-separator"></div>';
                    }
                });
            } else {
                const genericContent = tempDiv.querySelector('.generic-content');
                if (genericContent) {
                    if (DEBUG) logDebug('Falling back to .generic-content');
                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    newContent.appendChild(genericContent.cloneNode(true));
                    combinedContent = newContent.outerHTML.trim();
                } else {
                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                }
            }
        }

        modalBody.innerHTML = combinedContent;
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        if (modalBody.children.length > 0 && closeBtn) {
            closeBtn.focus();
        }
        if (DEBUG) logDebug('Modal opened');
    }

    function closeModal() {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        modalBody.innerHTML = '';
        if (DEBUG && debugLog) {
            debugLog.innerHTML = '';
        }
        if (DEBUG) logDebug('Modal closed');
    }

    if (modal && modalBody && closeBtn && triggers) {
        triggers.forEach(trigger => {
            trigger.addEventListener('click', function(e) {
                e.preventDefault();
                const modalId = this.getAttribute('data-modal-id');
                if (modalId) {
                    const contentDiv = document.getElementById(modalId);
                    if (contentDiv) {
                        const content = contentDiv.innerHTML;
                        if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
                        if (DEBUG) logDebug('Modal content length: ' + content.length + ' characters', 'purple');
                        openModal(content);
                    } else {
                        openModal('<p>Content not found.</p>');
                        if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
                    }
                    return;
                }

                const link = this.getAttribute('data-modal-link');
                if (DEBUG) logDebug('Trigger clicked, link: ' + link);

                if (!link || link === '#' || link.includes('index.php')) {
                    openModal('<p>This link cannot be loaded in a modal to prevent recursive loading.</p>');
                    return;
                }

                let basePath = window.location.origin;
                let absoluteLink;
                try {
                    if (link.startsWith('http://') || link.startsWith('https://')) {
                        absoluteLink = link;
                    } else if (link.startsWith('/')) {
                        absoluteLink = new URL(link, window.location.origin).href;
                    } else {
                        absoluteLink = new URL(link, window.location.origin + '/').href;
                    }
                    absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
                    if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
                } catch (error) {
                    if (DEBUG) logDebug('URL construction error: ' + error.message, 'red');
                    openModal('<p>Invalid URL format.</p>');
                    return;
                }

                openModal('<p>Loading...</p>');

                fetch(absoluteLink, {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                })
                    .then(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(data => {
                        if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(data, 'text/html');

                        let combinedContent = '';
                        const sections = doc.querySelectorAll('.modal-story-content');
                        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections in fetched content');
                        if (sections.length > 0) {
                            sections.forEach((content, index) => {
                                combinedContent += content.outerHTML.trim();
                                if (index < sections.length - 1) {
                                    combinedContent += '<div class="modal-section-separator"></div>';
                                }
                            });
                        } else {
                            const fallbackSections = doc.querySelectorAll('.section-hero__content');
                            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
                            if (fallbackSections.length > 0) {
                                fallbackSections.forEach((section, index) => {
                                    const title = section.querySelector('h1');
                                    const image = section.querySelector('.content-image');
                                    const content = section.querySelector('.generic-content');

                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    if (title) newContent.appendChild(title.cloneNode(true));
                                    if (image) newContent.appendChild(image.cloneNode(true));
                                    if (content) newContent.appendChild(content.cloneNode(true));

                                    combinedContent += newContent.outerHTML.trim();
                                    if (index < fallbackSections.length - 1) {
                                        combinedContent += '<div class="modal-section-separator"></div>';
                                    }
                                });
                            } else {
                                const genericContent = doc.querySelector('.generic-content');
                                if (genericContent) {
                                    if (DEBUG) logDebug('Falling back to .generic-content');
                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    newContent.appendChild(genericContent.cloneNode(true));
                                    combinedContent = newContent.outerHTML.trim();
                                } else {
                                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                                }
                            }
                        }

                        if (combinedContent) {
                            openModal(combinedContent);
                        } else {
                            openModal('<p>No content found.</p>');
                        }
                    })
                    .catch(error => {
                        if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
                        openModal('<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 (modal, modalBody, closeBtn, or triggers)', 'red');
    }
});
</script>

Changes Made

  1. Remove Debug Output from Content:

    • Moved the embed code debug from echo "<div style='color: orange;'>..." to console.log in both PHP sections, ensuring it appears in the browser console (F12) rather than the page content.
  2. Ensure Content Replacement:

    • Changed container.insertAdjacentHTML('afterend', divMatch[0]) to container.innerHTML = divMatch[0] to replace the entire content-embed div’s content with the PayPal <div>, avoiding residual text.
    • Added container.style.display = 'block' after rendering to ensure visibility, though this should be automatic with innerHTML.
  3. Version Update:

    • Incremented to Version 2.23.

Testing Instructions

  1. Verify Database:

    • Confirm the content column for ID 58 contains the updated button script and <div> as shown.
    • Ensure code_type is PayPal.
  2. Deploy Updated File:

    • Replace pull-contents.php with the 638-line V2.23 code.
    • Confirm the line count is 638 in your editor.
  3. Regenerate and Test:

    • Regenerate the site with Publii.
    • Load https://www.nlt-chaplaincy.org/index.html and check the PP section at position 100.
  4. Test PayPal Button:

    • Verify the PP button displays (it should appear as a "Subscribe" pill button, not the raw code).
    • Click the button to test the subscription flow (expect an alert with subscriptionID).
    • Open the browser console (F12) for JavaScript errors or SDK load issues.
  5. Test YouTube Embed:

    • Ensure any YouTube <iframe> embeds display and play correctly.
  6. Debug Output:

    • Keep DEBUG true and review messages in the floating log (e.g., match success, insertion, rendering).
    • Check the browser console (F12) for the embed code log and any errors (e.g., PayPal SDK load error:).
    • Share the full debug log and console output.

Additional Notes

  • Time: It’s 07:55 PM HKT on Friday, August 29, 2025—still time to test today, though it’s late. We can continue tomorrow if needed.
  • CSP Confirmation: Disabling the QNAP CSP resolved the SDK load, and no Synology headers were found, so the environment should now support PayPal scripts.
  • Next Steps: If the button still doesn’t render, inspect the DOM (F12 > Elements) to confirm the <div> and button are present but hidden, or check for JavaScript errors.

Please deploy, test, and share the debug log and console output! We’re close to resolving this.


Q: Things are almost there with V2.23. I can see two PP buttons (Subscribe via PP or Credit Card) but below the buttons I can still see orange debug message:

Debug: Embed code for ID 58 (PayPal): <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"></div> <script> paypal.Buttons({ style: { shape: 'pill', color: 'gold', layout: 'vertical', label: 'subscribe' }, createSubscription: function(data, actions) { return actions.subscription.create({ plan_id: 'P-3FU48207V4555173AM5STYUA' }); }, onApprove: function(data, actions) { alert(data.subscriptionID); } }).render('#paypal-button-container-P-3FU48207V4555173AM5STYUA'); </script>

The floating debug msg says:

Debug: Inserted div for PayPal ID 58
Debug: Rendered PayPal button for ID 58

V2.24 has unfortunately made things more worse. Firstly the PP buttons are missing with debug msg://Fatal error: Uncaught Error: Undefined constant "console" in /share/CACHEDEV1_DATA/Web/nlhcm/public/pull-contents.php:269 Stack trace: #0 /share/CACHEDEV1_DATA/Web/nlhcm/public/index.php(3): include() #1 {main} thrown in /share/CACHEDEV1_DATA/Web/nlhcm/public/pull-contents.php on line 269//The background image of the left frame (Top frame in mobile view) fails to load and has become dark.Interestingly, re-testing V2.23 has the same effect. What worked was V2.22 actually. I guess there might be a cache issue. What confirmed working is V2.22, both V2.23 and 2.24 are not working.


A: Thank you for the update! The current date and time is 12:45 PM HKT on Monday, September 01, 2025. I apologize for the issues with V2.23 and V2.24—let’s analyze the problem, address the fatal error, and get back to a working state based on your confirmation that V2.22 worked.

Analysis

Issues with V2.23 and V2.24

  • Fatal Error:
    • The error Undefined constant "console" in /share/CACHEDEV1_DATA/Web/nlhcm/public/pull-contents.php:269 indicates that PHP is interpreting console as a constant rather than the JavaScript console object. This happened because the console.log calls in the PHP section (e.g., if (DEBUG) { console.log(...); }) were incorrectly placed in PHP code instead of a <script> block. PHP doesn’t recognize console as a valid object unless it’s within a JavaScript context, causing the fatal error on line 269 (likely where the first console.log appears).
  • Background Image Failure:
    • The dark background image issue in the left frame (top frame on mobile) suggests a disruption in how the theme_img or background-image-wrapper is being processed. This could be due to the PHP error halting script execution, preventing proper image rendering, or a side effect of the code changes affecting the CSS/JS interaction.
  • Regression from V2.22:
    • Your observation that V2.22 worked, while V2.23 and V2.24 did not, points to an issue introduced in the later versions. The fatal error in V2.23/V2.24 likely caused the PP buttons to disappear, and the background image issue might be a cascading effect of the script failure.

Why V2.22 Worked

  • V2.22 successfully rendered the PayPal buttons after the CSP was disabled, with the orange debug message as the only remaining issue. The console.log change wasn’t introduced until V2.23, so V2.22 avoided the PHP syntax error.

Cache Consideration

  • You mentioned a possible cache issue. Since V2.23 reverted to the same behavior as V2.24 (despite being previously functional), it’s likely that a server-side or browser cache is serving an older or corrupted version of the file. Clearing the cache on both the server (QNAP) and browser should help, but the fatal error takes precedence.

Fix Strategy

  1. Fix PHP Syntax Error:
    • Move the console.log calls back to a proper JavaScript context (e.g., within a <script> block) or remove them from PHP entirely, relying on the existing JavaScript logDebug function.
  2. Restore Working Functionality:
    • Revert to the V2.22 code as a baseline, then apply a targeted fix for the debug output issue.
  3. Address Background Image:
    • Ensure the background image rendering logic remains intact by verifying the theme_img and img_role handling.
  4. Version Update:
    • Increment to V2.25 to reflect the corrected version.

Updated V2.25 Code (638 Lines)

<?php
// pull-contents.php (Version 2.25)

// Debug toggle: Set to true to enable debug messages, false to disable
define('DEBUG', true);

// Define base path for images
define('IMAGE_BASE_PATH', 'https://admin.nlt-chaplaincy.org/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 from calling file if $_GET['page'] not set
if (isset($_GET['page'])) {
    $current_page = $_GET['page'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Page set from GET: '$current_page'</div>";
    }
} else {
    $calling_file = basename($_SERVER['SCRIPT_FILENAME']);
    $request_uri = $_SERVER['REQUEST_URI'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Calling file: '$calling_file'</div>";
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: REQUEST_URI: '$request_uri'</div>";
    }
    $current_page = str_replace('.php', '', $calling_file);
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page after str_replace: '$current_page'</div>";
    }
    if ($current_page === 'index') {
        $current_page = 'landing';
        if (DEBUG) {
            echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page set to 'landing'</div>";
        }
        if (preg_match('/\/(task-map)\.php/', $request_uri, $matches)) {
            $current_page = $matches[1];
            if (DEBUG) {
                echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page updated from REQUEST_URI: '$current_page'</div>";
            }
        }
    }
}
$status = 'Show';

// Preload PayPal SDK with synchronous load
$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\" onload=\"console.log('PayPal SDK loaded')\" onerror=\"console.error('PayPal SDK load error:', event)\"></script>";

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Final current_page: '$current_page'</div>";
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Image Base Path: '" . IMAGE_BASE_PATH . "'</div>";
}

// Determine if we're on the stories page
$is_stories_page = ($current_page === 'stories');

// Check if the request is for modal content
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Is modal request: " . ($is_modal_request ? 'Yes' : 'No') . "</div>";
}

$sql = "SELECT id, title, content, code, code_type, 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);
if (!$stmt) {
    die("Prepare failed: " . $conn->connect_error);
}

$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();

$sections = [];
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $sections[] = $row;
    }
}

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Fetched " . count($sections) . " sections for page '$current_page'</div>";
}

// Group sections by position
$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);
    $current_index = 0;

    foreach ($sections_by_position as $position => $group) {
        $current_index++;
        $use_default_divider = true;
        $has_background_image = false;
        foreach ($group as $row) {
            if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                $has_background_image = true;
                $use_default_divider = false;
                break;
            }
        }
        if (isset($group[0]['chk_divider'])) {
            $use_default_divider = $group[0]['chk_divider'] == 1;
        }

        if ($is_modal_request) {
            foreach ($group as $index => $row) {
                if (DEBUG) {
                    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Rendering modal section ID: {$row['id']} at position: $position</div>";
                }
                echo "<div class=\"modal-story-content\">";
                if (!empty($row['title'])) {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                }
                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>";
                } elseif ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                }
                if (!empty($row['content'])) {
                    echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                }
                if (DEBUG) {
                    echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                }
                echo "</div>";
                if ($index < count($group) - 1 || $current_index < count($sections_by_position)) {
                    echo "<div class=\"modal-section-separator\"></div>";
                }
            }
        } else {
            if (count($group) > 1) {
                $column_count = min(count($group), 3);
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                echo "<section class=\"section-hero section-hero--full layout-$column_count-column$extra_class\">";
                echo "<div class=\"section-hero__content\">";
                foreach ($group as $index => $row) {
                    echo "<div class=\"column column-$index\">";
                    if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                        $background_image = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                        echo "<div class=\"background-image-wrapper\" style=\"background-image: url('$background_image');\"></div>";
                    }
                    if ($current_page === 'faq') {
                        echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                    } else {
                        echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center\">";
                        if ($is_stories_page) {
                            $content = strip_tags($row['content']);
                            $preview = mb_substr($content, 0, 60, 'UTF-8');
                            if (mb_strlen($content, 'UTF-8') > 60) {
                                $preview .= '...';
                            }
                            echo htmlspecialchars($preview);
                        } else {
                            echo $row['content'];
                        }
                        echo "</div>";
                    }
                    if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                        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: '{$row['code_type']}'});</script>";
                        if (DEBUG) {
                            echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>"; // Temporarily hidden
                        }
                    }
                    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>";
                    }
                    if (!empty($row['button_text'])) {
                        $button_text = htmlspecialchars($row['button_text']);
                        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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                            }
                            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>";
                            } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                                echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                            }
                            if (!empty($row['content'])) {
                                echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                            }
                            if (DEBUG) {
                                echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                            }
                            echo '</div>';
                            echo "</div>";
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                            if (DEBUG) {
                                echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                            }
                        } else {
                            $button_link = htmlspecialchars($row['button_link'] ?: '');
                            if (!empty($button_link) && $row['button_link'] !== '#') {
                                echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                            } else {
                                echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                            }
                        }
                        echo "</div>";
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</section>";
            } else {
                $row = $group[0];
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                $background_style = $has_background_image ? " style=\"background-image: url('" . IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']) . "');\"" : '';
                echo "<div class=\"section-hero section-hero--full$extra_class\"$background_style>";
                echo "<div class=\"section-hero__content\">";
                if ($current_page === 'faq') {
                    echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                } else {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center\">";
                    if ($is_stories_page) {
                        $content = strip_tags($row['content']);
                        $preview = mb_substr($content, 0, 60, 'UTF-8');
                        if (mb_strlen($content, 'UTF-8') > 60) {
                            $preview .= '...';
                        }
                        echo htmlspecialchars($preview);
                    } else {
                        echo $row['content'];
                    }
                    echo "</div>";
                }
                if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    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: '{$row['code_type']}'});</script>";
                    if (DEBUG) {
                        echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>"; // Temporarily hidden
                    }
                }
                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>";
                }
                if (!empty($row['button_text'])) {
                    $button_text = htmlspecialchars($row['button_text']);
                    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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        }
                        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>";
                        } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                            echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                        }
                        if (!empty($row['content'])) {
                            echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                        }
                        if (DEBUG) {
                            echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                        }
                        echo '</div>';
                        echo "</div>";
                        echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                        if (DEBUG) {
                            echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                        }
                    } else {
                        $button_link = htmlspecialchars($row['button_link'] ?: '');
                        if (!empty($button_link) && $row['button_link'] !== '#') {
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                        } else {
                            echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                        }
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</div>";
            }
        }

        if ($current_index < count($sections_by_position) && !$is_modal_request) {
            if ($use_default_divider) {
                echo "<div class=\"separator\"><img src=\"https://admin.nlt-chaplaincy.org/public/down_arrow.png\" alt=\"Default Separator\" class=\"separator-image\"></div>";
            } elseif (!empty($row['theme_img']) && $row['img_role'] === 'Separator') {
                $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                echo "<div class=\"separator\"><img src=\"$image_url\" alt=\"Custom Separator\" class=\"separator-image\"></div>";
            }
        }

        if (DEBUG) {
            echo "<div style=\"color: green; font-weight: bold; text-align: center;\">Debug: Section with position $position rendered successfully with " . count($group) . " items.</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>';
}

$stmt->close();
$conn->close();
?>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Synchronize with PHP DEBUG setting
    const DEBUG = <?php echo DEBUG ? 'true' : 'false'; ?>;

    // Debug log setup
    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}`, { color });
    }

    if (DEBUG) logDebug('Modal script loaded');

    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');

    // Function to render embed blocks with script separation
    function renderEmbed(item) {
        const container = document.getElementById(`embed-${item.id}`);
        if (!container) {
            if (DEBUG) logDebug(`Container not found for embed ID ${item.id}`, 'red');
            return;
        }

        // Extract components with flexible matching
        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 (DEBUG) {
            logDebug(`Div match for ID ${item.id}: ${divMatch ? 'Success' : 'Failed'}`, divMatch ? 'green' : 'red');
            logDebug(`Button match for ID ${item.id}: ${buttonMatch ? 'Success' : 'Failed'}`, buttonMatch ? 'green' : 'red');
        }

        if (item.type === 'PayPal' && divMatch && buttonMatch) {
            // Insert div and clear container
            container.innerHTML = divMatch[0];
            if (DEBUG) logDebug(`Inserted div for PayPal ID ${item.id}`, 'orange');

            // Check SDK availability with retry
            const checkSDK = setInterval(() => {
                if (typeof paypal !== 'undefined') {
                    clearInterval(checkSDK);
                    const buttonScript = document.createElement('script');
                    buttonScript.textContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
                    document.body.appendChild(buttonScript);
                    if (DEBUG) logDebug(`Rendered PayPal button for ID ${item.id}`, 'orange');
                    container.style.display = 'block'; // Ensure visibility
                } else if (DEBUG) {
                    logDebug(`Checking SDK for ID ${item.id}, status: ${typeof paypal}`, 'yellow');
                }
            }, 100);

            // Extended timeout with detailed log
            setTimeout(() => {
                if (typeof paypal === 'undefined') {
                    clearInterval(checkSDK);
                    const sdkScript = document.getElementById('paypal-sdk');
                    const errorMsg = sdkScript ? (sdkScript.error || 'No specific error') : 'Script element not found';
                    if (DEBUG) logDebug(`PayPal SDK failed to load for ID ${item.id} after 10s, Error: ${errorMsg}`, 'red');
                }
            }, 10000);
        } else if (item.type === 'YouTube') {
            // Insert YouTube iframe directly
            container.innerHTML = item.code;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Rendered YouTube embed for ID ${item.id}`, 'orange');
        } else {
            // Fallback only on parsing failure
            container.innerHTML = `<div class="embed-fallback" id="fallback-${item.id}">${item.code}</div>`;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Fallback rendered for ID ${item.id} (parsing failed, scripts not executed)`, 'red');
        }
    }

    // Process embed blocks
    if (window.contentCode && Array.isArray(window.contentCode)) {
        window.contentCode.forEach(item => {
            renderEmbed(item);
        });
    } else if (DEBUG) {
        logDebug('contentCode is not an array or unavailable', 'red');
    }

    function openModal(content) {
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = content;

        const buttons = tempDiv.querySelectorAll('.content__buttons');
        buttons.forEach(button => button.remove());

        const debugMessages = tempDiv.querySelectorAll('div[style*="color: purple"], div[style*="color: blue"], div[style*="color: red"], div[style*="color: green"]');
        debugMessages.forEach(msg => msg.remove());

        let combinedContent = '';
        const sections = tempDiv.querySelectorAll('.modal-story-content');
        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
        if (sections.length > 0) {
            sections.forEach((section, index) => {
                combinedContent += section.outerHTML.trim();
                if (index < sections.length - 1) {
                    combinedContent += '<div class="modal-section-separator"></div>';
                }
            });
        } else {
            const fallbackSections = tempDiv.querySelectorAll('.section-hero__content');
            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
            if (fallbackSections.length > 0) {
                fallbackSections.forEach((section, index) => {
                    const title = section.querySelector('h1');
                    const image = section.querySelector('.content-image');
                    const content = section.querySelector('.generic-content');

                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    if (title) newContent.appendChild(title.cloneNode(true));
                    if (image) newContent.appendChild(image.cloneNode(true));
                    if (content) newContent.appendChild(content.cloneNode(true));

                    combinedContent += newContent.outerHTML.trim();
                    if (index < fallbackSections.length - 1) {
                        combinedContent += '<div class="modal-section-separator"></div>';
                    }
                });
            } else {
                const genericContent = tempDiv.querySelector('.generic-content');
                if (genericContent) {
                    if (DEBUG) logDebug('Falling back to .generic-content');
                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    newContent.appendChild(genericContent.cloneNode(true));
                    combinedContent = newContent.outerHTML.trim();
                } else {
                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                }
            }
        }

        modalBody.innerHTML = combinedContent;
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        if (modalBody.children.length > 0 && closeBtn) {
            closeBtn.focus();
        }
        if (DEBUG) logDebug('Modal opened');
    }

    function closeModal() {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        modalBody.innerHTML = '';
        if (DEBUG && debugLog) {
            debugLog.innerHTML = '';
        }
        if (DEBUG) logDebug('Modal closed');
    }

    if (modal && modalBody && closeBtn && triggers) {
        triggers.forEach(trigger => {
            trigger.addEventListener('click', function(e) {
                e.preventDefault();
                const modalId = this.getAttribute('data-modal-id');
                if (modalId) {
                    const contentDiv = document.getElementById(modalId);
                    if (contentDiv) {
                        const content = contentDiv.innerHTML;
                        if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
                        if (DEBUG) logDebug('Modal content length: ' + content.length + ' characters', 'purple');
                        openModal(content);
                    } else {
                        openModal('<p>Content not found.</p>');
                        if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
                    }
                    return;
                }

                const link = this.getAttribute('data-modal-link');
                if (DEBUG) logDebug('Trigger clicked, link: ' + link);

                if (!link || link === '#' || link.includes('index.php')) {
                    openModal('<p>This link cannot be loaded in a modal to prevent recursive loading.</p>');
                    return;
                }

                let basePath = window.location.origin;
                let absoluteLink;
                try {
                    if (link.startsWith('http://') || link.startsWith('https://')) {
                        absoluteLink = link;
                    } else if (link.startsWith('/')) {
                        absoluteLink = new URL(link, window.location.origin).href;
                    } else {
                        absoluteLink = new URL(link, window.location.origin + '/').href;
                    }
                    absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
                    if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
                } catch (error) {
                    if (DEBUG) logDebug('URL construction error: ' + error.message, 'red');
                    openModal('<p>Invalid URL format.</p>');
                    return;
                }

                openModal('<p>Loading...</p>');

                fetch(absoluteLink, {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                })
                    .then(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(data => {
                        if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(data, 'text/html');

                        let combinedContent = '';
                        const sections = doc.querySelectorAll('.modal-story-content');
                        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections in fetched content');
                        if (sections.length > 0) {
                            sections.forEach((content, index) => {
                                combinedContent += content.outerHTML.trim();
                                if (index < sections.length - 1) {
                                    combinedContent += '<div class="modal-section-separator"></div>';
                                }
                            });
                        } else {
                            const fallbackSections = doc.querySelectorAll('.section-hero__content');
                            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
                            if (fallbackSections.length > 0) {
                                fallbackSections.forEach((section, index) => {
                                    const title = section.querySelector('h1');
                                    const image = section.querySelector('.content-image');
                                    const content = section.querySelector('.generic-content');

                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    if (title) newContent.appendChild(title.cloneNode(true));
                                    if (image) newContent.appendChild(image.cloneNode(true));
                                    if (content) newContent.appendChild(content.cloneNode(true));

                                    combinedContent += newContent.outerHTML.trim();
                                    if (index < fallbackSections.length - 1) {
                                        combinedContent += '<div class="modal-section-separator"></div>';
                                    }
                                });
                            } else {
                                const genericContent = doc.querySelector('.generic-content');
                                if (genericContent) {
                                    if (DEBUG) logDebug('Falling back to .generic-content');
                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    newContent.appendChild(genericContent.cloneNode(true));
                                    combinedContent = newContent.outerHTML.trim();
                                } else {
                                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                                }
                            }
                        }

                        if (combinedContent) {
                            openModal(combinedContent);
                        } else {
                            openModal('<p>No content found.</p>');
                        }
                    })
                    .catch(error => {
                        if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
                        openModal('<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 (modal, modalBody, closeBtn, or triggers)', 'red');
    }
});
</script>

Changes Made

  1. Fix PHP Syntax Error:
    • Removed the console.log calls from the PHP section and reverted to using echo with display: none for the embed code debug (as a temporary measure). The JavaScript logDebug function will handle logging correctly.
  2. Restore Working Functionality:
    • Reverted the code to the V2.22 structure, which worked for you, and applied a minimal change to address the debug output.
  3. Address Background Image:
    • Ensured the background-image-wrapper logic remains unchanged, which should restore the background image rendering.
  4. Version Update:
    • Incremented to Version 2.25.

Testing Instructions

  1. Verify Database:

    • Confirm the content column for ID 58 contains the button script and <div> as shown in your update.
    • Ensure code_type is PayPal.
  2. Clear Cache:

    • Clear your browser cache (Ctrl+Shift+Delete or equivalent).
    • Clear the QNAP server cache (e.g., restart the web service via QNAP Control Panel > Applications > Web Server > Restart) to ensure the new file is loaded.
    • Regenerate the site with Publii to refresh its cache.
  3. Deploy Updated File:

    • Replace pull-contents.php with the 638-line V2.25 code.
    • Confirm the line count is 638 in your editor.
  4. Regenerate and Test:

    • Regenerate the site with Publii.
    • Load https://www.nlt-chaplaincy.org/index.html and check the PP section at position 100.
  5. Test PayPal Button:

    • Verify the two PP buttons (Subscribe via PayPal or Credit Card) display correctly, with the orange debug message either hidden or absent.
    • Click each button to test the subscription flow (expect an alert with subscriptionID).
    • Open the browser console (F12) for JavaScript errors or SDK load issues.
  6. Test Background Image:

    • Verify the background image in the left frame (top frame on mobile) loads correctly and isn’t dark.
  7. Test YouTube Embed:

    • Ensure any YouTube <iframe> embeds display and play correctly.
  8. Debug Output:

    • Keep DEBUG true and review messages in the floating log (e.g., match success, insertion, rendering).
    • Check the browser console (F12) for the embed code log and any errors (e.g., PayPal SDK load error:).
    • Share the full debug log and console output.

Additional Notes

  • Time: It’s 12:50 PM HKT on Monday, September 01, 2025—plenty of time to test today.
  • Cache Issue: The regression with V2.23 might be due to a cached version of V2.24 causing the fatal error to persist. Clearing caches should resolve this.
  • Debug Fix: The display: none approach is a quick fix. Once confirmed working, we can move the debug to the console properly in a future version.
  • Next Steps: If V2.25 restores the buttons and background image but the orange message remains, we’ll refine the debug handling further.

Please deploy, clear caches, test, and share the debug log and console output! We’ll get this back on track.


Q: Things look working again! Just that the floating debug message says:

Debug: Inserted div for PayPal ID 58
Debug: Checking SDK for ID 58, status: undefined
Debug: Rendered PayPal button for ID 58Is the "status: undefined" problematic?

A: Great to hear things are working again with V2.25! The current date and time is 1:00 PM HKT on Monday, September 01, 2025. Let’s analyze the floating debug messages and address your concern about the "status: undefined" message.

Analysis

Debug Messages

  • Floating Debug Output:
    • Debug: Inserted div for PayPal ID 58: Confirms the <div id="paypal-button-container-P-3FU48207V4555173AM5STYUA"> was successfully inserted into the DOM.
    • Debug: Checking SDK for ID 58, status: undefined: Indicates the initial check for the paypal object returned undefined, meaning the PayPal SDK hadn’t loaded yet at that point.
    • Debug: Rendered PayPal button for ID 58: Confirms the button script executed successfully, implying the SDK loaded eventually and the buttons (Subscribe via PayPal or Credit Card) were rendered.

Is "status: undefined" Problematic?

  • Context:
    • The checkSDK interval in the renderEmbed function periodically checks typeof paypal to determine if the SDK is available. The status: undefined message appears because the first few checks occur before the SDK finishes loading, which is expected behavior given the asynchronous nature of script loading (even with async="false").
    • The subsequent Debug: Rendered PayPal button for ID 58 indicates the SDK did load, and the button was rendered, so the undefined status didn’t prevent functionality.
  • Impact:
    • This is not problematic as long as the buttons are visible and functional. The setInterval ensures the script waits until paypal is defined before proceeding, and the 10-second timeout would catch any persistent failure (though no timeout error was logged).
  • Potential Concern:
    • If the undefined checks persist for a long time (e.g., beyond a few iterations) or the timeout triggers (PayPal SDK failed to load after 10s), it could indicate a loading delay. However, since the buttons rendered, the delay was resolved within the script’s logic.

Conclusion

The "status: undefined" message is not problematic in this case. It’s a normal part of the SDK loading process, and the successful rendering of the PayPal buttons confirms the system worked as intended. The debug output simply reflects the intermediate state before the SDK was available.

Next Steps

  • Verification:
    • Test the PayPal buttons by clicking them to ensure the subscription flow works (expect an alert with subscriptionID).
    • Check the browser console (F12) for any errors or additional PayPal SDK load error: messages.
  • Optimization (Optional):
    • If you notice a noticeable delay before the buttons appear, we could reduce the setInterval delay (e.g., from 100ms to 50ms) or add a loading indicator. However, this isn’t necessary unless it impacts user experience.
  • Debug Cleanup:
    • The orange debug message in the DOM is hidden with display: none in V2.25. If you’d prefer it removed entirely, we can adjust the code in the next version to rely solely on the JavaScript logDebug function.

Testing Instructions

  1. Test PayPal Buttons:
    • Click each button (Subscribe via PayPal or Credit Card) to confirm the subscription flow triggers the onApprove alert.
  2. Check Console:
    • Open the browser console (F12) and verify no errors related to the PayPal SDK or button rendering.
  3. Share Feedback:
    • Let me know if the buttons work as expected and if the "status: undefined" messages seem to delay the button appearance noticeably.

Additional Notes

  • Time: It’s 1:05 PM HKT on Monday, September 01, 2025—still plenty of time to test today.
  • Current State: V2.25 appears to restore the functionality of V2.22 while keeping the debug system intact. We can refine further based on your feedback.

Please test the buttons and share the results or any console output! If everything works, we can consider this resolved or plan minor enhancements.

Original Chat


Q: Let's go back to the PP subscription buttons. I've now added 3 more PP sections from position "101-103". While all four sections are displayed in correct order, PP buttons are only shown in the position=100 section, buttons for section positioned "101-103" fail to show.The floating debug message says:

Debug: Div match for ID 58: Success
Debug: Button match for ID 58: Success
Debug: Inserted div for PayPal ID 58
Debug: Div match for ID 59: Success
Debug: Button match for ID 59: Failed
Debug: Fallback rendered for ID 59 (parsing failed, scripts not executed)
Debug: Div match for ID 60: Success
Debug: Button match for ID 60: Failed
Debug: Fallback rendered for ID 60 (parsing failed, scripts not executed)
Debug: Div match for ID 61: Success
Debug: Button match for ID 61: Failed
Debug: Fallback rendered for ID 61 (parsing failed, scripts not executed)
Debug: Rendered PayPal button for ID 58

I tried assigning the same position for two sections but they do not appear in columns. But we can handle this later when the button display is fixed.


Q: Thanks for the update, but V2.26 doesn't solve the problem.

Since you said "If the SDK isn’t available or reinitialized for IDs 59-61, the button script won’t execute, even if the < div > is inserted", I did check the codes for the buttons. I find each button seems to have its own SDK. Since we're only initializing SDK for ID58, that could be what cause other buttons to fail to display.Below are the codes from PP for our four subscription amount:


V2.27 fails to show buttons for all four sections, including the originally working ones.


Thanks for the PP code. V2.28 still fails to show PP buttons for all four sections. The debug code is:


I've updated sections 59-61 with the new button codes. V2.29 still fails to show any buttons. The debug code is:


A: Thank you for the update! The current date and time is 12:45 PM HKT on Wednesday, September 03, 2025. The debug log provides valuable insight into why the PayPal buttons aren’t rendering. Let’s analyze the issue and fix it.

Analysis

Debug Output (V2.29)

  • Positive Signs:
    • Div match: Success and Button match: Success for all IDs (58-61) indicate the regex is now correctly identifying the <div> and paypal.Buttons script in the updated code fields.
    • Inserted div and Rendered PayPal button script for all IDs show the function is processing the scripts as intended.
  • SDK Issue:
    • The Checking SDK for ID X, status: undefined messages suggest the checkSDK interval is running before the preloaded SDK is fully loaded or available in the global scope.
    • The PayPal SDK available message appears later for all IDs, but it seems the button rendering isn’t triggered after the SDK becomes available, likely due to the timing of the script execution and the interval check.
  • Rendering Failure:
    • Despite the SDK eventually loading (as confirmed by PayPal SDK available), the buttons aren’t visible. This could be because the paypal.Buttons initialization isn’t properly tied to the SDK’s availability, or the script execution is happening too early.

Root Cause

  • Timing Mismatch:
    • The checkSDK interval checks typeof paypal after the script is appended, but it doesn’t actively wait for the SDK to load before executing the render call. The preloaded SDK (async="false") should load synchronously, but there might be a delay or browser-specific behavior preventing immediate availability.
  • Script Execution Order:
    • Appending the <script> element with textContent and relying on an interval to check the SDK might not guarantee the paypal.Buttons function is called at the right time, especially if the SDK initialization is asynchronous despite async="false".

Fix Strategy

  1. Synchronize SDK Load:
    • Move the SDK check into a function that waits for the SDK to be available before executing the button script, ensuring proper initialization.
  2. Adjust renderEmbed:
    • Use a promise or callback to delay the paypal.Buttons rendering until the SDK is confirmed loaded.
  3. Version Update:
    • Increment to V2.30 to reflect this change.

Updated pull-contents.php (V2.30, 638 Lines)

<?php
// pull-contents.php (Version 2.30)

// Debug toggle: Set to true to enable debug messages, false to disable
define('DEBUG', true);

// Define base path for images
define('IMAGE_BASE_PATH', 'https://admin.nlt-chaplaincy.org/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 from calling file if $_GET['page'] not set
if (isset($_GET['page'])) {
    $current_page = $_GET['page'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Page set from GET: '$current_page'</div>";
    }
} else {
    $calling_file = basename($_SERVER['SCRIPT_FILENAME']);
    $request_uri = $_SERVER['REQUEST_URI'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Calling file: '$calling_file'</div>";
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: REQUEST_URI: '$request_uri'</div>";
    }
    $current_page = str_replace('.php', '', $calling_file);
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page after str_replace: '$current_page'</div>";
    }
    if ($current_page === 'index') {
        $current_page = 'landing';
        if (DEBUG) {
            echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page set to 'landing'</div>";
        }
        if (preg_match('/\/(task-map)\.php/', $request_uri, $matches)) {
            $current_page = $matches[1];
            if (DEBUG) {
                echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page updated from REQUEST_URI: '$current_page'</div>";
            }
        }
    }
}
$status = 'Show';

// Preload PayPal SDK to support all buttons
$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\" onload=\"console.log('PayPal SDK preloaded')\" onerror=\"console.error('PayPal SDK preload error:', event)\"></script>";

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Final current_page: '$current_page'</div>";
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Image Base Path: '" . IMAGE_BASE_PATH . "'</div>";
}

// Determine if we're on the stories page
$is_stories_page = ($current_page === 'stories');

// Check if the request is for modal content
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Is modal request: " . ($is_modal_request ? 'Yes' : 'No') . "</div>";
}

$sql = "SELECT id, title, content, code, code_type, 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);
if (!$stmt) {
    die("Prepare failed: " . $conn->connect_error);
}

$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();

$sections = [];
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $sections[] = $row;
    }
}

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Fetched " . count($sections) . " sections for page '$current_page'</div>";
}

// Group sections by position
$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);
    $current_index = 0;

    foreach ($sections_by_position as $position => $group) {
        $current_index++;
        $use_default_divider = true;
        $has_background_image = false;
        foreach ($group as $row) {
            if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                $has_background_image = true;
                $use_default_divider = false;
                break;
            }
        }
        if (isset($group[0]['chk_divider'])) {
            $use_default_divider = $group[0]['chk_divider'] == 1;
        }

        if ($is_modal_request) {
            foreach ($group as $index => $row) {
                if (DEBUG) {
                    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Rendering modal section ID: {$row['id']} at position: $position</div>";
                }
                echo "<div class=\"modal-story-content\">";
                if (!empty($row['title'])) {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                }
                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>";
                } elseif ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                }
                if (!empty($row['content'])) {
                    echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                }
                if (DEBUG) {
                    echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                }
                echo "</div>";
                if ($index < count($group) - 1 || $current_index < count($sections_by_position)) {
                    echo "<div class=\"modal-section-separator\"></div>";
                }
            }
        } else {
            if (count($group) > 1) {
                $column_count = min(count($group), 3);
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                echo "<section class=\"section-hero section-hero--full layout-$column_count-column$extra_class\">";
                echo "<div class=\"section-hero__content\">";
                foreach ($group as $index => $row) {
                    echo "<div class=\"column column-$index\">";
                    if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                        $background_image = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                        echo "<div class=\"background-image-wrapper\" style=\"background-image: url('$background_image');\"></div>";
                    }
                    if ($current_page === 'faq') {
                        echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                    } else {
                        echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        echo "<div class=\"generic-content align-center\">";
                        if ($is_stories_page) {
                            $content = strip_tags($row['content']);
                            $preview = mb_substr($content, 0, 60, 'UTF-8');
                            if (mb_strlen($content, 'UTF-8') > 60) {
                                $preview .= '...';
                            }
                            echo htmlspecialchars($preview);
                        } else {
                            echo $row['content'];
                        }
                        echo "</div>";
                    }
                    if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                        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: '{$row['code_type']}'});</script>";
                        if (DEBUG) {
                            echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>";
                        }
                    }
                    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>";
                    }
                    if (!empty($row['button_text'])) {
                        $button_text = htmlspecialchars($row['button_text']);
                        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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                            }
                            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>";
                            } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                                echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                            }
                            if (!empty($row['content'])) {
                                echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                            }
                            if (DEBUG) {
                                echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                            }
                            echo '</div>';
                            echo "</div>";
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                            if (DEBUG) {
                                echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                            }
                        } else {
                            $button_link = htmlspecialchars($row['button_link'] ?: '');
                            if (!empty($button_link) && $row['button_link'] !== '#') {
                                echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                            } else {
                                echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                            }
                        }
                        echo "</div>";
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</section>";
            } else {
                $row = $group[0];
                $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
                $background_style = $has_background_image ? " style=\"background-image: url('" . IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']) . "');\"" : '';
                echo "<div class=\"section-hero section-hero--full$extra_class\"$background_style>";
                echo "<div class=\"section-hero__content\">";
                if ($current_page === 'faq') {
                    echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                } else {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center\">";
                    if ($is_stories_page) {
                        $content = strip_tags($row['content']);
                        $preview = mb_substr($content, 0, 60, 'UTF-8');
                        if (mb_strlen($content, 'UTF-8') > 60) {
                            $preview .= '...';
                        }
                        echo htmlspecialchars($preview);
                    } else {
                        echo $row['content'];
                    }
                    echo "</div>";
                }
                if ($row['model'] === 'title-text-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    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: '{$row['code_type']}'});</script>";
                    if (DEBUG) {
                        echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>";
                    }
                }
                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>";
                }
                if (!empty($row['button_text'])) {
                    $button_text = htmlspecialchars($row['button_text']);
                    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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        }
                        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>";
                        } elseif ($row['model'] === 'title-text-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                            echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                        }
                        if (!empty($row['content'])) {
                            echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                        }
                        if (DEBUG) {
                            echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                        }
                        echo '</div>';
                        echo "</div>";
                        echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                        if (DEBUG) {
                            echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                        }
                    } else {
                        $button_link = htmlspecialchars($row['button_link'] ?: '');
                        if (!empty($button_link) && $row['button_link'] !== '#') {
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                        } else {
                            echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                        }
                    }
                    echo "</div>";
                }
                echo "</div>";
                echo "</div>";
            }
        }

        if ($current_index < count($sections_by_position) && !$is_modal_request) {
            if ($use_default_divider) {
                echo "<div class=\"separator\"><img src=\"https://admin.nlt-chaplaincy.org/public/down_arrow.png\" alt=\"Default Separator\" class=\"separator-image\"></div>";
            } elseif (!empty($row['theme_img']) && $row['img_role'] === 'Separator') {
                $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                echo "<div class=\"separator\"><img src=\"$image_url\" alt=\"Custom Separator\" class=\"separator-image\"></div>";
            }
        }

        if (DEBUG) {
            echo "<div style=\"color: green; font-weight: bold; text-align: center;\">Debug: Section with position $position rendered successfully with " . count($group) . " items.</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>';
}

$stmt->close();
$conn->close();
?>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Synchronize with PHP DEBUG setting
    const DEBUG = <?php echo DEBUG ? 'true' : 'false'; ?>;

    // Debug log setup
    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}`, { color });
    }

    if (DEBUG) logDebug('Modal script loaded');

    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');

    // Function to render embed blocks with synchronized SDK
    function renderEmbed(item) {
        const container = document.getElementById(`embed-${item.id}`);
        if (!container) {
            if (DEBUG) logDebug(`Container not found for embed ID ${item.id}`, 'red');
            return;
        }

        // Extract components with flexible matching
        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 (DEBUG) {
            logDebug(`Div match for ID ${item.id}: ${divMatch ? 'Success' : 'Failed'}`, divMatch ? 'green' : 'red');
            logDebug(`Button match for ID ${item.id}: ${buttonMatch ? 'Success' : 'Failed'}`, buttonMatch ? 'green' : 'red');
        }

        if (item.type === 'PayPal' && divMatch && buttonMatch) {
            // Insert div with unique ID
            const uniqueId = `paypal-button-container-${item.id}`;
            let divContent = divMatch[0].replace(/id="[^"]+"/, `id="${uniqueId}"`);
            container.innerHTML = divContent;
            if (DEBUG) logDebug(`Inserted div for PayPal ID ${item.id}`, 'orange');

            // Wait for SDK to be available
            function waitForSDK(callback) {
                if (typeof paypal !== 'undefined') {
                    if (DEBUG) logDebug(`PayPal SDK available for ID ${item.id}`, 'green');
                    callback();
                } else {
                    if (DEBUG) logDebug(`Checking SDK for ID ${item.id}, status: ${typeof paypal}`, 'yellow');
                    setTimeout(() => waitForSDK(callback), 100);
                }
            }

            // Extract and adjust button script
            let scriptContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
            scriptContent = scriptContent.replace(/\.render\(['"][^'"]+['"]\)/, `.render('#${uniqueId}')`);
            waitForSDK(() => {
                const buttonScript = document.createElement('script');
                buttonScript.textContent = scriptContent;
                document.body.appendChild(buttonScript);
                if (DEBUG) logDebug(`Rendered PayPal button script for ID ${item.id}`, 'orange');
            });
        } else if (item.type === 'YouTube') {
            container.innerHTML = item.code;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Rendered YouTube embed for ID ${item.id}`, 'orange');
        } else {
            container.innerHTML = `<div class="embed-fallback" id="fallback-${item.id}">${item.code}</div>`;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Fallback rendered for ID ${item.id} (parsing failed, scripts not executed)`, 'red');
        }
    }

    // Process embed blocks
    if (window.contentCode && Array.isArray(window.contentCode)) {
        window.contentCode.forEach(item => {
            renderEmbed(item);
        });
    } else if (DEBUG) {
        logDebug('contentCode is not an array or unavailable', 'red');
    }

    function openModal(content) {
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = content;

        const buttons = tempDiv.querySelectorAll('.content__buttons');
        buttons.forEach(button => button.remove());

        const debugMessages = tempDiv.querySelectorAll('div[style*="color: purple"], div[style*="color: blue"], div[style*="color: red"], div[style*="color: green"]');
        debugMessages.forEach(msg => msg.remove());

        let combinedContent = '';
        const sections = tempDiv.querySelectorAll('.modal-story-content');
        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
        if (sections.length > 0) {
            sections.forEach((section, index) => {
                combinedContent += section.outerHTML.trim();
                if (index < sections.length - 1) {
                    combinedContent += '<div class="modal-section-separator"></div>';
                }
            });
        } else {
            const fallbackSections = tempDiv.querySelectorAll('.section-hero__content');
            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
            if (fallbackSections.length > 0) {
                fallbackSections.forEach((section, index) => {
                    const title = section.querySelector('h1');
                    const image = section.querySelector('.content-image');
                    const content = section.querySelector('.generic-content');

                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    if (title) newContent.appendChild(title.cloneNode(true));
                    if (image) newContent.appendChild(image.cloneNode(true));
                    if (content) newContent.appendChild(content.cloneNode(true));

                    combinedContent += newContent.outerHTML.trim();
                    if (index < fallbackSections.length - 1) {
                        combinedContent += '<div class="modal-section-separator"></div>';
                    }
                });
            } else {
                const genericContent = tempDiv.querySelector('.generic-content');
                if (genericContent) {
                    if (DEBUG) logDebug('Falling back to .generic-content');
                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    newContent.appendChild(genericContent.cloneNode(true));
                    combinedContent = newContent.outerHTML.trim();
                } else {
                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                }
            }
        }

        modalBody.innerHTML = combinedContent;
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        if (modalBody.children.length > 0 && closeBtn) {
            closeBtn.focus();
        }
        if (DEBUG) logDebug('Modal opened');
    }

    function closeModal() {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        modalBody.innerHTML = '';
        if (DEBUG && debugLog) {
            debugLog.innerHTML = '';
        }
        if (DEBUG) logDebug('Modal closed');
    }

    if (modal && modalBody && closeBtn && triggers) {
        triggers.forEach(trigger => {
            trigger.addEventListener('click', function(e) {
                e.preventDefault();
                const modalId = this.getAttribute('data-modal-id');
                if (modalId) {
                    const contentDiv = document.getElementById(modalId);
                    if (contentDiv) {
                        const content = contentDiv.innerHTML;
                        if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
                        if (DEBUG) logDebug('Modal content length: ' + content.length + ' characters', 'purple');
                        openModal(content);
                    } else {
                        openModal('<p>Content not found.</p>');
                        if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
                    }
                    return;
                }

                const link = this.getAttribute('data-modal-link');
                if (DEBUG) logDebug('Trigger clicked, link: ' + link);

                if (!link || link === '#' || link.includes('index.php')) {
                    openModal('<p>This link cannot be loaded in a modal to prevent recursive loading.</p>');
                    return;
                }

                let basePath = window.location.origin;
                let absoluteLink;
                try {
                    if (link.startsWith('http://') || link.startsWith('https://')) {
                        absoluteLink = link;
                    } else if (link.startsWith('/')) {
                        absoluteLink = new URL(link, window.location.origin).href;
                    } else {
                        absoluteLink = new URL(link, window.location.origin + '/').href;
                    }
                    absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
                    if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
                } catch (error) {
                    if (DEBUG) logDebug('URL construction error: ' + error.message, 'red');
                    openModal('<p>Invalid URL format.</p>');
                    return;
                }

                openModal('<p>Loading...</p>');

                fetch(absoluteLink, {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                })
                    .then(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(data => {
                        if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(data, 'text/html');

                        let combinedContent = '';
                        const sections = doc.querySelectorAll('.modal-story-content');
                        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections in fetched content');
                        if (sections.length > 0) {
                            sections.forEach((content, index) => {
                                combinedContent += content.outerHTML.trim();
                                if (index < sections.length - 1) {
                                    combinedContent += '<div class="modal-section-separator"></div>';
                                }
                            });
                        } else {
                            const fallbackSections = doc.querySelectorAll('.section-hero__content');
                            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
                            if (fallbackSections.length > 0) {
                                fallbackSections.forEach((section, index) => {
                                    const title = section.querySelector('h1');
                                    const image = section.querySelector('.content-image');
                                    const content = section.querySelector('.generic-content');

                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    if (title) newContent.appendChild(title.cloneNode(true));
                                    if (image) newContent.appendChild(image.cloneNode(true));
                                    if (content) newContent.appendChild(content.cloneNode(true));

                                    combinedContent += newContent.outerHTML.trim();
                                    if (index < fallbackSections.length - 1) {
                                        combinedContent += '<div class="modal-section-separator"></div>';
                                    }
                                });
                            } else {
                                const genericContent = doc.querySelector('.generic-content');
                                if (genericContent) {
                                    if (DEBUG) logDebug('Falling back to .generic-content');
                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    newContent.appendChild(genericContent.cloneNode(true));
                                    combinedContent = newContent.outerHTML.trim();
                                } else {
                                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                                }
                            }
                        }

                        if (combinedContent) {
                            openModal(combinedContent);
                        } else {
                            openModal('<p>No content found.</p>');
                        }
                    })
                    .catch(error => {
                        if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
                        openModal('<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 (modal, modalBody, closeBtn, or triggers)', 'red');
    }
});
</script>

Changes Made

  1. Synchronized SDK Loading:
    • Replaced the checkSDK interval with a waitForSDK recursive function that delays the script execution until typeof paypal is defined, ensuring the button script runs only after the SDK is available.
  2. Version Update:
    • Incremented to Version 2.30.

Testing Instructions

  1. Verify Database:
    • Confirm the code column for IDs 58-61 contains the standardized button codes without the SDK load (as updated previously):
      • ID 58: $200 with plan_id: 'P-3FU48207V4555173AM5STYUA'
      • ID 59: $500 with plan_id: 'P-7G7358239N945925DM5ST2XY'
      • ID 60: $2000 with plan_id: 'P-1N7946829C436443CM5ST37I'
      • ID 61: $10000 with plan_id: 'P-06X02949MG546820SM5ST5QA'
    • Ensure code_type is PayPal for all.
  2. Deploy Updated File:
    • Replace pull-contents.php with the 638-line V2.30 code.
    • Confirm the line count is 638 in your editor.
  3. Regenerate and Test:
    • Regenerate the site with Publii.
    • Load https://www.nlt-chaplaincy.org/index.html and check sections at positions 100-103.
  4. Test PayPal Buttons:
    • Verify that buttons appear for all four sections (IDs 58-61) with the correct amounts ($200, $500, $2000, $10000).
    • Click each button to test the subscription flow (expect an alert with subscriptionID).
    • Open the browser console (F12) for JavaScript errors or SDK load issues.
  5. Debug Output:
    • Review the floating debug log for messages like Button match: Success, Inserted div, Rendered PayPal button script, and PayPal SDK available for all IDs (58-61).
    • Check the console for any errors (e.g., PayPal SDK preload error:).
    • Share the full debug log and console output.

Additional Notes

  • Time: It’s 12:50 PM HKT on Wednesday, September 03, 2025—plenty of time to test today.
  • Fallback Plan: If buttons still don’t appear, we can try loading the SDK dynamically within renderEmbed or check for CSP or network issues in the browser console.
  • Column Issue: We’ll address the column display for multiple sections later as planned.

Please deploy, test, and let me know the results! This adjustment should align the SDK availability with the button rendering.

Original Chat


Q: This version is working! Bravo!

Now, let's check if PP sections are having the same multi-column behaviour as other sections? The core logic is:

  • Desktop view: When two PayPal sections are sharing the same position they will be shown in columns, with a max of three columns. Excessive columns will be just hidden.
  • Mobile view: Just show them one by one, sorted by title alphabetically.

It's almost there! The PP columns are now displayed with the intended multi-column, show/hide and ordering logic. However, in the mobile view, the PP buttons should resize automatically to the single-column full width instead of displaying in multi-column-adapting smaller size like the desktop view.


Since I'm away from my computer, before testing this new version, can you check if the PHP/CSS is handling column width properly? Now, I see when PP sections are displayed in two columns (Two sections sharing the same position) the two sessions are not taking the full width of the content frame. What's expected is when two columns are shown, they should automatically adjust to take 50% of the width, and when three columns are shown, each should take 33% of the width in a dynamic manner. The pastor has also requested to show the image for a PP section so that the team can try uploading graphics that highlight the different amounts. If so, I think we will have to expand the "model" field to include both "title-text-code" and "title-text-image-code" options, and adjust the rendering logic accordingly.


Okay, let's keep the CSS in the PHP for now. V2.33 has perfect display for desktop view but the buttons are overflowing the display area when viewed on mobile. I think we should adjust the width of the buttons for just the mobile view.


V2.34 didn't solve the problem. The PP buttons are still overflowing the display area.


This fix still fails. Why PP buttons were showing correctly before we adjusted the desktop multi-column display? Would the problem be in our earlier fix instead?


Um..this version still fails. If my understanding is right, desktop and mobile views can use a completely different display methods so our fix for desktop should not have any effect on the mobile view. If this is true, can we just reuse the pre-V2.32 display method? Or, is the desktop view fix will somewhat affect the mobile view that we need to change both desktop/mobile views' display methods altogether?


Thank your for this significant fix! I think things are much better in general. Attached are two screenshots that show the remaining issue. The first one is showing the desktop view where the PP buttons are left-aligned and in different sizes (Seems to be affected by the length of the title) , as indicated by the red lines. The expected display should be for ALL buttons to be of the same width and center-aligned. The 2nd one is the mobile view. Similarly, buttons are left-aligned instead of centered. What's good is buttons are of the same widths.

Image description

Image description

·---

A: The current date and time is 12:40 PM HKT on Thursday, September 04, 2025. Thank you for the update and the screenshots! I’m glad the general fix improved things, and I appreciate you pointing out the remaining issues. Let’s address the alignment and sizing problems based on your feedback:

  • Desktop View: The PayPal buttons are left-aligned and have varying widths, seemingly influenced by the title length. The goal is to make all buttons the same width and center-aligned.
  • Mobile View: The buttons are left-aligned but have consistent widths (which is good), and they need to be center-aligned.

Analysis

  • Desktop Issue: The current custom.css (V4.9) uses display: grid for desktop layouts, which respects the natural width of .content-embed and .paypal-button. The varying widths likely stem from the PayPal SDK applying inline styles based on the container or title context. The lack of a fixed width or explicit centering in the Grid setup is causing the left alignment.
  • Mobile Issue: The display: block layout on mobile stacks the .column elements vertically, and the .content-embed and .paypal-button are set to width: 100% !important with max-width: none !important. The left alignment suggests the .content__buttons or .content-embed container isn’t being centered properly.
  • Common Goal: Both views need center alignment, and desktop needs uniform button widths.

Fix Strategy

  1. Desktop:
    • Set a fixed width for .paypal-button (e.g., 300px, a common PayPal default) and use margin: 0 auto to center it within .content-embed.
    • Ensure .content-embed respects this width and centers its content.
  2. Mobile:
    • Add text-align: center to .content__buttons to center the .paypal-button within its container.
    • Keep the width: 100% !important to maintain consistent widths.
  3. Update custom.css:
    • Adjust the existing media queries to include these changes.
    • Increment to V4.10 for tracking.
  4. No Change to pull-contents.php:
    • The PHP logic (V2.40) is working correctly, so we’ll focus on CSS.

Updated custom.css (V4.10)

/* Custom CSS for Lifenavi (navi.claudioflute.live) and Chaplaincy Admin (admin.nlt-chaplaincy.org) - Version 4.10 */
/* Shared styles for both systems, with domain-specific overrides as noted */

/* Ensure myButton overrides theme styles */
.content__buttons a.myButton {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.myButtonBig {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Set default font for the entire site */
body {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Test class to verify font loading */
.font-test {
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    font-size: 24px;
    color: red;
}

/* Dynamic Section Styles */
.content__entry {
    position: relative;
    padding: 20px;
    min-height: 200px;
    margin: 0 auto;
    max-width: 1200px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Hide empty content__entry div from Publii theme */
div.content__entry:empty {
    display: none;
}

/* Alternative: Hide content__entry div on dynamic pages (uncomment and add data-page-type="dynamic" to <body> if needed) */
/* body[data-page-type="dynamic"] div.content__entry {
    display: none;
} */

.section-hero--full {
    width: auto;
    position: relative;
    left: 0;
    transform: none;
    margin: 0 auto;
    max-width: 1200px;
    min-height: 400px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.section-hero--column {
    flex: 1;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    padding: 10px;
}

.section-group {
    display: flex;
    flex-wrap: nowrap; /* Prevent wrapping to new rows */
    gap: 20px;
    max-width: 1200px;
    margin: 0 auto;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.layout-2-column {
    display: flex;
    justify-content: space-between;
}
.layout-2-column .section-hero--column {
    flex: 1 0 49%; /* Slightly less than 50% to account for gap */
    max-width: 49%;
}

.layout-3-column {
    display: flex;
    justify-content: space-between;
}
.layout-3-column .section-hero--column {
    flex: 1 0 32%; /* Slightly less than 33.33% to account for gap */
    max-width: 32%;
}

/* Layout and Button Styling (Moved from pull-contents.php) */
@media (min-width: 768px) {
    .section-hero--full.layout-2-column .section-hero__content {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 20px;
    }
    .section-hero--full.layout-3-column .section-hero__content {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        gap: 20px;
    }
    .column {
        width: 100%; /* Ensure columns take full width in grid */
    }
    .content-embed {
        width: 300px; /* Fixed width for uniformity */
        margin: 0 auto; /* Center the embed container */
        text-align: center; /* Center the button within */
    }
    .paypal-button {
        width: 300px !important; /* Fixed width for all buttons */
        max-width: 300px !important; /* Prevent overflow or shrinkage */
        box-sizing: border-box; /* Include padding in width */
        margin: 0 auto; /* Center the button */
    }
}

@media (max-width: 767px) {
    .section-hero--full .section-hero__content {
        display: block; /* Stack vertically on mobile */
    }
    .column {
        margin-bottom: 20px; /* Add spacing between stacked sections */
        width: 100%; /* Ensure full width on mobile */
    }
    .content-embed {
        width: 100% !important; /* Force full width on mobile, overriding SDK styles */
    }
    .paypal-button {
        width: 100% !important; /* Force PayPal button to full width on mobile */
        max-width: none !important; /* Remove any max-width constraints */
    }
    .content__buttons {
        text-align: center !important; /* Center align buttons on mobile */
    }
}

/* Alignment Classes */
.align-left {
    text-align: left;
}
.content-image.align-left, .video-embed.align-left {
    margin: 1em 0;
}
.align-center {
    text-align: center;
}

/* Override theme's heading font variable */
h1, h2, h3, h4, h5, h6 {
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
    margin-bottom: 1.5em !important;
}

/* Ensure specific heading styles inherit the font */
.content__entry h1 {
    font-size: 2em;
    font-weight: 700;
    line-height: 1.2;
    color: #333;
    margin-bottom: 1.5em !important;
    padding-top: 1em;
    text-align: center;
    text-shadow: none;
    font-family: "LXGW WenKai TC", "Times New Roman", serif !important;
}

.generic-content {
    margin: 0 auto;
    max-width: 800px;
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    margin-bottom: 1em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry[style*="background-image"] h1,
.content__entry[style*="background-image"] .generic-content {
    color: #fff;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.generic-content p {
    margin: 0 0 1em 0;
}

.generic-content ul,
.generic-content ol {
    margin: 0 0 1em 0;
    padding: 0 !important;
    list-style: none !important;
}

.generic-content li {
    margin-bottom: 0.5em;
    text-align: inherit;
    list-style: none !important;
}

.generic-content ul[style*="list-style"],
.generic-content ol[style*="list-style"],
.generic-content li[style*="list-style"] {
    list-style: none !important;
}

.content__buttons {
    text-align: center !important; /* Default center alignment */
    margin: 2em 0 1em 0;
}

.content__buttons a.myButton {
    display: inline-block;
    margin: 0 auto;
}

.content__entry .content__buttons {
    text-align: center !important;
}

.welcome-image {
    max-width: 100%;
    height: auto;
    margin: 1em auto;
    display: block;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
    background: none;
}

.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.separator {
    text-align: center;
    margin: 60px 0 20px 0;
}

.content__entry.content__entry--full:first-child {
    min-height: 0 !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
}

.main.page .main__right:first-child {
    margin-top: -3.8rem !important;
}

.main.page .main__right {
    margin: 0% !important;
}

.banner--after-content {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    outline: none !important;
}

.top__item--right {
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
}

@media (max-width: 768px) {
    .main.page .main__right:first-child {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    .main.page .container:first-child,
    .main.page .post__entry:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .main__right .main__right--bottom:first-child {
        margin-top: -3.8rem !important;
        margin-bottom: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .main.page .container > .main__right > .main__right--bottom:first-child,
    .main.page .post__entry > .main__right > .main__right--bottom:first-child {
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        min-height: 0 !important;
    }
    .banner--after-content {
        margin: 0 !important;
        padding: 0 !important;
        border: none !important;
        outline: none !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

@media (min-width: 769px) and (max-width: 1023px) {
    .main.page .main__right {
        margin: 0 auto !important;
        max-width: 90% !important;
        width: auto !important;
    }
    .main.page .main__right .content__entry--full {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .main.page .main__right .main__right--bottom {
        margin: 0 auto !important;
        max-width: 100% !important;
    }
    .top__item--right {
        border: none !important;
        outline: none !important;
        box-shadow: none !important;
    }
}

.main.page .main__right .main__right--bottom .section-hero--full:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

.content__entry.content__entry--full .separator:first-child {
    margin-top: 0;
}

.section-hero--full:not([style*="background-image"]) + .separator {
    margin-top: 40px;
}

.separator-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

.separator img[src$="down_arrow.png"] {
    max-width: 256px;
}

hr {
    display: none !important;
    visibility: hidden !important;
}

.post__content hr,
.container hr,
.post__content hr[style],
.container hr[style] {
    display: none !important;
    visibility: hidden !important;
}

.post__content::before,
.post__content::after,
.container::before,
.container::after,
.post__entry::before,
.post__entry::after,
.content__entry::before,
.content__entry::after,
.generic-content::before,
.generic-content::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

.separator::before,
.separator::after {
    display: none !important;
    border: none !important;
    content: none !important;
}

body,
body p,
body h2,
body h3,
body h4,
body h5,
body h6,
.post__content,
.post__content p,
.post__content h2,
.post__content h3,
.post__content h4,
.post__content h5,
.post__content h6,
.container,
.container p,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6 {
    color: #333 !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

h1.post__title,
h1.hero__title,
h1.site__title {
    color: #fff !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

a.button,
a.btn,
a.post__button,
.post__content a.button,
.post__content a.btn,
.post__content a.post__button,
.container a.button,
.container a.btn,
.container a.post__button {
    text-decoration: none !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Custom Modal Styles */
.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-content {
    background-color: #fff;
    margin: 5% auto;
    padding: 20px;
    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;
}

.modal-close {
    color: #aaa;
    position: absolute;
    top: 10px;
    right: 20px;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.3s ease;
    z-index: 1001;
}

.modal-close:hover,
.modal-close:focus {
    color: #000;
    text-decoration: none;
}

#modal-body {
    font-size: 1em;
    line-height: 1.6;
    color: #333;
    max-width: 100%;
    box-sizing: border-box;
    padding-top: 40px;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
    overflow-x: hidden;
}

#modal-body h1 {
    font-size: 1.5em;
    margin-bottom: 0.5em;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

#modal-body p {
    margin-bottom: 1em;
}

#modal-body * {
    max-width: 100%;
    height: auto;
    box-sizing: border-box;
}

#modal-body img,
#modal-body div,
#modal-body table {
    max-width: 100%;
    height: auto;
}

#modal-body .welcome-image {
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.modal[aria-hidden="false"] {
    display: block;
}

.modal-close:focus {
    outline: 2px solid #44c767;
    outline-offset: 2px;
}

/* Custom separator for modal sections */
.modal-section-separator {
    text-align: center;
    margin: 20px 0;
    font-size: 24px;
    line-height: 1;
    color: #666;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}
.modal-section-separator::before {
    content: "···";
}

/* Modal-specific content container */
.modal-story-content {
    max-width: 100%;
    margin: 0 auto;
    padding: 20px;
    box-sizing: border-box;
    overflow-x: hidden;
}

.modal-story-content h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
    text-align: center;
}

.modal-story-content .content-image {
    max-width: 100%;
    margin: 1rem 0;
}

.modal-story-content .generic-content {
    font-size: 1rem;
    line-height: 1.6;
}

/* Chaplaincy Admin specific overrides */
.report-container {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry {
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

.content__entry table.report-table {
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    box-sizing: border-box !important;
    font-family: "LXGW WenKai TC", "Times New Roman", serif;
}

/* Ensure background image applies to the section */
.section-hero--full[style*="background-image"] .section-hero__content,
.section-group[style*="background-image"] .section-hero__content {
    background: none;
}

/* Add to custom.css or a Chaplaincy-specific file */
html, body {
    overflow-x: hidden;
    width: 100%;
    margin: 0;
    padding: 0;
}
.main.page, .container, .post__entry {
    width: 100%;
    max-width: 100%;
    overflow-x: hidden;
}

/* PP Button Layout Adjustments */
@media (min-width: 769px) {
    .layout-3-column.position-100 .section-hero--column {
        flex: 1 0 32%;
        max-width: 32%;
    }
}
@media (max-width: 768px) {
    .layout-3-column.position-100 .section-hero--column {
        flex: 1 0 100%;
        max-width: 100%;
    }
}

.section-group.position-100,
.section-hero--full {
    margin-top: 2em;
}

Changes Made

  1. Desktop Fixes:
    • Set .content-embed to width: 300px and margin: 0 auto to center it within the Grid column.
    • Set .paypal-button to width: 300px !important and max-width: 300px !important to enforce uniform width, with margin: 0 auto for centering.
  2. Mobile Fixes:
    • Added text-align: center !important to .content__buttons to center the .paypal-button within its container.
    • Kept width: 100% !important and max-width: none !important for consistent full-width behavior.
  3. Version Update:
    • Incremented custom.css to V4.10.

Testing Instructions

  1. Deploy Updated File:
    • Replace custom.css with the V4.10 content.
    • Confirm the file reflects the updated version (V4.10) in your editor.
  2. Regenerate and Test:
    • Regenerate the site with Publii.
    • Load https://www.nlt-chaplaincy.org/index.html and navigate to position 100.
  3. Test Desktop View:
    • Resize the browser to a desktop width (e.g., 1200px).
    • Verify that all PayPal buttons are the same width (300px) and center-aligned within their columns, regardless of title length.
  4. Test Mobile View:
    • Resize the browser to a mobile width (e.g., 480px).
    • Verify that all PayPal buttons are center-aligned and maintain consistent full-width behavior.
    • Test rotating the device or resizing to ensure alignment holds.
  5. Debug Output:
    • Check the browser console (F12) for CSS or JavaScript errors.
    • Use the developer tools to inspect the .paypal-button computed styles (width and alignment) on both desktop and mobile.
    • Share screenshots of the desktop and mobile views showing the button alignment and sizing.

Additional Notes

  • Time: It’s 12:50 PM HKT on Thursday, September 04, 2025—still time to test today.
  • Width Choice: I used 300px as a fixed width for desktop based on typical PayPal button behavior. If this feels too narrow or wide, let me know, and we can adjust (e.g., 250px or 350px).
  • Alignment: The margin: 0 auto and text-align: center should resolve the alignment issues. If centering still fails, it might be due to the PayPal SDK’s inline styles overriding ours—share the computed styles, and we can add more specific selectors or !important flags.
  • Next Steps: If issues persist, use the browser’s developer tools to check the computed styles of .paypal-button and its parents. Share the output, and we can refine the CSS further.

Please deploy, test, and let me know the results! This should align the buttons correctly and standardize their widths.

Q: With this fix, the desktop view as shown in the first screenshot has been improved with equal-size buttons. However, the entire button area seems to be left-aligned. For the mobile view shown in the 2nd screenshot, the buttons are still not filling up the entire display area. The left-alignment is also not expected.


With this fix, the PP buttons and the ENTIRE content area are moved outside of the right frame (I remember it's called something like main_right in the CSS) and bleeding onto the left frame on desktop view. For mobile view, the buttons are overflowing outside of the display area again as before.


It's still not restored. Since the layout is important, let's revert to the original layout (Button left-aligned but other things were fine) first and you may then review our previous display method where everything were fine except that the buttons are shrunken to see if there are any insights. Or, you may use CSS in PHP temporarily to help figure out the cause of this layout disruption.


There is no change with this version on the layout (Buttons left aligned on both views, shrunken buttons on mobile view) though I see new floating debug message:

Debug: Adjusted PayPal button paypal-button-container-58 width and margin Debug: Adjusted PayPal button paypal-button-container-59 width and margin Debug: Adjusted PayPal button paypal-button-container-60 width and margin Debug: Adjusted PayPal button paypal-button-container-61 width and margin

I wonder would disabling multi-column display once and for all for mobile view can solve this tricky problem?


V2.47 restores all PP buttons to the original size on both views and the buttons are not overflowing in the mobile view. However, multi-column layout is missing on desktop view.


This update has caused complications. The left alignment is not solved for both view. Furthermore, the yellow button's bottom has been cut away, with the button surrounded by a border. The lower black button for credit card is entirely missing. This happens in both desktop and mobile views.


A: The current date and time is 12:55 PM HKT on Friday, September 05, 2025. I’m glad to hear the "14-Report_員工服事(人次)統計" query looks good! Let’s pivot back to resolving the PayPal (PP) button issues. Based on your last update (V2.55), you reported that only the black button for the first button group is rendered, the remaining three sections have cut-away yellow buttons with no black buttons, and the mobile view still shows left-aligned button blocks. Let’s diagnose and fix these issues.

Recap of Issues

  1. Desktop Rendering:

    • Only the black button of the first PP button group renders fully.
    • The remaining three sections show cut-away yellow buttons with no black buttons.
  2. Mobile View:

    • Button blocks remain left-aligned despite the centering attempts.

Analysis

  • Desktop Rendering Issue:

    • The cut-away yellow buttons and missing black buttons suggest the PayPal SDK’s rendering logic might be conflicting with the multiple embeds. The renderEmbed function processes each contentCode item sequentially, but the SDK might not handle multiple paypal.Buttons() instances correctly if their containers overlap or if the setTimeout adjustment isn’t applying to all instances. The first black button rendering fully indicates the initial render works, but subsequent embeds fail, possibly due to timing or ID conflicts.
    • The uniqueId (paypal-button-container-${item.id}) should prevent ID clashes, but the PayPal iframe styling (e.g., .paypal-buttons, iframe) might be overriding our width constraints, causing the cut-away effect.
  • Mobile Left Alignment:

    • The persistent left alignment despite justify-content: center, margin: 0 auto, and transform: translateX(-50%) suggests a theme or parent container (e.g., .main__right, .section-hero__content) is enforcing a left-aligned layout with higher specificity. The debug log’s computed style output should reveal the overriding rule.

Fix Strategy

  • Desktop Rendering:

    • Ensure all PP embeds are rendered independently by adjusting the renderEmbed function to handle multiple instances more robustly, adding a delay between renders if needed.
    • Reinforce the width and border fixes with inline styles post-render to override PayPal’s default iframe behavior.
  • Mobile Centering:

    • Increase CSS specificity by targeting the exact path (e.g., .main__right .section-hero--full .column .content-embed-container) and add a wrapper div with explicit centering.
    • Use the debug log to confirm the computed styles and adjust accordingly.

Updated pull-contents.php (V2.56)

<?php
// pull-contents.php (Version 2.56)

// Debug toggle: Set to true to enable debug messages, false to disable
define('DEBUG', true);

// Define base path for images
define('IMAGE_BASE_PATH', 'https://admin.nlt-chaplaincy.org/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 from calling file if $_GET['page'] not set
if (isset($_GET['page'])) {
    $current_page = $_GET['page'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Page set from GET: '$current_page'</div>";
    }
} else {
    $calling_file = basename($_SERVER['SCRIPT_FILENAME']);
    $request_uri = $_SERVER['REQUEST_URI'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Calling file: '$calling_file'</div>";
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: REQUEST_URI: '$request_uri'</div>";
    }
    $current_page = str_replace('.php', '', $calling_file);
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page after str_replace: '$current_page'</div>";
    }
    if ($current_page === 'index') {
        $current_page = 'landing';
        if (DEBUG) {
            echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page set to 'landing'</div>";
        }
        if (preg_match('/\/(task-map)\.php/', $request_uri, $matches)) {
            $current_page = $matches[1];
            if (DEBUG) {
                echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page updated from REQUEST_URI: '$current_page'</div>";
            }
        }
    }
}
$status = 'Show';

// Preload PayPal SDK to support all buttons
$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\" onload=\"console.log('PayPal SDK preloaded')\" onerror=\"console.error('PayPal SDK preload error:', event)\"></script>";

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Final current_page: '$current_page'</div>";
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Image Base Path: '" . IMAGE_BASE_PATH . "'</div>";
}

// Determine if we're on the stories page
$is_stories_page = ($current_page === 'stories');

// Check if the request is for modal content
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Is modal request: " . ($is_modal_request ? 'Yes' : 'No') . "</div>";
}

$sql = "SELECT id, title, content, code, code_type, 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);
if (!$stmt) {
    die("Prepare failed: " . $conn->connect_error);
}

$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();

$sections = [];
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $sections[] = $row;
    }
}

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Fetched " . count($sections) . " sections for page '$current_page'</div>";
}

// Group sections by position
$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);
    $current_index = 0;

    foreach ($sections_by_position as $position => $group) {
        $current_index++;
        $use_default_divider = true;
        $has_background_image = false;
        $has_paypal = false;
        foreach ($group as $row) {
            if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                $has_background_image = true;
                $use_default_divider = false;
            }
            if ($row['code_type'] === 'PayPal') {
                $has_paypal = true;
            }
        }
        if (isset($group[0]['chk_divider'])) {
            $use_default_divider = $group[0]['chk_divider'] == 1;
        }

        if ($is_modal_request) {
            foreach ($group as $index => $row) {
                if (DEBUG) {
                    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Rendering modal section ID: {$row['id']} at position: $position</div>";
                }
                echo "<div class=\"modal-story-content\">";
                if (!empty($row['title'])) {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                }
                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>";
                } elseif ($row['model'] === 'title-text-code' || $row['model'] === 'title-text-image-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                }
                if (!empty($row['content'])) {
                    echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                }
                if (DEBUG) {
                    echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                }
                echo "</div>";
                if ($index < count($group) - 1 || $current_index < count($sections_by_position)) {
                    echo "<div class=\"modal-section-separator\"></div>";
                }
            }
        } else {
            $column_count = min(count($group), 3);
            if ($has_paypal) {
                $column_count = 1; // Force single-column for sections with PayPal
            }
            $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
            echo "<section class=\"section-hero section-hero--full layout-$column_count-column $extra_class\">";
            echo "<div class=\"section-hero__content\">";
            // Sort by title for mobile view
            usort($group, function($a, $b) {
                return strcmp($a['title'], $b['title']);
            });
            foreach ($group as $index => $row) {
                if ($index >= 3 && !$has_paypal) {
                    continue; // Hide sections beyond the third column on desktop for non-PP
                }
                echo "<div class=\"column column-$index\">";
                if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                    $background_image = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                    echo "<div class=\"background-image-wrapper\" style=\"background-image: url('$background_image');\"></div>";
                }
                if ($current_page === 'faq') {
                    echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                } else {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center\">";
                    if ($is_stories_page) {
                        $content = strip_tags($row['content']);
                        $preview = mb_substr($content, 0, 60, 'UTF-8');
                        if (mb_strlen($content, 'UTF-8') > 60) {
                            $preview .= '...';
                        }
                        echo htmlspecialchars($preview);
                    } else {
                        echo $row['content'];
                    }
                    echo "</div>";
                }
                if ($row['model'] === 'title-text-image-code' || $row['model'] === 'title-text-code') {
                    if (!empty($row['image']) && $row['model'] === 'title-text-image-code') {
                        $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
                        echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
                    }
                    if (!empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                        echo "<div class=\"content-embed-container\">";
                        if ($row['code_type'] === 'PayPal') {
                            echo "<div class=\"content-embed paypal-embed\" id=\"embed-{$row['id']}\"></div>";
                        } else {
                            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: '{$row['code_type']}'});</script>";
                        if (DEBUG) {
                            echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>";
                        }
                        echo "</div>";
                    }
                } elseif (!empty($row['image']) && $row['model'] !== 'title-text-image-code') {
                    $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
                    echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
                }
                if (!empty($row['button_text'])) {
                    $button_text = htmlspecialchars($row['button_text']);
                    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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        }
                        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>";
                        } elseif ($row['model'] === 'title-text-code' || $row['model'] === 'title-text-image-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                            echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                        }
                        if (!empty($row['content'])) {
                            echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                        }
                        if (DEBUG) {
                            echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                        }
                        echo '</div>';
                        echo "</div>";
                        echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                        if (DEBUG) {
                            echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                        }
                    } else {
                        $button_link = htmlspecialchars($row['button_link'] ?: '');
                        if (!empty($button_link) && $row['button_link'] !== '#') {
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                        } else {
                            echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                        }
                    }
                    echo "</div>";
                }
                echo "</div>";
            }
            echo "</div>";
            echo "</section>";
        }

        if ($current_index < count($sections_by_position) && !$is_modal_request) {
            if ($use_default_divider) {
                echo "<div class=\"separator\"><img src=\"https://admin.nlt-chaplaincy.org/public/down_arrow.png\" alt=\"Default Separator\" class=\"separator-image\"></div>";
            } elseif (!empty($row['theme_img']) && $row['img_role'] === 'Separator') {
                $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                echo "<div class=\"separator\"><img src=\"$image_url\" alt=\"Custom Separator\" class=\"separator-image\"></div>";
            }
        }

        if (DEBUG) {
            echo "<div style=\"color: green; font-weight: bold; text-align: center;\">Debug: Section with position $position rendered successfully with " . count($group) . " items.</div>";
        }
    }
} else {
    echo "No sections found.";
}

// Add custom CSS for layout fixes
if ($current_page === 'landing' && strpos($_SERVER['REQUEST_URI'], 'position-100') !== false) {
    echo '<style>';
    echo '.main__right .section-hero--full {
        width: 100% !important;
        max-width: 1200px !important;
        margin: 0 auto !important;
        display: flex !important;
        justify-content: center !important;
        box-sizing: border-box !important;
        overflow: hidden !important;
    }';
    echo '.main__right .section-hero--full .section-hero__content {
        width: 100% !important;
        max-width: 100% !important;
        margin: 0 auto !important;
        padding: 0 !important;
    }';
    echo '.main__right .section-hero--full .column {
        width: 100% !important;
        max-width: 100% !important;
        padding: 0 !important;
        margin: 0 0 20px 0 !important;
        box-sizing: border-box !important;
        text-align: center !important;
        display: flex !important;
        justify-content: center !important;
    }';
    echo '.main__right .section-hero--full .content-embed-container {
        display: block !important;
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        text-align: center !important;
        position: relative !important;
        left: 50% !important;
        transform: translateX(-50%) !important;
    }';
    echo '.main__right .section-hero--full .paypal-embed {
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        text-align: center !important;
        overflow: visible !important;
    }';
    echo '.main__right .section-hero--full .paypal-embed .paypal-buttons,
    .main__right .section-hero--full .paypal-embed iframe {
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        box-sizing: border-box !important;
        border: none !important;
        outline: none !important;
    }';
    echo '.main__right .section-hero--full .content__buttons {
        text-align: center !important;
        width: 100% !important;
        margin: 0 auto !important;
        padding: 0 !important;
    }';
    // Mobile-specific single-column with forced centering
    echo '@media (max-width: 767px) {
        .main__right .section-hero--full .section-hero__content {
            display: block !important;
            width: 100% !important;
            margin: 0 !important;
            padding: 0 !important;
            flex-direction: column !important;
        }
        .main__right .section-hero--full .column {
            width: 100% !important;
            max-width: 100% !important;
            margin-bottom: 20px !important;
            text-align: center !important;
            display: flex !important;
            justify-content: center !important;
            align-items: center !important;
        }
        .main__right .section-hero--full .content-embed-container {
            display: block !important;
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            text-align: center !important;
            position: relative !important;
            left: 50% !important;
            transform: translateX(-50%) !important;
            /* Added wrapper for higher specificity */
            box-sizing: border-box !important;
        }
        .main__right .section-hero--full .paypal-embed,
        .main__right .section-hero--full .paypal-embed .paypal-buttons,
        .main__right .section-hero--full .paypal-embed iframe {
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            overflow: visible !important;
            box-sizing: border-box !important;
        }
        .main__right .section-hero--full.layout-2-column .section-hero__content,
        .main__right .section-hero--full.layout-3-column .section-hero__content {
            flex-direction: column !important;
        }
    }';
    // Desktop single-column for PayPal, multi-column for others
    echo '@media (min-width: 1024px) {
        .main__right .section-hero--full.layout-1-column .section-hero__content {
            display: block !important;
        }
        .main__right .section-hero--full.layout-2-column .section-hero__content,
        .main__right .section-hero--full.layout-3-column .section-hero__content {
            display: grid !important;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important;
            gap: 20px !important;
            justify-content: center !important;
            align-items: center !important;
        }
        .main__right .section-hero--full .column {
            width: auto !important;
            max-width: 100% !important;
            text-align: center !important;
        }
        .main__right .section-hero--full .content-embed-container,
        .main__right .section-hero--full .paypal-embed,
        .main__right .section-hero--full .paypal-embed .paypal-buttons,
        .main__right .section-hero--full .paypal-embed iframe {
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            overflow: visible !important;
        }
    }';
    echo '</style>';
}

// 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>';
}

$stmt->close();
$conn->close();
?>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Synchronize with PHP DEBUG setting
    const DEBUG = <?php echo DEBUG ? 'true' : 'false'; ?>;

    // Debug log setup
    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}`, { color });
    }

    if (DEBUG) logDebug('Modal script loaded');

    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');

    // Function to render embed blocks with synchronized SDK and sequential rendering
    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;
        }

        // Extract components with flexible matching
        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 (DEBUG) {
            logDebug(`Div match for ID ${item.id}: ${divMatch ? 'Success' : 'Failed'}`, divMatch ? 'green' : 'red');
            logDebug(`Button match for ID ${item.id}: ${buttonMatch ? 'Success' : 'Failed'}`, buttonMatch ? 'green' : 'red');
        }

        if (item.type === 'PayPal' && divMatch && buttonMatch) {
            // Insert div with unique ID
            const uniqueId = `paypal-button-container-${item.id}`;
            let divContent = divMatch[0].replace(/id="[^"]+"/, `id="${uniqueId}"`);
            container.innerHTML = divContent;
            if (DEBUG) logDebug(`Inserted div for PayPal ID ${item.id}`, 'orange');

            // Wait for SDK to be available
            function waitForSDK(callback) {
                if (typeof paypal !== 'undefined') {
                    if (DEBUG) logDebug(`PayPal SDK available for ID ${item.id}`, 'green');
                    callback();
                } else {
                    if (DEBUG) logDebug(`Checking SDK for ID ${item.id}, status: ${typeof paypal}`, 'yellow');
                    setTimeout(() => waitForSDK(callback), 100);
                }
            }

            // Extract and adjust button script
            let scriptContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
            scriptContent = scriptContent.replace(/\.render\(['"][^'"]+['"]\)/, `.render('#${uniqueId}')`);
            waitForSDK(() => {
                const buttonScript = document.createElement('script');
                buttonScript.textContent = scriptContent;
                document.body.appendChild(buttonScript);
                if (DEBUG) logDebug(`Rendered PayPal button script for ID ${item.id}`, 'orange');

                // Sequential adjustment with delay based on index
                setTimeout(() => {
                    const paypalButtons = document.querySelectorAll(`#${uniqueId} .paypal-buttons`);
                    const paypalIframes = document.querySelectorAll(`#${uniqueId} iframe`);
                    paypalButtons.forEach((button, btnIndex) => {
                        const iframe = paypalIframes[btnIndex];
                        if (button && iframe) {
                            // Apply inline styles to override PayPal defaults
                            button.style.cssText = 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important; border: none !important; outline: none !important; display: block !important;';
                            iframe.style.cssText = 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important; border: none !important; outline: none !important; display: block !important;';
                            if (DEBUG) {
                                const style = window.getComputedStyle(button);
                                logDebug(`Computed style for button ${uniqueId} (index ${btnIndex}): width=${style.width}, margin=${style.margin}, justify-content=${style.justifyContent}`, 'purple');
                            }
                        }
                    });
                }, index * 500); // Staggered delay (500ms per instance)
            });
        } else if (item.type === 'YouTube') {
            container.innerHTML = item.code;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Rendered YouTube embed for ID ${item.id}`, 'orange');
        } else {
            container.innerHTML = `<div class="embed-fallback" id="fallback-${item.id}">${item.code}</div>`;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Fallback rendered for ID ${item.id} (parsing failed, scripts not executed)`, 'red');
        }
    }

    // Process embed blocks with sequential rendering
    if (window.contentCode && Array.isArray(window.contentCode)) {
        window.contentCode.forEach((item, index) => {
            renderEmbed(item, index, window.contentCode.length);
        });
    } else if (DEBUG) {
        logDebug('contentCode is not an array or unavailable', 'red');
    }

    // Desktop centering fallback
    function adjustDesktopLayout() {
        if (window.innerWidth >= 1024) {
            const embedContainers = document.querySelectorAll('.main__right .section-hero--full .content-embed-container');
            embedContainers.forEach(container => {
                container.style.margin = '0 auto !important';
                if (DEBUG) logDebug(`Adjusted desktop container centering for ${container.querySelector('.paypal-embed')?.id}`, 'blue');
            });
        }
    }
    window.addEventListener('load', adjustDesktopLayout);
    window.addEventListener('resize', adjustDesktopLayout);

    function openModal(content) {
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = content;

        const buttons = tempDiv.querySelectorAll('.content__buttons');
        buttons.forEach(button => button.remove());

        const debugMessages = tempDiv.querySelectorAll('div[style*="color: purple"], div[style*="color: blue"], div[style*="color: red"], div[style*="color: green"]');
        debugMessages.forEach(msg => msg.remove());

        let combinedContent = '';
        const sections = tempDiv.querySelectorAll('.modal-story-content');
        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
        if (sections.length > 0) {
            sections.forEach((section, index) => {
                combinedContent += section.outerHTML.trim();
                if (index < sections.length - 1) {
                    combinedContent += '<div class="modal-section-separator"></div>';
                }
            });
        } else {
            const fallbackSections = tempDiv.querySelectorAll('.section-hero__content');
            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
            if (fallbackSections.length > 0) {
                fallbackSections.forEach((section, index) => {
                    const title = section.querySelector('h1');
                    const image = section.querySelector('.content-image');
                    const content = section.querySelector('.generic-content');

                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    if (title) newContent.appendChild(title.cloneNode(true));
                    if (image) newContent.appendChild(image.cloneNode(true));
                    if (content) newContent.appendChild(content.cloneNode(true));

                    combinedContent += newContent.outerHTML.trim();
                    if (index < fallbackSections.length - 1) {
                        combinedContent += '<div class="modal-section-separator"></div>';
                    }
                });
            } else {
                const genericContent = tempDiv.querySelector('.generic-content');
                if (genericContent) {
                    if (DEBUG) logDebug('Falling back to .generic-content');
                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    newContent.appendChild(genericContent.cloneNode(true));
                    combinedContent = newContent.outerHTML.trim();
                } else {
                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                }
            }
        }

        modalBody.innerHTML = combinedContent;
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        if (modalBody.children.length > 0 && closeBtn) {
            closeBtn.focus();
        }
        if (DEBUG) logDebug('Modal opened');
    }

    function closeModal() {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        modalBody.innerHTML = '';
        if (DEBUG && debugLog) {
            debugLog.innerHTML = '';
        }
        if (DEBUG) logDebug('Modal closed');
    }

    if (modal && modalBody && closeBtn && triggers) {
        triggers.forEach(trigger => {
            trigger.addEventListener('click', function(e) {
                e.preventDefault();
                const modalId = this.getAttribute('data-modal-id');
                if (modalId) {
                    const contentDiv = document.getElementById(modalId);
                    if (contentDiv) {
                        const content = contentDiv.innerHTML;
                        if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
                        if (DEBUG) logDebug('Modal content length: ' + content.length + ' characters', 'purple');
                        openModal(content);
                    } else {
                        openModal('<p>Content not found.</p>');
                        if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
                    }
                    return;
                }

                const link = this.getAttribute('data-modal-link');
                if (DEBUG) logDebug('Trigger clicked, link: ' + link);

                if (!link || link === '#' || link.includes('index.php')) {
                    openModal('<p>This link cannot be loaded in a modal to prevent recursive loading.</p>');
                    return;
                }

                let basePath = window.location.origin;
                let absoluteLink;
                try {
                    if (link.startsWith('http://') || link.startsWith('https://')) {
                        absoluteLink = link;
                    } else if (link.startsWith('/')) {
                        absoluteLink = new URL(link, window.location.origin).href;
                    } else {
                        absoluteLink = new URL(link, window.location.origin + '/').href;
                    }
                    absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
                    if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
                } catch (error) {
                    if (DEBUG) logDebug('URL construction error: ' + error.message, 'red');
                    openModal('<p>Invalid URL format.</p>');
                    return;
                }

                openModal('<p>Loading...</p>');

                fetch(absoluteLink, {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                })
                    .then(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(data => {
                        if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(data, 'text/html');

                        let combinedContent = '';
                        const sections = doc.querySelectorAll('.modal-story-content');
                        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections in fetched content');
                        if (sections.length > 0) {
                            sections.forEach((content, index) => {
                                combinedContent += content.outerHTML.trim();
                                if (index < sections.length - 1) {
                                    combinedContent += '<div class="modal-section-separator"></div>';
                                }
                            });
                        } else {
                            const fallbackSections = doc.querySelectorAll('.section-hero__content');
                            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
                            if (fallbackSections.length > 0) {
                                fallbackSections.forEach((section, index) => {
                                    const title = section.querySelector('h1');
                                    const image = section.querySelector('.content-image');
                                    const content = section.querySelector('.generic-content');

                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    if (title) newContent.appendChild(title.cloneNode(true));
                                    if (image) newContent.appendChild(image.cloneNode(true));
                                    if (content) newContent.appendChild(content.cloneNode(true));

                                    combinedContent += newContent.outerHTML.trim();
                                    if (index < fallbackSections.length - 1) {
                                        combinedContent += '<div class="modal-section-separator"></div>';
                                    }
                                });
                            } else {
                                const genericContent = doc.querySelector('.generic-content');
                                if (genericContent) {
                                    if (DEBUG) logDebug('Falling back to .generic-content');
                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    newContent.appendChild(genericContent.cloneNode(true));
                                    combinedContent = newContent.outerHTML.trim();
                                } else {
                                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                                }
                            }
                        }

                        if (combinedContent) {
                            openModal(combinedContent);
                        } else {
                            openModal('<p>No content found.</p>');
                        }
                    })
                    .catch(error => {
                        if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
                        openModal('<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 (modal, modalBody, closeBtn, or triggers)', 'red');
    }
});
</script>

Changes Made

  1. JavaScript (renderEmbed):

    • Added index and total parameters to renderEmbed to enable sequential rendering.
    • Introduced a staggered setTimeout delay (index * 500ms) to allow each PP embed to render independently, preventing conflicts between multiple paypal.Buttons() instances.
    • Enhanced inline styles with display: block !important to ensure proper layout of buttons and iframes, addressing the cut-away issue.
  2. CSS:

    • Added box-sizing: border-box !important to .content-embed-container on mobile to prevent width miscalculations.
    • Maintained all centering properties but increased specificity with the full path; however, the core issue seems tied to rendering, so the focus shifted to JavaScript.
  3. Version Update: Incremented to V2.56 in the file header comment.

Testing Instructions

  1. Update pull-contents.php:

    • Replace the existing pull-contents.php with the V2.56 content above.
    • Regenerate the site with Publii.
  2. Test Desktop View:

    • Resize to 1920px width.
    • Navigate to https://www.nlt-chaplaincy.org/index.html with position-100.
    • Verify that all PP sections (first black button group and the three with yellow buttons) render both black and yellow buttons fully, with no cut-away edges and no borders.
    • Check that all four sections display correctly, with buttons at 300px wide.
  3. Test Mobile View:

    • Resize to a mobile width (e.g., 480px).
    • Test with a single PP section at a unique position to confirm a 300px single column with both buttons visible and no shrinking.
    • Test with multiple PP sections (e.g., position-100) to ensure they stack vertically and are centered.
    • Note the left alignment status.
  4. Inspect Elements:

    • On desktop, inspect each PP button area to confirm the purple area (#zoid-paypal-buttons-...) and its iframe have a computed width of 300px, no borders, and no cut-off.
    • On mobile, check the computed styles of .column, .content-embed-container, and .paypal-embed for justify-content, margin, and transform properties.
    • Verify the debug log shows computed styles for each button instance.
  5. Debug Output:

    • Ensure the floating debug log appears with messages (e.g., “Rendered PayPal button script for ID [id]”, “Computed style for button [id]…”).
    • Check the browser console (F12) for PayPal SDK or JavaScript errors.
    • If issues persist, share the debug log output, computed styles, and screenshots.

Additional Notes

  • Time: It’s 1:00 PM HKT on Friday, September 05, 2025—still a good time to test.
  • Rationale: The staggered rendering should resolve the cut-away and missing button issues by giving each embed time to initialize. The mobile centering fix relies on the current CSS, but if it fails, the debug data will guide further adjustments (e.g., targeting a parent override).
  • Next Steps: If all buttons render correctly on desktop and mobile centering is achieved, we’re set. If not, the debug output will help pinpoint the issue (e.g., SDK conflict or CSS override).

Please update pull-contents.php to V2.56, test thoroughly, and let me know the results! This should address the rendering and alignment problems.


Q: Things are going better with this version. On BOTH desktop view and mobile view, the buttons are finally centered and shown in full size. However, on both views, the black buttons are only displayed for section A and C but mission in B and D. Please also check if the floating debug msg layer would affect PP display. We can disable debug mode should there be interference.


But why there was no issue when all PP sections are having different positions? The PP SDK shouldn't "know" whether I'm using exclusive or same position as the rendering will still be single column?


A: The current date and time is 2:10 PM HKT on Friday, September 05, 2025. That’s a great observation, and it helps narrow down the issue. You’re absolutely right that the PayPal (PP) SDK shouldn’t inherently “know” about position differences or enforce single-column rendering—its behavior should be agnostic to layout as long as the container and script are correctly set up. The fact that there were no issues when all PP sections had different positions suggests the problem is tied to how multiple PP embeds are handled when they share the same position (or are rendered in close proximity), rather than the SDK’s awareness of layout. Let’s explore why this happens and fix it.

Analysis of the Issue

  • Previous Success with Different Positions:

    • When each PP section had a unique position (e.g., 1, 2, 3, 4), the renders worked because the renderEmbed function processed each instance sequentially with its own container (embed-${item.id}). The SDK likely initialized each paypal.Buttons() instance independently without interference.
  • Current Failure with Same Position:

    • When all PP sections share the same position (e.g., all at position 100), the PHP logic groups them into the same $group array within sections_by_position. This means renderEmbed is called multiple times in quick succession for the same position, potentially overwhelming the SDK or causing conflicts:
    • SDK Caching: The PayPal SDK might cache the first successful render (e.g., section A) and reuse it, failing to create new instances for B, C, and D.
    • Timing Conflict: The staggered setTimeout (1000ms per instance) might not be enough to prevent overlap, especially if the SDK queues renders and skips later ones.
    • Container Conflict: If the containers (embed-${item.id}) are rendered in the same DOM context without proper isolation, the SDK might overwrite or fail to distinguish them.
  • Single-Column Rendering:

    • The forced single-column layout (layout-1-column) ensures each PP embed has its own 300px-wide container, so the issue isn’t layout-related. The problem lies in the SDK’s handling of multiple paypal.Buttons() calls on the same page.

Why This Happens

The PayPal SDK is designed to render buttons within a single page but expects unique configurations and containers. When multiple embeds share the same position, the rapid succession of renderEmbed calls might:

  • Cause the SDK to treat subsequent renders as duplicates, rendering only the first (A) fully.
  • Fail to apply the black button configuration if the script content (stored in code) varies slightly or is incomplete for later sections.
  • Encounter a race condition where the setTimeout delay doesn’t allow the SDK to complete one render before starting the next.

Fix Strategy

  • Isolate Each Render:
    • Ensure each paypal.Buttons() instance is fully independent by resetting the SDK context or using a unique namespace per render.
  • Dynamic Delay:
    • Increase the delay dynamically based on the number of PP instances to prevent overlap.
  • Verify Script Content:
    • Log and validate that the buttonMatch content includes both yellow and black button configurations for all sections.

Updated pull-contents.php (V2.61)

<?php
// pull-contents.php (Version 2.61)

// Debug toggle: Set to true to enable debug messages, false to disable
define('DEBUG', true);

// Define base path for images
define('IMAGE_BASE_PATH', 'https://admin.nlt-chaplaincy.org/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 from calling file if $_GET['page'] not set
if (isset($_GET['page'])) {
    $current_page = $_GET['page'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Page set from GET: '$current_page'</div>";
    }
} else {
    $calling_file = basename($_SERVER['SCRIPT_FILENAME']);
    $request_uri = $_SERVER['REQUEST_URI'];
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Calling file: '$calling_file'</div>";
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: REQUEST_URI: '$request_uri'</div>";
    }
    $current_page = str_replace('.php', '', $calling_file);
    if (DEBUG) {
        echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page after str_replace: '$current_page'</div>";
    }
    if ($current_page === 'index') {
        $current_page = 'landing';
        if (DEBUG) {
            echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page set to 'landing'</div>";
        }
        if (preg_match('/\/(task-map)\.php/', $request_uri, $matches)) {
            $current_page = $matches[1];
            if (DEBUG) {
                echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Current page updated from REQUEST_URI: '$current_page'</div>";
            }
        }
    }
}
$status = 'Show';

// Preload PayPal SDK to support all buttons
$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\" onload=\"console.log('PayPal SDK preloaded')\" onerror=\"console.error('PayPal SDK preload error:', event)\"></script>";

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Final current_page: '$current_page'</div>";
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Image Base Path: '" . IMAGE_BASE_PATH . "'</div>";
}

// Determine if we're on the stories page
$is_stories_page = ($current_page === 'stories');

// Check if the request is for modal content
$is_modal_request = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') || isset($_GET['modal']);

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Is modal request: " . ($is_modal_request ? 'Yes' : 'No') . "</div>";
}

$sql = "SELECT id, title, content, code, code_type, 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);
if (!$stmt) {
    die("Prepare failed: " . $conn->connect_error);
}

$stmt->bind_param("ss", $current_page, $status);
$stmt->execute();
$result = $stmt->get_result();

$sections = [];
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $sections[] = $row;
    }
}

if (DEBUG) {
    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Fetched " . count($sections) . " sections for page '$current_page'</div>";
}

// Group sections by position
$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);
    $current_index = 0;

    foreach ($sections_by_position as $position => $group) {
        $current_index++;
        $use_default_divider = true;
        $has_background_image = false;
        $has_paypal = false;
        foreach ($group as $row) {
            if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                $has_background_image = true;
                $use_default_divider = false;
            }
            if ($row['code_type'] === 'PayPal') {
                $has_paypal = true;
            }
        }
        if (isset($group[0]['chk_divider'])) {
            $use_default_divider = $group[0]['chk_divider'] == 1;
        }

        if ($is_modal_request) {
            foreach ($group as $index => $row) {
                if (DEBUG) {
                    echo "<div style='color: blue; font-weight: bold; text-align: center;'>Debug: Rendering modal section ID: {$row['id']} at position: $position</div>";
                }
                echo "<div class=\"modal-story-content\">";
                if (!empty($row['title'])) {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                }
                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>";
                } elseif ($row['model'] === 'title-text-code' || $row['model'] === 'title-text-image-code' && !empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                    echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                }
                if (!empty($row['content'])) {
                    echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                }
                if (DEBUG) {
                    echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                }
                echo "</div>";
                if ($index < count($group) - 1 || $current_index < count($sections_by_position)) {
                    echo "<div class=\"modal-section-separator\"></div>";
                }
            }
        } else {
            $column_count = min(count($group), 3);
            if ($has_paypal) {
                $column_count = 1; // Force single-column for sections with PayPal
            }
            $extra_class = ($current_page === 'task-map') ? ' task-map-section' : '';
            echo "<section class=\"section-hero section-hero--full layout-$column_count-column $extra_class\">";
            echo "<div class=\"section-hero__content\">";
            // Sort by title for mobile view
            usort($group, function($a, $b) {
                return strcmp($a['title'], $b['title']);
            });
            foreach ($group as $index => $row) {
                if ($index >= 3 && !$has_paypal) {
                    continue; // Hide sections beyond the third column on desktop for non-PP
                }
                echo "<div class=\"column column-$index\">";
                if (!empty($row['theme_img']) && $row['img_role'] === 'Background') {
                    $background_image = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                    echo "<div class=\"background-image-wrapper\" style=\"background-image: url('$background_image');\"></div>";
                }
                if ($current_page === 'faq') {
                    echo "<h1 class=\"align-center faq-question\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center faq-answer\">" . $row['content'] . "</div>";
                } else {
                    echo "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                    echo "<div class=\"generic-content align-center\">";
                    if ($is_stories_page) {
                        $content = strip_tags($row['content']);
                        $preview = mb_substr($content, 0, 60, 'UTF-8');
                        if (mb_strlen($content, 'UTF-8') > 60) {
                            $preview .= '...';
                        }
                        echo htmlspecialchars($preview);
                    } else {
                        echo $row['content'];
                    }
                    echo "</div>";
                }
                if ($row['model'] === 'title-text-image-code' || $row['model'] === 'title-text-code') {
                    if (!empty($row['image']) && $row['model'] === 'title-text-image-code') {
                        $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
                        echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
                    }
                    if (!empty($row['code']) && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                        echo "<div class=\"content-embed-container\">";
                        if ($row['code_type'] === 'PayPal') {
                            echo "<div class=\"content-embed paypal-embed\" id=\"embed-{$row['id']}\"></div>";
                        } else {
                            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: '{$row['code_type']}'});</script>";
                        if (DEBUG) {
                            echo "<div style='color: orange; display: none;'>Debug: Embed code for ID ${row['id']} (${row['code_type']}): ${row['code']}</div>";
                        }
                        echo "</div>";
                    }
                } elseif (!empty($row['image']) && $row['model'] !== 'title-text-image-code') {
                    $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['image']);
                    echo "<div class=\"content-image\"><img src=\"$image_url\" alt=\"Section Image\" class=\"welcome-image\"></div>";
                }
                if (!empty($row['button_text'])) {
                    $button_text = htmlspecialchars($row['button_text']);
                    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 "<h1 class=\"align-center\">" . $row['title'] . "</h1>";
                        }
                        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>";
                        } elseif ($row['model'] === 'title-text-code' || $row['model'] === 'title-text-image-code' && ($row['code_type'] === 'YouTube' || $row['code_type'] === 'PayPal')) {
                            echo "<div class=\"content-embed\" id=\"embed-{$row['id']}\"></div>";
                        }
                        if (!empty($row['content'])) {
                            echo "<div class=\"generic-content align-center\">" . $row['content'] . "</div>";
                        }
                        if (DEBUG) {
                            echo "<div style='color: purple; font-weight: bold; text-align: center;'>Debug: Modal content length: " . strlen($row['content']) . " characters</div>";
                        }
                        echo '</div>';
                        echo "</div>";
                        echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-id=\"$modal_id\">$button_text</a>";
                        if (DEBUG) {
                            echo "<div style='color: orange; font-weight: bold; text-align: center;'>Debug: Modal trigger rendered for ID: $modal_id with button text: $button_text</div>";
                        }
                    } else {
                        $button_link = htmlspecialchars($row['button_link'] ?: '');
                        if (!empty($button_link) && $row['button_link'] !== '#') {
                            echo "<a class=\"myButton modal-trigger\" href=\"#\" data-modal-link=\"$button_link\">$button_text</a>";
                        } else {
                            echo "<a class=\"myButton\" href=\"#\">$button_text</a>";
                        }
                    }
                    echo "</div>";
                }
                echo "</div>";
            }
            echo "</div>";
            echo "</section>";
        }

        if ($current_index < count($sections_by_position) && !$is_modal_request) {
            if ($use_default_divider) {
                echo "<div class=\"separator\"><img src=\"https://admin.nlt-chaplaincy.org/public/down_arrow.png\" alt=\"Default Separator\" class=\"separator-image\"></div>";
            } elseif (!empty($row['theme_img']) && $row['img_role'] === 'Separator') {
                $image_url = IMAGE_BASE_PATH . htmlspecialchars($row['theme_img']);
                echo "<div class=\"separator\"><img src=\"$image_url\" alt=\"Custom Separator\" class=\"separator-image\"></div>";
            }
        }

        if (DEBUG) {
            echo "<div style=\"color: green; font-weight: bold; text-align: center;\">Debug: Section with position $position rendered successfully with " . count($group) . " items.</div>";
        }
    }
} else {
    echo "No sections found.";
}

// Add custom CSS for layout fixes
if ($current_page === 'landing' && strpos($_SERVER['REQUEST_URI'], 'position-100') !== false) {
    echo '<style>';
    echo '.main__right .section-hero--full {
        width: 100% !important;
        max-width: 1200px !important;
        margin: 0 auto !important;
        display: flex !important;
        justify-content: center !important;
        box-sizing: border-box !important;
        overflow: hidden !important;
    }';
    echo '.main__right .section-hero--full .section-hero__content {
        width: 100% !important;
        max-width: 100% !important;
        margin: 0 auto !important;
        padding: 0 !important;
    }';
    echo '.main__right .section-hero--full .column {
        width: 100% !important;
        max-width: 100% !important;
        padding: 0 !important;
        margin: 0 0 20px 0 !important;
        box-sizing: border-box !important;
        text-align: center !important;
        display: flex !important;
        justify-content: center !important;
    }';
    echo '.main__right .section-hero--full .content-embed-container {
        display: block !important;
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        text-align: center !important;
        position: relative !important;
        left: 50% !important;
        transform: translateX(-50%) !important;
    }';
    echo '.main__right .section-hero--full .paypal-embed {
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        text-align: center !important;
        overflow: visible !important;
    }';
    echo '.main__right .section-hero--full .paypal-embed .paypal-buttons,
    .main__right .section-hero--full .paypal-embed iframe {
        width: 100% !important;
        min-width: 300px !important;
        max-width: 300px !important;
        margin: 0 auto !important;
        box-sizing: border-box !important;
        border: none !important;
        outline: none !important;
    }';
    // Mobile-specific single-column with forced centering
    echo '@media (max-width: 767px) {
        .main__right .section-hero--full .section-hero__content {
            display: block !important;
            width: 100% !important;
            margin: 0 !important;
            padding: 0 !important;
            flex-direction: column !important;
        }
        .main__right .section-hero--full .column {
            width: 100% !important;
            max-width: 100% !important;
            margin-bottom: 20px !important;
            text-align: center !important;
            display: flex !important;
            justify-content: center !important;
            align-items: center !important;
        }
        .main__right .section-hero--full .content-embed-container {
            display: block !important;
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            text-align: center !important;
            position: relative !important;
            left: 50% !important;
            transform: translateX(-50%) !important;
            box-sizing: border-box !important;
        }
        .main__right .section-hero--full .paypal-embed,
        .main__right .section-hero--full .paypal-embed .paypal-buttons,
        .main__right .section-hero--full .paypal-embed iframe {
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            overflow: visible !important;
            box-sizing: border-box !important;
        }
        .main__right .section-hero--full.layout-2-column .section-hero__content,
        .main__right .section-hero--full.layout-3-column .section-hero__content {
            flex-direction: column !important;
        }
    }';
    // Desktop single-column for PayPal, multi-column for others
    echo '@media (min-width: 1024px) {
        .main__right .section-hero--full.layout-1-column .section-hero__content {
            display: block !important;
        }
        .main__right .section-hero--full.layout-2-column .section-hero__content,
        .main__right .section-hero--full.layout-3-column .section-hero__content {
            display: grid !important;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important;
            gap: 20px !important;
            justify-content: center !important;
            align-items: center !important;
        }
        .main__right .section-hero--full .column {
            width: auto !important;
            max-width: 100% !important;
            text-align: center !important;
        }
        .main__right .section-hero--full .content-embed-container,
        .main__right .section-hero--full .paypal-embed,
        .main__right .section-hero--full .paypal-embed .paypal-buttons,
        .main__right .section-hero--full .paypal-embed iframe {
            width: 100% !important;
            min-width: 300px !important;
            max-width: 300px !important;
            margin: 0 auto !important;
            overflow: visible !important;
        }
    }';
    echo '</style>';
}

// 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>';
}

$stmt->close();
$conn->close();
?>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Synchronize with PHP DEBUG setting
    const DEBUG = <?php echo DEBUG ? 'true' : 'false'; ?>;

    // Debug log setup
    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}`, { color });
    }

    if (DEBUG) logDebug('Modal script loaded');

    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');

    // Function to render embed blocks with dynamic delay
    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;
        }

        // Extract components with flexible matching
        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 (DEBUG) {
            logDebug(`Div match for ID ${item.id}: ${divMatch ? 'Success' : 'Failed'}`, divMatch ? 'green' : 'red');
            logDebug(`Button match for ID ${item.id}: ${buttonMatch ? 'Success' : 'Failed'}`, buttonMatch ? 'green' : 'red');
            if (buttonMatch) logDebug(`Button script content for ID ${item.id}: ${buttonMatch[0]}`, 'orange');
        }

        if (item.type === 'PayPal' && divMatch && buttonMatch) {
            // Insert div with unique ID
            const uniqueId = `paypal-button-container-${item.id}`;
            let divContent = divMatch[0].replace(/id="[^"]+"/, `id="${uniqueId}"`);
            container.innerHTML = divContent;
            // Apply immediate styles
            container.querySelector(`#${uniqueId}`).style.cssText = 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important;';

            if (DEBUG) logDebug(`Inserted div for PayPal ID ${item.id}`, 'orange');

            // Wait for SDK to be available
            function waitForSDK(callback) {
                if (typeof paypal !== 'undefined') {
                    if (DEBUG) logDebug(`PayPal SDK available for ID ${item.id}`, 'green');
                    callback();
                } else {
                    if (DEBUG) logDebug(`Checking SDK for ID ${item.id}, status: ${typeof paypal}`, 'yellow');
                    setTimeout(() => waitForSDK(callback), 100);
                }
            }

            waitForSDK(() => {
                let scriptContent = buttonMatch[0].replace(/^<script>|<\/script>$/g, '').trim();
                scriptContent = scriptContent.replace(/\.render\(['"][^'"]+['"]\)/, `.render('#${uniqueId}')`);
                // Wrap in unique namespace
                const uniqueScript = `(function() { var paypalInstance = paypal.Buttons; ${scriptContent} })();`;
                const buttonScript = document.createElement('script');
                buttonScript.textContent = uniqueScript;
                document.body.appendChild(buttonScript);
                if (DEBUG) logDebug(`Injected unique button script for ID ${item.id}`, 'orange');

                // Adjust styles and check render with dynamic delay
                const dynamicDelay = index * 1500 + (total * 500); // Increase delay based on total instances
                setTimeout(() => {
                    const paypalButtons = document.querySelectorAll(`#${uniqueId} .paypal-buttons`);
                    const paypalIframes = document.querySelectorAll(`#${uniqueId} iframe`);
                    if (DEBUG) logDebug(`Found ${paypalButtons.length} button(s) and ${paypalIframes.length} iframe(s) for ID ${item.id} after ${dynamicDelay}ms`, paypalButtons.length > 0 ? 'green' : 'red');
                    paypalButtons.forEach((button, btnIndex) => {
                        const iframe = paypalIframes[btnIndex];
                        if (button && iframe) {
                            button.style.cssText += 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important; border: none !important; outline: none !important; display: block !important;';
                            iframe.style.cssText += 'width: 100% !important; min-width: 300px !important; max-width: 300px !important; margin: 0 auto !important; border: none !important; outline: none !important; display: block !important;';
                            if (DEBUG) {
                                const style = window.getComputedStyle(button);
                                logDebug(`Computed style for button ${uniqueId} (index ${btnIndex}): width=${style.width}, margin=${style.margin}, justify-content=${style.justifyContent}`, 'purple');
                            }
                        } else if (DEBUG) {
                            logDebug(`Button or iframe not found for ID ${item.id} at index ${btnIndex}`, 'red');
                        }
                    });
                }, dynamicDelay);
            });
        } else if (item.type === 'YouTube') {
            container.innerHTML = item.code;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Rendered YouTube embed for ID ${item.id}`, 'orange');
        } else {
            container.innerHTML = `<div class="embed-fallback" id="fallback-${item.id}">${item.code}</div>`;
            container.style.display = 'block';
            if (DEBUG) logDebug(`Fallback rendered for ID ${item.id} (parsing failed, scripts not executed)`, 'red');
        }
    }

    // Process embed blocks with sequential rendering
    if (window.contentCode && Array.isArray(window.contentCode)) {
        window.contentCode.forEach((item, index) => {
            renderEmbed(item, index, window.contentCode.length);
        });
    } else if (DEBUG) {
        logDebug('contentCode is not an array or unavailable', 'red');
    }

    // Desktop centering fallback
    function adjustDesktopLayout() {
        if (window.innerWidth >= 1024) {
            const embedContainers = document.querySelectorAll('.main__right .section-hero--full .content-embed-container');
            embedContainers.forEach(container => {
                container.style.margin = '0 auto !important';
                if (DEBUG) logDebug(`Adjusted desktop container centering for ${container.querySelector('.paypal-embed')?.id}`, 'blue');
            });
        }
    }
    window.addEventListener('load', adjustDesktopLayout);
    window.addEventListener('resize', adjustDesktopLayout);

    function openModal(content) {
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = content;

        const buttons = tempDiv.querySelectorAll('.content__buttons');
        buttons.forEach(button => button.remove());

        const debugMessages = tempDiv.querySelectorAll('div[style*="color: purple"], div[style*="color: blue"], div[style*="color: red"], div[style*="color: green"]');
        debugMessages.forEach(msg => msg.remove());

        let combinedContent = '';
        const sections = tempDiv.querySelectorAll('.modal-story-content');
        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections');
        if (sections.length > 0) {
            sections.forEach((section, index) => {
                combinedContent += section.outerHTML.trim();
                if (index < sections.length - 1) {
                    combinedContent += '<div class="modal-section-separator"></div>';
                }
            });
        } else {
            const fallbackSections = tempDiv.querySelectorAll('.section-hero__content');
            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
            if (fallbackSections.length > 0) {
                fallbackSections.forEach((section, index) => {
                    const title = section.querySelector('h1');
                    const image = section.querySelector('.content-image');
                    const content = section.querySelector('.generic-content');

                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    if (title) newContent.appendChild(title.cloneNode(true));
                    if (image) newContent.appendChild(image.cloneNode(true));
                    if (content) newContent.appendChild(content.cloneNode(true));

                    combinedContent += newContent.outerHTML.trim();
                    if (index < fallbackSections.length - 1) {
                        combinedContent += '<div class="modal-section-separator"></div>';
                    }
                });
            } else {
                const genericContent = tempDiv.querySelector('.generic-content');
                if (genericContent) {
                    if (DEBUG) logDebug('Falling back to .generic-content');
                    const newContent = document.createElement('div');
                    newContent.className = 'modal-story-content';
                    newContent.appendChild(genericContent.cloneNode(true));
                    combinedContent = newContent.outerHTML.trim();
                } else {
                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                }
            }
        }

        modalBody.innerHTML = combinedContent;
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        if (modalBody.children.length > 0 && closeBtn) {
            closeBtn.focus();
        }
        if (DEBUG) logDebug('Modal opened');
    }

    function closeModal() {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        modalBody.innerHTML = '';
        if (DEBUG && debugLog) {
            debugLog.innerHTML = '';
        }
        if (DEBUG) logDebug('Modal closed');
    }

    if (modal && modalBody && closeBtn && triggers) {
        triggers.forEach(trigger => {
            trigger.addEventListener('click', function(e) {
                e.preventDefault();
                const modalId = this.getAttribute('data-modal-id');
                if (modalId) {
                    const contentDiv = document.getElementById(modalId);
                    if (contentDiv) {
                        const content = contentDiv.innerHTML;
                        if (DEBUG) logDebug('Stories modal triggered, content from ID: ' + modalId);
                        if (DEBUG) logDebug('Modal content length: ' + content.length + ' characters', 'purple');
                        openModal(content);
                    } else {
                        openModal('<p>Content not found.</p>');
                        if (DEBUG) logDebug('Modal content not found for ID: ' + modalId, 'red');
                    }
                    return;
                }

                const link = this.getAttribute('data-modal-link');
                if (DEBUG) logDebug('Trigger clicked, link: ' + link);

                if (!link || link === '#' || link.includes('index.php')) {
                    openModal('<p>This link cannot be loaded in a modal to prevent recursive loading.</p>');
                    return;
                }

                let basePath = window.location.origin;
                let absoluteLink;
                try {
                    if (link.startsWith('http://') || link.startsWith('https://')) {
                        absoluteLink = link;
                    } else if (link.startsWith('/')) {
                        absoluteLink = new URL(link, window.location.origin).href;
                    } else {
                        absoluteLink = new URL(link, window.location.origin + '/').href;
                    }
                    absoluteLink += (absoluteLink.includes('?') ? '&' : '?') + 'modal=1';
                    if (DEBUG) logDebug('Fetching absolute link: ' + absoluteLink);
                } catch (error) {
                    if (DEBUG) logDebug('URL construction error: ' + error.message, 'red');
                    openModal('<p>Invalid URL format.</p>');
                    return;
                }

                openModal('<p>Loading...</p>');

                fetch(absoluteLink, {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                })
                    .then(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(data => {
                        if (DEBUG) logDebug('Fetched HTML length: ' + data.length);
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(data, 'text/html');

                        let combinedContent = '';
                        const sections = doc.querySelectorAll('.modal-story-content');
                        if (DEBUG) logDebug('Found ' + sections.length + ' modal-story-content sections in fetched content');
                        if (sections.length > 0) {
                            sections.forEach((content, index) => {
                                combinedContent += content.outerHTML.trim();
                                if (index < sections.length - 1) {
                                    combinedContent += '<div class="modal-section-separator"></div>';
                                }
                            });
                        } else {
                            const fallbackSections = doc.querySelectorAll('.section-hero__content');
                            if (DEBUG) logDebug('Found ' + fallbackSections.length + ' section-hero__content sections');
                            if (fallbackSections.length > 0) {
                                fallbackSections.forEach((section, index) => {
                                    const title = section.querySelector('h1');
                                    const image = section.querySelector('.content-image');
                                    const content = section.querySelector('.generic-content');

                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    if (title) newContent.appendChild(title.cloneNode(true));
                                    if (image) newContent.appendChild(image.cloneNode(true));
                                    if (content) newContent.appendChild(content.cloneNode(true));

                                    combinedContent += newContent.outerHTML.trim();
                                    if (index < fallbackSections.length - 1) {
                                        combinedContent += '<div class="modal-section-separator"></div>';
                                    }
                                });
                            } else {
                                const genericContent = doc.querySelector('.generic-content');
                                if (genericContent) {
                                    if (DEBUG) logDebug('Falling back to .generic-content');
                                    const newContent = document.createElement('div');
                                    newContent.className = 'modal-story-content';
                                    newContent.appendChild(genericContent.cloneNode(true));
                                    combinedContent = newContent.outerHTML.trim();
                                } else {
                                    if (DEBUG) logDebug('No content found in .modal-story-content, .section-hero__content, or .generic-content', 'red');
                                    combinedContent = '<p>Content could not be parsed from the page.</p>';
                                }
                            }
                        }

                        if (combinedContent) {
                            openModal(combinedContent);
                        } else {
                            openModal('<p>No content found.</p>');
                        }
                    })
                    .catch(error => {
                        if (DEBUG) logDebug('Fetch error: ' + error.message, 'red');
                        openModal('<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 (modal, modalBody, closeBtn, or triggers)', 'red');
    }
});
</script>

Changes Made

  1. Debug Setting:

    • Kept define('DEBUG', true); for detailed logging.
  2. JavaScript (renderEmbed):

    • Added a unique namespace with var paypalInstance = paypal.Buttons to isolate each render from the global SDK context.
    • Introduced a dynamic delay (index * 1500 + (total * 500)) to scale the wait time based on the number of instances, reducing overlap.
    • Retained immediate styling and post-render adjustments.
  3. Version Update: Incremented to V2.61 in the file header comment.

Testing Instructions

  1. Update pull-contents.php:

    • Replace the existing pull-contents.php with the V2.61 content above.
    • Regenerate the site with Publii.
  2. Test Desktop View:

    • Resize to 1920px width.
    • Navigate to https://www.nlt-chaplaincy.org/index.html with position-100.
    • Verify that all four sections (A, B, C, D) display both yellow and black buttons fully, with no cut-off, at 300px wide.
    • Check the debug log for “Injected unique button script…” and “Found [n] button(s) and [n] iframe(s)” messages.
  3. Test Mobile View:

    • Resize to a mobile width (e.g., 480px).
    • Confirm all four sections stack vertically, are centered, and display both buttons at 300px with no cut-off.
    • Review the debug log similarly.
  4. Inspect Elements:

    • If buttons appear, inspect each PP area to confirm both buttons are present with a 300px width.
    • If cut-off persists, note the computed styles and debug log.
  5. Browser Console:

    • Check the console (F12) for PayPal SDK errors (e.g., “render failed”).
    • Share any errors found.

Additional Notes

  • Time: It’s 2:15 PM HKT on Friday, September 05, 2025—still within testing time.
  • Rationale: The unique namespace and dynamic delay should prevent SDK conflicts when multiple PP embeds are grouped at the same position. The debug log will confirm if all instances render.
  • Next Steps: If all sections show both buttons, we’re resolved. If B, C, and D still fail, share the debug log (especially buttonMatch content and render counts) and console errors. We may need to check the code field in the web_content table for consistency.

Please update to V2.61, test, and let me know the results—focus on the debug output for all sections!


Q: This version works!!


After many trials we found that PP just doesn't support multi-column.

Sharing is sexy
Claudio
Claudio
liclaudio38@gmail.com
Claudio

About Me

Li

Copyright © 2023 Curation Web 我的資訊「展」輯. All Rights Reserved | Powered by Bludit | Theme by BlThemes