Files
vantage-shared/mail/templates/layout.html.tmpl
T
2026-09-10 12:06:29 +00:00

292 lines
17 KiB
Cheetah

{{- /*
The Vantage email shell.
Every colour in the email system lives in this file and nowhere else, in the
same way no component in web/, site/ or adminsite/ carries a hex. The values
are web/app/globals.css's tokens - an email is read before the recipient
clicks through to the control plane, so the two should not look like
different products. They are written as literal hex here because email
clients support neither var() nor a reliable prefers-color-scheme, so the
usual token indirection is not available: when you change a token in the
three globals.css files, change it here too.
--ground #071628 --rule #1e3855
--panel #0d2138 --accent #5b9be8
--panel-2 #102842 --up #4fb484
--ink #e4ecf6 --down #e2705a
--ink-2 #9fb3ca --pend #d6a63f
--ink-3 #71879f --well #04101f
Derived here only, never in a front end: each tone has a tint (15% over
--panel) for washes and a border (35% over --panel) for outlines.
up #173743 / #245452 pend #2b3539 / #534f3a
down #2d2d3d / #573d44 accent #193352 / #284b75
Layout is tables and inline styles throughout, which is not a stylistic
choice - it is the only thing Outlook renders predictably.
A message file overrides "title", "pill", "body", "category", "preheader"
and "why"; its txt file defines "subject" and "tone". The defaults below
exist so that a message needing no pill does not have to define one.
Colour choices that depend on a tone go through pick, which takes the tone
and one value per tone (up, down, pend, accent, anything else), so the hex
stays in this file while the branching stays out of it.
*/ -}}
{{- define "title"}}{{end -}}
{{- define "pill"}}{{end -}}
{{- define "body"}}{{end -}}
{{- define "category"}}Account{{end -}}
{{- define "preheader"}}{{end -}}
{{- define "why"}}it relates to your Vantage account.{{end -}}
{{- /* p renders one paragraph of body copy. */ -}}
{{- define "p" -}}
<p style="margin:0 0 16px;color:#9fb3ca;font-size:14px;line-height:1.65;">{{.}}</p>
{{- end -}}
{{- /* lead is the first paragraph: larger, brighter, sets the subject. */ -}}
{{- define "lead" -}}
<p style="margin:0 0 20px;color:#e4ecf6;font-size:16px;line-height:1.6;">{{.}}</p>
{{- end -}}
{{- /* small is fine print under a section. */ -}}
{{- define "small" -}}
<p style="margin:0 0 16px;color:#71879f;font-size:12px;line-height:1.6;">{{.}}</p>
{{- end -}}
{{- /* label is an eyebrow over the section that follows it. */ -}}
{{- define "label" -}}
<p style="margin:24px 0 10px;color:#71879f;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;">{{.}}</p>
{{- end -}}
{{- define "divider" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 20px;"><tr><td style="height:1px;line-height:1px;font-size:0;background:#1e3855;">&nbsp;</td></tr></table>
{{- end -}}
{{- /* button takes dict "label" "…" "url" "…", and optionally "nofallback".
The bare URL is printed underneath on purpose: a plain-text-preferring
client, a stripped-styles inbox and a forwarded message all lose the
anchor, and a verification email whose link cannot be reached is a
support ticket. nofallback is for mailto: links, where it would be noise. */ -}}
{{- define "button" -}}
<table role="presentation" cellpadding="0" cellspacing="0" style="margin:4px 0 14px;">
<tr>
<td style="border-radius:6px;background:#5b9be8;">
<a href="{{.url}}" style="display:inline-block;padding:12px 24px;color:#04101f;font-size:14px;font-weight:700;text-decoration:none;border-radius:6px;">{{.label}} &rarr;</a>
</td>
</tr>
</table>
{{- if not .nofallback}}
<p style="margin:0 0 22px;color:#71879f;font-size:12px;line-height:1.5;word-break:break-all;">
Button not working? Paste this into your browser:<br>
<a href="{{.url}}" style="color:#5b9be8;text-decoration:none;">{{.url}}</a>
</p>
{{- end}}
{{- end -}}
{{- /* well shows machine output - a licence blob, an install ID. Mirrors
web/'s --well surface, the floor beneath the ground. */ -}}
{{- define "well" -}}
<pre style="margin:0 0 20px;padding:14px 16px;background:#04101f;border:1px solid #1e3855;border-radius:6px;color:#9fb3ca;font-family:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,monospace;font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-all;">{{.}}</pre>
{{- end -}}
{{- /* note is a quoted callout, used for a free-text message we did not
write ourselves. Line breaks are kept: it is somebody's own words. */ -}}
{{- define "note" -}}
<p style="margin:0 0 20px;padding:14px 16px;background:#102842;border:1px solid #1e3855;border-radius:6px;color:#e4ecf6;font-size:13px;line-height:1.65;white-space:pre-wrap;">{{.}}</p>
{{- end -}}
{{- /* rows takes a list of dict "k" "…" "v" "…", and renders them as one
panel of facts. */ -}}
{{- define "rows" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:4px 0 20px;background:#102842;border:1px solid #1e3855;border-radius:6px;border-collapse:separate;">
{{- range $i, $r := .}}
<tr>
<td style="padding:11px 16px;{{if $i}}border-top:1px solid #172c44;{{end}}color:#71879f;font-size:13px;width:140px;vertical-align:top;">{{$r.k}}</td>
<td style="padding:11px 16px 11px 0;{{if $i}}border-top:1px solid #172c44;{{end}}color:#e4ecf6;font-size:13px;font-weight:600;word-break:break-word;">{{$r.v}}</td>
</tr>
{{- end}}
</table>
{{- end -}}
{{- /* chip takes dict "label" "…" "tone" "up|down|pend|accent|none". Tone is
never the only signal: the label spells the state out. */ -}}
{{- define "chip" -}}
{{- $fg := pick .tone "#4fb484" "#e2705a" "#d6a63f" "#5b9be8" "#9fb3ca"}}{{$bg := pick .tone "#173743" "#2d2d3d" "#2b3539" "#193352" "#102842"}}{{$bd := pick .tone "#245452" "#573d44" "#534f3a" "#284b75" "#1e3855" -}}
<span style="display:inline-block;padding:4px 11px;background:{{$bg}};border:1px solid {{$bd}};border-radius:9999px;color:{{$fg}};font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;">{{.label}}</span>
{{- end -}}
{{- /* callout takes dict "tone" "…" "title" "…" "text" "…": one tinted panel
for the sentence the reader must not miss. */ -}}
{{- define "callout" -}}
{{- $fg := pick .tone "#4fb484" "#e2705a" "#d6a63f" "#5b9be8" "#9fb3ca"}}{{$bg := pick .tone "#173743" "#2d2d3d" "#2b3539" "#193352" "#102842"}}{{$bd := pick .tone "#245452" "#573d44" "#534f3a" "#284b75" "#1e3855" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:4px 0 20px;background:{{$bg}};border:1px solid {{$bd}};border-radius:6px;border-collapse:separate;">
<tr>
<td style="padding:14px 16px;">
<p style="margin:0 0 4px;color:{{$fg}};font-size:13px;font-weight:700;">{{.title}}</p>
<p style="margin:0;color:#c3d1e1;font-size:13px;line-height:1.6;">{{.text}}</p>
</td>
</tr>
</table>
{{- end -}}
{{- /* steps takes a list of dict "t" "…" "d" "…". Numbered because the
content is always an order the reader follows. */ -}}
{{- define "steps" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 20px;">
{{- range $i, $s := .}}
<tr>
<td width="28" style="width:28px;padding:0 12px 14px 0;vertical-align:top;">
<table role="presentation" cellpadding="0" cellspacing="0"><tr>
<td width="26" height="26" align="center" style="width:26px;height:26px;border-radius:9999px;background:#193352;border:1px solid #284b75;color:#7fb2f0;font-size:12px;font-weight:700;line-height:26px;text-align:center;">{{add $i 1}}</td>
</tr></table>
</td>
<td style="padding:3px 0 14px;vertical-align:top;">
<p style="margin:0 0 2px;color:#e4ecf6;font-size:14px;font-weight:600;line-height:1.4;">{{$s.t}}</p>
<p style="margin:0;color:#9fb3ca;font-size:13px;line-height:1.55;">{{$s.d}}</p>
</td>
</tr>
{{- end}}
</table>
{{- end -}}
{{- /* stats takes a list of dict "big" "…" "label" "…" "tone" "…": the
figures a message exists to state, such as days left. */ -}}
{{- define "stats" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 20px;">
<tr>
{{- range $i, $s := .}}
{{- if $i}}<td width="10" style="width:10px;font-size:0;">&nbsp;</td>{{end}}
<td style="padding:14px 16px;background:#102842;border:1px solid #1e3855;border-radius:6px;vertical-align:top;">
<p style="margin:0;color:{{pick $s.tone "#4fb484" "#e2705a" "#d6a63f" "#7fb2f0" "#e4ecf6"}};font-size:26px;font-weight:700;line-height:1.15;letter-spacing:-.01em;">{{$s.big}}</p>
<p style="margin:4px 0 0;color:#71879f;font-size:12px;line-height:1.4;">{{$s.label}}</p>
</td>
{{- end}}
</tr>
</table>
{{- end -}}
{{- /* timeline takes a list of dict "label" "…" "date" "…" "state"
"done|now|next" "tone" "…". It is the licence lifecycle, drawn as
segments so the reader sees where they are in it. */ -}}
{{- define "timeline" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:4px 0 22px;table-layout:fixed;">
<tr>
{{- range $i, $s := .}}
{{- if $i}}<td width="6" style="width:6px;font-size:0;">&nbsp;</td>{{end}}
<td style="vertical-align:top;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="height:4px;line-height:4px;font-size:0;border-radius:2px;background:{{if eq $s.state "now"}}{{pick $s.tone "#4fb484" "#e2705a" "#d6a63f" "#5b9be8" "#5b9be8"}}{{else if eq $s.state "done"}}#3d5673{{else}}#1e3855{{end}};">&nbsp;</td></tr></table>
<p style="margin:10px 0 2px;color:{{if eq $s.state "now"}}{{pick $s.tone "#4fb484" "#e2705a" "#d6a63f" "#7fb2f0" "#7fb2f0"}}{{else}}#71879f{{end}};font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;">{{$s.label}}</p>
<p style="margin:0;color:{{if eq $s.state "next"}}#9fb3ca{{else}}#e4ecf6{{end}};font-size:13px;font-weight:600;line-height:1.4;">{{$s.date}}</p>
</td>
{{- end}}
</tr>
</table>
{{- end -}}
{{- /* findings takes a list of VulnDigestRow: one line per finding, the
severity as a chip, the advisory linked when its ID says where it is. */ -}}
{{- define "findings" -}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 16px;background:#102842;border:1px solid #1e3855;border-radius:6px;border-collapse:separate;">
{{- range $i, $r := .}}
<tr>
<td style="padding:12px 16px;{{if $i}}border-top:1px solid #172c44;{{end}}vertical-align:top;">
<p style="margin:0 0 6px;">{{template "chip" (dict "label" $r.Severity "tone" (sevTone $r.Severity))}}&nbsp;&nbsp;{{with advisoryURL $r.CVEID}}<a href="{{.}}" style="color:#7fb2f0;font-family:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,monospace;font-size:13px;font-weight:700;text-decoration:none;">{{$r.CVEID}}</a>{{else}}<span style="color:#e4ecf6;font-family:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,monospace;font-size:13px;font-weight:700;">{{$r.CVEID}}</span>{{end}}</p>
<p style="margin:0;color:#9fb3ca;font-size:13px;line-height:1.5;"><span style="color:#e4ecf6;font-weight:600;">{{$r.PackageName}}</span> on {{$r.ServerName}}</p>
</td>
<td align="right" style="padding:12px 16px 12px 0;{{if $i}}border-top:1px solid #172c44;{{end}}vertical-align:top;white-space:nowrap;font-size:12px;font-weight:600;">
{{- if $r.FixedIn}}<span style="color:#4fb484;">Fixed in {{$r.FixedIn}}</span>{{else}}<span style="color:#d6a63f;">No fix yet</span>{{end -}}
</td>
</tr>
{{- end}}
</table>
{{- end -}}
{{- $tone := tone -}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="dark">
<meta name="supported-color-schemes" content="dark">
<title>{{template "title" .}}</title>
</head>
<body style="margin:0;padding:0;background:#071628;" bgcolor="#071628">
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:#071628;font-size:1px;line-height:1px;">{{template "preheader" .}}&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;&#8199;&#847;</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" bgcolor="#071628" style="background:#071628;">
<tr>
<td align="center" style="padding:32px 12px 40px;">
<table role="presentation" width="560" cellpadding="0" cellspacing="0" style="max-width:560px;width:100%;font-family:ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;">
<tr>
<td style="padding:0 4px 16px;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td style="vertical-align:middle;">
<table role="presentation" cellpadding="0" cellspacing="0"><tr>
<td width="28" height="28" align="center" style="width:28px;height:28px;border-radius:7px;background:#5b9be8;color:#04101f;font-size:15px;font-weight:800;line-height:28px;text-align:center;">V</td>
<td style="padding-left:10px;color:#e4ecf6;font-size:16px;font-weight:700;letter-spacing:-.01em;">Vantage</td>
</tr></table>
</td>
<td align="right" style="vertical-align:middle;color:#71879f;font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;">{{template "category" .}}</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#0d2138;border:1px solid #1e3855;border-radius:10px;border-collapse:separate;overflow:hidden;">
<tr><td style="height:4px;line-height:4px;font-size:0;background:{{pick $tone "#4fb484" "#e2705a" "#d6a63f" "#5b9be8" "#5b9be8"}};border-radius:10px 10px 0 0;">&nbsp;</td></tr>
<tr>
<td style="padding:28px 32px 8px;background-color:#0d2138;background-image:linear-gradient(180deg,{{pick $tone "#173743" "#2d2d3d" "#2b3539" "#193352" "#193352"}} 0%,#0d2138 100%);">
<table role="presentation" cellpadding="0" cellspacing="0" style="margin:0 0 16px;"><tr>
<td width="40" height="40" align="center" style="width:40px;height:40px;border-radius:9999px;background:{{pick $tone "#173743" "#2d2d3d" "#2b3539" "#193352" "#193352"}};border:1px solid {{pick $tone "#245452" "#573d44" "#534f3a" "#284b75" "#284b75"}};color:{{pick $tone "#4fb484" "#e2705a" "#d6a63f" "#7fb2f0" "#7fb2f0"}};font-size:18px;font-weight:800;line-height:40px;text-align:center;">{{pick $tone "✓" "!" "!" "→" "→"}}</td>
<td style="padding-left:12px;vertical-align:middle;">{{template "pill" .}}</td>
</tr></table>
<h1 style="margin:0 0 6px;font-size:23px;font-weight:700;line-height:1.3;letter-spacing:-.01em;color:#e4ecf6;">{{template "title" .}}</h1>
</td>
</tr>
<tr>
<td style="padding:12px 32px 12px;">
{{template "body" .}}
</td>
</tr>
<tr>
<td style="padding:16px 32px 18px;border-top:1px solid #1e3855;background:#0a1c30;border-radius:0 0 10px 10px;">
<p style="margin:0;color:#71879f;font-size:12px;line-height:1.6;"><span style="color:#9fb3ca;font-weight:600;">Why you got this:</span> {{template "why" .}}</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding:22px 8px 0;">
{{- with hq}}
<p style="margin:0 0 10px;font-size:12px;line-height:1.6;">
<a href="{{.}}" style="color:#9fb3ca;text-decoration:none;font-weight:600;">Vantage HQ</a>
<span style="color:#3d5673;">&nbsp;&middot;&nbsp;</span>
<a href="{{.}}/instances" style="color:#9fb3ca;text-decoration:none;font-weight:600;">Instances</a>
<span style="color:#3d5673;">&nbsp;&middot;&nbsp;</span>
<a href="{{.}}/billing" style="color:#9fb3ca;text-decoration:none;font-weight:600;">Billing</a>
<span style="color:#3d5673;">&nbsp;&middot;&nbsp;</span>
<a href="{{.}}/settings" style="color:#9fb3ca;text-decoration:none;font-weight:600;">Settings</a>
</p>
{{- end}}
<p style="margin:0 0 4px;color:#71879f;font-size:12px;line-height:1.6;">Vantage &middot; infrastructure control plane</p>
<p style="margin:0;color:#4f6680;font-size:11px;line-height:1.6;">&copy; {{year}} Vantage. This is an automated message about your service.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>