Compare commits

..

1 commit

Author SHA1 Message Date
cloudflare-workers-and-pages[bot]
63e79665e1
Update wrangler config name to rda-react-sanity 2026-04-30 20:35:57 +00:00
37 changed files with 2001 additions and 21114 deletions

View file

@ -1,14 +0,0 @@
# ── Sanity CMS ───────────────────────────────────────────────────────────────
# Get these values from https://www.sanity.io/manage after creating a project.
# Without SANITY_PROJECT_ID the site falls back to the local markdown files
# in src/content/news/.
# Your Sanity project ID (required to enable Sanity)
SANITY_PROJECT_ID=
# Dataset name — "production" is the default created by `sanity init`
SANITY_DATASET=production
# Read token — only needed for draft/preview content.
# Leave blank to serve published content without authentication.
SANITY_API_TOKEN=

7
.gitignore vendored
View file

@ -1,12 +1,5 @@
node_modules
dist
public/studio
.env
.env.local
.env.*.local
studio/.env
studio/.env.local
studio/.sanity
.DS_Store
server/public
vite.config.ts.*

View file

@ -1,51 +0,0 @@
FROM node:20-alpine AS base
WORKDIR /app
# ── 1. Install main project deps ─────────────────────────────────────────────
FROM base AS deps
COPY package.json package-lock.json ./
RUN npm install --legacy-peer-deps
# ── 2. Install studio deps ────────────────────────────────────────────────────
FROM base AS studio-deps
COPY studio/package.json studio/package-lock.json* ./
RUN npm install --legacy-peer-deps
# ── 3. Build Sanity Studio ────────────────────────────────────────────────────
FROM base AS studio-build
COPY --from=studio-deps /app/node_modules ./node_modules
COPY studio/ ./
ARG SANITY_STUDIO_PROJECT_ID
ARG SANITY_STUDIO_DATASET=production
ENV SANITY_STUDIO_PROJECT_ID=$SANITY_STUDIO_PROJECT_ID
ENV SANITY_STUDIO_DATASET=$SANITY_STUDIO_DATASET
RUN npm run build
# ── 4. Build Astro site (with studio output as static files) ──────────────────
FROM base AS build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
COPY --from=studio-build /app/dist ./public/studio
# Remove index.html from public so static middleware doesn't serve it —
# the SSR route rewrites asset paths before serving it.
RUN rm -f ./public/studio/index.html
ARG SANITY_PROJECT_ID
ARG SANITY_DATASET=production
ARG SANITY_API_TOKEN
ENV SANITY_PROJECT_ID=$SANITY_PROJECT_ID
ENV SANITY_DATASET=$SANITY_DATASET
ENV SANITY_API_TOKEN=$SANITY_API_TOKEN
RUN npm run build
# Place studio index.html outside dist/client/ so SSR can read it without
# it being served as a static file.
COPY --from=studio-build /app/dist/index.html ./dist/studio-index.html
# ── 5. Runtime ────────────────────────────────────────────────────────────────
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD ["node", "./dist/server/entry.mjs"]

View file

@ -1,11 +1,7 @@
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import node from "@astrojs/node";
export default defineConfig({
output: "server",
adapter: node({
mode: "standalone",
}),
output: "static",
integrations: [tailwind()],
});

View file

