Compare commits

...

7 Commits

Author SHA1 Message Date
c8e89e1725 style(home): transparent header at top, opaque navy on scroll
On the home page only, the header sits transparently over the 100vh video
hero so the cinematic background is fully visible. As soon as the user
scrolls, Bricks's native sticky JS adds the `.on-scroll` class to
#brx-header — we fade in the navy background + a soft shadow over 350ms
for a polished cinematic feel.

Implementation : pure CSS via wp_head(), gated on is_front_page() so other
pages keep their solid navy header.

Extras :
- A subtle navy gradient (rgba 0.55 → 0) layered behind the menu at the
  very top via ::before, keeping nav text readable on bright video frames.
  Fades out once .on-scroll kicks in (gradient redundant once bg is solid).
- Box-shadow on scrolled state (0 2px 14px rgba(0,0,0,0.22)) so the
  header lifts off the page content visually.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:26:18 +02:00
98dfa6d8b6 style(home): full-viewport-height video hero (100vh) with vertical centering
Hero section now min-height: 100vh — fills the entire viewport, content
vertically centered via flex-column + justify-content: center. Scrolling
reveals the rest of the page below (3-pillars cards, industries, etc.).

Replaces the previous fixed-padding approach (9rem top, 8rem bottom) which
left visible space below the hero on tall monitors and forced the hero
shorter on mobile.

Padding kept minimal (2rem top/bottom) for breathing room when content
gets close to the viewport edges on very short displays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:22:48 +02:00
621425025c feat(home): cinematic video-background hero with overlay text
Replaces the home page's plain-navy hero with a full-bleed video-background
section + dark navy overlay (60%) for text readability. The video URL is
read from the WP option `av_home_hero_video_url`. If empty, the hero
gracefully falls back to a solid navy background — same layout, same text,
just no video.

Settings (Bricks-native background.videoUrl pattern from container.php
get_background_video_html) :
  - videoUrl : the configured option value
  - videoScale : 'cover'
  - videoAspectRatio : '16/9'
  - overlay : rgba(11,31,58,0.6) navy 60%
  - color fallback while video loads

Typography :
  - H1 : 3.5rem white, weight 800, tight letter-spacing, responsive
    breakpoints down to 1.875rem on mobile
  - Lead : muted-white rgba 0.85
  - CTAs : gold primary + outline-on-dark secondary

Helper mu-plugin av-set-home-video.php :
  /?av_set_home_video=https://...mp4    sets the URL
  /?av_set_home_video=clear              empties (back to navy)

