Apply website QA: light theme, clearer contact form, office emails

- Add a light theme with a header toggle; the choice is remembered and applied before paint
- Make the globe, product visuals and footer logo follow the theme
- Give the message field a full outline, strengthen the captcha box border and keep autofilled fields readable
- Accept website addresses without https://
- List the info email under every office
- Keep the phone header on one row: languages move into the menu below 480px
This commit is contained in:
Alejandro Veras
2026-10-16 11:56:10 -04:00
parent 50c5868da6
commit 2d7e7f2fe1
15 changed files with 240 additions and 54 deletions
+9 -1
View File
@@ -5,6 +5,7 @@ import "../globals.css";
import SiteHeader from "@/components/site-header";
import SiteFooter from "@/components/site-footer";
import Analytics, { AnalyticsNoScript } from "@/components/analytics";
import { themeScript } from "@/components/theme-toggle";
import { dirOf, locales, localize, ogLocale } from "@/i18n/config";
import { mainNav } from "@/i18n/nav";
import { getUI } from "@/i18n/server";
@@ -51,7 +52,7 @@ export async function generateMetadata(): Promise<Metadata> {
export const viewport: Viewport = {
themeColor: "#000000",
colorScheme: "dark",
colorScheme: "dark light",
};
export default async function RootLayout({ children }: LayoutProps<"/[lang]">) {
@@ -63,6 +64,8 @@ export default async function RootLayout({ children }: LayoutProps<"/[lang]">) {
closeMenu: t.nav.closeMenu,
language: t.nav.language,
logoHome: t.logoHome,
lightTheme: t.nav.lightTheme,
darkTheme: t.nav.darkTheme,
};
return (
@@ -71,7 +74,12 @@ export default async function RootLayout({ children }: LayoutProps<"/[lang]">) {
dir={dirOf(locale)}
className={`${inter.variable} ${arabic.variable}`}
data-scroll-behavior="smooth"
// The theme script sets data-theme before React hydrates.
suppressHydrationWarning
>
<head>
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
</head>
<body>
<AnalyticsNoScript />
<a href="#main" className="skip-link">
+133 -33
View File
@@ -13,6 +13,13 @@
--fumo: #8f8f8f;
--piombo: #666666;
--argento: #d2d2d2;
/* The names keep their roles in both themes: bianco is ink, nero is paper. */
--ink-rgb: 255, 255, 255;
--paper-rgb: 0, 0, 0;
--cap-line: #262626;
--panel: #070707;
--panel-line: #1c1c1c;
color-scheme: dark;
--font-sans: var(--font-inter), "Helvetica Neue", Arial, sans-serif;
@@ -28,6 +35,23 @@
--dir: -1;
}
/* Light theme, chosen with the header toggle. */
:root[data-theme="light"] {
--bianco: #0a0a0a;
--nero: #ffffff;
--notte: #f4f4f2;
--grafite: #d9d9d6;
--fumo: #5c5c5c;
--piombo: #808080;
--argento: #333333;
--ink-rgb: 10, 10, 10;
--paper-rgb: 255, 255, 255;
--cap-line: #e2e2e2;
--panel: #f7f7f5;
--panel-line: #e4e4e1;
color-scheme: light;
}
*,
*::before,
*::after {
@@ -55,7 +79,7 @@ body {
::selection {
background: var(--rosso);
color: var(--bianco);
color: #fff;
}
a {
@@ -220,7 +244,7 @@ a {
.btn-ghost {
background: transparent;
color: var(--bianco);
border-color: rgba(255, 255, 255, 0.35);
border-color: rgba(var(--ink-rgb), 0.35);
}
.btn-ghost:hover {
@@ -261,7 +285,7 @@ a {
align-items: center;
height: 30px;
padding: 0 14px;
border: 1px solid rgba(255, 255, 255, 0.28);
border: 1px solid rgba(var(--ink-rgb), 0.28);
border-radius: 9999px;
font-size: 10px;
font-weight: 500;
@@ -303,7 +327,7 @@ a {
}
.site-header.is-scrolled {
background: rgba(0, 0, 0, 0.82);
background: rgba(var(--paper-rgb), 0.82);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border-color: var(--grafite);
@@ -350,6 +374,7 @@ a {
}
.logo-type b {
white-space: nowrap;
font-size: 14px;
font-weight: 600;
letter-spacing: 0.2em;
@@ -376,7 +401,7 @@ a {
font-weight: 500;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.78);
color: rgba(var(--ink-rgb), 0.78);
transition: color 0.3s var(--ease);
}
@@ -512,9 +537,9 @@ a {
inset: 0;
z-index: -1;
background:
linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, transparent 22%),
linear-gradient(0deg, #000 4%, rgba(0, 0, 0, 0.6) 34%, transparent 62%),
linear-gradient(90deg, rgba(0, 0, 0, 0.7) 0%, transparent 55%);
linear-gradient(180deg, rgba(var(--paper-rgb), 0.55) 0%, transparent 22%),
linear-gradient(0deg, var(--nero) 4%, rgba(var(--paper-rgb), 0.6) 34%, transparent 62%),
linear-gradient(90deg, rgba(var(--paper-rgb), 0.7) 0%, transparent 55%);
pointer-events: none;
}
@@ -858,13 +883,13 @@ a {
}
.cap-map line {
stroke: #262626;
stroke: var(--cap-line);
stroke-width: 1;
transition: stroke 0.5s var(--ease);
}
.cap-map line.on {
stroke: rgba(255, 255, 255, 0.6);
stroke: rgba(var(--ink-rgb), 0.6);
}
.cap-map .node {
@@ -882,7 +907,7 @@ a {
.cap-map .core {
fill: var(--nero);
stroke: rgba(255, 255, 255, 0.4);
stroke: rgba(var(--ink-rgb), 0.4);
}
.cap-map text {
@@ -1002,8 +1027,8 @@ a {
grid-column: 1 / span 6;
position: relative;
aspect-ratio: 600 / 412;
background: #070707;
outline: 1px solid #1c1c1c;
background: var(--panel);
outline: 1px solid var(--panel-line);
overflow: hidden;
}
@@ -1071,7 +1096,7 @@ a {
/* journey viz */
.viz-journey .path {
fill: none;
stroke: rgba(255, 255, 255, 0.16);
stroke: rgba(var(--ink-rgb), 0.16);
stroke-width: 1;
}
@@ -1099,7 +1124,7 @@ a {
.viz-journey .node {
fill: var(--nero);
stroke: rgba(255, 255, 255, 0.6);
stroke: rgba(var(--ink-rgb), 0.6);
}
.viz-journey .goal {
@@ -1117,11 +1142,11 @@ a {
/* radar viz */
.viz-radar .ring {
fill: none;
stroke: rgba(255, 255, 255, 0.12);
stroke: rgba(var(--ink-rgb), 0.12);
}
.viz-radar .axis {
stroke: rgba(255, 255, 255, 0.07);
stroke: rgba(var(--ink-rgb), 0.07);
}
.viz-radar .sweep {
@@ -1160,12 +1185,12 @@ a {
/* grid viz */
.viz-grid .cell {
fill: rgba(255, 255, 255, 0.035);
stroke: rgba(255, 255, 255, 0.08);
fill: rgba(var(--ink-rgb), 0.035);
stroke: rgba(var(--ink-rgb), 0.08);
}
.viz-grid .cell.on {
fill: rgba(255, 255, 255, 0.85);
fill: rgba(var(--ink-rgb), 0.85);
stroke: none;
animation: cell 4.8s var(--ease) infinite;
animation-delay: var(--t);
@@ -1201,7 +1226,7 @@ a {
.viz-grid .frame {
fill: none;
stroke: rgba(255, 255, 255, 0.5);
stroke: rgba(var(--ink-rgb), 0.5);
}
@media (max-width: 1023px) {
@@ -1290,7 +1315,7 @@ a {
place-items: center;
width: 96px;
height: 96px;
border: 1px solid rgba(255, 255, 255, 0.35);
border: 1px solid rgba(var(--ink-rgb), 0.35);
border-radius: 9999px;
font-size: 9px;
font-weight: 600;
@@ -1360,7 +1385,7 @@ a {
}
.journey ol::after {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.7), var(--rosso));
background: linear-gradient(90deg, rgba(var(--ink-rgb), 0.7), var(--rosso));
transform: scaleX(0);
transform-origin: left;
transition: transform 2.6s var(--ease) 0.2s;
@@ -1434,7 +1459,7 @@ a {
height: auto;
}
.journey ol::after {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.7), var(--rosso));
background: linear-gradient(180deg, rgba(var(--ink-rgb), 0.7), var(--rosso));
transform: scaleY(0);
transform-origin: top;
}
@@ -1542,7 +1567,7 @@ a {
}
.gaps .break line {
stroke: rgba(255, 255, 255, 0.45);
stroke: rgba(var(--ink-rgb), 0.45);
}
.gaps .break .cut {
@@ -1635,7 +1660,7 @@ a {
position: absolute;
inset: 0;
z-index: -1;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.9) 55%, #000 80%);
background: radial-gradient(ellipse at center, rgba(var(--paper-rgb), 0.6) 0%, rgba(var(--paper-rgb), 0.9) 55%, var(--nero) 80%);
pointer-events: none;
}
@@ -1758,7 +1783,7 @@ a {
line-height: 0.9;
letter-spacing: -0.05em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.07);
color: rgba(var(--ink-rgb), 0.07);
white-space: nowrap;
user-select: none;
}
@@ -1873,7 +1898,7 @@ a {
.nav-panel-inner {
padding: 22px 28px 26px;
background: rgba(0, 0, 0, 0.94);
background: rgba(var(--paper-rgb), 0.94);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border: 1px solid var(--grafite);
@@ -2115,7 +2140,7 @@ a {
}
.link-card:hover {
background: rgba(255, 255, 255, 0.03);
background: rgba(var(--ink-rgb), 0.03);
}
.link-card:hover .arrow-link {
@@ -2516,11 +2541,28 @@ a {
resize: vertical;
}
/* The message gets a full outline so it reads as a box to type in. */
.field textarea {
min-height: 132px;
padding: 12px 14px;
border: 1px solid var(--grafite);
}
.field input:focus,
.field textarea:focus {
border-color: var(--rosso);
}
/* Browser autofill paints its own pale background; keep the field on-theme and readable. */
.field input:-webkit-autofill,
.field input:-webkit-autofill:hover,
.field input:-webkit-autofill:focus {
-webkit-text-fill-color: var(--bianco);
caret-color: var(--bianco);
-webkit-box-shadow: 0 0 0 1000px var(--nero) inset;
transition: background-color 9999s;
}
.field input::placeholder {
color: var(--piombo);
}
@@ -2680,6 +2722,64 @@ a {
color: var(--bianco);
}
.theme-toggle {
display: inline-grid;
place-items: center;
width: 36px;
height: 36px;
flex: none;
padding: 0;
border: 1px solid var(--grafite);
border-radius: 50%;
background: transparent;
color: var(--bianco);
cursor: pointer;
transition: border-color 0.3s var(--ease);
}
.theme-toggle:hover {
border-color: var(--bianco);
}
.theme-toggle svg {
width: 16px;
height: 16px;
fill: none;
stroke: currentColor;
stroke-width: 1.5;
stroke-linecap: round;
}
/* Shows the theme you'd switch to: a sun on dark pages, a moon on light ones. */
.theme-toggle .moon,
:root[data-theme="light"] .theme-toggle .sun {
display: none;
}
:root[data-theme="light"] .theme-toggle .moon {
display: block;
}
/* Phones: the languages move into the menu (it carries its own switch) so the logo has room. */
@media (max-width: 480px) {
.site-header .lang-switch {
display: none;
}
.site-header .theme-toggle {
margin-inline-start: auto;
}
}
/* The stacked logo's wordmark is white; the light theme swaps in the dark-lettered file. */
.logo-stacked .on-light,
:root[data-theme="light"] .logo-stacked .on-dark {
display: none;
}
:root[data-theme="light"] .logo-stacked .on-light {
display: block;
}
.lang-switch [aria-current] {
color: var(--bianco);
box-shadow: inset 0 -1px 0 var(--rosso);
@@ -2706,7 +2806,7 @@ a {
gap: 16px;
max-width: 340px;
padding: 14px 18px;
border: 1px solid var(--grafite);
border: 1px solid var(--piombo);
transition: border-color 0.3s var(--ease);
}
@@ -2739,7 +2839,7 @@ a {
width: 24px;
height: 24px;
flex: none;
border: 1px solid var(--fumo);
border: 1.5px solid var(--argento);
transition:
border-color 0.3s var(--ease),
background-color 0.3s var(--ease);
@@ -2754,7 +2854,7 @@ a {
width: 18px;
height: 18px;
fill: none;
stroke: var(--bianco);
stroke: #fff;
stroke-width: 1.8;
stroke-dasharray: 16;
stroke-dashoffset: 16;
@@ -2810,7 +2910,7 @@ a {
}
[dir="rtl"] .journey ol::after {
background: linear-gradient(270deg, rgba(255, 255, 255, 0.7), var(--rosso));
background: linear-gradient(270deg, rgba(var(--ink-rgb), 0.7), var(--rosso));
transform-origin: right;
}
+6 -2
View File
@@ -98,9 +98,13 @@ export default function ContactForm({
<span>{t.website}</span>
<input
name="website"
type="url"
// Plain text, not type="url": people write "company.com" and shouldn't be made to add https://.
type="text"
inputMode="url"
placeholder="https://"
autoComplete="url"
autoCapitalize="none"
spellCheck={false}
placeholder="company.com"
maxLength={300}
defaultValue={values?.website}
dir="ltr"
+17 -6
View File
@@ -59,6 +59,10 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// Dots and lines use the theme's ink colour, re-read whenever the theme changes.
const readInk = () => getComputedStyle(document.documentElement).getPropertyValue("--ink-rgb").trim() || "255, 255, 255";
let ink = readInk();
// Fibonacci sphere
const COUNT = 1500;
const points: Vec[] = [];
@@ -159,15 +163,15 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
// soft halo
const halo = ctx.createRadialGradient(cx, cy, R * 0.7, cx, cy, R * 1.45);
halo.addColorStop(0, "rgba(255,255,255,0.045)");
halo.addColorStop(1, "rgba(255,255,255,0)");
halo.addColorStop(0, `rgba(${ink},0.045)`);
halo.addColorStop(1, `rgba(${ink},0)`);
ctx.fillStyle = halo;
ctx.fillRect(0, 0, w, h);
// outline
ctx.beginPath();
ctx.arc(cx, cy, R * 1.0, 0, Math.PI * 2);
ctx.strokeStyle = "rgba(255,255,255,0.08)";
ctx.strokeStyle = `rgba(${ink},0.08)`;
ctx.lineWidth = 1;
ctx.stroke();
@@ -177,7 +181,7 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
const front = p.z > 0;
const a = front ? 0.18 + p.z * 0.62 : 0.05 + (1 + p.z) * 0.05;
const size = (front ? 1.15 : 0.8) * p.s;
ctx.fillStyle = `rgba(255,255,255,${a.toFixed(3)})`;
ctx.fillStyle = `rgba(${ink},${a.toFixed(3)})`;
ctx.fillRect(p.x - size / 2, p.y - size / 2, size, size);
}
@@ -190,7 +194,7 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
const b = proj[s];
const vis = (a.z + b.z) / 2;
if (vis < -0.15) continue;
ctx.strokeStyle = `rgba(255,255,255,${(0.08 + Math.max(0, vis) * 0.22).toFixed(3)})`;
ctx.strokeStyle = `rgba(${ink},${(0.08 + Math.max(0, vis) * 0.22).toFixed(3)})`;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
@@ -224,7 +228,7 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
ctx.beginPath();
ctx.arc(p.x, p.y, 3 + pulse * 14, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = `rgba(255,255,255,${(0.4 + p.z * 0.6).toFixed(3)})`;
ctx.fillStyle = `rgba(${ink},${(0.4 + p.z * 0.6).toFixed(3)})`;
ctx.fillRect(p.x - 2.5, p.y - 2.5, 5, 5);
}
@@ -243,6 +247,12 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
});
ro.observe(canvas);
const themeObserver = new MutationObserver(() => {
ink = readInk();
if (reduced) draw(performance.now());
});
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
const io = new IntersectionObserver(([entry]) => {
const visible = entry.isIntersecting;
if (visible && !running) {
@@ -263,6 +273,7 @@ export default function GlobeCanvas({ layout = "hero", className = "hero-canvas"
running = false;
cancelAnimationFrame(raf);
ro.disconnect();
themeObserver.disconnect();
io.disconnect();
window.removeEventListener("pointermove", onMove);
};
+2 -1
View File
@@ -16,7 +16,8 @@ export default function Logo({ href, label }: { href: string; label: string }) {
export function LogoStacked({ href, label }: { href: string; label: string }) {
return (
<Link href={href} className="logo-stacked" aria-label={label}>
<Image src="/logo-stacked.png" alt="AI Growth Technologies" width={480} height={547} />
<Image className="on-dark" src="/logo-stacked.png" alt="AI Growth Technologies" width={480} height={547} />
<Image className="on-light" src="/logo-stacked-dark.png" alt="AI Growth Technologies" width={480} height={547} />
</Link>
);
}
+2 -2
View File
@@ -25,7 +25,7 @@ function JourneyViz({ labels }: { labels: VisualLabels }) {
return (
<svg className="viz viz-journey" viewBox={`0 0 ${W} ${H}`} aria-hidden="true">
{[180, 280, 380].map((x) => (
<line key={x} x1={x} y1={28} x2={x} y2={H - 28} stroke="rgba(255,255,255,0.05)" />
<line key={x} x1={x} y1={28} x2={x} y2={H - 28} style={{ stroke: "rgba(var(--ink-rgb), 0.05)" }} />
))}
{sources.map((s, i) => {
const y = top + i * gap;
@@ -98,7 +98,7 @@ function RadarViz({ labels }: { labels: VisualLabels }) {
))}
<g className="sweep">
<path d={`M${cx} ${cy} L${cx + sweepR} ${cy} A${sweepR} ${sweepR} 0 0 0 ${ex} ${ey} Z`} fill="url(#sweepGrad)" />
<line x1={cx} y1={cy} x2={cx + sweepR} y2={cy} stroke="rgba(255,255,255,0.6)" />
<line x1={cx} y1={cy} x2={cx + sweepR} y2={cy} style={{ stroke: "rgba(var(--ink-rgb), 0.6)" }} />
</g>
{dots.map((d, i) => {
const x = cx + Math.cos(rad(d.a)) * d.r;
+4
View File
@@ -4,6 +4,7 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Logo from "./logo";
import ThemeToggle from "./theme-toggle";
import { localeNames, localeShort, locales, localize, splitLocale, type Locale } from "@/i18n/config";
import type { NavItem } from "@/i18n/nav";
@@ -14,6 +15,8 @@ export type HeaderLabels = {
closeMenu: string;
language: string;
logoHome: string;
lightTheme: string;
darkTheme: string;
};
function LanguageSwitch({ locale, path, label }: { locale: Locale; path: string; label: string }) {
@@ -120,6 +123,7 @@ export default function SiteHeader({ locale, nav, t }: { locale: Locale; nav: Na
)}
</nav>
<LanguageSwitch locale={locale} path={path} label={t.language} />
<ThemeToggle t={t} />
<Link href={contactHref} className="btn btn-solid header-cta">
{t.bookCall} <span className="arrow">→</span>
</Link>
+52
View File
@@ -0,0 +1,52 @@
"use client";
import { useSyncExternalStore } from "react";
export type Theme = "dark" | "light";
// The site is dark by default; a visitor's choice is remembered in localStorage under this key.
const KEY = "theme";
/** Runs in <head> before the page paints, so a saved light theme never flashes dark first. */
export const themeScript = `try{if(localStorage.getItem("${KEY}")==="light")document.documentElement.dataset.theme="light"}catch(e){}`;
const read = (): Theme => (document.documentElement.dataset.theme === "light" ? "light" : "dark");
function subscribe(onChange: () => void) {
const observer = new MutationObserver(onChange);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
return () => observer.disconnect();
}
/** The current theme, kept in sync with <html data-theme>. */
export function useTheme(): Theme {
return useSyncExternalStore(subscribe, read, () => "dark");
}
export default function ThemeToggle({ t }: { t: { lightTheme: string; darkTheme: string } }) {
const theme = useTheme();
const label = theme === "dark" ? t.lightTheme : t.darkTheme;
function toggle() {
const next: Theme = theme === "dark" ? "light" : "dark";
if (next === "light") document.documentElement.dataset.theme = "light";
else delete document.documentElement.dataset.theme;
try {
localStorage.setItem(KEY, next);
} catch {
// Storage can be blocked (private mode); the theme still switches for this visit.
}
}
return (
<button type="button" className="theme-toggle" onClick={toggle} aria-label={label} title={label}>
<svg className="sun" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="12" cy="12" r="4.2" />
<path d="M12 2.5v2.2M12 19.3v2.2M2.5 12h2.2M19.3 12h2.2M5.3 5.3l1.6 1.6M17.1 17.1l1.6 1.6M5.3 18.7l1.6-1.6M17.1 6.9l1.6-1.6" />
</svg>
<svg className="moon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M20 14.6A8.5 8.5 0 0 1 9.4 4a8.5 8.5 0 1 0 10.6 10.6z" />
</svg>
</button>
);
}
+3 -3
View File
@@ -128,7 +128,7 @@
"subtitle": "مكتب شريك",
"body": [
"نقطة تواصل في الولايات المتحدة للشركات في تكساس وخارجها.",
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465"
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -139,7 +139,7 @@
"subtitle": "مكتب شريك",
"body": [
"نقطة تواصل في الولايات المتحدة مقرها جنوب فلوريدا.",
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974"
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -150,7 +150,7 @@
"subtitle": "مكتب شريك",
"body": [
"نقطة تواصل في الولايات المتحدة في أحد أبرز أسواق الأعمال في العالم.",
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577"
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
+3 -3
View File
@@ -128,7 +128,7 @@
"subtitle": "Partner Office",
"body": [
"A US point of contact for businesses across Texas and beyond.",
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465"
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -139,7 +139,7 @@
"subtitle": "Partner Office",
"body": [
"A US point of contact based in South Florida.",
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974"
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -150,7 +150,7 @@
"subtitle": "Partner Office",
"body": [
"A US point of contact in one of the world’s leading business markets.",
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577"
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
+3 -3
View File
@@ -128,7 +128,7 @@
"subtitle": "Oficina asociada",
"body": [
"Un punto de contacto en EE. UU. para empresas de Texas y más allá.",
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465"
"Downtown Houston, TX, USA\nPhone: +1 832-350-0465\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -139,7 +139,7 @@
"subtitle": "Oficina asociada",
"body": [
"Un punto de contacto en EE. UU. ubicado en el sur de Florida.",
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974"
"Brickell Avenue, FL, USA\nPhone: +1 954-235-4974\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
@@ -150,7 +150,7 @@
"subtitle": "Oficina asociada",
"body": [
"Un punto de contacto en EE. UU. en uno de los mercados de negocios más importantes del mundo.",
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577"
"Manhattan Financial District, NY, USA\nPhone: +1 713-289-4577\nEmail: info@aigrowthtechnologies.com"
],
"list": [],
"cta": null
+2
View File
@@ -19,6 +19,8 @@ export const ar: UI = {
openMenu: "فتح القائمة",
closeMenu: "إغلاق القائمة",
language: "اللغة",
lightTheme: "التبديل إلى الوضع الفاتح",
darkTheme: "التبديل إلى الوضع الداكن",
},
bookCall: "احجز مكالمة استراتيجية",
services: {
+2
View File
@@ -19,6 +19,8 @@ export const en = {
openMenu: "Open menu",
closeMenu: "Close menu",
language: "Language",
lightTheme: "Switch to light theme",
darkTheme: "Switch to dark theme",
},
bookCall: "Book a Strategy Call",
services: {
+2
View File
@@ -19,6 +19,8 @@ export const es: UI = {
openMenu: "Abrir menú",
closeMenu: "Cerrar menú",
language: "Idioma",
lightTheme: "Cambiar a tema claro",
darkTheme: "Cambiar a tema oscuro",
},
bookCall: "Agenda una llamada estratégica",
services: {