Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
// - Git
"git.autofetch": true,

// - TypeScript
"typescript.updateImportsOnFileMove.enabled": "always",

// - Actions on save
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},

// - Lint and format
"editor.formatOnSave": true,
"eslint.format.enable": true,
"eslint.validate": [
"javascript"
],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "stylelint.vscode-stylelint"
},

// - Common
"editor.tabSize": 2,
}
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Provider } from "react-wrap-balancer";
import Footer from "@/components/layout/footer/footer";
import Script from "next/script";
import { Toaster } from "@/components/ui/toaster";
import { Figtree } from "next/font/google"
import { Figtree } from "next/font/google";
import { OrganizationSchema } from "@/components/json-ld/organization-schema";

export const figtree = Figtree({
Expand Down
50 changes: 50 additions & 0 deletions src/components/marketing/landing/testimonial/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import Image from "next/image";

export interface TestimonialProps {
quote: string;
author: string;
role: string;
company: string;
imageSrc: string;
imageAlt?: string;
}

const Testimonial: React.FC<TestimonialProps> = ({
quote,
author,
role,
company,
imageSrc,
imageAlt = `Photo ${author}`,
}) => {
return (
<section className="max-w-7xl mx-auto px-4 py-8 md:py-16">
<div className="flex flex-col md:flex-row justify-center items-center gap-8 md:gap-16 max-w-5xl mx-auto">
{/* Image - centrée sur mobile, à gauche sur desktop */}
<div className="flex-shrink-0 order-1 md:order-none">
<Image
src={imageSrc}
alt={imageAlt}
width={120}
height={120}
className="w-24 h-24 sm:w-32 sm:h-32 md:w-36 md:h-36 lg:w-[150px] lg:h-[150px] rounded-lg rotate-[-10deg] md:rotate-[-15deg] shadow-lg bg-white px-2 pt-2 pb-4 md:pb-6 transition-all duration-300 ease-in-out hover:rotate-0 hover:scale-105 mx-auto md:mx-0"
/>
</div>

{/* Contenu texte */}
<div className="flex-1 text-center md:text-left order-2 md:order-none">
<p className="text-lg sm:text-xl md:text-2xl lg:text-3xl font-medium text-black leading-relaxed mb-4 md:mb-6">
&ldquo;{quote}&rdquo;
</p>
<p className="text-base md:text-lg text-gray-600">
<span className="font-semibold">{author}</span>, {role} @{" "}
<strong className="text-black">{company}</strong>
</p>
</div>
</div>
</section>
);
};

export default Testimonial;
140 changes: 135 additions & 5 deletions src/components/marketing/landing/visitor/featured/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,139 @@ const Featured: React.FC = () => {
/>
</div>

<div className="relative mt-12 rounded-sm overflow-hidden">
<div className="relative mt-12 rounded-sm">
{/* SVG décoratif gauche - visible uniquement desktop */}
<div className="absolute -top-20 left-20 z-[-10] pointer-events-none hidden md:block">
<svg
width="150"
height="150"
viewBox="0 0 171 171"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="rotate-[-15deg]"
>
<g filter="url(#filter0_d_2071_266)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M55.2564 52.8785C44.3091 56.2504 38.168 67.8584 41.5399 78.8057L52.8784 115.618C56.2503 126.565 67.8583 132.706 78.8055 129.334L115.617 117.995C126.565 114.624 132.706 103.016 129.334 92.0684L117.995 55.2565C114.623 44.3092 103.016 38.1682 92.0683 41.54L55.2564 52.8785ZM93.7871 63.9959C90.8942 62.4654 87.3083 63.5699 85.7779 66.4628L69.9428 96.3946C68.4124 99.2875 69.5168 102.873 72.4097 104.404L77.0866 106.878C79.9795 108.409 83.5653 107.304 85.0958 104.411L100.931 74.4793C102.461 71.5864 101.357 68.0005 98.4639 66.4701L93.7871 63.9959Z"
fill="#111111"
/>
<path
d="M90.5104 36.4805C104.252 32.2483 118.822 39.9572 123.055 53.6986L134.393 90.5105C138.626 104.252 130.917 118.823 117.175 123.055L80.3643 134.393C66.6227 138.626 52.0514 130.917 47.8189 117.175L36.4804 80.3635C32.2481 66.6221 39.957 52.0515 53.6985 47.819L90.5104 36.4805ZM91.3109 68.6752C91.0026 68.5124 90.62 68.63 90.4569 68.9382L74.6219 98.8701C74.4588 99.1783 74.5768 99.5607 74.8849 99.724L79.5626 102.198C79.871 102.361 80.2524 102.244 80.4156 101.935L96.2516 72.0031C96.4143 71.6947 96.2968 71.3122 95.9886 71.1491L91.3109 68.6752Z"
stroke="#F7F7F7"
strokeWidth="10.5882"
/>
</g>
<defs>
<filter
id="filter0_d_2071_266"
x="0.0244141"
y="0.0245361"
width="170.825"
height="170.825"
filterUnits="userSpaceOnUse"
colorInterpolationFilters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset />
<feGaussianBlur stdDeviation="15" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_2071_266"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_2071_266"
result="shape"
/>
</filter>
</defs>
</svg>
</div>

{/* SVG décoratif droite - visible uniquement desktop */}
<div className="absolute -top-20 right-20 z-[-10] pointer-events-none hidden md:block">
<svg
width="150"
height="150"
viewBox="0 0 174 174"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="rotate-[-15deg]"
>
<g filter="url(#filter0_d_2071_267)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M84.9995 42.6393C74.4663 38.1378 62.2783 43.0274 57.7767 53.5605L42.6395 88.9801C38.1379 99.5133 43.0275 111.701 53.5607 116.203L88.9802 131.34C99.5134 135.842 111.701 130.952 116.203 120.419L131.34 84.9994C135.842 74.4662 130.952 62.2781 120.419 57.7765L84.9995 42.6393ZM107.219 76.0239C106 72.9864 102.55 71.5119 99.5123 72.7305L68.0846 85.3386C65.0471 86.5572 63.5726 90.0074 64.7912 93.0449L66.7612 97.9554C67.9798 100.993 71.43 102.467 74.4675 101.249L105.895 88.6407C108.933 87.4222 110.407 83.9719 109.189 80.9345L107.219 76.0239Z"
fill="#2294FF"
/>
<path
d="M122.5 52.9087C135.721 58.5595 141.859 73.8586 136.208 87.0803L121.071 122.5C115.42 135.721 100.121 141.858 86.8993 136.208L51.4807 121.071C38.2589 115.421 32.121 100.121 37.7716 86.8992L52.9089 51.4797C58.5596 38.2581 73.8587 32.121 87.0804 37.7715L122.5 52.9087ZM102.305 77.9945C102.175 77.6711 101.807 77.5135 101.483 77.6434L70.0555 90.2515C69.7319 90.3814 69.5749 90.7494 69.7044 91.0732L71.6753 95.984C71.8052 96.3079 72.1722 96.4647 72.496 96.3348L103.925 83.727C104.248 83.5969 104.406 83.229 104.276 82.9054L102.305 77.9945Z"
stroke="#F7F7F7"
strokeWidth="10.5882"
/>
</g>
<defs>
<filter
id="filter0_d_2071_267"
x="0.374512"
y="0.37439"
width="173.231"
height="173.231"
filterUnits="userSpaceOnUse"
colorInterpolationFilters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset />
<feGaussianBlur stdDeviation="15" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_2071_267"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_2071_267"
result="shape"
/>
</filter>
</defs>
</svg>
</div>

<Image
src={"/static/images/onruntime-team.jpg"}
alt={"Équipe onRuntime Studio"}
width={1024}
height={510}
className="z-[-1] max-h-[510px] object-cover"
className="relative z-0 max-h-[510px] object-cover"
priority
sizes="100vw"
/>
Expand All @@ -79,12 +205,16 @@ const Featured: React.FC = () => {
intéressés par les nouvelles technologies et le monde de la
création (design, musique, etc.). À partir de là nous avons créé
un Studio et une communauté autour de celui-ci, nous adorons
nous retrouver autour d&apos;une tasse de café ☕️ pour
discuter, jouer et surtout créer des projets.
nous retrouver autour d&apos;une tasse de café ☕️ pour discuter,
jouer et surtout créer des projets.
</Balancer>
</p>

<OnRuntimeWordMark fill="white" height={24} className="self-end h-6" />
<OnRuntimeWordMark
fill="white"
height={24}
className="self-end h-6"
/>
</div>
</div>

Expand Down
108 changes: 108 additions & 0 deletions src/components/marketing/landing/visitor/reviews/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from "react";
import Image from "next/image";
import Testimonial from "../../testimonial";

// Données extraites pour éviter la répétition
const reviewsData = [
{
id: 1,
text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.",
author: "Cécile Pislor",
role: "CEO @ DroitAuCoeurCoaching",
avatar: "/favicon-32x32.png",
},
{
id: 2,
text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.",
author: "Cécile Pislor",
role: "CEO @ DroitAuCoeurCoaching",
avatar: "/favicon-32x32.png",
},
{
id: 3,
text: "Composé de talents variés, allant de développeurs à des designers passionnés. Chacun apporte sa touche unique à nos projets. Ensemble, nous formons un collectif où l'innovation est au cœur de tout ce que nous entreprenons.",
author: "Cécile Pislor",
role: "CEO @ DroitAuCoeurCoaching",
avatar: "/favicon-32x32.png",
},
];

const Reviews: React.FC = () => {
return (
<section className="pt-[4em] md:pt-[15em] py-8 md:py-16 px-4 relative min-h-screen">
<div className="max-w-7xl mx-auto relative">
{/* Titre sticky qui reste derrière les commentaires */}
<div className="sticky top-16 md:top-20 text-center z-0 mb-8 md:mb-16">
<h2 className="text-black font-semibold text-2xl sm:text-3xl md:text-4xl mb-2 md:mb-4 pointer-events-none px-4">
Ce que nos experts disent de nous.
</h2>
<p className="text-gray-600 text-base md:text-lg pointer-events-none px-4">
On ne les a pas du tout forcés à parler.
</p>
</div>

{/* Grille des avis qui défile devant le titre */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 md:gap-8 relative z-10">
{reviewsData.map((review, index) => {
const getTransform = (index: number) => {
switch (index) {
case 0:
return "translate-y-0";
case 1:
return "sm:translate-y-8 md:translate-y-[30rem]"; // Moins de décalage sur mobile
case 2:
return "sm:translate-y-4 md:translate-y-32"; // Moins de décalage sur mobile
default:
return "translate-y-0";
}
};

return (
<article
key={review.id}
className={`bg-white rounded-lg p-4 sm:p-5 md:p-6 shadow-sm hover:shadow-md transition-all duration-300 ${getTransform(index)} z-10 relative`}
>
<blockquote className="mb-4 md:mb-6">
<p className="text-gray-700 italic leading-relaxed text-sm sm:text-base">
&ldquo;{review.text}&rdquo;
</p>
</blockquote>
<div className="flex items-center gap-3 md:gap-4">
<Image
src={review.avatar}
alt={`Photo de ${review.author}`}
width={50}
height={50}
className="rounded-full w-12 h-12 sm:w-14 sm:h-14 md:w-15 md:h-15 object-cover"
/>
<div className="min-w-0 flex-1">
<h3 className="font-semibold text-base sm:text-lg text-gray-900 truncate">
{review.author}
</h3>
<p className="text-xs sm:text-sm text-gray-600 line-clamp-2">
{review.role}
</p>
</div>
</div>
</article>
);
})}
</div>

{/* Spacer responsive */}
<div className="h-[2rem] sm:h-[30rem] md:h-[40rem]"></div>
</div>

{/* Témoignage Lucas Bodin */}
<Testimonial
quote="onRuntime Studio c'est bien plus qu'un simple studio de créateurs. C'est un espace où l'innovation, la collaboration, et la passion se lient pour donner vie à des projets uniques. Nous croyons au pouvoir de l'intelligence collective, où chaque créateur, apporte sa vision afin qu'ensemble, nous transformions des idées en réalités."
author="Lucas Bodin"
role="Head of Design"
company="onRuntime Studio"
imageSrc="/static/images/members/lucas-bodin.jpg"
/>
</section>
);
};

export default Reviews;
Loading