Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c67c13d82f | ||
|
|
ca0eddfde8 | ||
|
|
5b037d7e0c |
@@ -477,8 +477,8 @@ jobs:
|
||||
run: |
|
||||
BASE_URL="${{ needs.prepare.outputs.next_public_url }}"
|
||||
|
||||
# The legal pages must be reachable.
|
||||
for PAGE in /imprint /privacy; do
|
||||
# The legal pages must be reachable in both languages.
|
||||
for PAGE in /impressum /datenschutz /en/imprint /en/privacy; do
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL$PAGE")
|
||||
if [ "$STATUS" != "200" ]; then
|
||||
echo "❌ $PAGE failed! (Status: $STATUS)"
|
||||
@@ -488,7 +488,7 @@ jobs:
|
||||
done
|
||||
|
||||
# URLs of the previous site must redirect instead of returning 404.
|
||||
for OLD in /blog /contact /websites /case-studies/klz-cables /impressum; do
|
||||
for OLD in /blog /contact /websites /case-studies/klz-cables; do
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL$OLD")
|
||||
if [ "$STATUS" != "308" ]; then
|
||||
echo "❌ $OLD does not redirect! (Status: $STATUS)"
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from "@/src/components/illustrations/WebPresentation";
|
||||
import { WorkShowcase } from "@/src/components/illustrations/WorkShowcase";
|
||||
import { Reveal } from "@/src/components/Reveal";
|
||||
import { SwipeRow } from "@/src/components/SwipeRow";
|
||||
import { contentFor } from "@/src/content";
|
||||
import type { SplitTitle } from "@/src/content/types";
|
||||
import { DEFAULT_LOCALE, parseLocale } from "@/src/domain/locale";
|
||||
@@ -122,7 +123,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Reveal>
|
||||
<AutomationFlow />
|
||||
</Reveal>
|
||||
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-24 md:grid-cols-3 md:gap-10">
|
||||
<SwipeRow className="mt-12 grid grid-cols-1 gap-12 md:mt-24 md:grid-cols-3 md:gap-10">
|
||||
{automation.notes.map((group, i) => (
|
||||
<Reveal key={group.title} delay={0.1 * i}>
|
||||
<div className="border-t border-slate-900 pt-6">
|
||||
@@ -140,7 +141,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</SwipeRow>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
@@ -162,7 +163,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</BitAura>
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-16 grid grid-cols-1 gap-x-10 gap-y-14 sm:grid-cols-2 md:mt-28 lg:grid-cols-4">
|
||||
<SwipeRow className="mt-12 grid grid-cols-1 gap-x-10 gap-y-14 md:mt-28 md:grid-cols-2 lg:grid-cols-4">
|
||||
{presentations.reasons.map((reason, i) => (
|
||||
<Reveal key={reason.title} delay={0.08 * i}>
|
||||
<div className="rounded-2xl border border-slate-800 bg-slate-950/60 p-4">
|
||||
@@ -176,7 +177,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</p>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</SwipeRow>
|
||||
|
||||
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-28 md:grid-cols-12 md:gap-12">
|
||||
<Reveal className="md:col-span-6">
|
||||
@@ -220,7 +221,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
label={technology.label}
|
||||
title={<Title title={technology.title} />}
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-3 md:gap-10">
|
||||
<SwipeRow className="grid grid-cols-1 gap-12 md:grid-cols-3 md:gap-10">
|
||||
{technology.fit.map((group, i) => (
|
||||
<Reveal key={group.title} delay={0.1 * i}>
|
||||
<div
|
||||
@@ -242,12 +243,12 @@ export default async function LandingPage({ params }: Props) {
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</SwipeRow>
|
||||
|
||||
<Reveal>
|
||||
<div className="mt-16 rounded-3xl bg-slate-50 p-6 md:mt-24 md:p-10">
|
||||
<div className="mt-12 rounded-3xl bg-slate-50 p-6 md:mt-24 md:p-10">
|
||||
<p className={`${MONO} m-0`}>{technology.techLabel}</p>
|
||||
<div className="mt-8 grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="mt-6 grid grid-cols-1 gap-7 sm:grid-cols-2 md:mt-8 md:gap-10 lg:grid-cols-4">
|
||||
{technology.tech.map((group) => (
|
||||
<div key={group.area}>
|
||||
<p className="m-0 font-sans text-xl font-bold tracking-tight text-slate-900 md:text-2xl">
|
||||
@@ -269,7 +270,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-16 md:mt-24">
|
||||
<div className="mt-12 md:mt-24">
|
||||
<Reveal>
|
||||
<h3 className="m-0 font-sans text-3xl font-bold tracking-tighter text-slate-900 md:text-5xl">
|
||||
{technology.pipelineTitle}
|
||||
@@ -296,7 +297,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</div>
|
||||
</BitAura>
|
||||
</Reveal>
|
||||
<div className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
|
||||
<SwipeRow className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
|
||||
{working.principles.map((item, i) => (
|
||||
<Reveal key={item.title} delay={0.08 * i}>
|
||||
<div className="border-t border-slate-900 pt-6">
|
||||
@@ -307,7 +308,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</SwipeRow>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
|
||||
@@ -1352,6 +1352,38 @@ pre:has(code[class*="language-"]) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SwipeRow: on phones equal items stand side by side and are swiped through,
|
||||
* with the next one peeking in. From tablet width on the slide wrappers
|
||||
* dissolve and the items sit in the grid the row was given.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.swipe-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-x: contain;
|
||||
scroll-snap-type: x mandatory;
|
||||
/* Bleed to the screen edges, but start in line with the text. */
|
||||
margin-inline: -1.25rem;
|
||||
padding-inline: 1.25rem;
|
||||
scroll-padding-inline: 1.25rem;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.swipe-row::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.swipe-slide {
|
||||
flex: 0 0 82%;
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.swipe-slide {
|
||||
display: contents;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Native feel: the page should behave like a well-made app on every input type.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Renders the share images (Open Graph and Twitter) from the real hero, one per
|
||||
// language, so they always match the site. Run it after the hero headline or its
|
||||
// design changes:
|
||||
// language, so they always match the site. The hero is shown the way the dark
|
||||
// sections of the site look: light type on a dark surface, a lit edge, and
|
||||
// binary digits coming off it. Run it after the hero headline or its design
|
||||
// changes:
|
||||
//
|
||||
// pnpm --filter @mintel/web share-image (uses the live site)
|
||||
// pnpm --filter @mintel/web share-image http://localhost:3000
|
||||
@@ -37,7 +39,9 @@ for (const shot of SHOTS) {
|
||||
main > div > section:first-of-type .grid { margin-top: 0 !important; display: block !important; }
|
||||
main > div > section:first-of-type h1 span:nth-of-type(2) { color: rgba(15, 23, 42, 0.2) !important; }
|
||||
main > div > section:first-of-type > div.relative { padding-top: 34px !important; }
|
||||
html, body { overflow: hidden !important; }
|
||||
html, body { overflow: hidden !important; background: #000 !important; }
|
||||
/* Dark version of the hero: inverted, with the hues turned back. */
|
||||
main > div > section:first-of-type { filter: invert(1) hue-rotate(180deg) !important; }
|
||||
`,
|
||||
});
|
||||
// Let the page finish starting up first; it would discard the added labels.
|
||||
@@ -54,6 +58,66 @@ for (const shot of SHOTS) {
|
||||
"position:absolute;right:72px;top:54px;z-index:20;font:500 15px ui-monospace,Menlo,monospace;letter-spacing:.2em;color:#64748b";
|
||||
section.style.position = "relative";
|
||||
section.append(tag, site);
|
||||
|
||||
// Everything below lies over the inverted hero and is not inverted itself.
|
||||
const W = 1200;
|
||||
const H = 630;
|
||||
const layer = (css) => {
|
||||
const el = document.createElement("div");
|
||||
el.style.cssText = `position:absolute;left:0;top:0;width:${W}px;height:${H}px;z-index:60;pointer-events:none;${css}`;
|
||||
document.body.append(el);
|
||||
return el;
|
||||
};
|
||||
document.body.style.position = "relative";
|
||||
// Black becomes the slate of the site's dark sections; light type stays light.
|
||||
layer("background:#0f172a;mix-blend-mode:screen");
|
||||
// Light coming from the lower right corner.
|
||||
layer(
|
||||
"mix-blend-mode:screen;background:radial-gradient(58% 62% at 92% 108%, rgba(99,102,241,.85), transparent 70%),radial-gradient(34% 40% at 104% -6%, rgba(129,140,248,.4), transparent 70%)",
|
||||
);
|
||||
// A frame whose lower right edge is lit, with a soft glow behind it.
|
||||
const INSET = 22;
|
||||
const edge = (blur, width, opacity) => {
|
||||
const el = layer(
|
||||
`left:${INSET}px;top:${INSET}px;width:${W - 2 * INSET}px;height:${H - 2 * INSET}px;box-sizing:border-box;border-radius:30px;padding:${width}px;opacity:${opacity};filter:blur(${blur}px);` +
|
||||
"background:conic-gradient(from 60deg at 50% 50%, transparent 0 8%, rgba(129,140,248,.95) 22%, #fff 30%, rgba(129,140,248,.95) 40%, transparent 56% 100%);" +
|
||||
"-webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0)",
|
||||
);
|
||||
return el;
|
||||
};
|
||||
layer(
|
||||
`left:${INSET}px;top:${INSET}px;width:${W - 2 * INSET}px;height:${H - 2 * INSET}px;box-sizing:border-box;border-radius:30px;border:1px solid rgba(165,180,252,.16)`,
|
||||
);
|
||||
edge(26, 14, 0.9);
|
||||
edge(8, 5, 0.9);
|
||||
edge(0, 2, 1);
|
||||
|
||||
// Digits coming off the lit edge. Seeded, so the picture is the same on every run.
|
||||
let seed = 20261004;
|
||||
const random = () => {
|
||||
seed = (seed * 1664525 + 1013904223) % 4294967296;
|
||||
return seed / 4294967296;
|
||||
};
|
||||
const digits = layer(
|
||||
"font:500 15px var(--font-jetbrains-mono), ui-monospace, Menlo, monospace;color:#c7d2fe",
|
||||
);
|
||||
for (let i = 0; i < 150; i++) {
|
||||
// Along the bottom edge (right half) and up the right edge (lower half).
|
||||
const alongBottom = random() < 0.62;
|
||||
const spread = Math.pow(random(), 1.8);
|
||||
const x = alongBottom ? W * 0.42 + random() * (W * 0.58 - INSET - 30) : 0;
|
||||
// Under the lead text the digits stay close to the edge.
|
||||
const reach = spread * (alongBottom && x < 820 ? 54 : 120);
|
||||
const left = alongBottom ? x : W - INSET - 14 - reach;
|
||||
const y = alongBottom
|
||||
? H - INSET - 16 - reach
|
||||
: H * 0.3 + random() * (H * 0.7 - INSET - 30);
|
||||
const digit = document.createElement("span");
|
||||
digit.textContent = random() < 0.5 ? "0" : "1";
|
||||
const big = random() < 0.2;
|
||||
digit.style.cssText = `position:absolute;left:${left.toFixed(1)}px;top:${y.toFixed(1)}px;opacity:${((1 - spread) * (0.35 + random() * 0.65)).toFixed(2)};font-size:${big ? 19 : 14}px;font-weight:${big ? 700 : 500}`;
|
||||
digits.append(digit);
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
await page.waitForTimeout(4500);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 277 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 278 KiB |
@@ -0,0 +1,18 @@
|
||||
import * as React from "react";
|
||||
|
||||
/**
|
||||
* SwipeRow: on phones the items stand side by side and are swiped through,
|
||||
* with the next one peeking in; from tablet width on they fall back into the
|
||||
* grid given by `className`. It keeps long lists of equal items from
|
||||
* stretching the page on a small screen. Styles: `swipe-row` in globals.css.
|
||||
*/
|
||||
export const SwipeRow: React.FC<{
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ className = "", children }) => (
|
||||
<div className={`swipe-row ${className}`}>
|
||||
{React.Children.map(children, (child) => (
|
||||
<div className="swipe-slide">{child}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -49,7 +49,7 @@ export const Block: React.FC<BlockProps> = ({
|
||||
/>
|
||||
)}
|
||||
{dark && <BitSeam />}
|
||||
<div className="relative mx-auto w-full max-w-[1400px] px-5 py-20 md:px-12 md:py-36">
|
||||
<div className="relative mx-auto w-full max-w-[1400px] px-5 py-14 md:px-12 md:py-36">
|
||||
<Reveal>
|
||||
<div className="mb-8 flex items-center gap-4 md:mb-12">
|
||||
<span
|
||||
@@ -78,7 +78,7 @@ export const Block: React.FC<BlockProps> = ({
|
||||
{title}
|
||||
</h2>
|
||||
</BitWipe>
|
||||
<div className="mt-14 md:mt-24">{children}</div>
|
||||
<div className="mt-10 md:mt-24">{children}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ClosingCta: React.FC<{ contact: Content["contact"] }> = ({
|
||||
/>
|
||||
<div className="bit-glow bit-glow-dots" aria-hidden="true" />
|
||||
<BitSeam />
|
||||
<div className="relative mx-auto grid w-full max-w-[1400px] grid-cols-1 gap-14 px-5 py-24 md:px-12 md:py-40 lg:grid-cols-12 lg:items-center lg:gap-12">
|
||||
<div className="relative mx-auto grid w-full max-w-[1400px] grid-cols-1 gap-12 px-5 py-16 md:px-12 md:py-40 lg:grid-cols-12 lg:items-center lg:gap-12">
|
||||
<Reveal className="lg:col-span-6">
|
||||
<BitHeadline
|
||||
as="h2"
|
||||
|
||||
@@ -92,7 +92,7 @@ const PathStory: React.FC<{ path: ServicePath; active: boolean }> = ({
|
||||
setStep(index);
|
||||
setAuto(false);
|
||||
}}
|
||||
className={`relative flex w-full gap-5 border-t py-6 text-left transition-colors duration-300 md:gap-6 ${
|
||||
className={`relative flex w-full gap-5 border-t py-4 text-left transition-colors duration-300 md:gap-6 md:py-6 ${
|
||||
selected ? "border-slate-900" : "border-slate-200"
|
||||
}`}
|
||||
>
|
||||
@@ -122,8 +122,11 @@ const PathStory: React.FC<{ path: ServicePath; active: boolean }> = ({
|
||||
{item.title}
|
||||
</span>
|
||||
<span
|
||||
className={`mt-1.5 block font-sans text-base leading-relaxed transition-colors duration-300 ${
|
||||
selected ? "text-slate-600" : "text-slate-400"
|
||||
className={`mt-1.5 font-sans text-base leading-relaxed transition-colors duration-300 ${
|
||||
// On phones only the current step is spelled out.
|
||||
selected
|
||||
? "block text-slate-600"
|
||||
: "hidden text-slate-400 md:block"
|
||||
}`}
|
||||
>
|
||||
{item.text}
|
||||
@@ -153,7 +156,7 @@ export const PathPicker: React.FC = () => {
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label={services.tablistLabel}
|
||||
className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4 lg:gap-4"
|
||||
className="grid grid-cols-2 gap-2 md:gap-3 lg:grid-cols-4 lg:gap-4"
|
||||
>
|
||||
{paths.map((path, index) => {
|
||||
const selected = path.id === activeId;
|
||||
@@ -167,7 +170,7 @@ export const PathPicker: React.FC = () => {
|
||||
aria-controls={`path-panel-${path.id}`}
|
||||
onClick={() => setActiveId(path.id)}
|
||||
data-glow
|
||||
className={`bit-card group flex h-full flex-col justify-between rounded-3xl border p-5 text-left transition-all duration-300 md:p-7 ${
|
||||
className={`bit-card group flex h-full flex-col justify-between rounded-3xl border p-4 text-left transition-all duration-300 md:p-7 ${
|
||||
selected
|
||||
? "border-slate-900 bg-slate-900 text-white"
|
||||
: "border-slate-200 bg-white text-slate-900 hover:-translate-y-1 hover:border-slate-400"
|
||||
@@ -177,11 +180,11 @@ export const PathPicker: React.FC = () => {
|
||||
<span className="font-mono text-[11px] tracking-[0.2em] text-slate-400">
|
||||
0{index + 1}
|
||||
</span>
|
||||
<span className="mt-3 block font-sans text-xl font-bold leading-tight tracking-tight md:mt-10 md:text-2xl">
|
||||
<span className="mt-3 block font-sans text-base font-bold leading-tight tracking-tight md:mt-10 md:text-2xl">
|
||||
{path.need}
|
||||
</span>
|
||||
<span
|
||||
className={`mt-2 block font-sans text-sm leading-relaxed md:mt-4 md:text-base ${selected ? "text-slate-300" : "text-slate-500"}`}
|
||||
className={`mt-2 hidden font-sans text-sm leading-relaxed md:mt-4 md:block md:text-base ${selected ? "text-slate-300" : "text-slate-500"}`}
|
||||
>
|
||||
{path.forWhom}
|
||||
</span>
|
||||
@@ -199,7 +202,7 @@ export const PathPicker: React.FC = () => {
|
||||
id={`path-panel-${path.id}`}
|
||||
aria-labelledby={`path-tab-${path.id}`}
|
||||
hidden={!selected}
|
||||
className="mt-12 md:mt-20"
|
||||
className="mt-8 md:mt-20"
|
||||
>
|
||||
<motion.div
|
||||
key={selected ? "on" : "off"}
|
||||
@@ -251,7 +254,7 @@ export const PathPicker: React.FC = () => {
|
||||
{path.pricingText}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 md:col-span-6">
|
||||
<div className="grid grid-cols-2 gap-6 md:col-span-6 md:gap-8">
|
||||
{path.prices.map((price) => (
|
||||
<div key={price.label}>
|
||||
<p className="m-0 font-sans text-base text-slate-500 md:text-lg">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BitAura } from "@/src/components/bits/BitAura";
|
||||
import { SwipeRow } from "@/src/components/SwipeRow";
|
||||
import * as React from "react";
|
||||
import { useNarrow } from "./useNarrow";
|
||||
import { useContent } from "@/src/components/ContentProvider";
|
||||
@@ -391,7 +392,7 @@ export const AutomationFlow: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 border-t border-slate-800 md:grid-cols-12">
|
||||
<div className="border-b border-slate-800 px-5 py-5 md:col-span-7 md:border-b-0 md:border-r md:px-7">
|
||||
<div className="px-5 py-5 md:col-span-7 md:border-r md:border-slate-800 md:px-7">
|
||||
<p className={`${MONO} m-0 text-slate-500`}>{automation.log}</p>
|
||||
<ol
|
||||
className="m-0 mt-3 min-h-[7.5rem] list-none p-0 font-mono text-[12px] leading-relaxed text-slate-300 md:text-[13px]"
|
||||
@@ -413,7 +414,7 @@ export const AutomationFlow: React.FC = () => {
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
<ul className="m-0 flex list-none flex-col justify-center gap-2 px-5 py-5 md:col-span-5 md:px-7">
|
||||
<ul className="m-0 hidden list-none flex-col justify-center gap-2 px-5 py-5 md:col-span-5 md:flex md:px-7">
|
||||
{KINDS.filter((kind) =>
|
||||
flow.nodes.some((node) => node.kind === kind),
|
||||
).map((kind) => (
|
||||
@@ -442,7 +443,7 @@ export const AutomationFlow: React.FC = () => {
|
||||
</div>
|
||||
</BitAura>
|
||||
|
||||
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<SwipeRow className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<div className="rounded-3xl border border-slate-200 p-6 md:p-8">
|
||||
<p className={`${MONO} m-0 text-slate-400`}>{automation.byHand}</p>
|
||||
<p className="m-0 mt-3 font-sans text-lg leading-relaxed text-slate-500 md:text-xl">
|
||||
@@ -455,7 +456,7 @@ export const AutomationFlow: React.FC = () => {
|
||||
{flow.automated}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</SwipeRow>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -241,7 +241,10 @@ export const PipelineRun: React.FC = () => {
|
||||
</p>
|
||||
<p
|
||||
className={`m-0 mt-2 font-sans text-base leading-relaxed transition-colors duration-300 ${
|
||||
active ? "text-slate-600" : "text-slate-400"
|
||||
// On phones only the current step is spelled out.
|
||||
active
|
||||
? "block text-slate-600"
|
||||
: "hidden text-slate-400 md:block"
|
||||
}`}
|
||||
>
|
||||
{item.text}
|
||||
|
||||
@@ -203,7 +203,7 @@ export const WorkShowcase: React.FC = () => {
|
||||
{copy.text}
|
||||
</p>
|
||||
|
||||
<ul className="m-0 mt-8 list-none p-0">
|
||||
<ul className="m-0 mt-8 hidden list-none p-0 md:block">
|
||||
{copy.did.map((line) => (
|
||||
<li
|
||||
key={line}
|
||||
|
||||
Reference in New Issue
Block a user