@ -1,348 +0,0 @@
type Env = {
TURNSTILE_SECRET_KEY?: string;
RESEND_API_KEY?: string;
CONTACT_FROM_EMAIL?: string;
CONTACT_TO_EMAIL?: string;
};
const jsonResponse = (data: Record<string, unknown>, status = 200) =>
new Response(JSON.stringify(data), {
status,
headers: { "content-type": "application/json" },
});
const fetchWithTimeout = async (
input: RequestInfo | URL,
init: RequestInit,
timeoutMs: number,
label: string,
) => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
return await fetch(input, { ...init, signal: controller.signal });
} catch (error) {
const message =
error instanceof Error ? error.message : "Unknown error";
throw new Error(`${label} request failed: ${message}`);
} finally {
clearTimeout(timeout);
}
};
const readTextField = (value: string | null) => (value ?? "").trim();
const toTitleCase = (value: string) =>
value
.replace(/_/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase());
const parseFormFields = async (request: Request) => {
const contentType = request.headers.get("content-type") ?? "";
const data = new Map<string, string[]>();
const appendValue = (key: string, value: string | null) => {
const normalized = readTextField(value);
if (!normalized) return;
data.set(key, [...(data.get(key) ?? []), normalized]);
};
if (contentType.includes("application/x-www-form-urlencoded")) {
const bodyText = await request.text();
const params = new URLSearchParams(bodyText);
for (const [key, value] of params.entries()) {
appendValue(key, value);
}
return data;
}
const formData = await request.formData();
for (const [key, value] of formData.entries()) {
appendValue(key, typeof value === "string" ? value : value.name);
}
return data;
};
const formatValue = (values: string[] | undefined) => (values ?? []).join(", ");
const labelMap: Record<string, string> = {
applicant_first_names: "Applicant first name(s)",
applicant_last_name: "Applicant last name",
referee_name: "Referee full name",
referee_job_title: "Referee job title or role",
referee_organisation: "Referee organisation",
referee_address: "Referee address",
referee_telephone: "Referee telephone",
referee_email: "Referee email",
relationship_capacity: "Capacity in which referee knows the applicant",
relationship_other: "Relationship (if Other)",
known_duration: "How long referee has known the applicant",
related_to_applicant: "Related to the applicant",
character_assessment: "Character and personal qualities",
reliable_punctual: "Reliable and punctual",
suitable_for_role: "Suitable to work with people with disabilities",
suitability_concerns: "Concerns regarding suitability",
additional_comments: "Additional comments",
declaration_truth: "Declaration of truth and accuracy",
declaration_consent: "Declaration of confidentiality understanding",
declaration_signature: "Signature name",
declaration_date: "Date",
};
const requiredFields = [
"applicant_first_names",
"applicant_last_name",
"referee_name",
"referee_email",
"known_duration",
"character_assessment",
"suitable_for_role",
"declaration_truth",
];
const sectionOrder = [
{
title: "Applicant",
fields: ["applicant_first_names", "applicant_last_name"],
},
{
title: "Referee Details",
fields: [
"referee_name",
"referee_job_title",
"referee_organisation",
"referee_address",
"referee_telephone",
"referee_email",
],
},
{
title: "Relationship to Applicant",
fields: [
"relationship_capacity",
"relationship_other",
"known_duration",
"related_to_applicant",
],
},
{
title: "Assessment",
fields: [
"character_assessment",
"reliable_punctual",
"suitable_for_role",
"suitability_concerns",
"additional_comments",
],
},
{
title: "Declaration",
fields: [
"declaration_truth",
"declaration_consent",
"declaration_signature",
"declaration_date",
],
},
];
const handlePost: PagesFunction<Env> = async ({ request, env }) => {
const fields = await parseFormFields(request);
const missingRequired = requiredFields.filter(
(field) => !formatValue(fields.get(field)),
);
if (missingRequired.length > 0) {
return jsonResponse(
{
ok: false,
error: "Missing required fields.",
details: missingRequired.map((field) => labelMap[field] ?? field).join(", "),
},
400,
);
}
if (!env.TURNSTILE_SECRET_KEY) {
return jsonResponse(
{ ok: false, error: "Turnstile secret key not configured." },
500,
);
}
const turnstileToken = formatValue(fields.get("cf-turnstile-response"));
if (!turnstileToken) {
return jsonResponse(
{ ok: false, error: "Turnstile verification failed." },
400,
);
}
const verifyBody = new URLSearchParams({
secret: env.TURNSTILE_SECRET_KEY,
response: turnstileToken,
remoteip: request.headers.get("CF-Connecting-IP") ?? "",
});
const verifyResponse = await fetchWithTimeout(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: verifyBody,
},
8000,
"Turnstile",
);
const verifyResult = (await verifyResponse.json()) as {
success?: boolean;
"error-codes"?: string[];
messages?: string[];
hostname?: string;
action?: string;
};
if (!verifyResult.success) {
const details = [
verifyResult["error-codes"]?.length
? `codes=${verifyResult["error-codes"].join(",")}`
: "",
verifyResult.messages?.length
? `messages=${verifyResult.messages.join(",")}`
: "",
verifyResult.hostname ? `hostname=${verifyResult.hostname}` : "",
verifyResult.action ? `action=${verifyResult.action}` : "",
]
.filter(Boolean)
.join(" | ")
.slice(0, 500);
return jsonResponse(
{
ok: false,
error: "Turnstile verification failed.",
details: details || undefined,
},
400,
);
}
if (!env.RESEND_API_KEY) {
return jsonResponse(
{ ok: false, error: "Email provider not configured." },
500,
);
}
const fromEmail = env.CONTACT_FROM_EMAIL;
if (!fromEmail) {
return jsonResponse(
{ ok: false, error: "Sender email not configured." },
500,
);
}
const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in";
const applicantName = [
formatValue(fields.get("applicant_first_names")),
formatValue(fields.get("applicant_last_name")),
]
.filter(Boolean)
.join(" ");
const refereeName = formatValue(fields.get("referee_name"));
const bodyText = sectionOrder
.map(({ title, fields: sectionFields }) => {
const lines = sectionFields
.map((field) => {
const value = formatValue(fields.get(field));
if (!value) return null;
return `${labelMap[field] ?? toTitleCase(field)}: ${value}`;
})
.filter(Boolean)
.join("\n");
return lines ? `${title}\n${lines}` : "";
})
.filter(Boolean)
.join("\n\n");
const extraFields = [...fields.keys()].filter(
(field) =>
field !== "cf-turnstile-response" &&
!sectionOrder.some((section) => section.fields.includes(field)),
);
const extraText = extraFields
.map((field) => {
const value = formatValue(fields.get(field));
return value ? `${labelMap[field] ?? toTitleCase(field)}: ${value}` : "";
})
.filter(Boolean)
.join("\n");
const emailPayload = {
from: fromEmail,
to: [toEmail],
reply_to: formatValue(fields.get("referee_email")) || undefined,
subject: `New volunteer reference${applicantName ? ` for ${applicantName}` : ""}${refereeName ? ` from ${refereeName}` : ""}`,
text: [bodyText, extraText ? `Additional Fields\n${extraText}` : ""]
.filter(Boolean)
.join("\n\n"),
};
const response = await fetchWithTimeout(
"https://api.resend.com/emails",
{
method: "POST",
headers: {
Authorization: `Bearer ${env.RESEND_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(emailPayload),
},
8000,
"Resend",
);
if (!response.ok) {
const contentType = response.headers.get("content-type") ?? "";
const errorText = contentType.includes("application/json")
? JSON.stringify(await response.json().catch(() => ({})))
: await response.text();
const errorCode = response.headers.get("x-ms-error-code");
const details = [
`${response.status} ${response.statusText}`.trim(),
errorCode ? `code=${errorCode}` : "",
errorText.trim(),
]
.filter(Boolean)
.join(" | ")
.slice(0, 500);
return jsonResponse(
{
ok: false,
error: "Failed to send email.",
details,
},
502,
);
}
return jsonResponse({ ok: true });
};
export const onRequest: PagesFunction<Env> = async (context) => {
try {
if (context.request.method !== "POST") {
return jsonResponse(
{ ok: false, error: "Method not allowed." },
405,
);
}
return await handlePost(context);
} catch (error) {
const message =
error instanceof Error ? error.message : "Unknown error";
return jsonResponse(
{ ok: false, error: "Unhandled exception.", details: message },
500,
);
}
};

1447
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,11 +14,9 @@
"db:push": "drizzle-kit push"
},
"dependencies": {
"@astrojs/node": "^9.5.3",
"@hookform/resolvers": "^3.10.0",
"@jridgewell/trace-mapping": "^0.3.25",
"@neondatabase/serverless": "^0.10.4",
"@portabletext/to-html": "^5.0.2",
"@radix-ui/react-accordion": "^1.2.4",
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-aspect-ratio": "^1.1.3",
@ -46,7 +44,6 @@
"@radix-ui/react-toggle": "^1.1.3",
"@radix-ui/react-toggle-group": "^1.1.3",
"@radix-ui/react-tooltip": "^1.2.0",
"@sanity/client": "^7.22.1",
"@tanstack/react-query": "^5.60.5",
"astro": "^5.16.6",
"class-variance-authority": "^0.7.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View file

@ -50,11 +50,11 @@ const year = new Date().getFullYear();
Quick links
</p>
<nav>
<ul class="flex flex-col">
<ul class="flex flex-col gap-2.5">
{quickLinks.map((link) => (
<li>
<a
class="[font-family:'Public_Sans',sans-serif] font-semibold text-sm text-black/70 hover:underline flex items-center min-h-[44px]"
class="[font-family:'Public_Sans',sans-serif] font-semibold text-sm text-black/70 hover:underline"
href={link.href}
>
{link.label}

View file

@ -1,6 +1,5 @@
import { format } from "date-fns";
import { marked } from "marked";
import { isSanityConfigured, getSanityPosts, getSanityPost } from "./sanity";
export type NewsArticle = {
title: string;
@ -77,27 +76,3 @@ export const formatNewsDate = (date: string): string => {
if (Number.isNaN(parsed)) return date;
return format(new Date(parsed), "MMM yyyy");
};
// ── Async data access (prefers Sanity when configured) ────────────────────────
/**
* Returns all articles. Uses Sanity when SANITY_PROJECT_ID is set,
* otherwise falls back to the local markdown files.
*/
export async function getArticles(): Promise<NewsArticle[]> {
if (isSanityConfigured()) {
return getSanityPosts();
}
return newsArticles;
}
/**
* Returns a single article by slug. Uses Sanity when SANITY_PROJECT_ID is set,
* otherwise falls back to the local markdown files.
*/
export async function getArticle(slug: string): Promise<NewsArticle | undefined> {
if (isSanityConfigured()) {
return getSanityPost(slug);
}
return getNewsArticle(slug);
}

View file

@ -1,141 +0,0 @@
import { createClient } from '@sanity/client';
import { toHTML } from '@portabletext/to-html';
import type { NewsArticle } from './news';
// ── Client ────────────────────────────────────────────────────────────────────
export const sanityClient = createClient({
projectId: import.meta.env.SANITY_PROJECT_ID ?? '',
dataset: import.meta.env.SANITY_DATASET ?? 'production',
apiVersion: '2024-01-01',
// useCdn in production; always false during dev so edits show immediately
useCdn: import.meta.env.PROD ?? false,
// Token only needed for draft previews; public published content works without one
token: import.meta.env.SANITY_API_TOKEN,
});
export function isSanityConfigured(): boolean {
return Boolean(import.meta.env.SANITY_PROJECT_ID);
}
// ── GROQ queries ──────────────────────────────────────────────────────────────
const POST_FIELDS = /* groq */ `
_id,
title,
"slug": slug.current,
date,
category,
summary,
"image": image.asset->url,
body
`;
const ALL_POSTS_QUERY = /* groq */ `*[_type == "post"] | order(date desc) { ${POST_FIELDS} }`;
const POST_BY_SLUG_QUERY = /* groq */ `*[_type == "post" && slug.current == $slug][0] { ${POST_FIELDS} }`;
// ── Raw Sanity shape ──────────────────────────────────────────────────────────
interface SanityPost {
_id: string;
title: string;
slug: string;
date: string;
category: string;
summary: string;
image?: string | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body?: any[];
}
// ── Converters ────────────────────────────────────────────────────────────────
function toNewsArticle(post: SanityPost): NewsArticle {
const html = post.body
? toHTML(post.body, {
components: {
marks: {
link: ({ children, value }) => {
const target = value?.blank ? ' target="_blank" rel="noreferrer"' : '';
return `<a href="${value?.href ?? '#'}"${target}>${children}</a>`;
},
},
},
})
: '';
return {
title: post.title,
date: post.date ?? '',
summary: post.summary ?? '',
image: post.image ?? undefined,
slug: post.slug,
category: post.category ?? 'News',
body: '', // raw markdown not used when coming from Sanity
html,
};
}
// ── Home page ─────────────────────────────────────────────────────────────────
export interface SanityCtaCard {
label: string;
title: string;
description: string;
image?: string | null;
href: string;
}
export interface SanitySponsor {
name: string;
logo?: string | null;
url?: string | null;
}
export interface HomePageData {
heroHeadline: string;
heroMission: string;
videoHeading: string;
videoBody: string;
videoYoutubeId: string;
ctaCards: SanityCtaCard[];
sponsorHeading: string;
sponsors: SanitySponsor[];
}
const HOME_PAGE_QUERY = /* groq */ `*[_type == "homePage"][0]{
heroHeadline,
heroMission,
videoHeading,
videoBody,
videoYoutubeId,
ctaCards[]{
label,
title,
description,
"image": image.asset->url,
href,
},
sponsorHeading,
sponsors[]{
name,
"logo": logo.asset->url,
url,
}
}`;
export async function getSanityHomePage(): Promise<HomePageData | null> {
return sanityClient.fetch<HomePageData | null>(HOME_PAGE_QUERY);
}
// ── Public API ────────────────────────────────────────────────────────────────
export async function getSanityPosts(): Promise<NewsArticle[]> {
const posts = await sanityClient.fetch<SanityPost[]>(ALL_POSTS_QUERY);
return posts.map(toNewsArticle);
}
export async function getSanityPost(slug: string): Promise<NewsArticle | undefined> {
const post = await sanityClient.fetch<SanityPost | null>(POST_BY_SLUG_QUERY, { slug });
return post ? toNewsArticle(post) : undefined;
}

View file

@ -16,62 +16,17 @@ const pageSeo = {
const stats = [
{ number: "1975", label: "Founded" },
{ number: "50+", label: "Years serving the Highlands" },
{ number: "4", label: "Days a week, TueFri" },
{ number: "Free", label: "Sessions for all participants" },
{ number: "TueFri", label: "Morning sessions" },
];
const ponies = [
{
name: "Breagha",
img: "/Breagh-bc8e70e8-2880w.webp",
details: "13hh · Skewbald cob mare · 19 yo",
pos: "50% 40%",
quote: "I am the most recent arrival at RDA, part of the herd for the past three and a half years. I take my job very seriously and am careful and steady with riders who are gaining confidence while enjoying a faster walk and trot from time to time. People say that I make them feel calm and happy — that makes me very happy too.",
},
{
name: "Harley",
img: "/Harley-6ece9eca-2880w.webp",
details: "15.3hh · Skewbald cob/Clydesdale · 17 yo",
pos: "50% 35%",
quote: "I like to think that I am good at my job. I stand quietly at the mounting block and always try to do what I have been asked as well as I can. After lessons, nothing makes me more excited than searching for the odd piece of carrot or apple scattered about the field — that is when I do my happy prance about!",
},
{
name: "Connolly",
img: "/connolly-73f3e69f-2880w-2.webp",
details: "15.1hh · Piebald cob gelding · 18 yo",
pos: "50% 30%",
quote: "People say that I am a calm, sensible boy who likes to tackle life at a leisurely pace while at work. I will stand forever to be groomed, often relaxing so much that I look like I have fallen asleep. I have a very thick mane and love to give my head a cheeky little shake just when it is all lying nice and flat!",
},
{
name: "Puzzle",
img: "/puzzle-5de6e325-2880w.webp",
details: "15.1hh · Skewbald cob mare · 16 yo",
pos: "50% 35%",
quote: "Everyone says that I am a kind, loving girl. I enjoy being groomed and pampered, and I like to be around people who are calm, quiet and patient — that fills me with confidence. After work I like to wander slowly round the field searching for the tastiest patches of grass, or a nice dusty place to have a roll!",
},
{
name: "The Minis",
img: "/alineofponies-1920w.webp",
details: "Orris · Bru · Sam — Miniature Shetland ponies",
pos: "50% 60%",
quote: "We are better known as the minis! People often like to get to know us first if they are nervous about meeting the bigger horses. We might be small but make up for it with our cheeky personalities — and our ability to know when the electric fence has been switched off.",
},
{
name: "Lady Suede",
img: "/Suede-2880w.webp",
details: "13hh · Mare · 23 yo",
pos: "50% 50%",
memorial: true,
memorialNote: "RIP October 2024",
},
{
name: "Wispa",
img: "/Wispa-2880w.webp",
details: "14.2hh · Gelding · 15 yo",
pos: "50% 20%",
memorial: true,
memorialNote: "RIP 2025",
},
{ name: "Breagh", img: "/Breagh-bc8e70e8-2880w.webp", details: "13.2hh · Mare · 15 yo", desc: "She is very willing and independent", pos: "50% 50%" },
{ name: "Connolly", img: "/connolly-73f3e69f-2880w-2.webp", details: "14.2hh · Gelding · 16 yo", desc: "A true gentleman and everyone's friend", pos: "50% 50%" },
{ name: "Harley", img: "/Harley-6ece9eca-2880w.webp", details: "15.3hh · Gelding · 15 yo", desc: "He is the boss but very obliging", pos: "50% 50%" },
{ name: "Puzzle", img: "/puzzle-5de6e325-2880w.webp", details: "15.3hh · Mare · 14 yo", desc: "She is very sensitive and gentle", pos: "50% 50%" },
{ name: "Lady Suede", img: "/Suede-2880w.webp", details: "13hh · Mare · 23 yo", desc: "RIP October 2024", pos: "50% 50%", memorial: true },
{ name: "Wispa", img: "/Wispa-2880w.webp", details: "14.2hh · Gelding · 15 yo", desc: "RIP 2025", pos: "50% 20%", memorial: true },
];
const testimonials = [
@ -87,7 +42,8 @@ const testimonials = [
<link
rel="preload"
as="image"
href="/hero-about.jpg"
href="/about-960.webp"
imagesrcset="/about-320.webp 320w, /about-480.webp 480w, /about-640.webp 640w, /about-960.webp 960w"
imagesizes="100vw"
/>
</Fragment>
@ -137,7 +93,9 @@ const testimonials = [
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 42%;"
alt=""
src="/hero-about.jpg"
src="/about-960.webp"
srcset="/about-320.webp 320w, /about-480.webp 480w, /about-640.webp 640w, /about-960.webp 960w"
sizes="100vw"
loading="eager"
fetchpriority="high"
decoding="async"
@ -159,7 +117,7 @@ const testimonials = [
Enriching lives through horses in the Highlands since 1975.
</h1>
<p style="margin-top: 22px; font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: clamp(15px, 1.5vw, 18px); color: rgba(246,241,232,0.85); line-height: 1.65; max-width: 580px;">
We are a voluntary organisation based at Sandycroft, Reelig, Kirkhill — providing therapeutic and recreational riding for people with disabilities across the Highlands.
We are a voluntary organisation based at Sandycroft, Reelig, Kirkhill — providing free therapeutic and recreational riding for people with disabilities across the Highlands.
</p>
</div>
</div>
@ -194,10 +152,10 @@ const testimonials = [
<div>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: rgba(0,0,0,0.45); margin-bottom: 14px;">What we do</p>
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(22px, 2.5vw, 32px); color: #000; line-height: 1.15; margin-bottom: 20px;">
Riding and so much more.
Riding, carriage driving, and so much more.
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(0,0,0,0.7); line-height: 1.7; margin-bottom: 16px;">
We provide riding sessions for participants with a wide range of disabilities. Working with horses improves balance, coordination, confidence, and provides a uniquely meaningful interaction with animals.
We provide free riding and carriage driving sessions for participants with a wide range of disabilities. Working with horses improves balance, coordination, confidence, and provides a uniquely meaningful interaction with animals.
</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(0,0,0,0.7); line-height: 1.7;">
Sessions run Tuesday to Friday mornings from our base at Sandycroft. Each session is supported by trained volunteers and supervised by qualified coaches.
@ -251,7 +209,7 @@ const testimonials = [
You can support them directly by sponsoring a pony — covering the costs of hoof trimming, hay, vet visits, and more.
</p>
<a
href="/support-us#sponsor"
href="/support-us"
style="align-self: flex-start; display: inline-flex; align-items: center; gap: 10px; background: #7DA371; color: white; border-radius: 9999px; padding: 13px 28px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;"
>
Sponsor a pony
@ -262,81 +220,39 @@ const testimonials = [
</div>
</section>
<!-- ── HORSE PROFILES ── -->
<!-- ── PONY PROFILES ── -->
<section style="background: #d6d6d6; padding: clamp(48px, 8vw, 72px) clamp(24px, 6vw, 72px);">
<div style="max-width: 1200px; margin: 0 auto;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: rgba(0,0,0,0.45); margin-bottom: 14px;">
Meet the horses
Meet the ponies
</p>
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(24px, 2.8vw, 36px); color: #000; line-height: 1.15; margin-bottom: 12px;">
In their own words.
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(0,0,0,0.6); line-height: 1.65; max-width: 620px; margin-bottom: 40px;">
Highland Group RDA are very proud of the contribution made by our fantastic team of horses. We'll let them introduce themselves.
</p>
<!-- Active horses -->
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 22px;">
{ponies.filter(p => !p.memorial).map((pony) => (
<div style="background: rgba(255,255,255,0.6); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column;">
<div style="position: relative; height: 220px; overflow: hidden; flex-shrink: 0;">
<div style="width: 36px; height: 2px; background: #7DA371; margin-bottom: 40px;" />
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px;">
{ponies.map((pony) => (
<div style={`background: ${pony.memorial ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.55)'}; border-radius: 7px; overflow: hidden;`}>
<div style="position: relative; height: 180px; overflow: hidden;">
<img
src={pony.img}
alt={pony.name}
style={`position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: ${pony.pos};`}
style={`position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: ${pony.pos}; ${pony.memorial ? 'filter: grayscale(40%);' : ''}`}
loading="lazy"
decoding="async"
/>
</div>
<div style="padding: 22px 24px 26px; flex: 1; display: flex; flex-direction: column;">
<h3 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 20px; color: #000; margin-bottom: 5px;">
<div style="padding: 18px 20px;">
<h3 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 18px; color: #000; margin-bottom: 6px;">
{pony.name}
</h3>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: #7DA371; margin-bottom: 16px;">
<p style={`font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.05em; color: ${pony.memorial ? 'rgba(0,0,0,0.4)' : '#7DA371'}; margin-bottom: 4px;`}>
{pony.details}
</p>
<div style="border-left: 2px solid #7DA371; padding-left: 16px; flex: 1;">
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-style: italic; font-size: 14px; color: rgba(0,0,0,0.7); line-height: 1.7;">
"{pony.quote}"
</p>
</div>
<p style={`font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: ${pony.memorial ? 'rgba(0,0,0,0.45)' : 'rgba(0,0,0,0.65)'}; line-height: 1.5;`}>
{pony.desc}
</p>
</div>
</div>
))}
</div>
<!-- Memorial section -->
<div style="margin-top: 56px; padding-top: 40px; border-top: 1px solid rgba(0,0,0,0.12);">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: rgba(0,0,0,0.35); margin-bottom: 20px;">
Remembered fondly
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; max-width: 560px;">
{ponies.filter(p => p.memorial).map((pony) => (
<div style="background: rgba(0,0,0,0.05); border-radius: 8px; overflow: hidden;">
<div style="position: relative; height: 160px; overflow: hidden;">
<img
src={pony.img}
alt={pony.name}
style={`position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: ${pony.pos}; filter: grayscale(50%);`}
loading="lazy"
decoding="async"
/>
</div>
<div style="padding: 16px 18px;">
<h3 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 17px; color: rgba(0,0,0,0.7); margin-bottom: 4px;">
{pony.name}
</h3>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.05em; color: rgba(0,0,0,0.35); margin-bottom: 4px;">
{pony.details}
</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 13px; color: rgba(0,0,0,0.4); font-style: italic;">
{pony.memorialNote}
</p>
</div>
</div>
))}
</div>
</div>
</div>
</section>
@ -480,11 +396,13 @@ const testimonials = [
>
Support us
</a>
<a
href="/support-us#sponsor"
style="display: inline-flex; align-items: center; gap: 10px; background: #7DA371; color: white; border-radius: 9999px; padding: 14px 28px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;"
>
Sponsor a pony
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer">
<img
src="/Button.webp"
alt="Donate via JustGiving"
style="height: 46px; object-fit: contain; display: block;"
loading="lazy"
/>
</a>
</div>
</div>

View file

@ -124,7 +124,7 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 45%;"
alt=""
src="/hero-contact.webp"
src="/contact-960.webp"
loading="eager"
fetchpriority="high"
decoding="async"

View file

@ -3,14 +3,16 @@ import Base from "../../layouts/Base.astro";
import SiteHeader from "../../components/SiteHeader.astro";
import SiteFooter from "../../components/SiteFooter.astro";
import { navigationItems } from "../../lib/navigation";
import { getArticle, formatNewsDate } from "../../lib/news";
import { newsArticles, formatNewsDate } from "../../lib/news";
const { slug } = Astro.params;
const article = await getArticle(slug!);
if (!article) {
return Astro.redirect("/events", 302);
export function getStaticPaths() {
return newsArticles.map((article) => ({
params: { slug: article.slug },
props: { article },
}));
}
const { article } = Astro.props;
---
<Base

View file

@ -3,11 +3,10 @@ import BaseLayout from "../../layouts/BaseLayout.astro";
import SiteHeader from "../../components/SiteHeader.astro";
import SiteFooter from "../../components/SiteFooter.astro";
import { navigationItems } from "../../lib/navigation";
import { getArticles } from "../../lib/news";
import { newsArticles } from "../../lib/news";
const allArticles = await getArticles();
const featured = allArticles[0];
const rest = allArticles.slice(1);
const featured = newsArticles[0];
const rest = newsArticles.slice(1);
const pageSeo = {
title: "News",
@ -108,7 +107,7 @@ const typeColors: Record<string, string> = {
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 30%;"
alt=""
src="/hero-events.webp"
src="/news.webp"
loading="eager"
fetchpriority="high"
decoding="async"

View file

@ -3,14 +3,21 @@ import BaseLayout from "../../../layouts/BaseLayout.astro";
import SiteHeader from "../../../components/SiteHeader.astro";
import SiteFooter from "../../../components/SiteFooter.astro";
import { navigationItems } from "../../../lib/navigation";
import { getArticles } from "../../../lib/news";
import { newsArticles } from "../../../lib/news";
export function getStaticPaths() {
const PER_PAGE = 6;
const totalPages = Math.ceil(newsArticles.length / PER_PAGE);
return Array.from({ length: totalPages }, (_, i) => i + 1)
.filter((page) => page > 1)
.map((page) => ({ params: { page: String(page) } }));
}
const allArticles = await getArticles();
const currentPage = Number(Astro.params.page ?? "1");
const PER_PAGE = 6;
const totalPages = Math.ceil(allArticles.length / PER_PAGE);
const totalPages = Math.ceil(newsArticles.length / PER_PAGE);
const start = (currentPage - 1) * PER_PAGE;
const pagedArticles = allArticles.slice(start, start + PER_PAGE);
const pagedArticles = newsArticles.slice(start, start + PER_PAGE);
const pageHref = (page: number) => (page === 1 ? "/events" : `/events/page/${page}`);
const pageSeo = {
@ -67,7 +74,7 @@ function formatDate(date: string) {
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 30%;"
alt=""
src="/hero-events.webp"
src="/news.webp"
loading="eager"
fetchpriority="high"
decoding="async"

View file

@ -5,7 +5,9 @@ import SiteFooter from "../components/SiteFooter.astro";
import { navigationItems } from "../lib/navigation";
const facebookPageUrl =
"https://www.facebook.com/people/Highland-Group-RDA/100064729054822/";
"https://www.facebook.com/profile.php?id=100064729054822";
const facebookEmbedUrl =
"https://www.facebook.com/plugins/page.php?href=https%3A%2F%2Fwww.facebook.com%2Fprofile.php%3Fid%3D100064729054822&tabs=timeline&width=500&height=900&small_header=false&adapt_container_width=true&hide_cover=false&show_facepile=true";
const pageSeo = {
title: "Facebook",
@ -15,436 +17,88 @@ const pageSeo = {
"Highland Group RDA Facebook, RDA social media, Highland RDA updates, RDA events Facebook, therapeutic riding community",
canonicalPath: "/facebook",
};
const posts = [
{
id: 1,
date: "3 days ago",
body: "What a glorious morning at Sandycroft! All four ponies were out, the sun was shining, and our riders had a brilliant session. Huge thanks to our amazing volunteer team who make every Tuesday possible. 🐴☀️",
img: "/Samantha-and-Connolly-1280.webp",
imgPos: "50% 38%",
likes: 47,
comments: 6,
shares: 3,
},
{
id: 2,
date: "1 week ago",
body: "We are thrilled to announce that three of our team have just completed their RDA Coach training! Welcome to the qualified ranks — and thank you for the hours of study, practice, and dedication. The Highlands are lucky to have you. 🎉",
img: "/Vols5-2880w-1024x768.avif",
imgPos: "50% 28%",
likes: 132,
comments: 24,
shares: 11,
},
{
id: 3,
date: "2 weeks ago",
body: "MEET THE PONIES — Today we'd like to introduce you to Breagh, one of our most willing and independent ponies. Breagh has been a firm favourite with our riders for years. She loves a good back-scratch. 💚",
img: "/Breagh-bc8e70e8-2880w.webp",
imgPos: "50% 45%",
likes: 218,
comments: 41,
shares: 15,
},
{
id: 4,
date: "3 weeks ago",
body: "Open Day reminder — Sunday 10 May, 11am to 3pm at Sandycroft. Come and meet the ponies, chat to our coaches, and find out how you can get involved. Tea, coffee, and home baking on offer! All welcome.",
img: "/about-960.webp",
imgPos: "50% 35%",
likes: 89,
comments: 18,
shares: 27,
},
{
id: 5,
date: "1 month ago",
body: "Incredible news — our sponsored ride raised over £1,200! 🙏 That's enough to cover hoof trimming and hay for the whole summer. To everyone who donated, sponsored, walked, or cheered us on: thank you. You make this work possible.",
img: null,
imgPos: "",
likes: 165,
comments: 32,
shares: 9,
},
];
const photosGrid = [
{ src: "/Samantha-and-Connolly-1280.webp", pos: "50% 38%" },
{ src: "/alineofponies-1920w.webp", pos: "50% 50%" },
{ src: "/Vols5-2880w-1024x768.avif", pos: "50% 28%" },
{ src: "/about-960.webp", pos: "50% 42%" },
{ src: "/contact-960.webp", pos: "50% 40%" },
{ src: "/Breagh-bc8e70e8-2880w.webp", pos: "50% 50%" },
{ src: "/Harley-6ece9eca-2880w.webp", pos: "50% 50%" },
{ src: "/puzzle-5de6e325-2880w.webp", pos: "50% 50%" },
{ src: "/Wispa-2880w.webp", pos: "50% 20%" },
];
---
<BaseLayout {...pageSeo}>
<style>
#site-nav {
transition: background 0.4s ease, backdrop-filter 0.4s ease;
}
#site-nav.scrolled {
background: rgba(0,0,0,0.82);
backdrop-filter: blur(8px);
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(18px); }
to { opacity: 1; transform: translateY(0); }
}
.anim-fade-up { animation: fadeUp 0.9s ease both; }
.fb-card {
background: #fff;
border-radius: 8px;
border: 1px solid rgba(0,0,0,0.08);
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
overflow: hidden;
}
.fb-action {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex: 1;
padding: 10px 0;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
color: #65676b;
font-family: 'Public Sans', sans-serif;
font-weight: 600;
font-size: 14px;
border: none;
background: transparent;
text-decoration: none;
}
.fb-action:hover { background: #f2f2f2; }
.fb-action[data-liked="true"] { color: #1877F2; font-weight: 700; }
.feed-grid {
display: grid;
grid-template-columns: 1fr 320px;
gap: 32px;
align-items: flex-start;
}
.sidebar { position: sticky; top: 88px; }
@media (max-width: 900px) {
.feed-grid { grid-template-columns: 1fr; }
.sidebar { position: static; }
}
@media (max-width: 767px) {
.page-header-info { flex-wrap: wrap; }
.page-header-buttons { padding-top: 12px !important; }
}
</style>
<!-- Fixed nav -->
<div id="site-nav" class="fixed top-0 left-0 right-0 z-50 px-4 sm:px-10">
<SiteHeader navigationItems={navigationItems} theme="dark" />
</div>
<div class="flex flex-col min-h-screen bg-[#e2e2e2]">
<div class="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main class="w-full">
<!-- ── HERO ── -->
<section class="relative w-full overflow-hidden" style="min-height: 48vh;">
<img
class="absolute inset-0 w-full h-full object-cover"
style="object-position: 50% 30%;"
src="/Vols5-2880w-1024x768.avif"
alt=""
loading="eager"
fetchpriority="high"
decoding="async"
/>
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0">
<div
class="absolute inset-0"
style="background: linear-gradient(160deg, rgba(0,0,0,0.78) 0%, rgba(0,0,0,0.4) 60%, rgba(0,0,0,0.5) 100%);"
/>
<div
class="relative flex flex-col justify-end"
style="min-height: 48vh; padding: 100px clamp(24px, 5vw, 72px) 60px;"
class="relative w-full sm:rounded-[7px] overflow-hidden bg-cover bg-[50%_35%]"
style="background-image: url(/320711306_865749364475187_6152104707200224701_n-1d6b6ac8-1920w-1.webp);"
>
<div class="anim-fade-up" style="max-width: 680px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.25em; text-transform: uppercase; color: rgba(255,255,255,0.7); margin-bottom: 14px;">
Social
</p>
<div style="width: 48px; height: 2px; background: #7DA371; margin-bottom: 20px;" />
<h1 style="font-family: 'Merriweather', serif; font-weight: 900; font-size: clamp(32px, 4.5vw, 52px); color: #F6F1E8; line-height: 1.1; margin-bottom: 18px;">
Follow us on Facebook.
</h1>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: clamp(15px, 1.5vw, 18px); color: rgba(246,241,232,0.82); line-height: 1.65;">
The latest from Sandycroft — meet the ponies, see our riders in action, and follow our sessions, events, and fundraising.
</p>
</div>
</div>
</section>
<!-- ── PAGE HEADER CARD ── -->
<section style="background: #e2e2e2; padding: clamp(32px, 5vw, 56px) clamp(24px, 5vw, 72px) 0;">
<div style="max-width: 1100px; margin: 0 auto;">
<div class="fb-card">
<!-- Cover photo -->
<div style="position: relative; height: 200px; overflow: hidden;">
<img
src="/Samantha-and-Connolly-1280.webp"
alt=""
style="width: 100%; height: 100%; object-fit: cover; object-position: 50% 35%;"
loading="lazy"
/>
</div>
<!-- Info row -->
<div class="page-header-info" style="padding: 0 32px 28px; display: flex; align-items: flex-end; gap: 24px; margin-top: -50px; flex-wrap: wrap;">
<div style="width: 120px; height: 120px; border-radius: 9999px; background: #fff; padding: 5px; flex-shrink: 0; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
<div style="width: 100%; height: 100%; border-radius: 9999px; background: #1e2e1a; display: flex; align-items: center; justify-content: center;">
<img src="/figmaAssets/rdalogo-1.svg" alt="" style="height: 64px; filter: brightness(0) invert(1);" />
</div>
</div>
<div style="flex: 1; padding-top: 50px; min-width: 200px;">
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.2vw, 28px); color: #000; line-height: 1.2;">
Highland Group RDA
</h2>
<p style="margin-top: 6px; font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: #65676b;">
Nonprofit organisation · Riding for the Disabled
</p>
</div>
<div class="page-header-buttons" style="display: flex; gap: 10px; padding-top: 50px; flex-wrap: wrap;">
<a
href={facebookPageUrl}
target="_blank"
rel="noreferrer"
style="display: inline-flex; align-items: center; gap: 8px; background: #1877F2; color: #fff; border-radius: 6px; padding: 10px 20px; font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 14px; text-decoration: none;"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
Follow
</a>
<a
href={facebookPageUrl}
target="_blank"
rel="noreferrer"
style="display: inline-flex; align-items: center; gap: 8px; background: #e4e6eb; color: #000; border-radius: 6px; padding: 10px 20px; font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 14px; text-decoration: none;"
>
Visit page
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 3h7v7M13 3L4 12"/></svg>
</a>
</div>
</div>
</div>
</div>
</section>
<!-- ── FEED + SIDEBAR ── -->
<section style="background: #e2e2e2; padding: clamp(24px, 4vw, 40px) clamp(24px, 5vw, 72px) clamp(56px, 8vw, 88px);">
<div style="max-width: 1100px; margin: 0 auto;" class="feed-grid">
<!-- Feed column -->
<div style="display: flex; flex-direction: column; gap: 16px;">
<div style="display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 8px; gap: 12px; flex-wrap: wrap;">
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.2vw, 28px); color: #000;">Recent posts</h2>
<a
href={facebookPageUrl}
target="_blank"
rel="noreferrer"
style="font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; color: #7DA371; letter-spacing: 0.05em; text-transform: uppercase; text-decoration: underline;"
<div class="absolute inset-0 bg-black/45"></div>
<SiteHeader
navigationItems={navigationItems}
theme="dark"
className="px-6 sm:px-4"
facebookHref={facebookPageUrl}
/>
<div
class="relative px-6 sm:px-8 pb-32 pt-6 sm:pb-24 sm:pt-8"
>
<div
class="max-w-none sm:max-w-[760px] rounded-[7px] bg-black/45 px-5 py-5 sm:px-6 sm:py-6 backdrop-blur-sm"
>
<p
class="[font-family:'Public_Sans',Helvetica] font-semibold text-xs sm:text-sm uppercase tracking-[0.2em] text-white/90"
>
See all on Facebook
</a>
</div>
{posts.map((post) => (
<div class="fb-card" data-post-id={post.id}>
<div style="padding: 14px 16px; display: flex; align-items: center; gap: 12px;">
<div style="width: 40px; height: 40px; border-radius: 9999px; background: #1e2e1a; display: flex; align-items: center; justify-content: center; flex-shrink: 0;">
<img src="/figmaAssets/rdalogo-1.svg" alt="" style="height: 24px; filter: brightness(0) invert(1);" />
</div>
<div>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 14px; color: #000;">Highland Group RDA</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 12px; color: #65676b;">
{post.date} · 🌍
</p>
</div>
</div>
<div style="padding: 0 16px 14px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 15px; color: #000; line-height: 1.55; white-space: pre-line;">
{post.body}
</p>
</div>
{post.img && (
<div style="position: relative; width: 100%; max-height: 520px; overflow: hidden; background: #f0f2f5;">
<img
src={post.img}
alt=""
style={`width: 100%; max-height: 520px; object-fit: cover; object-position: ${post.imgPos}; display: block;`}
loading="lazy"
decoding="async"
/>
</div>
)}
<div style="padding: 10px 16px 6px; display: flex; align-items: center; justify-content: space-between; font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 13px; color: #65676b;">
<div style="display: flex; align-items: center; gap: 6px;">
<div style="display: flex;">
<span style="width: 18px; height: 18px; border-radius: 9999px; background: #1877F2; display: inline-flex; align-items: center; justify-content: center; border: 2px solid #fff; position: relative; z-index: 2;">
<svg width="9" height="9" viewBox="0 0 24 24" fill="white" aria-hidden="true"><path d="M2 21h4V9H2zm20-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L13.17 1 7.58 6.59C7.22 6.95 7 7.45 7 8v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73z"/></svg>
</span>
<span style="width: 18px; height: 18px; border-radius: 9999px; background: #f33e58; display: inline-flex; align-items: center; justify-content: center; border: 2px solid #fff; margin-left: -5px; position: relative; z-index: 1;">
<svg width="9" height="9" viewBox="0 0 24 24" fill="white" aria-hidden="true"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</span>
</div>
<span data-like-count={post.likes}>{post.likes}</span>
</div>
<div style="display: flex; gap: 14px;">
<span>{post.comments} comments</span>
<span>{post.shares} shares</span>
</div>
</div>
<div style="margin: 0 12px; border-top: 1px solid rgba(0,0,0,0.1); padding: 4px 0; display: flex;">
<button class="fb-action" data-like-btn aria-label="Like" aria-pressed="false">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M7 11v9H3v-9zm14-1l-2 9c-.2.8-.9 1-1.5 1H10c-.6 0-1-.4-1-1V10c0-.2.1-.5.3-.7L15 4l1 1c.3.3.4.7.3 1.1L15 10h5c.6 0 1.1.4 1 1z"/>
</svg>
Like
</button>
<a href={facebookPageUrl} target="_blank" rel="noreferrer" class="fb-action">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8z"/>
</svg>
Comment
</a>
<a href={facebookPageUrl} target="_blank" rel="noreferrer" class="fb-action">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13"/>
</svg>
Share
</a>
</div>
</div>
))}
<div style="text-align: center; margin-top: 8px;">
<a
href={facebookPageUrl}
target="_blank"
rel="noreferrer"
style="display: inline-flex; align-items: center; gap: 10px; background: #1877F2; color: #fff; border-radius: 9999px; padding: 14px 32px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none;"
Social
</p>
<div class="mt-3 h-[2px] w-10 bg-white/70"></div>
<h1
class="mt-4 [font-family:'Merriweather',Helvetica] font-bold text-white text-[34px] sm:text-[44px] leading-[1.15]"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
See more on Facebook
</a>
Facebook updates
</h1>
<p
class="mt-4 [font-family:'Public_Sans',Helvetica] font-semibold text-white text-base sm:text-lg leading-[1.6] max-w-[720px]"
>
Follow our latest news, events, and photos on
Facebook.
</p>
</div>
</div>
<!-- Sidebar -->
<aside class="sidebar" style="display: flex; flex-direction: column; gap: 16px;">
<!-- About card -->
<div class="fb-card" style="padding: 20px 22px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 12px; color: #65676b; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 14px;">About</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 14px; color: #000; line-height: 1.6; margin-bottom: 18px;">
Highland Group RDA provides therapeutic riding for people with disabilities in the Highlands. Based at Sandycroft, Reelig, Kirkhill.
</p>
<div style="display: flex; flex-direction: column; gap: 10px; font-family: 'Public Sans', sans-serif; font-size: 13px; color: #65676b;">
<div style="display: flex; gap: 10px; align-items: flex-start;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink: 0; margin-top: 2px;" aria-hidden="true">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/>
</svg>
<span>Sandycroft, Reelig, Kirkhill IV5 7PP</span>
</div>
<div style="display: flex; gap: 10px; align-items: center;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink: 0;" aria-hidden="true">
<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 6l-10 7L2 6"/>
</svg>
<a href="mailto:info@highlandgrouprda.org.uk" style="color: #65676b;">info@highlandgrouprda.org.uk</a>
</div>
<div style="display: flex; gap: 10px; align-items: center;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink: 0;" aria-hidden="true">
<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
</svg>
<span>Tue Fri mornings</span>
</div>
</div>
</div>
<!-- Photos grid -->
<div class="fb-card" style="padding: 20px 22px;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 12px; color: #65676b; letter-spacing: 0.08em; text-transform: uppercase;">Photos</p>
<a href={facebookPageUrl} target="_blank" rel="noreferrer" style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px; color: #1877F2; text-decoration: none;">See all</a>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;">
{photosGrid.map((photo) => (
<div style="aspect-ratio: 1/1; overflow: hidden; border-radius: 4px;">
<img
src={photo.src}
alt=""
style={`width: 100%; height: 100%; object-fit: cover; object-position: ${photo.pos};`}
loading="lazy"
decoding="async"
/>
</div>
))}
</div>
</div>
<!-- Donate CTA -->
<div class="fb-card" style="padding: 22px; background: #1e2e1a;">
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 18px; color: #F6F1E8; line-height: 1.3; margin-bottom: 8px;">
Like what you see?
</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 14px; color: rgba(246,241,232,0.7); line-height: 1.6; margin-bottom: 18px;">
Donate today to help us keep going.
</p>
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer">
<img src="/Button.webp" alt="Donate via JustGiving" style="height: 44px; object-fit: contain; display: block;" loading="lazy" />
</a>
</div>
</aside>
<a
class="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 w-[190px] sm:w-[240px] h-[48px] sm:h-[50px]"
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
>
<img
class="w-full h-full object-contain"
alt="Donate via JustGiving"
src="/Button.webp"
/>
</a>
</div>
</section>
<!-- ── EMBED NOTE ── -->
<section style="background: #d6d6d6; padding: 24px clamp(24px, 5vw, 72px); border-top: 1px solid rgba(0,0,0,0.1);">
<div style="max-width: 1100px; margin: 0 auto; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;">
<div style="flex-shrink: 0; width: 32px; height: 32px; border-radius: 9999px; background: rgba(125,163,113,0.15); display: flex; align-items: center; justify-content: center;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7DA371" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>
</svg>
<section
class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0 pb-0"
>
<div class="bg-[#d6d6d6] px-6 sm:px-8 py-6 sm:py-8">
<div class="mt-6 w-full max-w-[500px] mx-auto">
<iframe
title="Highland Group RDA Facebook feed"
src={facebookEmbedUrl}
width="500"
height="900"
class="w-full h-[700px] sm:h-[900px] border-0 rounded-[7px] bg-white"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"
loading="lazy"></iframe>
</div>
<p style="flex: 1; font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 13px; color: rgba(0,0,0,0.6); line-height: 1.6; min-width: 200px;">
The posts above are a curated preview of our Facebook activity. For the full feed, visit us directly on <a href={facebookPageUrl} target="_blank" rel="noreferrer" style="color: #1877F2; text-decoration: underline;">Facebook</a>.
</p>
<a
class="mt-6 inline-flex [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg underline"
href={facebookPageUrl}
target="_blank"
rel="noreferrer"
>
Visit our Facebook page
</a>
</div>
</section>
<SiteFooter fullBleedOnMobile />
</main>
<SiteFooter fullBleedOnMobile />
</div>
<script>
const nav = document.getElementById("site-nav");
if (nav) {
window.addEventListener("scroll", () => {
nav.classList.toggle("scrolled", window.scrollY > 40);
}, { passive: true });
}
document.querySelectorAll("[data-like-btn]").forEach((btn) => {
btn.addEventListener("click", () => {
const card = btn.closest("[data-post-id]");
if (!card) return;
const liked = btn.getAttribute("data-liked") === "true";
const countEl = card.querySelector("[data-like-count]");
if (countEl) {
const base = parseInt(countEl.getAttribute("data-like-count") ?? "0", 10);
countEl.textContent = String(base + (liked ? 0 : 1));
}
btn.setAttribute("data-liked", liked ? "false" : "true");
btn.setAttribute("aria-pressed", liked ? "false" : "true");
});
});
</script>
</BaseLayout>

View file

@ -3,7 +3,6 @@ import BaseLayout from "../layouts/BaseLayout.astro";
import SiteHeader from "../components/SiteHeader.astro";
import SiteFooter from "../components/SiteFooter.astro";
import { navigationItems } from "../lib/navigation";
import { isSanityConfigured, getSanityHomePage } from "../lib/sanity";
const pageSeo = {
title: "Highland Group RDA",
@ -14,48 +13,32 @@ const pageSeo = {
canonicalPath: "/",
};
// ── Fallback content (used when Sanity is not configured or document is empty) ─
const DEFAULTS = {
heroHeadline: "Enriching lives through horses in the Highlands.",
heroMission: "Our mission is to bring about meaningful and positive changes in the health and well-being of people with physical or mental disabilities through activities with our horses and ponies.",
videoHeading: "What is Riding for the Disabled?",
videoBody: "Across the Highlands, we provide riding sessions for people with a wide range of physical and mental disabilities — connecting them with our ponies in a way that's therapeutic, joyful, and life-changing.",
videoYoutubeId: "IX2DGd6m8BU",
sponsorHeading: "We are grateful for the generous support of our sponsors.",
ctaCards: [
{ label: "Fundraising", title: "Sponsor a pony", description: "Your support keeps our ponies healthy and our sessions running — from hoof trimming to hay bales.", image: "/alineofponies-1920w.webp", href: "/support-us#sponsor" },
{ label: "Get involved", title: "Apply to volunteer", description: "No horse experience needed. Sidestep walkers, groomers, fundraisers — all roles make a real difference.", image: "/Vols5-2880w-1024x768.avif", href: "/volunteer-application" },
{ label: "Riding sessions",title: "Apply to participate", description: "We welcome riders and carriage drivers with a wide range of physical and mental disabilities.", image: "/Samantha-and-Connolly-1280.webp", href: "/participant-application" },
],
sponsors: [
{ name: "Ffordes", logo: "/sponsors/ffordes_logo.png", url: null },
],
};
const cms = isSanityConfigured() ? (await getSanityHomePage()) : null;
const heroHeadline = cms?.heroHeadline || DEFAULTS.heroHeadline;
const heroMission = cms?.heroMission || DEFAULTS.heroMission;
const videoHeading = cms?.videoHeading || DEFAULTS.videoHeading;
const videoBody = cms?.videoBody || DEFAULTS.videoBody;
const videoYoutubeId = cms?.videoYoutubeId || DEFAULTS.videoYoutubeId;
const sponsorHeading = cms?.sponsorHeading || DEFAULTS.sponsorHeading;
// Always produce 3 cards; merge Sanity data per-slot over defaults so a
// partially-filled CMS (e.g. only 2 cards) never hides the third card or
// leaves a card with an empty href.
const sanityCards = cms?.ctaCards ?? [];
const ctaCards = DEFAULTS.ctaCards.map((def, i) => {
const s = sanityCards[i];
if (!s) return def;
return {
label: s.label || def.label,
title: s.title || def.title,
description: s.description || def.description,
image: s.image || def.image,
href: s.href || def.href,
};
});
const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsors;
const ctaCards = [
{
label: "Fundraising",
title: "Sponsor a pony",
sub: "Your support keeps our ponies healthy and our sessions running — from hoof trimming to hay bales.",
img: "/alineofponies-1920w.webp",
objectPos: "50% 50%",
href: "/support-us",
},
{
label: "Get involved",
title: "Apply to volunteer",
sub: "No horse experience needed. Sidestep walkers, groomers, fundraisers — all roles make a real difference.",
img: "/Vols5-2880w-1024x768.avif",
objectPos: "50% 30%",
href: "/volunteer-application",
},
{
label: "Riding sessions",
title: "Apply to participate",
sub: "We welcome riders and carriage drivers with a wide range of physical and mental disabilities.",
img: "/Samantha-and-Connolly-1280.webp",
objectPos: "50% 38%",
href: "/participant-application",
},
];
---
<BaseLayout {...pageSeo}>
@ -63,7 +46,8 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<link
rel="preload"
as="image"
href="/hero-home.webp"
href="/Samantha-and-Connolly-1280.webp"
imagesrcset="/Samantha-and-Connolly-480.webp 480w, /Samantha-and-Connolly-640.webp 640w, /Samantha-and-Connolly-960.webp 960w, /Samantha-and-Connolly-1280.webp 1280w, /Samantha-and-Connolly-1920.webp 1920w"
imagesizes="100vw"
/>
</Fragment>
@ -117,11 +101,8 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
.cta-card:hover .cta-more { opacity: 1; }
@media (max-width: 767px) {
.cta-editorial { flex-direction: column; height: auto; overflow: visible; }
.cta-card { flex: none !important; height: 280px; transition: none; }
.cta-card:hover .cta-card-img { transform: none; }
.cta-sub { opacity: 1 !important; }
.cta-more { opacity: 1 !important; }
.cta-editorial { flex-direction: column; height: auto; }
.cta-card { flex: none; height: 200px; }
}
.video-embed {
@ -139,104 +120,24 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
}
.sponsor-logo {
width: 140px;
height: 64px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
}
.sponsor-logo img {
max-height: 64px;
max-width: 200px;
object-fit: contain;
filter: brightness(0) invert(1);
opacity: 0.85;
transition: opacity 0.2s ease;
}
.sponsor-logo img:hover {
opacity: 1;
}
.sponsor-cta {
border: 2px dashed rgba(255, 255, 255, 0.25);
border-radius: 8px;
padding: 20px 28px;
text-align: center;
max-width: 380px;
margin: 0 auto;
font-family: 'Public Sans', sans-serif;
font-weight: 700;
font-size: 13px;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 0.08em;
text-transform: uppercase;
}
@media (max-width: 767px) {
.scroll-indicator { display: none !important; }
}
/* On mobile, margin-top: auto can collapse to 0 when the headline
fills most of the hero — enforce a minimum gap above the blockquote */
.hero-mission { margin-top: auto; }
@media (max-width: 639px) {
.hero-mission { margin-top: clamp(40px, 6vh, 80px); }
}
/* Hero content padding: clear the fixed nav (mobile logo 80px + 24px py = ~104px; desktop logo 93px + 32px py = ~125px) */
#hero-content {
padding: 144px 48px 48px;
}
@media (max-width: 639px) {
#hero-content { padding: 120px 24px 32px; }
}
/* ── Donate modal ── */
.donate-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 1000;
align-items: flex-start;
justify-content: center;
padding: 4vh 16px 48px;
overflow-y: auto;
background: rgba(0,0,0,0);
-webkit-backdrop-filter: blur(0);
backdrop-filter: blur(0);
transition: background 0.28s ease, backdrop-filter 0.28s ease, -webkit-backdrop-filter 0.28s ease;
}
.donate-overlay.visible {
background: rgba(0,0,0,0.55);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
}
.donate-panel {
position: relative;
width: 100%;
max-width: 680px;
background: #F6F1E8;
border-radius: 7px;
padding: clamp(28px, 4vw, 44px);
margin: auto;
box-shadow: 0 24px 80px rgba(0,0,0,0.4);
transform: translateY(16px) scale(0.985);
opacity: 0;
transition: transform 0.32s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.28s ease;
}
.donate-overlay.visible .donate-panel {
transform: translateY(0) scale(1);
opacity: 1;
}
.donate-fi {
font-family: 'Public Sans', sans-serif;
font-weight: 600;
font-size: 15px;
color: #000;
background: #fff;
border: 1.5px solid rgba(0,0,0,0.15);
border-radius: 6px;
padding: 12px 14px;
outline: none;
width: 100%;
box-sizing: border-box;
transition: border-color 0.18s, box-shadow 0.18s;
}
.donate-fi:focus {
border-color: #7DA371;
box-shadow: 0 0 0 3px rgba(125,163,113,0.2);
.scroll-indicator { display: none; }
.hero-donate { display: none; }
}
</style>
@ -249,12 +150,14 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<main class="w-full">
<!-- ── HERO ── -->
<section class="relative w-full overflow-hidden" style="min-height: 70vh;">
<section class="relative w-full overflow-hidden" style="min-height: 63vh;">
<img
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 38%;"
alt=""
src="/hero-home.webp"
src="/Samantha-and-Connolly-1280.webp"
srcset="/Samantha-and-Connolly-480.webp 480w, /Samantha-and-Connolly-640.webp 640w, /Samantha-and-Connolly-960.webp 960w, /Samantha-and-Connolly-1280.webp 1280w, /Samantha-and-Connolly-1920.webp 1920w"
sizes="100vw"
loading="eager"
fetchpriority="high"
decoding="async"
@ -266,44 +169,48 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<!-- Hero content -->
<div
id="hero-content"
class="relative flex flex-col"
style="min-height: 70vh;"
style="min-height: 63vh; padding: 120px 28px 72px;"
>
<!-- Headline block -->
<div class="anim-fade-up" style="max-width: 640px; margin-top: 8px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 13px; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(255,255,255,0.75);">
<div class="anim-fade-up" style="max-width: 640px; margin-top: 16px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.25em; text-transform: uppercase; color: rgba(255,255,255,0.75);">
Highland Group RDA · Since 1975
</p>
<div style="margin-top: 16px; width: 48px; height: 2px; background: #7DA371;" />
<h1 style="margin-top: 16px; font-family: 'Merriweather', serif; font-weight: 900; font-size: clamp(31px, 5.5vw, 61px); color: #F6F1E8; line-height: 1.1;">
{heroHeadline}
<div style="margin-top: 14px; width: 48px; height: 2px; background: #7DA371;" />
<h1 style="margin-top: 18px; font-family: 'Merriweather', serif; font-weight: 900; font-size: clamp(34px, 5vw, 62px); color: #F6F1E8; line-height: 1.12;">
Enriching lives through horses in the Highlands.
</h1>
<div style="margin-top: 32px;">
<button
onclick="openDonateModal()"
style="display: inline-flex; align-items: center; gap: 10px; background: #f5d445; color: #000; border: none; border-radius: 9999px; padding: 16px 28px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer; white-space: nowrap; box-shadow: 0 4px 18px rgba(0,0,0,0.32); transition: background 0.18s ease;"
onmouseover="this.style.background='#e8c933'" onmouseout="this.style.background='#f5d445'"
aria-label="Open donation form"
>
Donate
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
</div>
<!-- Mission blockquote -->
<div
class="anim-fade-up2 hero-mission"
style="max-width: 500px; background: rgba(0,0,0,0.48); backdrop-filter: blur(6px); border-radius: 7px; padding: 22px 26px;"
class="anim-fade-up2"
style="margin-top: auto; max-width: 500px; background: rgba(0,0,0,0.48); backdrop-filter: blur(6px); border-radius: 7px; padding: 22px 26px;"
>
<div style="width: 32px; height: 3px; background: #7DA371; margin-bottom: 14px;" />
<blockquote style="font-family: 'Merriweather', serif; font-weight: 700; font-style: italic; font-size: 16px; color: #F6F1E8; line-height: 1.6; margin: 0;">
{heroMission}
<blockquote style="font-family: 'Merriweather', serif; font-weight: 700; font-style: italic; font-size: clamp(15px, 1.5vw, 18px); color: #F6F1E8; line-height: 1.55; margin: 0;">
Our mission is to bring about meaningful and positive changes in the health and well-being of people with physical or mental disabilities through activities with our horses and ponies.
</blockquote>
</div>
</div>
<!-- Donate button (absolute, hidden on mobile) -->
<a
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
class="absolute hero-donate"
style="bottom: 40px; right: 28px;"
>
<img
src="/Button.webp"
alt="Donate via JustGiving"
style="height: 46px; object-fit: contain; display: block;"
loading="eager"
/>
</a>
<!-- Scroll indicator (absolute, hidden on mobile) -->
<div
class="scroll-indicator absolute"
@ -319,18 +226,18 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<!-- ── VIDEO ── -->
<section style="background: #1e2e1a; padding: clamp(48px, 8vw, 80px) clamp(24px, 5vw, 56px);">
<div style="max-width: 960px; margin: 0 auto;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 13px; letter-spacing: 0.2em; text-transform: uppercase; color: #7DA371; margin-bottom: 16px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.25em; text-transform: uppercase; color: #7DA371; margin-bottom: 12px;">
Our Story
</p>
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(24px, 3vw, 38px); color: #F6F1E8; margin-bottom: 20px; line-height: 1.2; max-width: 600px;">
{videoHeading}
What is Riding for the Disabled?
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(246,241,232,0.75); line-height: 1.6; max-width: 620px; margin-bottom: 40px;">
{videoBody}
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(246,241,232,0.75); line-height: 1.65; max-width: 680px; margin-bottom: 40px;">
Across the Highlands, we provide free riding and carriage driving sessions to people with a wide range of physical and mental disabilities — connecting them with our ponies in a way that's therapeutic, joyful, and life-changing.
</p>
<div class="video-embed" style="max-width: 860px; margin: 0 auto; box-shadow: 0 20px 60px rgba(0,0,0,0.5);">
<iframe
src={`https://www.youtube.com/embed/${videoYoutubeId}?rel=0&modestbranding=1`}
src="https://www.youtube.com/embed/IX2DGd6m8BU?rel=0&modestbranding=1"
title="What is Riding for the Disabled Association (RDA)?"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
@ -345,15 +252,14 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(18px, 2vw, 24px); color: #000; line-height: 1.4; max-width: 700px;">
Find out more about <a href="/about" style="text-decoration: underline; color: inherit;">who we are</a>, what we do, and <a href="/support-us" style="text-decoration: underline; color: inherit;">how you can support us</a> to help make a difference.
</p>
<button
onclick="openDonateModal()"
style="display: inline-flex; align-items: center; gap: 10px; background: #000; color: #fff; border: none; border-radius: 9999px; padding: 16px 28px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer; white-space: nowrap; flex-shrink: 0; transition: background 0.2s ease;"
onmouseover="this.style.background='#222'" onmouseout="this.style.background='#000'"
aria-label="Open donation form"
>
Donate now
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer">
<img
src="/Button.webp"
alt="Donate via JustGiving"
style="height: 46px; object-fit: contain; display: block;"
loading="lazy"
/>
</a>
</div>
</section>
@ -363,13 +269,13 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<a class="cta-card" href={card.href}>
<img
class="cta-card-img"
src={card.image ?? ''}
src={card.img}
alt={card.title}
style="object-position: 50% 35%;"
style={`object-position: ${card.objectPos};`}
loading="lazy"
decoding="async"
/>
<div style="position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,0.05) 100%); pointer-events: none;" />
<div style="position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,0.05) 100%);" />
<div style="position: absolute; bottom: 0; left: 0; right: 0; padding: 28px 28px 32px; background: rgba(0,0,0,0.48); backdrop-filter: blur(4px);">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: rgba(255,255,255,0.65); margin-bottom: 8px;">
{card.label}
@ -378,7 +284,7 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
{card.title}
</h3>
<p class="cta-sub" style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: rgba(255,255,255,0.8); line-height: 1.6; max-width: 260px;">
{card.description}
{card.sub}
</p>
<div class="cta-more" style="margin-top: 16px; display: flex; align-items: center; gap: 8px;">
<span style="font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; color: white; letter-spacing: 0.08em; text-transform: uppercase;">
@ -396,78 +302,18 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<!-- ── SPONSORS ── -->
<section style="background: #1e2e1a; padding: clamp(48px, 8vw, 72px) clamp(24px, 5vw, 56px);">
<div style="max-width: 1100px; margin: 0 auto;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 13px; letter-spacing: 0.2em; text-transform: uppercase; color: #7DA371; text-align: center; margin-bottom: 32px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.25em; text-transform: uppercase; color: #7DA371; text-align: center; margin-bottom: 10px;">
Supporters &amp; Sponsors
</p>
<!-- Current sponsors -->
<div style="display: flex; flex-wrap: wrap; gap: 32px; justify-content: center; align-items: center; margin-bottom: 48px;">
{sponsors.map((s) => (
<div class="sponsor-logo">
{s.url
? <a href={s.url} target="_blank" rel="noreferrer"><img src={s.logo ?? ''} alt={s.name} loading="lazy" decoding="async" /></a>
: <img src={s.logo ?? ''} alt={s.name} loading="lazy" decoding="async" />
}
</div>
<div style="width: 36px; height: 2px; background: #7DA371; margin: 0 auto 32px;" />
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.5vw, 30px); color: #F6F1E8; text-align: center; margin-bottom: 48px; line-height: 1.2;">
We are grateful for the generous support of our sponsors.
</p>
<div style="display: flex; flex-wrap: wrap; gap: 24px; justify-content: center; align-items: center;">
{["Sponsor 1", "Sponsor 2", "Sponsor 3", "Sponsor 4", "Sponsor 5", "Sponsor 6"].map((s) => (
<div class="sponsor-logo">{s}</div>
))}
</div>
<!-- Corporate sponsorship package -->
<div style="border-top: 1px solid rgba(255,255,255,0.12); padding-top: 56px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.25em; text-transform: uppercase; color: #7DA371; text-align: center; margin-bottom: 12px;">
Corporate Sponsorship
</p>
<h3 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.5vw, 30px); color: #F6F1E8; text-align: center; margin-bottom: 12px; line-height: 1.2;">
Sponsor one of our horses — £750 per year
</h3>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(246,241,232,0.7); text-align: center; max-width: 620px; margin: 0 auto 48px; line-height: 1.65;">
Your sponsorship covers the annual livery costs of one of our horses, making a direct and lasting difference to the lives of our participants.
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; max-width: 960px; margin: 0 auto 48px;">
<div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 24px 22px;">
<div style="width: 32px; height: 32px; background: #7DA371; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 14px;">
<svg width="16" height="16" viewBox="0 0 16 16" fill="white" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zm3.5 6.5l-4 4-2-2-1 1 3 3 5-5-1-1z"/></svg>
</div>
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 15px; color: #F6F1E8; margin-bottom: 8px; line-height: 1.3;">Social media recognition</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: rgba(246,241,232,0.65); line-height: 1.6;">Your company featured in our posts on Facebook and Instagram, and in any press coverage we receive.</p>
</div>
<div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 24px 22px;">
<div style="width: 32px; height: 32px; background: #7DA371; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 14px;">
<svg width="16" height="16" viewBox="0 0 16 16" fill="white" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zm3.5 6.5l-4 4-2-2-1 1 3 3 5-5-1-1z"/></svg>
</div>
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 15px; color: #F6F1E8; margin-bottom: 8px; line-height: 1.3;">Branded banner at Sandycroft</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: rgba(246,241,232,0.65); line-height: 1.6;">A 1m × 1.8m printed banner with your company logo displayed at our Sandycroft base, seen by volunteers, participants, and the public.</p>
</div>
<div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 24px 22px;">
<div style="width: 32px; height: 32px; background: #7DA371; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 14px;">
<svg width="16" height="16" viewBox="0 0 16 16" fill="white" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zm3.5 6.5l-4 4-2-2-1 1 3 3 5-5-1-1z"/></svg>
</div>
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 15px; color: #F6F1E8; margin-bottom: 8px; line-height: 1.3;">Logo on our website</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: rgba(246,241,232,0.65); line-height: 1.6;">Your company logo displayed as a sponsor banner on the Highland Group RDA website.</p>
</div>
<div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 24px 22px;">
<div style="width: 32px; height: 32px; background: #7DA371; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 14px;">
<svg width="16" height="16" viewBox="0 0 16 16" fill="white" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zm3.5 6.5l-4 4-2-2-1 1 3 3 5-5-1-1z"/></svg>
</div>
<p style="font-family: 'Merriweather', serif; font-weight: 700; font-size: 15px; color: #F6F1E8; margin-bottom: 8px; line-height: 1.3;">Event invitations &amp; pony updates</p>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 14px; color: rgba(246,241,232,0.65); line-height: 1.6;">Invitations to Highland Group RDA events, plus three-monthly updates on the specific pony you are sponsoring.</p>
</div>
</div>
<div style="text-align: center;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 15px; color: rgba(246,241,232,0.8); margin-bottom: 20px;">
Interested in becoming a corporate sponsor?
</p>
<a href="/contact" style="display: inline-block; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: #1e2e1a; background: #7DA371; border-radius: 999px; padding: 12px 28px; text-decoration: none; transition: opacity 0.2s ease;">
Get in touch
</a>
<span style="display: inline-block; margin: 0 12px; color: rgba(246,241,232,0.4); font-size: 14px;">or</span>
<a href="/support-us" style="display: inline-block; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(246,241,232,0.85); border: 1px solid rgba(246,241,232,0.3); border-radius: 999px; padding: 12px 28px; text-decoration: none;">
Support us
</a>
</div>
</div>
</div>
</section>
@ -475,245 +321,6 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
<SiteFooter fullBleedOnMobile />
</div>
<!-- ── DONATE MODAL ── -->
<div id="donate-overlay" class="donate-overlay" role="dialog" aria-modal="true" aria-labelledby="donate-modal-title">
<div id="donate-panel" class="donate-panel">
<!-- Header -->
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:24px;">
<div>
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.22em;text-transform:uppercase;color:#7DA371;margin-bottom:10px;">Donate · Highland Group RDA</p>
<h2 id="donate-modal-title" style="font-family:'Merriweather',serif;font-weight:900;font-size:clamp(22px,2.6vw,30px);color:#000;line-height:1.2;margin:0;">Make a donation</h2>
<p id="donate-header-sub" style="margin-top:6px;font-family:'Public Sans',sans-serif;font-weight:600;font-size:14px;color:rgba(0,0,0,0.6);line-height:1.5;">Every gift goes directly to our ponies and riders.</p>
</div>
<button onclick="closeDonateModal()" aria-label="Close"
style="flex-shrink:0;width:38px;height:38px;border-radius:9999px;border:none;background:rgba(0,0,0,0.07);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background 0.18s;"
onmouseover="this.style.background='rgba(0,0,0,0.14)'" onmouseout="this.style.background='rgba(0,0,0,0.07)'">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#000" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3l10 10M13 3L3 13"/></svg>
</button>
</div>
<!-- Running total chip -->
<div id="donate-total-chip" style="display:flex;align-items:baseline;justify-content:space-between;gap:16px;flex-wrap:wrap;background:#1e2e1a;color:#F6F1E8;border-radius:7px;padding:14px 18px;margin-bottom:22px;">
<span id="donate-chip-label" style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;letter-spacing:0.16em;text-transform:uppercase;color:rgba(246,241,232,0.6);">Your gift</span>
<span>
<span id="donate-chip-amount" style="font-family:'Merriweather',serif;font-weight:900;font-size:26px;color:#F6F1E8;">£25</span>
<span id="donate-chip-freq" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(246,241,232,0.6);margin-left:4px;">/ month</span>
<span id="donate-chip-ga" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#7DA371;margin-left:10px;"></span>
</span>
</div>
<!-- ─── STEP: AMOUNT ─── -->
<div id="donate-step-amount">
<!-- Mode toggle -->
<div role="tablist" style="display:grid;grid-template-columns:1fr 1fr;background:rgba(0,0,0,0.06);border:1.5px solid rgba(0,0,0,0.1);border-radius:9999px;padding:4px;margin-bottom:22px;">
<button id="donate-tab-oneoff" role="tab" aria-selected="true" type="button" onclick="dSetMode('oneoff')"
style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.02em;padding:11px 18px;border-radius:9999px;border:none;cursor:pointer;background:#000;color:#fff;transition:all 0.22s;">
One-off donation
</button>
<button id="donate-tab-monthly" role="tab" aria-selected="false" type="button" onclick="dSetMode('monthly')"
style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.02em;padding:11px 18px;border-radius:9999px;border:none;cursor:pointer;background:transparent;color:rgba(0,0,0,0.55);transition:all 0.22s;">
Monthly donation
</button>
</div>
<!-- Choose an amount label -->
<p style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.08em;text-transform:uppercase;color:rgba(0,0,0,0.55);margin-bottom:12px;">Choose an amount</p>
<!-- 2×2 chips grid — filled by JS -->
<div id="donate-chips-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;"></div>
<!-- "Or another amount" inline row -->
<div style="display:flex;align-items:center;gap:10px;background:#fff;border:1.5px solid rgba(0,0,0,0.12);border-radius:7px;padding:14px 16px;margin-bottom:22px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:14px;color:rgba(0,0,0,0.55);">Or another amount</span>
<span style="margin-left:auto;font-family:'Merriweather',serif;font-weight:900;font-size:22px;color:rgba(0,0,0,0.65);">£</span>
<input id="donate-other" type="number" inputmode="decimal" min="1" placeholder="0"
oninput="dOnOther()"
style="width:86px;text-align:right;font-family:'Merriweather',serif;font-weight:900;font-size:22px;border:none;outline:none;background:transparent;color:#000;" />
<span id="donate-other-mo" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.55);">/ month</span>
</div>
<!-- Gift Aid block -->
<div style="background:rgba(0,0,0,0.03);border:1.5px solid rgba(0,0,0,0.1);border-radius:7px;padding:16px 18px;margin-bottom:22px;">
<div style="display:flex;gap:12px;cursor:pointer;align-items:flex-start;" onclick="dToggleGA()">
<span id="donate-ga-box"
style="flex-shrink:0;width:22px;height:22px;border-radius:4px;margin-top:1px;background:#fff;border:1.5px solid rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;transition:all 0.18s;">
</span>
<span>
<span style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;color:#000;display:block;margin-bottom:4px;">Make my donation worth 25% more with Gift Aid</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.6);line-height:1.5;">I'm a UK taxpayer and I'd like Highland Group RDA to reclaim Gift Aid on my donation and any donations I make in the future or have made in the last four years.</span>
</span>
</div>
<!-- Expandable name/address fields -->
<div id="donate-ga-fields" style="overflow:hidden;max-height:0;transition:max-height 0.4s ease;">
<div style="display:flex;flex-direction:column;gap:10px;padding-top:16px;">
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<label style="display:flex;flex-direction:column;gap:6px;flex:1 1 120px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">First name</span>
<input type="text" placeholder="Jane" class="donate-fi" />
</label>
<label style="display:flex;flex-direction:column;gap:6px;flex:1 1 120px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Last name</span>
<input type="text" placeholder="MacDonald" class="donate-fi" />
</label>
</div>
<label style="display:flex;flex-direction:column;gap:6px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Home address</span>
<input type="text" placeholder="House number and street" class="donate-fi" />
</label>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<label style="display:flex;flex-direction:column;gap:6px;flex:1 1 120px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Town</span>
<input type="text" placeholder="Inverness" class="donate-fi" />
</label>
<label style="display:flex;flex-direction:column;gap:6px;flex:1 1 120px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Postcode</span>
<input type="text" placeholder="IV5 7PP" class="donate-fi" />
</label>
</div>
</div>
</div>
</div>
<!-- Continue button -->
<button type="button" id="donate-continue-btn" onclick="dProceed()"
style="display:inline-flex;align-items:center;gap:10px;background:#7DA371;color:#fff;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.08em;text-transform:uppercase;padding:16px 28px;border-radius:9999px;transition:background 0.18s;"
onmouseover="this.style.background='#5a7d55'" onmouseout="this.style.background='#7DA371'">
Continue to payment
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
<!-- ─── STEP: PAY ─── -->
<div id="donate-step-pay" style="display:none;">
<!-- Back -->
<button type="button" onclick="dBack()"
style="background:transparent;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.6);display:inline-flex;align-items:center;gap:6px;padding:0;margin-bottom:20px;">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="transform:rotate(180deg);"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
Back to amount
</button>
<!-- Express checkout -->
<p style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.08em;text-transform:uppercase;color:rgba(0,0,0,0.55);margin-bottom:12px;">Express checkout</p>
<div style="display:flex;gap:8px;margin-bottom:20px;">
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer" onclick="closeDonateModal()"
style="flex:1;height:46px;border-radius:6px;background:#000;color:#fff;display:flex;align-items:center;justify-content:center;text-decoration:none;gap:4px;font-family:'Public Sans',sans-serif;font-weight:600;font-size:14px;transition:filter 0.18s;"
onmouseover="this.style.filter='brightness(0.8)'" onmouseout="this.style.filter='none'">
<svg width="13" height="15" viewBox="0 0 14 16" fill="white" aria-hidden="true"><path d="M10.4 8.5c0-1.9 1.5-2.8 1.6-2.8-.9-1.3-2.2-1.5-2.7-1.5-1.2-.1-2.2.7-2.8.7-.6 0-1.5-.7-2.5-.6-1.3 0-2.5.7-3.1 1.9-1.4 2.3-.3 5.8 1 7.6.7.9 1.5 1.9 2.5 1.8 1 0 1.4-.6 2.6-.6 1.2 0 1.5.6 2.6.6 1.1 0 1.8-.9 2.4-1.8.4-.6.7-1.1.9-1.9-1.5-.6-2.5-1.8-2.5-3.4zM8.6 2.8c.6-.7 1-1.7.8-2.7-.9 0-1.9.5-2.5 1.2-.5.6-1 1.6-.9 2.6 1 .1 2-.5 2.6-1.1z"/></svg>
Pay
</a>
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer" onclick="closeDonateModal()"
style="flex:1;height:46px;border-radius:6px;background:#000;color:#fff;display:flex;align-items:center;justify-content:center;text-decoration:none;font-size:14px;transition:filter 0.18s;"
onmouseover="this.style.filter='brightness(0.8)'" onmouseout="this.style.filter='none'">
<span style="font-weight:600;"><span style="color:#4285F4;">G</span>Pay</span>
</a>
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer" onclick="closeDonateModal()"
style="flex:1;height:46px;border-radius:6px;background:#FFC439;display:flex;align-items:center;justify-content:center;text-decoration:none;font-family:Verdana,sans-serif;font-weight:900;font-style:italic;font-size:15px;letter-spacing:-0.02em;transition:filter 0.18s;"
onmouseover="this.style.filter='brightness(0.92)'" onmouseout="this.style.filter='none'">
<span style="color:#003087;">Pay</span><span style="color:#009CDE;">Pal</span>
</a>
</div>
<!-- Or divider -->
<div style="display:flex;align-items:center;gap:12px;margin:4px 0 20px;">
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:rgba(0,0,0,0.45);">Or pay with card</span>
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
</div>
<!-- Card form (visual mock) -->
<div style="display:flex;flex-direction:column;gap:14px;margin-bottom:16px;">
<label style="display:flex;flex-direction:column;gap:6px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Email</span>
<input type="email" placeholder="you@example.com" class="donate-fi" />
</label>
<label style="display:flex;flex-direction:column;gap:6px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Card information</span>
<div style="background:#fff;border:1.5px solid rgba(0,0,0,0.15);border-radius:6px;display:flex;align-items:center;overflow:hidden;">
<input type="text" placeholder="1234 1234 1234 1234"
style="flex:1;padding:12px 14px;border:none;outline:none;font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;background:transparent;" />
<div style="display:flex;align-items:center;gap:4px;padding-right:10px;">
<span style="display:inline-flex;align-items:center;justify-content:center;height:18px;padding:0 5px;border-radius:3px;background:#1A1F71;color:#fff;font-family:'Public Sans',sans-serif;font-weight:900;font-size:9px;letter-spacing:0.04em;">VISA</span>
<span style="display:inline-flex;align-items:center;justify-content:center;height:18px;padding:0 5px;border-radius:3px;background:#EB001B;color:#fff;font-family:'Public Sans',sans-serif;font-weight:900;font-size:9px;">MC</span>
<span style="display:inline-flex;align-items:center;justify-content:center;height:18px;padding:0 5px;border-radius:3px;background:#016FD0;color:#fff;font-family:'Public Sans',sans-serif;font-weight:900;font-size:9px;">AMEX</span>
</div>
</div>
</label>
<div style="display:flex;gap:10px;">
<label style="display:flex;flex-direction:column;gap:6px;flex:1;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Expiry</span>
<input type="text" placeholder="MM / YY" class="donate-fi" />
</label>
<label style="display:flex;flex-direction:column;gap:6px;flex:1;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">CVC</span>
<input type="text" placeholder="123" class="donate-fi" />
</label>
</div>
<label style="display:flex;flex-direction:column;gap:6px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Name on card</span>
<input type="text" placeholder="Full name" class="donate-fi" />
</label>
<div style="display:flex;gap:10px;">
<label style="display:flex;flex-direction:column;gap:6px;flex:1;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Country</span>
<select class="donate-fi" style="cursor:pointer;appearance:none;">
<option>United Kingdom</option>
<option>Ireland</option>
<option>United States</option>
<option>Other</option>
</select>
</label>
<label style="display:flex;flex-direction:column;gap:6px;flex:1;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.65);">Postcode</span>
<input type="text" placeholder="IV5 7PP" class="donate-fi" />
</label>
</div>
</div>
<!-- Direct Debit note (monthly only) -->
<div id="donate-dd-note" style="display:none;margin-bottom:16px;padding:12px 14px;background:rgba(0,0,0,0.04);border-radius:6px;border:1px dashed rgba(0,0,0,0.18);">
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#000;margin-bottom:4px;">Prefer Direct Debit?</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.6);line-height:1.5;">We can also set up your monthly gift via Direct Debit (BACS). <a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer" style="text-decoration:underline;">Donate via JustGiving →</a></p>
</div>
<!-- Donate button -->
<button type="button" id="donate-pay-btn" onclick="dComplete()"
style="margin-top:4px;display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;background:#7DA371;color:#fff;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.08em;text-transform:uppercase;padding:16px 28px;border-radius:9999px;box-sizing:border-box;transition:background 0.18s;"
onmouseover="this.style.background='#5a7d55'" onmouseout="this.style.background='#7DA371'">
<svg width="12" height="13" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>
<span id="donate-pay-label">Donate £25</span>
</button>
<!-- Trust row -->
<div style="display:flex;flex-wrap:wrap;gap:16px;row-gap:8px;align-items:center;padding-top:16px;border-top:1px solid rgba(0,0,0,0.1);margin-top:16px;">
<span style="display:inline-flex;align-items:center;gap:5px;font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.55);">
<svg width="11" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>
Secured by <span style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;color:#635BFF;margin-left:2px;">stripe</span>
</span>
<span id="donate-trust-cancel" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.55);">You can cancel anytime</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.55);">Charity SC007357</span>
</div>
</div>
<!-- ─── STEP: DONE ─── -->
<div id="donate-step-done" style="display:none;">
<div style="display:flex;flex-direction:column;gap:16px;padding:32px 0 8px;text-align:left;">
<div style="width:56px;height:56px;border-radius:9999px;background:#7DA371;display:flex;align-items:center;justify-content:center;">
<svg width="28" height="28" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8l3.5 3.5L13 5"/></svg>
</div>
<h3 style="font-family:'Merriweather',serif;font-weight:900;font-size:28px;color:#000;line-height:1.2;">Thank you — from all of us at Highland Group RDA.</h3>
<p id="donate-done-msg" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:16px;color:rgba(0,0,0,0.7);line-height:1.6;max-width:520px;"></p>
<button type="button" onclick="closeDonateModal()"
style="align-self:flex-start;margin-top:8px;background:transparent;color:#000;border:1.5px solid #7DA371;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.06em;text-transform:uppercase;padding:12px 24px;border-radius:9999px;transition:background 0.18s;"
onmouseover="this.style.background='rgba(125,163,113,0.1)'" onmouseout="this.style.background='transparent'">
Close
</button>
</div>
</div>
</div>
</div>
<script>
const nav = document.getElementById("site-nav");
if (nav) {
@ -721,205 +328,5 @@ const sponsors = cms?.sponsors?.length ? cms.sponsors : DEFAULTS.sponsor
nav.classList.toggle("scrolled", window.scrollY > 40);
}, { passive: true });
}
// ── Donate modal ──
const D_PRESETS = {
oneoff: [
{ amount: 10, impact: 'Hay for a pony for two days' },
{ amount: 25, impact: "Funds one rider's session" },
{ amount: 50, impact: 'Covers a routine vet visit' },
{ amount: 100, impact: 'A full week of farriery' },
],
monthly: [
{ amount: 5, impact: 'A grooming kit each month' },
{ amount: 10, impact: 'Helps feed our ponies' },
{ amount: 20, impact: 'Funds a weekly session' },
{ amount: 35, impact: "A pony's regular farriery" },
],
};
let _dMode = 'oneoff';
let _dAmt = 25;
let _dOther = '';
let _dGA = false;
function dGetAmt() {
const v = Number(_dOther);
return (_dOther && v > 0) ? v : _dAmt;
}
function dRenderChips() {
const grid = document.getElementById('donate-chips-grid');
if (!grid) return;
const presets = D_PRESETS[_dMode];
const noOther = !_dOther;
grid.innerHTML = presets.map(({ amount, impact }) => {
const sel = noOther && _dAmt === amount;
return `<button type="button" onclick="dSelectChip(${amount})"
style="text-align:left;cursor:pointer;
background:${sel ? '#000' : '#fff'};
color:${sel ? '#fff' : '#000'};
border:1.5px solid ${sel ? '#000' : 'rgba(0,0,0,0.12)'};
border-radius:7px;padding:16px 18px;
display:flex;flex-direction:column;gap:4px;
transition:all 0.18s;"
onmouseover="if(this.dataset.sel!=='1'){this.style.borderColor='rgba(0,0,0,0.3)';}"
onmouseout="if(this.dataset.sel!=='1'){this.style.borderColor='rgba(0,0,0,0.12)';}"
data-sel="${sel ? '1' : '0'}">
<span style="font-family:'Merriweather',serif;font-weight:900;font-size:28px;line-height:1;">£${amount}</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;line-height:1.4;opacity:${sel ? 0.9 : 0.75};">${impact}</span>
</button>`;
}).join('');
}
function dUpdateChip() {
const amt = dGetAmt();
const gaAmt = _dGA && amt > 0 ? Math.round(amt * 0.25 * 100) / 100 : 0;
const el = (id) => document.getElementById(id);
if (el('donate-chip-label')) el('donate-chip-label').textContent = _dMode === 'monthly' ? 'Your monthly gift' : 'Your gift';
if (el('donate-chip-amount')) el('donate-chip-amount').textContent = '£' + (Number.isFinite(amt) ? amt : 0);
if (el('donate-chip-freq')) el('donate-chip-freq').style.display = _dMode === 'monthly' ? 'inline' : 'none';
if (el('donate-chip-ga')) {
el('donate-chip-ga').style.display = (_dGA && amt > 0) ? 'inline' : 'none';
el('donate-chip-ga').textContent = `+ £${gaAmt.toFixed(2)} Gift Aid`;
}
// Update pay button label
const gaTotal = amt + gaAmt;
const payLabel = el('donate-pay-label');
if (payLabel) {
payLabel.textContent = `Donate £${Number.isFinite(amt) ? amt : 0}${_dMode === 'monthly' ? ' / month' : ''}${_dGA && amt > 0 ? ` (£${gaTotal.toFixed(2)} with Gift Aid)` : ''}`;
}
}
function dSelectChip(amount) {
_dAmt = amount;
_dOther = '';
const inp = document.getElementById('donate-other');
if (inp) inp.value = '';
dRenderChips();
dUpdateChip();
}
function dOnOther() {
const inp = document.getElementById('donate-other');
_dOther = inp ? inp.value : '';
dRenderChips();
dUpdateChip();
}
function dSetMode(mode) {
_dMode = mode;
const toOne = document.getElementById('donate-tab-oneoff');
const toMo = document.getElementById('donate-tab-monthly');
if (toOne) { toOne.style.background = mode === 'oneoff' ? '#000' : 'transparent'; toOne.style.color = mode === 'oneoff' ? '#fff' : 'rgba(0,0,0,0.55)'; toOne.setAttribute('aria-selected', String(mode === 'oneoff')); }
if (toMo) { toMo.style.background = mode === 'monthly' ? '#000' : 'transparent'; toMo.style.color = mode === 'monthly' ? '#fff' : 'rgba(0,0,0,0.55)'; toMo.setAttribute('aria-selected', String(mode === 'monthly')); }
_dAmt = mode === 'oneoff' ? 25 : 10;
_dOther = '';
const inp = document.getElementById('donate-other');
if (inp) inp.value = '';
const moTag = document.getElementById('donate-other-mo');
if (moTag) moTag.style.display = mode === 'monthly' ? 'inline' : 'none';
dRenderChips();
dUpdateChip();
}
function dToggleGA() {
_dGA = !_dGA;
const box = document.getElementById('donate-ga-box');
const fields = document.getElementById('donate-ga-fields');
if (box) {
box.style.background = _dGA ? '#7DA371' : '#fff';
box.style.borderColor = _dGA ? '#7DA371' : 'rgba(0,0,0,0.3)';
box.innerHTML = _dGA
? '<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8l3.5 3.5L13 5"/></svg>'
: '';
}
if (fields) fields.style.maxHeight = _dGA ? '420px' : '0';
dUpdateChip();
}
function dProceed() {
const el = (id) => document.getElementById(id);
el('donate-step-amount').style.display = 'none';
el('donate-step-pay').style.display = 'block';
const dd = el('donate-dd-note');
if (dd) dd.style.display = _dMode === 'monthly' ? 'block' : 'none';
const tc = el('donate-trust-cancel');
if (tc) tc.style.display = _dMode === 'monthly' ? 'inline' : 'none';
dUpdateChip();
document.getElementById('donate-overlay')?.scrollTo(0, 0);
}
function dBack() {
document.getElementById('donate-step-pay').style.display = 'none';
document.getElementById('donate-step-amount').style.display = 'block';
}
function dComplete() {
const amt = dGetAmt();
const gaAmt = _dGA && amt > 0 ? Math.round(amt * 0.25 * 100) / 100 : 0;
const el = (id) => document.getElementById(id);
el('donate-step-pay').style.display = 'none';
el('donate-step-done').style.display = 'block';
el('donate-total-chip').style.display = 'none';
el('donate-modal-title').textContent = 'Thank you.';
el('donate-header-sub').style.display = 'none';
const msg = el('donate-done-msg');
if (msg) {
msg.innerHTML = `Your ${_dMode === 'monthly' ? 'monthly' : 'one-off'} gift of <strong>£${Number.isFinite(amt) ? amt : 0}</strong>${_dGA ? ` (worth £${(amt + gaAmt).toFixed(2)} with Gift Aid)` : ''} goes directly to our ponies and riders. A receipt is on its way to your inbox.`;
}
document.getElementById('donate-overlay')?.scrollTo(0, 0);
}
function openDonateModal() {
const el = (id) => document.getElementById(id);
// Reset state
_dMode = 'oneoff'; _dAmt = 25; _dOther = ''; _dGA = false;
// Reset UI
dSetMode('oneoff');
const gaBox = el('donate-ga-box');
const gaFields = el('donate-ga-fields');
const otherInp = el('donate-other');
if (gaBox) { gaBox.style.background = '#fff'; gaBox.style.borderColor = 'rgba(0,0,0,0.3)'; gaBox.innerHTML = ''; }
if (gaFields) gaFields.style.maxHeight = '0';
if (otherInp) otherInp.value = '';
// Reset steps
el('donate-step-amount').style.display = 'block';
el('donate-step-pay').style.display = 'none';
el('donate-step-done').style.display = 'none';
el('donate-total-chip').style.display = 'flex';
el('donate-modal-title').textContent = 'Make a donation';
const sub = el('donate-header-sub');
if (sub) sub.style.display = 'block';
dRenderChips();
dUpdateChip();
// Open overlay with animation
const overlay = el('donate-overlay');
if (overlay) {
overlay.style.display = 'flex';
document.body.style.overflow = 'hidden';
requestAnimationFrame(() => requestAnimationFrame(() => overlay.classList.add('visible')));
}
}
function closeDonateModal() {
const overlay = document.getElementById('donate-overlay');
if (!overlay) return;
overlay.classList.remove('visible');
document.body.style.overflow = '';
setTimeout(() => { overlay.style.display = 'none'; }, 300);
}
document.getElementById('donate-overlay')?.addEventListener('click', function(e) {
if (e.target === this) closeDonateModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeDonateModal();
});
Object.assign(window, { openDonateModal, closeDonateModal, dSetMode, dSelectChip, dOnOther, dToggleGA, dProceed, dBack, dComplete });
dRenderChips();
dUpdateChip();
</script>
</BaseLayout>

