Wrong server was published before. This is the one that runs on the hosting account the video was made on: FiveRP - two-step registration (account, then an identity printed onto a passport), login that returns you to your resident, and a loading screen driven by the game's own streaming events. resources/[local]/fiverp-auth NUI + scrypt hashes + oxmysql resources/[local]/fiverp-characters identity, character load, spawn resources/[local]/fiverp-loadscreen loading screen sql/schema.sql accounts, characters docs/DESIGN.md the design system every screen follows docs/screens/ shot from the live server Only our own code is in here. cfx-server-data and oxmysql are fetched by install.sh instead of being vendored, which keeps the repo at 3.7 MB. install.sh does the whole box in one command: recommended FXServer build, cfx-server-data, oxmysql, MariaDB with the schema, resources, server.cfg (mode 600 - it carries the key and the database password), boot entry, then it waits for the Cfx registration. Tested end to end on a spare install root: 29 resources scanned, database and schema created, and it stopped exactly where a wrong licence key should stop it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
728 lines
20 KiB
CSS
728 lines
20 KiB
CSS
/* FiveRP — passport office (registration + sign in).
|
|
Design system: /opt/fivem/server-data/CLAUDE.md
|
|
|
|
Deliberately zero box-shadow on panels. CEF composites the shadow of a
|
|
backdrop-filtered element as an opaque rectangle, which is what put a black
|
|
square behind this panel in game. Depth here comes from blur, hairlines and
|
|
inner highlights only. */
|
|
|
|
@font-face {
|
|
font-family: 'Unbounded';
|
|
font-style: normal;
|
|
font-weight: 300 800;
|
|
font-display: block;
|
|
src: url('fonts/unbounded-latin.woff2') format('woff2');
|
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
|
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
|
|
U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
}
|
|
@font-face {
|
|
font-family: 'Unbounded';
|
|
font-style: normal;
|
|
font-weight: 300 800;
|
|
font-display: block;
|
|
src: url('fonts/unbounded-latin-ext.woff2') format('woff2');
|
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
|
|
U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
|
|
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
}
|
|
@font-face {
|
|
font-family: 'Unbounded';
|
|
font-style: normal;
|
|
font-weight: 300 800;
|
|
font-display: block;
|
|
src: url('fonts/unbounded-cyrillic.woff2') format('woff2');
|
|
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
}
|
|
|
|
:root {
|
|
--surface: rgba(255, 255, 255, 0.72);
|
|
--panel: rgba(255, 255, 255, 0.80);
|
|
--field-bg: rgba(0, 0, 0, 0.04);
|
|
--field-bg-focus:#ffffff;
|
|
|
|
--fg: #1d1d1f;
|
|
--fg-secondary: #6e6e73;
|
|
--fg-tertiary: #a1a1a6;
|
|
|
|
--accent: #0071e3;
|
|
--accent-strong: #0077ed;
|
|
--accent-soft: rgba(0, 113, 227, 0.08);
|
|
--ring: rgba(0, 113, 227, 0.18);
|
|
|
|
--line: rgba(0, 0, 0, 0.06);
|
|
--line-strong: rgba(0, 0, 0, 0.10);
|
|
--hover: rgba(0, 0, 0, 0.04);
|
|
|
|
--success: #34c759;
|
|
--danger: #ff3b30;
|
|
|
|
--radius-lg: 1.5rem;
|
|
--radius-md: 1rem;
|
|
--radius-sm: 0.7rem;
|
|
|
|
/* passport ink */
|
|
--pp-navy: #10315e;
|
|
--pp-label: #46719f;
|
|
--pp-value: #14202f;
|
|
|
|
--sans: 'Unbounded', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
'Helvetica Neue', Arial, sans-serif;
|
|
--mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
|
|
'Liberation Mono', 'DejaVu Sans Mono', monospace;
|
|
--ease: cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
color-scheme: light;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
html, body {
|
|
width: 100%; height: 100%;
|
|
background: transparent; /* the game is behind this page */
|
|
overflow: hidden;
|
|
font-family: var(--sans);
|
|
letter-spacing: -0.005em;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------- stage */
|
|
.stage {
|
|
position: fixed;
|
|
inset: 0;
|
|
display: grid;
|
|
place-items: center;
|
|
padding: 32px;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: opacity 460ms var(--ease), visibility 0s linear 460ms;
|
|
/* Lightening only. Never darken the game to make the panel readable —
|
|
that is what produced the grey box before. */
|
|
background: radial-gradient(90% 70% at 50% 50%,
|
|
rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 72%);
|
|
}
|
|
.stage.is-open {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transition: opacity 460ms var(--ease), visibility 0s;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- slab */
|
|
.slab {
|
|
position: relative;
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) 520px;
|
|
width: min(1080px, 94vw);
|
|
background: var(--surface);
|
|
border: 1px solid rgba(255, 255, 255, 0.55);
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
transform: translateY(14px) scale(0.985);
|
|
opacity: 0;
|
|
transition:
|
|
width 620ms var(--ease),
|
|
grid-template-columns 620ms var(--ease),
|
|
transform 620ms var(--ease),
|
|
opacity 480ms var(--ease);
|
|
}
|
|
.stage.is-open .slab { transform: none; opacity: 1; }
|
|
|
|
/* Signing in needs no passport — collapse to a single column. */
|
|
.slab[data-view="login"] {
|
|
width: min(460px, 94vw);
|
|
grid-template-columns: minmax(0, 1fr) 0px;
|
|
}
|
|
.slab[data-view="login"] .pane-card {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transform: translateX(24px);
|
|
}
|
|
|
|
.pane { min-width: 0; }
|
|
|
|
.pane-form {
|
|
padding: 40px 40px 34px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.pane-card {
|
|
padding: 32px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: 14px;
|
|
border-left: 1px solid var(--line);
|
|
background: linear-gradient(180deg, rgba(255,255,255,0.34), rgba(255,255,255,0.14));
|
|
transition: opacity 420ms var(--ease), transform 620ms var(--ease);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- eyebrow */
|
|
.eyebrow {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.20em;
|
|
text-transform: uppercase;
|
|
color: var(--fg-tertiary);
|
|
margin-bottom: 24px;
|
|
}
|
|
.eyebrow .dot {
|
|
width: 3px; height: 3px;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- rail */
|
|
.rail {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
margin-bottom: 0;
|
|
opacity: 0;
|
|
height: 0;
|
|
overflow: hidden;
|
|
transition: opacity 360ms var(--ease);
|
|
}
|
|
/* Signing in has no steps, so the rail collapses margin and all. */
|
|
.slab[data-view="account"] .rail,
|
|
.slab[data-view="identity"] .rail { opacity: 1; height: auto; margin-bottom: 30px; }
|
|
|
|
.rail-step { display: flex; align-items: center; gap: 8px; }
|
|
.rail-mark {
|
|
position: relative;
|
|
width: 11px; height: 11px;
|
|
border-radius: 50%;
|
|
border: 1.5px solid var(--line-strong);
|
|
background: transparent;
|
|
transition: background-color .3s var(--ease), border-color .3s var(--ease);
|
|
}
|
|
.rail-step.is-active .rail-mark {
|
|
border-color: var(--accent);
|
|
background: var(--accent);
|
|
}
|
|
.rail-step.is-done .rail-mark {
|
|
border-color: var(--accent);
|
|
background: var(--accent);
|
|
}
|
|
/* Done and active must not look identical — done gets a tick. */
|
|
.rail-step.is-done .rail-mark::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 2.4px; top: 1px;
|
|
width: 3px; height: 5.5px;
|
|
border: solid #fff;
|
|
border-width: 0 1.5px 1.5px 0;
|
|
transform: rotate(42deg);
|
|
}
|
|
.rail-label {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.09em;
|
|
text-transform: uppercase;
|
|
color: var(--fg-tertiary);
|
|
transition: color .3s var(--ease);
|
|
}
|
|
.rail-step.is-active .rail-label,
|
|
.rail-step.is-done .rail-label { color: var(--fg-secondary); }
|
|
|
|
.rail-line {
|
|
position: relative;
|
|
flex: 1;
|
|
height: 1.5px;
|
|
border-radius: 2px;
|
|
background: var(--line-strong);
|
|
overflow: hidden;
|
|
}
|
|
.rail-line i {
|
|
position: absolute;
|
|
inset: 0 auto 0 0;
|
|
width: 0;
|
|
background: var(--accent);
|
|
transition: width 560ms var(--ease);
|
|
}
|
|
.rail-line i.is-full { width: 100%; }
|
|
|
|
/* ------------------------------------------------------------------- views */
|
|
.view {
|
|
display: none;
|
|
flex-direction: column;
|
|
}
|
|
.view.is-current { display: flex; animation: viewIn .5s var(--ease) both; }
|
|
|
|
@keyframes viewIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: none; }
|
|
}
|
|
|
|
.title {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.035em;
|
|
line-height: 1.1;
|
|
color: var(--fg);
|
|
margin-bottom: 8px;
|
|
}
|
|
.sub {
|
|
font-size: 13.5px;
|
|
font-weight: 400;
|
|
line-height: 1.55;
|
|
color: var(--fg-secondary);
|
|
margin-bottom: 26px;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ fields */
|
|
.field {
|
|
display: block;
|
|
margin-bottom: 16px;
|
|
}
|
|
.label {
|
|
display: block;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.09em;
|
|
text-transform: uppercase;
|
|
color: var(--fg-secondary);
|
|
margin-bottom: 7px;
|
|
}
|
|
.field input {
|
|
width: 100%;
|
|
border-radius: var(--radius-md);
|
|
padding: 13px 14px;
|
|
font-family: var(--sans);
|
|
font-size: 14.5px;
|
|
font-weight: 400;
|
|
letter-spacing: -0.005em;
|
|
color: var(--fg);
|
|
background: var(--field-bg);
|
|
border: 1px solid transparent;
|
|
outline: none;
|
|
transition: background-color .2s, border-color .2s, box-shadow .2s;
|
|
}
|
|
.field input::placeholder { color: var(--fg-tertiary); }
|
|
.field input:focus {
|
|
background: var(--field-bg-focus);
|
|
border-color: var(--accent);
|
|
}
|
|
.field input.is-bad {
|
|
border-color: var(--danger);
|
|
}
|
|
|
|
.hint {
|
|
display: block;
|
|
margin-top: 7px;
|
|
font-size: 11px;
|
|
font-weight: 400;
|
|
color: var(--fg-tertiary);
|
|
}
|
|
|
|
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
|
.row .field { margin-bottom: 16px; }
|
|
|
|
/* password strength */
|
|
.pwmeter {
|
|
display: block;
|
|
height: 3px;
|
|
margin-top: 9px;
|
|
border-radius: 2px;
|
|
background: var(--line);
|
|
opacity: 0;
|
|
transition: opacity .25s ease;
|
|
}
|
|
.pwmeter.is-live { opacity: 1; }
|
|
.pwmeter i {
|
|
display: block;
|
|
height: 100%;
|
|
width: 0;
|
|
border-radius: 2px;
|
|
background: var(--danger);
|
|
transition: width .3s var(--ease), background-color .3s ease;
|
|
}
|
|
.pwmeter[data-level="2"] i { background: #ff9f0a; }
|
|
.pwmeter[data-level="3"] i { background: var(--success); }
|
|
|
|
/* ------------------------------------------------------------------ notice */
|
|
.notice {
|
|
font-size: 12.5px;
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
color: var(--danger);
|
|
max-height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
transition: max-height .3s var(--ease), opacity .3s ease, margin .3s var(--ease);
|
|
}
|
|
.notice.is-shown {
|
|
max-height: 60px;
|
|
opacity: 1;
|
|
margin-bottom: 14px;
|
|
}
|
|
.notice.is-good { color: #1f8f3d; }
|
|
|
|
/* ----------------------------------------------------------------- buttons */
|
|
.btn {
|
|
position: relative;
|
|
width: 100%;
|
|
border: 0;
|
|
border-radius: 999px;
|
|
padding: 14px 24px;
|
|
font-family: var(--sans);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
letter-spacing: -0.01em;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
background-image: linear-gradient(180deg, var(--accent-strong), var(--accent));
|
|
transition: filter .2s ease, transform .12s var(--ease), opacity .2s ease;
|
|
}
|
|
.btn:hover:not(:disabled) { filter: brightness(1.08); }
|
|
.btn:active:not(:disabled) { transform: scale(0.978); }
|
|
.btn:disabled { opacity: .55; cursor: default; }
|
|
|
|
.btn.is-busy span { opacity: 0; }
|
|
.btn.is-busy::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%; left: 50%;
|
|
width: 16px; height: 16px;
|
|
margin: -8px 0 0 -8px;
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(255, 255, 255, 0.35);
|
|
border-top-color: #fff;
|
|
animation: spin .7s linear infinite;
|
|
}
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.switch {
|
|
margin-top: 18px;
|
|
text-align: center;
|
|
font-size: 12.5px;
|
|
font-weight: 400;
|
|
color: var(--fg-secondary);
|
|
}
|
|
.link {
|
|
border: 0;
|
|
background: none;
|
|
padding: 0;
|
|
font-family: var(--sans);
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
letter-spacing: -0.01em;
|
|
color: var(--accent);
|
|
cursor: pointer;
|
|
}
|
|
.link:hover { text-decoration: underline; }
|
|
|
|
/* ==================================================================== PASSPORT
|
|
A United States passport data page. This is the one saturated object on the
|
|
screen; the form beside it stays neutral so the document reads as the hero. */
|
|
|
|
.passport {
|
|
position: relative;
|
|
width: 100%;
|
|
aspect-ratio: 125 / 88; /* ID-3, the real bio-page proportion */
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
padding: 11px 15px 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
color: var(--pp-value);
|
|
background:
|
|
radial-gradient(78% 66% at 16% 14%, rgba(255,255,255,0.92), rgba(255,255,255,0) 62%),
|
|
linear-gradient(155deg, #f8fbff 0%, #e9f1fa 46%, #f5f1e7 100%);
|
|
border: 1px solid rgba(16, 49, 94, 0.16);
|
|
transition: transform 700ms var(--ease);
|
|
}
|
|
.passport.is-issued { transform: translateY(-3px); }
|
|
|
|
/* Engine-turned security print. Three cheap repeating gradients read
|
|
convincingly as guilloche at this scale. */
|
|
.pp-guilloche {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
opacity: .55;
|
|
background:
|
|
repeating-radial-gradient(circle at 20% 44%, rgba(16,49,94,.055) 0 1px, transparent 1px 7px),
|
|
repeating-radial-gradient(circle at 80% 62%, rgba(16,49,94,.05) 0 1px, transparent 1px 9px),
|
|
repeating-linear-gradient(58deg, rgba(16,49,94,.032) 0 1px, transparent 1px 6px);
|
|
}
|
|
|
|
/* Laminate glare that follows the cursor. */
|
|
.pp-sheen {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
opacity: .55;
|
|
background: radial-gradient(circle at var(--mx, 78%) var(--my, 10%),
|
|
rgba(255,255,255,0.75) 0%, rgba(255,255,255,0) 46%);
|
|
transition: opacity .5s ease;
|
|
}
|
|
|
|
/* ------------------------------------------------------------ passport head */
|
|
.pp-head {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding-bottom: 9px;
|
|
border-bottom: 1px solid rgba(16, 49, 94, 0.18);
|
|
}
|
|
.pp-seal { flex: none; width: 30px; height: 30px; display: block; }
|
|
.pp-seal svg { width: 100%; height: 100%; display: block; }
|
|
.pp-seal .ring-out,
|
|
.pp-seal .ring-in { fill: none; stroke: rgba(16,49,94,.55); stroke-width: 1.1; }
|
|
.pp-seal .ring-in { stroke-width: .6; }
|
|
.pp-seal .stars { fill: rgba(16,49,94,.6); }
|
|
.pp-seal .shield-chief { fill: rgba(16,49,94,.75); }
|
|
.pp-seal .shield-pales { fill: rgba(16,49,94,.28); }
|
|
.pp-seal .shield-edge { fill: none; stroke: rgba(16,49,94,.65); stroke-width: 1.2; }
|
|
|
|
.pp-head-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
.pp-kind {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.30em;
|
|
text-transform: uppercase;
|
|
color: var(--pp-navy);
|
|
}
|
|
.pp-country {
|
|
font-size: 8px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
color: var(--pp-label);
|
|
}
|
|
.pp-chip { margin-left: auto; width: 19px; height: 15px; opacity: .6; }
|
|
.pp-chip svg { width: 100%; height: 100%; fill: none; stroke: rgba(16,49,94,.6); stroke-width: 1; }
|
|
|
|
/* ------------------------------------------------------------ passport body */
|
|
.pp-body {
|
|
position: relative;
|
|
display: grid;
|
|
grid-template-columns: 84px minmax(0, 1fr);
|
|
gap: 12px;
|
|
padding-top: 8px;
|
|
}
|
|
|
|
.pp-left { display: flex; flex-direction: column; gap: 9px; }
|
|
|
|
.pp-photo {
|
|
position: relative;
|
|
align-self: start;
|
|
border: 1px solid rgba(16, 49, 94, 0.22);
|
|
border-radius: 3px;
|
|
background: linear-gradient(180deg, #eef2f7, #dde5ee);
|
|
aspect-ratio: 35 / 45; /* passport photo proportion */
|
|
overflow: hidden;
|
|
display: grid;
|
|
place-items: end center;
|
|
}
|
|
.pp-bust { width: 76%; height: 88%; align-self: end; }
|
|
.pp-photo-label {
|
|
position: absolute;
|
|
top: 4px; left: 5px;
|
|
font-size: 6px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: rgba(16, 49, 94, 0.42);
|
|
}
|
|
|
|
/* The secondary faded portrait real passports carry to the right of the data. */
|
|
.pp-ghost {
|
|
position: absolute;
|
|
right: 2px; top: 34%;
|
|
width: 50px;
|
|
opacity: .1;
|
|
pointer-events: none;
|
|
}
|
|
.pp-ghost svg { width: 100%; display: block; fill: var(--pp-navy); }
|
|
|
|
.pp-fields {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 4px 10px;
|
|
align-content: start;
|
|
min-width: 0;
|
|
}
|
|
.pp-f { grid-column: span 2; min-width: 0; }
|
|
.pp-f--type { grid-column: span 1; }
|
|
.pp-f--code { grid-column: span 1; }
|
|
.pp-f--no { grid-column: span 4; }
|
|
.pp-f--wide { grid-column: span 6; }
|
|
|
|
.pp-f dt {
|
|
font-size: 6.5px;
|
|
font-weight: 600;
|
|
line-height: 1.35;
|
|
letter-spacing: 0.13em;
|
|
text-transform: uppercase;
|
|
color: var(--pp-label);
|
|
white-space: nowrap;
|
|
}
|
|
.pp-f dd {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
line-height: 1.3;
|
|
letter-spacing: -0.01em;
|
|
color: var(--pp-value);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.pp-f dd.mono {
|
|
font-family: var(--mono);
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
.pp-f--auth { grid-column: span 4; }
|
|
.pp-f--auth dd { font-size: 8.5px; font-weight: 400; color: var(--pp-label); }
|
|
|
|
/* The two fields the player actually controls — set larger than the rest. */
|
|
.pp-name {
|
|
font-size: 15px !important;
|
|
font-weight: 700 !important;
|
|
line-height: 1.25 !important;
|
|
letter-spacing: -0.02em !important;
|
|
text-transform: uppercase;
|
|
color: var(--pp-navy) !important;
|
|
min-height: 19px;
|
|
}
|
|
.pp-name:empty::before {
|
|
content: attr(data-empty);
|
|
color: rgba(16, 49, 94, 0.25);
|
|
}
|
|
/* Each keystroke re-prints the value; the fade makes it feel typeset. */
|
|
.printed { display: inline-block; animation: printIn .4s var(--ease) both; }
|
|
@keyframes printIn {
|
|
from { opacity: 0; transform: translateY(3px); filter: blur(3px); }
|
|
to { opacity: 1; transform: none; filter: blur(0); }
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- signature */
|
|
.pp-sign {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.pp-sign-ink {
|
|
font-family: 'Segoe Script', 'Bradley Hand', 'Snell Roundhand', cursive;
|
|
font-size: 11px;
|
|
color: rgba(16, 49, 94, 0.75);
|
|
height: 14px;
|
|
line-height: 14px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
transform: rotate(-1.5deg);
|
|
transform-origin: left bottom;
|
|
}
|
|
.pp-sign-rule { height: 1px; background: rgba(16, 49, 94, 0.28); }
|
|
.pp-sign-label {
|
|
font-size: 6px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.14em;
|
|
text-transform: uppercase;
|
|
color: var(--pp-label);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- MRZ */
|
|
.pp-mrz {
|
|
margin: 6px -15px 0;
|
|
margin-top: auto; /* pinned to the foot of the page */
|
|
padding: 6px 15px 7px;
|
|
border-top: 1px solid rgba(16, 49, 94, 0.18);
|
|
background: rgba(255, 255, 255, 0.62);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
.pp-mrz span {
|
|
display: block;
|
|
font-family: var(--mono);
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
line-height: 1.25;
|
|
letter-spacing: 0.055em;
|
|
color: var(--pp-navy);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------- stamp */
|
|
.pp-stamp {
|
|
position: absolute;
|
|
right: 13px; bottom: 50px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1px;
|
|
padding: 7px 13px;
|
|
border: 2px solid rgba(176, 42, 42, 0.62);
|
|
border-radius: 4px;
|
|
color: rgba(176, 42, 42, 0.78);
|
|
/* multiply lets the guilloche show through, like real stamp ink */
|
|
mix-blend-mode: multiply;
|
|
transform: rotate(-13deg) scale(1.5);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 420ms var(--ease), transform 520ms cubic-bezier(.2,1.4,.4,1);
|
|
}
|
|
.pp-stamp.is-stamped { opacity: 1; transform: rotate(-13deg) scale(1); }
|
|
|
|
.pp-stamp-top, .pp-stamp-bot {
|
|
font-size: 6.5px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.2em;
|
|
text-transform: uppercase;
|
|
}
|
|
.pp-stamp-mid {
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.pp-caption {
|
|
text-align: center;
|
|
font-size: 11px;
|
|
font-weight: 400;
|
|
color: var(--fg-tertiary);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------- motion */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: .001ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: .001ms !important;
|
|
}
|
|
}
|
|
|
|
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
|
|
::selection { background: var(--accent-soft); color: var(--accent-strong); }
|
|
|
|
/* ---------------------------------------------------------------- NUI final
|
|
CEF turns backdrop-filter and box-shadow into opaque black plates at the
|
|
element bounds, so both are gone from this file entirely. Panels earn their
|
|
separation from opacity and hairlines instead of blur, which means the
|
|
surface has to carry more white than it did when a blur sat behind it.
|
|
Appended last so it wins over anything above. */
|
|
:root {
|
|
--surface: rgba(255, 255, 255, 0.95);
|
|
--panel: rgba(255, 255, 255, 0.97);
|
|
}
|
|
.slab {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
}
|
|
.stage { background: none; }
|
|
.field:focus,
|
|
.field:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: -1px;
|
|
}
|