feat: UI polish — SolutionSection stacked cards, BeamButton CTA, Footer redesign
- SolutionSection: replace flat cards with interactive stacked card layout (skew, grayscale, hover lift, click-to-expand with z-50 + skew reset) - BeamButton: new component with framer-motion SVG beam animation along pill border (emerald→blue gradient); variants dark/light - HeroSection + FinalCTASection: primary CTA switched to BeamButton - Footer: replace programmatic SVG logo with logo-axil.png via next/image; add AImpress LTD credit link; increase hero text height - TextHoverEffect: fix viewBox (920×100), use SVG-unit fontSize, split AXIL (blue #1B9AD6) / ACCOUNTANTS (emerald #3CC68A) via tspan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
83a8878f4a
commit
7cd698d6d6
7 changed files with 297 additions and 86 deletions
|
|
@ -30,7 +30,8 @@
|
|||
"Bash(\"/Volumes/SSD/Projects/Clients/Axil Accountants/eslint.config.mjs\":*)",
|
||||
"Bash(node_modules/.bin/eslint:*)",
|
||||
"Bash(node --input-type=module:*)",
|
||||
"Bash(python3:*)"
|
||||
"Bash(python3:*)",
|
||||
"Bash(ls:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { TextHoverEffect, FooterBackgroundGradient } from '@/components/ui/TextHoverEffect';
|
||||
|
||||
const SERVICES = [
|
||||
|
|
@ -54,7 +55,7 @@ export function Footer() {
|
|||
<FooterBackgroundGradient />
|
||||
|
||||
{/* TextHoverEffect brand name */}
|
||||
<div className="relative z-10 flex h-28 items-center justify-center border-b border-white/8 sm:h-36">
|
||||
<div className="relative z-10 flex h-36 items-center justify-center border-b border-white/8 sm:h-48">
|
||||
<TextHoverEffect text="AXIL ACCOUNTANTS" duration={0} />
|
||||
</div>
|
||||
|
||||
|
|
@ -63,28 +64,14 @@ export function Footer() {
|
|||
<div className="grid grid-cols-2 gap-10 py-14 sm:grid-cols-4">
|
||||
{/* Brand column */}
|
||||
<div className="col-span-2 sm:col-span-1">
|
||||
{/* SVG logo — light version for dark background */}
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<svg width="52" height="32" viewBox="0 0 76 44" fill="none" aria-hidden="true">
|
||||
<rect x="0" y="2" width="38" height="40" rx="5" fill="#1B9AD6" />
|
||||
<path d="M19 6 L34 40 H4 L19 6Z" fill="white" />
|
||||
<path d="M19 14 L27 32 H11 L19 14Z" fill="#1B9AD6" />
|
||||
<rect x="5" y="33" width="28" height="6" rx="1.5" fill="#3CC68A" />
|
||||
<rect x="45" y="30" width="7" height="14" rx="2" fill="#3CC68A" />
|
||||
<rect x="56" y="22" width="7" height="22" rx="2" fill="#3CC68A" />
|
||||
<rect x="67" y="11" width="7" height="33" rx="2" fill="#3CC68A" />
|
||||
</svg>
|
||||
<div className="flex flex-col leading-none">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="font-display text-blue text-base font-black tracking-[0.08em]">
|
||||
AXIL
|
||||
</span>
|
||||
<span className="font-display text-emerald text-base font-bold tracking-[0.04em]">
|
||||
ACCOUNTANTS
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-blue/50 mt-0.5 h-[1px] rounded-full" />
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<Image
|
||||
src="/logo-axil.png"
|
||||
alt="Axil Accountants"
|
||||
width={140}
|
||||
height={97}
|
||||
className="h-10 w-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="mb-6 max-w-[220px] text-sm leading-relaxed text-white/60">
|
||||
|
|
@ -125,10 +112,23 @@ export function Footer() {
|
|||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="flex flex-col items-center justify-between gap-4 border-t border-white/10 py-6 sm:flex-row">
|
||||
<div className="flex flex-col items-center justify-between gap-3 border-t border-white/10 py-6 sm:flex-row">
|
||||
<p className="text-xs text-white/40">
|
||||
© {new Date().getFullYear()} Axil Accountants Ltd. All rights reserved.
|
||||
</p>
|
||||
|
||||
<p className="text-xs text-white/30">
|
||||
Developed by{' '}
|
||||
<a
|
||||
href="https://ai-impress.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-emerald text-white/50 transition-colors"
|
||||
>
|
||||
AImpress LTD
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p className="text-xs text-white/40">
|
||||
ICAEW Member · ACCA Certified · Registered in England & Wales
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { FadeIn } from '@/components/ui/FadeIn';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { BeamButton } from '@/components/ui/BeamButton';
|
||||
import { CheckCircleIcon } from '@/components/ui/icons';
|
||||
|
||||
const REASSURANCES = [
|
||||
|
|
@ -35,13 +36,9 @@ export function FinalCTASection() {
|
|||
</p>
|
||||
|
||||
<div className="mb-10 flex flex-wrap items-center justify-center gap-4">
|
||||
<Button
|
||||
size="lg"
|
||||
className="text-charcoal hover:bg-emerald-mist bg-white"
|
||||
trailingArrow
|
||||
>
|
||||
<BeamButton size="lg" variant="light" trailingArrow>
|
||||
Book a Free Consultation
|
||||
</Button>
|
||||
</BeamButton>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="secondary"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { BeamButton } from '@/components/ui/BeamButton';
|
||||
import { StarRating } from '@/components/ui/StarRating';
|
||||
import { CheckCircleIcon } from '@/components/ui/icons';
|
||||
|
||||
|
|
@ -162,9 +163,9 @@ export function HeroSection() {
|
|||
</p>
|
||||
|
||||
<div className="mb-7 flex flex-wrap gap-3">
|
||||
<Button size="lg" trailingArrow>
|
||||
<BeamButton size="lg" trailingArrow>
|
||||
Book a Free Consultation
|
||||
</Button>
|
||||
</BeamButton>
|
||||
<Button size="lg" variant="secondary">
|
||||
See Our Services
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { FadeIn } from '@/components/ui/FadeIn';
|
||||
import { CheckCircleIcon } from '@/components/ui/icons';
|
||||
|
||||
|
|
@ -26,11 +29,32 @@ const CHECKLIST = [
|
|||
'Real-time financial dashboard',
|
||||
];
|
||||
|
||||
// Per-card: stack position + whether to show grayscale overlay in idle state
|
||||
const STACK = [
|
||||
{
|
||||
pos: '',
|
||||
hover: 'hover:-translate-y-10',
|
||||
idle: "before:absolute before:inset-0 before:z-10 before:rounded-xl before:content-[''] before:bg-bg/50 before:transition-opacity before:duration-500 grayscale",
|
||||
},
|
||||
{
|
||||
pos: 'translate-x-20 translate-y-12',
|
||||
hover: 'hover:-translate-y-2',
|
||||
idle: "before:absolute before:inset-0 before:z-10 before:rounded-xl before:content-[''] before:bg-bg/50 before:transition-opacity before:duration-500 grayscale",
|
||||
},
|
||||
{
|
||||
pos: 'translate-x-40 translate-y-24',
|
||||
hover: 'hover:translate-y-6',
|
||||
idle: '',
|
||||
},
|
||||
];
|
||||
|
||||
export function SolutionSection() {
|
||||
const [active, setActive] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<section className="bg-bg py-24 lg:py-32">
|
||||
<div className="mx-auto max-w-[1440px] px-4 sm:px-6 lg:px-8 xl:px-16">
|
||||
<div className="grid grid-cols-1 items-start gap-16 lg:grid-cols-2">
|
||||
<div className="grid grid-cols-1 items-center gap-16 lg:grid-cols-2">
|
||||
{/* Left — text */}
|
||||
<FadeIn x={-20} y={0}>
|
||||
<p className="text-emerald mb-4 text-sm font-semibold tracking-widest uppercase">
|
||||
|
|
@ -56,22 +80,47 @@ export function SolutionSection() {
|
|||
</ul>
|
||||
</FadeIn>
|
||||
|
||||
{/* Right — numbered features */}
|
||||
<FadeIn x={20} y={0} delay={0.15}>
|
||||
<div className="rounded-card flex flex-col divide-y divide-black/6 border border-black/7 bg-white">
|
||||
{FEATURES.map((f) => (
|
||||
<div key={f.title} className="flex gap-5 p-7">
|
||||
<span className="text-emerald/60 mt-0.5 shrink-0 font-mono text-sm font-medium">
|
||||
{f.number}
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="font-display text-charcoal mb-1.5 text-base font-semibold">
|
||||
{f.title}
|
||||
</h3>
|
||||
<p className="text-muted text-sm leading-relaxed">{f.body}</p>
|
||||
{/* Right — stacked feature cards */}
|
||||
<FadeIn x={20} y={0} delay={0.15} className="flex items-center justify-center py-24">
|
||||
<div className="grid place-items-center [grid-template-areas:'stack']">
|
||||
{FEATURES.map((f, i) => {
|
||||
const isActive = active === i;
|
||||
const isDimmed = active !== null && !isActive;
|
||||
const { pos, hover, idle } = STACK[i];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={f.number}
|
||||
onClick={() => setActive(isActive ? null : i)}
|
||||
className={[
|
||||
'[grid-area:stack]',
|
||||
pos,
|
||||
'relative flex h-56 w-[30rem] cursor-pointer flex-col justify-between rounded-xl border px-8 py-6 transition-all duration-500 select-none',
|
||||
isActive
|
||||
? 'border-emerald/30 z-50 skew-y-0 bg-white shadow-2xl'
|
||||
: [
|
||||
'-skew-y-[8deg] border-black/7 bg-white/90',
|
||||
hover,
|
||||
"after:from-bg after:absolute after:top-[-5%] after:-right-1 after:h-[110%] after:w-80 after:bg-gradient-to-l after:to-transparent after:content-['']",
|
||||
idle,
|
||||
].join(' '),
|
||||
isDimmed ? 'opacity-40' : '',
|
||||
]
|
||||
.join(' ')
|
||||
.trim()}
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="text-emerald/60 font-mono text-sm font-medium">
|
||||
{f.number}
|
||||
</span>
|
||||
<h3 className="font-display text-charcoal text-lg font-semibold">
|
||||
{f.title}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-muted text-base leading-relaxed">{f.body}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
|
|
|||
144
src/components/ui/BeamButton.tsx
Normal file
144
src/components/ui/BeamButton.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useId, useRef, useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowRightIcon } from './icons';
|
||||
import { Spinner } from './Spinner';
|
||||
|
||||
type Size = 'sm' | 'md' | 'lg';
|
||||
type Variant = 'dark' | 'light';
|
||||
|
||||
interface BeamButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
size?: Size;
|
||||
variant?: Variant;
|
||||
loading?: boolean;
|
||||
trailingArrow?: boolean;
|
||||
leadingIcon?: React.ReactNode;
|
||||
}
|
||||
|
||||
const HEIGHTS: Record<Size, number> = { sm: 36, md: 44, lg: 56 };
|
||||
const PADDINGS: Record<Size, string> = {
|
||||
sm: 'px-4 text-sm',
|
||||
md: 'px-6 text-base',
|
||||
lg: 'px-8 text-lg',
|
||||
};
|
||||
const GAPS: Record<Size, string> = { sm: 'gap-1.5', md: 'gap-2', lg: 'gap-2.5' };
|
||||
|
||||
const VARIANTS: Record<Variant, { bg: string; text: string; border: string }> = {
|
||||
dark: {
|
||||
bg: 'bg-charcoal hover:bg-charcoal/85',
|
||||
text: 'text-white',
|
||||
border: 'rgba(255,255,255,0.1)',
|
||||
},
|
||||
light: {
|
||||
bg: 'bg-white hover:bg-emerald-mist',
|
||||
text: 'text-charcoal',
|
||||
border: 'rgba(22,37,32,0.1)',
|
||||
},
|
||||
};
|
||||
|
||||
export function BeamButton({
|
||||
children,
|
||||
size = 'md',
|
||||
variant = 'dark',
|
||||
loading = false,
|
||||
trailingArrow = false,
|
||||
leadingIcon,
|
||||
className = '',
|
||||
disabled,
|
||||
...props
|
||||
}: BeamButtonProps) {
|
||||
const uid = useId().replace(/:/g, '');
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const [w, setW] = useState(180);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const ro = new ResizeObserver(([e]) => setW(Math.round(e.contentRect.width)));
|
||||
ro.observe(el);
|
||||
return () => ro.disconnect();
|
||||
}, []);
|
||||
|
||||
const h = HEIGHTS[size];
|
||||
// Perimeter of pill: two straight edges + one full circle (r = h/2)
|
||||
const perimeter = 2 * (w - h) + Math.PI * h;
|
||||
const beamLen = Math.min(perimeter * 0.28, 90);
|
||||
const { bg, text, border } = VARIANTS[variant];
|
||||
const arrowSize = size === 'sm' ? 14 : size === 'lg' ? 20 : 16;
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
disabled={disabled || loading}
|
||||
style={{ height: h }}
|
||||
className={[
|
||||
'rounded-pill relative inline-flex cursor-pointer items-center justify-center font-medium',
|
||||
'transition-all duration-200 select-none',
|
||||
'active:scale-[0.98]',
|
||||
'focus-visible:ring-emerald/50 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100',
|
||||
bg,
|
||||
text,
|
||||
PADDINGS[size],
|
||||
GAPS[size],
|
||||
className,
|
||||
].join(' ')}
|
||||
{...props}
|
||||
>
|
||||
{/* Beam border */}
|
||||
<svg aria-hidden className="pointer-events-none absolute inset-0" width={w} height={h}>
|
||||
{/* Static base border */}
|
||||
<rect
|
||||
x="1"
|
||||
y="1"
|
||||
width={w - 2}
|
||||
height={h - 2}
|
||||
rx={9999}
|
||||
fill="none"
|
||||
stroke={border}
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
{/* Traveling beam */}
|
||||
<motion.rect
|
||||
x="1"
|
||||
y="1"
|
||||
width={w - 2}
|
||||
height={h - 2}
|
||||
rx={9999}
|
||||
fill="none"
|
||||
stroke={`url(#${uid})`}
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeDasharray={`${beamLen} ${perimeter}`}
|
||||
initial={{ strokeDashoffset: 0 }}
|
||||
animate={{ strokeDashoffset: -(perimeter + beamLen) }}
|
||||
transition={{
|
||||
duration: 2.8,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
repeatDelay: 0.6,
|
||||
}}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id={uid} gradientUnits="userSpaceOnUse" x1="0" y1="0" x2={w} y2="0">
|
||||
<stop offset="0%" stopColor="#3CC68A" stopOpacity="0" />
|
||||
<stop offset="30%" stopColor="#3CC68A" stopOpacity="1" />
|
||||
<stop offset="70%" stopColor="#1B9AD6" stopOpacity="1" />
|
||||
<stop offset="100%" stopColor="#1B9AD6" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
{/* Content */}
|
||||
{loading ? <Spinner size={size === 'lg' ? 'md' : 'sm'} /> : (leadingIcon ?? null)}
|
||||
{children}
|
||||
{!loading && trailingArrow && (
|
||||
<ArrowRightIcon
|
||||
size={arrowSize}
|
||||
className="shrink-0 transition-transform duration-200 group-hover:translate-x-0.5"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
@ -20,46 +20,36 @@ export const TextHoverEffect = ({
|
|||
const [maskPosition, setMaskPosition] = useState({ cx: '50%', cy: '50%' });
|
||||
|
||||
useEffect(() => {
|
||||
if (svgRef.current && cursor.x !== null && cursor.y !== null) {
|
||||
if (svgRef.current) {
|
||||
const svgRect = svgRef.current.getBoundingClientRect();
|
||||
const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;
|
||||
const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;
|
||||
setMaskPosition({
|
||||
cx: `${cxPercentage}%`,
|
||||
cy: `${cyPercentage}%`,
|
||||
});
|
||||
setMaskPosition({ cx: `${cxPercentage}%`, cy: `${cyPercentage}%` });
|
||||
}
|
||||
}, [cursor]);
|
||||
|
||||
// First word → blue (#1B9AD6), remaining → emerald (#3CC68A) — matches logo
|
||||
const [firstWord, ...rest] = text.split(' ');
|
||||
const secondWord = rest.join(' ');
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={svgRef}
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 300 100"
|
||||
viewBox="0 0 920 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })}
|
||||
className={cn('cursor-pointer uppercase select-none', className)}
|
||||
className={cn('cursor-pointer select-none', className)}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="textGradient" gradientUnits="userSpaceOnUse" cx="50%" cy="50%" r="25%">
|
||||
{hovered && (
|
||||
<>
|
||||
<stop offset="0%" stopColor="var(--color-emerald)" />
|
||||
<stop offset="35%" stopColor="var(--color-blue)" />
|
||||
<stop offset="65%" stopColor="var(--color-emerald)" />
|
||||
<stop offset="100%" stopColor="var(--color-emerald-dark)" />
|
||||
</>
|
||||
)}
|
||||
</linearGradient>
|
||||
|
||||
{/* @ts-expect-error — motion.radialGradient is valid framer-motion SVG element */}
|
||||
<motion.radialGradient
|
||||
id="revealMask"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20%"
|
||||
r="22%"
|
||||
initial={{ cx: '50%', cy: '50%' }}
|
||||
animate={maskPosition}
|
||||
transition={{ duration: duration ?? 0, ease: 'easeOut' }}
|
||||
|
|
@ -73,47 +63,76 @@ export const TextHoverEffect = ({
|
|||
</mask>
|
||||
</defs>
|
||||
|
||||
{/* Stroke outline — visible on hover */}
|
||||
{/* Base outlines — always visible at low opacity, correct brand colors */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
y="52%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.3"
|
||||
className="fill-transparent stroke-white/20 font-[helvetica] text-7xl font-bold"
|
||||
style={{ opacity: hovered ? 0.7 : 0 }}
|
||||
fontSize={68}
|
||||
fontFamily="'Satoshi', sans-serif"
|
||||
fontWeight={700}
|
||||
letterSpacing={3}
|
||||
fill="transparent"
|
||||
strokeWidth={0.5}
|
||||
>
|
||||
{text}
|
||||
<tspan stroke="#1B9AD6" strokeOpacity={0.3}>
|
||||
{firstWord}{' '}
|
||||
</tspan>
|
||||
<tspan stroke="#3CC68A" strokeOpacity={0.3}>
|
||||
{secondWord}
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
{/* Animated draw-on stroke */}
|
||||
<motion.text
|
||||
x="50%"
|
||||
y="50%"
|
||||
y="52%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.3"
|
||||
className="fill-transparent font-[helvetica] text-7xl font-bold"
|
||||
style={{ stroke: 'var(--color-emerald)' }}
|
||||
fontSize={68}
|
||||
fontFamily="'Satoshi', sans-serif"
|
||||
fontWeight={700}
|
||||
letterSpacing={3}
|
||||
fill="transparent"
|
||||
strokeWidth={0.6}
|
||||
initial={{ strokeDashoffset: 1000, strokeDasharray: 1000 }}
|
||||
animate={{ strokeDashoffset: 0, strokeDasharray: 1000 }}
|
||||
transition={{ duration: 4, ease: 'easeInOut' }}
|
||||
>
|
||||
{text}
|
||||
<tspan stroke="#1B9AD6">{firstWord} </tspan>
|
||||
<tspan stroke="#3CC68A">{secondWord}</tspan>
|
||||
</motion.text>
|
||||
|
||||
{/* Colour reveal on mouse move */}
|
||||
{/* Hover fill reveal under radial cursor mask */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
y="52%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
stroke="url(#textGradient)"
|
||||
strokeWidth="0.3"
|
||||
fontSize={68}
|
||||
fontFamily="'Satoshi', sans-serif"
|
||||
fontWeight={700}
|
||||
letterSpacing={3}
|
||||
strokeWidth={0.5}
|
||||
mask="url(#textMask)"
|
||||
className="fill-transparent font-[helvetica] text-7xl font-bold"
|
||||
>
|
||||
{text}
|
||||
<tspan
|
||||
fill="#1B9AD6"
|
||||
stroke="#1B9AD6"
|
||||
fillOpacity={hovered ? 0.3 : 0}
|
||||
style={{ transition: 'fill-opacity 0.3s' }}
|
||||
>
|
||||
{firstWord}{' '}
|
||||
</tspan>
|
||||
<tspan
|
||||
fill="#3CC68A"
|
||||
stroke="#3CC68A"
|
||||
fillOpacity={hovered ? 0.3 : 0}
|
||||
style={{ transition: 'fill-opacity 0.3s' }}
|
||||
>
|
||||
{secondWord}
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -124,7 +143,7 @@ export const FooterBackgroundGradient = () => (
|
|||
className="absolute inset-0 z-0"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(125% 125% at 50% 10%, rgba(22,37,32,0.6) 50%, rgba(27,154,214,0.15) 100%)',
|
||||
'radial-gradient(110% 110% at 50% 0%, rgba(22,37,32,0.8) 40%, rgba(27,154,214,0.1) 100%)',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue