diff --git a/.gitignore b/.gitignore index 807f36a..56d2180 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ src/extension/lib/* extension.crx extension.pem .DS_Store -extension.zip \ No newline at end of file +extension.zip +client_secret_331738423824-bt52qqf8k1aaa5icjfkp0fu1ebo29e07.apps.googleusercontent.com.json \ No newline at end of file diff --git a/how-to-use.html b/how-to-use.html new file mode 100644 index 0000000..39058f6 --- /dev/null +++ b/how-to-use.html @@ -0,0 +1,69 @@ + + + + + + How to Use - Coverquai + + + + + +
+

How to Use Coverquai

+

Follow these simple steps to generate your perfect cover letter

+
+ +
+
+
+
+

Step 1: Install the Extension

+

Add Coverquai to Chrome from the Chrome Web Store. It's completely free!

+
+
+ Installation step +
+
+ +
+
+

Step 2: Upload Your Resume

+

Upload your resume once and set it as default for quick access.

+
+
+ Resume upload step +
+
+ +
+
+

Step 3: Find a Job

+

Navigate to any job posting on supported websites.

+

Currently supported websites: LinkedIn, Glassdoor, Workday, Eightfold AI

+
+
+ +
+
+

Step 4: Generate Cover Letter and Download

+

Click the Coverquai icon and watch as AI creates your personalized cover letter.

+

Download the cover letter as a PDF or copy the text directly.

+
+
+ Generation step +
+
+
+
+ + \ No newline at end of file diff --git a/index.html b/index.html index 39c9f57..f6d75eb 100644 --- a/index.html +++ b/index.html @@ -4,65 +4,20 @@ Coverquai - Chrome Extension - + + +

Coverquai

Generate tailored cover letters instantly using AI

diff --git a/screenshots/glassdoor_generation.png b/screenshots/glassdoor_generation.png new file mode 100644 index 0000000..bc9b32c Binary files /dev/null and b/screenshots/glassdoor_generation.png differ diff --git a/screenshots/linkedin_generation.png b/screenshots/linkedin_generation.png new file mode 100644 index 0000000..2d5b198 Binary files /dev/null and b/screenshots/linkedin_generation.png differ diff --git a/screenshots/options_page.png b/screenshots/options_page.png new file mode 100644 index 0000000..2817e51 Binary files /dev/null and b/screenshots/options_page.png differ diff --git a/screenshots/step1.png b/screenshots/step1.png new file mode 100644 index 0000000..483e82c Binary files /dev/null and b/screenshots/step1.png differ diff --git a/screenshots/step2.png b/screenshots/step2.png new file mode 100644 index 0000000..fdacdf8 Binary files /dev/null and b/screenshots/step2.png differ diff --git a/screenshots/step4.png b/screenshots/step4.png new file mode 100644 index 0000000..2bf638a Binary files /dev/null and b/screenshots/step4.png differ diff --git a/src/backend/main.py b/src/backend/main.py index 521d9e6..3d9142c 100644 --- a/src/backend/main.py +++ b/src/backend/main.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, UploadFile, HTTPException, Depends, Header +from fastapi import FastAPI, UploadFile, HTTPException, Depends, Header, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse import PyPDF2 @@ -11,7 +11,7 @@ import logging import uuid from supabase import create_client, Client -from datetime import datetime +from datetime import datetime, date app = FastAPI() @@ -29,10 +29,27 @@ # Configure CORS app.add_middleware( CORSMiddleware, - allow_origins=["chrome-extension://*","http://localhost:8000"], + allow_origins=[ + "chrome-extension://*", + "http://localhost:8000", + "https://accounts.google.com", + "https://*.googleusercontent.com", + "https://oauth2.googleapis.com", + ], allow_credentials=True, allow_methods=["*"], - allow_headers=["*"], + allow_headers=[ + "Authorization", + "Content-Type", + "X-Requested-With", + "Accept", + "Origin", + "Access-Control-Request-Method", + "Access-Control-Request-Headers", + "Access-Control-Allow-Origin", + ], + expose_headers=["*"], + max_age=600, # Cache preflight requests for 10 minutes ) # Configure logging @@ -74,6 +91,10 @@ class SignInRequest(BaseModel): class RefreshTokenRequest(BaseModel): refresh_token: str +# Add new auth model +class GoogleAuthRequest(BaseModel): + credential: str # This will be the ID token from Google + # Auth dependency async def verify_token(authorization: Annotated[str | None, Header()] = None): if not authorization or not authorization.startswith("Bearer "): @@ -118,6 +139,19 @@ async def logout(user = Depends(verify_token)): except Exception as e: raise HTTPException(status_code=400, detail=str(e)) +@app.post("/auth/google") +async def google_auth(request: GoogleAuthRequest): + try: + print(request.credential) + # Verify the Google token and sign in/up with Supabase + response = supabase.auth.sign_in_with_id_token({ + "provider": "google", + "token": request.credential + }) + print(response.dict()) + return response.dict() + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) @app.post("/upload-resume") async def upload_resume(file: UploadFile,user = Depends(verify_token)): @@ -314,4 +348,24 @@ async def refresh_token(request: RefreshTokenRequest): } except Exception as e: logger.error(f"Token refresh failed: {str(e)}") - raise HTTPException(status_code=401, detail="Invalid refresh token") \ No newline at end of file + raise HTTPException(status_code=401, detail="Invalid refresh token") + +@app.get("/countgenerations") +async def count_generations(user = Depends(verify_token)): + try: + # Get today's date in UTC + today = datetime.now().strftime('%Y-%m-%d') + + # Query Supabase using proper timestamp comparison + response = supabase.table('cover_letter_requests')\ + .select('id')\ + .eq('userid', user.user.id)\ + .gte('created_at', f'{today}T00:00:00')\ + .lt('created_at', f'{today}T23:59:59')\ + .execute() + + count = len(response.data) + return {"count": count} + except Exception as e: + logger.error(f"Failed to count generations: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/src/extension/background.js b/src/extension/background.js index 115a79a..ad44f4f 100644 --- a/src/extension/background.js +++ b/src/extension/background.js @@ -6,6 +6,7 @@ Paragraph 2: Summarize your experience. Paragraph 3: How your experience translates to the job. Paragraph 4: Closing and contact information.`; +const API_URL = 'https://cvwriter-bhargavyagniks-projects.vercel.app'; chrome.runtime.onInstalled.addListener(() => { console.log('Coverquai Extension installed'); @@ -21,6 +22,12 @@ chrome.runtime.onInstalled.addListener(() => { .catch(error => sendResponse({ success: false, error: error.message })); return true; // Required for async response } + if (request.action === 'googleLogin') { + handleGoogleLogin() + .then(data => sendResponse({ success: true, data })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; // Required to use sendResponse asynchronously + } }); chrome.runtime.onConnect.addListener((port) => { if (port.name === 'coverLetterStream') { @@ -45,7 +52,7 @@ chrome.runtime.onInstalled.addListener(() => { systemPrompt: DEFAULT_SYSTEM_PROMPT }); - const response = await fetch('https://cvwriter-git-dev-bhargavyagniks-projects.vercel.app/generate-cover-letter', { + const response = await fetch(`${API_URL}/generate-cover-letter`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -117,4 +124,77 @@ chrome.runtime.onInstalled.addListener(() => { } finally { port.postMessage({ type: 'done' }); } +} + +async function handleGoogleLogin() { + const authParams = new URLSearchParams({ + client_id: '331738423824-jqcka65qqq8bp0c6dmi4q662s4f027v5.apps.googleusercontent.com', + response_type: 'id_token', + access_type: 'offline', + redirect_uri: chrome.identity.getRedirectURL(), + scope: 'openid email profile', + prompt: 'consent' + }); + + const authUrl = `https://accounts.google.com/o/oauth2/auth?${authParams.toString()}`; + + try { + const responseUrl = await new Promise((resolve, reject) => { + chrome.identity.launchWebAuthFlow({ + url: authUrl, + interactive: true + }, (response) => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + } else { + resolve(response); + } + }); + }); + + const hashFragment = responseUrl.split('#')[1]; + if (!hashFragment) { + throw new Error('No token received from Google'); + } + + const urlParams = new URLSearchParams(hashFragment); + const googleToken = urlParams.get('id_token'); + if (!googleToken) { + throw new Error('No ID token found in response'); + } + + const response = await fetch(`${API_URL}/auth/google`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ credential: googleToken }) + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.detail || 'Google login failed'); + } + // Store auth data + await chrome.storage.local.set({ + 'authToken': data.session.access_token, + 'refreshToken': data.session.refresh_token, + 'user': data.user.id + }); + + // Reopen the extension popup + const extensionId = chrome.runtime.id; + await chrome.action.openPopup(); + + // Return the data to the original request + return data; + } catch (error) { + console.error('Google login error:', error); + throw error; + } +} + +// Helper function to get the popup URL +function getPopupUrl() { + return chrome.runtime.getURL('popup.html'); } \ No newline at end of file diff --git a/src/extension/content.js b/src/extension/content.js index 233dc49..f80d6e4 100644 --- a/src/extension/content.js +++ b/src/extension/content.js @@ -11,17 +11,53 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { return true; // Required for async response }); + function scrapeCurrentPage() { if (window.location.href.includes('linkedin.com')) { + console.log("linkedin.com"); const description = document.querySelector('#job-details > div > p')?.textContent || ''; + console.log(description); return { title: "-",//title.trim(), company: "-",//company.trim(), description: description,//description.trim(), }; } - + if (window.location.href.includes('eightfold.ai')) { + console.log("eightfold.ai"); + const title = document.querySelector('#pcs-body-container > div:nth-child(2) > div.search-results-main-container > div > div.inline-block.mobile-hide.position-top-container > div > div > div:nth-child(2) > div > h1')?.textContent || ''; + const description = document.querySelector('#pcs-body-container > div:nth-child(2) > div.search-results-main-container > div > div.inline-block.mobile-hide.position-top-container > div > div > div.position-details > div.row > div > div > div:nth-child(2) > div > div').textContent || ''; + console.log(title, description, company); + return { + title: title.trim(), + company: window.location.hostname.split('.')[0], + description: description.trim(), + }; + } + if (window.location.href.includes('glassdoor.com') || window.location.href.includes('glassdoor.ca')) { + console.log("glassdoor.com"); + const company = document.evaluate('/html/body/div[3]/div[2]/div[4]/div/div[2]/div/div[1]/header/div[1]/a/div[2]/div[1]/h4',document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.textContent || ''; + const title = document.evaluate('/html/body/div[3]/div[2]/div[4]/div/div[2]/div/div[1]/header/div[1]/h1',document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.textContent || ''; + const description = document.evaluate('/html/body/div[3]/div[2]/div[4]/div/div[2]/div/div[1]/section/div[2]/div[1]',document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.textContent || ''; + console.log(title, description, company); + return { + title: title.trim(), + company: company.trim(), + description: description.trim(), + }; + } + if (window.location.href.includes('myworkdayjobs.com')) { + const title = document.querySelector('[data-automation-id="jobPostingHeader"]')?.textContent || ''; + const description = document.querySelector('[data-automation-id="jobPostingDescription"]')?.textContent || ''; + const company = window.location.hostname.split('.')[0] || ''; // Gets 'fil' from fil.wd3.myworkdayjobs.com + console.log("Workday scraping:", { title, description, company }); + return { + title: title.trim(), + company: company.trim(), + description: description.trim(), + }; + } return { title: "Job",//title.trim(), company: "Company",//company.trim(), diff --git a/src/extension/login.html b/src/extension/login.html index 8bb8acc..75ddca1 100644 --- a/src/extension/login.html +++ b/src/extension/login.html @@ -6,10 +6,10 @@
-

Login to Coverquai

-
+ Coverquai

Cover Letter AI

+

Generate Unlimited Cover Letters with AI

-
+ + + + + +

By signing in, you agree to our Terms of Service and Privacy Policy.

diff --git a/src/extension/login.js b/src/extension/login.js index 88aed6c..093b6c3 100644 --- a/src/extension/login.js +++ b/src/extension/login.js @@ -7,31 +7,45 @@ document.addEventListener('DOMContentLoaded', async function() { return; } }); -document.getElementById('loginForm').addEventListener('submit', async (e) => { - e.preventDefault(); - const email = document.getElementById('email').value; - const password = document.getElementById('password').value; - const errorMessage = document.getElementById('error-message'); - try { - await AuthService.login(email, password); - await new Promise((resolve) => { - chrome.action.setPopup({ popup: 'popup.html' }, resolve); - }); - window.location.href = 'popup.html'; - } catch (error) { - errorMessage.textContent = error.message; - } -}); +// document.getElementById('loginForm').addEventListener('submit', async (e) => { +// e.preventDefault(); +// const email = document.getElementById('email').value; +// const password = document.getElementById('password').value; +// const errorMessage = document.getElementById('error-message'); -document.getElementById('signupBtn').addEventListener('click', async () => { - const email = document.getElementById('email').value; - const password = document.getElementById('password').value; - const errorMessage = document.getElementById('error-message'); +// try { +// await AuthService.login(email, password); +// await new Promise((resolve) => { +// chrome.action.setPopup({ popup: 'popup.html' }, resolve); +// }); +// window.location.href = 'popup.html'; +// } catch (error) { +// errorMessage.textContent = error.message; +// } +// }); +// document.getElementById('signupBtn').addEventListener('click', async () => { +// const email = document.getElementById('email').value; +// const password = document.getElementById('password').value; +// const errorMessage = document.getElementById('error-message'); + +// try { +// await AuthService.signup(email, password); +// await AuthService.login(email, password); +// await new Promise((resolve) => { +// chrome.action.setPopup({ popup: 'popup.html' }, resolve); +// }); +// window.location.href = 'popup.html'; +// } catch (error) { +// errorMessage.textContent = error.message; +// } +// }); + +document.getElementById('googleSignInBtn').addEventListener('click', async () => { + const errorMessage = document.getElementById('error-message'); try { - await AuthService.signup(email, password); - await AuthService.login(email, password); + await AuthService.loginWithGoogle(); await new Promise((resolve) => { chrome.action.setPopup({ popup: 'popup.html' }, resolve); }); diff --git a/src/extension/manifest.json b/src/extension/manifest.json index 41ae6c4..0783295 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Coverquai", - "version": "0.0.3", + "version": "0.1.0", "description": "Automatically generate cover letters based on job descriptions", "icons": { "16": "icons/icon16.png", @@ -12,7 +12,8 @@ "permissions": [ "activeTab", "storage", - "scripting" + "scripting", + "identity" ], "action": { "default_popup": "popup.html" @@ -26,7 +27,11 @@ "matches": [""], "js": ["content.js"], "type": "module" - }] - } + }], + "oauth2": { + "client_id": "331738423824-jqcka65qqq8bp0c6dmi4q662s4f027v5.apps.googleusercontent.com", + "scopes": ["openid", "email", "profile"] + } +} \ No newline at end of file diff --git a/src/extension/options.html b/src/extension/options.html index fbb52c4..5efd4fb 100644 --- a/src/extension/options.html +++ b/src/extension/options.html @@ -163,7 +163,11 @@

Settings

- + diff --git a/src/extension/options.js b/src/extension/options.js index 18a8e9f..ccb5b27 100644 --- a/src/extension/options.js +++ b/src/extension/options.js @@ -5,6 +5,14 @@ Paragraph 2: Summarize your experience. Paragraph 3: How your experience translates to the job. Paragraph 4: Closing and contact information.`; +const API_URL = 'https://cvwriter-bhargavyagniks-projects.vercel.app'; + +document.addEventListener('DOMContentLoaded', async function() { + const isAuthenticated = await AuthService.getValidToken(); + if (!isAuthenticated) { + window.location.href = 'login.html'; + } +}); // Saves options to chrome.storage function saveOptions() { @@ -22,7 +30,7 @@ function saveOptions() { const formData = new FormData(); formData.append('file', file); const token = await AuthService.getValidToken(); - const response = await fetch('https://cvwriter-git-dev-bhargavyagniks-projects.vercel.app/upload-resume', { + const response = await fetch(`${API_URL}/upload-resume`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` diff --git a/src/extension/popup.html b/src/extension/popup.html index cf08768..673df82 100644 --- a/src/extension/popup.html +++ b/src/extension/popup.html @@ -7,7 +7,7 @@
Coverquai

Cover Letter AI

- +
@@ -47,7 +47,11 @@
diff --git a/src/extension/popup.js b/src/extension/popup.js index a91d1b1..bc7ada9 100644 --- a/src/extension/popup.js +++ b/src/extension/popup.js @@ -1,4 +1,5 @@ import AuthService from './services/auth.js'; +const API_URL = 'https://cvwriter-bhargavyagniks-projects.vercel.app'; document.addEventListener('DOMContentLoaded', async function() { // Add a small delay to ensure storage is updated @@ -13,6 +14,7 @@ document.addEventListener('DOMContentLoaded', async function() { // Add logout button const logoutBtn = document.createElement('button'); + logoutBtn.className = 'logout-btn'; logoutBtn.textContent = 'Logout'; logoutBtn.onclick = async () => { await AuthService.logout(); @@ -67,7 +69,7 @@ document.addEventListener('DOMContentLoaded', async function() { const formData = new FormData(); formData.append('file', file); const token = await AuthService.getValidToken(); - const response = await fetch('https://cvwriter-git-dev-bhargavyagniks-projects.vercel.app/upload-resume', { + const response = await fetch(`${API_URL}/upload-resume`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` @@ -139,29 +141,27 @@ document.addEventListener('DOMContentLoaded', async function() { statusDiv.textContent = 'Please upload a resume or set a default resume first'; return; } - + try { + const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); + const response = await chrome.tabs.sendMessage(tab.id, {action: 'scrapeJobDetails'}); + + if (response?.jobDetails) { + document.getElementById('jobTitle').value = response.jobDetails.title; + document.getElementById('companyName').value = response.jobDetails.company; + document.getElementById('jobDescription').value = response.jobDetails.description; + } + } catch (error) { + console.log('Could not scrape job details:', error); + statusDiv.textContent = 'Please fill in all job details'; + return; + } + const jobTitle = document.getElementById('jobTitle').value.trim(); const companyName = document.getElementById('companyName').value.trim(); const jobDescription = document.getElementById('jobDescription').value.trim(); - if (!jobDescription) { // Try to scrape job details from current tab first - try { - const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); - const response = await chrome.tabs.sendMessage(tab.id, {action: 'scrapeJobDetails'}); - - if (response?.jobDetails) { - document.getElementById('jobTitle').value = response.jobDetails.title; - document.getElementById('companyName').value = response.jobDetails.company; - document.getElementById('jobDescription').value = response.jobDetails.description; - return; - } - } catch (error) { - console.log('Could not scrape job details:', error); - statusDiv.textContent = 'Please fill in all job details'; - return; - } - } + // Show overlay overlay.style.display = 'flex'; @@ -180,11 +180,24 @@ document.addEventListener('DOMContentLoaded', async function() { overlayText.textContent = 'Cover letter generated successfully!'; overlay.style.display = 'none'; - // Hide overlay after a short delay - // setTimeout(() => { - // overlay.style.display = 'none'; - // statusDiv.textContent = 'Cover letter generated successfully!'; - // }, 100); + try { + const token = await AuthService.getValidToken(); + const response = await fetch(`${API_URL}/countgenerations`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (response.ok) { + const data = await response.json(); + document.getElementById('generationCount').textContent = data.count; + } else { + document.getElementById('generationCount').textContent = '?'; + } + } catch (error) { + console.error('Error fetching generation count:', error); + document.getElementById('generationCount').textContent = '?'; + } } catch (error) { // Update overlay text to show error @@ -252,6 +265,26 @@ document.addEventListener('DOMContentLoaded', async function() { // Add this at document load to ensure preview is hidden initially previewDiv.style.display = 'none'; + + // Add this near the top of your DOMContentLoaded function + try { + const token = await AuthService.getValidToken(); + const response = await fetch(`${API_URL}/countgenerations`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (response.ok) { + const data = await response.json(); + document.getElementById('generationCount').textContent = data.count; + } else { + document.getElementById('generationCount').textContent = '?'; + } + } catch (error) { + console.error('Error fetching generation count:', error); + document.getElementById('generationCount').textContent = '?'; + } }); async function generateCoverLetter(jobDetails, resumeData) { diff --git a/src/extension/services/auth.js b/src/extension/services/auth.js index 08c2212..cea34dd 100644 --- a/src/extension/services/auth.js +++ b/src/extension/services/auth.js @@ -1,4 +1,4 @@ -const API_URL = 'https://cvwriter-git-dev-bhargavyagniks-projects.vercel.app'; +const API_URL = 'https://cvwriter-bhargavyagniks-projects.vercel.app'; export class AuthService { static async signup(email, password) { @@ -137,6 +137,25 @@ export class AuthService { return await this.getToken(); } + + static async loginWithGoogle() { + try { + const response = await chrome.runtime.sendMessage({ action: 'googleLogin' }); + if (!response.success) { + throw new Error(response.error); + } + + // The popup will be reopened by the background script + // Just update the current view to dashboard + if (window.location.pathname.includes('popup.html')) { + window.location.href = 'dashboard.html'; + } + return response.data; + } catch (error) { + console.error('Google login error:', error); + throw error; + } + } } export default AuthService; \ No newline at end of file diff --git a/src/extension/styles.css b/src/extension/styles.css index c4d22d6..b55afc5 100644 --- a/src/extension/styles.css +++ b/src/extension/styles.css @@ -1,4 +1,3 @@ - body { width: 400px; padding: 20px; @@ -202,3 +201,47 @@ textarea { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } + +.google-btn { + width: 100%; + padding: 10px; + margin-top: 10px; + background-color: #4285f4; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.google-btn:hover { + background-color: #357ae8; +} + +.generation-counter { + /* font-size: 0.9rem; + color: #666; */ + /* margin: 5px 0 15px; + padding: 5px 10px; */ + /* background: #f5f5f5; */ + /* border-radius: 4px; */ + display: inline-block; +} +.logout-btn { + background: none; + border: none; + color: #333; + font-size: 13px; + cursor: pointer; + padding: 4px 8px; + border-radius: 4px; +} + +.logout-btn:hover { + background-color: white; + color: #2563eb; +} +.generation-counter #generationCount { + font-weight: bold; + color: var(--primary-color); +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..302ba53 --- /dev/null +++ b/styles.css @@ -0,0 +1,168 @@ +/* Common styles */ +:root { + --primary-color: #2196F3; + --primary-dark: #1976D2; + --text-color: #333; + --bg-light: #f8fafc; + --white: #ffffff; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + margin: 0; + padding: 0; + color: var(--text-color); +} + +/* Navigation */ +.nav { + background-color: var(--white); + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + padding: 1rem 2rem; + position: fixed; + width: 100%; + top: 0; + z-index: 1000; +} + +.nav-container { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.nav-logo { + font-size: 1.5rem; + font-weight: bold; + color: var(--primary-color); + text-decoration: none; +} + +.nav-links { + display: flex; + gap: 2rem; +} + +.nav-links a { + color: var(--text-color); + text-decoration: none; + font-weight: 500; + transition: color 0.3s; +} + +.nav-links a:hover { + color: var(--primary-color); +} + +/* Header */ +header { + background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); + color: white; + padding: 8rem 2rem 4rem; + text-align: center; +} + +/* Container */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +/* Buttons */ +.cta-button { + display: inline-block; + background: var(--primary-color); + color: white; + padding: 1rem 2rem; + border-radius: 25px; + text-decoration: none; + font-weight: bold; + transition: background 0.3s; +} + +.cta-button:hover { + background: var(--primary-dark); +} + +/* Features */ +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin: 3rem 0; +} + +.feature-card { + background: var(--white); + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); +} + +/* Sections */ +section { + margin: 4rem 0; +} + +h2 { + text-align: center; + margin-bottom: 2rem; +} + +/* TOS Specific */ +.tos-container { + max-width: 800px; + margin: 6rem auto 2rem; + padding: 40px; +} + +.tos-content { + background-color: var(--white); + padding: 32px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.step-image.reduced { + max-width: 70%; +} + +.step-card { + background: #fff; + border-radius: 8px; + padding: 24px; + margin-bottom: 24px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.step-content { + margin-bottom: 20px; +} + +.step-content h3 { + color: #333; + margin-bottom: 12px; +} + +.step-content p { + color: #666; + line-height: 1.6; +} + +.step-image-container { + background: #f5f5f5; + padding: 16px; + border-radius: 6px; + text-align: center; +} + +.step-image { + max-width: 100%; + height: auto; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} \ No newline at end of file diff --git a/tos.html b/tos.html new file mode 100644 index 0000000..2eae7dd --- /dev/null +++ b/tos.html @@ -0,0 +1,53 @@ + + + + Terms of Service - Coverquai + + + + +
+
+

Terms of Service

+ +

1. Acceptance of Terms

+

By using Coverquai, you agree to these Terms of Service. If you do not agree, please do not use the extension.

+ +

2. Service Description

+

Coverquai is a browser extension that generates cover letters using AI technology. While we strive for quality, we cannot guarantee the accuracy or suitability of generated content.

+ +

3. User Responsibilities

+
    +
  • You are responsible for reviewing and editing all generated content
  • +
  • You must not use the service for any illegal or unauthorized purpose
  • +
  • You must not share your account credentials with others
  • +
+ +

4. Privacy & Data

+

We handle your data according to our Privacy Policy. By using Coverquai, you consent to the collection and use of information as described therein.

+ +

5. Intellectual Property

+

You retain rights to your uploaded content. Generated cover letters are for your personal use only.

+ +

6. Limitations of Liability

+

Coverquai is provided "as is" without warranties of any kind. We are not liable for any damages arising from your use of the service.

+ +

7. Changes to Terms

+

We reserve the right to modify these terms at any time. Continued use of Coverquai constitutes acceptance of modified terms.

+ +

8. Contact

+

For questions about these terms, please contact us at coverquai@bhargavyagnik.com

+ +
+
+ + \ No newline at end of file