File diff suppressed because it is too large Load diff

View file

@ -52,7 +52,7 @@ const pageSeo = {
class="mt-3 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]"
>
Highland Group RDA is a registered charity providing
riding activities for disabled
riding and carriage-driving activities for disabled
children and adults.
</p>
<ul

View file

@ -1,27 +0,0 @@
import type { APIRoute } from "astro";
import { readFileSync, existsSync } from "fs";
import { join } from "path";
// Serve the studio SPA for all /studio/* paths (including /studio itself).
// Static assets under /studio/assets/* are served before this route fires.
export const GET: APIRoute = () => {
const indexPath = join(process.cwd(), "dist/studio-index.html");
if (!existsSync(indexPath)) {
return new Response(
"<p>Sanity Studio has not been built. Check the Docker build logs.</p>",
{ status: 503, headers: { "Content-Type": "text/html" } }
);
}
// Rewrite absolute asset paths from /static/ to /studio/static/ so
// the studio's JS/CSS/icons load correctly when hosted at /studio.
const html = readFileSync(indexPath, "utf-8")
.replaceAll('="/static/', '="/studio/static/')
.replaceAll("='/static/", "='/studio/static/");
return new Response(html, {
status: 200,
headers: { "Content-Type": "text/html; charset=utf-8" },
});
};

View file

@ -7,7 +7,7 @@ import { navigationItems } from "../lib/navigation";
const pageSeo = {
title: "Support Us",
description:
"Support Highland Group RDA through donations, volunteering, or fundraising. Help us provide life-changing horse riding sessions in the Highlands.",
"Support Highland Group RDA through donations, volunteering, or fundraising. Help us provide free, life-changing horse riding sessions in the Highlands.",
keywords:
"Support Highland RDA, donate to RDA, RDA fundraising, volunteer with RDA, charity donations Scotland, equestrian therapy support",
canonicalPath: "/support-us",
@ -17,10 +17,10 @@ const ways = [
{
label: "Donate",
heading: "Give today",
body: "A one-off or monthly donation goes directly towards running our sessions, caring for our ponies, and keeping our facilities safe.",
action: "Donate now",
href: "#donate",
external: false,
body: "A one-off or regular donation via JustGiving goes directly towards running our sessions, caring for our ponies, and keeping our facilities safe.",
action: "Donate via JustGiving",
href: "https://www.justgiving.com/charity/highlandgrouprda",
external: true,
img: "/contact-640.webp",
objectPos: "50% 40%",
},
@ -91,7 +91,7 @@ const faqs = [
},
{
q: "How do I pay to sponsor a pony?",
a: "You can sponsor one of our ponies directly on this page — choose your pony, pick a sponsorship level, and set up your monthly gift in a few steps. You can cancel at any time.",
a: "We are currently working on a dedicated payment method — thank you for your patience. In the meantime, you can donate via JustGiving or contact us directly to discuss sponsorship arrangements.",
},
];
---
@ -101,7 +101,7 @@ const faqs = [
<link
rel="preload"
as="image"
href="/hero-support.webp"
href="/Vols5-2880w-1024x768.avif"
imagesizes="100vw"
/>
</Fragment>
@ -220,7 +220,7 @@ const faqs = [
class="absolute inset-0 h-full w-full object-cover"
style="object-position: 50% 28%;"
alt=""
src="/hero-support.webp"
src="/Vols5-2880w-1024x768.avif"
loading="eager"
fetchpriority="high"
decoding="async"
@ -289,393 +289,84 @@ const faqs = [
))}
</div>
<!-- ── DONATE WIDGET ── -->
<section id="donate" style="background: #f5d445;">
<!-- Collapsed header bar -->
<div style="padding: clamp(28px, 4vw, 40px) clamp(24px, 6vw, 72px);">
<div style="max-width: 1200px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 32px; flex-wrap: wrap;">
<div>
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.5vw, 28px); color: #000; line-height: 1.3; margin-bottom: 6px;">
Donate today.
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 15px; color: rgba(0,0,0,0.6); line-height: 1.6;">
One-off or monthly — every gift goes directly to our riders and ponies.
</p>
</div>
<button id="dw-toggle-btn" onclick="dwOpen()"
style="flex-shrink:0;display:inline-flex;align-items:center;gap:10px;background:#000;color:#fff;border:none;border-radius:9999px;padding:16px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.08em;text-transform:uppercase;cursor:pointer;white-space:nowrap;transition:background 0.18s ease;"
onmouseover="this.style.background='#222'" onmouseout="this.style.background='#000'">
Donate now
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
</div>
<!-- Expandable form panel -->
<div id="dw-panel" style="display:none;opacity:0;transform:translateY(-8px);transition:opacity 0.3s ease,transform 0.3s ease;">
<div style="background:#F6F1E8;padding:clamp(28px,4vw,44px) clamp(24px,6vw,72px) clamp(32px,4vw,48px);">
<div style="max-width:1200px;margin:0 auto;display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap;">
<!-- Form column -->
<div style="flex:1 1 360px;min-width:0;">
<!-- Running total chip -->
<div id="dw-chip" style="display:flex;align-items:baseline;justify-content:space-between;gap:16px;background:#1e2e1a;color:#F6F1E8;border-radius:7px;padding:14px 18px;margin-bottom:22px;flex-wrap:wrap;">
<span id="dw-chip-label" style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;letter-spacing:0.16em;text-transform:uppercase;color:rgba(246,241,232,0.6);">Your gift</span>
<span>
<span id="dw-chip-amount" style="font-family:'Merriweather',serif;font-weight:900;font-size:26px;color:#F6F1E8;">£25</span>
<span id="dw-chip-freq" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(246,241,232,0.6);margin-left:4px;">/ month</span>
<span id="dw-chip-ga" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#7DA371;margin-left:10px;"></span>
</span>
</div>
<!-- Amount step -->
<div id="dw-step-amount">
<div style="display:flex;background:rgba(0,0,0,0.08);border-radius:9999px;padding:4px;width:fit-content;margin-bottom:24px;">
<button id="dw-tab-oneoff" onclick="dwSetMode('oneoff')"
style="padding:9px 22px;border-radius:9999px;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;letter-spacing:0.08em;text-transform:uppercase;transition:all 0.18s ease;background:#000;color:#fff;">
One-off
</button>
<button id="dw-tab-monthly" onclick="dwSetMode('monthly')"
style="padding:9px 22px;border-radius:9999px;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;letter-spacing:0.08em;text-transform:uppercase;transition:all 0.18s ease;background:transparent;color:rgba(0,0,0,0.5);">
Monthly
</button>
</div>
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.18em;text-transform:uppercase;color:rgba(0,0,0,0.4);margin-bottom:12px;">Choose an amount</p>
<div id="dw-chips-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px;"></div>
<div style="display:flex;align-items:center;gap:10px;margin-bottom:24px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.5);">Or</span>
<div style="position:relative;flex:1;display:flex;align-items:center;">
<span style="position:absolute;left:14px;font-family:'Public Sans',sans-serif;font-weight:700;font-size:15px;color:rgba(0,0,0,0.5);">£</span>
<input id="dw-other-input" type="number" min="1" placeholder="Other amount" oninput="dwOnOther()"
style="width:100%;padding:12px 14px 12px 30px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-weight:700;font-size:15px;background:white;outline:none;box-sizing:border-box;"
onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
<span id="dw-other-mo" style="display:none;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.5);">/ mo</span>
</div>
<!-- Gift Aid -->
<div style="background:white;border-radius:7px;padding:18px 20px;margin-bottom:24px;border:2px solid rgba(0,0,0,0.08);">
<div onclick="dwToggleGA()" style="display:flex;align-items:flex-start;gap:14px;cursor:pointer;">
<span id="dw-ga-check" style="flex-shrink:0;margin-top:2px;width:20px;height:20px;border-radius:5px;border:2px solid rgba(0,0,0,0.25);background:white;display:flex;align-items:center;justify-content:center;transition:all 0.15s ease;">
<svg id="dw-ga-tick" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="opacity:0;transition:opacity 0.15s ease;"><path d="M2 6l3 3 5-5"/></svg>
</span>
<div>
<p style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;color:#000;margin-bottom:2px;">Boost your donation with Gift Aid</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.55);line-height:1.5;">We can claim 25p of Gift Aid for every £1 you give at no extra cost to you.</p>
</div>
</div>
<div id="dw-ga-fields" style="max-height:0;overflow:hidden;transition:max-height 0.3s ease,opacity 0.3s ease;opacity:0;">
<div style="margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<input placeholder="First name" oninput="dwGaField('firstName',this.value)" style="padding:10px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Last name" oninput="dwGaField('lastName',this.value)" style="padding:10px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Address" oninput="dwGaField('address',this.value)" style="grid-column:1/-1;padding:10px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Town / City" oninput="dwGaField('town',this.value)" style="padding:10px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Postcode" oninput="dwGaField('postcode',this.value)" style="padding:10px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
<p style="margin-top:10px;font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(0,0,0,0.45);line-height:1.6;">I am a UK taxpayer and understand that if I pay less Income Tax and/or Capital Gains Tax than the amount of Gift Aid claimed on all my donations in that tax year it is my responsibility to pay any difference.</p>
</div>
</div>
<button onclick="dwProceed()"
style="width:100%;display:flex;align-items:center;justify-content:center;gap:10px;background:#1e2e1a;color:#F6F1E8;border:none;border-radius:9999px;padding:16px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:background 0.18s ease;"
onmouseover="this.style.background='#2d4427'" onmouseout="this.style.background='#1e2e1a'">
Continue to payment
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#F6F1E8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
<!-- Pay step -->
<div id="dw-step-pay" style="display:none;">
<button onclick="dwBack()" style="display:inline-flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.5);padding:0;margin-bottom:22px;">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8H3M7 4L3 8l4 4"/></svg>
Back
</button>
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.18em;text-transform:uppercase;color:rgba(0,0,0,0.4);margin-bottom:12px;">Express checkout</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:20px;">
<button style="padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:black;color:white;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;">Apple Pay</button>
<button style="padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:white;color:black;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;">G Pay</button>
<a href="https://www.justgiving.com/charity/highlandgrouprda" target="_blank" rel="noreferrer" style="display:flex;align-items:center;justify-content:center;padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:white;color:#003087;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;text-decoration:none;">PayPal</a>
</div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px;">
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.4);">Or pay with card</span>
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:20px;">
<input type="email" placeholder="Email address" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Card number" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<input placeholder="MM / YY" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="CVC" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
<input placeholder="Name on card" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<select style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:white;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'">
<option>United Kingdom</option>
<option>United States</option>
<option>Other</option>
</select>
<input placeholder="Postcode" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
</div>
<div id="dw-dd-note" style="display:none;background:rgba(0,0,0,0.05);border-radius:7px;padding:12px 16px;margin-bottom:16px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.6);line-height:1.5;">Monthly donations are collected via Direct Debit. You can cancel at any time.</p>
</div>
<button id="dw-pay-btn" onclick="dwComplete()"
style="width:100%;display:flex;align-items:center;justify-content:center;gap:10px;background:#1e2e1a;color:#F6F1E8;border:none;border-radius:9999px;padding:16px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:background 0.18s ease;margin-bottom:14px;"
onmouseover="this.style.background='#2d4427'" onmouseout="this.style.background='#1e2e1a'">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#F6F1E8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="5" width="12" height="9" rx="2"/><path d="M5 5V4a3 3 0 016 0v1"/></svg>
<span id="dw-pay-label">Donate £25</span>
</button>
<div style="display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">Secured by Stripe</span>
<span style="color:rgba(0,0,0,0.2);">·</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">Cancel anytime</span>
<span style="color:rgba(0,0,0,0.2);">·</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">SC007357</span>
</div>
</div>
<!-- Done step -->
<div id="dw-step-done" style="display:none;text-align:center;padding:40px 0 24px;">
<div style="width:56px;height:56px;background:#7DA371;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>
</div>
<h3 style="font-family:'Merriweather',serif;font-weight:900;font-size:28px;color:#000;margin-bottom:10px;">Thank you.</h3>
<p id="dw-done-msg" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:16px;color:rgba(0,0,0,0.6);line-height:1.6;max-width:400px;margin:0 auto 28px;"></p>
<button onclick="dwClose()" style="display:inline-flex;align-items:center;gap:10px;background:#1e2e1a;color:#F6F1E8;border:none;border-radius:9999px;padding:14px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;">
Close
</button>
</div>
</div>
<!-- Summary rail (desktop) -->
<div id="dw-rail" style="display:none;flex:0 0 260px;background:#1e2e1a;border-radius:7px;padding:28px 24px;align-self:flex-start;position:sticky;top:90px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:10px;letter-spacing:0.22em;text-transform:uppercase;color:rgba(246,241,232,0.5);margin-bottom:16px;">Your support</p>
<div id="dw-rail-amount" style="font-family:'Merriweather',serif;font-weight:900;font-size:38px;color:#F6F1E8;margin-bottom:4px;">£25</div>
<div id="dw-rail-freq" style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(246,241,232,0.5);margin-bottom:20px;">one-off gift</div>
<div id="dw-rail-impact" style="background:rgba(255,255,255,0.08);border-radius:6px;padding:14px 16px;margin-bottom:20px;">
<p id="dw-rail-impact-text" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.75);line-height:1.5;"></p>
</div>
<div id="dw-rail-ga" style="display:none;">
<div style="height:1px;background:rgba(255,255,255,0.1);margin-bottom:16px;"></div>
<div style="display:flex;justify-content:space-between;margin-bottom:6px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.6);">Your gift</span>
<span id="dw-rail-ga-base" style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#F6F1E8;"></span>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:12px;">
<span style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.6);">Gift Aid</span>
<span id="dw-rail-ga-extra" style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#7DA371;"></span>
</div>
<div style="display:flex;justify-content:space-between;">
<span style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;color:#F6F1E8;">Total to charity</span>
<span id="dw-rail-ga-total" style="font-family:'Merriweather',serif;font-weight:900;font-size:16px;color:#F6F1E8;"></span>
</div>
</div>
</div>
</div>
<!-- ── DONATE BAND ── -->
<section style="background: #f5d445; padding: clamp(32px, 5vw, 44px) clamp(24px, 6vw, 72px);">
<div style="max-width: 1200px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 32px; flex-wrap: wrap;">
<div>
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(20px, 2.5vw, 28px); color: #000; line-height: 1.3; margin-bottom: 8px;">
Donate today via JustGiving.
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 15px; color: rgba(0,0,0,0.65); line-height: 1.6;">
One-off or regular — every donation goes directly towards our riders and ponies.
</p>
</div>
<a
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
style="flex-shrink: 0;"
>
<img
src="/Button.webp"
alt="Donate via JustGiving"
style="height: 52px; object-fit: contain; display: block;"
loading="lazy"
/>
</a>
</div>
</section>
<!-- ── SPONSOR A PONY ── -->
<section id="sponsor">
<!-- Step 0: Idle -->
<div id="sp-idle" style="display:flex;width:100%;min-height:520px;overflow:hidden;" class="split-panel">
<div class="panel-wrap split-panel-img" style="flex:0 0 48%;position:relative;overflow:hidden;">
<img class="panel-img" src="/alineofponies-1920w.webp" alt="Our ponies"
style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%;"
loading="lazy" decoding="async" />
</div>
<div class="split-panel-text" style="flex:1 1 0;background:#1e2e1a;display:flex;flex-direction:column;justify-content:center;padding:64px 72px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.22em;text-transform:uppercase;color:#7DA371;margin-bottom:14px;">Sponsor a pony</p>
<div style="width:36px;height:2px;background:#7DA371;margin-bottom:22px;"></div>
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.5vw,34px);color:#F6F1E8;line-height:1.15;margin-bottom:16px;">By sponsoring a pony, you enable us to enrich lives.</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:16px;color:rgba(246,241,232,0.75);line-height:1.7;margin-bottom:32px;">Our ponies need regular farriery, feed, and veterinary care. Sponsoring one — even partially — makes a direct, tangible difference to a real animal and the riders who depend on them.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:36px;">
{costs.map((c) => (
<div class="cost-card" style="background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.12);border-radius:7px;padding:16px 18px;">
<p style="font-family:'Merriweather',serif;font-weight:900;font-size:26px;color:#7DA371;line-height:1;">{c.amount}</p>
<p style="margin-top:4px;font-family:'Public Sans',sans-serif;font-weight:600;font-size:11px;color:rgba(246,241,232,0.45);text-transform:uppercase;letter-spacing:0.08em;">{c.period}</p>
<p style="margin-top:8px;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:#F6F1E8;line-height:1.4;">{c.label}</p>
</div>
))}
</div>
<button onclick="spStart()"
style="align-self:flex-start;display:inline-flex;align-items:center;gap:10px;background:#7DA371;color:white;border:none;border-radius:9999px;padding:13px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:background 0.18s ease;"
onmouseover="this.style.background='#5a7d55'" onmouseout="this.style.background='#7DA371'">
Choose a pony to sponsor
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
<section id="sponsor" class="split-panel" style="display: flex; width: 100%; min-height: 520px; overflow: hidden;">
<div class="panel-wrap split-panel-img" style="flex: 0 0 48%; position: relative; overflow: hidden;">
<img
class="panel-img"
src="/alineofponies-1920w.webp"
alt="Our ponies"
style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%;"
loading="lazy"
decoding="async"
/>
</div>
<!-- Step 1: Pony picker -->
<div id="sp-picker" style="display:none;background:#1e2e1a;padding:clamp(40px,6vw,72px) clamp(24px,6vw,72px);">
<div style="max-width:1100px;margin:0 auto;">
<button onclick="spBack(1)" style="display:inline-flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(246,241,232,0.5);padding:0;margin-bottom:28px;">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8H3M7 4L3 8l4 4"/></svg>
Back
</button>
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.22em;text-transform:uppercase;color:#7DA371;margin-bottom:12px;">Sponsor a pony</p>
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.5vw,32px);color:#F6F1E8;line-height:1.2;margin-bottom:8px;">Who would you like to sponsor?</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(246,241,232,0.65);margin-bottom:36px;">Each of our four ponies plays a unique role in our sessions.</p>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px;">
<button onclick="spSelectPony('breagha')" style="position:relative;aspect-ratio:3/4;border:none;border-radius:10px;overflow:hidden;cursor:pointer;background:transparent;padding:0;transition:transform 0.2s ease;" onmouseover="this.style.transform='scale(1.03)'" onmouseout="this.style.transform='scale(1)'">
<img src="/Breagh-bc8e70e8-2880w.webp" alt="Breagha" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;" />
<div style="position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,0.75) 0%,rgba(0,0,0,0.1) 60%);"></div>
<div style="position:absolute;bottom:0;left:0;right:0;padding:20px 18px;">
<p style="font-family:'Merriweather',serif;font-weight:700;font-size:20px;color:white;margin-bottom:3px;">Breagha</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(255,255,255,0.7);">13hh · Skewbald cob mare · 19yo</p>
</div>
</button>
<button onclick="spSelectPony('harley')" style="position:relative;aspect-ratio:3/4;border:none;border-radius:10px;overflow:hidden;cursor:pointer;background:transparent;padding:0;transition:transform 0.2s ease;" onmouseover="this.style.transform='scale(1.03)'" onmouseout="this.style.transform='scale(1)'">
<img src="/Harley-6ece9eca-2880w.webp" alt="Harley" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;" />
<div style="position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,0.75) 0%,rgba(0,0,0,0.1) 60%);"></div>
<div style="position:absolute;bottom:0;left:0;right:0;padding:20px 18px;">
<p style="font-family:'Merriweather',serif;font-weight:700;font-size:20px;color:white;margin-bottom:3px;">Harley</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(255,255,255,0.7);">15.3hh · Skewbald cob · 17yo</p>
</div>
</button>
<button onclick="spSelectPony('connolly')" style="position:relative;aspect-ratio:3/4;border:none;border-radius:10px;overflow:hidden;cursor:pointer;background:transparent;padding:0;transition:transform 0.2s ease;" onmouseover="this.style.transform='scale(1.03)'" onmouseout="this.style.transform='scale(1)'">
<img src="/connolly-73f3e69f-2880w-2.webp" alt="Connolly" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;" />
<div style="position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,0.75) 0%,rgba(0,0,0,0.1) 60%);"></div>
<div style="position:absolute;bottom:0;left:0;right:0;padding:20px 18px;">
<p style="font-family:'Merriweather',serif;font-weight:700;font-size:20px;color:white;margin-bottom:3px;">Connolly</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(255,255,255,0.7);">15.1hh · Piebald cob gelding · 18yo</p>
</div>
</button>
<button onclick="spSelectPony('puzzle')" style="position:relative;aspect-ratio:3/4;border:none;border-radius:10px;overflow:hidden;cursor:pointer;background:transparent;padding:0;transition:transform 0.2s ease;" onmouseover="this.style.transform='scale(1.03)'" onmouseout="this.style.transform='scale(1)'">
<img src="/puzzle-5de6e325-2880w.webp" alt="Puzzle" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;" />
<div style="position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,0.75) 0%,rgba(0,0,0,0.1) 60%);"></div>
<div style="position:absolute;bottom:0;left:0;right:0;padding:20px 18px;">
<p style="font-family:'Merriweather',serif;font-weight:700;font-size:20px;color:white;margin-bottom:3px;">Puzzle</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(255,255,255,0.7);">15.1hh · Skewbald cob mare · 16yo</p>
</div>
</button>
</div>
<div class="split-panel-text" style="flex: 1 1 0; background: #1e2e1a; display: flex; flex-direction: column; justify-content: center; padding: 64px 72px;">
<p style="font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: #7DA371; margin-bottom: 14px;">
Sponsor a pony
</p>
<div style="width: 36px; height: 2px; background: #7DA371; margin-bottom: 22px;" />
<h2 style="font-family: 'Merriweather', serif; font-weight: 700; font-size: clamp(22px, 2.5vw, 34px); color: #F6F1E8; line-height: 1.15; margin-bottom: 16px;">
By sponsoring a pony, you enable us to enrich lives.
</h2>
<p style="font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 16px; color: rgba(246,241,232,0.75); line-height: 1.7; margin-bottom: 32px;">
Our ponies need regular farriery, feed, and veterinary care. Sponsoring one — even partially — makes a direct, tangible difference to a real animal and the riders who depend on them.
</p>
<!-- Cost grid -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 36px;">
{costs.map((c) => (
<div class="cost-card" style="background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.12); border-radius: 7px; padding: 16px 18px;">
<p style="font-family: 'Merriweather', serif; font-weight: 900; font-size: 26px; color: #7DA371; line-height: 1;">
{c.amount}
</p>
<p style="margin-top: 4px; font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 11px; color: rgba(246,241,232,0.45); text-transform: uppercase; letter-spacing: 0.08em;">
{c.period}
</p>
<p style="margin-top: 8px; font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 13px; color: #F6F1E8; line-height: 1.4;">
{c.label}
</p>
</div>
))}
</div>
<a
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
style="align-self: flex-start; display: inline-flex; align-items: center; gap: 10px; background: #7DA371; color: white; border-radius: 9999px; padding: 13px 28px; font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;"
>
Sponsor via JustGiving
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M3 8h10M9 4l4 4-4 4"/>
</svg>
</a>
</div>
<!-- Step 2: Tier picker -->
<div id="sp-tier" style="display:none;background:#1e2e1a;padding:clamp(40px,6vw,72px) clamp(24px,6vw,72px);">
<div style="max-width:800px;margin:0 auto;">
<button onclick="spBack(2)" style="display:inline-flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(246,241,232,0.5);padding:0;margin-bottom:28px;">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8H3M7 4L3 8l4 4"/></svg>
Back
</button>
<p id="sp-tier-pony-name" style="font-family:'Merriweather',serif;font-weight:900;font-size:clamp(22px,2.5vw,32px);color:#F6F1E8;margin-bottom:8px;"></p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(246,241,232,0.65);margin-bottom:32px;">Choose your sponsorship level.</p>
<div id="sp-tier-rows" style="display:flex;flex-direction:column;gap:10px;margin-bottom:28px;"></div>
<!-- Gift Aid -->
<div style="background:rgba(255,255,255,0.07);border-radius:7px;padding:18px 20px;margin-bottom:24px;">
<div onclick="spToggleGA()" style="display:flex;align-items:flex-start;gap:14px;cursor:pointer;">
<span id="sp-ga-check" style="flex-shrink:0;margin-top:2px;width:20px;height:20px;border-radius:5px;border:2px solid rgba(246,241,232,0.3);background:transparent;display:flex;align-items:center;justify-content:center;transition:all 0.15s ease;">
<svg id="sp-ga-tick" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="opacity:0;transition:opacity 0.15s ease;"><path d="M2 6l3 3 5-5"/></svg>
</span>
<div>
<p style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;color:#F6F1E8;margin-bottom:2px;">Boost your sponsorship with Gift Aid</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.55);line-height:1.5;">We can claim 25p for every £1 you give at no extra cost.</p>
</div>
</div>
<div id="sp-ga-fields" style="max-height:0;overflow:hidden;transition:max-height 0.3s ease,opacity 0.3s ease;opacity:0;">
<div style="margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<input placeholder="First name" style="padding:10px 14px;border:2px solid rgba(246,241,232,0.2);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:rgba(255,255,255,0.07);color:#F6F1E8;" />
<input placeholder="Last name" style="padding:10px 14px;border:2px solid rgba(246,241,232,0.2);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:rgba(255,255,255,0.07);color:#F6F1E8;" />
<input placeholder="Address" style="grid-column:1/-1;padding:10px 14px;border:2px solid rgba(246,241,232,0.2);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:rgba(255,255,255,0.07);color:#F6F1E8;" />
<input placeholder="Town / City" style="padding:10px 14px;border:2px solid rgba(246,241,232,0.2);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:rgba(255,255,255,0.07);color:#F6F1E8;" />
<input placeholder="Postcode" style="padding:10px 14px;border:2px solid rgba(246,241,232,0.2);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;background:rgba(255,255,255,0.07);color:#F6F1E8;" />
</div>
</div>
</div>
<button onclick="spProceedToPayment()"
style="width:100%;display:flex;align-items:center;justify-content:center;gap:10px;background:#7DA371;color:white;border:none;border-radius:9999px;padding:16px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:background 0.18s ease;"
onmouseover="this.style.background='#5a7d55'" onmouseout="this.style.background='#7DA371'">
Continue to payment
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
</div>
<!-- Step 3: Payment -->
<div id="sp-pay" style="display:none;background:#1e2e1a;padding:clamp(40px,6vw,72px) clamp(24px,6vw,72px);">
<div style="max-width:1000px;margin:0 auto;display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap;">
<div style="flex:1 1 360px;min-width:0;background:#F6F1E8;border-radius:10px;padding:32px;">
<button onclick="spBack(3)" style="display:inline-flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:700;font-size:13px;color:rgba(0,0,0,0.5);padding:0;margin-bottom:22px;">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8H3M7 4L3 8l4 4"/></svg>
Back
</button>
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;letter-spacing:0.18em;text-transform:uppercase;color:rgba(0,0,0,0.4);margin-bottom:12px;">Express checkout</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:20px;">
<button style="padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:black;color:white;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;">Apple Pay</button>
<button style="padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:white;color:black;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;">G Pay</button>
<button style="padding:12px;border:2px solid rgba(0,0,0,0.12);border-radius:8px;background:white;color:#003087;cursor:pointer;font-family:'Public Sans',sans-serif;font-weight:800;font-size:12px;">PayPal</button>
</div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px;">
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;color:rgba(0,0,0,0.4);">Or pay with card</span>
<div style="flex:1;height:1px;background:rgba(0,0,0,0.12);"></div>
</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:20px;">
<input type="email" placeholder="Email address" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="Card number" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<input placeholder="MM / YY" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
<input placeholder="CVC" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
<input placeholder="Name on card" style="padding:12px 14px;border:2px solid rgba(0,0,0,0.15);border-radius:7px;font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;outline:none;" onfocus="this.style.borderColor='#7DA371'" onblur="this.style.borderColor='rgba(0,0,0,0.15)'" />
</div>
<div style="background:rgba(0,0,0,0.05);border-radius:7px;padding:12px 16px;margin-bottom:16px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.6);line-height:1.5;">Sponsorship is collected monthly via Direct Debit. You can cancel at any time.</p>
</div>
<button id="sp-pay-btn" onclick="spComplete()"
style="width:100%;display:flex;align-items:center;justify-content:center;gap:10px;background:#1e2e1a;color:#F6F1E8;border:none;border-radius:9999px;padding:16px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:background 0.18s ease;margin-bottom:14px;"
onmouseover="this.style.background='#2d4427'" onmouseout="this.style.background='#1e2e1a'">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#F6F1E8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="5" width="12" height="9" rx="2"/><path d="M5 5V4a3 3 0 016 0v1"/></svg>
<span id="sp-pay-label">Sponsor Breagha — £5/mo</span>
</button>
<div style="display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;">
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">Secured by Stripe</span>
<span style="color:rgba(0,0,0,0.2);">·</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">Cancel anytime</span>
<span style="color:rgba(0,0,0,0.2);">·</span>
<span style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:11px;color:rgba(0,0,0,0.35);">SC007357</span>
</div>
</div>
<!-- Summary sidebar -->
<div style="flex:0 0 260px;background:rgba(255,255,255,0.06);border-radius:10px;padding:28px 24px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:10px;letter-spacing:0.22em;text-transform:uppercase;color:rgba(246,241,232,0.5);margin-bottom:16px;">Your sponsorship</p>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px;">
<img id="sp-pay-pony-img" src="" alt="" style="width:52px;height:52px;object-fit:cover;border-radius:8px;flex-shrink:0;" />
<div>
<p id="sp-pay-pony-name" style="font-family:'Merriweather',serif;font-weight:700;font-size:18px;color:#F6F1E8;"></p>
<p id="sp-pay-pony-desc" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(246,241,232,0.55);"></p>
</div>
</div>
<div style="height:1px;background:rgba(255,255,255,0.1);margin-bottom:16px;"></div>
<p id="sp-pay-tier-name" style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:14px;color:#F6F1E8;margin-bottom:4px;"></p>
<p id="sp-pay-tier-amount" style="font-family:'Merriweather',serif;font-weight:900;font-size:28px;color:#7DA371;margin-bottom:4px;"></p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.5);">per month</p>
</div>
</div>
</div>
<!-- Step 4: Done -->
<div id="sp-done" style="display:none;background:#1e2e1a;padding:clamp(60px,8vw,100px) clamp(24px,6vw,72px);text-align:center;">
<div style="max-width:560px;margin:0 auto;">
<div style="width:64px;height:64px;background:#7DA371;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 24px;">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>
</div>
<h2 style="font-family:'Merriweather',serif;font-weight:900;font-size:clamp(26px,3vw,38px);color:#F6F1E8;margin-bottom:14px;">Thank you.</h2>
<p id="sp-done-msg" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:17px;color:rgba(246,241,232,0.7);line-height:1.7;margin-bottom:32px;"></p>
<button onclick="spReset()" style="display:inline-flex;align-items:center;gap:10px;background:#7DA371;color:white;border:none;border-radius:9999px;padding:14px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;">
Back to support page
</button>
</div>
</div>
</section>
<!-- ── VOLUNTEERING ── -->
@ -780,342 +471,12 @@ const faqs = [
<SiteFooter fullBleedOnMobile />
</div>
<script is:inline>
// ── Nav scroll ────────────────────────────────────────────
<script>
const nav = document.getElementById("site-nav");
if (nav) {
window.addEventListener("scroll", () => {
nav.classList.toggle("scrolled", window.scrollY > 40);
}, { passive: true });
}
// ── Donate Widget (inline) ────────────────────────────────
const DW_PRESETS = {
oneoff: [
{ amount: 10, impact: 'Hay for a pony for two days' },
{ amount: 25, impact: "Funds one rider's session" },
{ amount: 50, impact: 'Covers a routine vet visit' },
{ amount: 100, impact: 'A full week of farriery' },
],
monthly: [
{ amount: 5, impact: 'A grooming kit each month' },
{ amount: 10, impact: 'Helps feed our ponies' },
{ amount: 20, impact: 'Funds a weekly session' },
{ amount: 35, impact: "A pony's regular farriery" },
],
};
let _dw_open = false, _dw_mode = 'oneoff', _dw_amount = 25, _dw_other = '', _dw_ga = false;
let _dw_gaFields = {};
function dwGetAmt() {
return _dw_other ? (Number(_dw_other) || 0) : _dw_amount;
}
function dwRenderChips() {
const grid = document.getElementById('dw-chips-grid');
if (!grid) return;
const presets = DW_PRESETS[_dw_mode];
grid.innerHTML = presets.map(p => {
const sel = p.amount === _dw_amount && !_dw_other;
return `<button onclick="dwSelectChip(${p.amount})"
style="padding:16px;border:2px solid ${sel ? '#000' : 'rgba(0,0,0,0.14)'};
border-radius:8px;background:${sel ? '#000' : 'white'};color:${sel ? 'white' : '#000'};
cursor:pointer;text-align:left;transition:all 0.15s ease;">
<p style="font-family:'Merriweather',serif;font-weight:900;font-size:22px;line-height:1;margin-bottom:4px;">£${p.amount}</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;line-height:1.4;opacity:${sel ? '0.7' : '0.55'};">${p.impact}</p>
</button>`;
}).join('');
}
function dwUpdateSummary() {
const amt = dwGetAmt();
const ga = _dw_ga ? Math.round(amt * 0.25 * 100) / 100 : 0;
const chipAmt = document.getElementById('dw-chip-amount');
const chipFreq = document.getElementById('dw-chip-freq');
const chipGA = document.getElementById('dw-chip-ga');
const chipLbl = document.getElementById('dw-chip-label');
if (chipAmt) chipAmt.textContent = `£${Number.isFinite(amt) ? amt : 0}`;
if (chipFreq) chipFreq.style.display = _dw_mode === 'monthly' ? 'inline' : 'none';
if (chipGA) { chipGA.style.display = (_dw_ga && amt > 0) ? 'inline' : 'none'; chipGA.textContent = `+ £${ga.toFixed(2)} Gift Aid`; }
if (chipLbl) chipLbl.textContent = _dw_mode === 'monthly' ? 'Your monthly gift' : 'Your gift';
const payLabel = document.getElementById('dw-pay-label');
if (payLabel) {
const total = amt + ga;
payLabel.textContent = `Donate £${Number.isFinite(total) ? total : 0}${_dw_mode === 'monthly' ? '/mo' : ''}`;
}
const rail = document.getElementById('dw-rail');
if (rail) rail.style.display = (window.innerWidth >= 768) ? 'block' : 'none';
const railAmt = document.getElementById('dw-rail-amount');
const railFreq = document.getElementById('dw-rail-freq');
if (railAmt) railAmt.textContent = `£${Number.isFinite(amt) ? amt : 0}`;
if (railFreq) railFreq.textContent = _dw_mode === 'monthly' ? 'per month' : 'one-off gift';
const presets = DW_PRESETS[_dw_mode];
const match = presets.find(p => p.amount === (_dw_other ? null : _dw_amount));
const impactTxt = document.getElementById('dw-rail-impact-text');
const impactBlk = document.getElementById('dw-rail-impact');
if (impactTxt && impactBlk) {
if (match) { impactBlk.style.display = 'block'; impactTxt.textContent = match.impact; }
else { impactBlk.style.display = 'none'; }
}
const railGA = document.getElementById('dw-rail-ga');
if (railGA) {
railGA.style.display = (_dw_ga && amt > 0) ? 'block' : 'none';
if (_dw_ga && amt > 0) {
const b = document.getElementById('dw-rail-ga-base');
const e = document.getElementById('dw-rail-ga-extra');
const t = document.getElementById('dw-rail-ga-total');
if (b) b.textContent = `£${amt}`;
if (e) e.textContent = `+£${ga.toFixed(2)}`;
if (t) t.textContent = `£${(amt + ga).toFixed(2)}`;
}
}
}
function dwOpen() {
if (_dw_open) return;
_dw_open = true;
_dw_mode = 'oneoff'; _dw_amount = 25; _dw_other = ''; _dw_ga = false;
const btn = document.getElementById('dw-toggle-btn');
if (btn) {
btn.innerHTML = 'Close <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3l10 10M13 3L3 13"/></svg>';
btn.onclick = dwClose;
}
dwShowStep('amount');
dwRenderChips();
dwUpdateSummary();
const panel = document.getElementById('dw-panel');
if (panel) {
panel.style.display = 'block';
requestAnimationFrame(() => requestAnimationFrame(() => {
panel.style.opacity = '1';
panel.style.transform = 'translateY(0)';
}));
}
if (window.innerWidth >= 768) {
const r = document.getElementById('dw-rail');
if (r) r.style.display = 'block';
}
setTimeout(() => {
document.getElementById('donate')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 50);
}
function dwClose() {
_dw_open = false;
const panel = document.getElementById('dw-panel');
if (panel) {
panel.style.opacity = '0';
panel.style.transform = 'translateY(-8px)';
setTimeout(() => { panel.style.display = 'none'; }, 300);
}
const btn = document.getElementById('dw-toggle-btn');
if (btn) {
btn.innerHTML = 'Donate now <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>';
btn.style.background = '#000';
btn.onclick = dwOpen;
}
}
function dwShowStep(step) {
['amount','pay','done'].forEach(s => {
const el = document.getElementById(`dw-step-${s}`);
if (el) el.style.display = s === step ? 'block' : 'none';
});
const chip = document.getElementById('dw-chip');
if (chip) chip.style.display = step === 'done' ? 'none' : 'flex';
}
function dwSetMode(mode) {
_dw_mode = mode;
_dw_amount = mode === 'monthly' ? 10 : 25;
_dw_other = '';
const oo = document.getElementById('dw-tab-oneoff');
const mo = document.getElementById('dw-tab-monthly');
if (oo) { oo.style.background = mode === 'oneoff' ? '#000' : 'transparent'; oo.style.color = mode === 'oneoff' ? '#fff' : 'rgba(0,0,0,0.5)'; }
if (mo) { mo.style.background = mode === 'monthly' ? '#000' : 'transparent'; mo.style.color = mode === 'monthly' ? '#fff' : 'rgba(0,0,0,0.5)'; }
const inp = document.getElementById('dw-other-input');
if (inp) inp.value = '';
const otherMo = document.getElementById('dw-other-mo');
if (otherMo) otherMo.style.display = mode === 'monthly' ? 'inline' : 'none';
dwRenderChips();
dwUpdateSummary();
}
function dwSelectChip(amount) {
_dw_amount = amount; _dw_other = '';
const inp = document.getElementById('dw-other-input');
if (inp) inp.value = '';
dwRenderChips(); dwUpdateSummary();
}
function dwOnOther() {
const inp = document.getElementById('dw-other-input');
_dw_other = inp ? inp.value : '';
dwRenderChips(); dwUpdateSummary();
}
function dwToggleGA() {
_dw_ga = !_dw_ga;
const chk = document.getElementById('dw-ga-check');
const tck = document.getElementById('dw-ga-tick');
const fld = document.getElementById('dw-ga-fields');
if (chk) { chk.style.background = _dw_ga ? '#7DA371' : 'white'; chk.style.borderColor = _dw_ga ? '#7DA371' : 'rgba(0,0,0,0.25)'; }
if (tck) tck.style.opacity = _dw_ga ? '1' : '0';
if (fld) { fld.style.maxHeight = _dw_ga ? '320px' : '0'; fld.style.opacity = _dw_ga ? '1' : '0'; }
dwUpdateSummary();
}
function dwGaField(f, v) { _dw_gaFields[f] = v; }
function dwProceed() {
dwShowStep('pay');
const dd = document.getElementById('dw-dd-note');
if (dd) dd.style.display = _dw_mode === 'monthly' ? 'block' : 'none';
dwUpdateSummary();
}
function dwBack() { dwShowStep('amount'); dwRenderChips(); }
function dwComplete() {
dwShowStep('done');
const msg = document.getElementById('dw-done-msg');
const amt = dwGetAmt();
if (msg) {
msg.textContent = _dw_mode === 'monthly'
? `Your £${amt}/month regular gift means the world to us. You can cancel at any time.`
: `Your gift of £${amt} goes directly to our ponies and riders. Thank you so much.`;
}
}
// ── Sponsor Pony Widget ───────────────────────────────────
const SP_PONIES = {
breagha: { name: 'Breagha', img: '/Breagh-bc8e70e8-2880w.webp', desc: '13hh · Skewbald cob mare · 19yo' },
harley: { name: 'Harley', img: '/Harley-6ece9eca-2880w.webp', desc: '15.3hh · Skewbald cob · 17yo' },
connolly: { name: 'Connolly', img: '/connolly-73f3e69f-2880w-2.webp', desc: '15.1hh · Piebald cob gelding · 18yo' },
puzzle: { name: 'Puzzle', img: '/puzzle-5de6e325-2880w.webp', desc: '15.1hh · Skewbald cob mare · 16yo' },
};
const SP_TIERS = [
{ id: 'companion', label: 'Companion', amount: 5, perks: 'Monthly update + name on our sponsor board' },
{ id: 'partner', label: 'Partner', amount: 15, perks: 'All above + annual photo of your pony' },
{ id: 'guardian', label: 'Guardian', amount: 25, perks: 'All above + visit invitation each year' },
{ id: 'patron', label: 'Patron', amount: 40, perks: 'All above + personal thank-you from the team' },
];
let _sp_pony = null, _sp_tier = null, _sp_ga = false;
function spShowStep(step) {
['idle','picker','tier','pay','done'].forEach(s => {
const el = document.getElementById(`sp-${s}`);
if (!el) return;
if (s === step) {
el.style.display = s === 'idle' ? 'flex' : 'block';
} else {
el.style.display = 'none';
}
});
}
function spStart() {
spShowStep('picker');
document.getElementById('sponsor')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function spSelectPony(ponyId) {
_sp_pony = ponyId; _sp_tier = null; _sp_ga = false;
const nameEl = document.getElementById('sp-tier-pony-name');
if (nameEl) nameEl.textContent = `Sponsor ${SP_PONIES[ponyId].name}`;
spRenderTiers();
spShowStep('tier');
document.getElementById('sponsor')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function spRenderTiers() {
const container = document.getElementById('sp-tier-rows');
if (!container) return;
container.innerHTML = SP_TIERS.map(t => {
const sel = t.id === _sp_tier;
return `<button onclick="spSelectTier('${t.id}')"
style="display:flex;align-items:center;justify-content:space-between;gap:16px;
padding:18px 20px;border-radius:8px;cursor:pointer;text-align:left;width:100%;
border:2px solid ${sel ? '#7DA371' : 'rgba(255,255,255,0.15)'};
background:${sel ? 'rgba(125,163,113,0.2)' : 'rgba(255,255,255,0.06)'};
transition:all 0.15s ease;">
<div>
<p style="font-family:'Public Sans',sans-serif;font-weight:800;font-size:15px;color:#F6F1E8;margin-bottom:3px;">${t.label}</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(246,241,232,0.55);line-height:1.4;">${t.perks}</p>
</div>
<div style="text-align:right;flex-shrink:0;">
<p style="font-family:'Merriweather',serif;font-weight:900;font-size:22px;color:#7DA371;line-height:1;">£${t.amount}</p>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:12px;color:rgba(246,241,232,0.45);">/ month</p>
</div>
</button>`;
}).join('');
}
function spSelectTier(tierId) { _sp_tier = tierId; spRenderTiers(); }
function spToggleGA() {
_sp_ga = !_sp_ga;
const chk = document.getElementById('sp-ga-check');
const tck = document.getElementById('sp-ga-tick');
const fld = document.getElementById('sp-ga-fields');
if (chk) { chk.style.background = _sp_ga ? '#7DA371' : 'transparent'; chk.style.borderColor = _sp_ga ? '#7DA371' : 'rgba(246,241,232,0.3)'; }
if (tck) tck.style.opacity = _sp_ga ? '1' : '0';
if (fld) { fld.style.maxHeight = _sp_ga ? '320px' : '0'; fld.style.opacity = _sp_ga ? '1' : '0'; }
}
function spProceedToPayment() {
if (!_sp_tier) {
const rows = document.getElementById('sp-tier-rows');
if (rows) { rows.style.outline = '2px solid #f5d445'; rows.style.borderRadius = '8px'; setTimeout(() => { rows.style.outline = 'none'; }, 1400); }
return;
}
const pony = SP_PONIES[_sp_pony];
const tier = SP_TIERS.find(t => t.id === _sp_tier);
const img = document.getElementById('sp-pay-pony-img');
const nm = document.getElementById('sp-pay-pony-name');
const dsc = document.getElementById('sp-pay-pony-desc');
const tnm = document.getElementById('sp-pay-tier-name');
const tamt = document.getElementById('sp-pay-tier-amount');
const lbl = document.getElementById('sp-pay-label');
if (img) { img.src = pony.img; img.alt = pony.name; }
if (nm) nm.textContent = pony.name;
if (dsc) dsc.textContent = pony.desc;
if (tnm) tnm.textContent = tier.label;
if (tamt) tamt.textContent = `£${tier.amount}`;
if (lbl) lbl.textContent = `Sponsor ${pony.name} — £${tier.amount}/mo`;
spShowStep('pay');
document.getElementById('sponsor')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function spComplete() {
const pony = SP_PONIES[_sp_pony];
const tier = SP_TIERS.find(t => t.id === _sp_tier);
const msg = document.getElementById('sp-done-msg');
if (msg) msg.textContent = `You are now a ${tier.label} sponsor of ${pony.name}. Your £${tier.amount}/month will help cover their care costs. A confirmation has been sent to your email.`;
spShowStep('done');
document.getElementById('sponsor')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function spBack(fromStep) {
if (fromStep === 1) spShowStep('idle');
else if (fromStep === 2) spShowStep('picker');
else if (fromStep === 3) spShowStep('tier');
document.getElementById('sponsor')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function spReset() { _sp_pony = null; _sp_tier = null; _sp_ga = false; spShowStep('idle'); }
</script>
</BaseLayout>

File diff suppressed because it is too large Load diff

View file

@ -1,704 +0,0 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import SiteHeader from "../components/SiteHeader.astro";
import SiteFooter from "../components/SiteFooter.astro";
import { navigationItems } from "../lib/navigation";
const pageSeo = {
title: "Volunteer Reference Form",
description:
"Volunteer reference form for Highland Group RDA. Complete this form if you have been asked to provide a reference for a volunteer applicant.",
keywords: "",
canonicalPath: "/volunteer-reference",
};
const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
---
<BaseLayout {...pageSeo}>
{turnstileSiteKey && (
<Fragment slot="head">
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</Fragment>
)}
<Fragment slot="head">
<meta name="robots" content="noindex, nofollow" />
</Fragment>
<style>
/* ── Nav ── */
#site-nav { transition: background 0.4s ease, backdrop-filter 0.4s ease; }
#site-nav.scrolled { background: rgba(0,0,0,0.82); backdrop-filter: blur(8px); }
/* ── Animations ── */
@keyframes fadeUp { from { opacity:0; transform:translateY(14px); } to { opacity:1; transform:translateY(0); } }
@keyframes slideIn { from { opacity:0; transform:translateX(18px); } to { opacity:1; transform:translateX(0); } }
.anim-fade-up { animation: fadeUp 0.9s ease both; }
.step-anim { animation: slideIn 0.35s ease both; }
/* ── Fields ── */
.field-input {
width: 100%;
border: 1.5px solid rgba(0,0,0,0.18);
border-radius: 6px;
padding: 11px 14px;
font-family: 'Public Sans', sans-serif;
font-size: 15px; font-weight: 600;
color: #000; background: #fff;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
appearance: none;
box-sizing: border-box;
}
.field-input:focus { border-color: #7DA371; box-shadow: 0 0 0 3px rgba(125,163,113,0.18); }
.field-input::placeholder { color: rgba(0,0,0,0.35); font-weight: 500; }
textarea.field-input { resize: vertical; line-height: 1.6; min-height: 110px; }
/* ── Choice cards ── */
.choice {
display: flex; align-items: flex-start; gap: 12px;
cursor: pointer; padding: 13px 15px; border-radius: 6px;
border: 1.5px solid rgba(0,0,0,0.15); background: #fff;
transition: border-color 0.2s ease, background 0.2s ease;
user-select: none;
}
.choice:hover { border-color: #7DA371; }
.choice.selected { border-color: #7DA371; background: rgba(125,163,113,0.07); }
.choice-mark {
width: 18px; height: 18px; flex-shrink: 0; margin-top: 2px;
border: 2px solid rgba(0,0,0,0.25);
display: flex; align-items: center; justify-content: center;
transition: all 0.2s;
}
.choice-mark.radio { border-radius: 9999px; }
.choice-mark.check { border-radius: 4px; }
.choice.selected .choice-mark { border-color: #7DA371; background: #7DA371; }
/* ── Buttons ── */
.btn-primary {
display: inline-flex; align-items: center; gap: 10px;
background: #7DA371; color: #fff; border: none; border-radius: 9999px;
padding: 13px 28px;
font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px;
letter-spacing: 0.06em; text-transform: uppercase;
cursor: pointer; transition: opacity 0.2s ease;
}
.btn-primary:hover:not(:disabled) { opacity: 0.88; }
.btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-secondary {
display: inline-flex; align-items: center; gap: 10px;
background: transparent; color: #000;
border: 2px solid rgba(0,0,0,0.2); border-radius: 9999px;
padding: 11px 24px;
font-family: 'Public Sans', sans-serif; font-weight: 800; font-size: 13px;
letter-spacing: 0.06em; text-transform: uppercase;
cursor: pointer; transition: border-color 0.2s ease;
}
.btn-secondary:hover:not(:disabled) { border-color: #7DA371; }
.btn-secondary:disabled { opacity: 0.35; cursor: not-allowed; }
/* ── Progress ── */
.progress-pill {
display: inline-flex; align-items: center; gap: 9px;
border-radius: 9999px; padding: 8px 15px;
font-family: 'Public Sans', sans-serif; font-weight: 700; font-size: 12px;
letter-spacing: 0.04em; white-space: nowrap;
cursor: default; border: 1.5px solid transparent;
transition: all 0.25s ease;
}
.progress-pill.active { background: #7DA371; color: #fff; border-color: #7DA371; cursor: default; }
.progress-pill.done { background: rgba(125,163,113,0.12); color: #7DA371; border-color: rgba(125,163,113,0.35); cursor: pointer; }
.progress-pill.upcoming { background: transparent; color: rgba(0,0,0,0.4); border-color: rgba(0,0,0,0.12); cursor: default; }
.pill-num {
width: 20px; height: 20px; border-radius: 9999px;
display: inline-flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 800;
}
.progress-pill.active .pill-num { background: rgba(255,255,255,0.25); color: #fff; }
.progress-pill.done .pill-num { background: #7DA371; color: #fff; }
.progress-pill.upcoming .pill-num { background: rgba(0,0,0,0.08); color: rgba(0,0,0,0.4); }
/* ── Field label ── */
.field-label {
font-family: 'Public Sans', sans-serif;
font-weight: 700; font-size: 13px;
color: rgba(0,0,0,0.7); letter-spacing: 0.04em;
text-transform: uppercase; margin-bottom: 8px; display: block;
}
.field-label .req { color: #7DA371; margin-left: 3px; }
.field-hint { margin-top: 4px; font-family: 'Public Sans', sans-serif; font-weight: 500; font-size: 13px; color: rgba(0,0,0,0.5); }
.field-error { font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 13px; color: #c0392b; margin-top: 6px; display: none; }
.field-error.visible { display: block; }
/* ── Steps ── */
.step-panel { display: none; }
.step-panel.active { display: block; }
/* ── Success ── */
#success-panel { display: none; }
@media (max-width: 767px) {
.grid-2 { grid-template-columns: 1fr !important; }
.grid-3 { grid-template-columns: 1fr !important; }
.progress-wrap { gap: 4px !important; }
.progress-pill { font-size: 0; padding: 8px 10px; }
.progress-pill .pill-label { display: none; }
}
</style>
<!-- Fixed nav -->
<div id="site-nav" class="fixed top-0 left-0 right-0 z-50 px-4 sm:px-10">
<SiteHeader navigationItems={navigationItems} theme="dark" />
</div>
<div class="flex flex-col min-h-screen bg-[#e2e2e2]">
<main class="w-full">
<!-- ── HERO ── -->
<section class="relative w-full overflow-hidden" style="min-height: 46vh;">
<img
class="absolute inset-0 w-full h-full object-cover"
style="object-position: 50% 35%;"
src="/274864973_7295359163837841_5927911936073070889_n-1920w.webp"
alt=""
loading="eager" fetchpriority="high" decoding="async"
/>
<div class="absolute inset-0" style="background: linear-gradient(160deg,rgba(0,0,0,0.78) 0%,rgba(0,0,0,0.42) 60%,rgba(0,0,0,0.55) 100%);" />
<div class="relative flex flex-col justify-end" style="min-height: 46vh; padding: 100px clamp(24px,5vw,72px) 60px;">
<div class="anim-fade-up" style="max-width: 680px;">
<p style="font-family:'Public Sans',sans-serif;font-weight:700;font-size:12px;letter-spacing:0.25em;text-transform:uppercase;color:rgba(255,255,255,0.7);margin-bottom:14px;">Volunteer reference</p>
<div style="width:48px;height:2px;background:#7DA371;margin-bottom:20px;"></div>
<h1 style="font-family:'Merriweather',serif;font-weight:900;font-size:clamp(30px,4.5vw,50px);color:#F6F1E8;line-height:1.1;margin-bottom:16px;">Volunteer Reference Form</h1>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:clamp(15px,1.5vw,18px);color:rgba(246,241,232,0.82);line-height:1.65;">You've been asked to provide a reference for someone applying to volunteer with us. All information is treated in the strictest confidence and used only for assessing the application.</p>
</div>
</div>
</section>
<!-- ── FORM SECTION ── -->
<section style="background:#d6d6d6;padding:clamp(48px,8vw,72px) clamp(24px,5vw,72px) clamp(64px,10vw,96px);">
<div style="max-width:820px;margin:0 auto;" id="form-root">
<!-- ── PROGRESS ── -->
<div id="progress-wrapper" style="margin-bottom:36px;">
<div class="progress-wrap" style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;" id="progress-pills"></div>
<div style="height:4px;background:rgba(0,0,0,0.1);border-radius:9999px;overflow:hidden;margin-top:20px;">
<div id="progress-bar" style="height:100%;background:#7DA371;border-radius:9999px;transition:width 0.4s ease;width:20%;"></div>
</div>
</div>
<!-- ── FORM CARD ── -->
<div style="background:#e2e2e2;border-radius:8px;padding:clamp(28px,5vw,48px) clamp(24px,5vw,52px);">
<!-- SUCCESS -->
<div id="success-panel" style="text-align:center;padding:48px 0;">
<div style="width:72px;height:72px;border-radius:9999px;background:#7DA371;display:flex;align-items:center;justify-content:center;margin:0 auto 24px;">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
</div>
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.5vw,30px);color:#000;margin-bottom:14px;">Thank you for your reference!</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:16px;color:rgba(0,0,0,0.65);line-height:1.7;max-width:460px;margin:0 auto 32px;">
Your reference has been received by Highland Group RDA and will be treated in the strictest confidence. The applicant's coordinator will be in touch if further information is needed.
</p>
<a href="/" style="display:inline-flex;align-items:center;gap:10px;background:#7DA371;color:#fff;border-radius:9999px;padding:13px 28px;font-family:'Public Sans',sans-serif;font-weight:800;font-size:13px;letter-spacing:0.06em;text-transform:uppercase;text-decoration:none;">Back to homepage</a>
</div>
<!-- ── STEP 0: THE APPLICANT ── -->
<div class="step-panel active step-anim" data-step="0">
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.2vw,28px);color:#000;margin-bottom:8px;">The applicant</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(0,0,0,0.6);line-height:1.6;margin-bottom:32px;">Please enter the name of the person you are providing a reference for. If you're unsure of the exact spelling, check with the person who sent you this link.</p>
<div class="grid-2" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div>
<label class="field-label" for="f-app-first">Applicant's first name(s) <span class="req">*</span></label>
<input class="field-input" id="f-app-first" name="applicant_first_names" type="text" placeholder="Jane" autocomplete="off" />
<p class="field-error" id="err-app-first">Please enter the applicant's first name.</p>
</div>
<div>
<label class="field-label" for="f-app-last">Applicant's last name <span class="req">*</span></label>
<input class="field-input" id="f-app-last" name="applicant_last_name" type="text" placeholder="Smith" autocomplete="off" />
<p class="field-error" id="err-app-last">Please enter the applicant's last name.</p>
</div>
</div>
</div>
<!-- ── STEP 1: YOUR DETAILS ── -->
<div class="step-panel step-anim" data-step="1">
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.2vw,28px);color:#000;margin-bottom:8px;">Your details</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(0,0,0,0.6);line-height:1.6;margin-bottom:32px;">Your contact details as the person providing this reference.</p>
<div class="grid-2" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div>
<label class="field-label" for="f-ref-name">Your full name <span class="req">*</span></label>
<input class="field-input" id="f-ref-name" name="referee_name" type="text" placeholder="Alex Mackenzie" autocomplete="name" />
<p class="field-error" id="err-ref-name">Please enter your full name.</p>
</div>
<div>
<label class="field-label" for="f-ref-email">Your email address <span class="req">*</span></label>
<input class="field-input" id="f-ref-email" name="referee_email" type="email" placeholder="alex@example.com" autocomplete="email" />
<p class="field-error" id="err-ref-email">Please enter a valid email address.</p>
</div>
<div>
<label class="field-label" for="f-ref-job">Job title or role</label>
<input class="field-input" id="f-ref-job" name="referee_job_title" type="text" placeholder="e.g. Teacher" />
</div>
<div>
<label class="field-label" for="f-ref-tel">Telephone</label>
<input class="field-input" id="f-ref-tel" name="referee_telephone" type="tel" placeholder="07700 900000" autocomplete="tel" />
</div>
<div style="grid-column:1/-1;">
<label class="field-label" for="f-ref-org">Organisation (if applicable)</label>
<input class="field-input" id="f-ref-org" name="referee_organisation" type="text" placeholder="e.g. Inverness Academy" />
</div>
<div style="grid-column:1/-1;">
<label class="field-label" for="f-ref-addr">Address</label>
<textarea class="field-input" id="f-ref-addr" name="referee_address" placeholder="Street, Town, Postcode" style="min-height:80px;"></textarea>
</div>
</div>
</div>
<!-- ── STEP 2: YOUR RELATIONSHIP ── -->
<div class="step-panel step-anim" data-step="2">
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.2vw,28px);color:#000;margin-bottom:8px;">Your relationship</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(0,0,0,0.6);line-height:1.6;margin-bottom:32px;">Please tell us how you know the applicant and for how long.</p>
<div style="display:flex;flex-direction:column;gap:28px;">
<div>
<label class="field-label">In what capacity do you know the applicant? <span class="req">*</span></label>
<div class="grid-2" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:4px;">
{["Employer / line manager", "Colleague", "Teacher / tutor", "Community or faith leader", "Friend", "Other"].map((opt) => (
<div class="choice" data-group="relationship_capacity" data-val={opt}>
<div class="choice-mark radio">
<div class="choice-dot" style="display:none;width:7px;height:7px;border-radius:9999px;background:#fff;"></div>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;">{opt}</span>
</div>
))}
</div>
<input type="hidden" id="hid-capacity" />
<p class="field-error" id="err-capacity">Please select your relationship to the applicant.</p>
</div>
<div>
<label class="field-label" for="f-rel-other">If you selected "Other", please describe your relationship</label>
<input class="field-input" id="f-rel-other" name="relationship_other" type="text" placeholder="e.g. Neighbour, club leader…" />
</div>
<div>
<label class="field-label" for="f-known-duration">How long have you known the applicant? <span class="req">*</span></label>
<input class="field-input" id="f-known-duration" name="known_duration" type="text" placeholder="e.g. 3 years" />
<p class="field-error" id="err-known-duration">Please enter how long you have known the applicant.</p>
</div>
<div>
<label class="field-label">Are you related to the applicant?</label>
<p class="field-hint" style="margin-bottom:10px;">RDA requires references from people who are not related to the applicant.</p>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
{["Yes", "No"].map((opt) => (
<div class="choice" data-group="related_to_applicant" data-val={opt} style="flex:0 0 auto;min-width:100px;">
<div class="choice-mark radio">
<div class="choice-dot" style="display:none;width:7px;height:7px;border-radius:9999px;background:#fff;"></div>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;">{opt}</span>
</div>
))}
</div>
<input type="hidden" id="hid-related" />
</div>
</div>
</div>
<!-- ── STEP 3: ASSESSMENT ── -->
<div class="step-panel step-anim" data-step="3">
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.2vw,28px);color:#000;margin-bottom:8px;">Your assessment</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(0,0,0,0.6);line-height:1.6;margin-bottom:32px;">Please give your honest assessment. These questions help us ensure the safety and wellbeing of our participants, who include children and vulnerable adults.</p>
<div style="display:flex;flex-direction:column;gap:28px;">
<div>
<label class="field-label" for="f-character">How would you describe the applicant's character and personal qualities? <span class="req">*</span></label>
<textarea class="field-input" id="f-character" name="character_assessment" placeholder="Please share your honest impressions of their personality, values, and how they interact with others…"></textarea>
<p class="field-error" id="err-character">Please provide a character assessment.</p>
</div>
<div>
<label class="field-label">Do you consider them reliable and punctual?</label>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:4px;">
{["Yes", "No", "Not sure"].map((opt) => (
<div class="choice" data-group="reliable_punctual" data-val={opt} style="flex:0 0 auto;min-width:100px;">
<div class="choice-mark radio">
<div class="choice-dot" style="display:none;width:7px;height:7px;border-radius:9999px;background:#fff;"></div>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;">{opt}</span>
</div>
))}
</div>
<input type="hidden" id="hid-reliable" />
</div>
<div>
<label class="field-label">Do you consider them suitable to work with people with disabilities, including children and vulnerable adults? <span class="req">*</span></label>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:4px;">
{["Yes", "No", "Not sure"].map((opt) => (
<div class="choice" data-group="suitable_for_role" data-val={opt} style="flex:0 0 auto;min-width:100px;">
<div class="choice-mark radio">
<div class="choice-dot" style="display:none;width:7px;height:7px;border-radius:9999px;background:#fff;"></div>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;">{opt}</span>
</div>
))}
</div>
<input type="hidden" id="hid-suitable" />
<p class="field-error" id="err-suitable">Please indicate whether you consider the applicant suitable for this role.</p>
</div>
<div>
<label class="field-label" for="f-concerns">Is there anything we should be aware of regarding the applicant's suitability to work with vulnerable people?</label>
<textarea class="field-input" id="f-concerns" name="suitability_concerns" placeholder="If yes, please describe. If not, you can leave this blank."></textarea>
</div>
<div>
<label class="field-label" for="f-additional">Any other comments you would like to add?</label>
<textarea class="field-input" id="f-additional" name="additional_comments" placeholder="Optional — any further information that might be helpful."></textarea>
</div>
</div>
</div>
<!-- ── STEP 4: DECLARATION ── -->
<div class="step-panel step-anim" data-step="4">
<h2 style="font-family:'Merriweather',serif;font-weight:700;font-size:clamp(22px,2.2vw,28px);color:#000;margin-bottom:8px;">Declaration</h2>
<p style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:15px;color:rgba(0,0,0,0.6);line-height:1.6;margin-bottom:32px;">Please confirm the following before submitting your reference.</p>
<div style="display:flex;flex-direction:column;gap:28px;">
<div>
<label class="field-label">Please read and confirm <span class="req">*</span></label>
<div style="display:flex;flex-direction:column;gap:10px;margin-top:4px;">
<div class="choice" data-group="declaration_truth" data-val="Yes" data-multi="true">
<div class="choice-mark check">
<svg class="choice-tick" width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:none;"><path d="M10 3L4.5 8.5 2 6"/></svg>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;line-height:1.55;">I confirm that the information provided in this reference is true and accurate to the best of my knowledge. <span style="color:#7DA371;">*</span></span>
</div>
<div class="choice" data-group="declaration_consent" data-val="Yes" data-multi="true">
<div class="choice-mark check">
<svg class="choice-tick" width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:none;"><path d="M10 3L4.5 8.5 2 6"/></svg>
</div>
<span style="font-family:'Public Sans',sans-serif;font-size:14px;font-weight:600;color:#000;line-height:1.55;">I understand that this reference will be treated as confidential and used only to assess the above applicant's suitability to volunteer with Highland Group RDA, in line with the Data Protection Act 2018.</span>
</div>
</div>
<p class="field-error" id="err-declaration">Please confirm the declaration of truth to submit.</p>
</div>
<div class="grid-2" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div>
<label class="field-label" for="f-sig">Signature name</label>
<input class="field-input" id="f-sig" name="declaration_signature" type="text" placeholder="Your full name" autocomplete="name" />
</div>
<div>
<label class="field-label" for="f-date">Date</label>
<input class="field-input" id="f-date" name="declaration_date" type="date" />
</div>
</div>
{turnstileSiteKey && (
<div>
<div class="cf-turnstile" data-sitekey={turnstileSiteKey}></div>
</div>
)}
<p class="field-error" id="err-submit" style="margin-top:4px;"></p>
</div>
</div>
<!-- ── NAV BUTTONS ── -->
<div id="form-nav" style="margin-top:36px;padding-top:28px;border-top:1px solid rgba(0,0,0,0.12);display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;">
<button class="btn-secondary" id="btn-back" disabled>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 8H3M7 4L3 8l4 4"/></svg>
Back
</button>
<p id="step-counter" style="font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.5);">Step 1 of 5</p>
<button class="btn-primary" id="btn-next">
Continue
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
</button>
</div>
</div>
<p style="margin-top:20px;font-family:'Public Sans',sans-serif;font-weight:600;font-size:13px;color:rgba(0,0,0,0.5);text-align:center;line-height:1.6;" id="help-text">
Need help? <a href="/contact" style="text-decoration:underline;color:#7DA371;">Contact us</a> and we'll be happy to assist.
</p>
</div>
</section>
</main>
<SiteFooter fullBleedOnMobile />
</div>
<script>
(() => {
const STEPS = [
{ label: 'The applicant' },
{ label: 'Your details' },
{ label: 'Relationship' },
{ label: 'Assessment' },
{ label: 'Declaration' },
];
let currentStep = 0;
const completed = new Set();
const multiState = {
declaration_truth: [],
declaration_consent: [],
};
// ── Pill rendering ──
function renderPills() {
const wrap = document.getElementById('progress-pills');
wrap.innerHTML = '';
STEPS.forEach((s, i) => {
const isDone = completed.has(i);
const isActive = i === currentStep;
const pill = document.createElement('button');
pill.type = 'button';
pill.className = 'progress-pill ' + (isActive ? 'active' : isDone ? 'done' : 'upcoming');
pill.disabled = !(isDone || isActive);
pill.innerHTML = `
<span class="pill-num">${isDone
? '<svg width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L4.5 8.5 2 6"/></svg>'
: (i + 1)
}</span>
<span class="pill-label">${s.label}</span>`;
if (isDone) pill.addEventListener('click', () => goTo(i));
if (i < STEPS.length - 1) {
wrap.appendChild(pill);
const sep = document.createElement('div');
sep.style.cssText = 'width:14px;height:1.5px;background:rgba(0,0,0,0.15);flex-shrink:0;';
wrap.appendChild(sep);
} else {
wrap.appendChild(pill);
}
});
const bar = document.getElementById('progress-bar');
bar.style.width = `${((currentStep + 1) / STEPS.length) * 100}%`;
document.getElementById('step-counter').textContent = `Step ${currentStep + 1} of ${STEPS.length}`;
}
// ── Step visibility ──
function showStep(n) {
document.querySelectorAll('.step-panel').forEach(p => {
p.classList.remove('active');
p.classList.remove('step-anim');
});
const panel = document.querySelector(`.step-panel[data-step="${n}"]`);
if (panel) {
panel.classList.add('active');
void panel.offsetWidth;
panel.classList.add('step-anim');
}
document.getElementById('btn-back').disabled = n === 0;
const btnNext = document.getElementById('btn-next');
if (n === STEPS.length - 1) {
btnNext.innerHTML = `Submit reference <svg width="14" height="14" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L4.5 8.5 2 6"/></svg>`;
} else {
btnNext.innerHTML = `Continue <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>`;
}
document.getElementById('form-root').scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function goTo(n) {
currentStep = n;
renderPills();
showStep(n);
}
// ── Choice cards ──
document.querySelectorAll('.choice').forEach(card => {
card.addEventListener('click', () => {
const group = card.dataset.group;
const cardVal = card.dataset.val;
const multi = card.dataset.multi === 'true';
if (multi) {
const arr = multiState[group] || [];
const idx = arr.indexOf(cardVal);
if (idx === -1) arr.push(cardVal); else arr.splice(idx, 1);
multiState[group] = arr;
document.querySelectorAll(`.choice[data-group="${group}"]`).forEach(c => {
const isSelected = arr.includes(c.dataset.val);
c.classList.toggle('selected', isSelected);
const tick = c.querySelector('.choice-tick');
if (tick) tick.style.display = isSelected ? 'block' : 'none';
});
} else {
// Radio: deselect all in group
document.querySelectorAll(`.choice[data-group="${group}"]`).forEach(c => {
c.classList.remove('selected');
const dot = c.querySelector('.choice-dot');
if (dot) dot.style.display = 'none';
});
card.classList.add('selected');
const dot = card.querySelector('.choice-dot');
if (dot) dot.style.display = 'block';
// Update hidden input
const hiddenMap = {
relationship_capacity: 'hid-capacity',
related_to_applicant: 'hid-related',
reliable_punctual: 'hid-reliable',
suitable_for_role: 'hid-suitable',
};
const hidId = hiddenMap[group];
if (hidId) document.getElementById(hidId).value = cardVal;
}
// Clear relevant errors
const errMap = {
relationship_capacity: 'err-capacity',
suitable_for_role: 'err-suitable',
declaration_truth: 'err-declaration',
};
const errId = errMap[group];
if (errId) document.getElementById(errId)?.classList.remove('visible');
});
});
// ── Validation ──
function showErr(id, show) {
const el = document.getElementById(id);
if (el) el.classList.toggle('visible', show);
return show;
}
function fieldVal(id) {
return (document.getElementById(id)?.value || '').trim();
}
function validateStep(s) {
let ok = true;
if (s === 0) {
if (!fieldVal('f-app-first')) { showErr('err-app-first', true); ok = false; } else showErr('err-app-first', false);
if (!fieldVal('f-app-last')) { showErr('err-app-last', true); ok = false; } else showErr('err-app-last', false);
}
if (s === 1) {
if (!fieldVal('f-ref-name')) { showErr('err-ref-name', true); ok = false; } else showErr('err-ref-name', false);
const email = fieldVal('f-ref-email');
if (!email || !/\S+@\S+\.\S+/.test(email)) { showErr('err-ref-email', true); ok = false; } else showErr('err-ref-email', false);
}
if (s === 2) {
if (!fieldVal('hid-capacity')) { showErr('err-capacity', true); ok = false; } else showErr('err-capacity', false);
if (!fieldVal('f-known-duration')) { showErr('err-known-duration', true); ok = false; } else showErr('err-known-duration', false);
}
if (s === 3) {
if (!fieldVal('f-character')) { showErr('err-character', true); ok = false; } else showErr('err-character', false);
if (!fieldVal('hid-suitable')) { showErr('err-suitable', true); ok = false; } else showErr('err-suitable', false);
}
if (s === 4) {
if (!multiState.declaration_truth.includes('Yes')) { showErr('err-declaration', true); ok = false; } else showErr('err-declaration', false);
}
return ok;
}
// ── Submit ──
async function submitForm() {
const btnNext = document.getElementById('btn-next');
const errEl = document.getElementById('err-submit');
btnNext.disabled = true;
btnNext.textContent = 'Submitting…';
errEl.classList.remove('visible');
const body = new URLSearchParams();
// All named text fields
const fields = [
['applicant_first_names', 'f-app-first'],
['applicant_last_name', 'f-app-last'],
['referee_name', 'f-ref-name'],
['referee_email', 'f-ref-email'],
['referee_job_title', 'f-ref-job'],
['referee_telephone', 'f-ref-tel'],
['referee_organisation', 'f-ref-org'],
['referee_address', 'f-ref-addr'],
['relationship_other', 'f-rel-other'],
['known_duration', 'f-known-duration'],
['character_assessment', 'f-character'],
['suitability_concerns', 'f-concerns'],
['additional_comments', 'f-additional'],
['declaration_signature', 'f-sig'],
['declaration_date', 'f-date'],
// radio hidden inputs
['relationship_capacity', 'hid-capacity'],
['related_to_applicant', 'hid-related'],
['reliable_punctual', 'hid-reliable'],
['suitable_for_role', 'hid-suitable'],
];
fields.forEach(([name, id]) => {
const el = document.getElementById(id);
if (el && el.value.trim()) body.append(name, el.value.trim());
});
// Checkbox declarations
if (multiState.declaration_truth.includes('Yes')) body.append('declaration_truth', 'Yes');
if (multiState.declaration_consent.includes('Yes')) body.append('declaration_consent', 'Yes');
// Turnstile
const tsInput = document.querySelector('[name="cf-turnstile-response"]');
if (tsInput) body.append('cf-turnstile-response', tsInput.value);
try {
const res = await fetch('/api/volunteer-reference', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
});
const json = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(json.details ? `${json.error} (${json.details})` : (json.error || `Error ${res.status}`));
}
// Show success
document.getElementById('progress-wrapper').style.display = 'none';
document.getElementById('form-nav').style.display = 'none';
document.getElementById('help-text').style.display = 'none';
document.querySelectorAll('.step-panel').forEach(p => p.classList.remove('active'));
document.getElementById('success-panel').style.display = 'block';
window.scrollTo({ top: 0, behavior: 'smooth' });
} catch (err) {
errEl.textContent = err instanceof Error ? err.message : 'Sorry, something went wrong. Please try again.';
errEl.classList.add('visible');
btnNext.disabled = false;
btnNext.innerHTML = `Submit reference <svg width="14" height="14" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L4.5 8.5 2 6"/></svg>`;
}
}
// ── Navigation buttons ──
document.getElementById('btn-next').addEventListener('click', () => {
if (currentStep === STEPS.length - 1) {
if (!validateStep(currentStep)) return;
submitForm();
return;
}
if (!validateStep(currentStep)) return;
completed.add(currentStep);
goTo(currentStep + 1);
});
document.getElementById('btn-back').addEventListener('click', () => {
if (currentStep > 0) goTo(currentStep - 1);
});
// ── Scroll-aware nav ──
const nav = document.getElementById('site-nav');
if (nav) {
window.addEventListener('scroll', () => {
nav.classList.toggle('scrolled', window.scrollY > 40);
}, { passive: true });
}
// ── Init ──
renderPills();
showStep(0);
})();
</script>
</BaseLayout>

View file

@ -1,6 +0,0 @@
# ── Sanity Studio ────────────────────────────────────────────────────────────
# Copy to .env and fill in your values from https://www.sanity.io/manage
# SANITY_STUDIO_ prefix is required — Sanity injects these into the studio bundle.
SANITY_STUDIO_PROJECT_ID=
SANITY_STUDIO_DATASET=production

14402
studio/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +0,0 @@
{
"name": "rda-studio",
"private": true,
"version": "1.0.0",
"scripts": {
"dev": "sanity dev",
"build": "sanity build",
"deploy": "sanity deploy"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sanity": "^3.66.0",
"styled-components": "^6.4.2"
}
}

View file

@ -1,20 +0,0 @@
import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
import { schemaTypes } from './schemaTypes';
export default defineConfig({
name: 'rda-v3',
title: 'Highland Group RDA',
basePath: '/studio',
projectId: process.env.SANITY_STUDIO_PROJECT_ID ?? '',
dataset: process.env.SANITY_STUDIO_DATASET ?? 'production',
plugins: [
structureTool(),
],
schema: {
types: schemaTypes,
},
});

View file

@ -1,83 +0,0 @@
import { defineType, defineField, defineArrayMember } from 'sanity';
export const homePageType = defineType({
name: 'homePage',
title: 'Home Page',
type: 'document',
// Singleton — only one document of this type should exist
__experimental_actions: ['update', 'publish'],
fields: [
defineField({
name: 'heroHeadline',
title: 'Hero headline',
type: 'string',
}),
defineField({
name: 'heroMission',
title: 'Mission statement (hero blockquote)',
type: 'text',
rows: 3,
}),
defineField({
name: 'videoHeading',
title: 'Video section heading',
type: 'string',
}),
defineField({
name: 'videoBody',
title: 'Video section body text',
type: 'text',
rows: 3,
}),
defineField({
name: 'videoYoutubeId',
title: 'YouTube video ID',
type: 'string',
description: 'Just the ID, e.g. IX2DGd6m8BU — not the full URL',
}),
defineField({
name: 'ctaCards',
title: 'CTA cards (editorial panel)',
type: 'array',
of: [
defineArrayMember({
type: 'object',
name: 'ctaCard',
fields: [
defineField({ name: 'label', title: 'Eyebrow label', type: 'string' }),
defineField({ name: 'title', title: 'Card title', type: 'string' }),
defineField({ name: 'description', title: 'Description', type: 'text', rows: 2 }),
defineField({ name: 'image', title: 'Background image', type: 'image', options: { hotspot: true } }),
defineField({ name: 'href', title: 'Link path', type: 'string' }),
],
preview: { select: { title: 'title', subtitle: 'label' } },
}),
],
}),
defineField({
name: 'sponsorHeading',
title: 'Sponsors section heading',
type: 'string',
}),
defineField({
name: 'sponsors',
title: 'Sponsors',
type: 'array',
of: [
defineArrayMember({
type: 'object',
name: 'sponsor',
fields: [
defineField({ name: 'name', title: 'Name', type: 'string' }),
defineField({ name: 'logo', title: 'Logo', type: 'image' }),
defineField({ name: 'url', title: 'Website URL', type: 'url' }),
],
preview: { select: { title: 'name' } },
}),
],
}),
],
preview: {
prepare: () => ({ title: 'Home Page' }),
},
});

View file

@ -1,4 +0,0 @@
import { postType } from './postType';
import { homePageType } from './homePageType';
export const schemaTypes = [postType, homePageType];

View file

@ -1,116 +0,0 @@
import { defineField, defineType } from 'sanity';
export const postType = defineType({
name: 'post',
title: 'Post',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (r) => r.required(),
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: { source: 'title', maxLength: 96 },
validation: (r) => r.required(),
}),
defineField({
name: 'date',
title: 'Date',
type: 'date',
options: { dateFormat: 'YYYY-MM-DD' },
validation: (r) => r.required(),
}),
defineField({
name: 'category',
title: 'Category',
type: 'string',
options: {
list: [
{ title: 'News', value: 'News' },
{ title: 'Events', value: 'Events' },
{ title: 'Fundraising', value: 'Fundraising' },
],
},
initialValue: 'News',
validation: (r) => r.required(),
}),
defineField({
name: 'summary',
title: 'Summary',
type: 'text',
rows: 3,
description: 'One or two sentences shown in article cards and meta descriptions.',
validation: (r) => r.required().max(300),
}),
defineField({
name: 'image',
title: 'Cover image',
type: 'image',
options: { hotspot: true },
fields: [
defineField({
name: 'alt',
title: 'Alt text',
type: 'string',
}),
],
}),
defineField({
name: 'body',
title: 'Body',
type: 'array',
of: [
{
type: 'block',
styles: [
{ title: 'Normal', value: 'normal' },
{ title: 'Heading 2', value: 'h2' },
{ title: 'Heading 3', value: 'h3' },
{ title: 'Quote', value: 'blockquote' },
],
marks: {
decorators: [
{ title: 'Bold', value: 'strong' },
{ title: 'Italic', value: 'em' },
],
annotations: [
{
name: 'link',
type: 'object',
title: 'Link',
fields: [
{
name: 'href',
type: 'url',
title: 'URL',
validation: (r) =>
r.uri({ allowRelative: true, scheme: ['http', 'https', 'mailto', 'tel'] }),
},
{
name: 'blank',
type: 'boolean',
title: 'Open in new tab',
initialValue: false,
},
],
},
],
},
},
{ type: 'image', options: { hotspot: true } },
],
}),
],
preview: {
select: {
title: 'title',
subtitle: 'date',
media: 'image',
},
},
});

View file

@ -1,5 +1,5 @@
{
"name": "rda-2026",
"name": "rda-react-sanity",
"main": "src/worker.ts",
"compatibility_date": "2025-12-21",
"assets": {