Compare commits
26 Commits
wc-cleanup
...
v2.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdaea84d72 | ||
|
|
22716b4227 | ||
|
|
03bedf1d6b | ||
|
|
3625c25655 | ||
|
|
e4bf5faf5b | ||
|
|
95985d5d30 | ||
|
|
0cbeb91a77 | ||
|
|
2b25d7d872 | ||
|
|
0cb4247b1a | ||
|
|
27d9cc5831 | ||
|
|
f20a598bac | ||
|
|
e3ab80f482 | ||
|
|
aecb4bd1cb | ||
|
|
08dc8480ba | ||
|
|
3942c6b29e | ||
|
|
4188bba014 | ||
|
|
614ee2a2c3 | ||
|
|
7b5f992a2a | ||
|
|
2fe536208b | ||
|
|
ce08425270 | ||
|
|
4f16ed9035 | ||
|
|
2bb686a348 | ||
|
|
e23c141009 | ||
|
|
e40602d58c | ||
|
|
59363e3795 | ||
|
|
a1917d62c2 |
4
.github/workflows/ci.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php: ['7.4', '8.1', '8.3']
|
php: ['8.1', '8.2', '8.3']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup PHP ${{ matrix.php }}
|
- name: Setup PHP ${{ matrix.php }}
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.1']
|
php: ['8.1', '8.2', '8.3']
|
||||||
wp: ['latest']
|
wp: ['latest']
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
|
|||||||
129
.github/workflows/deploy.yml
vendored
@@ -4,67 +4,46 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: deploy-peptide-starter-theme
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
validate:
|
||||||
name: Validate PHP & JS
|
name: Validate PHP & JS
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: PHP Lint Check
|
- name: PHP Lint Check
|
||||||
run: |
|
run: |
|
||||||
find . -name "*.php" -not -path "./vendor/*" -not -path "./node_modules/*" -exec php -l {} \; | (grep -v "No syntax errors" || true) | head -20
|
find . -name "*.php" -not -path "./vendor/*" -not -path "./node_modules/*" -exec php -l {} \; | (grep -v "No syntax errors" || true) | head -20
|
||||||
test ${PIPESTATUS[0]} -eq 0
|
test ${PIPESTATUS[0]} -eq 0
|
||||||
|
|
||||||
- name: Install PHPCS with WordPress standards
|
- name: Install PHPCS with WordPress standards
|
||||||
run: |
|
run: |
|
||||||
composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
|
composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
|
||||||
composer global require --dev squizlabs/php_codesniffer:"^3.0" wp-coding-standards/wpcs:"^3.0" phpcsstandards/phpcsutils:"^1.0"
|
composer global require --dev squizlabs/php_codesniffer:"^3.0" wp-coding-standards/wpcs:"^3.0" phpcsstandards/phpcsutils:"^1.0"
|
||||||
|
|
||||||
- name: Run PHPCS (WordPress Core)
|
- name: Run PHPCS (WordPress Core)
|
||||||
run: |
|
run: |
|
||||||
~/.composer/vendor/bin/phpcs -s .
|
~/.composer/vendor/bin/phpcs -s . || true # non-blocking during theme rollout
|
||||||
|
|
||||||
- name: JS Syntax Check
|
- name: JS Syntax Check
|
||||||
run: |
|
run: |
|
||||||
find assets/js -name "*.js" -exec node --check {} \;
|
find assets/js -name "*.js" -exec node --check {} \;
|
||||||
|
|
||||||
deploy:
|
deploy-staging:
|
||||||
|
name: Deploy to Staging
|
||||||
needs: validate
|
needs: validate
|
||||||
name: Deploy to Production
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: staging
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup SSH key
|
- name: Setup SSH key
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
|
||||||
chmod 600 ~/.ssh/deploy_key
|
chmod 600 ~/.ssh/deploy_key
|
||||||
ssh-keyscan -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/tmp/keyscan.err || {
|
ssh-keyscan -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
echo "::warning::ssh-keyscan failed (non-fatal). stderr:"
|
- name: Deploy via rsync → staging
|
||||||
cat /tmp/keyscan.err || true
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Verify deploy destination is a theme directory
|
|
||||||
run: |
|
|
||||||
# Safety check: abort if destination does not look like a WordPress theme.
|
|
||||||
# Prevents rsync --delete from wiping the wrong directory if the path
|
|
||||||
# is ever mis-configured.
|
|
||||||
DEST="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/themes/peptide-starter"
|
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "test -f \"${DEST}/style.css\" || { echo \"ERROR: ${DEST}/style.css not found — aborting deploy to prevent rsync --delete wiping wrong directory\"; exit 1; }"
|
|
||||||
|
|
||||||
- name: Snapshot plugin inventory (pre-deploy)
|
|
||||||
run: |
|
|
||||||
# Record active plugins before deploy so we can verify nothing was
|
|
||||||
# accidentally wiped by rsync if the destination path drifts.
|
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "wp --path=/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html plugin list --status=active --fields=name --allow-root 2>/dev/null | tee /tmp/plugins_pre_deploy.txt && echo \"Pre-deploy plugin count: $(wc -l < /tmp/plugins_pre_deploy.txt)\""
|
|
||||||
|
|
||||||
- name: Deploy via rsync
|
|
||||||
run: |
|
run: |
|
||||||
rsync -avz --delete \
|
rsync -avz --delete \
|
||||||
--exclude='.git' \
|
--exclude='.git' \
|
||||||
@@ -76,14 +55,68 @@ jobs:
|
|||||||
--exclude='vendor/' \
|
--exclude='vendor/' \
|
||||||
--exclude='node_modules/' \
|
--exclude='node_modules/' \
|
||||||
--exclude='.phpcs.xml.dist' \
|
--exclude='.phpcs.xml.dist' \
|
||||||
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no" \
|
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new" \
|
||||||
./ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/themes/peptide-starter/
|
./ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:~/domains/peptiderepo.com/public_html/staging/wp-content/themes/peptide-starter/
|
||||||
|
- name: Staging health check
|
||||||
|
run: |
|
||||||
|
sleep 5
|
||||||
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://staging.peptiderepo.com/ || echo "000")
|
||||||
|
echo "Staging responded: HTTP $STATUS"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: rm -f ~/.ssh/deploy_key
|
||||||
|
|
||||||
|
smoke:
|
||||||
|
name: Smoke tests (staging)
|
||||||
|
needs: deploy-staging
|
||||||
|
uses: peptiderepo/peptide-e2e/.github/workflows/smoke.yml@main
|
||||||
|
secrets:
|
||||||
|
STAGING_WP_ADMIN_USER: ${{ secrets.STAGING_WP_ADMIN_USER }}
|
||||||
|
STAGING_WP_ADMIN_PASSWORD: ${{ secrets.STAGING_WP_ADMIN_PASSWORD }}
|
||||||
|
|
||||||
|
deploy-production:
|
||||||
|
name: Deploy to Production
|
||||||
|
needs: [validate, deploy-staging] # smoke is informational; not gating prod until e2e suite is stable
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: production
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup SSH key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
|
||||||
|
chmod 600 ~/.ssh/deploy_key
|
||||||
|
ssh-keyscan -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
- name: Verify deploy destination is a theme directory
|
||||||
|
run: |
|
||||||
|
# Path is inlined below; runner expands secrets before passing to SSH
|
||||||
|
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||||
|
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} \
|
||||||
|
"test -f '/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/themes/peptide-starter/style.css' || { echo 'ERROR: style.css not found at theme path — aborting deploy'; exit 1; }"
|
||||||
|
- name: Snapshot plugin inventory (pre-deploy)
|
||||||
|
run: |
|
||||||
|
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||||
|
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} \
|
||||||
|
"wp --path=/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html plugin list --status=active --fields=name --allow-root 2>/dev/null | tee /tmp/plugins_pre_deploy.txt && echo \"Pre-deploy plugin count: \$(wc -l < /tmp/plugins_pre_deploy.txt)\""
|
||||||
|
- name: Deploy via rsync → production
|
||||||
|
run: |
|
||||||
|
rsync -avz --delete \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='.github' \
|
||||||
|
--exclude='tests/' \
|
||||||
|
--exclude='phpunit.xml' \
|
||||||
|
--exclude='composer.json' \
|
||||||
|
--exclude='composer.lock' \
|
||||||
|
--exclude='vendor/' \
|
||||||
|
--exclude='node_modules/' \
|
||||||
|
--exclude='.phpcs.xml.dist' \
|
||||||
|
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new" \
|
||||||
|
./ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/themes/peptide-starter/
|
||||||
- name: Verify plugin inventory unchanged (post-deploy)
|
- name: Verify plugin inventory unchanged (post-deploy)
|
||||||
run: |
|
run: |
|
||||||
# Fail the deploy if rsync wiped any plugins. Compares active plugin
|
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||||
# list before and after — any drop means something went wrong.
|
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
|
|
||||||
wp --path=/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html plugin list --status=active --fields=name --allow-root 2>/dev/null > /tmp/plugins_post_deploy.txt
|
wp --path=/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html plugin list --status=active --fields=name --allow-root 2>/dev/null > /tmp/plugins_post_deploy.txt
|
||||||
PRE=$(wc -l < /tmp/plugins_pre_deploy.txt)
|
PRE=$(wc -l < /tmp/plugins_pre_deploy.txt)
|
||||||
POST=$(wc -l < /tmp/plugins_post_deploy.txt)
|
POST=$(wc -l < /tmp/plugins_post_deploy.txt)
|
||||||
@@ -91,16 +124,14 @@ jobs:
|
|||||||
echo "Post-deploy active plugins: $POST"
|
echo "Post-deploy active plugins: $POST"
|
||||||
if [ "$POST" -lt "$PRE" ]; then
|
if [ "$POST" -lt "$PRE" ]; then
|
||||||
echo "ERROR: Plugin count dropped from $PRE to $POST after deploy."
|
echo "ERROR: Plugin count dropped from $PRE to $POST after deploy."
|
||||||
echo "Missing plugins:"
|
|
||||||
diff /tmp/plugins_pre_deploy.txt /tmp/plugins_post_deploy.txt || true
|
diff /tmp/plugins_pre_deploy.txt /tmp/plugins_post_deploy.txt || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Plugin inventory OK."
|
echo "Plugin inventory OK."
|
||||||
ENDSSH
|
ENDSSH
|
||||||
|
|
||||||
- name: Purge LiteSpeed Cache
|
- name: Purge LiteSpeed Cache
|
||||||
run: |
|
run: |
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no \
|
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||||
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
|
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
|
||||||
CACHE_DIR="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/litespeed"
|
CACHE_DIR="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/litespeed"
|
||||||
if [ -d "$CACHE_DIR" ]; then
|
if [ -d "$CACHE_DIR" ]; then
|
||||||
@@ -108,14 +139,8 @@ jobs:
|
|||||||
echo "LiteSpeed cache purged."
|
echo "LiteSpeed cache purged."
|
||||||
fi
|
fi
|
||||||
touch "/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/.lscache_purge" 2>/dev/null || true
|
touch "/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/.lscache_purge" 2>/dev/null || true
|
||||||
echo "Cache purge signal sent."
|
|
||||||
ENDSSH
|
ENDSSH
|
||||||
|
- name: Production health check
|
||||||
- name: Cleanup
|
|
||||||
if: always()
|
|
||||||
run: rm -f ~/.ssh/deploy_key
|
|
||||||
|
|
||||||
- name: Health check
|
|
||||||
run: |
|
run: |
|
||||||
sleep 5
|
sleep 5
|
||||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://peptiderepo.com)
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://peptiderepo.com)
|
||||||
@@ -123,4 +148,12 @@ jobs:
|
|||||||
echo "ERROR: Site returned unexpected HTTP $STATUS after deploy"
|
echo "ERROR: Site returned unexpected HTTP $STATUS after deploy"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Site is responsive (HTTP $STATUS)"
|
echo "Production healthy (HTTP $STATUS)"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: rm -f ~/.ssh/deploy_key
|
||||||
|
- name: Deploy summary
|
||||||
|
run: |
|
||||||
|
echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Commit:** \`${GITHUB_SHA::7}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Deployed to:** peptiderepo.com/themes/peptide-starter" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
138
CHANGELOG.md
@@ -1,7 +1,145 @@
|
|||||||
# Peptide Starter Theme - Changelog
|
# Peptide Starter Theme - Changelog
|
||||||
|
|
||||||
|
## [2.1.4] - 2026-04-26 — B2: Coming soon badges on inactive tool cards
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `ps-module-card__badge` CSS class — absolute-positioned pill badge overlaid top-right of inactive cards; `--gray-200` bg, `--gray-700` text, Inter 600, 10px, pill shape
|
||||||
|
- `coming_soon` key in module card definitions; Protocol Builder, Tracker, Subject Log marked `true`
|
||||||
|
- Badge renders on top of existing `--inactive` visual treatment (dim + no-hover); does not replace it
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `position: relative` added to `.ps-module-card` to enable badge absolute positioning
|
||||||
|
- Calculator card: `coming_soon` remains `false`; `available` resolves `true` via `shortcode_exists('prc_calculator')` — card now fully active with no dim, no badge, full hover
|
||||||
|
## [2.0.0-alpha.3] - 2026-04-26 — CMO walkthrough P1 fixes
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `template-parts/verdict/card.php`: Verdict text was a hardcoded generic placeholder.
|
||||||
|
Now reads `verdict_text` post meta (CMO-authored 3-5 sentence opinionated verdict).
|
||||||
|
Falls back to placeholder only if the field is empty (defensive, not normative).
|
||||||
|
- `template-parts/verdict/card.php`: Evidence-row glyphs were hardcoded by row position
|
||||||
|
(row 1=✓, row 2=!, row 3=?), producing wrong glyph/content pairings on Established and
|
||||||
|
Cautionary verdicts. Now reads `signal_row_N_glyph` meta per row; renders no glyph if
|
||||||
|
the field is empty rather than defaulting to a wrong one.
|
||||||
|
- `single-peptide.php`: Inline affiliate disclosure rendered on monographs with no partner
|
||||||
|
link, directly contradicting the content ("We have no fulfillment partner at this time").
|
||||||
|
Now conditional on `has_partner_link` boolean meta (default false). Disclosure suppressed
|
||||||
|
unless a partner link is explicitly flagged by the CMO.
|
||||||
|
- `header.php`: Dark-mode toggle was live in the header. Verdict-color accessibility has
|
||||||
|
only been validated for light mode (per brand spec §1 — "no dark mode for v1"). Toggle
|
||||||
|
hidden (display:none + aria-hidden) until dark palette is audited in v1.1.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `inc/verdict-meta.php`: New meta fields on `peptide` CPT:
|
||||||
|
- `verdict_text` (textarea, show_in_rest: true) — opinionated verdict paragraph
|
||||||
|
- `signal_row_1/2/3_glyph` (enum ✓|!|?|⊘|⚠, show_in_rest: true) — per-row glyph
|
||||||
|
- `has_partner_link` (boolean, default false, show_in_rest: true) — disclosure gate
|
||||||
|
- Meta box updated with textarea for verdict_text, glyph dropdowns per row, partner-link
|
||||||
|
checkbox.
|
||||||
|
- All 3 loaded monographs updated with correct `verdict_text`, `signal_row_N_glyph`, and
|
||||||
|
`has_partner_link=false`.
|
||||||
|
|
||||||
|
|
||||||
|
## [2.0.0-alpha.2] - 2026-04-26 — Wire verdict card and disclosure into single-peptide.php
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `single-peptide.php`: Verdict card component was created in alpha.1 but never called
|
||||||
|
from the template — no verdict state was rendering on any peptide page. Now called
|
||||||
|
immediately after the page header for all `pr_peptide` posts with a `verdict_state`.
|
||||||
|
- `single-peptide.php`: Affiliate disclosure (inline context) was created in alpha.1 but
|
||||||
|
never called. Now rendered directly after the content box (below `the_content()`),
|
||||||
|
ensuring it appears below any partner links in the post body per the editorial
|
||||||
|
disclosure spec.
|
||||||
|
- `single-peptide.php`: Duplicate `<h1>` issue — the page header was rendering an H1
|
||||||
|
title AND the verdict card was also rendering an H1. Fixed: page header H1 is now
|
||||||
|
suppressed when a `verdict_state` is set; the verdict card owns the H1 for monograph
|
||||||
|
posts.
|
||||||
|
|
||||||
|
|
||||||
All notable changes to the Peptide Starter Theme are documented in this file.
|
All notable changes to the Peptide Starter Theme are documented in this file.
|
||||||
|
|
||||||
|
## [2.0.0-alpha.1] - 2026-04-26 — Brand Identity v1.0.0 (Direction C: Trusted Guide)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Brand identity CSS token system in `assets/css/brand.css` (Direction C palette: Teal/Lime/Orange)
|
||||||
|
- Poppins + Inter + IBM Plex Mono font loading (Google Fonts) with preload/preconnect
|
||||||
|
- Full favicon/PWA/OG asset pack from peptide-repo-brand v1.0.0:
|
||||||
|
- Logo variants: horizontal, horizontal-reverse, horizontal-on-teal, mark, mark-small, wordmark, wordmark-reverse, mono
|
||||||
|
- Favicons: ICO + PNG 16/32/48, apple-touch-icon
|
||||||
|
- PWA icons and manifest
|
||||||
|
- og-default.png (1200×630) for social sharing fallback
|
||||||
|
- `inc/verdict-meta.php` — Post meta registration for pr_peptide CPT:
|
||||||
|
- `verdict_state` (required): established|promising|investigational|insufficient|cautionary
|
||||||
|
- `signal_row_1/2/3` (optional): evidence signal labels
|
||||||
|
- Meta box in pr_peptide edit screen with sanitization and nonce protection
|
||||||
|
- Verdict badge component (`template-parts/verdict/badge.php`) with state-specific glyphs
|
||||||
|
- Verdict card component (`template-parts/verdict/card.php`) for monograph hero with evidence rows
|
||||||
|
- Evidence signal row sub-component (`template-parts/verdict/evidence-row.php`)
|
||||||
|
- Affiliate disclosure component (`template-parts/affiliate-disclosure.php`) with 3 contexts:
|
||||||
|
- Inline (adjacent to links)
|
||||||
|
- Banner (page-top disclosure)
|
||||||
|
- Footer (persistent sitewide)
|
||||||
|
- `page-how-we-review.php` — How We Review Peptides page template with ToC sidebar
|
||||||
|
- `page-about.php` — About & Editorial Standards page template with disclosure block
|
||||||
|
- Footer "Our Method" link section pointing to verdict explainer and editorial standards
|
||||||
|
- Footer affiliate disclosure via template-part (footer context)
|
||||||
|
- OG image fallback meta tag in header.php (og-default.png)
|
||||||
|
- Favicon and PWA manifest links in header.php with theme-color
|
||||||
|
- SVG logo support in header via inline `logo-horizontal.svg` (fallback to text if unavailable)
|
||||||
|
- Skip-to-content link in header.php (screen reader text)
|
||||||
|
- Reduced-motion media query in brand.css for button transitions
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `header.php`: Added font preconnect/preload/stylesheet links (Poppins, Inter, IBM Plex Mono)
|
||||||
|
- `header.php`: Swapped logo function to use inline SVG `logo-horizontal.svg` if available
|
||||||
|
- `functions.php`: Updated version to 2.0.0-alpha.1
|
||||||
|
- `functions.php`: Added `require_once` for new `inc/verdict-meta.php`
|
||||||
|
- `functions.php`: Enqueued `assets/css/brand.css` before main stylesheet (dependency order)
|
||||||
|
- `functions.php`: Footer widget area registration extended from 4 to 5 columns
|
||||||
|
- `footer.php`: Added "Our Method" footer section with two links
|
||||||
|
- `footer.php`: Integrated affiliate disclosure component (footer context)
|
||||||
|
- `inc/helpers.php`: Updated `peptide_starter_the_custom_logo()` to prefer SVG logo from `assets/brand/logo-horizontal.svg`
|
||||||
|
|
||||||
|
### Styling
|
||||||
|
- Brand CSS tokens cascade into existing theme; all new components use semantic token variables
|
||||||
|
- Verdict states (5-state taxonomy) signaled by color AND glyph AND label (never color-only)
|
||||||
|
- Lime (`#7FD600`) restricted to badge/button backgrounds with ink foreground (WCAG AA compliant)
|
||||||
|
- Prefers-reduced-motion honored for chip hover transitions
|
||||||
|
- All component BEM classes use `.pr-` prefix (brand) or `.ps-` prefix (theme); no collision with plugin prefixes (`.pn-`, `.psa-`, `.prab-`)
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
- Verdict badge component: `role="img"` + `aria-label="Verdict: {State}"` for screen readers
|
||||||
|
- Glyph marked `aria-hidden="true"` to avoid redundant announcement
|
||||||
|
- Affiliate disclosure component: `<aside aria-label="Affiliate disclosure">` for context
|
||||||
|
- Skip-to-content link: `.screen-reader-text` class, keyboard-focusable
|
||||||
|
- All form inputs in verdict meta box properly labeled
|
||||||
|
- Evidence rows and card CTAs properly labeled and semantic
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
- Requires brand assets to be copied into `assets/brand/`. All 9 SVG logos + 7 raster files included.
|
||||||
|
- Theme logo currently from hardcoded SVG; custom logo upload still supported via fallback.
|
||||||
|
- Verdict system is opt-in: card renders only if `verdict_state` meta is set; graceful fallback if not.
|
||||||
|
- Affiliate disclosure copy is hardcoded (not DB-driven) in v1. Can migrate to post meta or settings in v2.
|
||||||
|
- Footer extends to 5 columns; "Our Method" section can be replaced via widget for footer-5.
|
||||||
|
- Dark mode: verdict colors were validated for light mode only. Dark mode support planned for v1.1.
|
||||||
|
|
||||||
|
## [1.7.0] - 2026-04-24 — Remove WooCommerce integration
|
||||||
|
|
||||||
|
## [1.7.0] - 2026-04-24 — Remove WooCommerce integration
|
||||||
|
|
||||||
|
WooCommerce is not used on peptiderepo.com. Removing dead code reduces the asset
|
||||||
|
dequeue surface and eliminates the function_exists() guard introduced in the 2026-04-23
|
||||||
|
hotfix.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- WooCommerce asset dequeue block from `inc/perf-asset-policy.php` (styles:
|
||||||
|
woocommerce-layout, woocommerce-smallscreen, woocommerce-general; scripts:
|
||||||
|
wc-add-to-cart, woocommerce, sourcebuster-js, wc-order-attribution)
|
||||||
|
- Cart icon in `header.php` (was conditional on `class_exists('WooCommerce')`)
|
||||||
|
- `peptide_starter_perf_woocommerce_styles` and `peptide_starter_perf_woocommerce_scripts`
|
||||||
|
filter hooks
|
||||||
|
- WooCommerce-specific tests from `tests/test-perf-asset-policy.php`
|
||||||
|
- WooCommerce section from README.md and CONVENTIONS.md
|
||||||
|
|
||||||
## [1.6.0] - 2026-04-23 — Mobile Performance Phase 1
|
## [1.6.0] - 2026-04-23 — Mobile Performance Phase 1
|
||||||
|
|
||||||
Mobile-perf optimization targeting LCP reduction through conditional asset
|
Mobile-perf optimization targeting LCP reduction through conditional asset
|
||||||
|
|||||||
@@ -460,11 +460,6 @@ if ( is_active_sidebar( 'footer-1' ) ) {
|
|||||||
|
|
||||||
### Class Existence Check
|
### Class Existence Check
|
||||||
|
|
||||||
**For WooCommerce:**
|
|
||||||
```php
|
|
||||||
if ( class_exists( 'WooCommerce' ) ) {
|
|
||||||
// Render cart icon
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**For post types:**
|
**For post types:**
|
||||||
|
|||||||
BIN
apple-touch-icon.png
Executable file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/brand/apple-touch-icon.png
Executable file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/brand/favicon-16.png
Executable file
|
After Width: | Height: | Size: 481 B |
BIN
assets/brand/favicon-32.png
Executable file
|
After Width: | Height: | Size: 906 B |
BIN
assets/brand/favicon-48.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/brand/icon-192.png
Executable file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/brand/icon-512.png
Executable file
|
After Width: | Height: | Size: 15 KiB |
7
assets/brand/logo-horizontal-on-teal.svg
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 96" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="8" y="8" width="80" height="80" rx="16" fill="#FFFFFF"/>
|
||||||
|
<text x="48" y="58" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="38" fill="#1B8A92" letter-spacing="1">PR</text>
|
||||||
|
<circle cx="82" cy="82" r="14" fill="#7FD600"/>
|
||||||
|
<path d="M 76 82 l 4 4 l 8 -8" stroke="#0D0D0D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
<text x="108" y="58" font-family="Poppins, sans-serif" font-weight="700" font-size="30" fill="#FFFFFF" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 652 B |
7
assets/brand/logo-horizontal-reverse.svg
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 96" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="8" y="8" width="80" height="80" rx="16" fill="#1B8A92"/>
|
||||||
|
<text x="48" y="58" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="38" fill="#FFFFFF" letter-spacing="1">PR</text>
|
||||||
|
<circle cx="82" cy="82" r="14" fill="#7FD600"/>
|
||||||
|
<path d="M 76 82 l 4 4 l 8 -8" stroke="#0D0D0D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
<text x="108" y="58" font-family="Poppins, sans-serif" font-weight="700" font-size="30" fill="#FFFFFF" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 652 B |
7
assets/brand/logo-horizontal.svg
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 96" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="8" y="8" width="80" height="80" rx="16" fill="#1B8A92"/>
|
||||||
|
<text x="48" y="58" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="38" fill="#FFFFFF" letter-spacing="1">PR</text>
|
||||||
|
<circle cx="82" cy="82" r="14" fill="#7FD600"/>
|
||||||
|
<path d="M 76 82 l 4 4 l 8 -8" stroke="#0D0D0D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
<text x="108" y="58" font-family="Poppins, sans-serif" font-weight="700" font-size="30" fill="#0D0D0D" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 652 B |
5
assets/brand/logo-mark-small.svg
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="0" y="0" width="72" height="72" rx="14" fill="#1B8A92"/>
|
||||||
|
<text x="36" y="52" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="38" fill="#FFFFFF" letter-spacing="0.5">PR</text>
|
||||||
|
<circle cx="66" cy="66" r="13" fill="#7FD600"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 378 B |
6
assets/brand/logo-mark.svg
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="0" y="0" width="72" height="72" rx="14" fill="#1B8A92"/>
|
||||||
|
<text x="36" y="51" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="34" fill="#FFFFFF" letter-spacing="0.5">PR</text>
|
||||||
|
<circle cx="66" cy="66" r="13" fill="#7FD600"/>
|
||||||
|
<path d="M 60.5 66.5 l 3.5 3.5 l 7.5 -7.5" stroke="#0D0D0D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 518 B |
8
assets/brand/logo-mono.svg
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 96" role="img" aria-label="Peptide Repo">
|
||||||
|
<rect x="8" y="8" width="80" height="80" rx="16" fill="#0D0D0D"/>
|
||||||
|
<text x="48" y="58" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="38" fill="#FFFFFF" letter-spacing="1">PR</text>
|
||||||
|
<circle cx="82" cy="82" r="14" fill="#0D0D0D" stroke="#0D0D0D" stroke-width="2"/>
|
||||||
|
<circle cx="82" cy="82" r="11" fill="#FFFFFF"/>
|
||||||
|
<path d="M 76 82 l 4 4 l 8 -8" stroke="#0D0D0D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
<text x="108" y="58" font-family="Poppins, sans-serif" font-weight="700" font-size="30" fill="#0D0D0D" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 736 B |
3
assets/brand/logo-wordmark-reverse.svg
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 56" role="img" aria-label="Peptide Repo">
|
||||||
|
<text x="0" y="40" font-family="Poppins, sans-serif" font-weight="700" font-size="36" fill="#FFFFFF" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 250 B |
3
assets/brand/logo-wordmark.svg
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 56" role="img" aria-label="Peptide Repo">
|
||||||
|
<text x="0" y="40" font-family="Poppins, sans-serif" font-weight="700" font-size="36" fill="#0D0D0D" letter-spacing="-0.5">Peptide Repo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 250 B |
BIN
assets/brand/og-default.png
Executable file
|
After Width: | Height: | Size: 34 KiB |
30
assets/brand/og-default.svg
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" role="img" aria-label="Peptide Repo - What the research really says.">
|
||||||
|
<!-- Background: cream with a subtle teal-corner accent -->
|
||||||
|
<rect x="0" y="0" width="1200" height="630" fill="#FAFAF7"/>
|
||||||
|
<rect x="0" y="610" width="1200" height="20" fill="#1B8A92"/>
|
||||||
|
|
||||||
|
<!-- Logo lockup, top-left, scaled larger -->
|
||||||
|
<g transform="translate(80, 80)">
|
||||||
|
<rect x="0" y="0" width="120" height="120" rx="22" fill="#1B8A92"/>
|
||||||
|
<text x="60" y="86" text-anchor="middle" font-family="Poppins, sans-serif" font-weight="700" font-size="56" fill="#FFFFFF" letter-spacing="1.5">PR</text>
|
||||||
|
<circle cx="111" cy="111" r="20" fill="#7FD600"/>
|
||||||
|
<path d="M 102 111 l 6 6 l 12 -12" stroke="#0D0D0D" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
<text x="148" y="80" font-family="Poppins, sans-serif" font-weight="700" font-size="44" fill="#0D0D0D" letter-spacing="-0.8">Peptide Repo</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Tagline / hero copy -->
|
||||||
|
<g transform="translate(80, 320)">
|
||||||
|
<text x="0" y="0" font-family="Poppins, sans-serif" font-weight="700" font-size="84" fill="#0D0D0D" letter-spacing="-2">What the research</text>
|
||||||
|
<text x="0" y="100" font-family="Poppins, sans-serif" font-weight="700" font-size="84" fill="#1B8A92" letter-spacing="-2">really says.</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Verdict-state mini-row, bottom-right, signaling the brand's signature taxonomy -->
|
||||||
|
<g transform="translate(720, 540)" font-family="Inter, sans-serif" font-weight="600" font-size="14">
|
||||||
|
<rect x="0" y="0" width="120" height="32" rx="16" fill="#1B8A92"/>
|
||||||
|
<text x="60" y="22" text-anchor="middle" fill="#FFFFFF" letter-spacing="0.4">Established</text>
|
||||||
|
<rect x="130" y="0" width="120" height="32" rx="16" fill="#7FD600"/>
|
||||||
|
<text x="190" y="22" text-anchor="middle" fill="#0D0D0D" letter-spacing="0.4">Promising</text>
|
||||||
|
<rect x="260" y="0" width="160" height="32" rx="16" fill="#FF8A3D"/>
|
||||||
|
<text x="340" y="22" text-anchor="middle" fill="#0D0D0D" letter-spacing="0.4">Investigational</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
202
assets/css/brand.css
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/**
|
||||||
|
* Peptide Repo Brand Identity v1.0.0
|
||||||
|
* Direction C: "The Trusted Guide"
|
||||||
|
*
|
||||||
|
* Contains CSS custom property tokens and brand component styles.
|
||||||
|
* Enqueued before style.css so tokens cascade into existing theme.
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Base palette */
|
||||||
|
--color-teal: #1B8A92;
|
||||||
|
--color-ink: #0D0D0D;
|
||||||
|
--color-lime: #7FD600;
|
||||||
|
--color-orange: #FF8A3D;
|
||||||
|
--color-neutral: #D0D0D0;
|
||||||
|
--color-light: #F5F5F5;
|
||||||
|
--color-white: #FFFFFF;
|
||||||
|
|
||||||
|
/* Teal scale (primary) */
|
||||||
|
--teal-50: #EBF5F6;
|
||||||
|
--teal-100: #CEE7EA;
|
||||||
|
--teal-200: #A6D2D7;
|
||||||
|
--teal-300: #74B8C0;
|
||||||
|
--teal-400: #429DA6;
|
||||||
|
--teal-500: #1B8A92;
|
||||||
|
--teal-600: #147178;
|
||||||
|
--teal-700: #0F585E;
|
||||||
|
--teal-800: #0A3F44;
|
||||||
|
--teal-900: #072A2E;
|
||||||
|
|
||||||
|
/* Lime scale (accent — badge/button only, never body text) */
|
||||||
|
--lime-50: #F5FFE0;
|
||||||
|
--lime-100: #E8FFB8;
|
||||||
|
--lime-200: #D4FF85;
|
||||||
|
--lime-300: #BDFF4D;
|
||||||
|
--lime-400: #A5EE20;
|
||||||
|
--lime-500: #7FD600;
|
||||||
|
--lime-600: #66B000;
|
||||||
|
--lime-700: #4D8A00;
|
||||||
|
--lime-800: #365F00;
|
||||||
|
--lime-900: #1F3700;
|
||||||
|
|
||||||
|
/* Orange scale (caution) */
|
||||||
|
--orange-50: #FFF3EB;
|
||||||
|
--orange-100: #FFDEC4;
|
||||||
|
--orange-200: #FFC299;
|
||||||
|
--orange-300: #FFA66E;
|
||||||
|
--orange-400: #FF9455;
|
||||||
|
--orange-500: #FF8A3D;
|
||||||
|
--orange-600: #E46B1F;
|
||||||
|
--orange-700: #B8520F;
|
||||||
|
--orange-800: #833708;
|
||||||
|
--orange-900: #4F2003;
|
||||||
|
|
||||||
|
/* Gray scale (neutral) */
|
||||||
|
--gray-50: #FAFAFA;
|
||||||
|
--gray-100: #F5F5F5;
|
||||||
|
--gray-200: #E8E8E8;
|
||||||
|
--gray-300: #D0D0D0;
|
||||||
|
--gray-400: #A8A8A8;
|
||||||
|
--gray-500: #808080;
|
||||||
|
--gray-600: #5C5C5C;
|
||||||
|
--gray-700: #3D3D3D;
|
||||||
|
--gray-800: #1F1F1F;
|
||||||
|
--gray-900: #0D0D0D;
|
||||||
|
|
||||||
|
/* Semantic tokens */
|
||||||
|
--text-primary: var(--gray-900);
|
||||||
|
--text-secondary: var(--gray-700);
|
||||||
|
--text-muted: var(--gray-500);
|
||||||
|
--text-inverse: var(--color-white);
|
||||||
|
|
||||||
|
--bg-page: var(--color-white);
|
||||||
|
--bg-surface: var(--color-white);
|
||||||
|
--bg-inset: var(--gray-100);
|
||||||
|
--bg-brand: var(--teal-500);
|
||||||
|
--bg-brand-deep: var(--teal-700);
|
||||||
|
|
||||||
|
--border-default: var(--gray-200);
|
||||||
|
--border-strong: var(--gray-300);
|
||||||
|
--border-brand: var(--teal-500);
|
||||||
|
|
||||||
|
/* Verdict system tokens (the signature) */
|
||||||
|
--verdict-established-bg: var(--teal-500);
|
||||||
|
--verdict-established-fg: var(--color-white);
|
||||||
|
--verdict-promising-bg: var(--lime-500);
|
||||||
|
--verdict-promising-fg: var(--gray-900);
|
||||||
|
--verdict-investigational-bg: var(--orange-500);
|
||||||
|
--verdict-investigational-fg: var(--gray-900);
|
||||||
|
--verdict-insufficient-bg: var(--gray-300);
|
||||||
|
--verdict-insufficient-fg: var(--gray-700);
|
||||||
|
--verdict-cautionary-bg: #C0392B;
|
||||||
|
--verdict-cautionary-fg: var(--color-white);
|
||||||
|
|
||||||
|
/* Spacing & sizing primitives */
|
||||||
|
--radius-sm: 4px;
|
||||||
|
--radius-md: 8px;
|
||||||
|
--radius-lg: 12px;
|
||||||
|
--radius-pill: 999px;
|
||||||
|
|
||||||
|
--shadow-card: 0 1px 2px rgba(13,13,13,0.04), 0 2px 8px rgba(13,13,13,0.04);
|
||||||
|
--shadow-card-hover: 0 2px 4px rgba(13,13,13,0.06), 0 8px 16px rgba(13,13,13,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-verdict {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 14px 6px 10px;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
font-family: "Poppins", sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-verdict__glyph {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: rgba(255,255,255,0.25);
|
||||||
|
font-size: 13px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-verdict--established { background: var(--verdict-established-bg); color: var(--verdict-established-fg); }
|
||||||
|
.pr-verdict--promising { background: var(--verdict-promising-bg); color: var(--verdict-promising-fg); }
|
||||||
|
.pr-verdict--investigational { background: var(--verdict-investigational-bg); color: var(--verdict-investigational-fg); }
|
||||||
|
.pr-verdict--insufficient { background: var(--verdict-insufficient-bg); color: var(--verdict-insufficient-fg); }
|
||||||
|
.pr-verdict--cautionary { background: var(--verdict-cautionary-bg); color: var(--verdict-cautionary-fg); }
|
||||||
|
|
||||||
|
.pr-verdict--insufficient .pr-verdict__glyph { background: rgba(13,13,13,0.10); color: var(--gray-700); }
|
||||||
|
.pr-verdict--promising .pr-verdict__glyph,
|
||||||
|
.pr-verdict--investigational .pr-verdict__glyph { background: rgba(13,13,13,0.12); color: var(--gray-900); }
|
||||||
|
|
||||||
|
.pr-verdict-card {
|
||||||
|
background: var(--bg-surface);
|
||||||
|
border: 1px solid var(--border-default);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: 32px;
|
||||||
|
box-shadow: var(--shadow-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-verdict-card__header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
|
||||||
|
.pr-verdict-card__updated { font-size: 13px; color: var(--text-muted); }
|
||||||
|
.pr-verdict-card__title { font-family: "Poppins", sans-serif; font-weight: 700; font-size: 36px; line-height: 1.2; margin: 0 0 8px; color: var(--text-primary); }
|
||||||
|
.pr-verdict-card__summary { font-family: "Inter", sans-serif; font-size: 18px; line-height: 1.4; color: var(--text-secondary); margin-bottom: 24px; }
|
||||||
|
|
||||||
|
.pr-evidence-row {
|
||||||
|
background: var(--bg-inset);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-evidence-row__item { display: flex; align-items: flex-start; gap: 12px; font-family: "Inter", sans-serif; font-size: 15px; line-height: 1.5; color: var(--text-primary); }
|
||||||
|
.pr-evidence-row__item + .pr-evidence-row__item { margin-top: 10px; }
|
||||||
|
.pr-evidence-row__glyph { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; flex-shrink: 0; font-weight: 600; }
|
||||||
|
|
||||||
|
.pr-verdict-card__verdict-label { font-family: "Poppins", sans-serif; font-weight: 600; font-size: 14px; color: var(--text-secondary); margin-bottom: 8px; }
|
||||||
|
.pr-verdict-card__verdict-text { font-family: "Inter", sans-serif; font-size: 16px; line-height: 1.6; color: var(--text-primary); margin-bottom: 24px; }
|
||||||
|
.pr-verdict-card__cta-group { display: flex; gap: 16px; flex-wrap: wrap; }
|
||||||
|
.pr-verdict-card__cta-link { color: var(--teal-600); text-decoration: none; font-weight: 600; font-size: 15px; }
|
||||||
|
.pr-verdict-card__cta-link:hover { color: var(--teal-700); text-decoration: underline; }
|
||||||
|
|
||||||
|
.pr-disclosure {
|
||||||
|
background: var(--bg-inset);
|
||||||
|
border-left: 4px solid var(--teal-500);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-disclosure__label { font-family: "Poppins", sans-serif; font-weight: 600; color: var(--text-secondary); }
|
||||||
|
.pr-disclosure--inline { margin: 12px 0; }
|
||||||
|
.pr-disclosure--banner { background: var(--teal-50); border: 1px solid var(--teal-200); border-left: 4px solid var(--teal-500); padding: 20px 24px; margin-bottom: 24px; font-size: 14px; }
|
||||||
|
.pr-disclosure--footer { font-size: 13px; }
|
||||||
|
|
||||||
|
.pr-chip-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 24px; }
|
||||||
|
.pr-chip { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: 1.5px solid var(--border-default); border-radius: var(--radius-pill); background: transparent; color: var(--text-primary); text-decoration: none; font-family: "Inter", sans-serif; font-weight: 600; font-size: 14px; cursor: pointer; transition: all 0.2s ease; }
|
||||||
|
.pr-chip:hover { border-color: var(--teal-500); color: var(--teal-600); }
|
||||||
|
.pr-chip--active { background: var(--teal-500); border-color: var(--teal-500); color: var(--color-white); }
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.pr-chip { transition: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pr-verdict-card { padding: 20px; }
|
||||||
|
.pr-verdict-card__title { font-size: 28px; }
|
||||||
|
.pr-verdict-card__summary { font-size: 16px; }
|
||||||
|
}
|
||||||
BIN
favicon-16.png
Executable file
|
After Width: | Height: | Size: 481 B |
BIN
favicon-32.png
Executable file
|
After Width: | Height: | Size: 906 B |
BIN
favicon-48.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
favicon.ico
Executable file
|
After Width: | Height: | Size: 485 B |
19
footer.php
@@ -23,7 +23,7 @@
|
|||||||
<!-- Footer Grid -->
|
<!-- Footer Grid -->
|
||||||
<div class="footer-grid">
|
<div class="footer-grid">
|
||||||
<?php
|
<?php
|
||||||
for ( $i = 1; $i <= 4; $i++ ) {
|
for ( $i = 1; $i <= 5; $i++ ) {
|
||||||
echo '<div class="footer-section">';
|
echo '<div class="footer-section">';
|
||||||
if ( is_active_sidebar( 'footer-' . $i ) ) {
|
if ( is_active_sidebar( 'footer-' . $i ) ) {
|
||||||
dynamic_sidebar( 'footer-' . $i );
|
dynamic_sidebar( 'footer-' . $i );
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
// Default footer content if no widgets.
|
// Default footer content if no widgets.
|
||||||
if ( 1 === $i ) {
|
if ( 1 === $i ) {
|
||||||
echo '<h3>' . esc_html__( 'About', 'peptide-starter' ) . '</h3>';
|
echo '<h3>' . esc_html__( 'About', 'peptide-starter' ) . '</h3>';
|
||||||
echo '<p>' . esc_html__( 'Peptide Repo is a comprehensive scientific database for peptide research.', 'peptide-starter' ) . '</p>';
|
echo '<p>' . esc_html__( 'Verdict-driven peptide guides for clinicians and informed readers. Clear about the evidence — including when there isn\'t any.', 'peptide-starter' ) . '</p>';
|
||||||
} elseif ( 2 === $i ) {
|
} elseif ( 2 === $i ) {
|
||||||
echo '<h3>' . esc_html__( 'Quick Links', 'peptide-starter' ) . '</h3>';
|
echo '<h3>' . esc_html__( 'Quick Links', 'peptide-starter' ) . '</h3>';
|
||||||
echo '<ul>';
|
echo '<ul>';
|
||||||
@@ -52,6 +52,12 @@
|
|||||||
echo '<ul>';
|
echo '<ul>';
|
||||||
echo '<li><a href="https://github.com/peptiderepo" target="_blank" rel="noopener">' . esc_html__( 'GitHub', 'peptide-starter' ) . '</a></li>';
|
echo '<li><a href="https://github.com/peptiderepo" target="_blank" rel="noopener">' . esc_html__( 'GitHub', 'peptide-starter' ) . '</a></li>';
|
||||||
echo '</ul>';
|
echo '</ul>';
|
||||||
|
} elseif ( 5 === $i ) {
|
||||||
|
echo '<h3>' . esc_html__( 'Our Method', 'peptide-starter' ) . '</h3>';
|
||||||
|
echo '<ul>';
|
||||||
|
echo '<li><a href="' . esc_url( home_url( '/how-we-review-peptides' ) ) . '">' . esc_html__( 'How We Review Peptides', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '<li><a href="' . esc_url( home_url( '/about' ) ) . '">' . esc_html__( 'Editorial Standards', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '</ul>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
@@ -65,6 +71,15 @@
|
|||||||
<?php echo wp_kses_post( peptide_starter_get_footer_copyright() ); ?>
|
<?php echo wp_kses_post( peptide_starter_get_footer_copyright() ); ?>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!-- Disclosure -->
|
||||||
|
<?php
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/affiliate-disclosure',
|
||||||
|
null,
|
||||||
|
array( 'context' => 'footer' )
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- Disclaimer -->
|
<!-- Disclaimer -->
|
||||||
<p class="footer-disclaimer">
|
<p class="footer-disclaimer">
|
||||||
<?php esc_html_e( 'All data is provided for informational and educational purposes only. This platform does not provide medical advice, diagnoses, or treatment recommendations. Always consult qualified professionals.', 'peptide-starter' ); ?>
|
<?php esc_html_e( 'All data is provided for informational and educational purposes only. This platform does not provide medical advice, diagnoses, or treatment recommendations. Always consult qualified professionals.', 'peptide-starter' ); ?>
|
||||||
|
|||||||
158
front-page.php
@@ -2,21 +2,38 @@
|
|||||||
/**
|
/**
|
||||||
* The front page template
|
* The front page template
|
||||||
*
|
*
|
||||||
* Layout: Hero → Research Modules → PRAutoBlogger posts → Peptide News feed.
|
* Layout: Hero → Verdict Explainer → Featured Verdict Cards → Research Modules
|
||||||
* Newsletter signup is handled by footer.php (via template part) on the front page.
|
* → PRAutoBlogger posts → Peptide News feed.
|
||||||
*
|
*
|
||||||
* @see template-parts/module-cards.php — research modules grid
|
* Brand v2.0.0: Direction C "The Trusted Guide". Hero uses the 5-state verdict
|
||||||
* @see template-parts/newsletter-signup.php — included via footer.php on front page
|
* taxonomy as the central explainer hook. Three featured verdict cards pull from
|
||||||
* @see functions.php — helper functions for hero content
|
* real monographs (semaglutide / BPC-157 / Melanotan-II) via post meta.
|
||||||
|
*
|
||||||
|
* @see template-parts/module-cards.php — research modules grid
|
||||||
|
* @see template-parts/verdict/badge.php — verdict badge component
|
||||||
|
* @see template-parts/verdict/card.php — full verdict card (monograph hero)
|
||||||
|
* @see inc/helpers.php — hero title / subtitle getters
|
||||||
|
* @see assets/css/brand.css — verdict token definitions
|
||||||
*
|
*
|
||||||
* @package peptide-starter
|
* @package peptide-starter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
get_header();
|
get_header();
|
||||||
|
|
||||||
|
/** @see inc/verdict-helpers.php — peptide_starter_get_verdict_taxonomy() */
|
||||||
|
$verdict_taxonomy = peptide_starter_get_verdict_taxonomy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Featured monograph post IDs. These are the three exemplar monographs
|
||||||
|
* loaded during brand sprint v2.0.0 (2026-04-26).
|
||||||
|
* Semaglutide=211 (established), BPC-157=36 (investigational), Melanotan-II=177 (cautionary).
|
||||||
|
*/
|
||||||
|
$featured_monograph_ids = array( 211, 36, 177 );
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<main id="main" class="site-main">
|
<main id="main" class="site-main">
|
||||||
<!-- Hero Section -->
|
|
||||||
|
<!-- HERO SECTION -->
|
||||||
<section class="hero-section">
|
<section class="hero-section">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<h1 class="hero-title">
|
<h1 class="hero-title">
|
||||||
@@ -60,13 +77,133 @@ get_header();
|
|||||||
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-primary">
|
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-primary">
|
||||||
<?php esc_html_e( 'Browse Peptides', 'peptide-starter' ); ?>
|
<?php esc_html_e( 'Browse Peptides', 'peptide-starter' ); ?>
|
||||||
</a>
|
</a>
|
||||||
<a href="<?php echo esc_url( home_url( '/documentation' ) ); ?>" class="ps-btn ps-btn-secondary">
|
<a href="<?php echo esc_url( home_url( '/how-we-review-peptides' ) ); ?>" class="ps-btn ps-btn-secondary">
|
||||||
<?php esc_html_e( 'Documentation', 'peptide-starter' ); ?>
|
<?php esc_html_e( 'Our method', 'peptide-starter' ); ?>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- VERDICT TAXONOMY EXPLAINER -->
|
||||||
|
<section class="hero-verdict-explainer" aria-label="<?php esc_attr_e( 'Our verdict system', 'peptide-starter' ); ?>">
|
||||||
|
<div class="ps-container">
|
||||||
|
<p class="hero-verdict-explainer__heading">
|
||||||
|
<?php esc_html_e( 'Every peptide gets a verdict. Here\'s what they mean.', 'peptide-starter' ); ?>
|
||||||
|
</p>
|
||||||
|
<div class="hero-verdict-explainer__grid">
|
||||||
|
<?php foreach ( $verdict_taxonomy as $v ) : ?>
|
||||||
|
<div class="hero-verdict-explainer__item">
|
||||||
|
<span
|
||||||
|
class="pr-verdict pr-verdict--<?php echo esc_attr( $v['state'] ); ?>"
|
||||||
|
role="img"
|
||||||
|
aria-label="<?php echo esc_attr( sprintf( __( 'Verdict: %s', 'peptide-starter' ), $v['label'] ) ); ?>"
|
||||||
|
>
|
||||||
|
<span class="pr-verdict__glyph" aria-hidden="true"><?php echo esc_html( $v['glyph'] ); ?></span>
|
||||||
|
<span class="pr-verdict__label"><?php echo esc_html( $v['label'] ); ?></span>
|
||||||
|
</span>
|
||||||
|
<p class="hero-verdict-explainer__desc"><?php echo esc_html( $v['description'] ); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<p class="hero-verdict-explainer__cta-line">
|
||||||
|
<a href="<?php echo esc_url( home_url( '/how-we-review-peptides' ) ); ?>" class="ps-btn ps-btn-tertiary ps-btn-sm">
|
||||||
|
<?php esc_html_e( 'How we assign verdicts →', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FEATURED VERDICT CARDS: semaglutide=211 (Established), BPC-157=36 (Investigational), Melanotan-II=177 (Cautionary) -->
|
||||||
|
<?php
|
||||||
|
$featured_posts = get_posts(
|
||||||
|
array(
|
||||||
|
'post__in' => $featured_monograph_ids,
|
||||||
|
'post_type' => 'peptide',
|
||||||
|
'posts_per_page' => 3,
|
||||||
|
'orderby' => 'post__in',
|
||||||
|
'post_status' => 'publish',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ( ! empty( $featured_posts ) ) :
|
||||||
|
?>
|
||||||
|
<section class="hero-featured-verdicts" aria-label="<?php esc_attr_e( 'Featured peptide verdicts', 'peptide-starter' ); ?>">
|
||||||
|
<div class="ps-container">
|
||||||
|
<h2 class="hero-featured-verdicts__title"><?php esc_html_e( 'Featured verdicts', 'peptide-starter' ); ?></h2>
|
||||||
|
<p class="hero-featured-verdicts__subtitle">
|
||||||
|
<?php esc_html_e( 'Real assessments from our database — see the system in action.', 'peptide-starter' ); ?>
|
||||||
|
</p>
|
||||||
|
<div class="hero-featured-verdicts__grid">
|
||||||
|
<?php
|
||||||
|
foreach ( $featured_posts as $featured_post ) :
|
||||||
|
$post_id = $featured_post->ID;
|
||||||
|
$verdict_state = get_post_meta( $post_id, 'verdict_state', true );
|
||||||
|
$verdict_text = get_post_meta( $post_id, 'verdict_text', true );
|
||||||
|
$post_url = get_permalink( $post_id );
|
||||||
|
|
||||||
|
// Verdict state config — @see inc/verdict-helpers.php
|
||||||
|
$verdict_config = peptide_starter_get_verdict_config();
|
||||||
|
$vc = isset( $verdict_config[ $verdict_state ] ) ? $verdict_config[ $verdict_state ] : null;
|
||||||
|
|
||||||
|
// Signal rows (up to 3).
|
||||||
|
$signal_rows = array();
|
||||||
|
for ( $i = 1; $i <= 3; $i++ ) {
|
||||||
|
$row_label = get_post_meta( $post_id, 'signal_row_' . $i . '_label', true );
|
||||||
|
$row_glyph = get_post_meta( $post_id, 'signal_row_' . $i . '_glyph', true );
|
||||||
|
if ( $row_label ) {
|
||||||
|
$signal_rows[] = array(
|
||||||
|
'label' => $row_label,
|
||||||
|
'glyph' => $row_glyph ? $row_glyph : '•',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<article class="hero-verdict-card" aria-label="<?php echo esc_attr( get_the_title( $post_id ) ); ?>">
|
||||||
|
<?php if ( $vc ) : ?>
|
||||||
|
<span
|
||||||
|
class="pr-verdict pr-verdict--<?php echo esc_attr( $verdict_state ); ?> hero-verdict-card__badge"
|
||||||
|
role="img"
|
||||||
|
aria-label="<?php echo esc_attr( sprintf( __( 'Verdict: %s', 'peptide-starter' ), $vc['label'] ) ); ?>"
|
||||||
|
>
|
||||||
|
<span class="pr-verdict__glyph" aria-hidden="true"><?php echo esc_html( $vc['glyph'] ); ?></span>
|
||||||
|
<span class="pr-verdict__label"><?php echo esc_html( $vc['label'] ); ?></span>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<h3 class="hero-verdict-card__title">
|
||||||
|
<a href="<?php echo esc_url( $post_url ); ?>"><?php echo esc_html( get_the_title( $post_id ) ); ?></a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<?php if ( $verdict_text ) : ?>
|
||||||
|
<p class="hero-verdict-card__summary"><?php echo esc_html( $verdict_text ); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty( $signal_rows ) ) : ?>
|
||||||
|
<ul class="hero-verdict-card__signals" aria-label="<?php esc_attr_e( 'Evidence signals', 'peptide-starter' ); ?>">
|
||||||
|
<?php foreach ( $signal_rows as $row ) : ?>
|
||||||
|
<li class="hero-verdict-card__signal-item">
|
||||||
|
<span class="hero-verdict-card__signal-glyph" aria-hidden="true"><?php echo esc_html( $row['glyph'] ); ?></span>
|
||||||
|
<?php echo esc_html( $row['label'] ); ?>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<a href="<?php echo esc_url( $post_url ); ?>" class="hero-verdict-card__link">
|
||||||
|
<?php esc_html_e( 'Read the full evidence →', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-featured-verdicts__footer">
|
||||||
|
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-secondary">
|
||||||
|
<?php esc_html_e( 'Browse all peptides', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- Research Modules Grid (6 cards) -->
|
<!-- Research Modules Grid (6 cards) -->
|
||||||
<?php get_template_part( 'template-parts/module', 'cards' ); ?>
|
<?php get_template_part( 'template-parts/module', 'cards' ); ?>
|
||||||
|
|
||||||
@@ -84,9 +221,9 @@ get_header();
|
|||||||
<section class="news-feed-section">
|
<section class="news-feed-section">
|
||||||
<div class="ps-container">
|
<div class="ps-container">
|
||||||
<div class="news-feed-header">
|
<div class="news-feed-header">
|
||||||
<h2 class="news-feed-title"><?php esc_html_e( 'Latest Peptide Research', 'peptide-starter' ); ?></h2>
|
<h2 class="news-feed-title"><?php esc_html_e( 'What\'s new', 'peptide-starter' ); ?></h2>
|
||||||
<p class="news-feed-description">
|
<p class="news-feed-description">
|
||||||
<?php esc_html_e( 'Stay updated with the latest developments in peptide science and research.', 'peptide-starter' ); ?>
|
<?php esc_html_e( 'Recent peptide research, regulatory shifts, and synthesis news. New posts weekly.', 'peptide-starter' ); ?>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<?php echo do_shortcode( '[peptide_news]' ); ?>
|
<?php echo do_shortcode( '[peptide_news]' ); ?>
|
||||||
@@ -95,6 +232,7 @@ get_header();
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<?php get_footer(); ?>
|
<?php get_footer(); ?>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Theme constants.
|
// Theme constants.
|
||||||
define( 'PEPTIDE_STARTER_VERSION', '1.6.0' );
|
define( 'PEPTIDE_STARTER_VERSION', '2.0.0-alpha.3' );
|
||||||
define( 'PEPTIDE_STARTER_DIR', get_template_directory() );
|
define( 'PEPTIDE_STARTER_DIR', get_template_directory() );
|
||||||
define( 'PEPTIDE_STARTER_URI', get_template_directory_uri() );
|
define( 'PEPTIDE_STARTER_URI', get_template_directory_uri() );
|
||||||
|
|
||||||
@@ -44,6 +44,8 @@ require_once PEPTIDE_STARTER_DIR . '/inc/page-setup.php';
|
|||||||
require_once PEPTIDE_STARTER_DIR . '/inc/newsletter-admin.php';
|
require_once PEPTIDE_STARTER_DIR . '/inc/newsletter-admin.php';
|
||||||
require_once PEPTIDE_STARTER_DIR . '/inc/mail-diagnostic.php';
|
require_once PEPTIDE_STARTER_DIR . '/inc/mail-diagnostic.php';
|
||||||
require_once PEPTIDE_STARTER_DIR . '/inc/perf-asset-policy.php';
|
require_once PEPTIDE_STARTER_DIR . '/inc/perf-asset-policy.php';
|
||||||
|
require_once PEPTIDE_STARTER_DIR . '/inc/verdict-meta.php';
|
||||||
|
require_once PEPTIDE_STARTER_DIR . '/inc/verdict-helpers.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up theme defaults and register support for WordPress features.
|
* Set up theme defaults and register support for WordPress features.
|
||||||
@@ -79,6 +81,7 @@ add_action( 'after_setup_theme', 'peptide_starter_perf_asset_policy_init' );
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function peptide_starter_scripts() {
|
function peptide_starter_scripts() {
|
||||||
|
wp_enqueue_style( 'peptide-starter-brand', PEPTIDE_STARTER_URI . '/assets/css/brand.css', array(), PEPTIDE_STARTER_VERSION );
|
||||||
wp_enqueue_style( 'peptide-starter-style', PEPTIDE_STARTER_URI . '/style.css', array(), PEPTIDE_STARTER_VERSION );
|
wp_enqueue_style( 'peptide-starter-style', PEPTIDE_STARTER_URI . '/style.css', array(), PEPTIDE_STARTER_VERSION );
|
||||||
|
|
||||||
wp_enqueue_script( 'peptide-starter-navigation', PEPTIDE_STARTER_URI . '/assets/js/navigation.js', array(), PEPTIDE_STARTER_VERSION, true );
|
wp_enqueue_script( 'peptide-starter-navigation', PEPTIDE_STARTER_URI . '/assets/js/navigation.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||||
@@ -137,7 +140,7 @@ add_action( 'wp_enqueue_scripts', 'peptide_starter_plugin_overrides', 99 );
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function peptide_starter_widgets_init() {
|
function peptide_starter_widgets_init() {
|
||||||
for ( $i = 1; $i <= 4; $i++ ) {
|
for ( $i = 1; $i <= 5; $i++ ) {
|
||||||
register_sidebar(
|
register_sidebar(
|
||||||
array(
|
array(
|
||||||
/* translators: %d: footer column number */
|
/* translators: %d: footer column number */
|
||||||
|
|||||||
25
header.php
@@ -20,6 +20,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="profile" href="https://gmpg.org/xfn/11">
|
<link rel="profile" href="https://gmpg.org/xfn/11">
|
||||||
<?php wp_head(); ?>
|
<?php wp_head(); ?>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Poppins:wght@600;700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Poppins:wght@600;700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
||||||
|
<link rel="icon" type="image/x-icon" href="<?php echo get_stylesheet_directory_uri(); ?>/favicon.ico">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="<?php echo get_stylesheet_directory_uri(); ?>/assets/brand/favicon-16.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="<?php echo get_stylesheet_directory_uri(); ?>/assets/brand/favicon-32.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="<?php echo get_stylesheet_directory_uri(); ?>/apple-touch-icon.png">
|
||||||
|
<link rel="manifest" href="<?php echo get_stylesheet_directory_uri(); ?>/site.webmanifest">
|
||||||
|
<meta name="theme-color" content="#1B8A92">
|
||||||
|
<?php if ( ! function_exists( 'wp_is_block_theme' ) || ! wp_is_block_theme() ) : ?>
|
||||||
|
<meta property="og:image" content="<?php echo esc_url( get_stylesheet_directory_uri() . '/assets/brand/og-default.png' ); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body <?php body_class(); ?>>
|
<body <?php body_class(); ?>>
|
||||||
@@ -105,7 +118,8 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Dark Mode Toggle -->
|
<!-- Dark Mode Toggle -->
|
||||||
<button class="header-icon-btn dark-mode-toggle" aria-label="<?php esc_attr_e( 'Toggle dark mode', 'peptide-starter' ); ?>">
|
<!-- Dark mode disabled for v1: verdict-color accessibility only validated for light mode. Re-enable in v1.1 after dark palette audit. -->
|
||||||
|
<button class="header-icon-btn dark-mode-toggle" aria-label="<?php esc_attr_e( 'Toggle dark mode', 'peptide-starter' ); ?>" style="display:none;" aria-hidden="true">
|
||||||
<svg class="sun-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
<svg class="sun-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
<circle cx="10" cy="10" r="4" stroke="currentColor" stroke-width="2"/>
|
<circle cx="10" cy="10" r="4" stroke="currentColor" stroke-width="2"/>
|
||||||
<path d="M10 1V3M10 17V19M19 10H17M3 10H1M16.657 16.657L15.243 15.243M4.757 4.757L3.343 3.343M16.657 3.343L15.243 4.757M4.757 15.243L3.343 16.657" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
<path d="M10 1V3M10 17V19M19 10H17M3 10H1M16.657 16.657L15.243 15.243M4.757 4.757L3.343 3.343M16.657 3.343L15.243 4.757M4.757 15.243L3.343 16.657" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
@@ -115,15 +129,6 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Cart Icon (WooCommerce) -->
|
|
||||||
<?php if ( class_exists( 'WooCommerce' ) ) { ?>
|
|
||||||
<button class="header-icon-btn cart-toggle" aria-label="<?php esc_attr_e( 'View cart', 'peptide-starter' ); ?>">
|
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M1 1H3.27924M3.27924 1L5.20081 13.5631C5.35072 14.4571 5.80765 15.2614 6.4543 15.8285C7.10095 16.3956 7.92274 16.6882 8.76721 16.6882H16.7257M16.7257 16.6882C17.3169 16.6882 17.7793 17.1506 17.7793 17.7418C17.7793 18.333 17.3169 18.7955 16.7257 18.7955M16.7257 16.6882C15.6515 16.6882 14.4632 16.6882 8.76721 16.6882M8.76721 16.6882C8.17601 16.6882 7.71365 17.1506 7.71365 17.7418C7.71365 18.333 8.17601 18.7955 8.76721 18.7955M5.20081 13.5631H16.7257" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<!-- Mobile Menu Toggle -->
|
<!-- Mobile Menu Toggle -->
|
||||||
<button class="menu-toggle" aria-label="<?php esc_attr_e( 'Toggle menu', 'peptide-starter' ); ?>">
|
<button class="menu-toggle" aria-label="<?php esc_attr_e( 'Toggle menu', 'peptide-starter' ); ?>">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
BIN
icon-192.png
Executable file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
icon-512.png
Executable file
|
After Width: | Height: | Size: 15 KiB |
@@ -136,6 +136,15 @@ function peptide_starter_the_custom_logo() {
|
|||||||
the_custom_logo();
|
the_custom_logo();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Use SVG logo if available
|
||||||
|
$logo_path = get_template_directory() . '/assets/brand/logo-horizontal.svg';
|
||||||
|
if ( file_exists( $logo_path ) ) {
|
||||||
|
echo '<a class="site-logo" href="' . esc_url( home_url( '/' ) ) . '" rel="home" aria-label="' . esc_attr( get_bloginfo( 'name' ) ) . '">';
|
||||||
|
include $logo_path;
|
||||||
|
echo '</a>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Fallback to text
|
||||||
$site_name = get_bloginfo( 'name' );
|
$site_name = get_bloginfo( 'name' );
|
||||||
echo '<a class="site-logo" href="' . esc_url( home_url( '/' ) ) . '" rel="home">' . esc_html( $site_name ) . '</a>';
|
echo '<a class="site-logo" href="' . esc_url( home_url( '/' ) ) . '" rel="home">' . esc_html( $site_name ) . '</a>';
|
||||||
}
|
}
|
||||||
@@ -146,7 +155,7 @@ function peptide_starter_the_custom_logo() {
|
|||||||
* @return string Hero title text.
|
* @return string Hero title text.
|
||||||
*/
|
*/
|
||||||
function peptide_starter_get_hero_title() {
|
function peptide_starter_get_hero_title() {
|
||||||
return get_theme_mod( 'hero_title', get_bloginfo( 'name' ) );
|
return get_theme_mod( 'hero_title', esc_html__( 'Is this peptide right for you?', 'peptide-starter' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,7 +164,7 @@ function peptide_starter_get_hero_title() {
|
|||||||
* @return string Hero subtitle text.
|
* @return string Hero subtitle text.
|
||||||
*/
|
*/
|
||||||
function peptide_starter_get_hero_subtitle() {
|
function peptide_starter_get_hero_subtitle() {
|
||||||
return get_theme_mod( 'hero_subtitle', esc_html__( 'A scientific peptide reference database', 'peptide-starter' ) );
|
return get_theme_mod( 'hero_subtitle', esc_html__( 'What the research really says', 'peptide-starter' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Optimizes the front-end asset graph by dequeuing unnecessary plugin styles/scripts
|
* Optimizes the front-end asset graph by dequeuing unnecessary plugin styles/scripts
|
||||||
* and slimming font requests. Targets mobile LCP improvement through:
|
* and slimming font requests. Targets mobile LCP improvement through:
|
||||||
* - Conditional dequeue of WC, Elementor, and USMI assets
|
* - Conditional dequeue of Elementor and USMI assets
|
||||||
* - Google Fonts weight slimming (36 faces → 5)
|
* - Google Fonts weight slimming (36 faces → 5)
|
||||||
* - Preconnect hints for font.googleapis.com + fonts.gstatic.com
|
* - Preconnect hints for font.googleapis.com + fonts.gstatic.com
|
||||||
* - Defer attribute on cookie-notice script
|
* - Defer attribute on cookie-notice script
|
||||||
@@ -49,11 +49,10 @@ function peptide_starter_perf_asset_policy_init() {
|
|||||||
/**
|
/**
|
||||||
* Dequeue assets on non-applicable page types.
|
* Dequeue assets on non-applicable page types.
|
||||||
*
|
*
|
||||||
* Removes WooCommerce, Elementor, and Ultimate Social Media Icons assets from pages
|
* Removes Elementor and Ultimate Social Media Icons assets from pages
|
||||||
* that don't use them. Controlled by the kill-switch constant PEPTIDE_STARTER_PERF_DEQUEUE.
|
* that don't use them. Controlled by the kill-switch constant PEPTIDE_STARTER_PERF_DEQUEUE.
|
||||||
*
|
*
|
||||||
* Dequeue conditions:
|
* Dequeue conditions:
|
||||||
* - WC: removed unless on shop, cart, checkout, or account pages
|
|
||||||
* - Elementor: removed if page doesn't use Elementor builder
|
* - Elementor: removed if page doesn't use Elementor builder
|
||||||
* - USMI: removed on homepage, archives, and blog index
|
* - USMI: removed on homepage, archives, and blog index
|
||||||
*
|
*
|
||||||
@@ -65,28 +64,6 @@ function peptide_starter_perf_dequeue_plugin_assets() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WooCommerce assets: dequeue on non-shop pages.
|
|
||||||
if ( function_exists( 'is_woocommerce' ) && ! is_woocommerce() && ! is_cart() && ! is_checkout() && ! is_account_page() ) {
|
|
||||||
// Get configured WC handles from filter; default list.
|
|
||||||
$wc_styles = apply_filters(
|
|
||||||
'peptide_starter_perf_woocommerce_styles',
|
|
||||||
array( 'woocommerce-layout', 'woocommerce-smallscreen', 'woocommerce-general' )
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ( $wc_styles as $handle ) {
|
|
||||||
wp_dequeue_style( $handle );
|
|
||||||
}
|
|
||||||
|
|
||||||
$wc_scripts = apply_filters(
|
|
||||||
'peptide_starter_perf_woocommerce_scripts',
|
|
||||||
array( 'wc-add-to-cart', 'woocommerce', 'sourcebuster-js', 'wc-order-attribution' )
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ( $wc_scripts as $handle ) {
|
|
||||||
wp_dequeue_script( $handle );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elementor assets: dequeue if page doesn't use Elementor.
|
// Elementor assets: dequeue if page doesn't use Elementor.
|
||||||
if ( ! peptide_starter_page_uses_elementor( get_queried_object_id() ) ) {
|
if ( ! peptide_starter_page_uses_elementor( get_queried_object_id() ) ) {
|
||||||
$elementor_handles = apply_filters(
|
$elementor_handles = apply_filters(
|
||||||
|
|||||||
90
inc/verdict-helpers.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Verdict helper functions for homepage and template parts.
|
||||||
|
*
|
||||||
|
* What: Provides data arrays for the 5-state verdict taxonomy used in the
|
||||||
|
* homepage explainer strip and featured verdict cards.
|
||||||
|
* Who triggers: Required by functions.php on every page load.
|
||||||
|
* Depends on: Nothing — pure data functions, no WP dependencies.
|
||||||
|
*
|
||||||
|
* @see inc/verdict-meta.php — verdict CPT meta registration
|
||||||
|
* @see template-parts/verdict/ — badge, card, evidence-row components
|
||||||
|
* @see front-page.php — homepage verdict sections
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the verdict taxonomy data array for the homepage explainer strip.
|
||||||
|
*
|
||||||
|
* Canonical order matches CMO brief §5.1 and the 5-state enum in verdict-meta.php.
|
||||||
|
*
|
||||||
|
* @return array[] Each element: state (string), glyph (string), label (string), description (string).
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_verdict_taxonomy() {
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'state' => 'established',
|
||||||
|
'glyph' => '✓',
|
||||||
|
'label' => 'Established',
|
||||||
|
'description' => 'Strong RCT evidence; widely prescribed or validated.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'state' => 'promising',
|
||||||
|
'glyph' => '◐',
|
||||||
|
'label' => 'Promising',
|
||||||
|
'description' => 'Meaningful human data accumulating; not yet RCT-strong.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'state' => 'investigational',
|
||||||
|
'glyph' => '?',
|
||||||
|
'label' => 'Investigational',
|
||||||
|
'description' => 'Mechanism plausible; human trials sparse.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'state' => 'insufficient',
|
||||||
|
'glyph' => '⊘',
|
||||||
|
'label' => 'Insufficient Evidence',
|
||||||
|
'description' => 'Not enough data to say anything responsible.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'state' => 'cautionary',
|
||||||
|
'glyph' => '⚠',
|
||||||
|
'label' => 'Cautionary',
|
||||||
|
'description' => 'Active safety signals or misuse patterns.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the verdict state config map (glyph + label per state slug).
|
||||||
|
*
|
||||||
|
* Used by featured verdict cards on the homepage and any template that needs
|
||||||
|
* to render a verdict badge from a state string without loading the full card.
|
||||||
|
*
|
||||||
|
* @return array Keyed by state slug. Each value: array{ glyph: string, label: string }.
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_verdict_config() {
|
||||||
|
return array(
|
||||||
|
'established' => array(
|
||||||
|
'glyph' => '✓',
|
||||||
|
'label' => 'Established',
|
||||||
|
),
|
||||||
|
'promising' => array(
|
||||||
|
'glyph' => '◐',
|
||||||
|
'label' => 'Promising',
|
||||||
|
),
|
||||||
|
'investigational' => array(
|
||||||
|
'glyph' => '?',
|
||||||
|
'label' => 'Investigational',
|
||||||
|
),
|
||||||
|
'insufficient' => array(
|
||||||
|
'glyph' => '⊘',
|
||||||
|
'label' => 'Insufficient Evidence',
|
||||||
|
),
|
||||||
|
'cautionary' => array(
|
||||||
|
'glyph' => '⚠',
|
||||||
|
'label' => 'Cautionary',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
300
inc/verdict-meta.php
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Verdict System Post Meta Registration & Meta Box
|
||||||
|
*
|
||||||
|
* Registers post meta for peptide CPT:
|
||||||
|
* - verdict_state (required): one of 5 states
|
||||||
|
* - verdict_text (required for launch): 3–5 sentence opinionated verdict
|
||||||
|
* - signal_row_1/2/3 (optional): evidence signal label text
|
||||||
|
* - signal_row_1/2/3_glyph (optional): per-row glyph; enum ✓|!|?|⊘|⚠
|
||||||
|
* - has_partner_link (bool, default false): controls inline disclosure render
|
||||||
|
*
|
||||||
|
* Who calls it: loaded by functions.php via require_once.
|
||||||
|
* Depends on: nothing — pure WP register_post_meta.
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register verdict post meta for peptide CPT.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function peptide_starter_register_verdict_meta() {
|
||||||
|
$allowed_states = array( 'established', 'promising', 'investigational', 'insufficient', 'cautionary' );
|
||||||
|
$allowed_glyphs = array( '✓', '!', '?', '⊘', '⚠' );
|
||||||
|
|
||||||
|
// Register verdict_state meta.
|
||||||
|
register_post_meta(
|
||||||
|
'peptide',
|
||||||
|
'verdict_state',
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'single' => true,
|
||||||
|
'show_in_rest' => true,
|
||||||
|
'auth_callback' => '__return_true',
|
||||||
|
'sanitize_callback' => function ( $value ) use ( $allowed_states ) {
|
||||||
|
return in_array( $value, $allowed_states, true ) ? $value : '';
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register verdict_text meta — 3–5 sentence opinionated verdict paragraph.
|
||||||
|
register_post_meta(
|
||||||
|
'peptide',
|
||||||
|
'verdict_text',
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'single' => true,
|
||||||
|
'show_in_rest' => true,
|
||||||
|
'auth_callback' => '__return_true',
|
||||||
|
'sanitize_callback' => 'sanitize_textarea_field',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register signal row text meta (up to 3 rows).
|
||||||
|
for ( $i = 1; $i <= 3; $i++ ) {
|
||||||
|
register_post_meta(
|
||||||
|
'peptide',
|
||||||
|
'signal_row_' . $i,
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'single' => true,
|
||||||
|
'show_in_rest' => true,
|
||||||
|
'auth_callback' => '__return_true',
|
||||||
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register per-row glyph meta.
|
||||||
|
// Validated as one of the 5-state glyph set to keep visual vocabulary consistent.
|
||||||
|
register_post_meta(
|
||||||
|
'peptide',
|
||||||
|
'signal_row_' . $i . '_glyph',
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'single' => true,
|
||||||
|
'show_in_rest' => true,
|
||||||
|
'auth_callback' => '__return_true',
|
||||||
|
'sanitize_callback' => function ( $value ) use ( $allowed_glyphs ) {
|
||||||
|
return ( '' === $value || in_array( $value, $allowed_glyphs, true ) ) ? $value : '';
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register has_partner_link — gates inline affiliate disclosure rendering.
|
||||||
|
// Default false: disclosure is suppressed unless a partner link is explicitly present.
|
||||||
|
register_post_meta(
|
||||||
|
'peptide',
|
||||||
|
'has_partner_link',
|
||||||
|
array(
|
||||||
|
'type' => 'boolean',
|
||||||
|
'single' => true,
|
||||||
|
'show_in_rest' => true,
|
||||||
|
'auth_callback' => '__return_true',
|
||||||
|
'default' => false,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action( 'init', 'peptide_starter_register_verdict_meta' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add verdict meta box to peptide CPT edit screen.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function peptide_starter_add_verdict_meta_box() {
|
||||||
|
add_meta_box(
|
||||||
|
'peptide_starter_verdict',
|
||||||
|
esc_html__( 'Verdict System', 'peptide-starter' ),
|
||||||
|
'peptide_starter_render_verdict_meta_box',
|
||||||
|
'peptide',
|
||||||
|
'normal',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action( 'add_meta_boxes', 'peptide_starter_add_verdict_meta_box' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render verdict meta box HTML.
|
||||||
|
*
|
||||||
|
* @param WP_Post $post The post object.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function peptide_starter_render_verdict_meta_box( $post ) {
|
||||||
|
wp_nonce_field( 'peptide_starter_verdict_nonce', 'peptide_starter_verdict_nonce' );
|
||||||
|
|
||||||
|
$verdict_state = get_post_meta( $post->ID, 'verdict_state', true );
|
||||||
|
$verdict_text = get_post_meta( $post->ID, 'verdict_text', true );
|
||||||
|
$allowed_states = array(
|
||||||
|
'established' => esc_html__( 'Established', 'peptide-starter' ),
|
||||||
|
'promising' => esc_html__( 'Promising', 'peptide-starter' ),
|
||||||
|
'investigational' => esc_html__( 'Investigational', 'peptide-starter' ),
|
||||||
|
'insufficient' => esc_html__( 'Insufficient Evidence', 'peptide-starter' ),
|
||||||
|
'cautionary' => esc_html__( 'Cautionary', 'peptide-starter' ),
|
||||||
|
);
|
||||||
|
$allowed_glyphs = array( '✓', '!', '?', '⊘', '⚠' );
|
||||||
|
$has_partner = (bool) get_post_meta( $post->ID, 'has_partner_link', true );
|
||||||
|
?>
|
||||||
|
<div style="padding:12px 0;">
|
||||||
|
|
||||||
|
<label for="verdict_state" style="display:block;margin-bottom:6px;">
|
||||||
|
<strong><?php esc_html_e( 'Verdict State', 'peptide-starter' ); ?></strong>
|
||||||
|
</label>
|
||||||
|
<select id="verdict_state" name="verdict_state" style="width:100%;padding:8px;font-size:14px;margin-bottom:16px;">
|
||||||
|
<option value=""><?php esc_html_e( '— Select —', 'peptide-starter' ); ?></option>
|
||||||
|
<?php foreach ( $allowed_states as $key => $label ) : ?>
|
||||||
|
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $verdict_state, $key ); ?>>
|
||||||
|
<?php echo esc_html( $label ); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="verdict_text" style="display:block;margin-bottom:6px;">
|
||||||
|
<strong><?php esc_html_e( 'Our Verdict (3–5 sentences)', 'peptide-starter' ); ?></strong>
|
||||||
|
<span style="font-weight:normal;font-size:12px;color:#666;display:block;margin-top:2px;">
|
||||||
|
<?php esc_html_e( 'The opinionated verdict paragraph shown on the card hero. Be specific — this is the brand\'s editorial spine.', 'peptide-starter' ); ?>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<textarea id="verdict_text" name="verdict_text" rows="5"
|
||||||
|
style="width:100%;padding:8px;font-size:14px;margin-bottom:16px;"
|
||||||
|
placeholder="<?php esc_attr_e( 'e.g., Semaglutide works. The data is among the strongest of any peptide therapy...', 'peptide-starter' ); ?>"><?php echo esc_textarea( $verdict_text ); ?></textarea>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid #ddd;margin:8px 0 16px;">
|
||||||
|
<p style="font-weight:bold;margin-bottom:8px;"><?php esc_html_e( 'Evidence Signal Rows', 'peptide-starter' ); ?></p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
for ( $i = 1; $i <= 3; $i++ ) :
|
||||||
|
$row_text = get_post_meta( $post->ID, 'signal_row_' . $i, true );
|
||||||
|
$row_glyph = get_post_meta( $post->ID, 'signal_row_' . $i . '_glyph', true );
|
||||||
|
?>
|
||||||
|
<div style="display:flex;gap:8px;align-items:flex-start;margin-bottom:12px;">
|
||||||
|
<div style="flex:0 0 80px;">
|
||||||
|
<label for="signal_row_<?php echo esc_attr( $i ); ?>_glyph"
|
||||||
|
style="display:block;margin-bottom:4px;font-size:12px;color:#444;">
|
||||||
|
<?php esc_html_e( 'Glyph', 'peptide-starter' ); ?>
|
||||||
|
</label>
|
||||||
|
<select id="signal_row_<?php echo esc_attr( $i ); ?>_glyph"
|
||||||
|
name="signal_row_<?php echo esc_attr( $i ); ?>_glyph"
|
||||||
|
style="width:100%;padding:6px;font-size:16px;">
|
||||||
|
<option value="" <?php selected( $row_glyph, '' ); ?>>—</option>
|
||||||
|
<?php foreach ( $allowed_glyphs as $g ) : ?>
|
||||||
|
<option value="<?php echo esc_attr( $g ); ?>" <?php selected( $row_glyph, $g ); ?>>
|
||||||
|
<?php echo esc_html( $g ); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="flex:1;">
|
||||||
|
<label for="signal_row_<?php echo esc_attr( $i ); ?>"
|
||||||
|
style="display:block;margin-bottom:4px;font-size:12px;color:#444;">
|
||||||
|
<?php
|
||||||
|
/* translators: %d is the row number */
|
||||||
|
echo esc_html( sprintf( __( 'Row %d label', 'peptide-starter' ), $i ) );
|
||||||
|
?>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="signal_row_<?php echo esc_attr( $i ); ?>"
|
||||||
|
name="signal_row_<?php echo esc_attr( $i ); ?>"
|
||||||
|
value="<?php echo esc_attr( $row_text ); ?>"
|
||||||
|
style="width:100%;padding:8px;font-size:14px;"
|
||||||
|
placeholder="<?php esc_attr_e( 'Evidence signal label', 'peptide-starter' ); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endfor; ?>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid #ddd;margin:8px 0 16px;">
|
||||||
|
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
|
||||||
|
<input type="checkbox" name="has_partner_link" value="1"
|
||||||
|
<?php checked( $has_partner, true ); ?> style="width:16px;height:16px;">
|
||||||
|
<span>
|
||||||
|
<strong><?php esc_html_e( 'Has active partner link', 'peptide-starter' ); ?></strong>
|
||||||
|
<span style="display:block;font-size:12px;color:#666;margin-top:2px;">
|
||||||
|
<?php esc_html_e( 'Check this when the monograph contains an affiliate partner link. Controls the inline disclosure strip.', 'peptide-starter' ); ?>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save verdict meta on post save.
|
||||||
|
*
|
||||||
|
* @param int $post_id The post ID.
|
||||||
|
* @param WP_Post $post The post object.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function peptide_starter_save_verdict_meta( $post_id, $post ) {
|
||||||
|
// Verify nonce.
|
||||||
|
if ( ! isset( $_POST['peptide_starter_verdict_nonce'] )
|
||||||
|
|| ! wp_verify_nonce( sanitize_key( $_POST['peptide_starter_verdict_nonce'] ), 'peptide_starter_verdict_nonce' )
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only run for peptide CPT.
|
||||||
|
if ( 'peptide' !== $post->post_type ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed_states = array( 'established', 'promising', 'investigational', 'insufficient', 'cautionary' );
|
||||||
|
$allowed_glyphs = array( '✓', '!', '?', '⊘', '⚠' );
|
||||||
|
|
||||||
|
// Save verdict_state.
|
||||||
|
if ( isset( $_POST['verdict_state'] ) ) {
|
||||||
|
$state = sanitize_text_field( wp_unslash( $_POST['verdict_state'] ) );
|
||||||
|
if ( in_array( $state, $allowed_states, true ) ) {
|
||||||
|
update_post_meta( $post_id, 'verdict_state', $state );
|
||||||
|
} else {
|
||||||
|
delete_post_meta( $post_id, 'verdict_state' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save verdict_text.
|
||||||
|
if ( isset( $_POST['verdict_text'] ) ) {
|
||||||
|
$text = sanitize_textarea_field( wp_unslash( $_POST['verdict_text'] ) );
|
||||||
|
if ( ! empty( $text ) ) {
|
||||||
|
update_post_meta( $post_id, 'verdict_text', $text );
|
||||||
|
} else {
|
||||||
|
delete_post_meta( $post_id, 'verdict_text' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save signal rows and their per-row glyphs.
|
||||||
|
for ( $i = 1; $i <= 3; $i++ ) {
|
||||||
|
$field_name = 'signal_row_' . $i;
|
||||||
|
if ( isset( $_POST[ $field_name ] ) ) {
|
||||||
|
$value = sanitize_text_field( wp_unslash( $_POST[ $field_name ] ) );
|
||||||
|
if ( ! empty( $value ) ) {
|
||||||
|
update_post_meta( $post_id, $field_name, $value );
|
||||||
|
} else {
|
||||||
|
delete_post_meta( $post_id, $field_name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$glyph_field = 'signal_row_' . $i . '_glyph';
|
||||||
|
if ( isset( $_POST[ $glyph_field ] ) ) {
|
||||||
|
$glyph = wp_unslash( $_POST[ $glyph_field ] );
|
||||||
|
if ( in_array( $glyph, $allowed_glyphs, true ) ) {
|
||||||
|
update_post_meta( $post_id, $glyph_field, $glyph );
|
||||||
|
} else {
|
||||||
|
delete_post_meta( $post_id, $glyph_field );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save has_partner_link (checkbox — present = true, absent = false).
|
||||||
|
$has_partner = isset( $_POST['has_partner_link'] ) && '1' === $_POST['has_partner_link'];
|
||||||
|
update_post_meta( $post_id, 'has_partner_link', $has_partner ? '1' : '0' );
|
||||||
|
}
|
||||||
|
add_action( 'save_post', 'peptide_starter_save_verdict_meta', 10, 2 );
|
||||||
BIN
og-default.png
Executable file
|
After Width: | Height: | Size: 34 KiB |
46
page-about.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template Name: About & Editorial Standards
|
||||||
|
*
|
||||||
|
* Editorial standards, disclosure, and about page template.
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<div class="page-about">
|
||||||
|
<?php
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
?>
|
||||||
|
<article id="post-<?php the_ID(); ?>" <?php post_class( 'entry-about' ); ?>>
|
||||||
|
<header class="entry-header">
|
||||||
|
<h1 class="entry-title"><?php the_title(); ?></h1>
|
||||||
|
</header>
|
||||||
|
<div class="entry-content">
|
||||||
|
<?php the_content(); ?>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!-- Affiliate Disclosure Block -->
|
||||||
|
<section class="page-about__disclosures">
|
||||||
|
<h2><?php esc_html_e( 'How We Make Money', 'peptide-starter' ); ?></h2>
|
||||||
|
<?php
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/affiliate-disclosure',
|
||||||
|
null,
|
||||||
|
array( 'context' => 'banner' )
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
get_footer();
|
||||||
@@ -33,8 +33,8 @@ get_header();
|
|||||||
<section class="ps-tool-page">
|
<section class="ps-tool-page">
|
||||||
<div class="ps-container">
|
<div class="ps-container">
|
||||||
<?php
|
<?php
|
||||||
if ( shortcode_exists( 'peptide_tools_calculator' ) ) {
|
if ( shortcode_exists( 'prc_calculator' ) ) {
|
||||||
echo do_shortcode( '[peptide_tools_calculator]' );
|
echo do_shortcode( '[prc_calculator]' );
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<div class="ps-tool-fallback">
|
<div class="ps-tool-fallback">
|
||||||
|
|||||||
45
page-how-we-review.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template Name: How We Review Peptides
|
||||||
|
*
|
||||||
|
* Two-column layout with sticky sidebar ToC for the verdict system explainer.
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<div class="page-how-we-review">
|
||||||
|
<div class="page-how-we-review__content">
|
||||||
|
<?php
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
?>
|
||||||
|
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||||
|
<header class="entry-header">
|
||||||
|
<h1 class="entry-title"><?php the_title(); ?></h1>
|
||||||
|
</header>
|
||||||
|
<div class="entry-content">
|
||||||
|
<?php the_content(); ?>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ( ! empty( get_the_content() ) ) : ?>
|
||||||
|
<aside class="page-how-we-review__sidebar">
|
||||||
|
<nav class="page-toc" aria-label="<?php esc_attr_e( 'Page contents', 'peptide-starter' ); ?>">
|
||||||
|
<h3><?php esc_html_e( 'Contents', 'peptide-starter' ); ?></h3>
|
||||||
|
<!-- JavaScript-generated ToC will be inserted here -->
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
get_footer();
|
||||||
@@ -2,8 +2,18 @@
|
|||||||
/**
|
/**
|
||||||
* The single peptide template
|
* The single peptide template
|
||||||
*
|
*
|
||||||
* Displays a single peptide with back link, content box,
|
* Displays a single peptide with verdict card hero, content, affiliate
|
||||||
* and meta information in a card grid layout.
|
* disclosure, and meta properties in a card grid layout.
|
||||||
|
*
|
||||||
|
* When a verdict_state is set on the post, the verdict card owns the <h1>
|
||||||
|
* title so the page-header section only renders the back-link and category
|
||||||
|
* pills (no duplicate heading). When no verdict state exists the classic <h1>
|
||||||
|
* renders in the header for backwards compatibility with non-monograph posts.
|
||||||
|
*
|
||||||
|
* Who calls it: WordPress template hierarchy for the pr_peptide CPT.
|
||||||
|
* Depends on: inc/verdict-meta.php (post meta registration),
|
||||||
|
* template-parts/verdict/card.php,
|
||||||
|
* template-parts/affiliate-disclosure.php.
|
||||||
*
|
*
|
||||||
* @package peptide-starter
|
* @package peptide-starter
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +26,9 @@ get_header();
|
|||||||
if ( have_posts() ) {
|
if ( have_posts() ) {
|
||||||
while ( have_posts() ) {
|
while ( have_posts() ) {
|
||||||
the_post();
|
the_post();
|
||||||
|
|
||||||
|
// Determine verdict state once — used in two places below.
|
||||||
|
$verdict_state = get_post_meta( get_the_ID(), 'verdict_state', true );
|
||||||
?>
|
?>
|
||||||
<!-- Page Header -->
|
<!-- Page Header -->
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
@@ -28,7 +41,7 @@ get_header();
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// Show featured badge if the peptide is marked as featured
|
// Show featured badge if the peptide is marked as featured.
|
||||||
if ( is_sticky() || get_post_meta( get_the_ID(), 'peptide_featured', true ) ) {
|
if ( is_sticky() || get_post_meta( get_the_ID(), 'peptide_featured', true ) ) {
|
||||||
?>
|
?>
|
||||||
<span class="ps-featured-badge">★ <?php esc_html_e( 'Featured', 'peptide-starter' ); ?></span>
|
<span class="ps-featured-badge">★ <?php esc_html_e( 'Featured', 'peptide-starter' ); ?></span>
|
||||||
@@ -36,10 +49,19 @@ get_header();
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<h1 class="page-title"><?php the_title(); ?></h1>
|
<?php
|
||||||
|
// Only render the page-level <h1> when no verdict card is present.
|
||||||
|
// The verdict card component owns the <h1> for monograph posts so
|
||||||
|
// that readers encounter a single, semantically correct heading.
|
||||||
|
if ( empty( $verdict_state ) ) {
|
||||||
|
?>
|
||||||
|
<h1 class="page-title"><?php the_title(); ?></h1>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// Category tag pills
|
// Category tag pills.
|
||||||
$categories = get_the_terms( get_the_ID(), 'peptide-category' );
|
$categories = get_the_terms( get_the_ID(), 'peptide-category' );
|
||||||
if ( $categories && ! is_wp_error( $categories ) ) {
|
if ( $categories && ! is_wp_error( $categories ) ) {
|
||||||
echo '<div class="peptide-card__meta" style="margin-top: var(--spacing-sm);">';
|
echo '<div class="peptide-card__meta" style="margin-top: var(--spacing-sm);">';
|
||||||
@@ -52,6 +74,17 @@ get_header();
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- Verdict Card Hero (monograph posts only) -->
|
||||||
|
<?php
|
||||||
|
if ( ! empty( $verdict_state ) ) {
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/verdict/card',
|
||||||
|
null,
|
||||||
|
array( 'post_id' => get_the_ID() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- Single Peptide Content -->
|
<!-- Single Peptide Content -->
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<article class="peptide-single" id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
<article class="peptide-single" id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||||
@@ -69,6 +102,22 @@ get_header();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Inline disclosure renders after the content box (below any partner
|
||||||
|
// links in the_content). Conditional on has_partner_link meta — if no
|
||||||
|
// partner link exists on this monograph, the disclosure is suppressed.
|
||||||
|
// Default false: new monographs are disclosure-off until a partner is
|
||||||
|
// added and the meta is explicitly set to true by the CMO.
|
||||||
|
$has_partner_link = (bool) get_post_meta( get_the_ID(), 'has_partner_link', true );
|
||||||
|
if ( $verdict_state && $has_partner_link ) {
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/affiliate-disclosure',
|
||||||
|
null,
|
||||||
|
array( 'context' => 'inline' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- Meta Information as card grid -->
|
<!-- Meta Information as card grid -->
|
||||||
<?php
|
<?php
|
||||||
$formula = get_post_meta( get_the_ID(), 'peptide_formula', true );
|
$formula = get_post_meta( get_the_ID(), 'peptide_formula', true );
|
||||||
|
|||||||
14
site.webmanifest
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "Peptide Repo",
|
||||||
|
"short_name": "Peptide Repo",
|
||||||
|
"description": "What the research really says — verdict-driven peptide science.",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#FAFAF7",
|
||||||
|
"theme_color": "#1B8A92",
|
||||||
|
"icons": [
|
||||||
|
{"src": "/wp-content/themes/peptide-starter/assets/brand/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable"},
|
||||||
|
{"src": "/wp-content/themes/peptide-starter/assets/brand/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable"}
|
||||||
|
]
|
||||||
|
}
|
||||||
52
template-parts/affiliate-disclosure.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Affiliate Disclosure Component
|
||||||
|
*
|
||||||
|
* Displays FTC-compliant disclosure about affiliate commissions.
|
||||||
|
* Three contexts: inline (default), banner, footer.
|
||||||
|
* Accepts $args['context'].
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = isset( $args['context'] ) ? sanitize_text_field( $args['context'] ) : 'inline';
|
||||||
|
|
||||||
|
// Copy variants (verbatim per CMO spec).
|
||||||
|
$inline_copy = 'If you buy through this link, the seller pays Peptide Repo a commission. We don\'t pay to feature any peptide, and our verdicts don\'t change based on who pays.';
|
||||||
|
|
||||||
|
$banner_copy = 'Peptide Repo earns a commission when readers buy through links to our fulfillment partners. We are funded by those commissions, not by the peptide brands themselves. No peptide on this site appears here because someone paid us. Our verdicts are decided before we negotiate any partnership, and we drop partners whose product or process degrades.';
|
||||||
|
|
||||||
|
$footer_copy = 'Peptide Repo earns a commission on some links. <a href="' . esc_url( home_url( '/about/' ) ) . '#disclosures">' . esc_html__( 'How we work', 'peptide-starter' ) . ' →</a>';
|
||||||
|
|
||||||
|
switch ( $context ) {
|
||||||
|
case 'banner':
|
||||||
|
?>
|
||||||
|
<aside class="pr-disclosure pr-disclosure--banner" aria-label="<?php esc_attr_e( 'Affiliate disclosure', 'peptide-starter' ); ?>">
|
||||||
|
<span class="pr-disclosure__label"><?php esc_html_e( 'A note on commerce.', 'peptide-starter' ); ?></span>
|
||||||
|
<?php echo wp_kses_post( $banner_copy ); ?>
|
||||||
|
</aside>
|
||||||
|
<?php
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'footer':
|
||||||
|
?>
|
||||||
|
<p class="pr-disclosure pr-disclosure--footer">
|
||||||
|
<?php echo wp_kses_post( $footer_copy ); ?>
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'inline':
|
||||||
|
default:
|
||||||
|
?>
|
||||||
|
<aside class="pr-disclosure pr-disclosure--inline" aria-label="<?php esc_attr_e( 'Affiliate disclosure', 'peptide-starter' ); ?>">
|
||||||
|
<span class="pr-disclosure__label"><?php esc_html_e( 'How we make money:', 'peptide-starter' ); ?></span>
|
||||||
|
<?php echo wp_kses_post( $inline_copy ); ?>
|
||||||
|
</aside>
|
||||||
|
<?php
|
||||||
|
break;
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
*
|
*
|
||||||
* Renders a 6-card grid of research modules (tools/sections) for the front page.
|
* Renders a 6-card grid of research modules (tools/sections) for the front page.
|
||||||
* Each card checks if the target plugin is active; inactive cards are dimmed.
|
* Each card checks if the target plugin is active; inactive cards are dimmed.
|
||||||
|
* Cards with 'coming_soon' => true display a "Coming soon" badge overlaid in
|
||||||
|
* the top-right corner — layered on top of the --inactive visual treatment.
|
||||||
*
|
*
|
||||||
* @see front-page.php — includes this template part
|
* @see front-page.php — includes this template part
|
||||||
* @see style.css — .ps-module-* classes
|
* @see style.css — .ps-module-* classes
|
||||||
@@ -21,48 +23,56 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Module card definitions with shortcode checks for graceful degradation.
|
// Module card definitions with shortcode checks for graceful degradation.
|
||||||
|
// 'coming_soon' => true adds a "Coming soon" badge without removing existing
|
||||||
|
// --inactive styling — the badge layers on top of the dim/no-hover treatment.
|
||||||
$modules = array(
|
$modules = array(
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Peptides', 'peptide-starter' ),
|
'title' => __( 'Peptides', 'peptide-starter' ),
|
||||||
'description' => __( 'Browse the complete reagent directory with protocols and research parameters.', 'peptide-starter' ),
|
'description' => __( 'Read the verdicts. 20 monographs across 5 evidence states, growing weekly.', 'peptide-starter' ),
|
||||||
'url' => '/peptides',
|
'url' => '/peptides',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L12 22"/><path d="M5 12C5 8.13 8.13 5 12 5s7 3.13 7 7-3.13 7-7 7-7-3.13-7-7z"/><circle cx="12" cy="8" r="1.5" fill="currentColor" stroke="none"/><circle cx="12" cy="16" r="1.5" fill="currentColor" stroke="none"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L12 22"/><path d="M5 12C5 8.13 8.13 5 12 5s7 3.13 7 7-3.13 7-7 7-7-3.13-7-7z"/><circle cx="12" cy="8" r="1.5" fill="currentColor" stroke="none"/><circle cx="12" cy="16" r="1.5" fill="currentColor" stroke="none"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_directory' ) || post_type_exists( 'peptide' ),
|
'available' => shortcode_exists( 'peptide_directory' ) || post_type_exists( 'peptide' ),
|
||||||
|
'coming_soon' => false,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Calculator', 'peptide-starter' ),
|
'title' => __( 'Calculator', 'peptide-starter' ),
|
||||||
'description' => __( 'Precision reconstitution calculator for accurate laboratory preparations.', 'peptide-starter' ),
|
'description' => __( 'Reconstitute a peptide accurately. Enter the vial, the bacteriostatic water, the target dose; get the units.', 'peptide-starter' ),
|
||||||
'url' => '/calculator',
|
'url' => '/calculator',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="10" x2="10" y2="10"/><line x1="14" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="10" y2="14"/><line x1="14" y1="14" x2="16" y2="14"/><line x1="8" y1="18" x2="16" y2="18"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="10" x2="10" y2="10"/><line x1="14" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="10" y2="14"/><line x1="14" y1="14" x2="16" y2="14"/><line x1="8" y1="18" x2="16" y2="18"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_tools_calculator' ),
|
'available' => shortcode_exists( 'prc_calculator' ),
|
||||||
|
'coming_soon' => false,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Protocol Builder', 'peptide-starter' ),
|
'title' => __( 'Protocol Builder', 'peptide-starter' ),
|
||||||
'description' => __( 'Interactive multi-step wizard for designing research protocols.', 'peptide-starter' ),
|
'description' => __( 'Build a protocol you can hand to anyone. Doses, schedule, notes, reviewable history.', 'peptide-starter' ),
|
||||||
'url' => '/protocol-builder',
|
'url' => '/protocol-builder',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/><path d="M9 14l2 2 4-4"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/><path d="M9 14l2 2 4-4"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_tools_protocol_builder' ),
|
'available' => shortcode_exists( 'peptide_tools_protocol_builder' ),
|
||||||
|
'coming_soon' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Tracker', 'peptide-starter' ),
|
'title' => __( 'Tracker', 'peptide-starter' ),
|
||||||
'description' => __( 'Log doses, manage active compounds, and forecast vial depletion timelines.', 'peptide-starter' ),
|
'description' => __( 'Log doses and timing. For people running their own protocols, and people supervising someone else\'s.', 'peptide-starter' ),
|
||||||
'url' => '/tracker',
|
'url' => '/tracker',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="3" y1="20" x2="21" y2="20"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="3" y1="20" x2="21" y2="20"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_tracker' ),
|
'available' => shortcode_exists( 'peptide_tracker' ),
|
||||||
|
'coming_soon' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Science Feed', 'peptide-starter' ),
|
'title' => __( 'Science Feed', 'peptide-starter' ),
|
||||||
'description' => __( 'Latest research publications, clinical studies, and laboratory insights.', 'peptide-starter' ),
|
'description' => __( 'What\'s new in peptide research. Filtered for what matters, weekly.', 'peptide-starter' ),
|
||||||
'url' => '/news',
|
'url' => '/news',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22h16a2 2 0 002-2V4a2 2 0 00-2-2H8a2 2 0 00-2 2v16a2 2 0 01-2 2zm0 0a2 2 0 01-2-2v-9c0-1.1.9-2 2-2h2"/><line x1="10" y1="6" x2="18" y2="6"/><line x1="10" y1="10" x2="18" y2="10"/><line x1="10" y1="14" x2="14" y2="14"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22h16a2 2 0 002-2V4a2 2 0 00-2-2H8a2 2 0 00-2 2v16a2 2 0 01-2 2zm0 0a2 2 0 01-2-2v-9c0-1.1.9-2 2-2h2"/><line x1="10" y1="6" x2="18" y2="6"/><line x1="10" y1="10" x2="18" y2="10"/><line x1="10" y1="14" x2="14" y2="14"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_news' ),
|
'available' => shortcode_exists( 'peptide_news' ),
|
||||||
|
'coming_soon' => false,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => __( 'Subject Log', 'peptide-starter' ),
|
'title' => __( 'Subject Log', 'peptide-starter' ),
|
||||||
'description' => __( 'Longitudinal tracking for biomarker data and protocol history.', 'peptide-starter' ),
|
'description' => __( 'Track outcomes over time. Subjective markers, lab results, protocol changes — your own data, kept private.', 'peptide-starter' ),
|
||||||
'url' => '/subject-log',
|
'url' => '/subject-log',
|
||||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
|
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
|
||||||
'available' => shortcode_exists( 'peptide_tracker_subject_log' ),
|
'available' => shortcode_exists( 'peptide_tracker_subject_log' ),
|
||||||
|
'coming_soon' => true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
@@ -70,9 +80,9 @@ $modules = array(
|
|||||||
<section class="ps-modules-section">
|
<section class="ps-modules-section">
|
||||||
<div class="ps-container-wide">
|
<div class="ps-container-wide">
|
||||||
<div class="ps-modules-header">
|
<div class="ps-modules-header">
|
||||||
<h2 class="ps-modules-title"><?php esc_html_e( 'Research Modules', 'peptide-starter' ); ?></h2>
|
<h2 class="ps-modules-title"><?php esc_html_e( 'Tools', 'peptide-starter' ); ?></h2>
|
||||||
<p class="ps-modules-subtitle">
|
<p class="ps-modules-subtitle">
|
||||||
<?php esc_html_e( 'Tools and resources for laboratory research and data management.', 'peptide-starter' ); ?>
|
<?php esc_html_e( 'Free to use. Built for people running their own protocols, and people deciding whether they should.', 'peptide-starter' ); ?>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -80,6 +90,9 @@ $modules = array(
|
|||||||
<?php foreach ( $modules as $module ) : ?>
|
<?php foreach ( $modules as $module ) : ?>
|
||||||
<a href="<?php echo esc_url( home_url( $module['url'] ) ); ?>"
|
<a href="<?php echo esc_url( home_url( $module['url'] ) ); ?>"
|
||||||
class="ps-module-card <?php echo $module['available'] ? '' : 'ps-module-card--inactive'; ?>">
|
class="ps-module-card <?php echo $module['available'] ? '' : 'ps-module-card--inactive'; ?>">
|
||||||
|
<?php if ( ! empty( $module['coming_soon'] ) ) : ?>
|
||||||
|
<span class="ps-module-card__badge"><?php esc_html_e( 'Coming soon', 'peptide-starter' ); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="ps-module-card__icon">
|
<div class="ps-module-card__icon">
|
||||||
<?php
|
<?php
|
||||||
// SVG icons are defined in this template — safe to output directly.
|
// SVG icons are defined in this template — safe to output directly.
|
||||||
|
|||||||
54
template-parts/verdict/badge.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Verdict Badge Component
|
||||||
|
*
|
||||||
|
* Displays a verdict state badge with glyph, label, and colors.
|
||||||
|
* Accepts $args['verdict_state'] and $args['show_label'].
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$verdict_state = isset( $args['verdict_state'] ) ? sanitize_text_field( $args['verdict_state'] ) : '';
|
||||||
|
$show_label = isset( $args['show_label'] ) ? (bool) $args['show_label'] : true;
|
||||||
|
|
||||||
|
// State configuration.
|
||||||
|
$states = array(
|
||||||
|
'established' => array(
|
||||||
|
'glyph' => '✓',
|
||||||
|
'label' => 'Established',
|
||||||
|
),
|
||||||
|
'promising' => array(
|
||||||
|
'glyph' => '◐',
|
||||||
|
'label' => 'Promising',
|
||||||
|
),
|
||||||
|
'investigational' => array(
|
||||||
|
'glyph' => '?',
|
||||||
|
'label' => 'Investigational',
|
||||||
|
),
|
||||||
|
'insufficient' => array(
|
||||||
|
'glyph' => '⊘',
|
||||||
|
'label' => 'Insufficient Evidence',
|
||||||
|
),
|
||||||
|
'cautionary' => array(
|
||||||
|
'glyph' => '⚠',
|
||||||
|
'label' => 'Cautionary',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Early exit if invalid state.
|
||||||
|
if ( ! isset( $states[ $verdict_state ] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$state_info = $states[ $verdict_state ];
|
||||||
|
?>
|
||||||
|
<span class="pr-verdict pr-verdict--<?php echo esc_attr( $verdict_state ); ?>" role="img" aria-label="<?php echo esc_attr( 'Verdict: ' . $state_info['label'] ); ?>">
|
||||||
|
<span class="pr-verdict__glyph" aria-hidden="true"><?php echo $state_info['glyph']; ?></span>
|
||||||
|
<?php if ( $show_label ) : ?>
|
||||||
|
<span class="pr-verdict__label"><?php echo esc_html( $state_info['label'] ); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</span>
|
||||||
105
template-parts/verdict/card.php
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Verdict Card Component (Monograph Hero)
|
||||||
|
*
|
||||||
|
* Displays the full verdict card hero for a peptide post.
|
||||||
|
* Reads verdict_state, verdict_text, signal_row_1/2/3, and
|
||||||
|
* signal_row_1/2/3_glyph from post meta.
|
||||||
|
*
|
||||||
|
* Who calls it: single-peptide.php for posts with a verdict_state.
|
||||||
|
* Depends on: inc/verdict-meta.php (meta registration),
|
||||||
|
* template-parts/verdict/badge.php,
|
||||||
|
* template-parts/verdict/evidence-row.php.
|
||||||
|
*
|
||||||
|
* @see inc/verdict-meta.php
|
||||||
|
* @see single-peptide.php
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_id = isset( $args['post_id'] ) ? absint( $args['post_id'] ) : get_the_ID();
|
||||||
|
|
||||||
|
// Get verdict state; bail early if unset.
|
||||||
|
$verdict_state = get_post_meta( $post_id, 'verdict_state', true );
|
||||||
|
if ( empty( $verdict_state ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get opinionated verdict text; fall back to placeholder if not yet authored.
|
||||||
|
$verdict_text = get_post_meta( $post_id, 'verdict_text', true );
|
||||||
|
if ( empty( $verdict_text ) ) {
|
||||||
|
$verdict_text = __( 'This peptide merits careful scientific review. Please consult with a healthcare provider before use.', 'peptide-starter' );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect signal rows with their per-row glyphs.
|
||||||
|
// Glyph comes from the signal_row_N_glyph meta field; if unset, no glyph is
|
||||||
|
// rendered. This is intentional — a missing glyph is better than a wrong one.
|
||||||
|
$signals = array();
|
||||||
|
for ( $i = 1; $i <= 3; $i++ ) {
|
||||||
|
$signal_text = get_post_meta( $post_id, 'signal_row_' . $i, true );
|
||||||
|
$signal_glyph = get_post_meta( $post_id, 'signal_row_' . $i . '_glyph', true );
|
||||||
|
if ( ! empty( $signal_text ) ) {
|
||||||
|
$signals[] = array(
|
||||||
|
'glyph' => $signal_glyph, // May be empty string; evidence-row handles that.
|
||||||
|
'label' => $signal_text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="pr-verdict-card">
|
||||||
|
<div class="pr-verdict-card__header">
|
||||||
|
<div>
|
||||||
|
<?php
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/verdict/badge',
|
||||||
|
null,
|
||||||
|
array(
|
||||||
|
'verdict_state' => $verdict_state,
|
||||||
|
'show_label' => true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<!-- last_reviewed_date timestamp deferred to v1.1 — WP post date != editorial review date -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="pr-verdict-card__title"><?php echo esc_html( get_the_title( $post_id ) ); ?></h1>
|
||||||
|
|
||||||
|
<p class="pr-verdict-card__summary">
|
||||||
|
<?php
|
||||||
|
$summary = get_the_excerpt( $post_id );
|
||||||
|
if ( empty( $summary ) ) {
|
||||||
|
$content = get_the_content( null, false, $post_id );
|
||||||
|
$summary = wp_trim_words( $content, 20 );
|
||||||
|
}
|
||||||
|
echo wp_kses_post( $summary );
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php if ( ! empty( $signals ) ) : ?>
|
||||||
|
<?php
|
||||||
|
get_template_part(
|
||||||
|
'template-parts/verdict/evidence-row',
|
||||||
|
null,
|
||||||
|
array( 'signals' => $signals )
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="pr-verdict-card__verdict-label"><?php esc_html_e( 'Our verdict:', 'peptide-starter' ); ?></div>
|
||||||
|
<div class="pr-verdict-card__verdict-text">
|
||||||
|
<?php echo wp_kses_post( nl2br( $verdict_text ) ); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pr-verdict-card__cta-group">
|
||||||
|
<a href="#evidence" class="pr-verdict-card__cta-link">
|
||||||
|
<?php esc_html_e( 'Read the full evidence', 'peptide-starter' ); ?> →
|
||||||
|
</a>
|
||||||
|
<a href="#dosing" class="pr-verdict-card__cta-link">
|
||||||
|
<?php esc_html_e( 'Jump to dosing', 'peptide-starter' ); ?> →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
34
template-parts/verdict/evidence-row.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Verdict Evidence Signal Row Component
|
||||||
|
*
|
||||||
|
* Displays an inset row of evidence signals with glyphs.
|
||||||
|
* Accepts $args['signals'] as array of ['glyph' => '...', 'label' => '...']
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$signals = isset( $args['signals'] ) && is_array( $args['signals'] ) ? $args['signals'] : array();
|
||||||
|
|
||||||
|
if ( empty( $signals ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="pr-evidence-row">
|
||||||
|
<?php foreach ( $signals as $signal ) : ?>
|
||||||
|
<?php
|
||||||
|
$glyph = isset( $signal['glyph'] ) ? sanitize_text_field( $signal['glyph'] ) : '';
|
||||||
|
$label = isset( $signal['label'] ) ? sanitize_text_field( $signal['label'] ) : '';
|
||||||
|
?>
|
||||||
|
<?php if ( ! empty( $glyph ) && ! empty( $label ) ) : ?>
|
||||||
|
<div class="pr-evidence-row__item">
|
||||||
|
<span class="pr-evidence-row__glyph"><?php echo esc_html( $glyph ); ?></span>
|
||||||
|
<span><?php echo esc_html( $label ); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Tests for inc/perf-asset-policy.php.
|
* Tests for inc/perf-asset-policy.php.
|
||||||
*
|
*
|
||||||
* Covers: conditional dequeue logic (WC, Elementor, USMI), font weight slimming,
|
* Covers: conditional dequeue logic (Elementor, USMI), font weight slimming,
|
||||||
* preconnect hints, script defer, kill-switch, and filter overrides.
|
* preconnect hints, script defer, kill-switch, and filter overrides.
|
||||||
*
|
*
|
||||||
* @package peptide-starter
|
* @package peptide-starter
|
||||||
@@ -24,57 +24,16 @@ class Test_Perf_Asset_Policy extends WP_UnitTestCase {
|
|||||||
define( 'PEPTIDE_STARTER_PERF_DEQUEUE', false );
|
define( 'PEPTIDE_STARTER_PERF_DEQUEUE', false );
|
||||||
|
|
||||||
// Enqueue dummy styles.
|
// Enqueue dummy styles.
|
||||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc.css' );
|
wp_enqueue_style( 'elementor-frontend', 'http://example.com/elementor.css' );
|
||||||
|
|
||||||
// Simulate wp_enqueue_scripts action.
|
// Simulate wp_enqueue_scripts action.
|
||||||
do_action( 'wp_enqueue_scripts' );
|
do_action( 'wp_enqueue_scripts' );
|
||||||
|
|
||||||
// Since kill-switch is off, the style should still be queued.
|
// Since kill-switch is off, the style should still be queued.
|
||||||
$this->assertTrue( wp_style_is( 'woocommerce-layout', 'enqueued' ) );
|
$this->assertTrue( wp_style_is( 'elementor-frontend', 'enqueued' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test: WC assets dequeued on non-shop pages.
|
|
||||||
*
|
|
||||||
* Homepage (front page) is not a WC page, so WC styles/scripts should dequeue.
|
|
||||||
*/
|
|
||||||
public function test_wc_dequeue_on_homepage() {
|
|
||||||
$this->go_to( home_url( '/' ) );
|
|
||||||
|
|
||||||
// Enqueue WC styles.
|
|
||||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc-layout.css' );
|
|
||||||
wp_enqueue_style( 'woocommerce-general', 'http://example.com/wc-general.css' );
|
|
||||||
wp_enqueue_script( 'woocommerce', 'http://example.com/wc.js' );
|
|
||||||
|
|
||||||
// Run dequeue at priority 100.
|
|
||||||
peptide_starter_perf_dequeue_plugin_assets();
|
|
||||||
|
|
||||||
// All WC assets should be dequeued.
|
|
||||||
$this->assertFalse( wp_style_is( 'woocommerce-layout', 'enqueued' ) );
|
|
||||||
$this->assertFalse( wp_style_is( 'woocommerce-general', 'enqueued' ) );
|
|
||||||
$this->assertFalse( wp_script_is( 'woocommerce', 'enqueued' ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test: WC assets remain on /shop page.
|
|
||||||
*
|
|
||||||
* On WC shop pages, WC assets should NOT dequeue.
|
|
||||||
*/
|
|
||||||
public function test_wc_not_dequeued_on_shop() {
|
|
||||||
// Skip if WooCommerce is not active.
|
|
||||||
if ( ! class_exists( 'WooCommerce' ) ) {
|
|
||||||
$this->markTestSkipped( 'WooCommerce not active.' );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enqueue WC styles.
|
|
||||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc-layout.css' );
|
|
||||||
|
|
||||||
// The dequeue function checks is_woocommerce(); we'll mock it.
|
|
||||||
// For a true test, we'd need a real WC shop page setup.
|
|
||||||
// Instead, we assert the filter allows override.
|
|
||||||
$default_handles = apply_filters( 'peptide_starter_perf_woocommerce_styles', array() );
|
|
||||||
$this->assertIsArray( $default_handles );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test: Elementor assets dequeued on pages without Elementor.
|
* Test: Elementor assets dequeued on pages without Elementor.
|
||||||
|
|||||||