persona section overhaul — editorial gap-px grid + fresh photography

SECTION REDESIGN:
- Killed standalone dashboard image (fake AI laptop, added nothing)
- New gap-px grid (signature pattern 2) with border-l-2 accents (pattern 1)
- Numbered anchors (01-04) as visual rhythm per brand guide
- Wider container: max-w-7xl matches hero width

PERSONA CHANGES:
- Renamed 'Organisation' -> 'Programme Manager'
- Reorder: Charity Manager, Programme Manager, Personal Fundraiser, Volunteer
- Updated /for/organisations page content to match

PHOTOGRAPHY (4 new images via gemini-3-pro-image-preview):
- persona-charity-manager.jpg — hijabi woman at mosque office desk
- persona-programme-manager.jpg — man at desk with campaign calendar
- persona-fundraiser.jpg — woman on London park bench with phone
- persona-volunteer.jpg — young man handing card at charity gala
- All optimized: 2.7MB -> 342KB (87% reduction via sharp)
- Consistent documentary candid style, 3:2 landscape, warm tones

FOOTER:
- 'Organisations' -> 'Programme Managers' in nav links
This commit is contained in:
2026-03-03 22:01:53 +08:00
parent 3ab440f103
commit c18dc50657
16 changed files with 1641 additions and 45 deletions

View File

@@ -0,0 +1 @@

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

View File