Workflow :
  1. Upload MP4 to Media Library, copy URL
  2. /?av_set_home_video=<URL>
  3. /?av_generate_home=1
  4. Visit / — cinematic hero with looping muted autoplay video bg

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:17:33 +02:00
ce56b2b936 fix(bricks): enable publicTemplates setting so /template/* URLs resolve
Bricks's frontend.php template_redirect() permanently redirects any
/template/* URL to the homepage (site_url(), 301) when :
  - is_singular(bricks_template)
  - !current_user_can_use_builder()
  - !isset(bricks_global_settings.publicTemplates)
  - !maintenance mode

This kicks in BEFORE the Bricks builder check can run, so clicking
"Edit with Bricks" on a template ends up redirected to home → admin
URL → Bricks's own builder.php sees current_post_type as empty (because
the post wasn't actually queried before the redirect) → final redirect
to wp-admin/edit.php?post_type=&bricks_notice=error_post_type with the
"Invalid post type" notice.

Setting bricks_global_settings.publicTemplates = '1' disables this
preemptive redirect. Templates stay excluded from search but their
permalinks resolve normally → builder can attach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:54:09 +02:00
aad32fd0f1 fix(bricks): remove bricks_template from REST bridge entirely
Even adding only 'custom-fields' to bricks_template's supports turned out
to break the Bricks builder ("Invalid post type" on Edit with Bricks for
all templates including pre-existing Header / Footer).

bricks_template is now left exactly as Bricks registers it natively :
  supports : author, revisions, thumbnail, title
  show_in_rest : false

The MCP server route to bricks_template was experimental and we don't use
it actively — direct PHP scripts (av-fix-template-meta, av-generate-*,
av-add-submenus) handle template management. Templates are exposed only
to the Bricks builder, not external REST clients.

case_study + scenario CPTs keep REST exposure (used for content type
generators later).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:48:50 +02:00
86cd37ed80 fix(bricks): stop overriding bricks_template supports — only add custom-fields
The REST bridge was rewriting the `supports` array on bricks_template (and
case_study, scenario) by force-adding ['title','editor','author','custom-fields'].
For bricks_template specifically, Bricks's own registration sets supports =
['author','revisions','thumbnail','title'] — deliberately WITHOUT 'editor',
because the post type is meant to be edited only via the Bricks builder, not
the WP block editor.

Adding 'editor' confused Bricks's builder : it would refuse to open the
template with "Invalid post type" because the post type now claimed to support
two incompatible editors.

Fix : keep the original supports list untouched, only append 'custom-fields'
(which IS required for /wp/v2/<type>/<id> to expose the `meta` field). All
templates (Header #25, Footer #46, Single Post #154) should now open in the
Bricks builder again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:39:44 +02:00
e52a1de268 fix(bricks): correct Single Post template conditions format
Bricks's templateConditions for single posts expects:
  main: 'postType', postType: ['post']      (array of post types)

NOT the singular/object variant I'd used:
  main: 'singular', object: 'post'

The wrong format triggered "Invalid post type" when opening the template
in the Bricks builder editor — Bricks's editor couldn't parse the
condition and refused to load the template.

Source : wp-content/themes/bricks/includes/admin.php line 1101 — the
switch case for 'postType' confirms the expected shape.

For 'any' (entire website, used by Header/Footer templates), no extra
keys are needed beyond main='any'. Section/single templates need this
postType array form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:35:12 +02:00
6 changed files with 282 additions and 18 deletions

View File

@@ -20,19 +20,26 @@ defined('ABSPATH') || exit;
---------------------------------------------------------------- */ ---------------------------------------------------------------- */
add_filter('register_post_type_args', function ($args, $post_type) { add_filter('register_post_type_args', function ($args, $post_type) {
$needs_rest = ['bricks_template', 'case_study', 'scenario']; // bricks_template intentionally excluded : touching its supports/show_in_rest
// breaks Bricks's own builder (it expects the original registration verbatim).
// The MCP server doesn't need REST access to templates anyway — we use direct
// PHP scripts to manage them. Only case_study + scenario stay REST-exposed.
$needs_rest = ['case_study', 'scenario'];
if (! in_array($post_type, $needs_rest, true)) { if (! in_array($post_type, $needs_rest, true)) {
return $args; return $args;
} }
$args['show_in_rest'] = true; $args['show_in_rest'] = true;
$args['rest_base'] = $post_type === 'bricks_template' ? 'bricks_template' : ($post_type . 's'); $args['rest_base'] = $post_type . 's';
$args['rest_controller_class'] = 'WP_REST_Posts_Controller'; $args['rest_controller_class'] = 'WP_REST_Posts_Controller';
$args['supports'] = array_unique(array_merge(
$args['supports'] ?? [], // Only add 'custom-fields' so the meta field is exposed via REST.
['title', 'editor', 'author', 'custom-fields'] // custom-fields needed so /wp/v2/<type> exposes the `meta` field $existing_supports = $args['supports'] ?? [];
)); if (! in_array('custom-fields', $existing_supports, true)) {
$existing_supports[] = 'custom-fields';
}
$args['supports'] = $existing_supports;
return $args; return $args;
}, 10, 2); }, 10, 2);

View File

