s-ahal commited on
Commit
04fa58e
·
verified ·
1 Parent(s): eae8c8b

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.js +395 -0
  2. get-organized.html +122 -0
  3. index.html +162 -0
  4. resources.html +154 -0
  5. styles.css +796 -0
app.js ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // DOM Elements
2
+ const uploadArea = document.getElementById("uploadArea");
3
+ const imageInput = document.getElementById("imageInput");
4
+ const imagePreview = document.getElementById("imagePreview");
5
+ const submitButton = document.getElementById("submitButton");
6
+ const errorMessage = document.getElementById("errorMessage");
7
+ const resultSection = document.getElementById("result");
8
+ const resultStatus = document.getElementById("resultStatus");
9
+ const resultDescription = document.getElementById("resultDescription");
10
+ const probabilityValue = document.getElementById("probabilityValue");
11
+ const probabilityFill = document.getElementById("probabilityFill");
12
+ const likelyInfo = document.getElementById("likelyInfo");
13
+ const resultImage = document.getElementById("resultImage");
14
+ const actionButtons = document.getElementById("actionButtons");
15
+ const navButtons = document.querySelectorAll(".nav-button");
16
+ const pages = document.querySelectorAll(".page");
17
+ const resourceCategories = document.querySelectorAll(".resource-category");
18
+ const optInCheckbox = document.getElementById("optInCheckbox");
19
+ const contactInfoInput = document.getElementById("contactInfo");
20
+ const contactOptin = document.querySelector(".contact-optin");
21
+
22
+ // State
23
+ let selectedImage = null;
24
+
25
+ // Custom dropdown logic
26
+ const dropdownToggle = document.getElementById("dropdownToggle");
27
+ const dropdownMenu = document.getElementById("dropdownMenu");
28
+ const dropdownOptions = dropdownMenu
29
+ ? dropdownMenu.querySelectorAll(".dropdown-option")
30
+ : [];
31
+ const selectedModelSpan = document.getElementById("selectedModel");
32
+ let selectedModelValue = "rupeshs/LCM-runwayml-stable-diffusion-v1-5";
33
+ const customDropdown = document.getElementById("modelDropdown");
34
+
35
+ // Loading animation
36
+ const loadingContainer = document.createElement("div");
37
+ loadingContainer.id = "loadingAnimation";
38
+ loadingContainer.style.display = "none";
39
+ loadingContainer.style.justifyContent = "center";
40
+ loadingContainer.style.alignItems = "center";
41
+ loadingContainer.style.flexDirection = "column";
42
+ loadingContainer.style.textAlign = "center";
43
+ loadingContainer.style.position = "absolute";
44
+ loadingContainer.style.top = "0";
45
+ loadingContainer.style.left = "0";
46
+ loadingContainer.style.width = "100%";
47
+ loadingContainer.style.height = "100%";
48
+ loadingContainer.style.zIndex = "2";
49
+ const loadingImg = document.createElement("img");
50
+ loadingImg.alt = "Loading...";
51
+ loadingImg.style.width = "64px";
52
+ loadingImg.style.height = "64px";
53
+ const loadingMsg = document.createElement("div");
54
+ loadingMsg.className = "loading-message";
55
+ loadingMsg.textContent = "Processing";
56
+ loadingContainer.appendChild(loadingImg);
57
+ loadingContainer.appendChild(loadingMsg);
58
+ resultSection.appendChild(loadingContainer);
59
+ let loadingFrame = 1;
60
+ let loadingInterval = null;
61
+ const resultContent = resultSection.querySelector(".result-content");
62
+
63
+ // About overlay logic
64
+ const aboutOverlay = document.getElementById("aboutOverlay");
65
+ const aboutCloseBtn = document.getElementById("aboutCloseBtn");
66
+
67
+ function showLoading() {
68
+ resultSection.hidden = false;
69
+ resultSection.style.display = "";
70
+ if (resultContent) resultContent.style.display = "none";
71
+ loadingContainer.style.display = "flex";
72
+ loadingFrame = 1;
73
+ updateLoadingFrame();
74
+ loadingInterval = setInterval(() => {
75
+ loadingFrame = (loadingFrame % 16) + 1;
76
+ updateLoadingFrame();
77
+ }, 60);
78
+ }
79
+ function hideLoading() {
80
+ loadingContainer.style.display = "none";
81
+ if (resultContent) resultContent.style.display = "";
82
+ if (loadingInterval) clearInterval(loadingInterval);
83
+ }
84
+ function updateLoadingFrame() {
85
+ loadingImg.src = `assets/Infer_LoadingAnimation/Property 1=Variant${loadingFrame}.svg`;
86
+ }
87
+
88
+ function hideResultSection() {
89
+ resultSection.hidden = true;
90
+ resultSection.style.display = "none";
91
+ if (likelyInfo) likelyInfo.hidden = true;
92
+ if (resultImage) resultImage.hidden = true;
93
+ if (resultContent) resultContent.style.display = "";
94
+ loadingContainer.style.display = "none";
95
+ }
96
+
97
+ function showResultSection() {
98
+ resultSection.hidden = false;
99
+ resultSection.style.display = "";
100
+ }
101
+
102
+ // Event Listeners
103
+ uploadArea.addEventListener("click", () => imageInput.click());
104
+ imageInput.addEventListener("change", handleImageUpload);
105
+ submitButton.addEventListener("click", handleSubmit);
106
+ navButtons.forEach((button) => {
107
+ button.addEventListener("click", () => {
108
+ const targetPage = button.dataset.page;
109
+ navigateToPage(targetPage);
110
+ });
111
+ });
112
+
113
+ // Hide result section initially
114
+ hideResultSection();
115
+
116
+ // Add drag and drop support
117
+ uploadArea.addEventListener("dragover", (e) => {
118
+ e.preventDefault();
119
+ e.stopPropagation();
120
+ uploadArea.classList.add("dragover");
121
+ });
122
+
123
+ uploadArea.addEventListener("dragleave", (e) => {
124
+ e.preventDefault();
125
+ e.stopPropagation();
126
+ uploadArea.classList.remove("dragover");
127
+ });
128
+
129
+ uploadArea.addEventListener("drop", (e) => {
130
+ e.preventDefault();
131
+ e.stopPropagation();
132
+ uploadArea.classList.remove("dragover");
133
+ const file = e.dataTransfer.files[0];
134
+ if (file && file.type.startsWith("image/")) {
135
+ handleImageUpload({ target: { files: [file] } });
136
+ }
137
+ });
138
+
139
+ // Resource category selection
140
+ resourceCategories.forEach((category) => {
141
+ category.addEventListener("click", () => {
142
+ resourceCategories.forEach((c) => c.classList.remove("active"));
143
+ category.classList.add("active");
144
+ });
145
+ });
146
+
147
+ // Functions
148
+ function handleImageUpload(event) {
149
+ const file = event.target.files[0];
150
+ if (!file) return;
151
+
152
+ if (!file.type.startsWith("image/")) {
153
+ showError("Please select an image file");
154
+ return;
155
+ }
156
+
157
+ selectedImage = file;
158
+ errorMessage.textContent = "";
159
+ submitButton.disabled = false;
160
+
161
+ // Hide result section when a new image is selected
162
+ hideResultSection();
163
+
164
+ // Hide contact opt-in on new upload
165
+ if (contactOptin) contactOptin.style.display = "none";
166
+
167
+ const reader = new FileReader();
168
+ reader.onload = (e) => {
169
+ imagePreview.src = e.target.result;
170
+ imagePreview.hidden = false;
171
+ document.querySelector(".upload-placeholder").style.display = "none";
172
+ };
173
+ reader.readAsDataURL(file);
174
+ }
175
+
176
+ async function handleSubmit() {
177
+ if (!selectedImage) {
178
+ showError("Please select an image first");
179
+ return;
180
+ }
181
+
182
+ submitButton.disabled = true;
183
+ errorMessage.textContent = "";
184
+ hideResultSection();
185
+ showLoading();
186
+
187
+ if (contactOptin) contactOptin.style.display = "none";
188
+
189
+ try {
190
+ const formData = new FormData();
191
+ formData.append("image", selectedImage);
192
+ formData.append("model", selectedModelValue);
193
+ // Only send opt-in if visible and checked
194
+ if (
195
+ contactOptin &&
196
+ contactOptin.style.display !== "none" &&
197
+ optInCheckbox &&
198
+ optInCheckbox.checked &&
199
+ contactInfoInput &&
200
+ contactInfoInput.value
201
+ ) {
202
+ formData.append("opt_in", "true");
203
+ formData.append("contact_info", contactInfoInput.value);
204
+ } else {
205
+ formData.append("opt_in", "false");
206
+ }
207
+
208
+ const response = await fetch(
209
+ "https://s-ahal-infer.hf.space/api/check-membership",
210
+ {
211
+ method: "POST",
212
+ body: formData,
213
+ }
214
+ );
215
+
216
+ if (!response.ok) {
217
+ throw new Error(`Server responded with ${response.status}`);
218
+ }
219
+
220
+ const data = await response.json();
221
+ displayResult(data);
222
+ } catch (error) {
223
+ showError(`Error: ${error.message}`);
224
+ console.error("Error:", error);
225
+ hideLoading();
226
+ } finally {
227
+ submitButton.disabled = false;
228
+ }
229
+ }
230
+
231
+ function displayResult(data) {
232
+ const probability = data.probability * 100;
233
+ const isLikely = data.probability > 0.5;
234
+
235
+ resultStatus.textContent = isLikely ? "Likely" : "Unlikely";
236
+ const modelName = selectedModelSpan.textContent;
237
+ const desc = `This image <strong class='result-description-strong'>${
238
+ isLikely ? "probably is" : "is probably not"
239
+ }</strong> in the training data for the ${modelName} model.`;
240
+ resultDescription.innerHTML = desc;
241
+ probabilityValue.innerHTML = `<span class='result-percentage'>${probability.toFixed(
242
+ 1
243
+ )}%</span>`;
244
+ probabilityFill.style.width = "0%";
245
+ setTimeout(() => {
246
+ probabilityFill.style.width = `${probability}%`;
247
+ }, 30);
248
+
249
+ if (imagePreview && resultImage) {
250
+ resultImage.src = imagePreview.src;
251
+ resultImage.hidden = false;
252
+ }
253
+
254
+ if (isLikely) {
255
+ likelyInfo.hidden = false;
256
+ let usersText = "";
257
+ if (typeof data.likely_count === "number" && data.likely_count > 1) {
258
+ usersText = `There are <strong>${
259
+ data.likely_count - 1
260
+ } other user-tested artworks</strong> that were found to likely be included in this model's training data.`;
261
+ }
262
+ likelyInfo.querySelector(".likely-users").innerHTML = usersText;
263
+ // Show contact opt-in below results
264
+ if (contactOptin) contactOptin.style.display = "";
265
+ } else {
266
+ likelyInfo.hidden = true;
267
+ if (contactOptin) contactOptin.style.display = "none";
268
+ }
269
+
270
+ hideLoading();
271
+ showResultSection();
272
+ }
273
+
274
+ function showError(message) {
275
+ errorMessage.textContent = message;
276
+ }
277
+
278
+ function navigateToPage(pageId) {
279
+ // Update active page
280
+ pages.forEach((page) => {
281
+ page.classList.remove("active");
282
+ if (page.id === pageId) {
283
+ page.classList.add("active");
284
+ }
285
+ });
286
+
287
+ // Update active nav button
288
+ navButtons.forEach((button) => {
289
+ button.classList.toggle("active", button.dataset.page === pageId);
290
+ });
291
+
292
+ // Scroll to top when changing pages
293
+ window.scrollTo(0, 0);
294
+ }
295
+
296
+ // Initialize
297
+ navigateToPage("model-select");
298
+
299
+ function closeDropdown() {
300
+ dropdownMenu.hidden = true;
301
+ dropdownToggle.setAttribute("aria-expanded", "false");
302
+ if (customDropdown) customDropdown.classList.remove("open");
303
+ }
304
+
305
+ function openDropdown() {
306
+ dropdownMenu.hidden = false;
307
+ dropdownToggle.setAttribute("aria-expanded", "true");
308
+ if (customDropdown) customDropdown.classList.add("open");
309
+ // Focus the selected option
310
+ const selected = dropdownMenu.querySelector(".dropdown-option.selected");
311
+ if (selected) selected.focus();
312
+ }
313
+
314
+ dropdownToggle.addEventListener("click", (e) => {
315
+ e.stopPropagation();
316
+ if (dropdownMenu.hidden) {
317
+ openDropdown();
318
+ } else {
319
+ closeDropdown();
320
+ }
321
+ });
322
+
323
+ dropdownOptions.forEach((option) => {
324
+ option.setAttribute("tabindex", "0");
325
+ option.addEventListener("click", (e) => {
326
+ dropdownOptions.forEach((opt) => opt.classList.remove("selected"));
327
+ option.classList.add("selected");
328
+ selectedModelSpan.textContent = option.textContent;
329
+ selectedModelValue = option.getAttribute("data-value");
330
+ closeDropdown();
331
+ });
332
+ option.addEventListener("keydown", (e) => {
333
+ if (e.key === "Enter" || e.key === " ") {
334
+ option.click();
335
+ } else if (e.key === "ArrowDown") {
336
+ e.preventDefault();
337
+ let next = option.nextElementSibling;
338
+ if (!next) next = dropdownMenu.firstElementChild;
339
+ next.focus();
340
+ } else if (e.key === "ArrowUp") {
341
+ e.preventDefault();
342
+ let prev = option.previousElementSibling;
343
+ if (!prev) prev = dropdownMenu.lastElementChild;
344
+ prev.focus();
345
+ } else if (e.key === "Escape") {
346
+ closeDropdown();
347
+ dropdownToggle.focus();
348
+ }
349
+ });
350
+ });
351
+
352
+ document.addEventListener("click", (e) => {
353
+ if (!dropdownMenu.hidden) {
354
+ closeDropdown();
355
+ }
356
+ });
357
+ dropdownToggle.addEventListener("keydown", (e) => {
358
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
359
+ openDropdown();
360
+ e.preventDefault();
361
+ }
362
+ });
363
+
364
+ document.addEventListener("keydown", (e) => {
365
+ if (e.key === "Escape") {
366
+ closeDropdown();
367
+ }
368
+ });
369
+
370
+ // Ensure About overlay logic runs after DOM is loaded
371
+ window.addEventListener("DOMContentLoaded", function () {
372
+ const aboutOverlay = document.getElementById("aboutOverlay");
373
+ const aboutCloseBtn = document.getElementById("aboutCloseBtn");
374
+ const aboutLink = Array.from(document.querySelectorAll(".nav-link")).find(
375
+ (link) => link.textContent.trim().toLowerCase() === "about"
376
+ );
377
+ if (aboutLink) {
378
+ aboutLink.addEventListener("click", function (e) {
379
+ e.preventDefault();
380
+ if (aboutOverlay) aboutOverlay.style.display = "flex";
381
+ });
382
+ }
383
+ if (aboutCloseBtn) {
384
+ aboutCloseBtn.addEventListener("click", function () {
385
+ if (aboutOverlay) aboutOverlay.style.display = "none";
386
+ });
387
+ }
388
+ if (aboutOverlay) {
389
+ aboutOverlay.addEventListener("click", function (e) {
390
+ if (e.target === aboutOverlay) {
391
+ aboutOverlay.style.display = "none";
392
+ }
393
+ });
394
+ }
395
+ });
get-organized.html ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Infer</title>
7
+ <link rel="stylesheet" href="styles.css" />
8
+ <link rel="icon" type="image/svg+xml" href="assets/favicon.svg" />
9
+ <link rel="icon" type="image/png" href="assets/favicon.png" />
10
+ </head>
11
+ <body>
12
+ <header class="main-header">
13
+ <div class="header-left">
14
+ <h1>
15
+ <a href="index.html" class="home-link"
16
+ >Infer <span class="star">❊</span></a
17
+ >
18
+ </h1>
19
+ <p class="subtitle">Making AI accountability tools for artists.</p>
20
+ </div>
21
+ <nav class="header-nav">
22
+ <a href="#" class="nav-link">About</a>
23
+ <span class="nav-separator">⎈</span>
24
+ <a href="resources.html" class="nav-link">Resources</a>
25
+ <span class="nav-separator">⎈</span>
26
+ <a href="mailto:[email protected]" class="nav-link">Contact</a>
27
+ </nav>
28
+ </header>
29
+ <main class="main-content">
30
+ <section class="get-organized-hero">
31
+ <h2>Get Organized</h2>
32
+ <p>Connect with others whose work has also been scraped.</p>
33
+ </section>
34
+ <div class="get-organized-columns">
35
+ <div class="get-organized-left">
36
+ <p class="big-number">
37
+ <strong>531 other users</strong> also have work that is likely in
38
+ the OpenAI CLIP model's training data.
39
+ </p>
40
+ <p class="not-alone">
41
+ <strong>You are not alone.</strong> Hundreds of artists are
42
+ impacted—many unknowingly.
43
+ </p>
44
+ <p class="explanation">
45
+ Your art may have helped train one of the most powerful AI models in
46
+ use today—without your consent. You're part of a growing group of
47
+ creators facing the same situation. This is a moment for collective
48
+ response.
49
+ </p>
50
+ <div class="together-list">
51
+ <p>Together, we can:</p>
52
+ <ul>
53
+ <li><strong>Explore legal options</strong></li>
54
+ <li><strong>Share tools and tactics</strong></li>
55
+ <li>
56
+ <strong>Coordinate public statements and campaigns</strong>
57
+ </li>
58
+ </ul>
59
+ </div>
60
+ </div>
61
+ <div class="get-organized-right">
62
+ <h3>Join the conversation.</h3>
63
+ <a class="org-btn" href="#"
64
+ ><span>Join the mailing list</span
65
+ ><img src="assets/email_icon.svg" alt="Email"
66
+ /></a>
67
+ <a class="org-btn" href="#"
68
+ ><span>Connect on Discord</span
69
+ ><img src="assets/discord_logo.svg" alt="Discord"
70
+ /></a>
71
+ <a class="org-btn" href="#"
72
+ ><span>Connect on Signal</span
73
+ ><img src="assets/signal_logo.svg" alt="Signal"
74
+ /></a>
75
+ </div>
76
+ </div>
77
+ </main>
78
+ <div id="aboutOverlay" class="about-overlay" style="display:none;">
79
+ <div class="about-modal">
80
+ <button class="about-close" id="aboutCloseBtn" aria-label="Close About">&times;</button>
81
+ <h2>About Infer</h2>
82
+ <p>
83
+ Infer helps artists and image creators check whether their work was likely used to train popular AI models. By comparing your image against model behaviors, we can estimate its presence in the training data—something that's often invisible by design.
84
+ </p>
85
+ <p>
86
+ If your image comes up as a likely match, you'll get two paths:
87
+ <ul>
88
+ <li>Protect Your Art with tools to cloak, license, or fight back</li>
89
+ <li>Get Organized with others facing the same issue—through legal coordination, shared resources, and community defense</li>
90
+ </ul>
91
+ </p>
92
+ </div>
93
+ </div>
94
+ <script>
95
+ document.addEventListener('DOMContentLoaded', function() {
96
+ var aboutOverlay = document.getElementById('aboutOverlay');
97
+ var aboutCloseBtn = document.getElementById('aboutCloseBtn');
98
+ var aboutLink = Array.from(document.querySelectorAll('.nav-link')).find(function(link) {
99
+ return link.textContent.trim().toLowerCase() === 'about';
100
+ });
101
+ if (aboutLink) {
102
+ aboutLink.addEventListener('click', function(e) {
103
+ e.preventDefault();
104
+ if (aboutOverlay) aboutOverlay.style.display = 'flex';
105
+ });
106
+ }
107
+ if (aboutCloseBtn) {
108
+ aboutCloseBtn.addEventListener('click', function() {
109
+ if (aboutOverlay) aboutOverlay.style.display = 'none';
110
+ });
111
+ }
112
+ if (aboutOverlay) {
113
+ aboutOverlay.addEventListener('click', function(e) {
114
+ if (e.target === aboutOverlay) {
115
+ aboutOverlay.style.display = 'none';
116
+ }
117
+ });
118
+ }
119
+ });
120
+ </script>
121
+ </body>
122
+ </html>
index.html ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Infer</title>
7
+ <link rel="stylesheet" href="styles.css" />
8
+ <link rel="icon" type="image/svg+xml" href="assets/favicon.svg" />
9
+ <link rel="icon" type="image/png" href="assets/favicon.png" />
10
+ </head>
11
+ <body>
12
+ <header class="main-header">
13
+ <div class="header-left">
14
+ <h1>
15
+ <a href="index.html" class="home-link"
16
+ >Infer <span class="star">❊</span></a
17
+ >
18
+ </h1>
19
+ <p class="subtitle">Making AI accountability tools for artists.</p>
20
+ </div>
21
+ <nav class="header-nav">
22
+ <a href="#" class="nav-link">About</a>
23
+ <span class="nav-separator">⎈</span>
24
+ <a href="resources.html" class="nav-link">Resources</a>
25
+ <span class="nav-separator">⎈</span>
26
+ <a href="mailto:[email protected]" class="nav-link">Contact</a>
27
+ </nav>
28
+ </header>
29
+ <main class="main-content">
30
+ <div class="home-columns">
31
+ <section class="upload-section" style="min-height: 540px">
32
+ <div class="card-header">
33
+ <h2>Model select</h2>
34
+ <p>Select an AI model to test against</p>
35
+ </div>
36
+ <div class="card-content">
37
+ <div class="model-selector">
38
+ <div class="custom-dropdown" id="modelDropdown">
39
+ <button
40
+ type="button"
41
+ class="dropdown-toggle"
42
+ id="dropdownToggle"
43
+ >
44
+ <span id="selectedModel">Runway Stable Diffusion v1.5</span>
45
+ <img
46
+ src="assets/arrow-down.svg"
47
+ alt="Dropdown arrow"
48
+ id="dropdown-arrow"
49
+ />
50
+ </button>
51
+ <ul class="dropdown-menu" id="dropdownMenu" hidden>
52
+ <li
53
+ class="dropdown-option selected"
54
+ data-value="rupeshs/LCM-runwayml-stable-diffusion-v1-5"
55
+ >
56
+ Runway Stable Diffusion v1.5
57
+ </li>
58
+ <li
59
+ class="dropdown-option"
60
+ data-value="CompVis/stable-diffusion-v1-4"
61
+ >
62
+ CompVis Stable Diffusion v1.4
63
+ </li>
64
+ <li
65
+ class="dropdown-option"
66
+ data-value="prompthero/openjourney-v4"
67
+ >
68
+ OpenJourney v4
69
+ </li>
70
+ </ul>
71
+ </div>
72
+ </div>
73
+ <div class="upload-area" id="uploadArea">
74
+ <input type="file" id="imageInput" accept="image/*" hidden />
75
+
76
+ <div class="upload-placeholder">
77
+ <img
78
+ src="assets/upload-icon.svg"
79
+ alt="Upload"
80
+ id="uploadIcon"
81
+ />
82
+ <p>Click or drag-and-drop to upload a file</p>
83
+ </div>
84
+ <img id="imagePreview" class="preview-image" hidden />
85
+ </div>
86
+ <div id="errorMessage" class="error-message"></div>
87
+ <button id="submitButton" class="submit-button" disabled>
88
+ <span>Let's go</span>
89
+ <span>⟶</span>
90
+ </button>
91
+ </div>
92
+ </section>
93
+ <section class="result-section" id="result" hidden>
94
+ <div class="result-content">
95
+ <div class="result-image-container">
96
+ <img id="resultImage" class="result-image" hidden />
97
+ </div>
98
+ <h2>Result: <span id="resultStatus"></span></h2>
99
+ <p id="resultDescription"></p>
100
+ <div class="probability-section">
101
+ <p>
102
+ Predicted membership probability:
103
+ <span id="probabilityValue"></span>
104
+ </p>
105
+ <div class="probability-bar">
106
+ <div id="probabilityFill" class="probability-fill"></div>
107
+ </div>
108
+ </div>
109
+ <div id="likelyInfo" class="likely-info" hidden>
110
+ <p class="likely-users">
111
+ There are <strong>531 other users</strong> that have work that
112
+ is likely in this model's training data.
113
+ </p>
114
+ <h3>What now?</h3>
115
+ <div class="result-actions">
116
+ <a href="resources.html" class="action-button"
117
+ >Protect your work <span class="arrow">⟶</span></a
118
+ >
119
+ <a href="get-organized.html" class="action-button"
120
+ >Get organized <span class="arrow">⟶</span></a
121
+ >
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </section>
126
+ </div>
127
+ </main>
128
+ <!-- <div
129
+ class="contact-optin"
130
+ style="display: none; margin: 2em auto 0 auto; max-width: 400px"
131
+ >
132
+ <input type="checkbox" id="optInCheckbox" />
133
+ <label for="optInCheckbox"
134
+ >Notify me if there are updates about this project (optional):</label
135
+ >
136
+ <input
137
+ type="email"
138
+ id="contactInfo"
139
+ class="styled-input"
140
+ placeholder="Your email (optional)"
141
+ style="margin-left: 0.5em"
142
+ />
143
+ </div> -->
144
+ <script src="app.js"></script>
145
+ <div id="aboutOverlay" class="about-overlay" style="display:none;">
146
+ <div class="about-modal">
147
+ <button class="about-close" id="aboutCloseBtn" aria-label="Close About">&times;</button>
148
+ <h2>About Infer</h2>
149
+ <p>
150
+ Infer helps artists and image creators check whether their work was likely used to train popular AI models. By comparing your image against model behaviors, we can estimate its presence in the training data—something that's often invisible by design.
151
+ </p>
152
+ <p>
153
+ If your image comes up as a likely match, you'll get two paths:
154
+ <ul>
155
+ <li>Protect Your Art with tools to cloak, license, or fight back</li>
156
+ <li>Get Organized with others facing the same issue—through legal coordination, shared resources, and community defense</li>
157
+ </ul>
158
+ </p>
159
+ </div>
160
+ </div>
161
+ </body>
162
+ </html>
resources.html ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Infer</title>
7
+ <link rel="stylesheet" href="styles.css" />
8
+ <link rel="icon" type="image/svg+xml" href="assets/favicon.svg" />
9
+ <link rel="icon" type="image/png" href="assets/favicon.png" />
10
+ </head>
11
+ <body>
12
+ <header class="main-header">
13
+ <div class="header-left">
14
+ <h1>
15
+ <a href="index.html" class="home-link"
16
+ >Infer <span class="star">❊</span></a
17
+ >
18
+ </h1>
19
+ <p class="subtitle">Making AI accountability tools for artists.</p>
20
+ </div>
21
+ <nav class="header-nav">
22
+ <a href="#" class="nav-link">About</a>
23
+ <span class="nav-separator">⎈</span>
24
+ <a href="resources.html" class="nav-link">Resources</a>
25
+ <span class="nav-separator">⎈</span>
26
+ <a href="mailto:[email protected]" class="nav-link">Contact</a>
27
+ </nav>
28
+ </header>
29
+ <main class="main-content">
30
+ <section class="resources-section">
31
+ <div class="card-header">
32
+ <h2>Resources</h2>
33
+ <p>
34
+ A compiled list of applications and legal resources to help you
35
+ protect your work.
36
+ </p>
37
+ </div>
38
+ <div class="resources-content">
39
+ <div class="resource-categories">
40
+ <button
41
+ class="resource-category active"
42
+ data-resource="do-not-train"
43
+ >
44
+ Do Not Train
45
+ </button>
46
+ <button class="resource-category" data-resource="nightshade">
47
+ Nightshade
48
+ </button>
49
+ <button class="resource-category" data-resource="glaze">
50
+ Glaze
51
+ </button>
52
+ <button class="resource-category" data-resource="copyright">
53
+ Copyright Resources
54
+ </button>
55
+ <button class="resource-category" data-resource="licensing">
56
+ Licensing Options
57
+ </button>
58
+ </div>
59
+ <div class="resource-details">
60
+ <img
61
+ src="assets/nightshade-preview.png"
62
+ alt="Nightshade Preview"
63
+ class="resource-preview"
64
+ style="display: none"
65
+ />
66
+ <div class="resource-info" id="resource-info">
67
+ <!-- JS will inject content here -->
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </section>
72
+ </main>
73
+ <div id="aboutOverlay" class="about-overlay" style="display:none;">
74
+ <div class="about-modal">
75
+ <button class="about-close" id="aboutCloseBtn" aria-label="Close About">&times;</button>
76
+ <h2>About Infer</h2>
77
+ <p>
78
+ Infer helps artists and image creators check whether their work was likely used to train popular AI models. By comparing your image against model behaviors, we can estimate its presence in the training data—something that's often invisible by design.
79
+ </p>
80
+ <p>
81
+ If your image comes up as a likely match, you'll get two paths:
82
+ <ul>
83
+ <li>Protect Your Art with tools to cloak, license, or fight back</li>
84
+ <li>Get Organized with others facing the same issue—through legal coordination, shared resources, and community defense</li>
85
+ </ul>
86
+ </p>
87
+ </div>
88
+ </div>
89
+ <script>
90
+ const resourceData = {
91
+ "do-not-train": {
92
+ title: "Do Not Train",
93
+ html: `<strong>What It Is:</strong> A simple metadata tag artists can add to their images to declare they don't consent to their work being used for training AI.<br><strong>How To Use:</strong> Add a do-not-train tag to your website's HTML or use platforms that support the tag.<br><strong>Note:</strong> It's not enforceable yet, but signals intent for future protections.<br><a href='https://spawning.ai/rightsholders-faq' class='action-button' target='_blank'><span>Learn more</span><span class='arrow'>⟶</span></a>`,
94
+ },
95
+ nightshade: {
96
+ title: "Nightshade",
97
+ html: `<strong>What It Is:</strong> A tool that poisons your artwork's pixels—imperceptibly to humans, but disruptive to training algorithms.<br><strong>Why It Works:</strong> It corrupts how AI models interpret your work, deterring them from using it.<br><a href='https://nightshade.cs.uchicago.edu/' class='action-button' target='_blank'><span>Try it out</span><span class='arrow'>⟶</span></a>`,
98
+ },
99
+ glaze: {
100
+ title: "Glaze",
101
+ html: `<strong>What It Is:</strong> A style cloak that protects your artistic fingerprint.<br><strong>What It Does:</strong> Applies a subtle, AI-visible filter that makes your work harder to mimic.<br><strong>Best For:</strong> Artists with a strong visual signature.<br><a href='https://glaze.cs.uchicago.edu/' class='action-button' target='_blank'><span>Try it out</span><span class='arrow'>⟶</span></a>`,
102
+ },
103
+ copyright: {
104
+ title: "Copyright Resources",
105
+ html: `<strong>Understand Your Rights:</strong><br>In many countries, your work is protected the moment it's created—but enforcing that is another story.<br><br><u>Includes:</u><ul><li>How to formally register your copyright</li><li>Templates for takedown notices</li><li>Intro to fair use & derivative work laws</li></ul>`,
106
+ },
107
+ licensing: {
108
+ title: "Licensing Tools",
109
+ html: `<strong>Put Terms in Writing:</strong><br>Use free or paid licenses to explicitly control how your work can be used.<br><br><u>Options to Explore:</u><ul><li>Creative Commons with "no AI use" clauses</li><li>Personal licensing agreements</li><li>Open-source licenses adapted for artists</li></ul>`,
110
+ },
111
+ };
112
+ const categories = document.querySelectorAll(".resource-category");
113
+ const info = document.getElementById("resource-info");
114
+ function showResource(key) {
115
+ categories.forEach((btn) =>
116
+ btn.classList.toggle("active", btn.dataset.resource === key)
117
+ );
118
+ info.innerHTML = `<h3>${resourceData[key].title}</h3><p>${resourceData[key].html}</p>`;
119
+ }
120
+ categories.forEach((btn) => {
121
+ btn.addEventListener("click", () => showResource(btn.dataset.resource));
122
+ });
123
+ // Show default
124
+ showResource("do-not-train");
125
+ </script>
126
+ <script>
127
+ document.addEventListener('DOMContentLoaded', function() {
128
+ var aboutOverlay = document.getElementById('aboutOverlay');
129
+ var aboutCloseBtn = document.getElementById('aboutCloseBtn');
130
+ var aboutLink = Array.from(document.querySelectorAll('.nav-link')).find(function(link) {
131
+ return link.textContent.trim().toLowerCase() === 'about';
132
+ });
133
+ if (aboutLink) {
134
+ aboutLink.addEventListener('click', function(e) {
135
+ e.preventDefault();
136
+ if (aboutOverlay) aboutOverlay.style.display = 'flex';
137
+ });
138
+ }
139
+ if (aboutCloseBtn) {
140
+ aboutCloseBtn.addEventListener('click', function() {
141
+ if (aboutOverlay) aboutOverlay.style.display = 'none';
142
+ });
143
+ }
144
+ if (aboutOverlay) {
145
+ aboutOverlay.addEventListener('click', function(e) {
146
+ if (e.target === aboutOverlay) {
147
+ aboutOverlay.style.display = 'none';
148
+ }
149
+ });
150
+ }
151
+ });
152
+ </script>
153
+ </body>
154
+ </html>
styles.css ADDED
@@ -0,0 +1,796 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url("https://fonts.googleapis.com/css?family=Hepta+Slab:400,200|Karla:400,700");
2
+
3
+ :root {
4
+ --primary-color: #e7503e;
5
+ --background-color: #fefefa;
6
+ --card-background: #fffde5;
7
+ --text-color: #e7503e;
8
+ --border-radius: 4px;
9
+ }
10
+
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ body {
18
+ font-family: "Karla", Helvetica, sans-serif;
19
+ line-height: 1.6;
20
+ color: var(--text-color);
21
+ background-color: var(--background-color);
22
+ }
23
+
24
+ .main-header {
25
+ display: flex;
26
+ justify-content: space-between;
27
+ align-items: flex-start;
28
+ padding: 1.2rem 40px 0 40px;
29
+ max-width: 950px;
30
+ margin: 0 auto;
31
+ }
32
+
33
+ .header-left h1 {
34
+ font-family: "Hepta Slab", Helvetica, sans-serif;
35
+ font-weight: 200;
36
+ font-size: 42px;
37
+ color: var(--primary-color);
38
+ margin-bottom: 0.3rem;
39
+ line-height: 1;
40
+ }
41
+
42
+ .header-left .star {
43
+ font-size: 1.1em;
44
+ vertical-align: middle;
45
+ }
46
+
47
+ .subtitle {
48
+ font-size: 1rem;
49
+ color: var(--primary-color);
50
+ font-family: "Karla", Helvetica, sans-serif;
51
+ }
52
+
53
+ .header-nav {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 12px;
57
+ margin-top: 0.3rem;
58
+ }
59
+
60
+ .nav-link {
61
+ font-family: "Hepta Slab", Helvetica, sans-serif;
62
+ font-size: 1.1rem;
63
+ color: var(--primary-color);
64
+ text-decoration: none;
65
+ transition: text-decoration 0.2s;
66
+ }
67
+
68
+ .nav-link:hover {
69
+ text-decoration: underline;
70
+ }
71
+
72
+ .nav-separator {
73
+ font-family: "Hepta Slab", Helvetica, sans-serif;
74
+ font-size: 1.1rem;
75
+ color: var(--primary-color);
76
+ }
77
+
78
+ .main-content {
79
+ max-width: 950px;
80
+ margin: 0 auto;
81
+ padding: 1.2rem 40px 1.2rem 40px;
82
+ }
83
+
84
+ .home-columns {
85
+ display: flex;
86
+ gap: 8rem;
87
+ align-items: flex-start;
88
+ }
89
+
90
+ .upload-section {
91
+ flex: 1 1 0;
92
+ min-width: 260px;
93
+ max-width: 400px;
94
+ background: var(--card-background);
95
+ border: 2px dashed var(--primary-color);
96
+ border-radius: var(--border-radius);
97
+ padding: 0;
98
+ margin: 0;
99
+ min-height: 540px;
100
+ }
101
+
102
+ .card-header {
103
+ background-color: var(--primary-color);
104
+ color: white;
105
+ padding: 10px 14px;
106
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
107
+ }
108
+
109
+ .card-header h2 {
110
+ color: white;
111
+ font-family: "Hepta Slab", Helvetica, sans-serif;
112
+ font-size: 1.3rem;
113
+ margin-bottom: 0.2rem;
114
+ }
115
+
116
+ .card-header p {
117
+ color: white;
118
+ font-size: 0.9rem;
119
+ }
120
+
121
+ .card-content {
122
+ padding: 1rem 1rem 1.2rem 1rem;
123
+ background-color: var(--card-background);
124
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
125
+ min-height: 400px;
126
+ }
127
+
128
+ .model-selector {
129
+ margin-bottom: 0.7rem;
130
+ }
131
+
132
+ .model-select {
133
+ width: 100%;
134
+ padding: 0.3rem 2rem 0.3rem 0.7rem;
135
+ font-family: "Karla", Helvetica, sans-serif;
136
+ font-size: 1rem;
137
+ color: var(--primary-color);
138
+ border: 2px dashed var(--primary-color);
139
+ background-color: white;
140
+ border-radius: var(--border-radius);
141
+ appearance: none;
142
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23e7503e" height="20" viewBox="0 0 24 24" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/></svg>');
143
+ background-repeat: no-repeat;
144
+ background-position: right 0.5rem center;
145
+ background-size: 1.1em;
146
+ box-sizing: border-box;
147
+ transition: border-color 0.2s;
148
+ }
149
+
150
+ .model-select:focus {
151
+ outline: none;
152
+ border-color: var(--primary-color);
153
+ }
154
+
155
+ /* Style the dropdown options (limited by browser support) */
156
+ .model-select option {
157
+ color: var(--primary-color);
158
+ font-family: "Karla", Helvetica, sans-serif;
159
+ font-size: 1rem;
160
+ background: white;
161
+ }
162
+
163
+ /* Chrome/Edge: highlight selected/hovered option */
164
+ .model-select option:checked,
165
+ .model-select option:focus,
166
+ .model-select option:hover {
167
+ background: var(--primary-color);
168
+ color: white;
169
+ }
170
+
171
+ /* Firefox: highlight selected option */
172
+ @-moz-document url-prefix() {
173
+ .model-select option:checked {
174
+ background: var(--primary-color) !important;
175
+ color: white !important;
176
+ }
177
+ }
178
+
179
+ .upload-area {
180
+ border: 2px dashed var(--primary-color);
181
+ border-radius: var(--border-radius);
182
+ padding: 1.1rem;
183
+ text-align: center;
184
+ cursor: pointer;
185
+ transition: border-color 0.3s ease;
186
+ margin-bottom: 0.7rem;
187
+ background: rgba(255, 255, 255, 0.32);
188
+ }
189
+
190
+ .upload-area:hover {
191
+ border-color: var(--primary-color);
192
+ }
193
+
194
+ .upload-placeholder {
195
+ display: flex;
196
+ flex-direction: column;
197
+ align-items: center;
198
+ gap: 0.7rem;
199
+ color: var(--primary-color);
200
+ font-size: 0.95rem;
201
+ }
202
+
203
+ .upload-placeholder img {
204
+ width: 32px;
205
+ height: auto;
206
+ opacity: 0.5;
207
+ }
208
+
209
+ .preview-image {
210
+ max-width: 100%;
211
+ max-height: 300px;
212
+ border-radius: var(--border-radius);
213
+ margin: 0.7rem 0;
214
+ }
215
+
216
+ .submit-button {
217
+ background-color: white;
218
+ color: var(--primary-color);
219
+ border: 2px solid var(--primary-color);
220
+ padding: 0.7rem 1.2rem;
221
+ border-radius: var(--border-radius);
222
+ cursor: pointer;
223
+ width: 100%;
224
+ font-size: 1rem;
225
+ font-family: "Karla", Helvetica, sans-serif;
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ margin: 1rem 0 0 0;
230
+ gap: 0.7rem;
231
+ transition: background 0.2s, color 0.2s;
232
+ }
233
+
234
+ .submit-button:disabled {
235
+ opacity: 0.5;
236
+ cursor: not-allowed;
237
+ }
238
+
239
+ .submit-button:hover:not(:disabled) {
240
+ background-color: var(--primary-color);
241
+ color: white;
242
+ }
243
+
244
+ .error-message {
245
+ color: var(--primary-color);
246
+ margin: 0.7rem 0;
247
+ text-align: center;
248
+ font-family: "Karla", Helvetica, sans-serif;
249
+ font-size: 0.95rem;
250
+ }
251
+
252
+ .result-section {
253
+ flex: 1 1 0;
254
+ min-width: 260px;
255
+ max-width: 400px;
256
+ background: none;
257
+ padding: 0 0 0 0;
258
+ margin: 0;
259
+ display: flex;
260
+ flex-direction: column;
261
+ align-items: stretch;
262
+ justify-content: center;
263
+ position: relative;
264
+ width: 100%;
265
+ min-height: 350px;
266
+ }
267
+
268
+ .result-image-container {
269
+ width: 100%;
270
+ margin-bottom: 0.3rem;
271
+ text-align: center;
272
+ }
273
+
274
+ .result-image {
275
+ max-height: 400px;
276
+ max-width: 400px;
277
+ width: auto;
278
+ border: 2px dashed var(--primary-color);
279
+ border-radius: var(--border-radius);
280
+ object-fit: contain;
281
+ display: block;
282
+ margin-left: auto;
283
+ margin-right: auto;
284
+ }
285
+
286
+ .result-image-label {
287
+ font-size: 0.8rem;
288
+ color: #e7503ebf;
289
+ margin-top: 0.1rem;
290
+ font-family: "Karla", Helvetica, sans-serif;
291
+ }
292
+
293
+ .result-section h2 {
294
+ font-family: "Hepta Slab", Helvetica, sans-serif;
295
+ font-size: 1.1rem;
296
+ color: var(--primary-color);
297
+ margin: 0.7rem 0 0.3rem 0;
298
+ }
299
+
300
+ #resultDescription {
301
+ color: var(--primary-color);
302
+ font-size: 0.95rem;
303
+ margin-bottom: 0.3rem;
304
+ }
305
+
306
+ .probability-section {
307
+ margin: 0.7rem 0 0.3rem 0;
308
+ width: 100%;
309
+ }
310
+
311
+ .probability-bar {
312
+ width: 100%;
313
+ height: 16px;
314
+ background-color: #f8f5c8;
315
+ border-radius: var(--border-radius);
316
+ overflow: hidden;
317
+ margin-top: 0.3rem;
318
+ box-sizing: border-box;
319
+ }
320
+
321
+ .probability-fill {
322
+ height: 100%;
323
+ background-color: var(--primary-color);
324
+ border-radius: 4px 0 0 4px;
325
+ transition: width 0.8s cubic-bezier(0.4, 1.4, 0.6, 1);
326
+ }
327
+
328
+ .likely-info {
329
+ margin-top: 1rem;
330
+ }
331
+
332
+ .likely-users {
333
+ color: var(--primary-color);
334
+ font-size: 0.95rem;
335
+ margin-bottom: 0.7rem;
336
+ }
337
+
338
+ .result-section h3 {
339
+ font-family: "Hepta Slab", Helvetica, sans-serif;
340
+ font-size: 1rem;
341
+ color: var(--primary-color);
342
+ margin: 0.7rem 0 0.3rem 0;
343
+ }
344
+
345
+ .result-actions {
346
+ display: flex;
347
+ flex-direction: column;
348
+ gap: 0.7rem;
349
+ margin-top: 0.7rem;
350
+ }
351
+
352
+ .action-button {
353
+ background: white;
354
+ color: var(--primary-color);
355
+ border: 2px solid var(--primary-color);
356
+ border-radius: var(--border-radius);
357
+ padding: 0.7rem 1.2rem;
358
+ font-size: 1rem;
359
+ font-family: "Karla", Helvetica, sans-serif;
360
+ text-align: center;
361
+ text-decoration: none;
362
+ display: flex;
363
+ align-items: center;
364
+ justify-content: center;
365
+ gap: 0.7rem;
366
+ transition: background 0.2s, color 0.2s;
367
+ }
368
+
369
+ .action-button:hover {
370
+ background: var(--primary-color);
371
+ color: white;
372
+ text-decoration: none;
373
+ }
374
+
375
+ .arrow {
376
+ font-size: 1.1em;
377
+ margin-left: 0.3em;
378
+ }
379
+
380
+ @media (max-width: 900px) {
381
+ .main-header,
382
+ .main-content {
383
+ padding: 0.7rem 2vw 0 2vw;
384
+ }
385
+ .home-columns {
386
+ flex-direction: column;
387
+ gap: 1rem;
388
+ }
389
+ .upload-section,
390
+ .result-section {
391
+ max-width: 100%;
392
+ min-width: 0;
393
+ }
394
+ }
395
+
396
+ .custom-dropdown {
397
+ position: relative;
398
+ width: 100%;
399
+ }
400
+
401
+ .dropdown-toggle {
402
+ width: 100%;
403
+ background: white;
404
+ color: var(--primary-color);
405
+ border: 2px dashed var(--primary-color);
406
+ border-radius: var(--border-radius);
407
+ font-family: "Karla", Helvetica, sans-serif;
408
+ font-size: 1rem;
409
+ padding: 0 0 0 0.7rem;
410
+ text-align: left;
411
+ display: flex;
412
+ align-items: center;
413
+ justify-content: space-between;
414
+ cursor: pointer;
415
+ transition: border-color 0.2s;
416
+ box-sizing: border-box;
417
+ }
418
+
419
+ #dropdown-arrow {
420
+ background-color: #e7503e;
421
+ border-radius: 0 4px 4px 0;
422
+ }
423
+
424
+ .dropdown-menu {
425
+ position: static;
426
+ width: 100%;
427
+ background: white;
428
+ border: 2px dashed var(--primary-color);
429
+ border-top: none;
430
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
431
+ box-shadow: none;
432
+ z-index: 1;
433
+ margin: 0;
434
+ list-style: none;
435
+ max-height: 0;
436
+ overflow: hidden;
437
+ transition: max-height 0.25s cubic-bezier(0.4, 1.4, 0.6, 1);
438
+ }
439
+
440
+ .custom-dropdown.open .dropdown-menu {
441
+ max-height: 400px;
442
+ transition: max-height 0.35s cubic-bezier(0.4, 1.4, 0.6, 1);
443
+ }
444
+
445
+ .dropdown-option {
446
+ font-family: "Karla", Helvetica, sans-serif;
447
+ font-size: 1rem;
448
+ color: var(--primary-color);
449
+ background: white;
450
+ padding: 0.6rem 1rem;
451
+ cursor: pointer;
452
+ transition: background 0.15s, color 0.15s;
453
+ border: none;
454
+ outline: none;
455
+ }
456
+
457
+ .dropdown-option.selected,
458
+ .dropdown-option:hover,
459
+ .dropdown-option:focus {
460
+ background: var(--primary-color);
461
+ color: white;
462
+ }
463
+
464
+ .custom-dropdown.open .dropdown-toggle {
465
+ border-bottom: none;
466
+ border-bottom-left-radius: 0;
467
+ border-bottom-right-radius: 0;
468
+ box-shadow: 0 4px 16px rgba(231, 80, 62, 0.1);
469
+ transition: box-shadow 0.2s;
470
+ }
471
+
472
+ .custom-dropdown.open .dropdown-menu {
473
+ border-top-left-radius: 0;
474
+ border-top-right-radius: 0;
475
+ }
476
+
477
+ .result-percentage {
478
+ font-weight: bold;
479
+ }
480
+
481
+ .result-description-strong {
482
+ font-weight: bold;
483
+ }
484
+
485
+ #loadingAnimation {
486
+ display: none;
487
+ position: absolute;
488
+ top: 0;
489
+ left: 0;
490
+ right: 0;
491
+ bottom: 0;
492
+ width: 100%;
493
+ height: 100%;
494
+ /* background: none; */
495
+ z-index: 2;
496
+ justify-content: center;
497
+ align-items: center;
498
+ flex-direction: column;
499
+ text-align: center;
500
+ }
501
+
502
+ #loadingAnimation img {
503
+ width: 64px;
504
+ height: 64px;
505
+ margin-bottom: 1rem;
506
+ }
507
+
508
+ #loadingAnimation .loading-message {
509
+ font-family: "Karla", Helvetica, sans-serif;
510
+ color: var(--primary-color);
511
+ font-size: 1.1rem;
512
+ }
513
+
514
+ /* --- Footer Styles --- */
515
+ .main-footer {
516
+ text-align: center;
517
+ font-size: 0.95em;
518
+ color: #e7503e;
519
+ margin-top: 3rem;
520
+ padding: 1.5rem 0 0.5rem 0;
521
+ background: none;
522
+ }
523
+ .footer-nav .nav-link {
524
+ color: #e7503e;
525
+ text-decoration: none;
526
+ margin: 0 0.5em;
527
+ }
528
+ .footer-nav .nav-separator {
529
+ color: #e7503e;
530
+ margin: 0 0.2em;
531
+ }
532
+
533
+ /* --- Get Organized Page Styles --- */
534
+ .get-organized-hero {
535
+ background: #e4573d;
536
+ color: #fff;
537
+ padding: 2rem 1.5rem 1rem 1.5rem;
538
+ border-radius: 4px;
539
+ margin-bottom: 2rem;
540
+ }
541
+ .get-organized-hero h2 {
542
+ font-size: 2.2rem;
543
+ margin-bottom: 0.2em;
544
+ }
545
+ .get-organized-hero p {
546
+ font-size: 1.1rem;
547
+ margin: 0;
548
+ }
549
+ .get-organized-columns {
550
+ display: flex;
551
+ gap: 3rem;
552
+ margin-top: 2rem;
553
+ flex-wrap: wrap;
554
+ }
555
+ .get-organized-left {
556
+ flex: 2;
557
+ min-width: 320px;
558
+ }
559
+ .get-organized-right {
560
+ flex: 1.2;
561
+ min-width: 260px;
562
+ display: flex;
563
+ flex-direction: column;
564
+ align-items: flex-start;
565
+ }
566
+ .big-number {
567
+ font-size: 1.5rem;
568
+ color: #e4573d;
569
+ font-weight: 700;
570
+ margin-bottom: 1.2rem;
571
+ }
572
+ .not-alone {
573
+ color: #e4573d;
574
+ font-weight: 600;
575
+ margin-bottom: 1rem;
576
+ }
577
+ .explanation {
578
+ color: #b85c4c;
579
+ margin-bottom: 1.5rem;
580
+ }
581
+ .together-list ul {
582
+ margin: 0.5em 0 0 1.2em;
583
+ padding: 0;
584
+ color: #e4573d;
585
+ }
586
+ .together-list li {
587
+ margin-bottom: 0.5em;
588
+ font-weight: 600;
589
+ }
590
+ .get-organized-right h3 {
591
+ color: #e4573d;
592
+ font-size: 1.3rem;
593
+ margin-bottom: 1.2rem;
594
+ }
595
+ .org-btn {
596
+ display: flex;
597
+ align-items: center;
598
+ border: 2px solid #e4573d;
599
+ color: #e4573d;
600
+ background: none;
601
+ border-radius: 6px;
602
+ padding: 0.8em 1.2em;
603
+ font-size: 1.15rem;
604
+ font-weight: 500;
605
+ margin-bottom: 1.1em;
606
+ text-decoration: none;
607
+ transition: background 0.15s, color 0.15s;
608
+ }
609
+ .org-btn img {
610
+ width: 1.5em;
611
+ height: 1.5em;
612
+ margin-left: 0.7em;
613
+ }
614
+ .org-btn:hover {
615
+ background: #e4573d;
616
+ color: #fff;
617
+ }
618
+
619
+ .home-link {
620
+ color: inherit;
621
+ text-decoration: none;
622
+ }
623
+ .home-link:hover {
624
+ text-decoration: none;
625
+ }
626
+
627
+ .resource-categories {
628
+ display: flex;
629
+ flex-direction: column;
630
+ gap: 0.7rem;
631
+ width: 320px;
632
+ }
633
+ .resource-category {
634
+ background: none;
635
+ border: 2px dashed #e7503e;
636
+ color: #e7503e;
637
+ font-family: "Hepta Slab", Helvetica, sans-serif;
638
+ font-size: 1.15rem;
639
+ padding: 0.8em 1.2em;
640
+ border-radius: 6px;
641
+ text-align: left;
642
+ cursor: pointer;
643
+ transition: background 0.15s, color 0.15s;
644
+ }
645
+ .resource-category.active {
646
+ background: #e7503e;
647
+ color: #fff;
648
+ border-style: solid;
649
+ }
650
+ .resources-content {
651
+ display: flex;
652
+ gap: 2.5rem;
653
+ margin-top: 2.5rem;
654
+ }
655
+ .resource-details {
656
+ flex: 1 1 0;
657
+ min-width: 340px;
658
+ max-width: 500px;
659
+ background: none;
660
+ border: 2px dashed #e7503e;
661
+ border-radius: 6px;
662
+ padding: 2.2rem 2.2rem 2.2rem 2.2rem;
663
+ display: flex;
664
+ flex-direction: column;
665
+ align-items: flex-start;
666
+ justify-content: flex-start;
667
+ }
668
+ .resource-preview {
669
+ width: 100%;
670
+ max-width: 340px;
671
+ margin-bottom: 1.2rem;
672
+ border-radius: 6px;
673
+ border: 2px dashed #e7503e;
674
+ display: none;
675
+ }
676
+ .resource-info h3 {
677
+ font-family: "Hepta Slab", Helvetica, sans-serif;
678
+ font-size: 2rem;
679
+ color: #e7503e;
680
+ margin-bottom: 1.1rem;
681
+ margin-top: 0;
682
+ }
683
+ .resource-info p {
684
+ color: #e7503e;
685
+ font-size: 1.1rem;
686
+ margin-bottom: 1.5rem;
687
+ }
688
+ .resource-info ul {
689
+ margin: 0.7em 0 0 1.2em;
690
+ padding: 0;
691
+ color: #e7503e;
692
+ }
693
+ .resource-info li {
694
+ margin-bottom: 0.5em;
695
+ font-weight: 600;
696
+ }
697
+ .resource-info .action-button {
698
+ background: white;
699
+ color: #e7503e;
700
+ border: 2px solid #e7503e;
701
+ border-radius: 6px;
702
+ padding: 0.7rem 1.2rem;
703
+ font-size: 1.1rem;
704
+ font-family: "Karla", Helvetica, sans-serif;
705
+ text-align: center;
706
+ text-decoration: none;
707
+ display: inline-flex;
708
+ align-items: center;
709
+ gap: 0.7rem;
710
+ transition: background 0.2s, color 0.2s;
711
+ margin-top: 1.2rem;
712
+ }
713
+ .resource-info .action-button:hover {
714
+ background: #e7503e;
715
+ color: white;
716
+ text-decoration: none;
717
+ }
718
+
719
+ .styled-input {
720
+ width: 260px;
721
+ padding: 0.3rem 2rem 0.3rem 0.7rem;
722
+ font-family: "Karla", Helvetica, sans-serif;
723
+ font-size: 1rem;
724
+ color: var(--primary-color);
725
+ border: 2px dashed var(--primary-color);
726
+ background-color: white;
727
+ border-radius: var(--border-radius);
728
+ box-sizing: border-box;
729
+ transition: border-color 0.2s;
730
+ outline: none;
731
+ margin-top: 0.5em;
732
+ }
733
+ .styled-input:focus {
734
+ border-color: var(--primary-color);
735
+ }
736
+
737
+ .about-overlay {
738
+ position: fixed;
739
+ top: 0;
740
+ left: 0;
741
+ right: 0;
742
+ bottom: 0;
743
+ background: rgba(231, 80, 62, 0.12);
744
+ z-index: 1000;
745
+ display: flex;
746
+ align-items: center;
747
+ justify-content: center;
748
+ }
749
+
750
+ .about-modal {
751
+ background: #fff;
752
+ border-radius: 8px;
753
+ max-width: 420px;
754
+ width: 90vw;
755
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
756
+ border: 6px solid #e4573d;
757
+ padding: 0 0 1.5em 0;
758
+ position: relative;
759
+ font-size: 1.1rem;
760
+ color: #e4573d;
761
+ margin: 2em 0;
762
+ }
763
+
764
+ .about-modal h2 {
765
+ background: #e4573d;
766
+ color: #fff;
767
+ font-family: "Hepta Slab", Helvetica, sans-serif;
768
+ font-size: 2rem;
769
+ margin: 0 0 1em 0;
770
+ padding: 0.7em 1.2em 0.5em 1.2em;
771
+ border-radius: 6px 6px 0 0;
772
+ }
773
+
774
+ .about-modal p {
775
+ margin: 1.2em 1.5em 0.5em 1.5em;
776
+ color: #e4573d;
777
+ font-size: 1.08rem;
778
+ }
779
+
780
+ .about-modal ul {
781
+ margin: 0.7em 2.2em 1em 2.2em;
782
+ color: #e4573d;
783
+ font-size: 1.08rem;
784
+ }
785
+
786
+ .about-close {
787
+ position: absolute;
788
+ top: 0.5em;
789
+ right: 1em;
790
+ background: none;
791
+ border: none;
792
+ font-size: 2rem;
793
+ color: #fff;
794
+ cursor: pointer;
795
+ z-index: 10;
796
+ }