@@ -0,0 +1,727 @@
<?php
use Manza\CharityRight\Etc;
use Manza\CharityRight\Mappers\AppealMapper;
use Manza\CharityRight\Theme;
require_once get_stylesheet_directory() . "/vendor/autoload.php";
// List of files which will be included.
// You can append ! then a comma limited list of class names which must exist to that file.
$vlTailwind_includes = array(
'/setup.php', // Some theme setup functions
'/template-tags.php', // Custom template tags
'/pagination.php', // Pagination
'/sidebars.php', // Sidebars
'/scripts-and-styles.php', // enqueue scripts and styles
'/page-titles.php', // page title function
'/breadcrumbs.php', // breadcrumbs trail function
'/acf.php', // ACF settings
'/menus.php', // MENU inits
'/ajax.php', // Custom AJAX returning.
'/blocks.php', // The custom block registry.
'/cpt.php', // Custom post type declarations
'/blog-import.php', // Blog post importer. NOTE: Remove this once we're deployed live and happy.
'/dashboard.php', // Configure the WP dashboard.
'/images.php', // Configure media library images.
"/ajax-campaigns.php", // custom AJAX campaign loader
"/ajax-blogs.php", // custom AJAX blog loader
'/ramadan-challenge.php', // receiving ramadan challenge appeals.
'/team365.php', // receiving team365 appeals.
);
foreach ( $vlTailwind_includes as $file ) {
$filepath = locate_template( 'includes' . $file );
if ( ! $filepath ) {
trigger_error( "Error locating /includes{$file} for inclusion", E_USER_ERROR );
}
require_once $filepath;
}
function buildHeaderTree( array &$elements, $parentId = 0 )
{
$branch = array();
foreach ( $elements as &$element )
{
if ( $element->menu_item_parent == $parentId )
{
$children = buildHeaderTree( $elements, $element->ID );
if ( $children )
$element->wpse_children = $children;
$branch[$element->ID] = $element;
unset( $element );
}
}
return $branch;
}
function searchSocialMedia($name, $array) {
foreach ($array as $key => $val) {
if ($val['name'] === $name) {
return $key;
}
}
return null;
}
add_image_size( 'cr_card_thumb', 600, 350 , true);
add_image_size( 'cr_large_blog_thumb', 600, 480 , true);
// custom excerpt length
function wpdocs_custom_excerpt_length( $length ) {
return 30;
}
add_filter( 'excerpt_length', 'wpdocs_custom_excerpt_length', 999 );
function new_excerpt_more( $more ) {
return '...';
}
add_filter('excerpt_more', 'new_excerpt_more');
(new Theme)->run();
// Force asset URLs to use www to avoid cross-origin script/font blocking.
add_filter( 'script_loader_src', function( $src ) {
return str_replace( 'https://charityright.org.uk', 'https://www.charityright.org.uk', $src );
} );
add_filter( 'style_loader_src', function( $src ) {
return str_replace( 'https://charityright.org.uk', 'https://www.charityright.org.uk', $src );
} );
// Redirect function from author pages
add_action('template_redirect', 'my_custom_disable_author_page');
function my_custom_disable_author_page() {
global $wp_query;
if ( is_author() ) {
// Redirect to homepage, set status to 301 permenant redirect.
// Function defaults to 302 temporary redirect.
wp_redirect(get_option('home'), 301);
exit;
}
}
function itsme_disable_feed() {
wp_die( __( 'No feed available, please visit the <a href="'. esc_url( home_url( '/' ) ) .'">homepage</a>!' ) );
}
function disable_attachment_pages() {
if (is_attachment()) {
// Get the parent post ID
$parent_post_id = wp_get_post_parent_id(get_the_ID());
// Redirect to parent post if available, or homepage if not
if ($parent_post_id) {
wp_redirect(get_permalink($parent_post_id), 301);
} else {
wp_redirect(home_url(), 301);
}
exit;
}
}
add_action('template_redirect', 'disable_attachment_pages');
add_action('do_feed', 'itsme_disable_feed', 1);
add_action('do_feed_rdf', 'itsme_disable_feed', 1);
add_action('do_feed_rss', 'itsme_disable_feed', 1);
add_action('do_feed_rss2', 'itsme_disable_feed', 1);
add_action('do_feed_atom', 'itsme_disable_feed', 1);
add_action('do_feed_rss2_comments', 'itsme_disable_feed', 1);
add_action('do_feed_atom_comments', 'itsme_disable_feed', 1);
add_action('init', function() {
// Add rewrite rule for campaign with query parameter
add_rewrite_rule(
'campaigns/([^/]+)/?$',
'index.php?post_type=campaign&name=$matches[1]',
'top'
);
// Register your query vars
add_filter('query_vars', function($vars) {
$vars[] = 'p'; // Using 'p' instead of 'page'
return $vars;
});
});
// Prevent canonical redirect
add_filter('redirect_canonical', function($redirect_url, $requested_url) {
if (is_singular('campaign')) {
return false;
}
return $redirect_url;
}, 10, 2);
// donations
add_action('rest_api_init', function () {
register_rest_route('charityright/v1', '/donations', [
'methods' => 'GET',
'callback' => 'get_external_donations',
'permission_callback' => '__return_true', // Adjust permissions if necessary
]);
});
if (!function_exists('wp_money_format')) {
function wp_money_format($amount, $currency_symbol = '£', $decimal_places = 2, $thousands_separator = ',', $decimal_separator = '.') {
// Ensure the amount is a float
$amount = floatval($amount);
// Format the amount
$formatted_amount = number_format($amount, $decimal_places, $decimal_separator, $thousands_separator);
// Return with the currency symbol
return $currency_symbol . $formatted_amount;
}
}
function get_external_donations($request) {
// Define the appeal ID if necessary, otherwise, pass it from the request
$appeal_id = $request->get_param('appeal_id');
$page = $request->get_param('page') ?? 1;
$per_page = 5; // Define how many donations you want per page
// External API URL with the appeal_id
$url = trim(str_replace("donate", "", get_field("generic_donate_url", "options")), "/") . "/api/v1/appeal-donations/{$appeal_id}?page={$page}&per_page={$per_page}";
// Fetch donations from the external API
$response = wp_remote_get($url);
if (is_wp_error($response)) {
return new WP_REST_Response([
'message' => 'Failed to fetch donations from external API',
'error' => $response->get_error_message()
], 500);
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (!$data || !isset($data['data']) || !is_array($data['data'])) {
return new WP_REST_Response([
'message' => 'No donations found or invalid data format'
], 404);
}
$formattedDonations = array_map(function($donation) {
return [
'id' => $donation['id'],
'donor_name' => $donation['is_anonymous'] ? 'Anonymous' : $donation['donor_name'],
'initials' => strtoupper(substr($donation['donor_name'], 0, 1)), // Extract initials
'donated_at' => $donation['created_at'], // Pass the raw date, format on frontend
'amount' => $donation['amount'],
'donor_message' => $donation['donor_message'] ?? ''
];
}, $data['data']);
// Return formatted donations and pagination info
return new WP_REST_Response([
'donations' => $formattedDonations,
'pagination' => [
'current_page' => $data['current_page'],
'last_page' => $data['last_page'],
'total' => $data['total']
]
], 200);
}
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/delete-post', array(
'methods' => 'DELETE',
'callback' => 'handle_delete_post',
'permission_callback' => '__return_true',
'args' => array(
'slug' => array(
'required' => true,
'validate_callback' => function($param) {
return is_string($param); // Ensure slug is a string
}
),
'api_key' => array(
'required' => true
)
)
));
register_rest_route('custom/v1', '/update-campaign-preview', array(
'methods' => 'POST',
'callback' => 'handle_update_campaign_preview',
'permission_callback' => '__return_true',
'args' => array(
'slug' => array(
'required' => true,
'validate_callback' => function($param) {
return is_string($param); // Ensure slug is a string
}
),
'api_key' => array(
'required' => true
)
)
));
register_rest_route('custom/v1', '/update-strava-leaderboard', array(
'methods' => 'POST',
'callback' => 'handle_strava_leaderboard',
'permission_callback' => '__return_true',
'args' => array(
'api_key' => array(
'required' => true
),
'list' => array(
'required' => false, // Set to true if `list` is mandatory
'validate_callback' => function ($param, $request, $key) {
return is_array($param); // Ensure `list` is an array
}
)
)
));
});
function handle_delete_post($request) {
// Validate API key (you should store this securely)
$valid_api_key = 'AKDJSHF8932YRJDFKSAFH120';
if ($request['api_key'] !== $valid_api_key) {
return new WP_Error(
'invalid_api_key',
'Invalid API key provided.',
array('status' => 403)
);
}
// Get the slug from the request
$slug = $request['slug'];
// Retrieve the post by slug
$post = get_page_by_path($slug, OBJECT, 'campaign');
if (!$post) {
return new WP_Error(
'post_not_found',
'Post not found',
array('status' => 404)
);
}
// Attempt to delete the post
$deleted = wp_delete_post($post->ID, true);
if (!$deleted) {
return new WP_Error(
'delete_failed',
'Failed to delete post',
array('status' => 500)
);
}
return array(
'status' => 'success',
'message' => 'Post deleted successfully',
'deleted_slug' => $slug
);
}
if (!function_exists("number_shorten")) {
/**
* Shorten long numbers and attaches K, M, B, etc. accordingly
* @param int $n
* @param int $precision
*
* @return string
*/
function number_shorten( $n, $precision = 1 ) {
if ($n < 900) {
// 0 - 900
$n_format = number_format($n, $precision);
$suffix = '';
} else if ($n < 900000) {
// 0.9k-850k
$n_format = number_format($n / 1000, $precision);
$suffix = 'K';
} else if ($n < 900000000) {
// 0.9m-850m
$n_format = number_format($n / 1000000, $precision);
$suffix = 'M';
} else if ($n < 900000000000) {
// 0.9b-850b
$n_format = number_format($n / 1000000000, $precision);
$suffix = 'B';
} else {
// 0.9t+
$n_format = number_format($n / 1000000000000, $precision);
$suffix = 'T';
}
// Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
// Intentionally does not affect partials, eg "1.50" -> "1.50"
if ( $precision > 0 ) {
$dotzero = '.' . str_repeat( '0', $precision );
$n_format = str_replace( $dotzero, '', $n_format );
}
return $n_format . $suffix;
}
}
function handle_update_campaign_preview ($request){
$existingPost = Etc::findPostByMeta('campaign-preview', 'appeal_id', $request['id']);
if ($existingPost) {
// Update the existing post
$postId = wp_update_post([
'ID' => $existingPost->ID,
'post_title' => $request['name'],
'post_name' => $request['slug'],
'post_content' => $request['story'],
'post_type' => 'campaign-preview'
]);
// Return the updated post object
$campaign_preview = get_post($postId);
} else {
// Create a new post
$postId = wp_insert_post([
'post_title' => $request['name'],
'post_name' => $request['slug'],
'post_content' => $request['story'],
'post_status' => 'publish',
'post_type' => 'campaign-preview'
]);
error_log($postId);
// Add the custom meta field for appeal_id
add_post_meta($postId, 'appeal_id', $request['id']);
// Return the newly created post object
$campaign_preview = get_post($postId);
}
$body = $request->get_body();
error_log(print_r(json_decode($body, true), true));
foreach (AppealMapper::toACF(json_decode($body, true)) as $fieldKey => $fieldValue) {
update_field($fieldKey, $fieldValue, $campaign_preview);
}
return $campaign_preview;
}
function handle_strava_leaderboard (WP_REST_Request $request){
delete_field('strava_leaderboard', 'option');
$list = $request->get_param('list');
if ($list) {
foreach ($list as $item) {
$row = array(
'athlete_name' => $item['name'],
'athlete_image' => $item['image'],
'distance' => $item['distance']
);
add_row('strava_leaderboard', $row, 'option');
}
} else {
error_log('No list received.');
}
}
function ajax_words_of_hope_action_submit() : void{
$email = $_REQUEST[ "email" ];
$name = $_REQUEST[ "name" ];
$school = isset($_REQUEST[ "school" ]) ? $_REQUEST[ "school" ] : null;
$form = $_REQUEST[ "form" ];
$address = $_REQUEST[ "address" ];
$message = $_REQUEST[ "message" ];
$us_marketing = isset($_REQUEST[ "us_marketing" ]) ? $_REQUEST[ "us_marketing" ] : null;
$uk_marketing = isset($_REQUEST[ "uk_marketing" ]) ? $_REQUEST[ "uk_marketing" ] : null;
$is_in_us = $_REQUEST[ "is_in_us" ];
$data = array(
'email' => $email,
'name' => $name,
'school' => $school,
'form' => $form,
'address' => $address,
'message' => $message,
'us_marketing' => $us_marketing !== "on" && $is_in_us,
'uk_marketing' => $uk_marketing == "on" && !$is_in_us,
);
// Set up the arguments for the HTTP request
$args = array(
'method' => 'POST',
'body' => json_encode($data),
'headers' => array(
'Content-Type' => 'application/json',
),
);
$base_url = trim(get_field('generic_donate_url', 'options') ?? 'http://crukv2.test/donate/', '/');
$wohURL = str_replace("/donate", "/api/word-of-hope/store", $base_url);
$response = wp_remote_post($wohURL, $args);
// Check for errors in the request
if (is_wp_error($response)) {
$error_message = $response->get_error_message();
error_log("Error making HTTP request: $error_message");
wp_send_json_error(array('message' => 'Error making HTTP request.'));
return;
}
// Check the response from the external API
$response_body = wp_remote_retrieve_body($response);
$response_code = wp_remote_retrieve_response_code($response);
error_log(print_r($response_body, true));
if ($response_code === 201) {
// Successfully submitted, return success response
wp_send_json_success(array('message' => 'Your message has been successfully submitted.'));
} else {
// API returned an error, return failure response
wp_send_json_error(array('message' => 'There was an error submitting your message.'));
}
}
add_action( "wp_ajax_words_of_hope_action", "ajax_words_of_hope_action_submit" );
add_action( "wp_ajax_nopriv_words_of_hope_action", "ajax_words_of_hope_action_submit" );
// === CR Live Data: Real-time donation updates ===
add_action('init', function() {
\Manza\CharityRight\Endpoints\CacheBust::register();
});
add_action('wp_enqueue_scripts', function() {
if (is_singular('campaign')) {
wp_enqueue_script('cr-live', get_template_directory_uri() . '/assets/js/cr-live.js', [], '1.0.0', true);
}
});
// === End CR Live Data ===
// menu-fix-bust: force cache bust for gute.js
add_filter('script_loader_src', function($src, $handle) {
if ($handle === 'vl_new_acf_blocks') {
$src = add_query_arg('bust', '20260218d', $src);
}
return $src;
}, 10, 2);
// === UTM Parameter Forwarding (2026-02-19) ===
add_action('wp_footer', function() {
?>
<script>
(function(){
var utmParams = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content','utm_id'];
var stored = {};
// 1. Check URL for UTM params
var urlParams = new URLSearchParams(window.location.search);
var hasNew = false;
utmParams.forEach(function(p) {
var v = urlParams.get(p);
if (v) { stored[p] = v; hasNew = true; }
});
// 2. If new UTMs found, save to localStorage
if (hasNew) {
localStorage.setItem('cr_utm', JSON.stringify(stored));
localStorage.setItem('cr_utm_ts', Date.now());
} else {
// 3. Load from localStorage (expire after 30 days)
try {
var s = localStorage.getItem('cr_utm');
var ts = localStorage.getItem('cr_utm_ts');
if (s && ts && (Date.now() - ts) < 30*86400000) {
stored = JSON.parse(s);
}
} catch(e) {}
}
// 4. Nothing stored? Exit
if (!Object.keys(stored).length) return;
// 5. Append UTMs to all links pointing to app.charityright.org.uk
function tagLinks() {
var links = document.querySelectorAll('a[href*="app.charityright.org.uk"]');
links.forEach(function(a) {
try {
var url = new URL(a.href);
var changed = false;
Object.keys(stored).forEach(function(k) {
if (!url.searchParams.has(k)) {
url.searchParams.set(k, stored[k]);
changed = true;
}
});
if (changed) a.href = url.toString();
} catch(e) {}
});
}
// Run on load and observe DOM changes
tagLinks();
var observer = new MutationObserver(function() { tagLinks(); });
observer.observe(document.body, { childList: true, subtree: true });
})();
</script>
<?php
}, 99);
// === END UTM Parameter Forwarding ===
// Add padding offset class for fixed header on pages that need it
add_filter('body_class', function($classes) {
// Pages with their own header offset handling
$excluded_templates = ['page-ramadan.php', 'page-fidya-kaffarah.php'];
$template = get_page_template_slug();
$template_file = basename(get_page_template());
if (!in_array($template, $excluded_templates) && !in_array($template_file, $excluded_templates)) {
$classes[] = 'cr-header-offset';
}
return $classes;
});
// Add crossorigin="anonymous" to third-party scripts for better error reporting
add_filter('script_loader_tag', function($tag, $handle, $src) {
if (strpos($src, home_url()) !== false || strpos($src, '/wp-') === 0) {
return $tag; // Skip local scripts
}
if (strpos($tag, 'crossorigin') !== false) {
return $tag; // Already has crossorigin
}
return str_replace(' src=', ' crossorigin="anonymous" src=', $tag);
}, 10, 3);
// ══════════════════════════════════════════════════════
// IN-APP BROWSER FIXES — SITE-WIDE
// Fixes conversion-killing issues in Instagram, Facebook,
// TikTok, WhatsApp, Snapchat, Twitter, Threads in-app browsers
// ══════════════════════════════════════════════════════
add_action("wp_head", "cr_inapp_browser_fixes", 1);
function cr_inapp_browser_fixes() {
?>
<style id="cr-inapp-fixes">
/* Prevent auto-zoom on input focus — iOS zooms on <16px inputs */
@supports (-webkit-touch-callout: none) {
input, select, textarea { font-size: max(16px, 1em) !important; }
}
/* Safe area insets for notched devices */
body { padding-bottom: env(safe-area-inset-bottom, 0px); }
/* Prevent text inflation in WebViews */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* Fix rubber-banding scroll issues */
html { overscroll-behavior-y: none; }
/* 100vh fix — add dvh fallback globally */
.min-h-screen, [style*="100vh"] { min-height: 100vh; min-height: 100dvh; }
/* Minimum touch targets (WCAG 2.5.8) */
a, button, [role="button"], input[type="submit"], input[type="button"] { min-height: 44px; }
/* Fix Livewire/Alpine flicker in WebViews */
[x-cloak] { display: none !important; }
/* In-app browser banner */
#cr-inapp-banner {
display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 999999;
padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px));
background: rgba(26,26,46,.97); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
border-top: 1px solid rgba(228,34,129,.25);
text-align: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
#cr-inapp-banner p {
color: #fff; font-size: 13px; font-weight: 600; margin: 0 0 10px; line-height: 1.3;
}
#cr-inapp-banner .cr-iab-open {
display: inline-block; padding: 11px 28px;
background: #E42281; color: #fff !important; font-size: 13px; font-weight: 700;
border-radius: 8px; text-decoration: none !important; min-height: 44px; line-height: 22px;
}
#cr-inapp-banner .cr-iab-dismiss {
display: block; margin: 10px auto 0; background: none; border: none;
color: rgba(255,255,255,.35); font-size: 11px; cursor: pointer; padding: 6px;
min-height: 30px;
}
</style>
<?php
}
add_action("wp_body_open", "cr_inapp_browser_banner", 1);
function cr_inapp_browser_banner() {
?>
<div id="cr-inapp-banner" role="complementary" aria-label="Open in browser">
<p>For the best experience, open in your browser</p>
<a id="cr-iab-open" class="cr-iab-open" href="#">Open in Safari ↗</a>
<button class="cr-iab-dismiss" id="cr-iab-dismiss" aria-label="Dismiss">Continue here</button>
</div>
<script>
(function(){
if(sessionStorage.getItem("cr_iab_off")) return;
var ua=navigator.userAgent||"";
var isInApp=false;
// Detect all major in-app browsers
if(/FBAN|FBAV/i.test(ua)) isInApp=true; // Facebook
if(/Instagram/i.test(ua)) isInApp=true; // Instagram
if(/TikTok|BytedanceWebview|musical_ly/i.test(ua)) isInApp=true; // TikTok
if(/Snapchat/i.test(ua)) isInApp=true; // Snapchat
if(/Twitter|Threads/i.test(ua)) isInApp=true; // Twitter/Threads
if(/Line\//i.test(ua)) isInApp=true; // LINE
if(/\bwv\b|WebView/i.test(ua)) isInApp=true; // Generic Android WebView
if(/LinkedIn/i.test(ua)) isInApp=true; // LinkedIn
if(/Pinterest/i.test(ua)) isInApp=true; // Pinterest
if(/Telegram/i.test(ua)) isInApp=true; // Telegram
// iOS: has iPhone/iPad but NOT Safari = in-app WebView
if(!isInApp&&/iPhone|iPad/.test(ua)&&!/Safari/i.test(ua)) isInApp=true;
if(!isInApp) return;
var b=document.getElementById("cr-inapp-banner");
var o=document.getElementById("cr-iab-open");
var d=document.getElementById("cr-iab-dismiss");
if(!b||!o) return;
var url=window.location.href;
if(/iPhone|iPad|iPod/i.test(ua)){
o.href=url; o.setAttribute("target","_blank"); o.textContent="Open in Safari ↗";
} else {
o.href="intent:"+url.replace(/^https?/,"")+"#Intent;scheme=https;end";
o.textContent="Open in Chrome ↗";
}
b.style.display="block";
if(d) d.addEventListener("click",function(){
b.style.display="none";
sessionStorage.setItem("cr_iab_off","1");
});
// CRITICAL: Intercept window.open calls and redirect to same-window
// This fixes ALL donate buttons site-wide that use window.open
var _origOpen = window.open;
window.open = function(url, target) {
if(url && typeof url === "string" && (url.indexOf("app.charityright.org.uk") > -1 || url.indexOf("/donate") > -1)) {
// In-app browser: redirect same-window instead of popup
window.location.href = url;
return null;
}
return _origOpen.apply(window, arguments);
};
})();
</script>
<?php
}
// ══════════════════════════════════════════════════════
// PREFETCH + PRECONNECT — Speed up checkout transition
// In-app browsers have cold DNS/TCP so this is critical
// ══════════════════════════════════════════════════════
add_action("wp_head", "cr_inapp_prefetch_hints", 2);
function cr_inapp_prefetch_hints() {
?>
<link rel="preconnect" href="https://app.charityright.org.uk" crossorigin>
<link rel="dns-prefetch" href="https://app.charityright.org.uk">
<link rel="preconnect" href="https://js.stripe.com" crossorigin>
<link rel="dns-prefetch" href="https://js.stripe.com">
<link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
<?php
}

View File

@@ -0,0 +1,463 @@
<?php
/**
* Template Name: Fidya Kaffarah
*/
get_header();
$site_url = get_site_url();
$uploads = get_site_url() . '/wp-content/uploads/2026/03';
?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
.fk, .fk * {
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
margin: 0; padding: 0;
}
.fk, .fk h1, .fk h2, .fk h3, .fk h4, .fk span, .fk div, .fk p, .fk li, .fk strong, .fk .fk-amount, .fk .fk-card-label { color: #fff !important; }
.fk {
padding: 0 !important;
background: #0a0a0a !important;
color: #fff;
}
.fk-inner {
max-width: 1200px;
padding: 0 24px;
margin: 0 auto;
}
/* Hero section with background image */
.fk-hero {
position: relative;
padding: 120px 20px 80px;
overflow: hidden;
min-height: 480px;
display: flex;
align-items: flex-end;
}
.fk-hero-bg {
position: absolute;
inset: 0;
background-image: url('<?php echo $uploads; ?>/fidya-hero.jpg');
background-size: cover;
background-position: center 25%;
z-index: 0;
}
.fk-hero-bg::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
to bottom,
rgba(10,10,10,0.3) 0%,
rgba(10,10,10,0.6) 40%,
rgba(10,10,10,0.92) 100%
);
}
.fk-hero .fk-inner {
position: relative;
z-index: 1;
}
.fk h1 {
color: #fff !important;
font-size: clamp(2rem, 5vw, 3.2rem);
font-weight: 900;
line-height: 1.1;
margin-bottom: 12px;
letter-spacing: -0.03em;
}
.fk h1 span { color: #e42281 !important; }
.fk .fk-sub {
font-size: 1.05rem;
color: #ccc !important;
font-weight: 300;
line-height: 1.6;
max-width: 600px;
margin-bottom: 0;
}
/* Content section */
.fk-content {
padding: 60px 20px 80px;
}
/* Cards */
.fk-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 60px;
}
.fk-card {
background: #141414;
border: 1px solid #222;
position: relative;
overflow: hidden;
transition: border-color 0.3s, background 0.3s;
}
.fk-card:hover {
border-color: #e42281 !important;
background: #1a1a1a;
}
/* Card images */
.fk-card-img {
width: 100%;
height: 220px;
object-fit: cover;
display: block;
filter: brightness(0.85);
transition: filter 0.3s, transform 0.4s;
}
.fk-card:hover .fk-card-img {
filter: brightness(1);
transform: scale(1.03);
}
.fk-card-img-wrap {
overflow: hidden;
position: relative;
}
.fk-card-img-wrap::after {
content: "";
position: absolute;
bottom: 0; left: 0; right: 0;
height: 60px;
background: linear-gradient(to bottom, transparent, #141414);
}
.fk-card:hover .fk-card-img-wrap::after {
background: linear-gradient(to bottom, transparent, #1a1a1a);
}
.fk-card-body {
padding: 32px 32px 40px;
}
.fk-card-label {
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
color: #e42281 !important;
margin-bottom: 20px;
display: block;
}
.fk-card h2 {
color: #fff !important;
font-size: 1.6rem;
font-weight: 800;
line-height: 1.15;
margin-bottom: 16px;
letter-spacing: -0.02em;
}
.fk-card p {
font-size: 0.92rem;
color: #999 !important;
line-height: 1.65;
margin-bottom: 24px;
font-weight: 300;
}
.fk-card .fk-amount {
font-size: 1.1rem;
font-weight: 700;
color: #fff;
margin-bottom: 8px;
}
.fk-card .fk-amount span {
color: #e42281 !important;
font-size: 1.4rem;
}
.fk-card .fk-calc {
font-size: 0.82rem;
color: #666 !important;
margin-bottom: 28px;
font-weight: 400;
}
.fk-btn {
display: inline-block;
background: #e42281;
color: #fff !important;
font-size: 0.85rem;
font-weight: 700;
padding: 14px 32px;
text-decoration: none !important;
letter-spacing: 0.5px;
text-transform: uppercase;
border-radius: 2px;
transition: background 0.2s, transform 0.2s;
}
.fk-btn:hover {
background: #c2185b;
transform: translateY(-1px);
}
.fk-btn:visited { color: #fff !important; }
/* Who is this for sections */
.fk-who {
margin-top: 20px;
padding-top: 24px;
border-top: 1px solid #222;
}
.fk-who-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: #555 !important;
margin-bottom: 12px;
display: block;
}
.fk-who ul {
list-style: none;
padding: 0;
margin: 0;
}
.fk-who li {
font-size: 0.85rem;
color: #bbb !important;
line-height: 1.5;
padding: 6px 0 6px 20px;
position: relative;
font-weight: 400;
}
.fk-who li::before {
content: "";
position: absolute;
left: 0;
top: 12px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #e42281;
}
/* Full-width image strip */
.fk-image-strip {
position: relative;
width: 100%;
height: 320px;
overflow: hidden;
margin-bottom: 60px;
}
.fk-image-strip img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center 30%;
display: block;
filter: brightness(0.7);
}
.fk-image-strip::before,
.fk-image-strip::after {
content: "";
position: absolute;
left: 0; right: 0;
height: 80px;
z-index: 1;
}
.fk-image-strip::before {
top: 0;
background: linear-gradient(to bottom, #0a0a0a, transparent);
}
.fk-image-strip::after {
bottom: 0;
background: linear-gradient(to top, #0a0a0a, transparent);
}
.fk-image-strip-text {
position: absolute;
bottom: 32px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
text-align: center;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 3px;
color: rgba(255,255,255,0.6) !important;
}
/* Not sure section */
.fk-unsure {
text-align: center;
margin-bottom: 60px;
padding: 32px;
background: #141414;
border: 1px solid #222;
}
.fk-unsure h3 {
color: #fff !important;
font-size: 1.1rem;
font-weight: 700;
margin-bottom: 12px;
color: #fff;
}
.fk-unsure p {
font-size: 0.92rem;
color: #999 !important;
line-height: 1.6;
margin-bottom: 12px;
font-weight: 300;
}
.fk-unsure p:last-child { margin-bottom: 0; }
.fk-unsure strong {
color: #fff;
font-weight: 600;
}
/* Bottom quote with background */
.fk-quote-section {
position: relative;
padding: 80px 20px;
overflow: hidden;
}
.fk-quote-bg {
position: absolute;
inset: 0;
background-image: url('<?php echo $uploads; ?>/fidya-child-meal.jpg');
background-size: cover;
background-position: center;
z-index: 0;
}
.fk-quote-bg::after {
content: "";
position: absolute;
inset: 0;
background: rgba(10,10,10,0.82);
}
.fk-quote {
position: relative;
z-index: 1;
text-align: center;
max-width: 600px;
margin: 0 auto;
}
.fk-quote p {
font-size: 1.05rem;
color: #ccc !important;
font-style: italic;
line-height: 1.7;
font-weight: 300;
}
.fk-quote .fk-attr {
font-size: 0.8rem;
color: #888 !important;
font-style: normal;
margin-top: 8px;
}
@media (max-width: 700px) {
.fk-cards {
grid-template-columns: 1fr;
}
.fk, .fk h1, .fk h2, .fk h3, .fk h4, .fk span, .fk div, .fk p, .fk li, .fk strong, .fk .fk-amount, .fk .fk-card-label { color: #fff !important; }
.fk-hero { padding: 80px 16px 50px; min-height: 380px; }
.fk-content { padding: 40px 16px 60px; }
.fk-card-body { padding: 24px 24px 32px; }
.fk-card-img { height: 180px; }
.fk-image-strip { height: 220px; }
}
</style>
<div class="fk">
<!-- Hero with background photography -->
<div class="fk-hero">
<div class="fk-hero-bg"></div>
<div class="fk-inner">
<h1>Missed Fasts?<br><span>Make it Right.</span></h1>
<p class="fk-sub">If you are unable to fast during Ramadan, or have broken a fast without a valid reason, Islam provides a way to fulfil your obligation. Choose the option that applies to you.</p>
</div>
</div>
<!-- Main content -->
<div class="fk-content">
<div class="fk-inner">
<div class="fk-cards">
<!-- FIDYA -->
<div class="fk-card">
<div class="fk-card-img-wrap">
<img class="fk-card-img" src="<?php echo $uploads; ?>/fidya-quran-elder.jpg" alt="An elderly man in prayer — for those who are unable to fast" loading="lazy">
</div>
<div class="fk-card-body">
<span class="fk-card-label">Fidya</span>
<h2>I am unable to fast<br>and cannot make it up.</h2>
<p>Fidya is a <strong style="color:#fff;font-weight:600;">compensation</strong> for those who cannot fast during Ramadan due to a long-term illness, old age, or a condition that is not expected to improve. For each day of fasting missed, you are required to feed one person in need.</p>
<div class="fk-amount"><span>&pound;5</span> per missed fast</div>
<div class="fk-calc">Feeds one person for each day you are unable to fast</div>
<a href="https://app.charityright.org.uk/donate/?v=2&project=18" class="fk-btn">Pay Fidya</a>
<div class="fk-who">
<span class="fk-who-label">This applies if you</span>
<ul>
<li>Have a long-term or chronic illness with no expectation of recovery</li>
<li>Are elderly and physically unable to fast</li>
<li>Have a medical condition requiring daily medication that prevents fasting</li>
<li>Have been advised by a doctor that fasting would be harmful to your health</li>
</ul>
</div>
</div>
</div>
<!-- KAFFARAH -->
<div class="fk-card">
<div class="fk-card-img-wrap">
<img class="fk-card-img" src="<?php echo $uploads; ?>/kaffarah-community.jpg" alt="Children receiving meals at a community feeding programme" loading="lazy">
</div>
<div class="fk-card-body">
<span class="fk-card-label">Kaffarah</span>
<h2>I deliberately broke<br>my fast without reason.</h2>
<p>Kaffarah is an <strong style="color:#fff;font-weight:600;">expiation</strong> required when a person deliberately breaks their Ramadan fast without a valid Islamic reason. The obligation is to fast for 60 consecutive days. If you are genuinely unable to do so, you must feed 60 people in need for each fast broken.</p>
<div class="fk-amount"><span>&pound;300</span> per broken fast</div>
<div class="fk-calc">Equivalent to feeding 60 people in need</div>
<a href="https://app.charityright.org.uk/donate/?v=2&project=28" class="fk-btn">Pay Kaffarah</a>
<div class="fk-who">
<span class="fk-who-label">This applies if you</span>
<ul>
<li>Deliberately ate or drank during fasting hours without a valid reason</li>
<li>Intentionally broke your fast in a way that invalidates it</li>
<li>Are unable to fast for 60 consecutive days as the primary expiation</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Full-width image strip — feeding the hungry -->
<div class="fk-image-strip">
<img src="<?php echo $uploads; ?>/fidya-feeding.jpg" alt="Food being served to those in need" loading="lazy">
<span class="fk-image-strip-text">Every meal matters</span>
</div>
<div class="fk-unsure">
<h3>Can you make up your fasts?</h3>
<p>If you missed fasts due to a <strong>temporary</strong> illness, travel, menstruation, or pregnancy, you are expected to make up the missed days before the next Ramadan. In this case, neither Fidya nor Kaffarah is required.</p>
<p>Fidya applies only when you are <strong>permanently unable</strong> to fast. Kaffarah applies only when you <strong>deliberately</strong> broke a fast without a valid reason. If you are unsure, consult a scholar.</p>
</div>
</div>
</div>
<!-- Quote section with background photography -->
<div class="fk-quote-section">
<div class="fk-quote-bg"></div>
<div class="fk-quote">
<p>&ldquo;And upon those who are able [to fast, but with hardship] &mdash; a ransom of feeding a poor person.&rdquo;</p>
<p class="fk-attr">&mdash; Qur&rsquo;an, Surah Al-Baqarah (2:184)</p>
</div>
</div>
</div>
<?php get_footer(); ?>

View File

@@ -0,0 +1,297 @@
<?php
/**
* Template Name: Fidya Kaffarah
*/
get_header();
$site_url = get_site_url();
?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
.fk, .fk * {
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
margin: 0; padding: 0;
}
.fk, .fk h1, .fk h2, .fk h3, .fk h4, .fk span, .fk div, .fk p, .fk li, .fk strong, .fk .fk-amount, .fk .fk-card-label { color: #fff !important; }
.fk {
padding: 76px 20px 80px !important;
background: #0a0a0a !important;
color: #fff;
min-height: 100vh; height: 100dvh; min-height: 100dvh;
}
.fk-inner {
max-width: 1200px;
padding: 0 24px;
margin: 0 auto;
}
.fk h1 {
color: #fff !important;
font-size: clamp(2rem, 5vw, 3.2rem);
font-weight: 900;
line-height: 1.1;
margin-bottom: 12px;
letter-spacing: -0.03em;
}
.fk h1 span { color: #e42281 !important; }
.fk .fk-sub {
font-size: 1.05rem;
color: #aaa !important;
font-weight: 300;
line-height: 1.6;
max-width: 600px;
margin-bottom: 50px;
}
/* Cards */
.fk-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 60px;
}
.fk-card {
background: #141414;
border: 1px solid #222;
padding: 40px 32px;
position: relative;
overflow: hidden;
transition: border-color 0.3s, background 0.3s;
}
.fk-card:hover {
border-color: #e42281 !important;
background: #1a1a1a;
}
.fk-card-label {
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
color: #e42281 !important;
margin-bottom: 20px;
display: block;
}
.fk-card h2 {
color: #fff !important;
font-size: 1.6rem;
font-weight: 800;
line-height: 1.15;
margin-bottom: 16px;
letter-spacing: -0.02em;
}
.fk-card p {
font-size: 0.92rem;
color: #999 !important;
line-height: 1.65;
margin-bottom: 24px;
font-weight: 300;
}
.fk-card .fk-amount {
font-size: 1.1rem;
font-weight: 700;
color: #fff;
margin-bottom: 8px;
}
.fk-card .fk-amount span {
color: #e42281 !important;
font-size: 1.4rem;
}
.fk-card .fk-calc {
font-size: 0.82rem;
color: #666 !important;
margin-bottom: 28px;
font-weight: 400;
}
.fk-btn {
display: inline-block;
background: #e42281;
color: #fff !important;
font-size: 0.85rem;
font-weight: 700;
padding: 14px 32px;
text-decoration: none !important;
letter-spacing: 0.5px;
text-transform: uppercase;
border-radius: 2px;
transition: background 0.2s, transform 0.2s;
}
.fk-btn:hover {
background: #c2185b;
transform: translateY(-1px);
}
.fk-btn:visited { color: #fff !important; }
/* Who is this for sections */
.fk-who {
margin-top: 20px;
padding-top: 24px;
border-top: 1px solid #222;
}
.fk-who-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: #555 !important;
margin-bottom: 12px;
display: block;
}
.fk-who ul {
list-style: none;
padding: 0;
margin: 0;
}
.fk-who li {
font-size: 0.85rem;
color: #bbb !important;
line-height: 1.5;
padding: 6px 0 6px 20px;
position: relative;
font-weight: 400;
}
.fk-who li::before {
content: "";
position: absolute;
left: 0;
top: 12px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #e42281;
}
/* Bottom quote */
.fk-quote {
text-align: center;
max-width: 600px;
margin: 0 auto;
padding-top: 40px;
border-top: 1px solid #1a1a1a;
}
.fk-quote p {
font-size: 0.95rem;
color: #777 !important;
font-style: italic;
line-height: 1.7;
font-weight: 300;
}
.fk-quote .fk-attr {
font-size: 0.8rem;
color: #555 !important;
font-style: normal;
margin-top: 8px;
}
/* Back link */
/* Not sure section */
.fk-unsure {
text-align: center;
margin-bottom: 60px;
padding: 32px;
background: #141414;
border: 1px solid #222;
}
.fk-unsure h3 {
color: #fff !important;
font-size: 1.1rem;
font-weight: 700;
margin-bottom: 12px;
color: #fff;
}
.fk-unsure p {
font-size: 0.92rem;
color: #999 !important;
line-height: 1.6;
margin-bottom: 12px;
font-weight: 300;
}
.fk-unsure p:last-child { margin-bottom: 0; }
.fk-unsure strong {
color: #fff;
font-weight: 600;
}
@media (max-width: 700px) {
.fk-cards {
grid-template-columns: 1fr;
}
.fk, .fk h1, .fk h2, .fk h3, .fk h4, .fk span, .fk div, .fk p, .fk li, .fk strong, .fk .fk-amount, .fk .fk-card-label { color: #fff !important; }
.fk {
padding: 76px 20px 80px !important; padding: 40px 16px 60px; }
.fk-card { padding: 32px 24px; }
}
</style>
<div class="fk">
<div class="fk-inner">
<h1>Missed Fasts?<br><span>Make it Right.</span></h1>
<p class="fk-sub">If you are unable to fast during Ramadan, or have broken a fast without a valid reason, Islam provides a way to fulfil your obligation. Choose the option that applies to you.</p>
<div class="fk-cards">
<!-- FIDYA -->
<div class="fk-card">
<span class="fk-card-label">Fidya</span>
<h2>I am unable to fast<br>and cannot make it up.</h2>
<p>Fidya is a <strong style="color:#fff;font-weight:600;">compensation</strong> for those who cannot fast during Ramadan due to a long-term illness, old age, or a condition that is not expected to improve. For each day of fasting missed, you are required to feed one person in need.</p>
<div class="fk-amount"><span>&pound;5</span> per missed fast</div>
<div class="fk-calc">Feeds one person for each day you are unable to fast</div>
<a href="https://app.charityright.org.uk/donate/?v=2&project=18" class="fk-btn">Pay Fidya</a>
<div class="fk-who">
<span class="fk-who-label">This applies if you</span>
<ul>
<li>Have a long-term or chronic illness with no expectation of recovery</li>
<li>Are elderly and physically unable to fast</li>
<li>Have a medical condition requiring daily medication that prevents fasting</li>
<li>Have been advised by a doctor that fasting would be harmful to your health</li>
</ul>
</div>
</div>
<!-- KAFFARAH -->
<div class="fk-card">
<span class="fk-card-label">Kaffarah</span>
<h2>I deliberately broke<br>my fast without reason.</h2>
<p>Kaffarah is an <strong style="color:#fff;font-weight:600;">expiation</strong> required when a person deliberately breaks their Ramadan fast without a valid Islamic reason. The obligation is to fast for 60 consecutive days. If you are genuinely unable to do so, you must feed 60 people in need for each fast broken.</p>
<div class="fk-amount"><span>&pound;300</span> per broken fast</div>
<div class="fk-calc">Equivalent to feeding 60 people in need</div>
<a href="https://app.charityright.org.uk/donate/?v=2&project=28" class="fk-btn">Pay Kaffarah</a>
<div class="fk-who">
<span class="fk-who-label">This applies if you</span>
<ul>
<li>Deliberately ate or drank during fasting hours without a valid reason</li>
<li>Intentionally broke your fast in a way that invalidates it</li>
<li>Are unable to fast for 60 consecutive days as the primary expiation</li>
</ul>
</div>
</div>
</div>
<div class="fk-unsure">
<h3>Can you make up your fasts?</h3>
<p>If you missed fasts due to a <strong>temporary</strong> illness, travel, menstruation, or pregnancy, you are expected to make up the missed days before the next Ramadan. In this case, neither Fidya nor Kaffarah is required.</p>
<p>Fidya applies only when you are <strong>permanently unable</strong> to fast. Kaffarah applies only when you <strong>deliberately</strong> broke a fast without a valid reason. If you are unsure, consult a scholar.</p>
</div>
<div class="fk-quote">
<p>&ldquo;And upon those who are able [to fast, but with hardship] &mdash; a ransom of feeding a poor person.&rdquo;</p>
<p class="fk-attr">&mdash; Qur&rsquo;an, Surah Al-Baqarah (2:184)</p>
</div>
</div>
</div>
<?php get_footer(); ?>