3 Commits

4 changed files with 521 additions and 47 deletions

237
public/legal.css Normal file
View File

@@ -0,0 +1,237 @@
:root {
color-scheme: dark;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #edf3ff;
background: #0c111a;
font-synthesis: none;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
min-width: 320px;
margin: 0;
background: #0c111a;
}
a {
text-decoration: none;
}
.page {
width: min(100% - 48px, 1120px);
margin: 0 auto;
padding: 20px 0 64px;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
min-height: 56px;
border-bottom: 1px solid #29354a;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
color: #f5f8ff;
font-size: 18px;
font-weight: 750;
text-decoration: none;
}
.brand-mark {
display: grid;
width: 32px;
height: 32px;
place-items: center;
overflow: hidden;
border-radius: 8px;
background: #17325f;
}
.brand-mark img {
display: block;
width: 100%;
height: 100%;
}
.topbar nav,
.legal-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 18px;
}
.topbar nav a,
.legal-footer a {
color: #aebbd0;
font-size: 14px;
text-decoration: none;
}
.topbar nav a:hover,
.topbar nav a[aria-current="page"],
.legal-footer a:hover {
color: #ffffff;
}
.document {
width: min(100%, 790px);
margin: 72px auto 0;
}
.document-header {
padding-bottom: 34px;
border-bottom: 1px solid #29354a;
}
.eyebrow {
margin: 0 0 12px;
color: #72afff;
font-size: 13px;
font-weight: 750;
letter-spacing: 0;
text-transform: uppercase;
}
h1 {
max-width: 680px;
margin: 0;
color: #f4f7ff;
font-size: clamp(36px, 6vw, 56px);
letter-spacing: 0;
line-height: 1.04;
}
.updated {
margin: 16px 0 0;
color: #8f9db3;
font-size: 15px;
}
.document-content {
padding-top: 32px;
}
.document-content > :first-child {
margin-top: 0;
}
.lead {
max-width: 740px;
color: #d3dcec;
font-size: 19px;
line-height: 1.55;
}
h2 {
margin: 48px 0 16px;
padding-top: 30px;
border-top: 1px solid #29354a;
color: #f2f6ff;
font-size: clamp(21px, 3vw, 26px);
letter-spacing: 0;
line-height: 1.24;
}
p,
li {
color: #b7c3d6;
font-size: 16px;
line-height: 1.68;
}
p {
margin: 0 0 18px;
}
ul {
display: grid;
gap: 9px;
margin: 0 0 20px;
padding-left: 22px;
}
li::marker {
color: #72afff;
}
.document-content a {
color: #8fc0ff;
overflow-wrap: anywhere;
text-underline-offset: 3px;
}
.address {
padding: 18px 20px;
border-left: 2px solid #4c8fe8;
background: #121c2b;
color: #d6dfef;
white-space: pre-line;
}
.legal-footer {
justify-content: space-between;
margin-top: 80px;
padding-top: 24px;
border-top: 1px solid #29354a;
}
.legal-footer p {
margin: 0;
color: #77869d;
font-size: 13px;
}
@media (max-width: 640px) {
.page {
width: min(100% - 32px, 1120px);
padding-top: 12px;
}
.topbar {
align-items: flex-start;
flex-direction: column;
gap: 14px;
padding-bottom: 16px;
}
.topbar nav,
.legal-footer {
gap: 14px;
}
.document {
margin-top: 48px;
}
.document-header {
padding-bottom: 26px;
}
.lead {
font-size: 18px;
}
h2 {
margin-top: 40px;
padding-top: 26px;
}
.legal-footer {
align-items: flex-start;
flex-direction: column;
margin-top: 56px;
}
}

View File

