1---
2import "../styles/tokens.css";
3import ThemeProvider from "../components/ThemeProvider.astro";
4import Logo from "../components/marketing/Logo.astro";
5import BrandIcons from "../brand/Icons.astro";
6
7export interface Props {
8 title: string;
9 message: string;
10 variant?: "success" | "error";
11 primaryAction?: { text: string; href?: string; onClick?: string };
12 secondaryAction?: { text: string; href?: string; onClick?: string };
13}
14
15const {
16 title,
17 message,
18 variant = "success",
19 primaryAction,
20 secondaryAction,
21} = Astro.props;
22---
23
24<!doctype html>
25<html lang="en">
26 <head>
27 <meta charset="utf-8" />
28 <meta name="viewport" content="width=device-width, initial-scale=1" />
29 <ThemeProvider />
30 <title>{title} — Alchemy</title>
31 {/* OAuth device-flow landing pages: no standalone content worth indexing. */}
32 <meta name="robots" content="noindex, follow" />
33 <BrandIcons />
34 </head>
35 <body class="alc-auth-body">
36 <main class="alc-auth">
37 <a href="/" class="alc-auth__brand" aria-label="Alchemy">
38 <Logo size={28} />
39 </a>
40
41 <h1 class={`alc-auth__title${variant === "error" ? " is-error" : ""}`}>
42 {title}
43 </h1>
44
45 <p class="alc-auth__message" set:html={message} />
46
47 <slot />
48
49 {(primaryAction || secondaryAction) && (
50 <div class="alc-auth__actions">
51 {primaryAction && (
52 primaryAction.href ? (
53 <a href={primaryAction.href} class="alc-auth__btn alc-auth__btn--primary">
54 {primaryAction.text}
55 </a>
56 ) : (
57 <button class="alc-auth__btn alc-auth__btn--primary" onclick={primaryAction.onClick}>
58 {primaryAction.text}
59 </button>
60 )
61 )}
62 {secondaryAction && (
63 secondaryAction.href ? (
64 <a href={secondaryAction.href} class="alc-auth__btn alc-auth__btn--secondary">
65 {secondaryAction.text}
66 </a>
67 ) : (
68 <button class="alc-auth__btn alc-auth__btn--secondary" onclick={secondaryAction.onClick}>
69 {secondaryAction.text}
70 </button>
71 )
72 )}
73 </div>
74 )}
75 </main>
76
77 <style is:global>
78 html, body {
79 margin: 0;
80 padding: 0;
81 background: var(--alc-bg);
82 color: var(--alc-fg-2);
83 font-family: var(--alc-font-sans);
84 -webkit-font-smoothing: antialiased;
85 }
86 </style>
87
88 <style>
89 .alc-auth-body {
90 min-height: 100vh;
91 display: grid;
92 place-items: center;
93 padding: 2rem 1.25rem;
94 }
95 .alc-auth {
96 width: 100%;
97 max-width: 28rem;
98 text-align: center;
99 display: flex;
100 flex-direction: column;
101 align-items: center;
102 gap: 1.25rem;
103 }
104 .alc-auth__brand {
105 text-decoration: none;
106 margin-bottom: 1rem;
107 }
108 .alc-auth__title {
109 font-family: var(--alc-font-serif);
110 font-size: 1.875rem;
111 font-weight: 600;
112 color: var(--alc-fg-1);
113 margin: 0;
114 letter-spacing: -0.01em;
115 }
116 .alc-auth__title.is-error {
117 color: var(--alc-terracotta-deep, var(--alc-terracotta));
118 }
119 .alc-auth__message {
120 font-size: 1rem;
121 line-height: 1.6;
122 color: var(--alc-fg-2);
123 margin: 0;
124 }
125 .alc-auth__actions {
126 display: flex;
127 gap: 0.75rem;
128 margin-top: 0.75rem;
129 flex-wrap: wrap;
130 justify-content: center;
131 }
132 .alc-auth__btn {
133 padding: 0.625rem 1.5rem;
134 border-radius: 0.5rem;
135 font: inherit;
136 font-weight: 500;
137 font-size: 0.95rem;
138 cursor: pointer;
139 text-decoration: none;
140 display: inline-block;
141 transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
142 border: 1px solid transparent;
143 }
144 .alc-auth__btn--primary {
145 background: var(--alc-accent);
146 color: var(--alc-fg-on-accent);
147 }
148 .alc-auth__btn--primary:hover {
149 background: var(--alc-accent-deep);
150 }
151 .alc-auth__btn--secondary {
152 background: transparent;
153 color: var(--alc-fg-1);
154 border-color: var(--alc-hairline-2);
155 }
156 .alc-auth__btn--secondary:hover {
157 background: var(--alc-bg-elev-1);
158 border-color: var(--alc-accent);
159 }
160 @media (max-width: 480px) {
161 .alc-auth__title { font-size: 1.5rem; }
162 .alc-auth__actions { flex-direction: column; width: 100%; }
163 .alc-auth__btn { width: 100%; }
164 }
165 </style>
166 </body>
167</html>