@@ -0,0 +1,34 @@
<?php
/**
* Plugin Name: Asterion — Enable Bricks publicTemplates setting
* /?av_enable_public_templates=1
*
* Bricks redirects /template/* URLs to the homepage when the user can't use
* the builder AND publicTemplates is unset. This causes "Edit with Bricks"
* to fail with "Invalid post type" because the builder URL (?bricks=run on
* the template permalink) gets redirected before reaching the builder.
*
* The fix : set publicTemplates=1 in Bricks global settings. Templates are
* still excluded from search results, but their permalinks resolve normally
* → the builder can load them.
*/
defined('ABSPATH') || exit;
add_action('template_redirect', function () {
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) return;
if (empty($_GET['av_enable_public_templates'])) return;
header('Content-Type: text/plain; charset=utf-8');
$opts = get_option('bricks_global_settings', []);
if (! is_array($opts)) $opts = [];
$before = $opts['publicTemplates'] ?? '(unset)';
$opts['publicTemplates'] = '1';
update_option('bricks_global_settings', $opts);
echo "bricks_global_settings.publicTemplates : " . var_export($before, true) . " → '1'\n";
echo "\nTemplate URLs (/template/...) should now resolve directly without redirecting to home.\n";
echo "Edit with Bricks on Header / Footer / Single Post should work.\n";
exit;
});

View File

