feat(redesign): 외주 페이지 + 의뢰폼 라이트 전환

페이지: 다크 캔버스/HeroField/스크림 제거, surface↔surface-alt 교차 8섹션.
HERO 비대칭 2단(우 FeedMock 목업). 앵커(#showcase/#portfolio/#process/#contact) 유지.
폼: --jsm-dark-* 전량 라이트 치환, jsm-dark-form 제거. 흰 카드 위 surface-alt 입력으로 가독성 확보.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2N6SziVSPfavx1j5rAs52
This commit is contained in:
2026-06-30 14:48:00 +09:00
parent 897e37f14e
commit c3be57ea1f
2 changed files with 144 additions and 400 deletions

View File

@@ -16,9 +16,9 @@ const KOR_TIGHT = { letterSpacing: '-0.02em' } as const;
const KOR_BODY = { letterSpacing: '-0.01em' } as const; const KOR_BODY = { letterSpacing: '-0.01em' } as const;
const INPUT_STYLE = { const INPUT_STYLE = {
background: 'var(--jsm-dark-surface)', background: 'var(--jsm-surface-alt)',
border: '1px solid var(--jsm-dark-line)', border: '1px solid var(--jsm-line)',
color: 'var(--jsm-dark-ink)', color: 'var(--jsm-ink)',
} as const; } as const;
const PROJECT_TYPES = [ const PROJECT_TYPES = [
@@ -195,13 +195,13 @@ export default function OutsourcingRequestForm() {
ref={setHeadingRef} ref={setHeadingRef}
tabIndex={-1} tabIndex={-1}
className="text-xl font-bold break-keep outline-none" className="text-xl font-bold break-keep outline-none"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }} style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}
> >
</h3> </h3>
<p <p
className="mt-3 text-sm leading-relaxed break-keep" className="mt-3 text-sm leading-relaxed break-keep"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
2 . 2 .
</p> </p>
@@ -218,7 +218,7 @@ export default function OutsourcingRequestForm() {
</Link> </Link>
<p <p
className="mt-3 text-xs leading-relaxed break-keep" className="mt-3 text-xs leading-relaxed break-keep"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
. .
</p> </p>
@@ -232,7 +232,7 @@ export default function OutsourcingRequestForm() {
const canAdvance = stepValid(step); const canAdvance = stepValid(step);
return ( return (
<div className="jsm-dark-form"> <div>
{/* 진행 표시기 */} {/* 진행 표시기 */}
<ol className="flex items-center gap-2 mb-7" aria-label="진행 단계"> <ol className="flex items-center gap-2 mb-7" aria-label="진행 단계">
{STEPS.map((s, i) => { {STEPS.map((s, i) => {
@@ -244,7 +244,7 @@ export default function OutsourcingRequestForm() {
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-bold shrink-0 transition-colors" className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-bold shrink-0 transition-colors"
style={ style={
state === 'upcoming' state === 'upcoming'
? { background: 'var(--jsm-dark-surface)', color: 'var(--jsm-dark-soft)', boxShadow: 'inset 0 0 0 1px var(--jsm-dark-line)' } ? { background: 'var(--jsm-surface-alt)', color: 'var(--jsm-ink-soft)', boxShadow: 'inset 0 0 0 1px var(--jsm-line)' }
: { background: 'var(--jsm-accent)', color: '#ffffff' } : { background: 'var(--jsm-accent)', color: '#ffffff' }
} }
aria-current={state === 'current' ? 'step' : undefined} aria-current={state === 'current' ? 'step' : undefined}
@@ -255,7 +255,7 @@ export default function OutsourcingRequestForm() {
className="text-xs font-semibold truncate hidden sm:inline" className="text-xs font-semibold truncate hidden sm:inline"
style={{ style={{
color: color:
state === 'upcoming' ? 'var(--jsm-dark-soft)' : 'var(--jsm-dark-ink)', state === 'upcoming' ? 'var(--jsm-ink-soft)' : 'var(--jsm-ink)',
...KOR_BODY, ...KOR_BODY,
}} }}
> >
@@ -264,7 +264,7 @@ export default function OutsourcingRequestForm() {
{i < STEPS.length - 1 && ( {i < STEPS.length - 1 && (
<span <span
className="w-4 sm:w-6 h-px shrink-0" className="w-4 sm:w-6 h-px shrink-0"
style={{ background: 'var(--jsm-dark-line)' }} style={{ background: 'var(--jsm-line)' }}
aria-hidden aria-hidden
/> />
)} )}
@@ -281,13 +281,13 @@ export default function OutsourcingRequestForm() {
ref={setHeadingRef} ref={setHeadingRef}
tabIndex={-1} tabIndex={-1}
className="text-lg font-bold break-keep outline-none mb-1" className="text-lg font-bold break-keep outline-none mb-1"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }} style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}
> >
? ?
</legend> </legend>
<p <p
className="text-sm leading-relaxed break-keep mb-5" className="text-sm leading-relaxed break-keep mb-5"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
. .
</p> </p>
@@ -303,12 +303,12 @@ export default function OutsourcingRequestForm() {
className="px-4 py-3.5 rounded-lg text-sm font-semibold text-center break-keep transition-colors outline-none focus-visible:ring-2 focus-visible:ring-[var(--jsm-accent)]" className="px-4 py-3.5 rounded-lg text-sm font-semibold text-center break-keep transition-colors outline-none focus-visible:ring-2 focus-visible:ring-[var(--jsm-accent)]"
style={{ style={{
border: selected border: selected
? '1px solid var(--jsm-accent-bright)' ? '1px solid var(--jsm-accent)'
: '1px solid var(--jsm-dark-line)', : '1px solid var(--jsm-line)',
background: selected background: selected
? 'rgba(96,165,250,0.12)' ? 'var(--jsm-accent-soft)'
: 'var(--jsm-dark-surface)', : 'var(--jsm-surface-alt)',
color: selected ? 'var(--jsm-accent-bright)' : 'var(--jsm-dark-ink)', color: selected ? 'var(--jsm-accent)' : 'var(--jsm-ink)',
...KOR_BODY, ...KOR_BODY,
}} }}
> >
@@ -327,13 +327,13 @@ export default function OutsourcingRequestForm() {
ref={setHeadingRef} ref={setHeadingRef}
tabIndex={-1} tabIndex={-1}
className="text-lg font-bold break-keep outline-none mb-1" className="text-lg font-bold break-keep outline-none mb-1"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }} style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}
> >
</h3> </h3>
<p <p
className="text-sm leading-relaxed break-keep mb-5" className="text-sm leading-relaxed break-keep mb-5"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
. . . .
</p> </p>
@@ -341,7 +341,7 @@ export default function OutsourcingRequestForm() {
<fieldset className="mb-6"> <fieldset className="mb-6">
<legend <legend
className="text-sm font-semibold mb-2.5" className="text-sm font-semibold mb-2.5"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink)', ...KOR_BODY }}
> >
</legend> </legend>
@@ -360,7 +360,7 @@ export default function OutsourcingRequestForm() {
<fieldset> <fieldset>
<legend <legend
className="text-sm font-semibold mb-2.5" className="text-sm font-semibold mb-2.5"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink)', ...KOR_BODY }}
> >
</legend> </legend>
@@ -385,13 +385,13 @@ export default function OutsourcingRequestForm() {
ref={setHeadingRef} ref={setHeadingRef}
tabIndex={-1} tabIndex={-1}
className="text-lg font-bold break-keep outline-none mb-1" className="text-lg font-bold break-keep outline-none mb-1"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }} style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}
> >
</h3> </h3>
<p <p
className="text-sm leading-relaxed break-keep mb-5" className="text-sm leading-relaxed break-keep mb-5"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
. 10 . . 10 .
</p> </p>
@@ -413,7 +413,7 @@ export default function OutsourcingRequestForm() {
/> />
<p <p
className="mt-1.5 text-xs" className="mt-1.5 text-xs"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
{trimmedMessage.length}/10 {trimmedMessage.length}/10
</p> </p>
@@ -427,13 +427,13 @@ export default function OutsourcingRequestForm() {
ref={setHeadingRef} ref={setHeadingRef}
tabIndex={-1} tabIndex={-1}
className="text-lg font-bold break-keep outline-none mb-1" className="text-lg font-bold break-keep outline-none mb-1"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }} style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}
> >
? ?
</h3> </h3>
<p <p
className="text-sm leading-relaxed break-keep mb-5" className="text-sm leading-relaxed break-keep mb-5"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}
> >
2 . 2 .
</p> </p>
@@ -443,7 +443,7 @@ export default function OutsourcingRequestForm() {
<label <label
htmlFor="req-name" htmlFor="req-name"
className="block text-sm font-medium mb-1.5" className="block text-sm font-medium mb-1.5"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink)', ...KOR_BODY }}
> >
<span style={{ color: 'var(--jsm-accent)' }}>*</span> <span style={{ color: 'var(--jsm-accent)' }}>*</span>
</label> </label>
@@ -465,7 +465,7 @@ export default function OutsourcingRequestForm() {
<label <label
htmlFor="req-email" htmlFor="req-email"
className="block text-sm font-medium mb-1.5" className="block text-sm font-medium mb-1.5"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink)', ...KOR_BODY }}
> >
<span style={{ color: 'var(--jsm-accent)' }}>*</span> <span style={{ color: 'var(--jsm-accent)' }}>*</span>
</label> </label>
@@ -487,7 +487,7 @@ export default function OutsourcingRequestForm() {
<label <label
htmlFor="req-phone" htmlFor="req-phone"
className="block text-sm font-medium mb-1.5" className="block text-sm font-medium mb-1.5"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_BODY }} style={{ color: 'var(--jsm-ink)', ...KOR_BODY }}
> >
</label> </label>
@@ -530,10 +530,10 @@ export default function OutsourcingRequestForm() {
type="button" type="button"
onClick={goPrev} onClick={goPrev}
disabled={submitting} disabled={submitting}
className="px-5 py-3 rounded-lg text-sm font-semibold border transition-colors hover:bg-[var(--jsm-dark-surface)] disabled:opacity-50 disabled:cursor-not-allowed" className="px-5 py-3 rounded-lg text-sm font-semibold border transition-colors hover:bg-[var(--jsm-surface-alt)] disabled:opacity-50 disabled:cursor-not-allowed"
style={{ style={{
...INPUT_STYLE, ...INPUT_STYLE,
borderColor: 'var(--jsm-dark-line)', borderColor: 'var(--jsm-line)',
...KOR_BODY, ...KOR_BODY,
}} }}
> >
@@ -548,7 +548,7 @@ export default function OutsourcingRequestForm() {
className="flex-1 py-3 rounded-lg text-sm font-semibold text-white transition-colors" className="flex-1 py-3 rounded-lg text-sm font-semibold text-white transition-colors"
style={{ style={{
background: !canAdvance || submitting background: !canAdvance || submitting
? 'var(--jsm-dark-line)' ? 'var(--jsm-line)'
: 'var(--jsm-accent)', : 'var(--jsm-accent)',
cursor: !canAdvance || submitting ? 'not-allowed' : 'pointer', cursor: !canAdvance || submitting ? 'not-allowed' : 'pointer',
...KOR_BODY, ...KOR_BODY,
@@ -563,7 +563,7 @@ export default function OutsourcingRequestForm() {
disabled={!canAdvance} disabled={!canAdvance}
className="flex-1 inline-flex items-center justify-center gap-2 py-3 rounded-lg text-sm font-semibold text-white transition-colors" className="flex-1 inline-flex items-center justify-center gap-2 py-3 rounded-lg text-sm font-semibold text-white transition-colors"
style={{ style={{
background: canAdvance ? 'var(--jsm-accent)' : 'var(--jsm-dark-line)', background: canAdvance ? 'var(--jsm-accent)' : 'var(--jsm-line)',
cursor: canAdvance ? 'pointer' : 'not-allowed', cursor: canAdvance ? 'pointer' : 'not-allowed',
...KOR_BODY, ...KOR_BODY,
}} }}
@@ -595,9 +595,9 @@ function Chip({
aria-pressed={selected} aria-pressed={selected}
className="px-4 py-2.5 rounded-lg text-sm font-semibold break-keep transition-colors outline-none focus-visible:ring-2 focus-visible:ring-[var(--jsm-accent)]" className="px-4 py-2.5 rounded-lg text-sm font-semibold break-keep transition-colors outline-none focus-visible:ring-2 focus-visible:ring-[var(--jsm-accent)]"
style={{ style={{
border: selected ? '1px solid var(--jsm-accent-bright)' : '1px solid var(--jsm-dark-line)', border: selected ? '1px solid var(--jsm-accent)' : '1px solid var(--jsm-line)',
background: selected ? 'rgba(96,165,250,0.12)' : 'var(--jsm-dark-surface)', background: selected ? 'var(--jsm-accent-soft)' : 'var(--jsm-surface-alt)',
color: selected ? 'var(--jsm-accent-bright)' : 'var(--jsm-dark-ink)', color: selected ? 'var(--jsm-accent)' : 'var(--jsm-ink)',
...KOR_BODY, ...KOR_BODY,
}} }}
> >

View File

@@ -2,15 +2,15 @@ import Link from 'next/link';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import OutsourcingRequestForm from '@/app/components/OutsourcingRequestForm'; import OutsourcingRequestForm from '@/app/components/OutsourcingRequestForm';
import HeroField from '@/app/components/deepfield/HeroField';
import ShowcaseGrid from '@/app/components/deepfield/ShowcaseGrid'; import ShowcaseGrid from '@/app/components/deepfield/ShowcaseGrid';
import ScrollReveal from '@/app/components/deepfield/ScrollReveal'; import ScrollReveal from '@/app/components/deepfield/ScrollReveal';
import MockWindow from '@/app/components/mock/MockWindow';
import { FeedMock } from '@/app/components/mock/screens';
import { SHOWCASE_SLOTS } from '@/lib/showcase'; import { SHOWCASE_SLOTS } from '@/lib/showcase';
// 외주 개발 의뢰 페이지 (서버 컴포넌트) — Deep Field 다크 캔버스. // 외주 개발 의뢰 페이지 (서버 컴포넌트) — 라이트 고craft.
// PublicShell이 TopNav(h-16, /outsourcing 다크 인지)·푸터·main 배경(라이트)을 제공한다. // PublicShell의 단일 라이트 셸을 따르며, 메인(/)과 동일한 비주얼 언어
// 이 페이지는 자기 풀-블리드 다크 배경을 소유해 main의 라이트 배경을 덮고, // (surface↔surface-alt 교차 + accent 모노 라벨 헤더 + 카드 스펙)를 공유한다.
// 메인(/)과 동일한 비주얼 언어(다크 루트 div + -mt-16 hero + 섹션 border-t 리듬 + 모노 라벨 헤더)를 따른다.
export const metadata: Metadata = { export const metadata: Metadata = {
title: '외주 개발', title: '외주 개발',
@@ -22,30 +22,12 @@ const KOR_TIGHT = { letterSpacing: '-0.02em' } as const;
const KOR_BODY = { letterSpacing: '-0.01em' } as const; const KOR_BODY = { letterSpacing: '-0.01em' } as const;
const FIELDS = [ const FIELDS = [
{ { t: '웹 서비스 개발', d: '회원·결제·관리자까지, 실제로 굴러가는 서비스를 기획부터 배포까지 만들어 드립니다.' },
t: '웹 서비스 개발', { t: '웹사이트 제작', d: '기업 소개·포트폴리오·랜딩 페이지를 반응형·SEO까지 갖춰 제작합니다.' },
d: '회원·결제·관리자까지, 실제로 굴러가는 서비스를 기획부터 배포까지 만들어 드립니다.', { t: '업무 자동화', d: 'RPA·엑셀 집계·웹 크롤링으로 반복 업무를 사람 손에서 떼어냅니다.' },
}, { t: 'API·백엔드', d: '데이터 모델 설계부터 인증·외부 연동까지 안정적인 서버를 구축합니다.' },
{ { t: '텔레그램·디스코드 봇', d: '알림·명령·자동 응답 봇으로 운영과 커뮤니티 관리를 자동화합니다.' },
t: '웹사이트 제작', { t: 'AI 연동 개발', d: 'LLM·생성형 AI를 업무 흐름에 붙여 초안 작성·분류·요약을 자동화합니다.' },
d: '기업 소개·포트폴리오·랜딩 페이지를 반응형·SEO까지 갖춰 제작합니다.',
},
{
t: '업무 자동화',
d: 'RPA·엑셀 집계·웹 크롤링으로 반복 업무를 사람 손에서 떼어냅니다.',
},
{
t: 'API·백엔드',
d: '데이터 모델 설계부터 인증·외부 연동까지 안정적인 서버를 구축합니다.',
},
{
t: '텔레그램·디스코드 봇',
d: '알림·명령·자동 응답 봇으로 운영과 커뮤니티 관리를 자동화합니다.',
},
{
t: 'AI 연동 개발',
d: 'LLM·생성형 AI를 업무 흐름에 붙여 초안 작성·분류·요약을 자동화합니다.',
},
]; ];
const PROCESS = [ const PROCESS = [
@@ -57,143 +39,66 @@ const PROCESS = [
{ n: '06', t: '무상 하자보수 30일', d: '납품 후 30일간 결함·수정을 무상으로 대응해 안정화까지 책임집니다.' }, { n: '06', t: '무상 하자보수 30일', d: '납품 후 30일간 결함·수정을 무상으로 대응해 안정화까지 책임집니다.' },
]; ];
// 기존 work/freelance(lib/freelance-portfolio) 실사례를 다크 토큰 기준으로 재구성.
const CASES = [ const CASES = [
{ { t: '주식 자동매매 시스템', cat: '실시간 트레이딩 · 직접 운영 중', live: true, d: '텔레그램과 연동해 실시간으로 주문을 집행하고 체결·손익 리포트를 자동 전송합니다.', tags: ['Python', 'Telegram Bot', '실시간 주문'] },
t: '주식 자동매매 시스템', { t: '부동산 청약 자동 수집·매칭', cat: '크롤링 · 직접 운영 중', live: true, d: '공고를 주기적으로 크롤링해 조건에 맞는 매물만 골라내고, 신규 매칭을 즉시 푸시합니다.', tags: ['Python', '크롤링', '조건 매칭'] },
cat: '실시간 트레이딩 · 직접 운영 중', { t: 'AI 콘텐츠 자동화 파이프라인', cat: 'AI 연동 · 직접 운영 중', live: true, d: '생성부터 검수, 발행까지 사람이 개입할 지점만 남기고 전 과정을 자동으로 연결합니다.', tags: ['AI 연동', '검수 워크플로우', '자동 발행'] },
live: true, { t: 'Gmail 자동화 RPA', cat: 'RPA · 납품 완료', live: false, d: '거래처 이메일 수신 시 자동 분류, 답장 초안 작성, 담당자 알림을 전송합니다.', tags: ['Python', 'Gmail API'] },
d: '텔레그램과 연동해 실시간으로 주문을 집행하고 체결·손익 리포트를 자동 전송합니다.', { t: '쇼핑몰 가격 모니터링 봇', cat: '웹 스크래핑 · 납품 완료', live: false, d: '경쟁사 상품 가격을 매일 모니터링해 변동 시 텔레그램으로 즉시 알립니다.', tags: ['Python', 'Selenium', 'Telegram Bot'] },
tags: ['Python', 'Telegram Bot', '실시간 주문'], { t: '영업 일보 자동화 시스템', cat: '엑셀 자동화 · 납품 완료', live: false, d: '엑셀 데이터를 자동 집계해 일·주·월별 보고서 PDF를 생성하고 매일 09시 발송합니다.', tags: ['Python', 'OpenPyXL', 'ReportLab'] },
},
{
t: '부동산 청약 자동 수집·매칭',
cat: '크롤링 · 직접 운영 중',
live: true,
d: '공고를 주기적으로 크롤링해 조건에 맞는 매물만 골라내고, 신규 매칭을 즉시 푸시합니다.',
tags: ['Python', '크롤링', '조건 매칭'],
},
{
t: 'AI 콘텐츠 자동화 파이프라인',
cat: 'AI 연동 · 직접 운영 중',
live: true,
d: '생성부터 검수, 발행까지 사람이 개입할 지점만 남기고 전 과정을 자동으로 연결합니다.',
tags: ['AI 연동', '검수 워크플로우', '자동 발행'],
},
{
t: 'Gmail 자동화 RPA',
cat: 'RPA · 납품 완료',
live: false,
d: '거래처 이메일 수신 시 자동 분류, 답장 초안 작성, 담당자 알림을 전송합니다.',
tags: ['Python', 'Gmail API'],
},
{
t: '쇼핑몰 가격 모니터링 봇',
cat: '웹 스크래핑 · 납품 완료',
live: false,
d: '경쟁사 상품 가격을 매일 모니터링해 변동 시 텔레그램으로 즉시 알립니다.',
tags: ['Python', 'Selenium', 'Telegram Bot'],
},
{
t: '영업 일보 자동화 시스템',
cat: '엑셀 자동화 · 납품 완료',
live: false,
d: '엑셀 데이터를 자동 집계해 일·주·월별 보고서 PDF를 생성하고 매일 09시 발송합니다.',
tags: ['Python', 'OpenPyXL', 'ReportLab'],
},
]; ];
const FAQ = [ const FAQ = [
{ { q: '견적은 어떻게 산정되나요?', a: '기능 범위와 구현 난이도를 기준으로 산정합니다. 상담에서 필요한 기능을 함께 정리한 뒤, 영업일 2일 내에 범위·일정·금액을 명시한 견적으로 회신드립니다. 추측으로 부풀리지 않고 실제 작업량 기준으로 잡습니다.' },
q: '견적은 어떻게 산정되나요?', { q: '수정 요청은 몇 번까지 가능한가요?', a: '합의한 범위 안에서는 2회까지 무상으로 수정해 드립니다. 범위를 벗어나는 기능 추가나 방향 전환은 별도로 협의해 진행합니다. 무엇이 범위 안/밖인지는 착수 전 견적에 미리 명시합니다.' },
a: '기능 범위와 구현 난이도를 기준으로 산정합니다. 상담에서 필요한 기능을 함께 정리한 뒤, 영업일 2일 내에 범위·일정·금액을 명시한 견적으로 회신드립니다. 추측으로 부풀리지 않고 실제 작업량 기준으로 잡습니다.', { q: '소스코드도 제공되나요?', a: '제공됩니다. 잔금 완납 시 전체 소스코드와 배포·실행 문서를 함께 전달합니다. 직접 운영하시거나 다른 개발자에게 이어 맡기셔도 문제없도록 인도합니다.' },
}, { q: '납품 후 유지보수는요?', a: '납품일로부터 30일간 결함·오류를 무상으로 하자보수합니다. 이후 기능 추가나 지속 운영이 필요하면 월 단위 유지보수 계약으로 이어갈 수 있습니다.' },
{
q: '수정 요청은 몇 번까지 가능한가요?',
a: '합의한 범위 안에서는 2회까지 무상으로 수정해 드립니다. 범위를 벗어나는 기능 추가나 방향 전환은 별도로 협의해 진행합니다. 무엇이 범위 안/밖인지는 착수 전 견적에 미리 명시합니다.',
},
{
q: '소스코드도 제공되나요?',
a: '제공됩니다. 잔금 완납 시 전체 소스코드와 배포·실행 문서를 함께 전달합니다. 직접 운영하시거나 다른 개발자에게 이어 맡기셔도 문제없도록 인도합니다.',
},
{
q: '납품 후 유지보수는요?',
a: '납품일로부터 30일간 결함·오류를 무상으로 하자보수합니다. 이후 기능 추가나 지속 운영이 필요하면 월 단위 유지보수 계약으로 이어갈 수 있습니다.',
},
]; ];
function ArrowRight() { function ArrowRight() {
return ( return (
<svg <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M5 12h14" /> <path d="M5 12h14" />
<path d="m13 5 7 7-7 7" /> <path d="m13 5 7 7-7 7" />
</svg> </svg>
); );
} }
function Eyebrow({ children }: { children: React.ReactNode }) {
return (
<p className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" style={{ color: 'var(--jsm-accent)' }}>
{children}
</p>
);
}
export default function OutsourcingPage() { export default function OutsourcingPage() {
return ( return (
// 풀-블리드 다크 캔버스 — main의 라이트 배경을 덮는다. <>
<div style={{ background: 'var(--jsm-dark-bg)', color: 'var(--jsm-dark-ink)' }}> {/* ─────────────────── 1. HERO ─────────────────── */}
{/* ─────────────────── 1. HERO (축약 ~60vh) ─────────────────── */} <section style={{ background: 'var(--jsm-surface)' }}>
{/* -mt-16 pt-16: 고정 헤더 아래로 끌어올려 상단 라이트 띠 제거 */} <div className="mx-auto grid max-w-6xl items-center gap-12 px-6 pt-20 pb-16 lg:grid-cols-2 lg:gap-16 lg:px-8 lg:pt-28 lg:pb-24">
<section className="relative -mt-16 flex min-h-[60vh] items-center overflow-hidden pt-16"> <div>
<HeroField className="absolute inset-0" /> <span className="inline-flex items-center gap-2 font-mono text-[11px] uppercase tracking-[0.22em]" style={{ color: 'var(--jsm-accent)' }}>
{/* 콘텐츠 가독성용 스크림 (radial 광원 위 텍스트 대비) */} <span className="inline-block h-1 w-1 rounded-full" style={{ background: 'var(--jsm-accent)' }} />
<div
aria-hidden
className="pointer-events-none absolute inset-0"
style={{
background:
'linear-gradient(to bottom, rgba(7,13,26,0.55) 0%, transparent 30%, transparent 62%, rgba(7,13,26,0.78) 100%)',
}}
/>
<div className="relative z-10 mx-auto w-full max-w-6xl px-6 py-20 lg:px-8 lg:py-24">
<div className="max-w-3xl">
<span
className="mb-7 inline-flex items-center gap-2 font-mono text-[11px] uppercase tracking-[0.22em]"
style={{ color: 'var(--jsm-accent-bright)' }}
>
<span
className="inline-block h-1 w-1 rounded-full"
style={{ background: 'var(--jsm-accent-bright)' }}
/>
outsourcing outsourcing
</span> </span>
<h1 <h1
className="font-bold break-keep" className="mt-6 font-extrabold break-keep"
style={{ style={{ color: 'var(--jsm-ink)', fontSize: 'clamp(2.3rem, 6vw, 3.6rem)', lineHeight: 1.1, letterSpacing: '-0.035em' }}
color: 'var(--jsm-dark-ink)',
fontSize: 'clamp(2.4rem, 7vw, 5rem)',
lineHeight: 1.06,
letterSpacing: '-0.04em',
}}
> >
<br /> <br />
<span style={{ color: 'var(--jsm-accent-bright)' }}>.</span> <span style={{ color: 'var(--jsm-accent)' }}>.</span>
</h1> </h1>
<p <p className="mt-7 max-w-xl break-keep text-lg leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-7 max-w-2xl break-keep text-lg leading-relaxed lg:text-xl"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
. . . .
</p> </p>
<div className="mt-10 flex flex-col gap-3 sm:flex-row"> <div className="mt-9 flex flex-col gap-3 sm:flex-row">
<Link <Link
href="#contact" href="#contact"
className="inline-flex items-center justify-center gap-2 rounded-lg px-6 py-3.5 font-semibold text-white transition-transform duration-200 hover:translate-y-[-1px]" className="inline-flex items-center justify-center gap-2 rounded-lg px-6 py-3.5 font-semibold text-white transition-colors duration-200 hover:bg-[var(--jsm-accent-hover)]"
style={{ background: 'var(--jsm-accent)', ...KOR_BODY }} style={{ background: 'var(--jsm-accent)', ...KOR_BODY }}
> >
@@ -201,119 +106,72 @@ export default function OutsourcingPage() {
</Link> </Link>
<Link <Link
href="#showcase" href="#showcase"
className="inline-flex items-center justify-center gap-2 rounded-lg border px-6 py-3.5 font-semibold transition-colors duration-200 hover:bg-[var(--jsm-dark-surface)]" className="inline-flex items-center justify-center gap-2 rounded-lg border px-6 py-3.5 font-semibold transition-colors duration-200 hover:bg-[var(--jsm-surface-alt)]"
style={{ style={{ color: 'var(--jsm-ink)', borderColor: 'var(--jsm-line)', ...KOR_BODY }}
color: 'var(--jsm-dark-ink)',
borderColor: 'var(--jsm-dark-line)',
...KOR_BODY,
}}
> >
</Link> </Link>
</div> </div>
</div> </div>
<div className="lg:pl-4">
<MockWindow title="telegram-bot.log">
<FeedMock />
</MockWindow>
</div>
</div> </div>
</section> </section>
{/* ─────────────────── 2. SHOWCASE (풀 그리드) ─────────────────── */} {/* ─────────────────── 2. SHOWCASE (풀 그리드) ─────────────────── */}
<section id="showcase" className="scroll-mt-20 border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section id="showcase" className="scroll-mt-20" style={{ background: 'var(--jsm-surface-alt)' }}>
{/* 하위 호환: 기존 /outsourcing#portfolio 링크(메인 footer 등)용 앵커 유지 */} {/* 하위 호환: 기존 /outsourcing#portfolio 링크 앵커 유지 */}
<div id="portfolio" className="scroll-mt-20" /> <div id="portfolio" className="scroll-mt-20" />
<div className="mx-auto max-w-6xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-6xl px-6 py-20 lg:px-8 lg:py-28">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>showcase</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.6rem] lg:leading-[1.12]" style={{ color: 'var(--jsm-ink)', letterSpacing: '-0.03em' }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
showcase
</p>
<h2
className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.75rem] lg:leading-[1.12]"
style={{ color: 'var(--jsm-dark-ink)', letterSpacing: '-0.03em' }}
>
</h2> </h2>
</ScrollReveal> </ScrollReveal>
<div className="mt-12">
<div className="mt-14">
<ShowcaseGrid slots={SHOWCASE_SLOTS} variant="full" /> <ShowcaseGrid slots={SHOWCASE_SLOTS} variant="full" />
</div> </div>
</div> </div>
</section> </section>
{/* ─────────────────── 3. 운영 실사례 ─────────────────── */} {/* ─────────────────── 3. 운영 실사례 ─────────────────── */}
<section className="border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section style={{ background: 'var(--jsm-surface)' }}>
<div className="mx-auto max-w-6xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-6xl px-6 py-20 lg:px-8 lg:py-28">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>in production</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.6rem] lg:leading-[1.12]" style={{ color: 'var(--jsm-ink)', letterSpacing: '-0.03em' }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
in production
</p>
<h2
className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.75rem] lg:leading-[1.12]"
style={{ color: 'var(--jsm-dark-ink)', letterSpacing: '-0.03em' }}
>
, ,
</h2> </h2>
<p <p className="mt-4 max-w-xl break-keep leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-4 max-w-xl break-keep leading-relaxed"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
. . . .
</p> </p>
</ScrollReveal> </ScrollReveal>
<div className="mt-14 grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div className="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{CASES.map((c, i) => ( {CASES.map((c, i) => (
<ScrollReveal key={c.t} delay={i * 80}> <ScrollReveal key={c.t} delay={i * 80}>
<div <div className="flex h-full flex-col rounded-2xl border p-7" style={{ background: 'var(--jsm-surface)', borderColor: 'var(--jsm-line)' }}>
className="flex h-full flex-col rounded-2xl border p-7"
style={{
background: 'var(--jsm-dark-surface)',
borderColor: 'var(--jsm-dark-line)',
}}
>
<span <span
className="mb-5 inline-flex items-center gap-1.5 self-start rounded-full px-2.5 py-1 text-[11px] font-semibold" className="mb-5 inline-flex items-center gap-1.5 self-start rounded-full px-2.5 py-1 text-[11px] font-semibold"
style={ style={c.live ? { color: 'var(--jsm-accent)', background: 'var(--jsm-accent-soft)' } : { color: 'var(--jsm-ink-soft)', background: 'var(--jsm-surface-alt)' }}
c.live
? { color: 'var(--jsm-accent-bright)', background: 'rgba(96,165,250,0.12)' }
: { color: 'var(--jsm-dark-soft)', background: 'rgba(148,163,184,0.08)' }
}
> >
{c.live && ( {c.live && <span className="h-1.5 w-1.5 rounded-full" style={{ background: 'var(--jsm-accent)' }} />}
<span
className="h-1.5 w-1.5 rounded-full"
style={{ background: 'var(--jsm-accent-bright)' }}
/>
)}
{c.cat} {c.cat}
</span> </span>
<h3 <h3 className="break-keep text-lg font-bold" style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}>
className="break-keep text-lg font-bold"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }}
>
{c.t} {c.t}
</h3> </h3>
<p <p className="mt-2.5 flex-1 break-keep text-sm leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-2.5 flex-1 break-keep text-sm leading-relaxed"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
{c.d} {c.d}
</p> </p>
<div className="mt-5 flex flex-wrap gap-1.5"> <div className="mt-5 flex flex-wrap gap-1.5">
{c.tags.map((tag) => ( {c.tags.map((tag) => (
<span <span key={tag} className="rounded px-2.5 py-1 text-xs" style={{ color: 'var(--jsm-ink-soft)', background: 'var(--jsm-surface-alt)', ...KOR_BODY }}>
key={tag}
className="rounded px-2.5 py-1 text-xs"
style={{
color: 'var(--jsm-dark-soft)',
background: 'rgba(148,163,184,0.08)',
...KOR_BODY,
}}
>
{tag} {tag}
</span> </span>
))} ))}
@@ -326,43 +184,23 @@ export default function OutsourcingPage() {
</section> </section>
{/* ─────────────────── 4a. 제공 분야 ─────────────────── */} {/* ─────────────────── 4a. 제공 분야 ─────────────────── */}
<section className="border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section style={{ background: 'var(--jsm-surface-alt)' }}>
<div className="mx-auto max-w-6xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-6xl px-6 py-20 lg:px-8 lg:py-28">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>scope</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.6rem] lg:leading-[1.12]" style={{ color: 'var(--jsm-ink)', letterSpacing: '-0.03em' }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
scope
</p>
<h2
className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.75rem] lg:leading-[1.12]"
style={{ color: 'var(--jsm-dark-ink)', letterSpacing: '-0.03em' }}
>
</h2> </h2>
</ScrollReveal> </ScrollReveal>
<div className="mt-14 grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div className="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{FIELDS.map((f, i) => ( {FIELDS.map((f, i) => (
<ScrollReveal key={f.t} delay={i * 80}> <ScrollReveal key={f.t} delay={i * 80}>
<div <div className="h-full rounded-2xl border p-7" style={{ background: 'var(--jsm-surface)', borderColor: 'var(--jsm-line)' }}>
className="h-full rounded-2xl border p-7" <h3 className="break-keep text-lg font-bold" style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}>
style={{
background: 'var(--jsm-dark-surface)',
borderColor: 'var(--jsm-dark-line)',
}}
>
<h3
className="break-keep text-lg font-bold"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }}
>
{f.t} {f.t}
</h3> </h3>
<p <p className="mt-2.5 break-keep text-sm leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-2.5 break-keep text-sm leading-relaxed"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
{f.d} {f.d}
</p> </p>
</div> </div>
@@ -373,53 +211,29 @@ export default function OutsourcingPage() {
</section> </section>
{/* ─────────────────── 4b. 진행 프로세스 ─────────────────── */} {/* ─────────────────── 4b. 진행 프로세스 ─────────────────── */}
<section id="process" className="scroll-mt-20 border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section id="process" className="scroll-mt-20" style={{ background: 'var(--jsm-surface)' }}>
<div className="mx-auto max-w-6xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-6xl px-6 py-20 lg:px-8 lg:py-28">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>process</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.6rem] lg:leading-[1.12]" style={{ color: 'var(--jsm-ink)', letterSpacing: '-0.03em' }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
process
</p>
<h2
className="max-w-2xl break-keep text-3xl font-bold lg:text-[2.75rem] lg:leading-[1.12]"
style={{ color: 'var(--jsm-dark-ink)', letterSpacing: '-0.03em' }}
>
, ,
</h2> </h2>
</ScrollReveal> </ScrollReveal>
<div className="mt-14 grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div className="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{PROCESS.map((s, i) => ( {PROCESS.map((s, i) => (
<ScrollReveal key={s.n} delay={i * 80}> <ScrollReveal key={s.n} delay={i * 80}>
<div <div className="relative h-full rounded-2xl border p-7 lg:p-8" style={{ background: 'var(--jsm-surface)', borderColor: 'var(--jsm-line)' }}>
className="relative h-full rounded-2xl border p-7 lg:p-8"
style={{
background: 'var(--jsm-dark-surface)',
borderColor: 'var(--jsm-dark-line)',
}}
>
<span <span
className="relative z-10 inline-flex h-12 w-12 items-center justify-center rounded-full font-mono text-sm font-bold" className="relative z-10 inline-flex h-12 w-12 items-center justify-center rounded-full font-mono text-sm font-bold"
style={{ style={{ color: 'var(--jsm-accent)', background: 'var(--jsm-surface)', boxShadow: 'inset 0 0 0 1px var(--jsm-line)' }}
color: 'var(--jsm-accent-bright)',
background: 'var(--jsm-dark-bg)',
boxShadow: 'inset 0 0 0 1px var(--jsm-dark-line)',
}}
> >
{s.n} {s.n}
</span> </span>
<h3 <h3 className="mt-5 break-keep text-lg font-bold" style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}>
className="mt-5 break-keep text-lg font-bold"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }}
>
{s.t} {s.t}
</h3> </h3>
<p <p className="mt-2 break-keep text-sm leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-2 break-keep text-sm leading-relaxed"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
{s.d} {s.d}
</p> </p>
</div> </div>
@@ -430,57 +244,26 @@ export default function OutsourcingPage() {
</section> </section>
{/* ─────────────────── 5. FAQ ─────────────────── */} {/* ─────────────────── 5. FAQ ─────────────────── */}
<section className="border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section style={{ background: 'var(--jsm-surface-alt)' }}>
<div className="mx-auto max-w-3xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-3xl px-6 py-20 lg:px-8 lg:py-28">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>faq</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="break-keep text-3xl font-bold lg:text-[2.6rem] lg:leading-[1.12]" style={{ color: 'var(--jsm-ink)', letterSpacing: '-0.03em' }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
faq
</p>
<h2
className="break-keep text-3xl font-bold lg:text-[2.75rem] lg:leading-[1.12]"
style={{ color: 'var(--jsm-dark-ink)', letterSpacing: '-0.03em' }}
>
</h2> </h2>
</ScrollReveal> </ScrollReveal>
<div className="mt-14 space-y-3"> <div className="mt-12 space-y-3">
{FAQ.map((item, i) => ( {FAQ.map((item, i) => (
<ScrollReveal key={item.q} delay={i * 80}> <ScrollReveal key={item.q} delay={i * 80}>
<details <details className="group overflow-hidden rounded-2xl border" style={{ background: 'var(--jsm-surface)', borderColor: 'var(--jsm-line)' }}>
className="group overflow-hidden rounded-2xl border" <summary className="flex cursor-pointer list-none items-center justify-between gap-4 break-keep px-6 py-5 font-semibold" style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}>
style={{
background: 'var(--jsm-dark-surface)',
borderColor: 'var(--jsm-dark-line)',
}}
>
<summary
className="flex cursor-pointer list-none items-center justify-between gap-4 break-keep px-6 py-5 font-semibold"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }}
>
{item.q} {item.q}
<svg <svg className="shrink-0 transition-transform duration-200 group-open:rotate-45" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden style={{ color: 'var(--jsm-ink-soft)' }}>
className="shrink-0 transition-transform duration-200 group-open:rotate-45"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
aria-hidden
style={{ color: 'var(--jsm-dark-soft)' }}
>
<path d="M12 5v14M5 12h14" /> <path d="M12 5v14M5 12h14" />
</svg> </svg>
</summary> </summary>
<p <p className="break-keep px-6 pb-5 text-sm leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="break-keep px-6 pb-5 text-sm leading-relaxed"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
{item.a} {item.a}
</p> </p>
</details> </details>
@@ -491,59 +274,26 @@ export default function OutsourcingPage() {
</section> </section>
{/* ─────────────────── 6. 의뢰 폼 ─────────────────── */} {/* ─────────────────── 6. 의뢰 폼 ─────────────────── */}
<section id="contact" className="scroll-mt-20 border-t" style={{ borderColor: 'var(--jsm-dark-line)' }}> <section id="contact" className="scroll-mt-20" style={{ background: 'var(--jsm-surface)' }}>
<div className="mx-auto max-w-6xl px-6 py-24 lg:px-8 lg:py-32"> <div className="mx-auto max-w-6xl px-6 py-20 lg:px-8 lg:py-28">
<div className="grid gap-10 lg:grid-cols-5 lg:gap-12"> <div className="grid gap-10 lg:grid-cols-5 lg:gap-12">
{/* 안내 */} {/* 안내 */}
<div className="lg:col-span-2"> <div className="lg:col-span-2">
<ScrollReveal> <ScrollReveal>
<p <Eyebrow>contact</Eyebrow>
className="mb-3 font-mono text-[11px] uppercase tracking-[0.22em]" <h2 className="break-keep text-3xl font-bold leading-tight lg:text-[2.4rem]" style={{ color: 'var(--jsm-ink)', ...KOR_TIGHT }}>
style={{ color: 'var(--jsm-accent-bright)' }}
>
contact
</p>
<h2
className="break-keep text-3xl font-bold leading-tight lg:text-[2.5rem]"
style={{ color: 'var(--jsm-dark-ink)', ...KOR_TIGHT }}
>
</h2> </h2>
<p <p className="mt-5 break-keep text-lg leading-relaxed" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
className="mt-5 break-keep text-lg leading-relaxed" 2 . .
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
2 .
.
</p> </p>
<div <div className="mt-8 space-y-3 border-t pt-8" style={{ borderColor: 'var(--jsm-line)' }}>
className="mt-8 space-y-3 border-t pt-8" <a href="mailto:bgg8988@gmail.com" className="flex items-center gap-3 text-sm transition-colors hover:text-[var(--jsm-ink)]" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
style={{ borderColor: 'var(--jsm-dark-line)' }} <span className="w-12 font-mono text-xs uppercase tracking-wider" style={{ color: 'var(--jsm-accent)' }}>Mail</span>
>
<a
href="mailto:bgg8988@gmail.com"
className="flex items-center gap-3 text-sm transition-colors hover:text-[var(--jsm-dark-ink)]"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
<span
className="w-12 font-mono text-xs uppercase tracking-wider"
style={{ color: 'var(--jsm-accent-bright)' }}
>
Mail
</span>
bgg8988@gmail.com bgg8988@gmail.com
</a> </a>
<a <a href="tel:010-3907-1392" className="flex items-center gap-3 text-sm transition-colors hover:text-[var(--jsm-ink)]" style={{ color: 'var(--jsm-ink-soft)', ...KOR_BODY }}>
href="tel:010-3907-1392" <span className="w-12 font-mono text-xs uppercase tracking-wider" style={{ color: 'var(--jsm-accent)' }}>Tel</span>
className="flex items-center gap-3 text-sm transition-colors hover:text-[var(--jsm-dark-ink)]"
style={{ color: 'var(--jsm-dark-soft)', ...KOR_BODY }}
>
<span
className="w-12 font-mono text-xs uppercase tracking-wider"
style={{ color: 'var(--jsm-accent-bright)' }}
>
Tel
</span>
010-3907-1392 010-3907-1392
</a> </a>
</div> </div>
@@ -553,13 +303,7 @@ export default function OutsourcingPage() {
{/* 폼 */} {/* 폼 */}
<div className="lg:col-span-3"> <div className="lg:col-span-3">
<ScrollReveal delay={100}> <ScrollReveal delay={100}>
<div <div className="rounded-2xl border p-6 shadow-[0_24px_60px_-32px_rgba(15,23,42,0.3)] lg:p-8" style={{ background: 'var(--jsm-surface)', borderColor: 'var(--jsm-line)' }}>
className="rounded-2xl border p-6 lg:p-8"
style={{
background: 'var(--jsm-dark-surface)',
borderColor: 'var(--jsm-dark-line)',
}}
>
<OutsourcingRequestForm /> <OutsourcingRequestForm />
</div> </div>
</ScrollReveal> </ScrollReveal>
@@ -567,6 +311,6 @@ export default function OutsourcingPage() {
</div> </div>
</div> </div>
</section> </section>
</div> </>
); );
} }