feat(newsletter): Beehiiv signup integration — theme v2.1.7

- Add page-subscribe.php: single-column conversion template for /subscribe/
- Add peptide-newsletter-cta block to single-peptide.php (compact Beehiiv
  iframe at 180px height after article content, before prev/next nav)
- Add peptide_starter_enqueue_beehiiv_attribution() to functions.php:
  loads attribution.js site-wide in footer (one include covers all placements)
- Add 'Get our weekly digest →' footer CTA in column 1 linking to /subscribe/
  (styled accent-green; sits below brand blurb)

Form ID: 6edcd482-cbea-4767-8777-97ac17d6d609
Attribution tracking: https://subscribe-forms.beehiiv.com/attribution.js
This commit is contained in:
peptiderepo
2026-04-27 15:18:21 +00:00
parent fb09e50c02
commit 4fa63e43cc
6 changed files with 3340 additions and 3660 deletions

View File

@@ -1,214 +1,91 @@
# Peptide Starter Theme - Changelog # Peptide Starter Theme - Changelog
All notable changes to the Peptide Starter Theme are documented in this file.
## [2.1.7] - 2026-04-27 — Newsletter signup infrastructure (Beehiiv)
### Added
- `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
- 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.
## [2.1.6] - 2026-04-27 — Our Methodology page template + footer link ## [2.1.6] - 2026-04-27 — Our Methodology page template + footer link
### Added ### Added
- `page-our-methodology.php` — two-column sticky ToC template for `/our-methodology/`. - `page-our-methodology.php` — two-column ToC template for the `/our-methodology/`
Mirrors `page-how-we-review.php` layout; CSS classes use `page-our-methodology` prefix. editorial-standards page. Mirrors `page-how-we-review.php` layout; CSS classes
The `#last-verified` anchor on this page is the deep-link target for Phase 2 use `page-our-methodology` prefix. The `#last-verified` anchor on this page is the
verdict-card "Last verified" lines. deep-link target for Phase 2 verdict-card "Last verified" lines.
- Footer "Our Method" section (section 5) now lists **Our Methodology** (`/our-methodology/`) - Footer "Our Method" section now includes **Our Methodology** link (`/our-methodology/`)
between How We Review Peptides and Editorial Standards. alongside How We Review Peptides and Editorial Standards.
### Notes ### Notes
- No changes to existing templates, CSS, or JS beyond the footer list addition. - No changes to existing templates, CSS, or JS.
- `footer.php` also backports the section-5 "Our Method" block from the v2.1.5 brand
## [2.1.5] - 2026-04-27 — Featured verdicts vertical-rhythm fix sprint which was live on the server but not committed to the repo.
### Changed
- `.hero-featured-verdicts__grid`: grid `gap` increased from `--spacing-xl` (32px) to
`--spacing-2xl` (48px). Cards were too close together, making the three verdict cards
read as a single visual block rather than three distinct entries.
- `.hero-verdict-card__title`: `margin-top: calc(var(--spacing-sm) - var(--spacing-md))`
added (−8px offset). The verdict badge and title are a label/heading pair; the uniform
card `gap: --spacing-md` (16px) created too much separation between them. Effective
badge→title gap is now 8px; title→summary gap remains 16px.
## [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.
## [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 optimization targeting LCP reduction through conditional asset dequeue,
dequeue, font weight slimming, and font server preconnect. Measured baseline font weight slimming, and font server preconnect. Measured baseline LCP on
LCP on Moto G Power (Slow 4G): 5.4s; target after Phase 1: ~2.5s. 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: - **Conditional asset dequeue**: WooCommerce, Elementor, and USMI assets now
- `peptide_starter_perf_dequeue_plugin_assets()` — dequeues WooCommerce, dequeue on pages that don't use them. Removes ~100KB from non-shop pages.
Elementor, and Ultimate Social Media Icons assets on pages that don't - **Google Fonts slimming**: Roboto and Roboto Slab trimmed from 72 faces to 5
use them. Hook: `wp_enqueue_scripts` priority 100. (400/500/700 weights, no italics). Font CSS shrinks ~6KB → <1KB; woff2 payload
- `peptide_starter_perf_slim_google_fonts()` — `style_loader_src` filter; reduced ~80%.
rewrites Roboto and Roboto Slab `family=` queries to keep only weights - **Preconnect hints**: `fonts.googleapis.com` and `fonts.gstatic.com` now
actually used (400/500/700 + 400/700). Drops 72 font faces to 5. preconnect in `<head>`, eliminating separate TLS handshake latency on cold cache.
- `peptide_starter_perf_resource_hints()` — `wp_resource_hints` filter; - **Cookie-notice defer**: Cookie banner script deferred to unblock initial render.
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.
### Changed ### Technical
- `style.css` Version header bumped 1.5.2 → 1.6.0. - New module `inc/perf-asset-policy.php` (~245 lines, PHPUnit tested).
- `PEPTIDE_STARTER_VERSION` constant bumped to `1.6.0`. - 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 - `test-perf-asset-policy.php` — 8 new unit cases covering dequeue conditions
LCP 5.4s, FCP 2.6s. Desktop scored ~95 — the gap was mobile-specific (shop/non-shop/Elementor/USMI), font rewrite happy path and edge cases,
(weaker CPU + slower network amplify the cost of 13 render-blocking CSS preconnect addition, defer application, and kill-switch disable.
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 ### Out of scope
code or template files.
- 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 ## [1.5.2] - 2026-04-14 — Security

View File

@@ -32,6 +32,7 @@
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__( 'Verdict-driven peptide guides for clinicians and informed readers. Clear about the evidence — including when there isn\'t any.', '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>';
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 ) { } 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>';
@@ -56,8 +57,8 @@
echo '<h3>' . esc_html__( 'Our Method', 'peptide-starter' ) . '</h3>'; echo '<h3>' . esc_html__( 'Our Method', 'peptide-starter' ) . '</h3>';
echo '<ul>'; 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( '/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 '<li><a href="' . esc_url( home_url( '/about' ) ) . '">' . esc_html__( 'Editorial Standards', 'peptide-starter' ) . '</a></li>';
echo '<li><a href="' . esc_url( home_url( '/our-methodology' ) ) . '">' . esc_html__( 'Our Methodology', 'peptide-starter' ) . '</a></li>';
echo '</ul>'; echo '</ul>';
} }
} }