@@ -116,16 +116,8 @@ add_action('template_redirect', function () {
function av_build_home_elements() { function av_build_home_elements() {
$els = []; $els = [];
// 1. HERO — full bleed, large tagline // 1. HERO — full-bleed cinematic video background + overlay text
$els = array_merge($els, av_build_hero('home', [ $els = array_merge($els, av_build_home_video_hero());
'eyebrow' => '',
'tagline' => 'Train like you operate.',
'hero_lead' => "PROSERVE\u{2122} is the modular XR training platform engineered for European security forces. Real recoil, autonomous AI, sovereign by design \u{2014} from solo officer to full academy.",
'ctas' => [
['label' => 'Book a demo', 'href' => '/request-demo/', 'variant' => 'primary'],
['label' => 'Request a T&E Kit', 'href' => '/request-te-kit/', 'variant' => 'secondary'],
],
]));
// 2. THREE PILLARS — 3-card grid (count===3 → 33% each, clean row of 3) // 2. THREE PILLARS — 3-card grid (count===3 → 33% each, clean row of 3)
$els = array_merge($els, av_build_cards_section('homep', 0, [ $els = array_merge($els, av_build_cards_section('homep', 0, [
@@ -230,6 +222,138 @@ function av_build_home_elements() {
return $els; return $els;
} }
/**
* Cinematic full-bleed hero with background video + dark overlay + light text.
* Video URL is read from the WP option `av_home_hero_video_url`. If empty,
* the hero falls back to a solid navy background (no video) — the layout and
* text stay identical so the page renders cleanly even without a video.
*
* To set the video URL :
* 1. Upload your MP4 to WP Admin → Media Library
* 2. Copy the file URL
* 3. Run /?av_set_home_video=<URL> (see av-set-home-video.php mu-plugin)
* or update_option('av_home_hero_video_url', '<URL>') via WP CLI / DB
* 4. Re-run /?av_generate_home=1
*/
function av_build_home_video_hero() {
$video_url = (string) get_option('av_home_hero_video_url', '');
$sid = 'hhomesc';
$oct = 'hhomeoc';
$nrr = 'hhomenr';
$eybId = 'hhomeeb';
$h1Id = 'hhomeh1';
$ledId = 'hhomeld';
$rowId = 'hhomerw';
// Background settings : video if URL configured, otherwise navy color.
if ($video_url !== '') {
$section_background = [
'videoUrl' => $video_url,
'videoScale' => 'cover', // fill the section, crop if needed
'videoAspectRatio' => '16/9',
'overlay' => [
'color' => ['raw' => 'rgba(11, 31, 58, 0.6)'], // navy 60% — readability
],
'color' => ['raw' => 'var(--color-brand-navy)'], // fallback while video loads
];
} else {
$section_background = ['color' => av_color('var(--color-brand-navy)')];
}
$els = [];
// SECTION — full-bleed, 100vh tall, content vertically centered.
// Using min-height (not fixed height) so content can still extend if needed.
// 100dvh fallback handles mobile address bar collapse correctly (newer browsers).
$els[] = [
'id' => $sid, 'name' => 'section', 'parent' => 0, 'children' => [$oct],
'settings' => [
'_padding' => av_sp('2rem', '0', '2rem', '0'), // breathing room at top/bottom
'_heightMin' => '100vh', // full viewport height
'_justifyContent' => 'center', // vertical centering (flex-column)
'_background' => $section_background,
'_position' => 'relative',
'_overflow' => 'hidden',
'_cssCustom' => '%root% { min-height: 100vh; min-height: 100dvh; }',
],
];
// OUTER CONTAINER — 1280 max-width, no horizontal centering of inner
$els[] = [
'id' => $oct, 'name' => 'container', 'parent' => $sid, 'children' => [$nrr],
'settings' => [
'_direction' => 'column',
'_widthMax' => '1280px',
'_width' => '100%',
'_padding' => av_sp('0', '1.5rem', '0', '1.5rem'),
'_padding:mobile_landscape' => av_sp('0', '1rem', '0', '1rem'),
'_alignItems' => 'flex-start',
'_position' => 'relative',
'_zIndex' => '2', // above the video overlay
],
];
// NARRATIVE INNER — 800 max-width, light text
$els[] = [
'id' => $nrr, 'name' => 'container', 'parent' => $oct, 'children' => [$h1Id, $ledId, $rowId],
'settings' => [
'_direction' => 'column',
'_widthMax' => '800px',
'_widthMin' => '0',
'_width' => '100%',
'_margin' => av_sp('0', 'auto', '0', '0'),
'_padding' => av_sp('0', '0', '0', '0'),
'_rowGap' => '1.5rem',
],
];
// H1 — large, white, tactical
$els[] = [
'id' => $h1Id, 'name' => 'heading', 'parent' => $nrr, 'children' => [],
'settings' => [
'tag' => 'h1',
'text' => 'Train like you operate.',
'_typography' => [
'color' => av_color('var(--color-text-on-dark)'),
'font-weight' => '800',
'font-size' => '3.5rem',
'line-height' => '1.1',
'letter-spacing'=> '-0.02em',
],
'_typography:tablet_portrait' => ['font-size' => '2.75rem'],
'_typography:mobile_landscape' => ['font-size' => '2.25rem'],
'_typography:mobile_portrait' => ['font-size' => '1.875rem'],
],
];
// LEAD — muted-white
$els[] = av_text(
$ledId, $nrr,
"PROSERVE\u{2122} is the modular XR training platform engineered for European security forces. Real recoil, autonomous AI, sovereign by design \u{2014} from solo officer to full academy.",
'rgba(255, 255, 255, 0.85)', '1.25rem', '1.55'
);
// CTA row
$b1 = 'hhomeb0';
$b2 = 'hhomeb1';
$els[] = [
'id' => $rowId, 'name' => 'container', 'parent' => $nrr, 'children' => [$b1, $b2],
'settings' => [
'_direction' => 'row',
'_columnGap' => '1rem',
'_rowGap' => '1rem',
'_flexWrap' => 'wrap',
'_width' => '100%',
'_padding' => av_sp('1rem', '0', '0', '0'),
],
];
$els[] = av_button($b1, $rowId, 'Book a demo', '/request-demo/', 'gold');
$els[] = av_button($b2, $rowId, 'Request a T&E Kit', '/request-te-kit/', 'outline-on-dark');
return $els;
}
function av_build_home_closer_cta() { function av_build_home_closer_cta() {
$sid = "schmhc"; $sid = "schmhc";
$oct = "ochmhc"; $oct = "ochmhc";

View File

@@ -254,11 +254,18 @@ function av_ensure_insight_single_template() {
]); ]);
} }
// Mark as type=single + apply to all single posts // Mark as type=single + apply to all single posts.
// Bricks expects main='postType' + postType=['post'] (array) — not the
// 'singular' + 'object' variant. Using the wrong format triggers
// 'Invalid post type' when opening the template in the builder.
update_post_meta($tpl_id, '_bricks_template_type', 'single'); update_post_meta($tpl_id, '_bricks_template_type', 'single');
$settings = get_post_meta($tpl_id, '_bricks_template_settings', true) ?: []; $settings = get_post_meta($tpl_id, '_bricks_template_settings', true) ?: [];
$settings['templateConditions'] = [ $settings['templateConditions'] = [
['id' => substr(md5('postSingle'), 0, 6), 'main' => 'singular', 'object' => 'post'], [
'id' => substr(md5('postSingle'), 0, 6),
'main' => 'postType',
'postType' => ['post'],
],
]; ];
update_post_meta($tpl_id, '_bricks_template_settings', $settings); update_post_meta($tpl_id, '_bricks_template_settings', $settings);

View File

@@ -0,0 +1,55 @@
<?php
/**
* Plugin Name: Asterion — Transparent header on home + opaque on scroll
* Description: On the home page only, the header sits transparently over the
* 100vh video hero. Once the user scrolls past the top, Bricks adds the
* `.on-scroll` class to `#brx-header` and we fade in the navy background +
* a subtle shadow. Other pages keep the default navy header.
*
* Implementation : pure CSS via wp_head(). Uses Bricks's native `.brx-sticky`
* + `.on-scroll` classes (added by the sticky header JS once the user scrolls).
*/
defined('ABSPATH') || exit;
add_action('wp_head', function () {
// Only inject on the home page (front page) — other pages keep default header
if (! is_front_page()) return;
?>
<style id="av-header-transparent-on-home">
/* Default header state on home : fully transparent over the hero video */
body.home #brx-header,
body.home #brx-header > section,
body.home #brx-header #brxe-hdrsec {
background: transparent !important;
box-shadow: none;
transition: background-color 0.35s ease, box-shadow 0.35s ease;
}
/* Subtle gradient at the very top so the menu text stays readable
on any frame of the video (handles bright frames gracefully). */
body.home #brx-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
background: linear-gradient(to bottom, rgba(11, 31, 58, 0.55) 0%, rgba(11, 31, 58, 0) 100%);
pointer-events: none;
z-index: -1;
transition: opacity 0.35s ease;
}
/* Scrolled state : navy background fades in, gradient fades out, shadow appears */
body.home #brx-header.on-scroll,
body.home #brx-header.on-scroll > section,
body.home #brx-header.on-scroll #brxe-hdrsec {
background: var(--color-brand-navy) !important;
box-shadow: 0 2px 14px rgba(0, 0, 0, 0.22);
}
body.home #brx-header.on-scroll::before {
opacity: 0;
}
</style>
<?php
}, 999);

View File

@@ -0,0 +1,37 @@
<?php
/**
* Plugin Name: Asterion — Set Home hero video URL (one-shot)
* Description:
* /?av_set_home_video=URL → stores the URL in WP option `av_home_hero_video_url`
* /?av_set_home_video=clear → empties the option (hero falls back to navy bg)
*
* After running, re-run /?av_generate_home=1 to apply the change to the hero.
*
* URL formats supported by Bricks's background video :
* - Self-hosted : https://yoursite.com/wp-content/uploads/.../video.mp4 (any MP4/WebM)
* - YouTube : full URL or ID
* - Vimeo : full URL or ID
*/
defined('ABSPATH') || exit;
add_action('template_redirect', function () {
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) return;
if (! isset($_GET['av_set_home_video'])) return;
header('Content-Type: text/plain; charset=utf-8');
$url = trim((string) $_GET['av_set_home_video']);
$before = (string) get_option('av_home_hero_video_url', '');
if ($url === 'clear' || $url === '') {
delete_option('av_home_hero_video_url');
echo "Home hero video URL CLEARED (was: '$before')\n";
echo "Hero will fall back to solid navy background.\n";
} else {
update_option('av_home_hero_video_url', esc_url_raw($url));
echo "Home hero video URL set : '$before' → '" . esc_url_raw($url) . "'\n";
}
echo "\nNow re-run /?av_generate_home=1 to apply the change.\n";
exit;
});