feat(phase6): BM tiers + Landing + Pricing + 평가원 기출 ProblemSet — tracks 2/4
트랙 2 — BM / Landing / Pricing - schema.prisma: SubscriptionTier enum(free/pro/school) + User.subscriptionTier + User.subscriptionUntil. Migration 20260411161837_bm_and_problemsets. - auth.service.ts / me.service.ts safeUser/toView 에 구독 필드 포함. - frontend/lib/api.ts MeUser 에 subscriptionTier/subscriptionUntil 추가. - app/page.tsx 전면 재작성: 토큰 있으면 /dashboard, 없으면 비로그인 Landing (히어로 + 왜 ReLoop 3카드 + 페르소나 4장 + 4단계 루프 + 가격 티저 + FAQ 4개 + footer). - app/pricing/page.tsx 신규: Free/Pro/School 3 티어 카드(Pro 강조 PopBadge), 월/년 cycle 토글, 기능 비교 테이블(데스크탑 전용), FAQ 5개, 바닥 CTA. Pro ₩7,900/월, 연간 ₩79,000(2개월 무료), School ₩20,000/월·30시트. - app/profile/page.tsx: 구독 섹션 + 티어 배지 + pricing 으로 가는 업그레이드 버튼. 트랙 4 — 평가원 기출 ProblemSet 데모 - schema.prisma: ProblemSet + Problem 신규 모델. StudyLog.problemId (optional, SetNull). 각 ProblemSet 은 (year, examType, subjectName) 복합 unique. - prisma/seed.ts: 2024학년도 수능 수학 샘플 10 문항 (1/5/8/11/13/15/20/22/ 28/30번) 대략치 난이도+정답률로 upsert. 실제 KICE 수치로 교체 가능하도록 주석 명시. - backend/src/problem-sets/: ProblemSetsModule + Service + Controller. GET /api/problem-sets (list, subject/year 필터) + GET /api/problem-sets/:id (문제 배열 포함). JwtAuthGuard 적용. - study-logs.service/controller: problemId optional 필드 수용. - frontend/lib/api.ts: ProblemSetSummary/ProblemSetDetail/Problem 타입. - frontend/app/study/page.tsx 재설계: 상단 탭 [직접 입력 | 문제집에서 가져오기]. 문제집 탭은 set 드롭다운 + 문제 리스트 클릭 → title/difficulty/baseCorrectRate 자동 채움 + subjectName 매핑으로 사용자 과목 자동 선택 + topic 을 tag 이름과 매칭. 결과/메모/시간만 수동 입력.
This commit is contained in:
@@ -1,13 +1,561 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import styled from 'styled-components';
|
||||
import { theme } from '@/styles/theme';
|
||||
import { hasToken } from '@/lib/auth';
|
||||
import { Button } from '@/components/ui/primitives';
|
||||
|
||||
export default function RootPage() {
|
||||
export default function LandingPage() {
|
||||
const router = useRouter();
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
router.replace(hasToken() ? '/dashboard' : '/login');
|
||||
if (hasToken()) {
|
||||
router.replace('/dashboard');
|
||||
return;
|
||||
}
|
||||
setReady(true);
|
||||
}, [router]);
|
||||
return null;
|
||||
|
||||
if (!ready) return null;
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Nav>
|
||||
<Brand>ReLoop</Brand>
|
||||
<NavLinks>
|
||||
<Link href="/pricing">요금제</Link>
|
||||
<Link href="/login">로그인</Link>
|
||||
<Link href="/register">
|
||||
<Button $size="sm">가입</Button>
|
||||
</Link>
|
||||
</NavLinks>
|
||||
</Nav>
|
||||
|
||||
<Hero>
|
||||
<HeroLabel>적응형 복습 스케줄링 · 페르소나 망각 곡선</HeroLabel>
|
||||
<HeroTitle>
|
||||
잊기 직전에 <Accent>딱 한 번 더</Accent>.
|
||||
</HeroTitle>
|
||||
<HeroSub>
|
||||
ReLoop 은 너의 학습 스타일에 맞춰 복습 타이밍을 계산해. 너가 푼 문제가
|
||||
기억에서 사라지기 직전 자동으로 큐에 올려서, 최소한의 노력으로 수능
|
||||
실력을 누적시킨다.
|
||||
</HeroSub>
|
||||
<CTARow>
|
||||
<Link href="/register">
|
||||
<Button $size="lg">무료로 시작하기 →</Button>
|
||||
</Link>
|
||||
<Link href="/login">
|
||||
<Button $size="lg" $variant="secondary">
|
||||
데모 계정으로 체험
|
||||
</Button>
|
||||
</Link>
|
||||
</CTARow>
|
||||
<HeroNote>demo@reloop.local / demo1234 로 바로 로그인 가능</HeroNote>
|
||||
</Hero>
|
||||
|
||||
<Section>
|
||||
<SectionEyebrow>왜 ReLoop 인가</SectionEyebrow>
|
||||
<SectionTitle2>기존 SRS 가 풀지 못하는 3가지</SectionTitle2>
|
||||
<ReasonGrid>
|
||||
<ReasonCard>
|
||||
<ReasonEmoji>🧭</ReasonEmoji>
|
||||
<ReasonTitle>나는 평균이 아니다</ReasonTitle>
|
||||
<ReasonBody>
|
||||
같은 문제라도 상위권/중위권/벼락치기형은 잊는 속도가 다르다.
|
||||
페르소나별 λ 값으로 복습 간격을 다르게 계산한다.
|
||||
</ReasonBody>
|
||||
</ReasonCard>
|
||||
<ReasonCard>
|
||||
<ReasonEmoji>🎯</ReasonEmoji>
|
||||
<ReasonTitle>쉽게 맞은 건 자주 안 봐도 된다</ReasonTitle>
|
||||
<ReasonBody>
|
||||
이미 익숙한 문제는 뒤로 미루고, 곧 까먹을 것만 큐에 올라간다.
|
||||
문제별 정답률(D) 과 현재 실력(S₀) 을 모두 반영한다.
|
||||
</ReasonBody>
|
||||
</ReasonCard>
|
||||
<ReasonCard>
|
||||
<ReasonEmoji>📈</ReasonEmoji>
|
||||
<ReasonTitle>과목별로 성장이 보인다</ReasonTitle>
|
||||
<ReasonBody>
|
||||
태그별 숙련도 누적 곡선이 한눈에. "며칠만 더 복습하면 이 단원은
|
||||
내 것" 이라는 마스터리 경로 뷰를 제공한다.
|
||||
</ReasonBody>
|
||||
</ReasonCard>
|
||||
</ReasonGrid>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<SectionEyebrow>페르소나 4 가지</SectionEyebrow>
|
||||
<SectionTitle2>나의 망각 속도를 고른다</SectionTitle2>
|
||||
<PersonaGrid>
|
||||
<PersonaCard $color={theme.persona.senior.color}>
|
||||
<PEmoji>{theme.persona.senior.emoji}</PEmoji>
|
||||
<PName>상위권</PName>
|
||||
<PLambda>λ = 0.1</PLambda>
|
||||
<PDesc>잘 안 잊는다. 간격이 길어도 버틴다.</PDesc>
|
||||
</PersonaCard>
|
||||
<PersonaCard $color={theme.persona.mid.color}>
|
||||
<PEmoji>{theme.persona.mid.emoji}</PEmoji>
|
||||
<PName>중위권</PName>
|
||||
<PLambda>λ = 0.2</PLambda>
|
||||
<PDesc>표준. 대부분 학생이 여기.</PDesc>
|
||||
</PersonaCard>
|
||||
<PersonaCard $color={theme.persona.junior.color}>
|
||||
<PEmoji>{theme.persona.junior.emoji}</PEmoji>
|
||||
<PName>하위권</PName>
|
||||
<PLambda>λ = 0.4</PLambda>
|
||||
<PDesc>빨리 잊는다. 짧은 간격으로 반복.</PDesc>
|
||||
</PersonaCard>
|
||||
<PersonaCard $color={theme.persona.crammer.color}>
|
||||
<PEmoji>{theme.persona.crammer.emoji}</PEmoji>
|
||||
<PName>벼락치기형</PName>
|
||||
<PLambda>λ = 0.6</PLambda>
|
||||
<PDesc>시험 직전 몰아서 박는 스타일.</PDesc>
|
||||
</PersonaCard>
|
||||
</PersonaGrid>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<SectionEyebrow>작동 방식</SectionEyebrow>
|
||||
<SectionTitle2>4 단계 루프</SectionTitle2>
|
||||
<Steps>
|
||||
<Step>
|
||||
<StepN>1</StepN>
|
||||
<StepTitle>문제를 기록한다</StepTitle>
|
||||
<StepBody>직접 입력하거나 수능 기출 문제집에서 고른다.</StepBody>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepN>2</StepN>
|
||||
<StepTitle>자동으로 다음 복습 일정이 잡힌다</StepTitle>
|
||||
<StepBody>
|
||||
너의 페르소나 × 문제 난이도 × 현재 실력에서 t 를 역산한다.
|
||||
</StepBody>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepN>3</StepN>
|
||||
<StepTitle>큐가 올라오면 푼다</StepTitle>
|
||||
<StepBody>✓ 맞음 / △ 부분 / ✗ 틀림 중 하나만 누르면 끝.</StepBody>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepN>4</StepN>
|
||||
<StepTitle>마스터리 경로를 본다</StepTitle>
|
||||
<StepBody>
|
||||
각 태그가 목표 P 에 몇 회차 · 며칠 뒤 도달하는지 궤적으로 확인한다.
|
||||
</StepBody>
|
||||
</Step>
|
||||
</Steps>
|
||||
</Section>
|
||||
|
||||
<PricingTeaser>
|
||||
<PTitle>가격</PTitle>
|
||||
<PSub>Free 로 시작해서 필요하면 Pro 로 넘어가.</PSub>
|
||||
<PRow>
|
||||
<PCell>
|
||||
<PCellName>Free</PCellName>
|
||||
<PCellPrice>₩0</PCellPrice>
|
||||
<PCellNote>과목 3개 · 복습 큐 · 기본 망각 곡선</PCellNote>
|
||||
</PCell>
|
||||
<PCell $highlight>
|
||||
<PCellName>Pro</PCellName>
|
||||
<PCellPrice>
|
||||
₩7,900<small>/월</small>
|
||||
</PCellPrice>
|
||||
<PCellNote>기출 연동 · 마스터리 경로 · 과목 무제한</PCellNote>
|
||||
</PCell>
|
||||
<PCell>
|
||||
<PCellName>School</PCellName>
|
||||
<PCellPrice>
|
||||
₩20,000<small>/월·30시트</small>
|
||||
</PCellPrice>
|
||||
<PCellNote>학급 관리 · 선생님 대시보드</PCellNote>
|
||||
</PCell>
|
||||
</PRow>
|
||||
<Link href="/pricing">
|
||||
<Button $size="md" $variant="secondary">
|
||||
자세한 가격 보기 →
|
||||
</Button>
|
||||
</Link>
|
||||
</PricingTeaser>
|
||||
|
||||
<Faq>
|
||||
<SectionEyebrow>자주 묻는 질문</SectionEyebrow>
|
||||
<FaqItem>
|
||||
<summary>AI 라고 쓰여있지 않은데 AI 인가요?</summary>
|
||||
<p>
|
||||
AI 라는 용어는 일부러 쓰지 않습니다. 페르소나 망각 곡선이라는 이름
|
||||
그대로, 심리학의 망각 이론에 통계적 파라미터를 얹은 적응형 스케줄링
|
||||
엔진이에요. 블랙박스 없이 수식이 공개됩니다.
|
||||
</p>
|
||||
</FaqItem>
|
||||
<FaqItem>
|
||||
<summary>수능 말고 다른 시험도 되나요?</summary>
|
||||
<p>
|
||||
공식 지원은 아직 수능이지만, 과목·태그·문제집을 직접 만들 수 있어서
|
||||
공무원/자격증/TOEIC 에도 쓸 수 있습니다. 학습 모델은 동일합니다.
|
||||
</p>
|
||||
</FaqItem>
|
||||
<FaqItem>
|
||||
<summary>데이터는 안전한가요?</summary>
|
||||
<p>
|
||||
모든 통신은 HTTPS, 비밀번호는 bcrypt 10 rounds 로 해싱, JWT 는 서버
|
||||
환경변수에서만 서명합니다. 개인 학습 데이터는 언제든 Pro 플랜에서
|
||||
JSON/CSV 로 export 할 수 있습니다.
|
||||
</p>
|
||||
</FaqItem>
|
||||
<FaqItem>
|
||||
<summary>무료 플랜에서도 망각 곡선을 쓸 수 있나요?</summary>
|
||||
<p>
|
||||
네. Free 플랜도 페르소나 망각 곡선을 그대로 사용합니다. Pro 에서
|
||||
추가되는 건 마스터리 경로 뷰 · 기출 문제집 연동 · 무제한 과목 ·
|
||||
데이터 export 입니다.
|
||||
</p>
|
||||
</FaqItem>
|
||||
</Faq>
|
||||
|
||||
<Footer>
|
||||
<FooterBrand>ReLoop v2</FooterBrand>
|
||||
<FooterLinks>
|
||||
<Link href="/pricing">요금제</Link>
|
||||
<Link href="/login">로그인</Link>
|
||||
<Link href="/register">가입</Link>
|
||||
</FooterLinks>
|
||||
<FooterNote>© 2026 Nabomhalang · 학생을 위한 적응형 복습</FooterNote>
|
||||
</Footer>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
// ── styled ──
|
||||
const Page = styled.div`
|
||||
min-height: 100vh;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: ${theme.space.lg} ${theme.space.lg} ${theme.space.xxl};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.xxl};
|
||||
`;
|
||||
|
||||
const Nav = styled.nav`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${theme.space.sm} 0;
|
||||
`;
|
||||
const Brand = styled.div`
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
background: linear-gradient(135deg, #818cf8, #c084fc);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
`;
|
||||
const NavLinks = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${theme.space.md};
|
||||
font-size: 14px;
|
||||
color: ${theme.color.textSub};
|
||||
a {
|
||||
transition: color 0.15s ease;
|
||||
&:hover {
|
||||
color: ${theme.color.textMain};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Hero = styled.section`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: ${theme.space.md};
|
||||
padding: ${theme.space.xl} 0;
|
||||
`;
|
||||
const HeroLabel = styled.span`
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${theme.color.textSub};
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
const HeroTitle = styled.h1`
|
||||
font-size: clamp(36px, 7vw, 64px);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.1;
|
||||
margin: ${theme.space.sm} 0;
|
||||
`;
|
||||
const Accent = styled.span`
|
||||
background: linear-gradient(135deg, #818cf8, #c084fc);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
`;
|
||||
const HeroSub = styled.p`
|
||||
font-size: 16px;
|
||||
color: ${theme.color.textSub};
|
||||
max-width: 640px;
|
||||
line-height: 1.6;
|
||||
`;
|
||||
const CTARow = styled.div`
|
||||
display: flex;
|
||||
gap: ${theme.space.md};
|
||||
margin-top: ${theme.space.md};
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
`;
|
||||
const HeroNote = styled.p`
|
||||
font-size: 12px;
|
||||
color: ${theme.color.textMute};
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
|
||||
const Section = styled.section`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.lg};
|
||||
`;
|
||||
const SectionEyebrow = styled.span`
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: ${theme.color.accent};
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
const SectionTitle2 = styled.h2`
|
||||
font-size: clamp(24px, 4vw, 36px);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
`;
|
||||
|
||||
const ReasonGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: ${theme.space.md};
|
||||
`;
|
||||
const ReasonCard = styled.div`
|
||||
padding: ${theme.space.lg};
|
||||
background: ${theme.color.surface};
|
||||
border: 1px solid ${theme.color.border};
|
||||
border-radius: ${theme.radius.lg};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.sm};
|
||||
`;
|
||||
const ReasonEmoji = styled.div`
|
||||
font-size: 32px;
|
||||
`;
|
||||
const ReasonTitle = styled.h3`
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
const ReasonBody = styled.p`
|
||||
font-size: 13px;
|
||||
color: ${theme.color.textSub};
|
||||
line-height: 1.6;
|
||||
`;
|
||||
|
||||
const PersonaGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: ${theme.space.md};
|
||||
`;
|
||||
const PersonaCard = styled.div<{ $color: string }>`
|
||||
padding: ${theme.space.lg};
|
||||
background: ${theme.color.surface};
|
||||
border: 1px solid ${theme.color.border};
|
||||
border-radius: ${theme.radius.lg};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.xs};
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: ${({ $color }) => $color};
|
||||
}
|
||||
`;
|
||||
const PEmoji = styled.div`
|
||||
font-size: 28px;
|
||||
`;
|
||||
const PName = styled.h3`
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
const PLambda = styled.span`
|
||||
font-size: 11px;
|
||||
color: ${theme.color.textMute};
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
const PDesc = styled.p`
|
||||
font-size: 12px;
|
||||
color: ${theme.color.textSub};
|
||||
margin-top: ${theme.space.xs};
|
||||
`;
|
||||
|
||||
const Steps = styled.ol`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: ${theme.space.md};
|
||||
`;
|
||||
const Step = styled.li`
|
||||
padding: ${theme.space.lg};
|
||||
background: ${theme.color.surface};
|
||||
border: 1px solid ${theme.color.border};
|
||||
border-radius: ${theme.radius.lg};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.sm};
|
||||
`;
|
||||
const StepN = styled.span`
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: ${theme.color.accent};
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
const StepTitle = styled.h3`
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
const StepBody = styled.p`
|
||||
font-size: 13px;
|
||||
color: ${theme.color.textSub};
|
||||
line-height: 1.55;
|
||||
`;
|
||||
|
||||
const PricingTeaser = styled.section`
|
||||
padding: ${theme.space.xl};
|
||||
background: ${theme.color.surface};
|
||||
border: 1px solid ${theme.color.border};
|
||||
border-radius: ${theme.radius.xl};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: ${theme.space.md};
|
||||
text-align: center;
|
||||
`;
|
||||
const PTitle = styled.h2`
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
`;
|
||||
const PSub = styled.p`
|
||||
font-size: 13px;
|
||||
color: ${theme.color.textSub};
|
||||
`;
|
||||
const PRow = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: ${theme.space.md};
|
||||
width: 100%;
|
||||
margin: ${theme.space.md} 0;
|
||||
`;
|
||||
const PCell = styled.div<{ $highlight?: boolean }>`
|
||||
padding: ${theme.space.lg};
|
||||
background: ${({ $highlight }) =>
|
||||
$highlight ? theme.color.surfaceHover : theme.color.surface2};
|
||||
border: 1px solid
|
||||
${({ $highlight }) => ($highlight ? theme.color.accent : theme.color.border)};
|
||||
border-radius: ${theme.radius.lg};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.xs};
|
||||
transform: ${({ $highlight }) => ($highlight ? 'translateY(-4px)' : 'none')};
|
||||
`;
|
||||
const PCellName = styled.div`
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: ${theme.color.textSub};
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
`;
|
||||
const PCellPrice = styled.div`
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
small {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: ${theme.color.textMute};
|
||||
}
|
||||
`;
|
||||
const PCellNote = styled.div`
|
||||
font-size: 12px;
|
||||
color: ${theme.color.textSub};
|
||||
margin-top: ${theme.space.xs};
|
||||
`;
|
||||
|
||||
const Faq = styled.section`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${theme.space.md};
|
||||
`;
|
||||
const FaqItem = styled.details`
|
||||
padding: ${theme.space.md} ${theme.space.lg};
|
||||
background: ${theme.color.surface};
|
||||
border: 1px solid ${theme.color.border};
|
||||
border-radius: ${theme.radius.md};
|
||||
cursor: pointer;
|
||||
|
||||
summary {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
list-style: none;
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
&::before {
|
||||
content: '+ ';
|
||||
color: ${theme.color.textMute};
|
||||
font-family: ${theme.font.mono};
|
||||
}
|
||||
}
|
||||
&[open] summary::before {
|
||||
content: '− ';
|
||||
}
|
||||
p {
|
||||
margin-top: ${theme.space.sm};
|
||||
font-size: 13px;
|
||||
color: ${theme.color.textSub};
|
||||
line-height: 1.6;
|
||||
}
|
||||
`;
|
||||
|
||||
const Footer = styled.footer`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: ${theme.space.sm};
|
||||
padding-top: ${theme.space.xl};
|
||||
border-top: 1px solid ${theme.color.borderSoft};
|
||||
`;
|
||||
const FooterBrand = styled.div`
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: ${theme.color.textSub};
|
||||
`;
|
||||
const FooterLinks = styled.div`
|
||||
display: flex;
|
||||
gap: ${theme.space.md};
|
||||
font-size: 12px;
|
||||
color: ${theme.color.textSub};
|
||||
a:hover {
|
||||
color: ${theme.color.textMain};
|
||||
}
|
||||
`;
|
||||
const FooterNote = styled.p`
|
||||
font-size: 11px;
|
||||
color: ${theme.color.textMute};
|
||||
font-family: ${theme.font.mono};
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user