@@ -79,14 +79,32 @@
} }
} }
</style> </style>
<link rel="stylesheet" href="/legal.css" />
</head> </head>
<body> <body>
<main> <main class="page">
<a class="back" href="/">Back to home</a> <header class="topbar">
<h1>Privacy Policy for Rosetta</h1> <a class="brand" href="/" aria-label="Rosetta home">
<p class="muted">Last updated: May 22, 2026</p> <span class="brand-mark"><img src="/rosetta-logo.png" alt="" /></span>
Rosetta
</a>
<nav aria-label="Legal pages">
<a href="/support/">Support</a>
<a href="/privacy/" aria-current="page">Privacy</a>
<a href="/terms/">Terms</a>
</nav>
</header>
<p> <article class="document">
<header class="document-header">
<p class="eyebrow">Rosetta legal</p>
<h1>Privacy Policy</h1>
<p class="updated">Last updated: May 22, 2026</p>
</header>
<div class="document-content">
<p class="lead">
Rosetta IM is operated by ROSETTA CLOUD SERVICES LIMITED ("Rosetta", Rosetta IM is operated by ROSETTA CLOUD SERVICES LIMITED ("Rosetta",
"we", "us", or "our"). This Privacy Policy explains how we handle "we", "us", or "our"). This Privacy Policy explains how we handle
information when you use Rosetta, our end-to-end encrypted messaging information when you use Rosetta, our end-to-end encrypted messaging
@@ -226,6 +244,17 @@ HONG KONG</p>
Support: <a href="/support/">https://rosetta.im/support/</a> Support: <a href="/support/">https://rosetta.im/support/</a>
</p> </p>
<p>Phone: <a href="tel:+85228910030">+852 2891 0030</a></p> <p>Phone: <a href="tel:+85228910030">+852 2891 0030</a></p>
</div>
</article>
<footer class="legal-footer">
<p>&copy; 2026 ROSETTA CLOUD SERVICES LIMITED</p>
<nav aria-label="Footer links">
<a href="/">Rosetta home</a>
<a href="/support/">Support</a>
<a href="/terms/">Terms of Use</a>
</nav>
</footer>
</main> </main>
</body> </body>
</html> </html>

View File

