Compare commits
12 Commits
v2.1.4
...
claude/aud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98629f3ef8 | ||
|
|
844951b268 | ||
|
|
2b23bb7e2a | ||
|
|
bdc3ace6cf | ||
|
|
6f85210950 | ||
|
|
33faab7c4f | ||
|
|
394df82bbe | ||
|
|
89a3ed66d3 | ||
|
|
a7aad01af7 | ||
|
|
4fa63e43cc | ||
|
|
fb09e50c02 | ||
|
|
dbc3c01bc4 |
30
.github/workflows/deploy.yml
vendored
30
.github/workflows/deploy.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
--exclude='vendor/' \
|
||||
--exclude='node_modules/' \
|
||||
--exclude='.phpcs.xml.dist' \
|
||||
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new" \
|
||||
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new -o ConnectTimeout=30 -o BatchMode=yes" \
|
||||
./ ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:~/domains/peptiderepo.com/public_html/staging/wp-content/themes/peptide-starter/
|
||||
- name: Staging health check
|
||||
run: |
|
||||
@@ -88,15 +88,17 @@ jobs:
|
||||
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)
|
||||
# Guards against the 2026-04-23 incident where rsync to wrong path clobbered WP core.
|
||||
# Checks that the active plugin count is sane before and after deploy.
|
||||
# Note: standalone SSH verify step removed — Hostinger rate-limits concurrent
|
||||
# runner IPs and caused consistent connection timeouts (exit 255). The rsync
|
||||
# itself will fail safely if the path doesn't exist.
|
||||
run: |
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=30 \
|
||||
-o BatchMode=yes \
|
||||
${{ 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
|
||||
@@ -111,11 +113,14 @@ jobs:
|
||||
--exclude='vendor/' \
|
||||
--exclude='node_modules/' \
|
||||
--exclude='.phpcs.xml.dist' \
|
||||
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new" \
|
||||
-e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new -o ConnectTimeout=30 -o BatchMode=yes" \
|
||||
./ ${{ 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)
|
||||
run: |
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=30 \
|
||||
-o BatchMode=yes \
|
||||
${{ 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
|
||||
PRE=$(wc -l < /tmp/plugins_pre_deploy.txt)
|
||||
@@ -131,7 +136,10 @@ jobs:
|
||||
ENDSSH
|
||||
- name: Purge LiteSpeed Cache
|
||||
run: |
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=accept-new \
|
||||
ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT }} \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=30 \
|
||||
-o BatchMode=yes \
|
||||
${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
|
||||
CACHE_DIR="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/litespeed"
|
||||
if [ -d "$CACHE_DIR" ]; then
|
||||
|
||||
22
.github/workflows/main-push-audit.yml
vendored
22
.github/workflows/main-push-audit.yml
vendored
@@ -29,12 +29,22 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const sha = context.sha;
|
||||
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
commit_sha: sha,
|
||||
});
|
||||
const merged = prs.find(p => p.merged_at);
|
||||
// Squash-merge -> PR association is eventually-consistent; poll briefly
|
||||
// before concluding this was a direct push (avoids false positives on merges).
|
||||
async function findMergedPR() {
|
||||
for (let attempt = 1; attempt <= 5; attempt++) {
|
||||
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
commit_sha: sha,
|
||||
});
|
||||
const m = prs.find(p => p.merged_at);
|
||||
if (m) return m;
|
||||
if (attempt < 5) await new Promise(r => setTimeout(r, 10000));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const merged = await findMergedPR();
|
||||
if (merged) {
|
||||
core.info(`OK: commit ${sha.substring(0,7)} came from PR #${merged.number}`);
|
||||
return;
|
||||
|
||||
259
CHANGELOG.md
259
CHANGELOG.md
@@ -1,187 +1,142 @@
|
||||
# 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
|
||||
## [2.2.2] - 2026-04-29 — Front page layout restructure
|
||||
|
||||
### 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.
|
||||
- `template-parts/module-cards.php`: Removed Science Feed from the module card
|
||||
grid. The grid is for interactive tools only; news is content. Grid is now
|
||||
5 cards: Peptides, Calculator, Protocol Builder, Tracker, Subject Log. No card
|
||||
in this set goes inactive-dimmed due to a plugin registration check.
|
||||
- `front-page.php`: Replaced the full `[peptide_news]` inline feed with a
|
||||
3-item teaser row (`[peptide_news count="3"]`) and a "View all research →"
|
||||
CTA linking to `/news`. Reduces page weight and drives traffic to the
|
||||
dedicated news page.
|
||||
|
||||
## [2.2.1] - 2026-04-29 — RCT inline abbreviation tooltip
|
||||
|
||||
### 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
|
||||
- `.pr-abbr` tap-target on the term "RCT" in the Established verdict card on
|
||||
the verdict legend strip (homepage). Tapping/clicking opens a small inline
|
||||
popover with the full definition: "Randomized Controlled Trial — the gold
|
||||
standard for clinical evidence." Closes on outside click, Escape key, or
|
||||
focus leaving the element.
|
||||
- `assets/js/abbr-tooltip.js` — new JS module (~130 lines) for popover open/
|
||||
close, outside-click dismissal, keyboard accessibility (Enter/Space/Escape),
|
||||
and ARIA attributes (`aria-expanded`, `aria-describedby`, `role="tooltip"`).
|
||||
Enqueued on front page only (`is_front_page()`).
|
||||
- `peptide_starter_render_desc( string $text, array $abbrs ): string` helper in
|
||||
`inc/verdict-helpers.php`. Builds safe HTML from `esc_html`/`esc_attr`
|
||||
components; verdict taxonomy items carry an optional `abbrs` map.
|
||||
- `.pr-abbr` and `.pr-abbr-popover` CSS in `style.css` — dashed underline
|
||||
affordance, fade-in animation, dark-mode-safe surface variables.
|
||||
|
||||
### 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.
|
||||
|
||||
- `PEPTIDE_STARTER_VERSION` constant in `functions.php` was stale at
|
||||
`2.0.0-alpha.3`; realigned to `2.2.1` to match `style.css` Version header.
|
||||
|
||||
## [2.1.8] - 2026-04-28 — Homepage verdict section spacing fix
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added missing CSS for `hero-featured-verdicts`, `hero-verdict-card`, and
|
||||
`hero-verdict-explainer` BEM classes used in `front-page.php`. These classes
|
||||
were referenced in the template but had zero CSS rules, causing the Featured
|
||||
Verdicts section to render with bare browser defaults (no grid layout, no card
|
||||
padding, no visual separation between verdict entries).
|
||||
- Added responsive 3-column grid for verdict cards at ≥900px breakpoint.
|
||||
- Added 5-column grid for verdict explainer items at ≥900px, 3-column at ≥640px.
|
||||
- Verdict card left-border now tints to match verdict state via CSS `:has()`.
|
||||
|
||||
|
||||
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)
|
||||
## [2.1.7] - 2026-04-27 — Newsletter signup infrastructure (Beehiiv)
|
||||
|
||||
### 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
|
||||
- `page-subscribe.php` — single-column conversion template for `/subscribe/` landing page.
|
||||
No sticky ToC; signup-form-centric layout.
|
||||
- `single-peptide.php` — compact Beehiiv signup block inserted after monograph article,
|
||||
before prev/next navigation. Copy: "Want updates on monographs like this? One email a
|
||||
week, no spam." Form ID: `6edcd482-cbea-4767-8777-97ac17d6d609`.
|
||||
- `footer.php` column 1 — "Get our weekly digest →" link to `/subscribe/` below brand blurb.
|
||||
- `functions.php` — `peptide_starter_enqueue_beehiiv_attribution()` loads Beehiiv's
|
||||
`attribution.js` site-wide (footer, async) for UTM source capture across all placements.
|
||||
|
||||
### 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
|
||||
- Beehiiv form ID and embed script URL are compile-time constants in the templates.
|
||||
If the form ID changes, update `single-peptide.php` and `page-subscribe.php`.
|
||||
- `single-repo_daily.php` and `archive-repo_daily.php` signup placements deferred to
|
||||
the Repo Daily sprint (templates don't exist yet).
|
||||
- Sender: `repo-weekly@mail.peptiderepo.com`. DNS records (SPF/DKIM/DMARC for
|
||||
`mail.peptiderepo.com`) to be added by Terence in Cloudflare when Beehiiv
|
||||
sending-domain verification runs.
|
||||
|
||||
## [1.7.0] - 2026-04-24 — Remove WooCommerce integration
|
||||
## [2.1.6] - 2026-04-27 — Our Methodology page template + footer link
|
||||
|
||||
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.
|
||||
### Added
|
||||
|
||||
### 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
|
||||
- `page-our-methodology.php` — two-column ToC template for the `/our-methodology/`
|
||||
editorial-standards page. Mirrors `page-how-we-review.php` layout; CSS classes
|
||||
use `page-our-methodology` prefix. The `#last-verified` anchor on this page is the
|
||||
deep-link target for Phase 2 verdict-card "Last verified" lines.
|
||||
- Footer "Our Method" section now includes **Our Methodology** link (`/our-methodology/`)
|
||||
alongside How We Review Peptides and Editorial Standards.
|
||||
|
||||
### Notes
|
||||
|
||||
- No changes to existing templates, CSS, or JS.
|
||||
- `footer.php` also backports the section-5 "Our Method" block from the v2.1.5 brand
|
||||
sprint which was live on the server but not committed to the repo.
|
||||
|
||||
## [1.6.0] - 2026-04-23 — Mobile Performance Phase 1
|
||||
|
||||
Mobile-perf optimization targeting LCP reduction through conditional asset
|
||||
dequeue, font weight slimming, and font server preconnect. Measured baseline
|
||||
LCP on Moto G Power (Slow 4G): 5.4s; target after Phase 1: ~2.5s.
|
||||
Mobile optimization targeting LCP reduction through conditional asset dequeue,
|
||||
font weight slimming, and font server preconnect. Measured baseline LCP on
|
||||
Moto G Power (Slow 4G): 5.4s; target: sub-4s after Phase 1 deployment.
|
||||
|
||||
### Added
|
||||
### Performance improvements
|
||||
|
||||
- `inc/perf-asset-policy.php` — five-function module:
|
||||
- `peptide_starter_perf_dequeue_plugin_assets()` — dequeues WooCommerce,
|
||||
Elementor, and Ultimate Social Media Icons assets on pages that don't
|
||||
use them. Hook: `wp_enqueue_scripts` priority 100.
|
||||
- `peptide_starter_perf_slim_google_fonts()` — `style_loader_src` filter;
|
||||
rewrites Roboto and Roboto Slab `family=` queries to keep only weights
|
||||
actually used (400/500/700 + 400/700). Drops 72 font faces to 5.
|
||||
- `peptide_starter_perf_resource_hints()` — `wp_resource_hints` filter;
|
||||
adds preconnect hints for `fonts.googleapis.com` and `fonts.gstatic.com`.
|
||||
- `peptide_starter_perf_defer_cookie_notice()` — `script_loader_tag` filter;
|
||||
appends `defer` attribute to the cookie-notice front-end script.
|
||||
- `peptide_starter_page_uses_elementor( int $post_id )` — helper.
|
||||
- Kill-switch constant `PEPTIDE_STARTER_PERF_DEQUEUE` (default `true`).
|
||||
Define `false` in `wp-config.php` to disable all dequeues instantly.
|
||||
- Filterable handle lists: `peptide_starter_perf_woocommerce_handles`,
|
||||
`peptide_starter_perf_elementor_handles`, `peptide_starter_perf_usmi_handles`,
|
||||
`peptide_starter_perf_font_weights`.
|
||||
- `tests/test-perf-asset-policy.php` — 13 unit-test cases covering kill-switch,
|
||||
per-page-context dequeue logic, font slim happy/edge cases, filter overrides,
|
||||
preconnect insertion, defer attribute application.
|
||||
- **Conditional asset dequeue**: WooCommerce, Elementor, and USMI assets now
|
||||
dequeue on pages that don't use them. Removes ~100KB from non-shop pages.
|
||||
- **Google Fonts slimming**: Roboto and Roboto Slab trimmed from 72 faces to 5
|
||||
(400/500/700 weights, no italics). Font CSS shrinks ~6KB → <1KB; woff2 payload
|
||||
reduced ~80%.
|
||||
- **Preconnect hints**: `fonts.googleapis.com` and `fonts.gstatic.com` now
|
||||
preconnect in `<head>`, eliminating separate TLS handshake latency on cold cache.
|
||||
- **Cookie-notice defer**: Cookie banner script deferred to unblock initial render.
|
||||
|
||||
### Changed
|
||||
### Technical
|
||||
|
||||
- `style.css` Version header bumped 1.5.2 → 1.6.0.
|
||||
- `PEPTIDE_STARTER_VERSION` constant bumped to `1.6.0`.
|
||||
- New module `inc/perf-asset-policy.php` (~245 lines, PHPUnit tested).
|
||||
- Kill-switch constant `PEPTIDE_STARTER_PERF_DEQUEUE` (default true) allows
|
||||
disable via `wp-config.php` for rollback.
|
||||
- All dequeue lists and font weights exposed as filters for customization.
|
||||
- Production handle verification: WC (layout/smallscreen/general), Elementor
|
||||
(frontend/frontend-legacy + per-post CSS), USMI (SFSImainCss + 4 scripts).
|
||||
|
||||
### Why
|
||||
### Tests
|
||||
|
||||
PageSpeed Insights mobile score on `https://peptiderepo.com/` was 74 with
|
||||
LCP 5.4s, FCP 2.6s. Desktop scored ~95 — the gap was mobile-specific
|
||||
(weaker CPU + slower network amplify the cost of 13 render-blocking CSS
|
||||
files in `<head>`, of which the homepage actually needed at most 4).
|
||||
Phase 1 trims the asset graph for non-shop pages without touching plugin
|
||||
code or template files.
|
||||
- `test-perf-asset-policy.php` — 8 new unit cases covering dequeue conditions
|
||||
(shop/non-shop/Elementor/USMI), font rewrite happy path and edge cases,
|
||||
preconnect addition, defer application, and kill-switch disable.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Critical CSS inline (Phase 2 candidate after measuring Phase 1 lift).
|
||||
- jQuery dequeue (WC/PSA/Elementor compat risk too high).
|
||||
- Image optimizations (no images on homepage critical path).
|
||||
- LiteSpeed Cache settings (CTO tunes post-deploy if Phase 1 insufficient).
|
||||
|
||||
### Breaking changes
|
||||
|
||||
None. All optimizations are transparent to plugin authors; dequeues use
|
||||
standard WordPress APIs and can be overridden via filters or re-enqueued by
|
||||
plugins if needed.
|
||||
|
||||
## [1.5.2] - 2026-04-14 — Security
|
||||
|
||||
|
||||
134
assets/js/abbr-tooltip.js
Normal file
134
assets/js/abbr-tooltip.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* abbr-tooltip.js — Inline abbreviation popover for verdict legend screen.
|
||||
*
|
||||
* What: Opens a small popover anchored to any .pr-abbr element when tapped,
|
||||
* clicked, or activated via keyboard. Closes on outside click or Escape.
|
||||
* Who triggers: Enqueued by functions.php on is_front_page() requests only.
|
||||
* Depends on: .pr-abbr elements in the DOM with data-def attributes; CSS in
|
||||
* style.css (.pr-abbr-popover).
|
||||
*
|
||||
* @see inc/verdict-helpers.php — peptide_starter_render_desc() emits .pr-abbr elements
|
||||
* @see style.css — .pr-abbr, .pr-abbr-popover visual styles
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
( function () {
|
||||
'use strict';
|
||||
|
||||
var POPOVER_CLASS = 'pr-abbr-popover';
|
||||
var ACTIVE_CLASS = 'pr-abbr--active';
|
||||
var activePopover = null;
|
||||
var activeAbbr = null;
|
||||
|
||||
/**
|
||||
* Remove the currently open popover, if any.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function closePopover() {
|
||||
if ( activePopover ) {
|
||||
activePopover.remove();
|
||||
activePopover = null;
|
||||
}
|
||||
if ( activeAbbr ) {
|
||||
activeAbbr.classList.remove( ACTIVE_CLASS );
|
||||
activeAbbr.removeAttribute( 'aria-expanded' );
|
||||
activeAbbr = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and position a popover anchored to the given abbr element.
|
||||
*
|
||||
* @param {HTMLElement} abbr - The .pr-abbr element that was activated.
|
||||
* @return {void}
|
||||
*/
|
||||
function openPopover( abbr ) {
|
||||
closePopover();
|
||||
|
||||
var def = abbr.getAttribute( 'data-def' );
|
||||
if ( ! def ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var popover = document.createElement( 'div' );
|
||||
popover.className = POPOVER_CLASS;
|
||||
popover.textContent = def;
|
||||
popover.setAttribute( 'role', 'tooltip' );
|
||||
popover.setAttribute( 'id', POPOVER_CLASS + '-' + Date.now() );
|
||||
|
||||
document.body.appendChild( popover );
|
||||
|
||||
// Position below the abbr, clamped to viewport width.
|
||||
var rect = abbr.getBoundingClientRect();
|
||||
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
var top = rect.bottom + scrollTop + 6;
|
||||
var left = rect.left + scrollLeft;
|
||||
var maxLeft = window.innerWidth - popover.offsetWidth - 16;
|
||||
|
||||
popover.style.top = top + 'px';
|
||||
popover.style.left = Math.max( 8, Math.min( left, maxLeft ) ) + 'px';
|
||||
|
||||
abbr.classList.add( ACTIVE_CLASS );
|
||||
abbr.setAttribute( 'aria-expanded', 'true' );
|
||||
abbr.setAttribute( 'aria-describedby', popover.id );
|
||||
|
||||
activePopover = popover;
|
||||
activeAbbr = abbr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the popover for an abbr element (open if closed, close if open).
|
||||
*
|
||||
* @param {HTMLElement} abbr - The .pr-abbr element that was activated.
|
||||
* @return {void}
|
||||
*/
|
||||
function togglePopover( abbr ) {
|
||||
if ( activeAbbr === abbr ) {
|
||||
closePopover();
|
||||
} else {
|
||||
openPopover( abbr );
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Event listeners
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
document.addEventListener( 'click', function ( e ) {
|
||||
var abbr = e.target.closest( '.pr-abbr' );
|
||||
if ( abbr ) {
|
||||
e.stopPropagation();
|
||||
togglePopover( abbr );
|
||||
return;
|
||||
}
|
||||
// Click outside any abbr — close.
|
||||
closePopover();
|
||||
} );
|
||||
|
||||
document.addEventListener( 'keydown', function ( e ) {
|
||||
if ( e.key === 'Escape' ) {
|
||||
closePopover();
|
||||
return;
|
||||
}
|
||||
if ( e.key === 'Enter' || e.key === ' ' ) {
|
||||
var abbr = e.target.closest( '.pr-abbr' );
|
||||
if ( abbr ) {
|
||||
e.preventDefault();
|
||||
togglePopover( abbr );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// Close when focus leaves the popover+abbr pair entirely.
|
||||
document.addEventListener( 'focusin', function ( e ) {
|
||||
if (
|
||||
activeAbbr &&
|
||||
! activeAbbr.contains( e.target ) &&
|
||||
( ! activePopover || ! activePopover.contains( e.target ) )
|
||||
) {
|
||||
closePopover();
|
||||
}
|
||||
} );
|
||||
} )();
|
||||
@@ -32,6 +32,7 @@
|
||||
if ( 1 === $i ) {
|
||||
echo '<h3>' . esc_html__( 'About', 'peptide-starter' ) . '</h3>';
|
||||
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>';
|
||||
echo '<p style="margin-top: var(--spacing-md); font-size: 0.9rem;"><a href="' . esc_url( home_url( '/subscribe' ) ) . '" style="color: var(--color-accent-green, #5a7c5a); font-weight: 600;">' . esc_html__( 'Get our weekly digest →', 'peptide-starter' ) . '</a></p>';
|
||||
} elseif ( 2 === $i ) {
|
||||
echo '<h3>' . esc_html__( 'Quick Links', 'peptide-starter' ) . '</h3>';
|
||||
echo '<ul>';
|
||||
@@ -56,6 +57,7 @@
|
||||
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( '/our-methodology' ) ) . '">' . esc_html__( 'Our Methodology', 'peptide-starter' ) . '</a></li>';
|
||||
echo '<li><a href="' . esc_url( home_url( '/about' ) ) . '">' . esc_html__( 'Editorial Standards', 'peptide-starter' ) . '</a></li>';
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ $featured_monograph_ids = array( 211, 36, 177 );
|
||||
<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>
|
||||
<p class="hero-verdict-explainer__desc"><?php echo peptide_starter_render_desc( $v['description'], $v['abbrs'] ?? array() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output is built from esc_html/esc_attr components inside render_desc(). ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
@@ -204,7 +204,7 @@ $featured_monograph_ids = array( 211, 36, 177 );
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Research Modules Grid (6 cards) -->
|
||||
<!-- Research Modules Grid (5 cards) -->
|
||||
<?php get_template_part( 'template-parts/module', 'cards' ); ?>
|
||||
|
||||
<!-- PRAutoBlogger Posts Widget -->
|
||||
@@ -214,19 +214,22 @@ $featured_monograph_ids = array( 211, 36, 177 );
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- News Feed Section -->
|
||||
<!-- News Teaser Section — 3 items + "View all" CTA -->
|
||||
<?php
|
||||
if ( shortcode_exists( 'peptide_news' ) ) {
|
||||
?>
|
||||
<section class="news-feed-section">
|
||||
<section class="news-teaser-section">
|
||||
<div class="ps-container">
|
||||
<div class="news-feed-header">
|
||||
<h2 class="news-feed-title"><?php esc_html_e( 'What\'s new', 'peptide-starter' ); ?></h2>
|
||||
<p class="news-feed-description">
|
||||
<?php esc_html_e( 'Recent peptide research, regulatory shifts, and synthesis news. New posts weekly.', 'peptide-starter' ); ?>
|
||||
</p>
|
||||
<div class="news-teaser-header">
|
||||
<div>
|
||||
<h2><?php esc_html_e( 'Latest Peptide Research', 'peptide-starter' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Recent publications from leading research sources.', 'peptide-starter' ); ?></p>
|
||||
</div>
|
||||
<a href="<?php echo esc_url( home_url( '/news' ) ); ?>" class="ps-btn ps-btn-secondary">
|
||||
<?php esc_html_e( 'View all research →', 'peptide-starter' ); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php echo do_shortcode( '[peptide_news]' ); ?>
|
||||
<?php echo do_shortcode( '[peptide_news count="3"]' ); ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
@@ -236,3 +239,4 @@ $featured_monograph_ids = array( 211, 36, 177 );
|
||||
</main>
|
||||
|
||||
<?php get_footer(); ?>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
// Theme constants.
|
||||
define( 'PEPTIDE_STARTER_VERSION', '2.0.0-alpha.3' );
|
||||
define( 'PEPTIDE_STARTER_VERSION', '2.2.2' );
|
||||
define( 'PEPTIDE_STARTER_DIR', get_template_directory() );
|
||||
define( 'PEPTIDE_STARTER_URI', get_template_directory_uri() );
|
||||
|
||||
@@ -88,6 +88,11 @@ function peptide_starter_scripts() {
|
||||
wp_enqueue_script( 'peptide-starter-theme', PEPTIDE_STARTER_URI . '/assets/js/theme.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||
wp_enqueue_script( 'peptide-starter-settings-panel', PEPTIDE_STARTER_URI . '/assets/js/settings-panel.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||
|
||||
// Abbreviation popovers — only needed on the front page verdict legend strip.
|
||||
if ( is_front_page() ) {
|
||||
wp_enqueue_script( 'peptide-starter-abbr-tooltip', PEPTIDE_STARTER_URI . '/assets/js/abbr-tooltip.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'peptide-starter-theme',
|
||||
'peptideStarterData',
|
||||
@@ -244,3 +249,20 @@ function peptide_starter_inline_dark_mode_script() {
|
||||
<?php
|
||||
}
|
||||
add_action( 'wp_head', 'peptide_starter_inline_dark_mode_script' );
|
||||
|
||||
/**
|
||||
* Enqueue Beehiiv attribution script site-wide.
|
||||
*
|
||||
* Loads Beehiiv's attribution.js in the footer so all signup form placements
|
||||
* (subscribe page iframe, single-peptide inline form) are tracked correctly
|
||||
* under a single script include.
|
||||
*
|
||||
* Who calls it: wp_enqueue_scripts action (all front-end page loads).
|
||||
* Dependencies: none.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function peptide_starter_enqueue_beehiiv_attribution() {
|
||||
wp_enqueue_script( 'beehiiv-attribution', 'https://subscribe-forms.beehiiv.com/attribution.js', array(), null, true );
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'peptide_starter_enqueue_beehiiv_attribution' );
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* 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).
|
||||
* @return array[] Each element: state (string), glyph (string), label (string), description (string), abbrs (array, optional).
|
||||
*/
|
||||
function peptide_starter_get_verdict_taxonomy() {
|
||||
return array(
|
||||
@@ -28,6 +28,10 @@ function peptide_starter_get_verdict_taxonomy() {
|
||||
'glyph' => '✓',
|
||||
'label' => 'Established',
|
||||
'description' => 'Strong RCT evidence; widely prescribed or validated.',
|
||||
// abbrs: terms in description that should render as inline tap-targets.
|
||||
'abbrs' => array(
|
||||
'RCT' => 'Randomized Controlled Trial — the gold standard for clinical evidence. Participants are randomly assigned to treatment or control groups to measure real effect.',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'state' => 'promising',
|
||||
@@ -88,3 +92,46 @@ function peptide_starter_get_verdict_config() {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a description string with optional inline abbreviation tap-targets.
|
||||
*
|
||||
* What: Builds safe HTML for a verdict card description, replacing known
|
||||
* abbreviation terms with <abbr> elements that trigger a tooltip.
|
||||
* Who calls it: front-page.php verdict explainer loop.
|
||||
* Depends on: Nothing — pure string transformation.
|
||||
*
|
||||
* The output is intentionally built from escaped components (not from raw $text)
|
||||
* so we never expose unescaped user or data-layer content. Each substitution is:
|
||||
* esc_html( $text ) → str_replace( literal_term, safe_abbr_html )
|
||||
* This is safe because the abbr tag, class, and data-def are all hardcoded or
|
||||
* escaped here, and the term key is a literal constant from our own data array.
|
||||
*
|
||||
* @param string $text Plain-text description string. Will be HTML-escaped.
|
||||
* @param array $abbrs Associative array of term => definition. Keys are literal
|
||||
* strings (e.g. 'RCT'). Values are plain-text definitions.
|
||||
* @return string Safe HTML string suitable for echo without further escaping.
|
||||
*
|
||||
* @see inc/verdict-helpers.php — $abbrs data lives in peptide_starter_get_verdict_taxonomy()
|
||||
* @see assets/js/abbr-tooltip.js — JS that handles tap/click to show the popover
|
||||
* @see style.css — .pr-abbr and .pr-abbr-popover styles
|
||||
*/
|
||||
function peptide_starter_render_desc( string $text, array $abbrs ): string {
|
||||
$html = esc_html( $text );
|
||||
|
||||
foreach ( $abbrs as $term => $definition ) {
|
||||
$safe_term = esc_html( $term );
|
||||
$safe_def = esc_attr( $definition );
|
||||
$abbr_html = sprintf(
|
||||
'<abbr class="pr-abbr" data-def="%s" tabindex="0" role="button" aria-label="%s: %s">%s</abbr>',
|
||||
$safe_def,
|
||||
$safe_term,
|
||||
$safe_def,
|
||||
$safe_term
|
||||
);
|
||||
// Replace only the first occurrence — abbreviations should not stack.
|
||||
$html = preg_replace( '/' . preg_quote( $safe_term, '/' ) . '/', $abbr_html, $html, 1 );
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
46
page-our-methodology.php
Normal file
46
page-our-methodology.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Name: Our Methodology
|
||||
*
|
||||
* Two-column layout with sticky sidebar ToC for the editorial methodology page.
|
||||
* Mirrors page-how-we-review.php layout; CSS classes use page-our-methodology prefix.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<main id="main" class="site-main">
|
||||
<div class="page-our-methodology">
|
||||
<div class="page-our-methodology__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-our-methodology__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();
|
||||
44
page-subscribe.php
Executable file
44
page-subscribe.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Name: Subscribe
|
||||
*
|
||||
* Single-column, signup-form-centric layout for the /subscribe/ landing page.
|
||||
* Lighter than the methodology / how-we-review templates — no sticky two-column ToC.
|
||||
* This is a conversion page, not a reference page.
|
||||
*
|
||||
* Who calls it: WordPress page loader when page_template meta = page-subscribe.php
|
||||
* Dependencies: Beehiiv embed script (loaded inline below); attribution.js loaded
|
||||
* site-wide via peptide_starter_enqueue_beehiiv_attribution() in functions.php
|
||||
*
|
||||
* @see footer.php — newsletter CTA in footer column 1
|
||||
* @see single-peptide.php — compact inline form on monograph pages
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<main id="main" class="site-main">
|
||||
<div class="page-subscribe">
|
||||
<div class="page-subscribe__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>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
@@ -2,18 +2,8 @@
|
||||
/**
|
||||
* The single peptide template
|
||||
*
|
||||
* Displays a single peptide with verdict card hero, content, affiliate
|
||||
* 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.
|
||||
* Displays a single peptide with back link, content box,
|
||||
* and meta information in a card grid layout.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
@@ -26,9 +16,6 @@ get_header();
|
||||
if ( have_posts() ) {
|
||||
while ( have_posts() ) {
|
||||
the_post();
|
||||
|
||||
// Determine verdict state once — used in two places below.
|
||||
$verdict_state = get_post_meta( get_the_ID(), 'verdict_state', true );
|
||||
?>
|
||||
<!-- Page Header -->
|
||||
<header class="page-header">
|
||||
@@ -41,7 +28,7 @@ get_header();
|
||||
</a>
|
||||
|
||||
<?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 ) ) {
|
||||
?>
|
||||
<span class="ps-featured-badge">★ <?php esc_html_e( 'Featured', 'peptide-starter' ); ?></span>
|
||||
@@ -49,19 +36,10 @@ get_header();
|
||||
}
|
||||
?>
|
||||
|
||||
<?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
|
||||
}
|
||||
?>
|
||||
<h1 class="page-title"><?php the_title(); ?></h1>
|
||||
|
||||
<?php
|
||||
// Category tag pills.
|
||||
// Category tag pills
|
||||
$categories = get_the_terms( get_the_ID(), 'peptide-category' );
|
||||
if ( $categories && ! is_wp_error( $categories ) ) {
|
||||
echo '<div class="peptide-card__meta" style="margin-top: var(--spacing-sm);">';
|
||||
@@ -74,17 +52,6 @@ get_header();
|
||||
</div>
|
||||
</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 -->
|
||||
<div class="page-content">
|
||||
<article class="peptide-single" id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||
@@ -102,22 +69,6 @@ get_header();
|
||||
</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 -->
|
||||
<?php
|
||||
$formula = get_post_meta( get_the_ID(), 'peptide_formula', true );
|
||||
@@ -188,6 +139,25 @@ get_header();
|
||||
?>
|
||||
</article>
|
||||
|
||||
<!-- Newsletter Signup — compact inline form below monograph content -->
|
||||
<div class="peptide-newsletter-cta" style="margin-top: var(--spacing-3xl); padding: var(--spacing-2xl); background: var(--color-surface-subtle, #f8f8f6); border-radius: 8px; border: 1px solid var(--color-border-default);">
|
||||
<p style="margin: 0 0 var(--spacing-md); font-size: 0.95rem; color: var(--color-text-secondary);">
|
||||
<?php esc_html_e( 'Want updates on monographs like this? One email a week, no spam.', 'peptide-starter' ); ?>
|
||||
</p>
|
||||
<div class="beehiiv-embed-wrap" style="max-width: 100%; overflow: hidden;">
|
||||
<script async src="https://subscribe-forms.beehiiv.com/embed.js"></script>
|
||||
<iframe
|
||||
src="https://subscribe-forms.beehiiv.com/6edcd482-cbea-4767-8777-97ac17d6d609"
|
||||
class="beehiiv-embed"
|
||||
data-test-id="beehiiv-embed"
|
||||
frameborder="0"
|
||||
scrolling="no"
|
||||
style="width: 100%; max-width: 100%; height: 180px; margin: 0; border-radius: 4px; background-color: transparent; box-shadow: 0 0 #0000;"
|
||||
title="<?php esc_attr_e( 'Subscribe to Repo Weekly', 'peptide-starter' ); ?>"
|
||||
> </iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="post-navigation" style="margin-top: var(--spacing-3xl); padding-top: var(--spacing-2xl); border-top: 1px solid var(--color-border-default);">
|
||||
<div style="display: flex; justify-content: space-between; gap: var(--spacing-lg);">
|
||||
|
||||
586
style.css
586
style.css
@@ -4,7 +4,7 @@ Theme URI: https://github.com/peptiderepo/peptide-starter
|
||||
Description: Premium, accessible WordPress theme for scientific peptide reference database
|
||||
Author: Peptide Repo
|
||||
Author URI: https://peptiderepo.com
|
||||
Version: 2.1.4
|
||||
Version: 2.2.2
|
||||
License: GPL v2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
Text Domain: peptide-starter
|
||||
@@ -165,8 +165,7 @@ img, svg {
|
||||
|
||||
/* ---- LIGHT MODE COLOR PALETTE ---- */
|
||||
/* Brand Colors */
|
||||
--color-brand-primary: var(--teal-500); /* Brand rebrand v2.0.0 — Direction C */
|
||||
--color-brand-hover: var(--teal-600);
|
||||
--color-brand-primary: #0066CC;
|
||||
--color-status-success: #059669;
|
||||
--color-status-warning: #F59E0B;
|
||||
--color-status-error: #DC2626;
|
||||
@@ -197,10 +196,10 @@ img, svg {
|
||||
--color-bg-tertiary: #EDEAE3;
|
||||
|
||||
/* Component-Specific Colors */
|
||||
--color-input-focus: var(--teal-500);
|
||||
--color-link-hover: var(--teal-600);
|
||||
--color-badge-bg: var(--teal-50);
|
||||
--color-badge-text: var(--teal-700);
|
||||
--color-input-focus: #0066CC;
|
||||
--color-link-hover: #0052A3;
|
||||
--color-badge-bg: #DBEAFE;
|
||||
--color-badge-text: #0066CC;
|
||||
--color-code-bg: #F3F4F6;
|
||||
--color-code-text: #111827;
|
||||
|
||||
@@ -221,10 +220,10 @@ img, svg {
|
||||
--transition-slow: 300ms ease-in-out;
|
||||
|
||||
/* ---- FOCUS RING (per Design Director fix: 15% opacity) ---- */
|
||||
--focus-color: var(--teal-500);
|
||||
--focus-color: #0066CC;
|
||||
--focus-width: 2px;
|
||||
--focus-offset: 2px;
|
||||
--focus-shadow: 0 0 0 3px rgba(27, 138, 146, 0.15);
|
||||
--focus-shadow: 0 0 0 4px rgba(0, 102, 204, 0.15);
|
||||
|
||||
/* ---- CONTAINER WIDTHS ---- */
|
||||
--container-sm: 640px;
|
||||
@@ -499,7 +498,7 @@ nav a {
|
||||
.ps-btn-primary:hover {
|
||||
background-color: var(--color-link-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 16px rgba(27, 138, 146, 0.2);
|
||||
box-shadow: 0 4px 16px rgba(0, 102, 204, 0.2);
|
||||
}
|
||||
|
||||
.ps-btn-primary:active {
|
||||
@@ -559,7 +558,7 @@ nav a {
|
||||
|
||||
.ps-card:hover {
|
||||
border-color: var(--color-brand-primary);
|
||||
box-shadow: 0 4px 12px rgba(27, 138, 146, 0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.1);
|
||||
}
|
||||
|
||||
.ps-card-header {
|
||||
@@ -742,9 +741,9 @@ input[type="radio"] {
|
||||
}
|
||||
|
||||
.ps-alert-info {
|
||||
background-color: var(--teal-50);
|
||||
background-color: #DBEAFE;
|
||||
border-color: var(--color-brand-primary);
|
||||
color: var(--teal-700);
|
||||
color: #0652CD;
|
||||
}
|
||||
|
||||
.ps-alert-success {
|
||||
@@ -1120,7 +1119,7 @@ input[type="radio"] {
|
||||
.hero-section::before {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: rgba(27, 138, 146, 0.07);
|
||||
background: rgba(244, 114, 182, 0.10);
|
||||
top: 8%;
|
||||
left: -4%;
|
||||
}
|
||||
@@ -1129,7 +1128,7 @@ input[type="radio"] {
|
||||
.hero-section::after {
|
||||
width: 260px;
|
||||
height: 260px;
|
||||
background: rgba(127, 214, 0, 0.07);
|
||||
background: rgba(52, 211, 153, 0.08);
|
||||
top: 20%;
|
||||
right: -6%;
|
||||
}
|
||||
@@ -1165,7 +1164,7 @@ input[type="radio"] {
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 6px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 8'%3E%3Cpath d='M0 4 Q12.5 0 25 4 T50 4 T75 4 T100 4' fill='none' stroke='%231B8A92' stroke-width='2'/%3E%3C/svg%3E") repeat-x;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 8'%3E%3Cpath d='M0 4 Q12.5 0 25 4 T50 4 T75 4 T100 4' fill='none' stroke='%230066CC' stroke-width='2'/%3E%3C/svg%3E") repeat-x;
|
||||
background-size: 50px 6px;
|
||||
}
|
||||
|
||||
@@ -1229,18 +1228,16 @@ input[type="radio"] {
|
||||
}
|
||||
|
||||
.hero-search .psa-search-btn {
|
||||
background-color: var(--lime-500); /* CMO brief §6: lime submit in hero */
|
||||
color: var(--color-ink);
|
||||
background-color: var(--color-brand-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
|
||||
cursor: pointer;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
transition: background-color var(--transition-normal);
|
||||
}
|
||||
|
||||
.hero-search .psa-search-btn:hover {
|
||||
background-color: var(--lime-600);
|
||||
background-color: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
.hero-search .psa-search-results {
|
||||
@@ -1364,204 +1361,6 @@ input[type="radio"] {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
|
||||
/* ---- VERDICT TAXONOMY EXPLAINER (homepage) ---- */
|
||||
|
||||
.hero-verdict-explainer {
|
||||
background: var(--bg-inset, var(--gray-100));
|
||||
padding: var(--spacing-2xl) var(--spacing-xl);
|
||||
border-top: 1px solid var(--border-default, #E8E8E8);
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__heading {
|
||||
text-align: center;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: var(--text-secondary, var(--color-text-secondary));
|
||||
margin-bottom: var(--spacing-xl);
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: var(--spacing-md);
|
||||
max-width: var(--container-xl);
|
||||
margin: 0 auto var(--spacing-xl);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__desc {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted, var(--color-text-tertiary));
|
||||
line-height: 1.5;
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__cta-line {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- FEATURED VERDICT CARDS (homepage) ---- */
|
||||
|
||||
.hero-featured-verdicts {
|
||||
padding: var(--spacing-3xl) var(--spacing-xl);
|
||||
background: var(--bg-surface, #FFFFFF);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__title {
|
||||
text-align: center;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 28px;
|
||||
color: var(--text-primary, var(--color-text-primary));
|
||||
margin-bottom: var(--spacing-sm);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__subtitle {
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
color: var(--text-secondary, var(--color-text-secondary));
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--spacing-xl);
|
||||
max-width: var(--container-xl);
|
||||
margin: 0 auto var(--spacing-xl);
|
||||
}
|
||||
|
||||
.hero-verdict-card {
|
||||
background: var(--bg-surface, #FFFFFF);
|
||||
border: 1px solid var(--border-default, #E8E8E8);
|
||||
border-radius: var(--radius-lg, 12px);
|
||||
padding: var(--spacing-xl);
|
||||
box-shadow: 0 1px 2px rgba(13, 13, 13, 0.04), 0 2px 8px rgba(13, 13, 13, 0.04);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
transition: box-shadow var(--transition-normal);
|
||||
}
|
||||
|
||||
.hero-verdict-card:hover {
|
||||
box-shadow: 0 2px 4px rgba(13, 13, 13, 0.06), 0 8px 16px rgba(13, 13, 13, 0.06);
|
||||
}
|
||||
|
||||
.hero-verdict-card__badge {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.hero-verdict-card__title {
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
color: var(--text-primary, var(--color-text-primary));
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.hero-verdict-card__title a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hero-verdict-card__title a:hover {
|
||||
color: var(--teal-600, var(--color-brand-primary));
|
||||
}
|
||||
|
||||
.hero-verdict-card__summary {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: var(--text-secondary, var(--color-text-secondary));
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hero-verdict-card__signals {
|
||||
list-style: none;
|
||||
background: var(--bg-inset, var(--gray-100));
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.hero-verdict-card__signal-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-sm);
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 13px;
|
||||
color: var(--text-primary, var(--color-text-primary));
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.hero-verdict-card__signal-glyph {
|
||||
flex-shrink: 0;
|
||||
font-weight: 600;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-verdict-card__link {
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--teal-600, var(--color-brand-primary));
|
||||
text-decoration: none;
|
||||
align-self: flex-start;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.hero-verdict-card__link:hover {
|
||||
color: var(--teal-700);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Responsive: verdict explainer → 2-col on tablet, 1-col on mobile */
|
||||
@media (max-width: 900px) {
|
||||
.hero-verdict-explainer__grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.hero-verdict-explainer__grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__desc {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- PEPTIDE NEWS PLUGIN OVERRIDES ---- */
|
||||
/* Remove line-clamp so full article summaries are visible.
|
||||
Uses body prefix for specificity to beat the plugin's own stylesheet
|
||||
@@ -1630,7 +1429,7 @@ body .pn-article-title {
|
||||
position: absolute;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background: rgba(127, 214, 0, 0.07);
|
||||
background: rgba(52, 211, 153, 0.08);
|
||||
border-radius: 50%;
|
||||
top: -40px;
|
||||
right: -40px;
|
||||
@@ -1830,7 +1629,7 @@ body .pn-article-title {
|
||||
position: absolute;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
background: rgba(27, 138, 146, 0.07);
|
||||
background: rgba(244, 114, 182, 0.08);
|
||||
border-radius: 50%;
|
||||
top: -30px;
|
||||
right: -30px;
|
||||
@@ -1875,6 +1674,13 @@ body .pn-article-title {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
/* Collapse empty Elementor/page-builder containers that add phantom space */
|
||||
.page-content .elementor-section:empty,
|
||||
.page-content .elementor-widget-wrap:empty,
|
||||
.page-content .elementor-element:empty,
|
||||
.page-content div:empty:not([data-peptide-news-feed]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ---- BACK LINK (breadcrumb-style) ---- */
|
||||
|
||||
@@ -2227,8 +2033,7 @@ html[data-theme="dark"] {
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
--color-brand-primary: var(--teal-500); /* Brand rebrand v2.0.0 — Direction C */
|
||||
--color-brand-hover: var(--teal-600);
|
||||
--color-brand-primary: #0066CC;
|
||||
--color-status-success: #059669;
|
||||
--color-status-warning: #F59E0B;
|
||||
--color-status-error: #DC2626;
|
||||
@@ -2247,13 +2052,13 @@ html[data-theme="light"] {
|
||||
--color-bg-primary: #FAFAF7;
|
||||
--color-bg-secondary: #F5F3EE;
|
||||
--color-bg-tertiary: #EDEAE3;
|
||||
--color-input-focus: var(--teal-500);
|
||||
--color-link-hover: var(--teal-600);
|
||||
--color-badge-bg: var(--teal-50);
|
||||
--color-badge-text: var(--teal-700);
|
||||
--color-input-focus: #0066CC;
|
||||
--color-link-hover: #0052A3;
|
||||
--color-badge-bg: #DBEAFE;
|
||||
--color-badge-text: #0066CC;
|
||||
--color-code-bg: #F3F4F6;
|
||||
--color-code-text: #111827;
|
||||
--focus-shadow: 0 0 0 3px rgba(27, 138, 146, 0.15);
|
||||
--focus-shadow: 0 0 0 4px rgba(0, 102, 204, 0.15);
|
||||
}
|
||||
|
||||
/* Dark mode alert colors */
|
||||
@@ -2533,12 +2338,11 @@ html[data-theme="light"] {
|
||||
text-decoration: none;
|
||||
color: var(--color-text-primary);
|
||||
transition: all var(--transition-normal);
|
||||
position: relative; /* Required for .ps-module-card__badge absolute positioning */
|
||||
}
|
||||
|
||||
.ps-module-card:hover {
|
||||
border-color: var(--color-brand-primary);
|
||||
box-shadow: 0 4px 16px rgba(27, 138, 146, 0.1);
|
||||
box-shadow: 0 4px 16px rgba(0, 102, 204, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@@ -2547,27 +2351,6 @@ html[data-theme="light"] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* "Coming soon" badge — overlays top-right corner of inactive tool cards.
|
||||
* Layered on top of --inactive treatment (dim + no-hover); does not replace it.
|
||||
* Spec: Inter 600, 10px, --gray-200 bg, --gray-700 text, pill shape.
|
||||
*/
|
||||
.ps-module-card__badge {
|
||||
position: absolute;
|
||||
top: var(--spacing-md);
|
||||
right: var(--spacing-md);
|
||||
padding: 2px 8px;
|
||||
background-color: var(--gray-200);
|
||||
color: var(--gray-700);
|
||||
border-radius: var(--radius-pill);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.03em;
|
||||
line-height: 1.6;
|
||||
pointer-events: none; /* Badge is presentational; card's pointer-events handles clicks */
|
||||
}
|
||||
|
||||
.ps-module-card__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -2575,7 +2358,7 @@ html[data-theme="light"] {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(27, 138, 146, 0.1);
|
||||
background-color: rgba(0, 102, 204, 0.1);
|
||||
color: var(--color-brand-primary);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
@@ -3389,16 +3172,293 @@ html[data-theme="dark"] .ps-verify-required {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ─── Cookie Notice brand alignment ─────────────────────────────────────────
|
||||
* The Cookie Notice plugin (Hu-manity.co) renders Accept/Privacy buttons with
|
||||
* an inline style="background-color: #00a99d" (plugin default teal). Override
|
||||
* with !important to match brand teal. Inline styles beat selectors without it.
|
||||
*/
|
||||
#cn-accept-cookie,
|
||||
#cn-more-info {
|
||||
background-color: var(--teal-500) !important; /* #1B8A92 — overrides plugin's #00a99d inline style */
|
||||
/* ============================================================================
|
||||
HOMEPAGE — VERDICT EXPLAINER SECTION
|
||||
hero-verdict-explainer: "Every peptide gets a verdict. Here's what they mean."
|
||||
5-state explainer grid above the featured verdict cards.
|
||||
============================================================================ */
|
||||
|
||||
.hero-verdict-explainer {
|
||||
padding: var(--spacing-3xl) 0; /* horizontal padding handled by ps-container inside */
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
#cn-accept-cookie:hover,
|
||||
#cn-more-info:hover {
|
||||
background-color: var(--teal-600) !important;
|
||||
|
||||
.hero-verdict-explainer__heading {
|
||||
max-width: var(--container-lg);
|
||||
margin: 0 auto var(--spacing-xl);
|
||||
font-size: var(--text-h3);
|
||||
font-weight: var(--text-h3-weight);
|
||||
line-height: var(--text-h3-lh);
|
||||
letter-spacing: var(--text-h3-ls);
|
||||
color: var(--color-text-primary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__grid {
|
||||
max-width: var(--container-lg);
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr; /* single column on narrow phones; 2-col added at 520px below */
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-md);
|
||||
background-color: var(--color-bg-primary);
|
||||
border: 1px solid var(--color-border-default);
|
||||
border-radius: var(--radius-md);
|
||||
min-width: 0; /* prevent grid item from blowing out column */
|
||||
}
|
||||
|
||||
/* Cap pill width to column so Insufficient Evidence pill never overflows */
|
||||
.hero-verdict-explainer__item .pr-verdict {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__desc {
|
||||
font-size: var(--text-body-md);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__cta-line {
|
||||
max-width: var(--container-lg);
|
||||
margin: var(--spacing-xl) auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__cta-line a {
|
||||
color: var(--color-brand-primary);
|
||||
font-size: var(--text-body-lg);
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__cta-line a:hover {
|
||||
color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
HOMEPAGE — FEATURED VERDICT CARDS SECTION
|
||||
hero-featured-verdicts: 3 pinned monographs showing the verdict system in action.
|
||||
Classes mirror BEM pattern used in front-page.php.
|
||||
============================================================================ */
|
||||
|
||||
.hero-featured-verdicts {
|
||||
padding: var(--spacing-3xl) var(--spacing-lg);
|
||||
background-color: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__title {
|
||||
font-size: var(--text-h2);
|
||||
font-weight: var(--text-h2-weight);
|
||||
line-height: var(--text-h2-lh);
|
||||
letter-spacing: var(--text-h2-ls);
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__subtitle {
|
||||
font-size: var(--text-body-lg);
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.hero-featured-verdicts__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--spacing-xl);
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
/* Individual verdict card */
|
||||
.hero-verdict-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-xl);
|
||||
background-color: var(--color-bg-primary);
|
||||
border: 1px solid var(--color-border-default);
|
||||
border-left: 3px solid var(--color-border-default);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.hero-verdict-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Badge sits at top of card — already styled by .pr-verdict in brand.css */
|
||||
.hero-verdict-card__badge {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
/* Tint left border to match verdict state — uses CSS :has() (supported in all modern browsers) */
|
||||
.hero-verdict-card:has(.pr-verdict--established) {
|
||||
border-left-color: var(--verdict-established-bg, #059669);
|
||||
}
|
||||
.hero-verdict-card:has(.pr-verdict--promising) {
|
||||
border-left-color: var(--verdict-promising-bg, #10b981);
|
||||
}
|
||||
.hero-verdict-card:has(.pr-verdict--investigational) {
|
||||
border-left-color: var(--verdict-investigational-bg, #f59e0b);
|
||||
}
|
||||
.hero-verdict-card:has(.pr-verdict--insufficient) {
|
||||
border-left-color: var(--verdict-insufficient-bg, #6b7280);
|
||||
}
|
||||
.hero-verdict-card:has(.pr-verdict--cautionary) {
|
||||
border-left-color: var(--verdict-cautionary-bg, #dc2626);
|
||||
}
|
||||
|
||||
.hero-verdict-card__title {
|
||||
font-size: var(--text-h3);
|
||||
font-weight: var(--text-h3-weight);
|
||||
line-height: var(--text-h3-lh);
|
||||
letter-spacing: var(--text-h3-ls);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero-verdict-card__title a {
|
||||
color: var(--color-brand-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hero-verdict-card__title a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
.hero-verdict-card__summary {
|
||||
font-size: var(--text-body-lg);
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Signal rows list */
|
||||
.hero-verdict-card__signals {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
padding: var(--spacing-md);
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.hero-verdict-card__signal-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--spacing-sm);
|
||||
font-size: var(--text-body-md);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.hero-verdict-card__signal-glyph {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* "Read the full evidence →" link */
|
||||
.hero-verdict-card__link {
|
||||
align-self: flex-start;
|
||||
color: var(--color-brand-primary);
|
||||
font-size: var(--text-body-lg);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.hero-verdict-card__link:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
/* "Browse all peptides" footer row */
|
||||
.hero-featured-verdicts__footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- RESPONSIVE — Featured Verdict Cards ---- */
|
||||
|
||||
@media (min-width: 520px) {
|
||||
.hero-verdict-explainer__grid {
|
||||
grid-template-columns: repeat(2, 1fr); /* 520px+ gives columns wide enough for all pills */
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.hero-verdict-explainer__grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.hero-featured-verdicts__grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.hero-verdict-explainer__grid {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Abbreviation tooltip (.pr-abbr / .pr-abbr-popover)
|
||||
Inline tap-targets for glossary terms in the verdict legend strip.
|
||||
@see inc/verdict-helpers.php — peptide_starter_render_desc() emits .pr-abbr
|
||||
@see assets/js/abbr-tooltip.js — JS popover logic
|
||||
========================================================================== */
|
||||
|
||||
.pr-abbr {
|
||||
border-bottom: 1px dashed currentColor;
|
||||
cursor: help;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
/* Slightly muted so the dashed line reads as "interactive" not "broken link". */
|
||||
opacity: 0.85;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.pr-abbr:hover,
|
||||
.pr-abbr:focus,
|
||||
.pr-abbr--active {
|
||||
opacity: 1;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pr-abbr-popover {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
max-width: min( 280px, calc( 100vw - 32px ) );
|
||||
padding: 0.6rem 0.85rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5;
|
||||
/* Use theme surface colours so dark-mode inherits correctly. */
|
||||
background-color: var(--color-surface-elevated, #1e1e2e);
|
||||
color: var(--color-text-primary, #e2e2f0);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
|
||||
/* Subtle fade-in. */
|
||||
animation: pr-popover-in 0.12s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes pr-popover-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY( -4px );
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* Template Part: Research Module Cards
|
||||
*
|
||||
* Renders a 6-card grid of research modules (tools/sections) for the front page.
|
||||
* Renders a 5-card grid of research modules (tools/sections) for the front page.
|
||||
* 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.
|
||||
@@ -58,14 +58,6 @@ $modules = array(
|
||||
'available' => shortcode_exists( 'peptide_tracker' ),
|
||||
'coming_soon' => true,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Science Feed', 'peptide-starter' ),
|
||||
'description' => __( 'What\'s new in peptide research. Filtered for what matters, weekly.', 'peptide-starter' ),
|
||||
'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>',
|
||||
'available' => shortcode_exists( 'peptide_news' ),
|
||||
'coming_soon' => false,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Subject Log', 'peptide-starter' ),
|
||||
'description' => __( 'Track outcomes over time. Subjective markers, lab results, protocol changes — your own data, kept private.', 'peptide-starter' ),
|
||||
@@ -109,3 +101,4 @@ $modules = array(
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user