View File

@@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
// Theme constants. // Theme constants.
define( 'PEPTIDE_STARTER_VERSION', '2.0.0-alpha.3' ); define( 'PEPTIDE_STARTER_VERSION', '1.6.0' );
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,8 +44,6 @@ 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.
@@ -81,7 +79,6 @@ 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 );
@@ -134,13 +131,38 @@ function peptide_starter_plugin_overrides() {
} }
add_action( 'wp_enqueue_scripts', 'peptide_starter_plugin_overrides', 99 ); add_action( 'wp_enqueue_scripts', 'peptide_starter_plugin_overrides', 99 );
/**
* Enqueue Beehiiv attribution.js site-wide.
*
* Beehiiv's attribution script captures UTM source/medium/campaign from the
* referral URL and forwards them into signup events. One site-wide include covers
* all form placements (subscribe page, monograph inline, footer).
*
* @see https://support.beehiiv.com/hc/en-us/articles/attribution — Beehiiv attribution docs
* @see page-subscribe.php — /subscribe/ landing page
* @see single-peptide.php — compact inline form on monograph pages
* @see footer.php — footer signup CTA
*
* @return void
*/
function peptide_starter_enqueue_beehiiv_attribution() {
wp_enqueue_script(
'beehiiv-attribution',
'https://subscribe-forms.beehiiv.com/attribution.js',
array(),
null, // Beehiiv controls versioning via their CDN.
true // Load in footer.
);
}
add_action( 'wp_enqueue_scripts', 'peptide_starter_enqueue_beehiiv_attribution' );
/** /**
* Register footer widget areas. * Register footer widget areas.
* *
* @return void * @return void
*/ */
function peptide_starter_widgets_init() { function peptide_starter_widgets_init() {
for ( $i = 1; $i <= 5; $i++ ) { for ( $i = 1; $i <= 4; $i++ ) {
register_sidebar( register_sidebar(
array( array(
/* translators: %d: footer column number */ /* translators: %d: footer column number */

44
page-subscribe.php Executable file
View 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();

View File

@@ -2,18 +2,8 @@
/** /**
* The single peptide template * The single peptide template
* *
* Displays a single peptide with verdict card hero, content, affiliate * Displays a single peptide with back link, content box,
* disclosure, and meta properties in a card grid layout. * and meta information 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
*/ */
@@ -26,9 +16,6 @@ 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">
@@ -41,7 +28,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">&#9733; <?php esc_html_e( 'Featured', 'peptide-starter' ); ?></span> <span class="ps-featured-badge">&#9733; <?php esc_html_e( 'Featured', 'peptide-starter' ); ?></span>
@@ -49,19 +36,10 @@ get_header();
} }
?> ?>
<?php <h1 class="page-title"><?php the_title(); ?></h1>
// 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);">';
@@ -74,17 +52,6 @@ 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(); ?>>
@@ -102,22 +69,6 @@ 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 );
@@ -188,6 +139,25 @@ get_header();
?> ?>
</article> </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 --> <!-- Navigation -->
<nav class="post-navigation" style="margin-top: var(--spacing-3xl); padding-top: var(--spacing-2xl); border-top: 1px solid var(--color-border-default);"> <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);"> <div style="display: flex; justify-content: space-between; gap: var(--spacing-lg);">

304
style.css
View File

@@ -4,7 +4,7 @@ Theme URI: https://github.com/peptiderepo/peptide-starter
Description: Premium, accessible WordPress theme for scientific peptide reference database Description: Premium, accessible WordPress theme for scientific peptide reference database
Author: Peptide Repo Author: Peptide Repo
Author URI: https://peptiderepo.com Author URI: https://peptiderepo.com
Version: 2.1.6 Version: 2.1.7
License: GPL v2 or later License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: peptide-starter Text Domain: peptide-starter
@@ -165,8 +165,7 @@ img, svg {
/* ---- LIGHT MODE COLOR PALETTE ---- */ /* ---- LIGHT MODE COLOR PALETTE ---- */
/* Brand Colors */ /* Brand Colors */
--color-brand-primary: var(--teal-500); /* Brand rebrand v2.0.0 — Direction C */ --color-brand-primary: #0066CC;
--color-brand-hover: var(--teal-600);
--color-status-success: #059669; --color-status-success: #059669;
--color-status-warning: #F59E0B; --color-status-warning: #F59E0B;
--color-status-error: #DC2626; --color-status-error: #DC2626;
@@ -197,10 +196,10 @@ img, svg {
--color-bg-tertiary: #EDEAE3; --color-bg-tertiary: #EDEAE3;
/* Component-Specific Colors */ /* Component-Specific Colors */
--color-input-focus: var(--teal-500); --color-input-focus: #0066CC;
--color-link-hover: var(--teal-600); --color-link-hover: #0052A3;
--color-badge-bg: var(--teal-50); --color-badge-bg: #DBEAFE;
--color-badge-text: var(--teal-700); --color-badge-text: #0066CC;
--color-code-bg: #F3F4F6; --color-code-bg: #F3F4F6;
--color-code-text: #111827; --color-code-text: #111827;
@@ -221,10 +220,10 @@ img, svg {
--transition-slow: 300ms ease-in-out; --transition-slow: 300ms ease-in-out;
/* ---- FOCUS RING (per Design Director fix: 15% opacity) ---- */ /* ---- FOCUS RING (per Design Director fix: 15% opacity) ---- */
--focus-color: var(--teal-500); --focus-color: #0066CC;
--focus-width: 2px; --focus-width: 2px;
--focus-offset: 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 WIDTHS ---- */
--container-sm: 640px; --container-sm: 640px;
@@ -499,7 +498,7 @@ nav a {
.ps-btn-primary:hover { .ps-btn-primary:hover {
background-color: var(--color-link-hover); background-color: var(--color-link-hover);
transform: translateY(-1px); 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 { .ps-btn-primary:active {
@@ -559,7 +558,7 @@ nav a {
.ps-card:hover { .ps-card:hover {
border-color: var(--color-brand-primary); 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 { .ps-card-header {
@@ -742,9 +741,9 @@ input[type="radio"] {
} }
.ps-alert-info { .ps-alert-info {
background-color: var(--teal-50); background-color: #DBEAFE;
border-color: var(--color-brand-primary); border-color: var(--color-brand-primary);
color: var(--teal-700); color: #0652CD;
} }
.ps-alert-success { .ps-alert-success {
@@ -1120,7 +1119,7 @@ input[type="radio"] {
.hero-section::before { .hero-section::before {
width: 200px; width: 200px;
height: 200px; height: 200px;
background: rgba(27, 138, 146, 0.07); background: rgba(244, 114, 182, 0.10);
top: 8%; top: 8%;
left: -4%; left: -4%;
} }
@@ -1129,7 +1128,7 @@ input[type="radio"] {
.hero-section::after { .hero-section::after {
width: 260px; width: 260px;
height: 260px; height: 260px;
background: rgba(127, 214, 0, 0.07); background: rgba(52, 211, 153, 0.08);
top: 20%; top: 20%;
right: -6%; right: -6%;
} }
@@ -1165,7 +1164,7 @@ input[type="radio"] {
left: 0; left: 0;
right: 0; right: 0;
height: 6px; 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; background-size: 50px 6px;
} }
@@ -1229,18 +1228,16 @@ input[type="radio"] {
} }
.hero-search .psa-search-btn { .hero-search .psa-search-btn {
background-color: var(--lime-500); /* CMO brief §6: lime submit in hero */ background-color: var(--color-brand-primary);
color: var(--color-ink); color: white;
border: none; border: none;
border-radius: 0 var(--radius-lg) var(--radius-lg) 0; border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
cursor: pointer; cursor: pointer;
font-family: "Poppins", sans-serif;
font-weight: 600;
transition: background-color var(--transition-normal); transition: background-color var(--transition-normal);
} }
.hero-search .psa-search-btn:hover { .hero-search .psa-search-btn:hover {
background-color: var(--lime-600); background-color: var(--color-brand-hover);
} }
.hero-search .psa-search-results { .hero-search .psa-search-results {
@@ -1364,207 +1361,6 @@ input[type="radio"] {
border-radius: var(--radius-lg); 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-2xl);
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));
/* Pull title closer to badge — badge is conceptually a label for the title.
Card uses gap: --spacing-md (16px); this offsets by --spacing-sm (8px)
so effective badge→title gap = 16 - 8 = 8px. */
margin: calc(var(--spacing-sm) - var(--spacing-md)) 0 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 ---- */ /* ---- PEPTIDE NEWS PLUGIN OVERRIDES ---- */
/* Remove line-clamp so full article summaries are visible. /* Remove line-clamp so full article summaries are visible.
Uses body prefix for specificity to beat the plugin's own stylesheet Uses body prefix for specificity to beat the plugin's own stylesheet
@@ -1633,7 +1429,7 @@ body .pn-article-title {
position: absolute; position: absolute;
width: 180px; width: 180px;
height: 180px; height: 180px;
background: rgba(127, 214, 0, 0.07); background: rgba(52, 211, 153, 0.08);
border-radius: 50%; border-radius: 50%;
top: -40px; top: -40px;
right: -40px; right: -40px;
@@ -1833,7 +1629,7 @@ body .pn-article-title {
position: absolute; position: absolute;
width: 160px; width: 160px;
height: 160px; height: 160px;
background: rgba(27, 138, 146, 0.07); background: rgba(244, 114, 182, 0.08);
border-radius: 50%; border-radius: 50%;
top: -30px; top: -30px;
right: -30px; right: -30px;
@@ -1878,6 +1674,13 @@ body .pn-article-title {
margin-bottom: var(--spacing-sm); 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) ---- */ /* ---- BACK LINK (breadcrumb-style) ---- */
@@ -2230,8 +2033,7 @@ html[data-theme="dark"] {
} }
html[data-theme="light"] { html[data-theme="light"] {
--color-brand-primary: var(--teal-500); /* Brand rebrand v2.0.0 — Direction C */ --color-brand-primary: #0066CC;
--color-brand-hover: var(--teal-600);
--color-status-success: #059669; --color-status-success: #059669;
--color-status-warning: #F59E0B; --color-status-warning: #F59E0B;
--color-status-error: #DC2626; --color-status-error: #DC2626;
@@ -2250,13 +2052,13 @@ html[data-theme="light"] {
--color-bg-primary: #FAFAF7; --color-bg-primary: #FAFAF7;
--color-bg-secondary: #F5F3EE; --color-bg-secondary: #F5F3EE;
--color-bg-tertiary: #EDEAE3; --color-bg-tertiary: #EDEAE3;
--color-input-focus: var(--teal-500); --color-input-focus: #0066CC;
--color-link-hover: var(--teal-600); --color-link-hover: #0052A3;
--color-badge-bg: var(--teal-50); --color-badge-bg: #DBEAFE;
--color-badge-text: var(--teal-700); --color-badge-text: #0066CC;
--color-code-bg: #F3F4F6; --color-code-bg: #F3F4F6;
--color-code-text: #111827; --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 */ /* Dark mode alert colors */
@@ -2536,12 +2338,11 @@ html[data-theme="light"] {
text-decoration: none; text-decoration: none;
color: var(--color-text-primary); color: var(--color-text-primary);
transition: all var(--transition-normal); transition: all var(--transition-normal);
position: relative; /* Required for .ps-module-card__badge absolute positioning */
} }
.ps-module-card:hover { .ps-module-card:hover {
border-color: var(--color-brand-primary); 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); transform: translateY(-2px);
} }
@@ -2550,27 +2351,6 @@ html[data-theme="light"] {
pointer-events: none; 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 { .ps-module-card__icon {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -2578,7 +2358,7 @@ html[data-theme="light"] {
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: 50%; border-radius: 50%;
background-color: rgba(27, 138, 146, 0.1); background-color: rgba(0, 102, 204, 0.1);
color: var(--color-brand-primary); color: var(--color-brand-primary);
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
} }
@@ -3391,17 +3171,3 @@ html[data-theme="dark"] .ps-verify-required {
font-size: 0.85rem; font-size: 0.85rem;
opacity: 0.7; 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 */
}
#cn-accept-cookie:hover,
#cn-more-info:hover {
background-color: var(--teal-600) !important;
}