Compare commits
8 Commits
v2.2.45-rc
...
v2.2.45
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f2509c3fc | |||
| 41a3c10cf8 | |||
| 103e4e63ed | |||
| 34860cab18 | |||
| 5105f449b5 | |||
| 1d07e47e5d | |||
| 3d48359768 | |||
| d9c2ea1ede |
@@ -44,8 +44,9 @@ export async function generateMetadata(props: {
|
|||||||
alternates: {
|
alternates: {
|
||||||
canonical: `${baseUrl}/${locale}`,
|
canonical: `${baseUrl}/${locale}`,
|
||||||
languages: {
|
languages: {
|
||||||
de: `${baseUrl}/de`,
|
'de': `${baseUrl}/de`,
|
||||||
en: `${baseUrl}/en`,
|
'en': `${baseUrl}/en`,
|
||||||
|
'x-default': `${baseUrl}/de`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -60,6 +61,9 @@ export const viewport: Viewport = {
|
|||||||
themeColor: '#117c61',
|
themeColor: '#117c61',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
return [{ locale: 'de' }, { locale: 'en' }];
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Layout(props: {
|
export default async function Layout(props: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -148,19 +152,9 @@ export default async function Layout(props: {
|
|||||||
const serverServices = getServerAppServices();
|
const serverServices = getServerAppServices();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { headers } = await import('next/headers');
|
|
||||||
const requestHeaders = await headers();
|
|
||||||
|
|
||||||
// Disable analytics in CI to prevent console noise/score penalties
|
// Disable analytics in CI to prevent console noise/score penalties
|
||||||
if (process.env.NEXT_PUBLIC_CI === 'true') {
|
if (process.env.NEXT_PUBLIC_CI === 'true') {
|
||||||
// Skip setting server context for analytics in CI
|
// Skip setting server context for analytics in CI
|
||||||
} else if ('setServerContext' in serverServices.analytics) {
|
|
||||||
(serverServices.analytics as any).setServerContext({
|
|
||||||
userAgent: requestHeaders.get('user-agent') || undefined,
|
|
||||||
language: requestHeaders.get('accept-language')?.split(',')[0] || undefined,
|
|
||||||
referrer: requestHeaders.get('referer') || undefined,
|
|
||||||
ip: requestHeaders.get('x-forwarded-for')?.split(',')[0] || undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server-side analytics tracking removed to prevent duplicate/empty events.
|
// Server-side analytics tracking removed to prevent duplicate/empty events.
|
||||||
@@ -182,13 +176,14 @@ export default async function Layout(props: {
|
|||||||
lang={safeLocale}
|
lang={safeLocale}
|
||||||
className={`scroll-smooth overflow-x-hidden ${inter.variable}`}
|
className={`scroll-smooth overflow-x-hidden ${inter.variable}`}
|
||||||
data-scroll-behavior="smooth"
|
data-scroll-behavior="smooth"
|
||||||
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
<head>
|
<head>
|
||||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
<link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180" />
|
<link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body className="relative flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white">
|
<body className="relative flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white" suppressHydrationWarning>
|
||||||
<NextIntlClientProvider messages={clientMessages} locale={safeLocale}>
|
<NextIntlClientProvider messages={clientMessages} locale={safeLocale}>
|
||||||
<TransitionProvider>
|
<TransitionProvider>
|
||||||
<CorporateBackground />
|
<CorporateBackground />
|
||||||
|
|||||||
@@ -37,6 +37,41 @@ const mdxComponents = (references: any[]) => ({
|
|||||||
AnimatedCounter,
|
AnimatedCounter,
|
||||||
InteractiveGermanyMap,
|
InteractiveGermanyMap,
|
||||||
ScaleOfImpact,
|
ScaleOfImpact,
|
||||||
|
ResponsiveImage: (props: any) => {
|
||||||
|
// Parse src if it's already a next image proxy
|
||||||
|
let src = props.src;
|
||||||
|
if (typeof src === 'string' && src.startsWith('/_next/image')) {
|
||||||
|
try {
|
||||||
|
const urlParam = new URLSearchParams(src.split('?')[1]).get('url');
|
||||||
|
if (urlParam) src = decodeURIComponent(urlParam);
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Next.js getImageProps for responsive srcSet generation
|
||||||
|
const {
|
||||||
|
props: { srcSet, src: finalSrc, sizes }
|
||||||
|
} = getImageProps({
|
||||||
|
src,
|
||||||
|
alt: props.alt || '',
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
sizes: '(max-width: 768px) 100vw, (max-width: 1200px) 75vw, 50vw',
|
||||||
|
quality: 80,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
src={finalSrc}
|
||||||
|
srcSet={srcSet}
|
||||||
|
sizes={sizes}
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
img: (props: any) => {
|
img: (props: any) => {
|
||||||
// Parse src if it's already a next image proxy
|
// Parse src if it's already a next image proxy
|
||||||
let src = props.src;
|
let src = props.src;
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ export default function manifest(): MetadataRoute.Manifest {
|
|||||||
background_color: '#001a4d',
|
background_color: '#001a4d',
|
||||||
theme_color: '#001a4d',
|
theme_color: '#001a4d',
|
||||||
icons: [
|
icons: [
|
||||||
{
|
|
||||||
src: '/favicon.ico',
|
|
||||||
sizes: 'any',
|
|
||||||
type: 'image/x-icon',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
src: '/logo.png',
|
src: '/logo.png',
|
||||||
sizes: '512x512',
|
sizes: '512x512',
|
||||||
|
|||||||
@@ -51,15 +51,14 @@ describe('ReferencesSlider TDD', () => {
|
|||||||
expect(screen.getByText('Referenz Projekt Zwei')).toBeTruthy();
|
expect(screen.getByText('Referenz Projekt Zwei')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders each tile as a link to the reference detail page', () => {
|
it('renders each tile as a static block and NOT wrapped in a link', () => {
|
||||||
render(<ReferencesSlider references={mockReferences} />);
|
render(<ReferencesSlider references={mockReferences} />);
|
||||||
|
|
||||||
|
const links = screen.queryAllByTestId('reference-tile-link');
|
||||||
|
expect(links).toHaveLength(0);
|
||||||
|
|
||||||
const tiles = screen.getAllByTestId('reference-tile');
|
const tiles = screen.getAllByTestId('reference-tile');
|
||||||
expect(tiles).toHaveLength(2);
|
expect(tiles).toHaveLength(2);
|
||||||
|
|
||||||
// Check if they have href attributes
|
|
||||||
expect(tiles[0].getAttribute('href')).toContain('/en/referenzen#referenz-projekt-eins');
|
|
||||||
expect(tiles[1].getAttribute('href')).toContain('/en/referenzen#referenz-projekt-zwei');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,27 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
|||||||
|
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const references = props.references || [];
|
const references = props.references || [];
|
||||||
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
const dragDistanceRef = React.useRef(0);
|
||||||
|
const startXRef = React.useRef(0);
|
||||||
|
const scrollLeftRef = React.useRef(0);
|
||||||
|
const isDraggingRef = React.useRef(false);
|
||||||
|
const [isDragging, setIsDragging] = React.useState(false);
|
||||||
|
|
||||||
|
// Center the first card on mount so it appears in the middle with peek on both sides
|
||||||
|
React.useEffect(() => {
|
||||||
|
const el = containerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
// Wait for layout to settle
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const firstCard = el.querySelector('[data-card]') as HTMLElement | null;
|
||||||
|
if (!firstCard) return;
|
||||||
|
const cardCenter = firstCard.offsetLeft + firstCard.offsetWidth / 2;
|
||||||
|
const containerCenter = el.offsetWidth / 2;
|
||||||
|
el.scrollLeft = cardCenter - containerCenter;
|
||||||
|
});
|
||||||
|
}, [references.length]);
|
||||||
|
|
||||||
const badge = props.badge || data?.badge || t('badge');
|
const badge = props.badge || data?.badge || t('badge');
|
||||||
const title = props.title || data?.title || t('title');
|
const title = props.title || data?.title || t('title');
|
||||||
const description = props.description || data?.description || t('description');
|
const description = props.description || data?.description || t('description');
|
||||||
@@ -57,71 +77,121 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
|||||||
'/assets/photos/DSC00010.JPG',
|
'/assets/photos/DSC00010.JPG',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const onMouseDown = (e: React.MouseEvent) => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
setIsDragging(true);
|
||||||
|
isDraggingRef.current = true;
|
||||||
|
const startXVal = e.clientX - (containerRef.current.offsetLeft || 0);
|
||||||
|
startXRef.current = startXVal;
|
||||||
|
scrollLeftRef.current = containerRef.current.scrollLeft || 0;
|
||||||
|
dragDistanceRef.current = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseLeave = () => {
|
||||||
|
setIsDragging(false);
|
||||||
|
isDraggingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
setIsDragging(false);
|
||||||
|
isDraggingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e: React.MouseEvent) => {
|
||||||
|
if (!isDraggingRef.current || !containerRef.current) return;
|
||||||
|
e.preventDefault();
|
||||||
|
const x = e.clientX - (containerRef.current.offsetLeft || 0);
|
||||||
|
const walk = (x - startXRef.current) * 2; // Scroll-fast
|
||||||
|
containerRef.current.scrollLeft = scrollLeftRef.current - walk;
|
||||||
|
dragDistanceRef.current = Math.abs(x - startXRef.current);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="referenzen" className="pt-16 pb-8 md:py-24 lg:py-32 bg-neutral-dark text-white relative overflow-hidden">
|
<section id="referenzen" className="pt-16 pb-8 md:py-24 lg:py-32 bg-neutral-dark text-white relative overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-gradient-to-b from-neutral-dark via-neutral-900 to-neutral-dark z-0" />
|
<div className="absolute inset-0 bg-gradient-to-b from-neutral-dark via-neutral-900 to-neutral-dark z-0" />
|
||||||
|
|
||||||
<div className="container relative z-10 mb-12 flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
|
<div className="container relative z-10 mb-12 flex flex-col items-center justify-center text-center gap-6">
|
||||||
<div className="text-left">
|
<div>
|
||||||
<h2 className="text-primary-light font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
|
<h2 className="text-primary-light font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
|
||||||
<h3 className="font-heading text-3xl md:text-5xl font-extrabold">{title}</h3>
|
<h3 className="font-heading text-3xl md:text-5xl font-extrabold">{title}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left max-w-3xl mx-auto md:mx-0 md:mb-16">
|
<div className="max-w-3xl mx-auto mb-4 md:mb-8 text-neutral-300">
|
||||||
{description}
|
{description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container relative z-10 w-full">
|
<div className="relative z-10">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8 justify-center items-stretch">
|
{/* Fade edges for peek effect */}
|
||||||
{references.map((ref, i) => {
|
<div className="absolute left-0 top-0 bottom-0 w-16 bg-gradient-to-r from-neutral-dark to-transparent z-20 pointer-events-none" />
|
||||||
const imgSrc = ref.image
|
<div className="absolute right-0 top-0 bottom-0 w-16 bg-gradient-to-l from-neutral-dark to-transparent z-20 pointer-events-none" />
|
||||||
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
|
|
||||||
: fallbacks[i % fallbacks.length];
|
{/* Carousel: centered first card with peek on both sides */}
|
||||||
|
<LazyMotion features={domAnimation}>
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
onMouseUp={onMouseUp}
|
||||||
|
onMouseMove={onMouseMove}
|
||||||
|
className={`select-none flex gap-6 overflow-x-auto pb-8 pt-4 px-[calc(50vw-160px)] md:px-[calc(50vw-240px)] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||||
|
>
|
||||||
|
{references.map((ref, i) => {
|
||||||
|
const imgSrc = ref.image
|
||||||
|
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
|
||||||
|
: fallbacks[i % fallbacks.length];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LazyMotion key={ref.id} features={domAnimation}>
|
|
||||||
<m.div
|
<m.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
key={ref.id}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
data-card
|
||||||
|
initial={{ opacity: 0, x: 20 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
viewport={{ once: true, margin: "-50px" }}
|
viewport={{ once: true, margin: "-50px" }}
|
||||||
transition={{ delay: i * 0.1, duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
transition={{ delay: i * 0.1, duration: 1.0, ease: [0.16, 1, 0.3, 1] }}
|
||||||
className="w-full h-full group"
|
className="flex-shrink-0 w-[320px] md:w-[480px] snap-center group pointer-events-auto"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/${locale}/referenzen#${ref.slug}`}
|
href={`/${locale}/referenzen#${ref.slug}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (dragDistanceRef.current > 5) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onDragStart={(e) => e.preventDefault()}
|
||||||
|
draggable={false}
|
||||||
data-testid="reference-tile"
|
data-testid="reference-tile"
|
||||||
className="block relative h-[250px] md:h-[300px] w-full rounded-2xl overflow-hidden shadow-xl shadow-black/20"
|
className="block relative select-none aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl cursor-pointer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
draggable={false}
|
||||||
src={imgSrc}
|
src={imgSrc}
|
||||||
alt={ref.title}
|
alt={ref.title}
|
||||||
fill
|
fill
|
||||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
sizes="(max-width: 768px) 320px, 480px"
|
||||||
className="object-cover object-center group-hover:scale-105 transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]"
|
className="object-cover saturate-50 opacity-70 group-hover:scale-105 group-hover:opacity-100 group-hover:saturate-100 transition-all duration-700 ease-in-out pointer-events-none"
|
||||||
priority={i < 3}
|
|
||||||
/>
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/30 to-transparent opacity-80 group-hover:opacity-90 transition-opacity duration-300 pointer-events-none" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-[#050B14]/90 via-[#050B14]/20 to-transparent pointer-events-none" />
|
|
||||||
|
|
||||||
<HoverShineOverlay />
|
<HoverShineOverlay />
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 p-6 md:p-8 right-0 transform transition-transform duration-500 ease-out group-hover:-translate-y-2">
|
<div className="absolute bottom-6 left-6 right-6 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-500 pointer-events-none bg-black/60 backdrop-blur-sm p-4 rounded-xl border border-white/10">
|
||||||
<div className="bg-primary/20 backdrop-blur-md border border-primary/30 text-white text-[10px] md:text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full mb-3 inline-block shadow-[0_0_15px_rgba(14,122,92,0.3)]">
|
<span className="inline-block py-1 px-3 rounded-full bg-primary/20 text-white text-[10px] font-bold uppercase tracking-widest mb-2 border border-primary/30">
|
||||||
{ref.category}
|
{ref.category}
|
||||||
</div>
|
</span>
|
||||||
<h4 className="text-xl md:text-2xl font-bold font-heading leading-tight drop-shadow-md text-white group-hover:text-primary-light transition-colors duration-300">
|
<h4 className="font-heading text-xl md:text-2xl font-bold leading-tight break-words">{ref.title}</h4>
|
||||||
{ref.title}
|
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</m.div>
|
</m.div>
|
||||||
</LazyMotion>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
</LazyMotion>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container relative z-10 flex justify-center mt-12 md:mt-20 pb-4">
|
<div className="container relative z-10 mt-4 flex justify-center">
|
||||||
<Link
|
<Link
|
||||||
href={ctaHref}
|
href={ctaHref}
|
||||||
className="px-8 py-4 rounded-xl bg-white/5 border border-white/20 hover:bg-white hover:text-neutral-dark hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] transition-all font-bold flex items-center gap-3 backdrop-blur-sm"
|
className="px-8 py-4 rounded-xl bg-white/5 border border-white/20 hover:bg-white hover:text-neutral-dark hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] transition-all font-bold flex items-center gap-3 backdrop-blur-sm"
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
{/* Right Column: Imagery & Mission */}
|
{/* Right Column: Imagery & Mission */}
|
||||||
<div className="lg:col-span-7 relative">
|
<div className="lg:col-span-7 relative">
|
||||||
<div className="aspect-[4/3] md:aspect-[16/9] lg:aspect-[4/3] rounded-3xl overflow-hidden shadow-2xl relative border border-neutral-200/50">
|
<div className="aspect-[4/3] md:aspect-[16/9] lg:aspect-[4/3] rounded-3xl overflow-hidden shadow-2xl relative border border-neutral-200/50">
|
||||||
<img src="/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg" alt="E-TIB im Einsatz" className="w-full h-full object-cover" loading="lazy" decoding="async" />
|
<ResponsiveImage src="/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg" alt="E-TIB im Einsatz" className="w-full h-full object-cover" loading="lazy" decoding="async" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark/95 via-neutral-dark/40 to-transparent"></div>
|
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark/95 via-neutral-dark/40 to-transparent"></div>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 w-full p-8 md:p-12">
|
<div className="absolute bottom-0 left-0 w-full p-8 md:p-12">
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
{/* Right Column: Imagery & Mission */}
|
{/* Right Column: Imagery & Mission */}
|
||||||
<div className="lg:col-span-7 relative">
|
<div className="lg:col-span-7 relative">
|
||||||
<div className="aspect-[4/3] md:aspect-[16/9] lg:aspect-[4/3] rounded-3xl overflow-hidden shadow-2xl relative border border-neutral-200/50">
|
<div className="aspect-[4/3] md:aspect-[16/9] lg:aspect-[4/3] rounded-3xl overflow-hidden shadow-2xl relative border border-neutral-200/50">
|
||||||
<img src="/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg" alt="E-TIB in action" className="w-full h-full object-cover" loading="lazy" decoding="async" />
|
<ResponsiveImage src="/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg" alt="E-TIB in action" className="w-full h-full object-cover" loading="lazy" decoding="async" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark/95 via-neutral-dark/40 to-transparent"></div>
|
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark/95 via-neutral-dark/40 to-transparent"></div>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 w-full p-8 md:p-12">
|
<div className="absolute bottom-0 left-0 w-full p-8 md:p-12">
|
||||||
|
|||||||
@@ -138,7 +138,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.2.44",
|
"version": "2.2.45",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
BIN
public/apple-touch-icon.png
Normal file
BIN
public/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user