The em-dash prefix on list items felt typographic but unbranded. Now each
list item gets a 3px-wide gold vertical bar on the left, evoking a tactical
breadcrumb. Implementation :
- Builders (av_build_list_section in _av-bricks-shared.php +
av_build_form_two_col promise list in av-generate-forms.php) emit clean
text without the '— ' prefix, and set _cssClasses = 'av-list-item'
- New mu-plugin av-list-bullet-style.php injects CSS via wp_head() :
.av-list-item { position: relative; padding-left: 0.9rem; }
.av-list-item::before { gold bar, 3px × 1.1em, aligned to first line }
Regen all sections (industries, solutions, technology, editorial, forms,
home) to switch existing pages to the new style.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
548 lines
26 KiB
PHP
548 lines
26 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: Asterion — Generate conversion forms pages (one-shot)
|
|
* Description: /?av_generate_forms=1 reads content-source/forms-data.php and creates
|
|
* 4 top-level pages : /request-demo/, /request-te-kit/, /request-quote/, /contact/
|
|
* with Bricks form element + 2-col layout (form + promise list).
|
|
*/
|
|
defined('ABSPATH') || exit;
|
|
|
|
require_once __DIR__ . '/_av-bricks-shared.php';
|
|
|
|
/* -----------------------------------------------------------------
|
|
FR strings dict for forms
|
|
----------------------------------------------------------------- */
|
|
add_filter('av_fr_translation_dict', function ($dict) {
|
|
return array_merge($dict, [
|
|
// Page titles + taglines + hero leads
|
|
'Request a Demo' => 'Réserver une démo',
|
|
'See PROSERVE in action.' => 'Voir PROSERVE en action.',
|
|
'A live demo, calibrated to your role and your unit. We respond within 48 hours.'
|
|
=> 'Une démo en direct, calibrée à votre rôle et votre unité. Nous répondons sous 48 heures.',
|
|
'Request a T&E Kit' => 'Demander un kit T&E',
|
|
'Try PROSERVE in your own facility, with your own people.'
|
|
=> 'Essayez PROSERVE dans vos locaux, avec votre équipe.',
|
|
"A demo only goes so far. The Test & Evaluation Kit programme lends a fully configured PROSERVE case to qualifying organizations for 5 to 10 days, on site, with our team's support."
|
|
=> "Une démo a ses limites. Le programme Test & Evaluation prête un coffre PROSERVE entièrement configuré aux organisations éligibles pour 5 à 10 jours, sur site, avec le support de notre équipe.",
|
|
'Request a Quote' => 'Demander un devis',
|
|
'From specification to deployment plan.' => 'De la spécification au plan de déploiement.',
|
|
'Tell us what you need. We send back a detailed quote \u{2014} including hardware, software, options, training, support, and deployment plan \u{2014} within 5 working days.'
|
|
=> "Dites-nous ce dont vous avez besoin. Nous renvoyons un devis détaillé \u{2014} matériel, logiciel, options, formation, support et plan de déploiement \u{2014} sous 5 jours ouvrés.",
|
|
'Contact' => 'Contact',
|
|
'How can we help?' => 'Comment pouvons-nous aider ?',
|
|
"If you know what you're looking for, the three CTAs below take you directly there. For everything else, the form at the bottom reaches our office."
|
|
=> "Si vous savez ce que vous cherchez, les trois CTA ci-dessous vous mènent directement. Pour tout le reste, le formulaire en bas atteint notre bureau.",
|
|
|
|
// Promise titles
|
|
'What you receive' => 'Ce que vous recevez',
|
|
'Other inquiries' => 'Autres demandes',
|
|
|
|
// Promise items (Demo)
|
|
"A 30-minute video call with a member of our team \u{2014} typically J\u{E9}r\u{F4}me, our founder, or a senior product manager."
|
|
=> "Un appel vidéo de 30 minutes avec un membre de notre équipe \u{2014} typiquement Jérôme, notre fondateur, ou un product manager senior.",
|
|
'A live walkthrough of the configuration most relevant to your needs (MY / FLEX / ACADEMY).'
|
|
=> 'Une démonstration en direct de la configuration la plus pertinente (MY / FLEX / ACADEMY).',
|
|
'A scenario demonstration matched to your segment (police, special forces, military, fire).'
|
|
=> 'Une démonstration de scénario adaptée à votre segment (police, forces spéciales, armée, pompiers).',
|
|
'Honest answers to your technical and procurement questions.'
|
|
=> 'Réponses honnêtes à vos questions techniques et d\'achat.',
|
|
'A follow-up email summarizing the discussion, with the right resources for your evaluation.'
|
|
=> "Un email de suivi résumant l'échange, avec les bonnes ressources pour votre évaluation.",
|
|
|
|
// Promise items (T&E Kit)
|
|
'A complete PROSERVE FLEX or MY PROSERVE configuration delivered to your facility.'
|
|
=> 'Une configuration complète PROSERVE FLEX ou MY PROSERVE livrée dans vos locaux.',
|
|
'On-site setup and instructor onboarding by our team (1-2 days).'
|
|
=> "Installation sur site et formation instructeur par notre équipe (1-2 jours).",
|
|
'Remote technical support throughout the loan period.'
|
|
=> 'Support technique à distance pendant toute la durée du prêt.',
|
|
'Up to 10 days of unrestricted use by your operators and instructors.'
|
|
=> 'Jusqu\'à 10 jours d\'utilisation sans restriction par vos opérateurs et instructeurs.',
|
|
'A debrief session at the end to capture observations and answer technical questions.'
|
|
=> 'Une séance de débrief en fin de période pour recueillir les observations et répondre aux questions techniques.',
|
|
|
|
// Promise items (Quote)
|
|
'A detailed quote (PDF, suitable for procurement records)'
|
|
=> 'Un devis détaillé (PDF, conforme aux exigences d\'archivage d\'achat)',
|
|
'A deployment plan with milestones and timing'
|
|
=> 'Un plan de déploiement avec jalons et calendrier',
|
|
'A 30-minute call with our commercial team to walk through the quote'
|
|
=> 'Un appel de 30 minutes avec notre équipe commerciale pour parcourir le devis',
|
|
'References for an introduction call with comparable customers (subject to NDA)'
|
|
=> 'Des références pour un appel d\'introduction avec des clients comparables (sous NDA)',
|
|
|
|
// Promise items (Contact)
|
|
'Press & media — press@asterionvr.com (response within 24 hours)'
|
|
=> 'Presse & médias — press@asterionvr.com (réponse sous 24 heures)',
|
|
'Partnerships & distributors — partners@asterionvr.com'
|
|
=> 'Partenariats & distributeurs — partners@asterionvr.com',
|
|
'Careers — careers@asterionvr.com' => 'Carrières — careers@asterionvr.com',
|
|
'General — hello@asterionvr.com' => 'Général — hello@asterionvr.com',
|
|
"Office \u{2014} Asterion VR \u{B7} 9 rue de la Mareb\u{E9}audi\u{E8}re \u{B7} 35760 Montgermont \u{B7} France \u{B7} +33 6 86 94 33 51"
|
|
=> "Bureau \u{2014} Asterion VR \u{B7} 9 rue de la Marebaudière \u{B7} 35760 Montgermont \u{B7} France \u{B7} +33 6 86 94 33 51",
|
|
|
|
// Reassurance lines
|
|
'We respond within 48 hours. We never share your information. No newsletter sign-up unless you ask.'
|
|
=> 'Nous répondons sous 48 heures. Nous ne partageons jamais vos informations. Pas d\'inscription newsletter sauf demande.',
|
|
'Limited slots per quarter. We respond within 5 working days with a yes-or-no on the loan, and a proposed schedule if eligible.'
|
|
=> "Places limitées par trimestre. Nous répondons sous 5 jours ouvrés avec un oui/non sur le prêt et un calendrier proposé si éligible.",
|
|
'We respond within 5 working days. Detailed quote suitable for procurement records.'
|
|
=> 'Nous répondons sous 5 jours ouvrés. Devis détaillé conforme aux exigences d\'achat.',
|
|
'We respond within 48 hours on weekdays.' => 'Nous répondons sous 48 heures en jours ouvrés.',
|
|
|
|
// Submit labels
|
|
'Schedule the demo' => 'Réserver la démo',
|
|
'Apply for a T&E Kit' => 'Postuler pour un kit T&E',
|
|
'Request the quote' => 'Demander le devis',
|
|
'Send the message' => 'Envoyer le message',
|
|
|
|
// Form field labels (shared across forms)
|
|
'Full name' => 'Nom complet',
|
|
'Organization' => 'Organisation',
|
|
'Job title' => 'Fonction',
|
|
'Job title / role' => 'Fonction / rôle',
|
|
'Country' => 'Pays',
|
|
'Email' => 'Email',
|
|
'Segment' => 'Segment',
|
|
'Estimated number of trainees in your unit' => 'Nombre estimé de stagiaires dans votre unité',
|
|
'Preferred timeframe' => 'Délai préféré',
|
|
'Message — anything specific you want us to demonstrate'
|
|
=> 'Message — éléments spécifiques à démontrer',
|
|
'Message' => 'Message',
|
|
'Address of evaluation facility' => 'Adresse du site d\'évaluation',
|
|
'Number of trainees who would participate in evaluation'
|
|
=> 'Nombre de stagiaires participant à l\'évaluation',
|
|
'Procurement timeline' => 'Calendrier d\'achat',
|
|
'Indicative budget range' => 'Fourchette budgétaire indicative',
|
|
'Authorization status' => 'Statut d\'autorisation',
|
|
'Procurement vehicle' => 'Véhicule d\'achat',
|
|
'Configuration desired' => 'Configuration souhaitée',
|
|
'Number of units / packs / seats' => 'Nombre d\'unités / packs / postes',
|
|
'Options' => 'Options',
|
|
'Deployment location and timeline' => 'Lieu et calendrier de déploiement',
|
|
'Administrative constraints (open tender deadline, cybersecurity review process, etc.)'
|
|
=> 'Contraintes administratives (échéance d\'appel d\'offres, processus de revue cyber, etc.)',
|
|
'Subject' => 'Sujet',
|
|
|
|
// Select options
|
|
'Police' => 'Police',
|
|
'Special Forces' => 'Forces spéciales',
|
|
'Military' => 'Armée',
|
|
'Firefighters' => 'Sapeurs-pompiers',
|
|
'Other' => 'Autre',
|
|
'This week' => 'Cette semaine',
|
|
'Next week' => 'La semaine prochaine',
|
|
'Next month' => 'Le mois prochain',
|
|
'No rush' => 'Pas pressé',
|
|
'3 months' => '3 mois',
|
|
'6 months' => '6 mois',
|
|
'12 months' => '12 mois',
|
|
'18+ months' => '18+ mois',
|
|
'Cleared' => 'Validé',
|
|
'Pending' => 'En attente',
|
|
'Non-applicable' => 'Non applicable',
|
|
'Direct purchase' => 'Achat direct',
|
|
'Open tender' => 'Appel d\'offres ouvert',
|
|
'Framework contract' => 'Marché-cadre',
|
|
|
|
// FAQ (Demo)
|
|
'Will there be a sales pitch?' => 'Y aura-t-il un pitch commercial ?',
|
|
"No. The first call is technical and pedagogical. We assume you'll evaluate three or four solutions before deciding \u{2014} we want to give you the information you need to compare honestly."
|
|
=> "Non. Le premier appel est technique et pédagogique. Nous partons du principe que vous évaluerez trois ou quatre solutions avant de décider \u{2014} nous voulons vous donner les informations nécessaires pour comparer honnêtement.",
|
|
'Can I bring colleagues?' => 'Puis-je amener des collègues ?',
|
|
'Of course. The more roles in the room (training officer, IT, finance, command), the more useful the call.'
|
|
=> "Bien sûr. Plus il y a de rôles dans la pièce (responsable formation, IT, finance, commandement), plus l'appel est utile.",
|
|
'In what language?' => 'En quelle langue ?',
|
|
'EN, FR. Other languages on a per-request basis.'
|
|
=> 'EN, FR. Autres langues sur demande.',
|
|
|
|
// Eligibility (T&E Kit)
|
|
'Eligibility' => 'Éligibilité',
|
|
'The T&E Kit programme is reserved for organizations with a clear procurement intent and the capacity to run meaningful evaluation drills. We accept applications from:'
|
|
=> "Le programme T&E Kit est réservé aux organisations avec une intention d'achat claire et la capacité à conduire des exercices d'évaluation significatifs. Nous acceptons les candidatures de :",
|
|
'National and regional police, gendarmerie, and security forces'
|
|
=> 'Polices nationales et régionales, gendarmerie et forces de sécurité',
|
|
'Special forces and tactical units' => 'Forces spéciales et unités tactiques',
|
|
'Military training centres' => 'Centres de formation militaire',
|
|
'Firefighter and emergency services training schools'
|
|
=> 'Écoles de formation des sapeurs-pompiers et services d\'urgence',
|
|
'Approved distributors and integrators bidding on identified projects'
|
|
=> "Distributeurs et intégrateurs agréés répondant à des projets identifiés",
|
|
|
|
// Shortcuts (Contact)
|
|
'Request a Demo (response within 48 hours)'
|
|
=> 'Réserver une démo (réponse sous 48 heures)',
|
|
'Request a T&E Kit (response within 5 working days)'
|
|
=> 'Demander un kit T&E (réponse sous 5 jours ouvrés)',
|
|
'Request a Quote (response within 5 working days)'
|
|
=> 'Demander un devis (réponse sous 5 jours ouvrés)',
|
|
]);
|
|
});
|
|
|
|
/* -----------------------------------------------------------------
|
|
URL trigger
|
|
----------------------------------------------------------------- */
|
|
add_action('template_redirect', function () {
|
|
if (empty($_GET['av_generate_forms'])) return;
|
|
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) wp_die('localhost only');
|
|
|
|
$data_file = 'C:\\ASTERION\\GIT\\WP2026\\content-source\\forms-data.php';
|
|
if (! file_exists($data_file)) wp_die("data file not found: $data_file");
|
|
|
|
$forms = include $data_file;
|
|
if (! is_array($forms)) wp_die('forms data not array');
|
|
|
|
$report = ["Rebuilding 4 conversion forms pages..."];
|
|
|
|
foreach ($forms as $slug => $d) {
|
|
// Custom page builder for forms (hero + 2-col + form + extras + closer)
|
|
$elements = av_build_form_page_elements($slug, $d);
|
|
|
|
// Ensure page exists top-level
|
|
$existing = get_page_by_path($slug);
|
|
if ($existing) {
|
|
wp_update_post([
|
|
'ID' => $existing->ID, 'post_title' => $d['title'],
|
|
'post_status' => 'publish', 'post_parent' => 0,
|
|
]);
|
|
$page_id = $existing->ID;
|
|
$action = 'updated';
|
|
} else {
|
|
$page_id = wp_insert_post([
|
|
'post_type' => 'page', 'post_title' => $d['title'],
|
|
'post_name' => $slug, 'post_parent' => 0,
|
|
'post_status' => 'publish', 'post_content' => '',
|
|
]);
|
|
$action = 'created';
|
|
}
|
|
|
|
$write = av_write_bricks_page_elements($page_id, $elements);
|
|
$report[] = " $slug → page #$page_id ($action) — " . count($elements)
|
|
. " elements (json={$write['json_len']}, stored={$write['stored_len']})";
|
|
}
|
|
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo implode("\n", $report);
|
|
exit;
|
|
});
|
|
|
|
/* -----------------------------------------------------------------
|
|
FORM PAGE BUILDER
|
|
----------------------------------------------------------------- */
|
|
function av_build_form_page_elements($slug, $d) {
|
|
$els = [];
|
|
|
|
// 1. HERO
|
|
$els = array_merge($els, av_build_hero($slug, $d));
|
|
|
|
// 2. 2-COLUMN SECTION : form left, promise right
|
|
$els = array_merge($els, av_build_form_two_col($slug, $d));
|
|
|
|
// 3. Optional: shortcuts (Contact page)
|
|
if (! empty($d['shortcuts'])) {
|
|
$els = array_merge($els, av_build_shortcuts_section($slug, $d['shortcuts']));
|
|
}
|
|
|
|
// 4. Optional: eligibility (T&E Kit)
|
|
if (! empty($d['eligibility'])) {
|
|
$els = array_merge($els, av_build_list_section($slug, 'elig', [
|
|
'eyebrow' => $d['eligibility']['title'] ?? 'Eligibility',
|
|
'title' => '',
|
|
'lead' => $d['eligibility']['lead'] ?? '',
|
|
'items' => $d['eligibility']['items'] ?? [],
|
|
], 'var(--color-bg-default)'));
|
|
}
|
|
|
|
// 5. Optional: FAQ (Demo)
|
|
if (! empty($d['faq'])) {
|
|
$els = array_merge($els, av_build_faq_section($slug, $d['faq']));
|
|
}
|
|
|
|
// 6. CLOSER CTA
|
|
$els = array_merge($els, av_build_closer_cta($slug));
|
|
|
|
return $els;
|
|
}
|
|
|
|
function av_build_form_two_col($slug, $d) {
|
|
$sid = "f{$slug}sc"; // section
|
|
$oct = "f{$slug}oc"; // outer container
|
|
$col1 = "f{$slug}c1"; // form column
|
|
$col2 = "f{$slug}c2"; // promise column
|
|
$frm = "f{$slug}fm"; // form element
|
|
$reas = "f{$slug}rs"; // reassurance text
|
|
$peyb = "f{$slug}pe"; // promise eyebrow
|
|
$plst = "f{$slug}pl"; // promise list container
|
|
|
|
$els = [];
|
|
|
|
$els[] = [
|
|
'id' => $sid, 'name' => 'section', 'parent' => 0, 'children' => [$oct],
|
|
'settings' => array_merge(av_section_padding_responsive(), [
|
|
'_background' => ['color' => av_color('var(--color-bg-default)')],
|
|
]),
|
|
];
|
|
$els[] = [
|
|
'id' => $oct, 'name' => 'container', 'parent' => $sid, 'children' => [$col1, $col2],
|
|
'settings' => [
|
|
'_direction' => 'row',
|
|
'_widthMax' => '1280px',
|
|
'_width' => '100%',
|
|
'_padding' => av_sp('0', '1.5rem', '0', '1.5rem'),
|
|
'_padding:mobile_landscape' => av_sp('0', '1rem', '0', '1rem'),
|
|
'_columnGap' => '3rem',
|
|
'_rowGap' => '2rem',
|
|
'_flexWrap' => 'wrap',
|
|
'_alignItems' => 'flex-start',
|
|
],
|
|
];
|
|
|
|
// LEFT COLUMN — form + reassurance
|
|
$els[] = [
|
|
'id' => $col1, 'name' => 'container', 'parent' => $oct, 'children' => [$frm, $reas],
|
|
'settings' => [
|
|
'_direction' => 'column',
|
|
'_widthMax' => '600px',
|
|
'_widthMin' => '0',
|
|
'_width' => '100%',
|
|
'_flexBasis' => 'calc((100% - 3rem) / 2)',
|
|
'_flexBasis:tablet_portrait' => '100%',
|
|
'_flexGrow' => '1',
|
|
'_padding' => av_sp('0', '0', '0', '0'),
|
|
'_rowGap' => '1.5rem',
|
|
],
|
|
];
|
|
|
|
// Bricks form element
|
|
$els[] = av_build_form_element($frm, $col1, $d);
|
|
|
|
// Reassurance line
|
|
if (! empty($d['reassurance'])) {
|
|
$els[] = av_text($reas, $col1, $d['reassurance'], 'var(--color-text-tertiary)', '0.875rem', '1.55');
|
|
}
|
|
|
|
// RIGHT COLUMN — promise eyebrow + list
|
|
$els[] = [
|
|
'id' => $col2, 'name' => 'container', 'parent' => $oct, 'children' => [$peyb, $plst],
|
|
'settings' => [
|
|
'_direction' => 'column',
|
|
'_widthMax' => '480px',
|
|
'_widthMin' => '0',
|
|
'_width' => '100%',
|
|
'_flexBasis' => 'calc((100% - 3rem) / 2)',
|
|
'_flexBasis:tablet_portrait' => '100%',
|
|
'_flexGrow' => '0',
|
|
'_padding' => av_sp('1rem', '1.75rem', '1.75rem', '1.75rem'),
|
|
'_background' => ['color' => av_color('var(--color-bg-subtle)')],
|
|
'_border' => [
|
|
'width' => av_sp('1px', '1px', '1px', '1px'),
|
|
'color' => av_color('var(--color-border-subtle)'),
|
|
'style' => 'solid',
|
|
'radius' => av_sp('4px', '4px', '4px', '4px'),
|
|
],
|
|
'_rowGap' => '0.75rem',
|
|
],
|
|
];
|
|
$els[] = av_heading($peyb, $col2, 'h3', $d['promise_title'] ?? 'What you receive');
|
|
|
|
// Promise list as text-basic items
|
|
$items = $d['promise_items'] ?? [];
|
|
$item_ids = [];
|
|
foreach ($items as $j => $_) $item_ids[] = "fp{$slug}i{$j}";
|
|
$els[] = [
|
|
'id' => $plst, 'name' => 'container', 'parent' => $col2, 'children' => $item_ids,
|
|
'settings' => [
|
|
'_direction' => 'column',
|
|
'_rowGap' => '0.6rem',
|
|
'_padding' => av_sp('0.5rem', '0', '0', '0'),
|
|
'_width' => '100%',
|
|
],
|
|
];
|
|
foreach ($items as $j => $item) {
|
|
$els[] = [
|
|
'id' => "fp{$slug}i{$j}", 'name' => 'text-basic', 'parent' => $plst, 'children' => [],
|
|
'settings' => [
|
|
'text' => $item,
|
|
'_cssClasses' => 'av-list-item',
|
|
'_typography' => [
|
|
'color' => av_color('var(--color-text-secondary)'),
|
|
'font-size' => '0.9375rem',
|
|
'line-height' => '1.55',
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
return $els;
|
|
}
|
|
|
|
/**
|
|
* Build a Bricks `form` element with the given fields, submit button, and
|
|
* email action (sends to hello@asterionvr.com). Fields config follows
|
|
* Bricks's repeater shape : each field is an associative array with 'id',
|
|
* 'type', 'label', 'required', and 'options' (for select).
|
|
*/
|
|
function av_build_form_element($id, $parent, $d) {
|
|
$bricks_fields = [];
|
|
foreach ($d['fields'] as $f) {
|
|
$field = [
|
|
'id' => substr(md5($id . $f['name']), 0, 6),
|
|
'type' => $f['type'],
|
|
'label' => $f['label'],
|
|
'name' => $f['name'],
|
|
];
|
|
if (! empty($f['required'])) $field['required'] = true;
|
|
if ($f['type'] === 'select' && ! empty($f['options'])) {
|
|
// Bricks expects a single string with "value : label\n" per line OR
|
|
// an array of {value, label}. We use a newline string for simplicity.
|
|
$opts_str = [];
|
|
foreach ($f['options'] as $opt) {
|
|
$opts_str[] = $opt === '' ? "" : "$opt : $opt";
|
|
}
|
|
$field['options'] = implode("\n", $opts_str);
|
|
}
|
|
$bricks_fields[] = $field;
|
|
}
|
|
|
|
return [
|
|
'id' => $id, 'name' => 'form', 'parent' => $parent, 'children' => [],
|
|
'settings' => [
|
|
'fields' => $bricks_fields,
|
|
'submitButtonText' => $d['submit_label'] ?? 'Submit',
|
|
'actions' => ['email'],
|
|
'emailTo' => 'custom',
|
|
'email' => 'hello@asterionvr.com',
|
|
'fromEmail' => 'noreply@asterionvr.com',
|
|
'fromName' => 'Asterion VR website',
|
|
'replyToEmail' => true,
|
|
'subject' => 'Asterion VR — ' . ($d['title'] ?? 'New submission'),
|
|
'successMessage' => 'Thank you. We will get back to you shortly.',
|
|
'_typography' => [
|
|
'font-size' => '1rem',
|
|
],
|
|
'submitButtonStyle' => 'primary',
|
|
],
|
|
];
|
|
}
|
|
|
|
function av_build_shortcuts_section($slug, $shortcuts) {
|
|
$sid = "fs{$slug}sc";
|
|
$oct = "fs{$slug}oc";
|
|
$row = "fs{$slug}rw";
|
|
|
|
$btn_ids = [];
|
|
foreach ($shortcuts as $j => $sh) $btn_ids[] = "fs{$slug}b{$j}";
|
|
|
|
$els = [];
|
|
$els[] = [
|
|
'id' => $sid, 'name' => 'section', 'parent' => 0, 'children' => [$oct],
|
|
'settings' => array_merge(av_section_padding_responsive('3rem', '3rem'), [
|
|
'_background' => ['color' => av_color('var(--color-bg-subtle)')],
|
|
]),
|
|
];
|
|
$els[] = [
|
|
'id' => $oct, 'name' => 'container', 'parent' => $sid, 'children' => [$row],
|
|
'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',
|
|
],
|
|
];
|
|
$els[] = [
|
|
'id' => $row, 'name' => 'container', 'parent' => $oct, 'children' => $btn_ids,
|
|
'settings' => [
|
|
'_direction' => 'row',
|
|
'_flexWrap' => 'wrap',
|
|
'_columnGap' => '1rem',
|
|
'_rowGap' => '1rem',
|
|
'_width' => '100%',
|
|
],
|
|
];
|
|
foreach ($shortcuts as $j => $sh) {
|
|
$variant = ($sh['variant'] ?? 'primary') === 'primary' ? 'primary' : 'secondary';
|
|
$els[] = av_button("fs{$slug}b{$j}", $row, $sh['label'], $sh['href'], $variant);
|
|
}
|
|
return $els;
|
|
}
|
|
|
|
function av_build_faq_section($slug, $faq) {
|
|
$sid = "fq{$slug}sc";
|
|
$oct = "fq{$slug}oc";
|
|
$nrr = "fq{$slug}nr";
|
|
$hId = "fq{$slug}h2";
|
|
$lst = "fq{$slug}lt";
|
|
|
|
$item_ids = [];
|
|
foreach ($faq as $j => $_) $item_ids[] = "fq{$slug}i{$j}";
|
|
|
|
$els = [];
|
|
$els[] = [
|
|
'id' => $sid, 'name' => 'section', 'parent' => 0, 'children' => [$oct],
|
|
'settings' => array_merge(av_section_padding_responsive(), [
|
|
'_background' => ['color' => av_color('var(--color-bg-default)')],
|
|
]),
|
|
];
|
|
$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',
|
|
],
|
|
];
|
|
$els[] = [
|
|
'id' => $nrr, 'name' => 'container', 'parent' => $oct, 'children' => array_merge([$hId, $lst]),
|
|
'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',
|
|
],
|
|
];
|
|
$els[] = av_heading($hId, $nrr, 'h2', 'FAQ');
|
|
|
|
$els[] = [
|
|
'id' => $lst, 'name' => 'container', 'parent' => $nrr, 'children' => $item_ids,
|
|
'settings' => [
|
|
'_direction' => 'column',
|
|
'_rowGap' => '1rem',
|
|
'_width' => '100%',
|
|
],
|
|
];
|
|
|
|
foreach ($faq as $j => $entry) {
|
|
$iid = "fq{$slug}i{$j}";
|
|
$qid = "fq{$slug}q{$j}";
|
|
$aid = "fq{$slug}a{$j}";
|
|
$els[] = [
|
|
'id' => $iid, 'name' => 'container', 'parent' => $lst, 'children' => [$qid, $aid],
|
|
'settings' => [
|
|
'_direction' => 'column',
|
|
'_rowGap' => '0.5rem',
|
|
'_padding' => av_sp('1rem', '1.25rem', '1rem', '1.25rem'),
|
|
'_background' => ['color' => av_color('var(--color-bg-subtle)')],
|
|
'_border' => [
|
|
'width' => av_sp('1px', '1px', '1px', '1px'),
|
|
'color' => av_color('var(--color-border-subtle)'),
|
|
'style' => 'solid',
|
|
'radius' => av_sp('4px', '4px', '4px', '4px'),
|
|
],
|
|
'_width' => '100%',
|
|
],
|
|
];
|
|
$els[] = av_heading($qid, $iid, 'h3', $entry['q']);
|
|
$els[] = av_text($aid, $iid, $entry['a'], 'var(--color-text-secondary)', '1rem', '1.55');
|
|
}
|
|
|
|
return $els;
|
|
}
|