@@ -11,6 +11,9 @@
<link rel="canonical" href="https://rosetta.im/support/" /> <link rel="canonical" href="https://rosetta.im/support/" />
<meta name="robots" content="index, follow" /> <meta name="robots" content="index, follow" />
<meta name="theme-color" content="#0a67ff" /> <meta name="theme-color" content="#0a67ff" />
<script>
document.documentElement.classList.add("js");
</script>
<style> <style>
:root { :root {
color-scheme: light dark; color-scheme: light dark;
@@ -33,6 +36,10 @@
background: #f6f8fc; background: #f6f8fc;
} }
a {
text-decoration: none;
}
a { a {
color: #075ed3; color: #075ed3;
} }
@@ -48,6 +55,33 @@
padding: 24px 0 64px; padding: 24px 0 64px;
} }
.js .page > * {
opacity: 0;
transform: translateY(14px);
transition: opacity 420ms ease, transform 520ms cubic-bezier(0.22, 1, 0.36, 1);
}
.js .page.is-ready > * {
opacity: 1;
transform: translateY(0);
}
.js .page.is-ready > :nth-child(1) {
transition-delay: 0ms;
}
.js .page.is-ready > :nth-child(2) {
transition-delay: 45ms;
}
.js .page.is-ready > :nth-child(3) {
transition-delay: 90ms;
}
.js .page.is-ready > :nth-child(n + 4) {
transition-delay: 130ms;
}
.topbar { .topbar {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -204,30 +238,35 @@
.faq { .faq {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: minmax(0, 1fr);
gap: 14px; gap: 12px;
} }
details { details {
display: grid; display: block;
grid-template-rows: auto 0fr; box-sizing: border-box;
padding: 19px 20px; padding: 19px 20px;
border: 1px solid #dbe2ee; border: 1px solid #dbe2ee;
border-radius: 8px; border-radius: 8px;
background: #ffffff; background: #ffffff;
overflow: hidden; overflow: hidden;
transition: grid-template-rows 260ms cubic-bezier(0.2, 0.8, 0.2, 1), border-color 180ms ease; transition: border-color 180ms ease, background-color 180ms ease;
} }
details[open] { details[open] {
grid-template-rows: auto 1fr; border-color: #b8cce8;
background: #f9fbff;
}
details.is-animating {
will-change: height;
} }
summary { summary {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) 28px; grid-template-columns: minmax(0, 1fr) 36px;
gap: 12px;
align-items: center; align-items: center;
gap: 16px;
min-height: 28px; min-height: 28px;
cursor: pointer; cursor: pointer;
color: #1d293d; color: #1d293d;
@@ -244,46 +283,85 @@
content: ""; content: "";
} }
summary::after { .faq-toggle {
display: grid; display: grid;
width: 28px; width: 36px;
height: 28px; height: 36px;
place-items: center; place-items: center;
justify-self: end;
border: 1px solid #c3d5ed;
border-radius: 50%;
background: #f3f8ff;
color: #075ed3; color: #075ed3;
content: "+"; transition: background-color 180ms ease, border-color 180ms ease;
font-size: 26px;
font-weight: 500;
line-height: 1;
transform: rotate(0deg);
transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
} }
details[open] summary::after { .faq-toggle svg {
transform: rotate(45deg); display: block;
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2.4;
stroke-linecap: round;
stroke-linejoin: round;
transform: translateZ(0) rotate(0deg);
will-change: transform;
transition: transform 240ms cubic-bezier(0.25, 0.1, 0.25, 1);
}
details[open] .faq-toggle {
border-color: #78a8df;
background: #deedff;
}
details[open] .faq-toggle svg {
transform: translateZ(0) rotate(180deg);
}
summary:focus-visible {
outline: none;
}
summary:focus-visible .faq-toggle {
box-shadow: 0 0 0 3px #8cbeff;
} }
details > p { details > p {
display: block; display: block;
min-height: 0; margin: 0 40px 0 0;
margin: 0;
overflow: hidden;
color: #526176; color: #526176;
font-size: 15px; font-size: 15px;
opacity: 0; opacity: 0;
transform: translateY(-4px); transform: translateY(-3px);
transition: opacity 140ms ease, transform 180ms ease, margin 260ms cubic-bezier(0.2, 0.8, 0.2, 1); transition: opacity 160ms ease, transform 220ms cubic-bezier(0.25, 0.1, 0.25, 1);
}
details:not([open]) > p {
display: none;
} }
details[open] > p { details[open] > p {
margin: 14px 0 0; margin: 14px 40px 0 0;
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
transition-delay: 70ms; }
details.is-closing > p {
margin: 0 40px 0 0;
opacity: 0;
transform: translateY(-3px);
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.js .page > * {
opacity: 1;
transform: none;
transition: none;
}
details, details,
summary::after, .faq-toggle,
.faq-toggle svg,
details > p { details > p {
transition: none; transition: none;
} }
@@ -333,6 +411,7 @@
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
overflow-wrap: anywhere; overflow-wrap: anywhere;
text-decoration: none;
} }
.legal-links { .legal-links {
@@ -345,6 +424,7 @@
.legal-links a { .legal-links a {
color: #526176; color: #526176;
font-size: 15px; font-size: 15px;
text-decoration: none;
} }
@media (max-width: 720px) { @media (max-width: 720px) {
@@ -399,6 +479,17 @@
color: #eef4ff; color: #eef4ff;
} }
.faq-toggle {
color: #ffffff;
border-color: #354c6d;
background: #1d2b3f;
}
details[open] .faq-toggle {
border-color: #5e92ca;
background: #29466c;
}
.intro, .intro,
.section-copy, .section-copy,
.quick-link span, .quick-link span,
@@ -413,6 +504,11 @@
background: #141c29; background: #141c29;
} }
details[open] {
border-color: #3c608b;
background: #18263a;
}
.quick-link:hover { .quick-link:hover {
border-color: #427fc7; border-color: #427fc7;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2); box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2);
@@ -479,28 +575,28 @@
</div> </div>
<div class="faq"> <div class="faq">
<details> <details>
<summary>How do I access my account on a new device?</summary> <summary><span>How do I access my account on a new device?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Install Rosetta and enter your recovery phrase in the exact order it was created. Install Rosetta and enter your recovery phrase in the exact order it was created.
Rosetta may ask you to confirm a new device to protect your account. Rosetta may ask you to confirm a new device to protect your account.
</p> </p>
</details> </details>
<details> <details>
<summary>I lost my recovery phrase. Can it be restored?</summary> <summary><span>I lost my recovery phrase. Can it be restored?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
No. Your recovery phrase is the key to your account. Rosetta cannot recover it No. Your recovery phrase is the key to your account. Rosetta cannot recover it
for you, so keep it private and backed up before you need it. for you, so keep it private and backed up before you need it.
</p> </p>
</details> </details>
<details> <details>
<summary>Why am I asked to confirm a new device?</summary> <summary><span>Why am I asked to confirm a new device?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
New-device confirmation helps prevent someone who has obtained a recovery phrase New-device confirmation helps prevent someone who has obtained a recovery phrase
from silently accessing your account on another device. from silently accessing your account on another device.
</p> </p>
</details> </details>
<details> <details>
<summary>How do I report an account-access problem?</summary> <summary><span>How do I report an account-access problem?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Email us with the device model, Rosetta version, and a short description of what Email us with the device model, Rosetta version, and a short description of what
happened. Do not include your recovery phrase or private keys. happened. Do not include your recovery phrase or private keys.
@@ -519,28 +615,28 @@
</div> </div>
<div class="faq"> <div class="faq">
<details> <details>
<summary>How do I send a photo, video, or file?</summary> <summary><span>How do I send a photo, video, or file?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Open a chat, tap the attachment button next to the message field, choose the item Open a chat, tap the attachment button next to the message field, choose the item
you want to share, then send it. Allow Photos or Files access when iOS asks for it. you want to share, then send it. Allow Photos or Files access when iOS asks for it.
</p> </p>
</details> </details>
<details> <details>
<summary>Why is an attachment still sending?</summary> <summary><span>Why is an attachment still sending?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Media uploads depend on the file size and your connection. Keep Rosetta open and Media uploads depend on the file size and your connection. Keep Rosetta open and
connected until the send indicator completes. You can retry a message that failed. connected until the send indicator completes. You can retry a message that failed.
</p> </p>
</details> </details>
<details> <details>
<summary>How do I send a voice message?</summary> <summary><span>How do I send a voice message?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
In a chat, press and hold the microphone control to record, then release it to send. In a chat, press and hold the microphone control to record, then release it to send.
Microphone permission is required the first time you record. Microphone permission is required the first time you record.
</p> </p>
</details> </details>
<details> <details>
<summary>How do I get help with a group?</summary> <summary><span>How do I get help with a group?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Contact us with the group name and a description of the issue. Do not send private Contact us with the group name and a description of the issue. Do not send private
recovery phrases, message contents, or encryption keys. recovery phrases, message contents, or encryption keys.
@@ -559,14 +655,14 @@
</div> </div>
<div class="faq"> <div class="faq">
<details> <details>
<summary>How is message content protected?</summary> <summary><span>How is message content protected?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Rosetta is designed to encrypt message content before it is sent. For details about Rosetta is designed to encrypt message content before it is sent. For details about
how we handle data, read the Rosetta Privacy Policy. how we handle data, read the Rosetta Privacy Policy.
</p> </p>
</details> </details>
<details> <details>
<summary>How do I report abuse or a security issue?</summary> <summary><span>How do I report abuse or a security issue?</span><span class="faq-toggle" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="m6 9 6 6 6-6" /></svg></span></summary>
<p> <p>
Email <a href="mailto:admin@rosetta.im?subject=Rosetta%20Security%20Report">admin@rosetta.im</a> Email <a href="mailto:admin@rosetta.im?subject=Rosetta%20Security%20Report">admin@rosetta.im</a>
with a description of the concern. Do not include recovery phrases or private keys. with a description of the concern. Do not include recovery phrases or private keys.
@@ -595,5 +691,88 @@
<a href="/terms/">Terms of Use</a> <a href="/terms/">Terms of Use</a>
</footer> </footer>
</main> </main>
<script>
window.addEventListener("DOMContentLoaded", () => {
requestAnimationFrame(() => {
document.querySelector(".page")?.classList.add("is-ready");
});
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const animationOptions = {
duration: 240,
easing: "cubic-bezier(0.25, 0.1, 0.25, 1)",
fill: "forwards",
};
document.querySelectorAll(".faq details").forEach((details) => {
const summary = details.querySelector("summary");
if (!summary) return;
let animation = null;
const setExpanded = (expanded) => summary.setAttribute("aria-expanded", String(expanded));
setExpanded(details.open);
summary.addEventListener("click", (event) => {
event.preventDefault();
if (animation) return;
if (prefersReducedMotion) {
details.open = !details.open;
setExpanded(details.open);
return;
}
const isOpening = !details.open;
const startHeight = details.getBoundingClientRect().height;
if (isOpening) {
details.open = true;
setExpanded(true);
const endHeight = details.scrollHeight;
details.classList.add("is-animating");
details.style.height = `${startHeight}px`;
animation = details.animate(
[{ height: `${startHeight}px` }, { height: `${endHeight}px` }],
animationOptions,
);
} else {
details.open = false;
const endHeight = details.getBoundingClientRect().height;
details.open = true;
details.classList.add("is-animating", "is-closing");
details.style.height = `${startHeight}px`;
animation = details.animate(
[{ height: `${startHeight}px` }, { height: `${endHeight}px` }],
animationOptions,
);
}
animation.onfinish = () => {
const completedAnimation = animation;
if (!isOpening) {
details.open = false;
setExpanded(false);
}
details.style.height = "";
details.classList.remove("is-animating", "is-closing");
completedAnimation.oncancel = null;
completedAnimation.cancel();
animation = null;
};
animation.oncancel = () => {
details.style.height = "";
details.classList.remove("is-animating", "is-closing");
animation = null;
};
});
});
});
</script>
</body> </body>
</html> </html>

View File

@@ -79,14 +79,32 @@
} }
} }
</style> </style>
<link rel="stylesheet" href="/legal.css" />
</head> </head>
<body> <body>
<main> <main class="page">
<a class="back" href="/">Back to home</a> <header class="topbar">
<h1>Terms of Use for Rosetta</h1> <a class="brand" href="/" aria-label="Rosetta home">
<p class="muted">Last updated: May 22, 2026</p> <span class="brand-mark"><img src="/rosetta-logo.png" alt="" /></span>
Rosetta
</a>
<nav aria-label="Legal pages">
<a href="/support/">Support</a>
<a href="/privacy/">Privacy</a>
<a href="/terms/" aria-current="page">Terms</a>
</nav>
</header>
<p> <article class="document">
<header class="document-header">
<p class="eyebrow">Rosetta legal</p>
<h1>Terms of Use</h1>
<p class="updated">Last updated: May 22, 2026</p>
</header>
<div class="document-content">
<p class="lead">
These Terms of Use and End User License Agreement ("Terms") govern your These Terms of Use and End User License Agreement ("Terms") govern your
use of Rosetta, an end-to-end encrypted messaging application operated by use of Rosetta, an end-to-end encrypted messaging application operated by
ROSETTA CLOUD SERVICES LIMITED ("Rosetta", "we", "us", or "our"). ROSETTA CLOUD SERVICES LIMITED ("Rosetta", "we", "us", or "our").
@@ -255,6 +273,17 @@ TUEN MUN
HONG KONG</p> HONG KONG</p>
<p>Email: <a href="mailto:admin@rosetta.im">admin@rosetta.im</a></p> <p>Email: <a href="mailto:admin@rosetta.im">admin@rosetta.im</a></p>
<p>Phone: <a href="tel:+85228910030">+852 2891 0030</a></p> <p>Phone: <a href="tel:+85228910030">+852 2891 0030</a></p>
</div>
</article>
<footer class="legal-footer">
<p>&copy; 2026 ROSETTA CLOUD SERVICES LIMITED</p>
<nav aria-label="Footer links">
<a href="/">Rosetta home</a>
<a href="/support/">Support</a>
<a href="/privacy/">Privacy Policy</a>
</nav>
</footer>
</main> </main>
</body> </body>
</html> </html>