Mobile responsive: hamburger sidebar + responsive grids
Browse filesPorts the Weather AI 2 sidebar pattern: off-canvas drawer with backdrop
on mobile, persistent rail on lg+. Adds mobile header with menu button.
Replaces inline repeat(N, 1fr) grids with responsive Tailwind classes
across Dashboard, Zones, Disbursements, ProgramDesigner, and Pipeline.
Tour tooltip clamped to viewport width.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- frontend/src/App.tsx +18 -3
- frontend/src/components/Sidebar.tsx +38 -7
- frontend/src/lib/tour.ts +1 -1
- frontend/src/pages/Dashboard.tsx +5 -12
- frontend/src/pages/Disbursements.tsx +3 -4
- frontend/src/pages/Pipeline.tsx +3 -13
- frontend/src/pages/ProgramDesigner.tsx +2 -6
- frontend/src/pages/Zones.tsx +1 -3
frontend/src/App.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import { useCallback, useEffect, useState } from 'react'
|
| 2 |
import { Routes, Route, useNavigate } from 'react-router-dom'
|
| 3 |
import Joyride, { type CallBackProps, STATUS, EVENTS, ACTIONS } from 'react-joyride'
|
|
|
|
| 4 |
import Sidebar from './components/Sidebar'
|
| 5 |
import Dashboard from './pages/Dashboard'
|
| 6 |
import Zones from './pages/Zones'
|
|
@@ -45,6 +46,7 @@ function stepTargetSelector(index: number): string | null {
|
|
| 45 |
export default function App() {
|
| 46 |
const [runTour, setRunTour] = useState(false)
|
| 47 |
const [stepIndex, setStepIndex] = useState(0)
|
|
|
|
| 48 |
const navigate = useNavigate()
|
| 49 |
|
| 50 |
// Joyride autostart is intentionally disabled in the reskin — the tour
|
|
@@ -100,10 +102,23 @@ export default function App() {
|
|
| 100 |
|
| 101 |
return (
|
| 102 |
<div className="flex min-h-screen bg-paper">
|
| 103 |
-
<Sidebar />
|
| 104 |
|
| 105 |
-
<main className="flex-1 ml-56">
|
| 106 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
<Routes>
|
| 108 |
<Route path="/" element={<Dashboard />} />
|
| 109 |
<Route path="/zones" element={<Zones />} />
|
|
|
|
| 1 |
import { useCallback, useEffect, useState } from 'react'
|
| 2 |
import { Routes, Route, useNavigate } from 'react-router-dom'
|
| 3 |
import Joyride, { type CallBackProps, STATUS, EVENTS, ACTIONS } from 'react-joyride'
|
| 4 |
+
import { Menu } from 'lucide-react'
|
| 5 |
import Sidebar from './components/Sidebar'
|
| 6 |
import Dashboard from './pages/Dashboard'
|
| 7 |
import Zones from './pages/Zones'
|
|
|
|
| 46 |
export default function App() {
|
| 47 |
const [runTour, setRunTour] = useState(false)
|
| 48 |
const [stepIndex, setStepIndex] = useState(0)
|
| 49 |
+
const [sidebarOpen, setSidebarOpen] = useState(false)
|
| 50 |
const navigate = useNavigate()
|
| 51 |
|
| 52 |
// Joyride autostart is intentionally disabled in the reskin — the tour
|
|
|
|
| 102 |
|
| 103 |
return (
|
| 104 |
<div className="flex min-h-screen bg-paper">
|
| 105 |
+
<Sidebar open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
|
| 106 |
|
| 107 |
+
<main className="flex-1 lg:ml-56 min-w-0">
|
| 108 |
+
<header
|
| 109 |
+
className="flex items-center h-12 px-4 lg:hidden"
|
| 110 |
+
style={{ background: '#ffffff' }}
|
| 111 |
+
>
|
| 112 |
+
<button
|
| 113 |
+
onClick={() => setSidebarOpen(true)}
|
| 114 |
+
aria-label="Open navigation menu"
|
| 115 |
+
className="p-2 -ml-2"
|
| 116 |
+
style={{ color: '#606373', background: 'none', border: 'none' }}
|
| 117 |
+
>
|
| 118 |
+
<Menu size={20} />
|
| 119 |
+
</button>
|
| 120 |
+
</header>
|
| 121 |
+
<div className="px-4 sm:px-8 lg:px-12 pt-6 lg:pt-16 pb-16" style={{ maxWidth: '1180px' }}>
|
| 122 |
<Routes>
|
| 123 |
<Route path="/" element={<Dashboard />} />
|
| 124 |
<Route path="/zones" element={<Zones />} />
|
frontend/src/components/Sidebar.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
| 5 |
SlidersHorizontal,
|
| 6 |
Banknote,
|
| 7 |
Settings,
|
|
|
|
| 8 |
} from 'lucide-react'
|
| 9 |
|
| 10 |
const NAV_ITEMS = [
|
|
@@ -15,15 +16,34 @@ const NAV_ITEMS = [
|
|
| 15 |
{ to: '/pipeline', label: 'How it works', icon: Settings, tourId: 'nav-pipeline' },
|
| 16 |
]
|
| 17 |
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
return (
|
| 20 |
-
<
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
{/* Brand */}
|
| 25 |
<div
|
| 26 |
-
className="flex items-center h-16 px-5"
|
| 27 |
style={{ borderBottom: '1px solid rgba(255,255,255,0.06)' }}
|
| 28 |
>
|
| 29 |
<Link to="/" className="flex items-baseline gap-2 no-underline">
|
|
@@ -39,6 +59,14 @@ export default function Sidebar() {
|
|
| 39 |
Heat insurance
|
| 40 |
</span>
|
| 41 |
</Link>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
</div>
|
| 43 |
|
| 44 |
{/* Navigation */}
|
|
@@ -49,6 +77,7 @@ export default function Sidebar() {
|
|
| 49 |
to={to}
|
| 50 |
end={to === '/'}
|
| 51 |
data-tour={tourId}
|
|
|
|
| 52 |
className="sidebar-link"
|
| 53 |
>
|
| 54 |
<Icon size={16} style={{ flexShrink: 0 }} />
|
|
@@ -71,6 +100,8 @@ export default function Sidebar() {
|
|
| 71 |
Worker personas are simulated
|
| 72 |
</div>
|
| 73 |
|
|
|
|
|
|
|
| 74 |
<style>{`
|
| 75 |
.sidebar-link {
|
| 76 |
display: flex;
|
|
@@ -93,6 +124,6 @@ export default function Sidebar() {
|
|
| 93 |
border-left-color: #8a3d28;
|
| 94 |
}
|
| 95 |
`}</style>
|
| 96 |
-
</
|
| 97 |
)
|
| 98 |
}
|
|
|
|
| 5 |
SlidersHorizontal,
|
| 6 |
Banknote,
|
| 7 |
Settings,
|
| 8 |
+
X,
|
| 9 |
} from 'lucide-react'
|
| 10 |
|
| 11 |
const NAV_ITEMS = [
|
|
|
|
| 16 |
{ to: '/pipeline', label: 'How it works', icon: Settings, tourId: 'nav-pipeline' },
|
| 17 |
]
|
| 18 |
|
| 19 |
+
interface Props {
|
| 20 |
+
open: boolean
|
| 21 |
+
onClose: () => void
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
export default function Sidebar({ open, onClose }: Props) {
|
| 25 |
return (
|
| 26 |
+
<>
|
| 27 |
+
{open && (
|
| 28 |
+
<div
|
| 29 |
+
className="fixed inset-0 z-40 lg:hidden"
|
| 30 |
+
style={{ background: 'rgba(27, 30, 45, 0.4)' }}
|
| 31 |
+
onClick={onClose}
|
| 32 |
+
/>
|
| 33 |
+
)}
|
| 34 |
+
|
| 35 |
+
<aside
|
| 36 |
+
className={`
|
| 37 |
+
fixed top-0 left-0 z-50 h-full w-56
|
| 38 |
+
flex flex-col transition-transform duration-200 ease-in-out
|
| 39 |
+
lg:translate-x-0
|
| 40 |
+
${open ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
| 41 |
+
`}
|
| 42 |
+
style={{ background: '#1b1e2d' }}
|
| 43 |
+
>
|
| 44 |
{/* Brand */}
|
| 45 |
<div
|
| 46 |
+
className="flex items-center justify-between h-16 px-5"
|
| 47 |
style={{ borderBottom: '1px solid rgba(255,255,255,0.06)' }}
|
| 48 |
>
|
| 49 |
<Link to="/" className="flex items-baseline gap-2 no-underline">
|
|
|
|
| 59 |
Heat insurance
|
| 60 |
</span>
|
| 61 |
</Link>
|
| 62 |
+
<button
|
| 63 |
+
onClick={onClose}
|
| 64 |
+
aria-label="Close navigation menu"
|
| 65 |
+
className="lg:hidden p-1"
|
| 66 |
+
style={{ color: '#8d909e', background: 'none', border: 'none' }}
|
| 67 |
+
>
|
| 68 |
+
<X size={18} />
|
| 69 |
+
</button>
|
| 70 |
</div>
|
| 71 |
|
| 72 |
{/* Navigation */}
|
|
|
|
| 77 |
to={to}
|
| 78 |
end={to === '/'}
|
| 79 |
data-tour={tourId}
|
| 80 |
+
onClick={onClose}
|
| 81 |
className="sidebar-link"
|
| 82 |
>
|
| 83 |
<Icon size={16} style={{ flexShrink: 0 }} />
|
|
|
|
| 100 |
Worker personas are simulated
|
| 101 |
</div>
|
| 102 |
|
| 103 |
+
</aside>
|
| 104 |
+
|
| 105 |
<style>{`
|
| 106 |
.sidebar-link {
|
| 107 |
display: flex;
|
|
|
|
| 124 |
border-left-color: #8a3d28;
|
| 125 |
}
|
| 126 |
`}</style>
|
| 127 |
+
</>
|
| 128 |
)
|
| 129 |
}
|
frontend/src/lib/tour.ts
CHANGED
|
@@ -135,7 +135,7 @@ export const tourStyles = {
|
|
| 135 |
tooltip: {
|
| 136 |
borderRadius: 10,
|
| 137 |
padding: '20px 22px',
|
| 138 |
-
maxWidth:
|
| 139 |
fontFamily: '"DM Sans", system-ui, sans-serif',
|
| 140 |
fontSize: '0.88rem',
|
| 141 |
lineHeight: 1.6,
|
|
|
|
| 135 |
tooltip: {
|
| 136 |
borderRadius: 10,
|
| 137 |
padding: '20px 22px',
|
| 138 |
+
maxWidth: 'min(400px, calc(100vw - 32px))',
|
| 139 |
fontFamily: '"DM Sans", system-ui, sans-serif',
|
| 140 |
fontSize: '0.88rem',
|
| 141 |
lineHeight: 1.6,
|
frontend/src/pages/Dashboard.tsx
CHANGED
|
@@ -282,16 +282,12 @@ function PipelineHero() {
|
|
| 282 |
|
| 283 |
{/* Step row */}
|
| 284 |
<div
|
| 285 |
-
|
| 286 |
-
display: 'grid',
|
| 287 |
-
gridTemplateColumns: `repeat(${HERO_STEPS.length}, 1fr)`,
|
| 288 |
-
gap: '8px',
|
| 289 |
-
position: 'relative',
|
| 290 |
-
}}
|
| 291 |
data-tour="stage-cards"
|
| 292 |
>
|
| 293 |
{/* connector line animated on mount */}
|
| 294 |
<div
|
|
|
|
| 295 |
style={{
|
| 296 |
position: 'absolute',
|
| 297 |
top: '20px',
|
|
@@ -311,6 +307,7 @@ function PipelineHero() {
|
|
| 311 |
<button
|
| 312 |
key={s.num}
|
| 313 |
onMouseEnter={() => !locked && setSelected(i)}
|
|
|
|
| 314 |
onClick={() => {
|
| 315 |
setSelected(i)
|
| 316 |
setLocked(true)
|
|
@@ -393,13 +390,11 @@ function PipelineHero() {
|
|
| 393 |
{/* Detail + output panel */}
|
| 394 |
<div
|
| 395 |
key={step.num}
|
| 396 |
-
className="animate-fade-in"
|
| 397 |
style={{
|
| 398 |
marginTop: '24px',
|
| 399 |
paddingTop: '20px',
|
| 400 |
borderTop: '1px solid #e8e5e1',
|
| 401 |
-
display: 'grid',
|
| 402 |
-
gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1.1fr)',
|
| 403 |
columnGap: '32px',
|
| 404 |
rowGap: '14px',
|
| 405 |
alignItems: 'start',
|
|
@@ -502,10 +497,8 @@ export default function Dashboard() {
|
|
| 502 |
{/* KPI row */}
|
| 503 |
<div
|
| 504 |
data-tour="metrics"
|
|
|
|
| 505 |
style={{
|
| 506 |
-
display: 'grid',
|
| 507 |
-
gridTemplateColumns: 'repeat(4, 1fr)',
|
| 508 |
-
gap: '32px',
|
| 509 |
borderTop: '1px solid #e8e5e1',
|
| 510 |
paddingTop: '20px',
|
| 511 |
marginTop: '12px',
|
|
|
|
| 282 |
|
| 283 |
{/* Step row */}
|
| 284 |
<div
|
| 285 |
+
className="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-2 relative"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
data-tour="stage-cards"
|
| 287 |
>
|
| 288 |
{/* connector line animated on mount */}
|
| 289 |
<div
|
| 290 |
+
className="hidden md:block"
|
| 291 |
style={{
|
| 292 |
position: 'absolute',
|
| 293 |
top: '20px',
|
|
|
|
| 307 |
<button
|
| 308 |
key={s.num}
|
| 309 |
onMouseEnter={() => !locked && setSelected(i)}
|
| 310 |
+
onFocus={() => !locked && setSelected(i)}
|
| 311 |
onClick={() => {
|
| 312 |
setSelected(i)
|
| 313 |
setLocked(true)
|
|
|
|
| 390 |
{/* Detail + output panel */}
|
| 391 |
<div
|
| 392 |
key={step.num}
|
| 393 |
+
className="animate-fade-in grid grid-cols-1 md:[grid-template-columns:minmax(0,1fr)_minmax(0,1.1fr)]"
|
| 394 |
style={{
|
| 395 |
marginTop: '24px',
|
| 396 |
paddingTop: '20px',
|
| 397 |
borderTop: '1px solid #e8e5e1',
|
|
|
|
|
|
|
| 398 |
columnGap: '32px',
|
| 399 |
rowGap: '14px',
|
| 400 |
alignItems: 'start',
|
|
|
|
| 497 |
{/* KPI row */}
|
| 498 |
<div
|
| 499 |
data-tour="metrics"
|
| 500 |
+
className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8"
|
| 501 |
style={{
|
|
|
|
|
|
|
|
|
|
| 502 |
borderTop: '1px solid #e8e5e1',
|
| 503 |
paddingTop: '20px',
|
| 504 |
marginTop: '12px',
|
frontend/src/pages/Disbursements.tsx
CHANGED
|
@@ -322,7 +322,8 @@ function SimulatedPayoutTab({
|
|
| 322 |
border: '1px solid #e8e5e1',
|
| 323 |
background: '#fcfaf7',
|
| 324 |
borderRadius: '2px',
|
| 325 |
-
|
|
|
|
| 326 |
}}
|
| 327 |
>
|
| 328 |
{zonesWithEnrollees.map((z) => {
|
|
@@ -879,10 +880,8 @@ export default function Disbursements() {
|
|
| 879 |
{/* Metric row */}
|
| 880 |
<div
|
| 881 |
data-tour="disbursements-metrics"
|
|
|
|
| 882 |
style={{
|
| 883 |
-
display: 'grid',
|
| 884 |
-
gridTemplateColumns: 'repeat(4, 1fr)',
|
| 885 |
-
gap: '32px',
|
| 886 |
borderTop: '1px solid #e8e5e1',
|
| 887 |
paddingTop: '28px',
|
| 888 |
marginBottom: '40px',
|
|
|
|
| 322 |
border: '1px solid #e8e5e1',
|
| 323 |
background: '#fcfaf7',
|
| 324 |
borderRadius: '2px',
|
| 325 |
+
width: '100%',
|
| 326 |
+
maxWidth: '320px',
|
| 327 |
}}
|
| 328 |
>
|
| 329 |
{zonesWithEnrollees.map((z) => {
|
|
|
|
| 880 |
{/* Metric row */}
|
| 881 |
<div
|
| 882 |
data-tour="disbursements-metrics"
|
| 883 |
+
className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8"
|
| 884 |
style={{
|
|
|
|
|
|
|
|
|
|
| 885 |
borderTop: '1px solid #e8e5e1',
|
| 886 |
paddingTop: '28px',
|
| 887 |
marginBottom: '40px',
|
frontend/src/pages/Pipeline.tsx
CHANGED
|
@@ -44,10 +44,8 @@ function HowItWorksSection() {
|
|
| 44 |
return (
|
| 45 |
<div style={{ marginBottom: '24px' }}>
|
| 46 |
<div
|
|
|
|
| 47 |
style={{
|
| 48 |
-
display: 'grid',
|
| 49 |
-
gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
|
| 50 |
-
gap: '28px',
|
| 51 |
borderTop: '1px solid #e8e5e1',
|
| 52 |
paddingTop: '18px',
|
| 53 |
}}
|
|
@@ -109,11 +107,7 @@ function ScalingCostPanel() {
|
|
| 109 |
What it costs to run at scale
|
| 110 |
</div>
|
| 111 |
<div
|
| 112 |
-
|
| 113 |
-
display: 'grid',
|
| 114 |
-
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
| 115 |
-
gap: '32px',
|
| 116 |
-
}}
|
| 117 |
>
|
| 118 |
{TIERS.map((tier) => (
|
| 119 |
<div key={tier.label} style={{ borderTop: '1px solid #e8e5e1', paddingTop: '14px' }}>
|
|
@@ -550,11 +544,7 @@ export default function Pipeline() {
|
|
| 550 |
Processing steps
|
| 551 |
</div>
|
| 552 |
<div
|
| 553 |
-
|
| 554 |
-
display: 'grid',
|
| 555 |
-
gridTemplateColumns: 'repeat(6, minmax(0, 1fr))',
|
| 556 |
-
gap: '16px',
|
| 557 |
-
}}
|
| 558 |
>
|
| 559 |
{run.steps.map((step, i) => (
|
| 560 |
<div
|
|
|
|
| 44 |
return (
|
| 45 |
<div style={{ marginBottom: '24px' }}>
|
| 46 |
<div
|
| 47 |
+
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-7"
|
| 48 |
style={{
|
|
|
|
|
|
|
|
|
|
| 49 |
borderTop: '1px solid #e8e5e1',
|
| 50 |
paddingTop: '18px',
|
| 51 |
}}
|
|
|
|
| 107 |
What it costs to run at scale
|
| 108 |
</div>
|
| 109 |
<div
|
| 110 |
+
className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
>
|
| 112 |
{TIERS.map((tier) => (
|
| 113 |
<div key={tier.label} style={{ borderTop: '1px solid #e8e5e1', paddingTop: '14px' }}>
|
|
|
|
| 544 |
Processing steps
|
| 545 |
</div>
|
| 546 |
<div
|
| 547 |
+
className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
>
|
| 549 |
{run.steps.map((step, i) => (
|
| 550 |
<div
|
frontend/src/pages/ProgramDesigner.tsx
CHANGED
|
@@ -132,10 +132,8 @@ export default function ProgramDesigner() {
|
|
| 132 |
<div data-tour="program-scope" style={{ marginBottom: '48px' }}>
|
| 133 |
<div className="section-header">Who's covered</div>
|
| 134 |
<div
|
|
|
|
| 135 |
style={{
|
| 136 |
-
display: 'grid',
|
| 137 |
-
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
|
| 138 |
-
gap: '32px',
|
| 139 |
borderTop: '1px solid #e8e5e1',
|
| 140 |
paddingTop: '24px',
|
| 141 |
}}
|
|
@@ -167,12 +165,10 @@ export default function ProgramDesigner() {
|
|
| 167 |
<div data-tour="program-cost" style={{ marginBottom: '48px' }}>
|
| 168 |
<div className="section-header">What it costs</div>
|
| 169 |
<div
|
|
|
|
| 170 |
style={{
|
| 171 |
borderTop: '1px solid #e8e5e1',
|
| 172 |
paddingTop: '28px',
|
| 173 |
-
display: 'grid',
|
| 174 |
-
gridTemplateColumns: 'minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr)',
|
| 175 |
-
gap: '32px',
|
| 176 |
alignItems: 'end',
|
| 177 |
}}
|
| 178 |
>
|
|
|
|
| 132 |
<div data-tour="program-scope" style={{ marginBottom: '48px' }}>
|
| 133 |
<div className="section-header">Who's covered</div>
|
| 134 |
<div
|
| 135 |
+
className="grid grid-cols-1 sm:grid-cols-2 gap-6 sm:gap-8"
|
| 136 |
style={{
|
|
|
|
|
|
|
|
|
|
| 137 |
borderTop: '1px solid #e8e5e1',
|
| 138 |
paddingTop: '24px',
|
| 139 |
}}
|
|
|
|
| 165 |
<div data-tour="program-cost" style={{ marginBottom: '48px' }}>
|
| 166 |
<div className="section-header">What it costs</div>
|
| 167 |
<div
|
| 168 |
+
className="grid grid-cols-1 md:grid-cols-[1.4fr_1fr_1fr] gap-6 md:gap-8"
|
| 169 |
style={{
|
| 170 |
borderTop: '1px solid #e8e5e1',
|
| 171 |
paddingTop: '28px',
|
|
|
|
|
|
|
|
|
|
| 172 |
alignItems: 'end',
|
| 173 |
}}
|
| 174 |
>
|
frontend/src/pages/Zones.tsx
CHANGED
|
@@ -97,10 +97,8 @@ export default function Zones() {
|
|
| 97 |
{/* Metric row */}
|
| 98 |
<div
|
| 99 |
data-tour="zones-metrics"
|
|
|
|
| 100 |
style={{
|
| 101 |
-
display: 'grid',
|
| 102 |
-
gridTemplateColumns: 'repeat(4, 1fr)',
|
| 103 |
-
gap: '32px',
|
| 104 |
borderTop: '1px solid #e8e5e1',
|
| 105 |
paddingTop: '28px',
|
| 106 |
marginBottom: '36px',
|
|
|
|
| 97 |
{/* Metric row */}
|
| 98 |
<div
|
| 99 |
data-tour="zones-metrics"
|
| 100 |
+
className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8"
|
| 101 |
style={{
|
|
|
|
|
|
|
|
|
|
| 102 |
borderTop: '1px solid #e8e5e1',
|
| 103 |
paddingTop: '28px',
|
| 104 |
marginBottom: '36px',
|