Files
redl-fivem-rp/server-code/data/resources/[rp]/rp_loading/html/app.css
T
redlandClaude Fable 5 80c1d75d2f Part 1 final build: Los Santos RP written on camera by the agent
rp_core (sessions, scrypt auth, self-written MySQL-wire driver rp_db),
county-records NUI: residency-file auth, identity-record character
intake, survey-grid spawn, procedural night-city loading screen.
Secrets replaced with .example files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 19:27:48 +00:00

303 lines
7.6 KiB
CSS

/* ---------------------------------------------------------------------------
Loading screen.
Layout: the city runs full bleed; the record sits in a rail on the left,
held there by a gradient scrim rather than a panel, so the two never feel
like separate windows.
--------------------------------------------------------------------------- */
#sky, .grain, .scrim, .vignette {
position: fixed;
inset: 0;
pointer-events: none;
}
#sky { width: 100%; height: 100%; display: block; }
.scrim {
background: linear-gradient(
90deg,
rgba(8, 9, 11, 0.97) 0%,
rgba(8, 9, 11, 0.93) 34%,
rgba(8, 9, 11, 0.72) 58%,
rgba(8, 9, 11, 0.15) 82%,
rgba(8, 9, 11, 0) 100%
);
}
.vignette {
background: radial-gradient(120% 90% at 62% 45%, transparent 40%, rgba(0, 0, 0, 0.55) 100%);
}
/* film grain, drawn once as an SVG turbulence tile */
.grain {
opacity: 0.16;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
animation: grainshift 640ms steps(2) infinite;
}
@keyframes grainshift {
0% { transform: translate(0, 0); }
50% { transform: translate(-3px, 2px); }
100% { transform: translate(2px, -2px); }
}
/* --- rail ---------------------------------------------------------------- */
.rail {
position: relative;
z-index: 2;
width: var(--rail);
height: 100%;
padding: var(--gut-y) var(--gut-x);
display: grid;
grid-template-rows: auto 1fr auto auto;
gap: clamp(20px, 2.4vh, 34px);
}
/* staged entrance; each element declares its own delay */
.reveal {
opacity: 0;
transform: translateY(10px);
animation: rise 900ms var(--ease-out) forwards;
animation-delay: var(--d, 0ms);
}
@keyframes rise {
to { opacity: 1; transform: none; }
}
/* --- file header --------------------------------------------------------- */
.file {
border-top: 1px solid var(--sodium-line);
padding-top: 14px;
}
.file .serial {
margin-top: 7px;
color: var(--paper-dim);
}
.file .serial span { color: var(--sodium); }
/* --- wordmark ------------------------------------------------------------ */
.brand { align-self: end; }
.wordmark {
font-size: clamp(46px, 5.2vw, 82px);
font-weight: 800;
line-height: 0.86;
letter-spacing: 0.3em;
text-indent: 0.3em; /* keeps the wide tracking optically centred */
opacity: 0;
animation: settle 1500ms var(--ease-out) 180ms forwards;
}
/* the name tightens into place, like a stamp being pressed */
@keyframes settle {
0% { opacity: 0; letter-spacing: 0.3em; text-indent: 0.3em; filter: blur(6px); }
60% { opacity: 1; }
100% { opacity: 1; letter-spacing: -0.028em; text-indent: 0; filter: blur(0); }
}
.tagrow {
display: flex;
align-items: center;
gap: 14px;
margin-top: 16px;
}
.tagline {
font-family: 'Plex Mono', monospace;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.42em;
color: var(--sodium);
text-transform: uppercase;
}
.dash {
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--sodium-line), transparent);
}
/* --- notices ------------------------------------------------------------- */
.notices { align-self: center; }
.notice-head {
display: flex;
justify-content: space-between;
align-items: baseline;
padding-bottom: 10px;
border-bottom: 1px solid var(--line);
}
.notice-index { color: var(--muted); }
.notice { padding-top: 18px; min-height: 132px; }
.notice-title {
font-size: 21px;
font-weight: 600;
letter-spacing: -0.015em;
margin-bottom: 8px;
}
.notice-body {
font-size: 14.5px;
color: var(--paper-dim);
max-width: 40ch;
}
.notice.swap .notice-title,
.notice.swap .notice-body { animation: none; }
.notice-title, .notice-body {
animation: noticein 620ms var(--ease-out) both;
}
.notice-body { animation-delay: 70ms; }
@keyframes noticein {
from { opacity: 0; transform: translateY(7px); }
to { opacity: 1; transform: none; }
}
/* --- progress ------------------------------------------------------------ */
.load { align-self: end; }
.loadhead {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 11px;
gap: 16px;
}
.step {
color: var(--paper-dim);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.pct {
font-family: 'Plex Mono', monospace;
font-weight: 500;
font-size: 28px;
line-height: 1;
color: var(--paper);
font-variant-numeric: tabular-nums;
}
.pct i {
font-style: normal;
font-size: 13px;
color: var(--muted);
margin-left: 2px;
}
.track {
position: relative;
height: 2px;
background: var(--line);
overflow: hidden;
}
.bar {
display: block;
height: 100%;
width: 0%;
background: var(--sodium);
transition: width 420ms var(--ease);
}
/* a light runs ahead of the fill so the bar reads as active even when the
game stalls on a long step */
.bar::after {
content: '';
position: absolute;
top: 0;
right: 0;
width: 64px;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 159, 69, 0.85));
animation: crawl 1900ms linear infinite;
}
@keyframes crawl {
0% { transform: translateX(-64px); opacity: 0; }
35% { opacity: 1; }
100% { transform: translateX(0); opacity: 0; }
}
/* --- volume fader -------------------------------------------------------- */
.fader {
display: flex;
align-items: center;
gap: 14px;
margin-top: 26px;
padding-top: 18px;
border-top: 1px solid var(--line-soft);
}
.mute {
width: 30px;
height: 30px;
display: grid;
place-items: center;
background: transparent;
border: 1px solid var(--line);
color: var(--paper-dim);
cursor: pointer;
transition: color 180ms var(--ease), border-color 180ms var(--ease), transform 120ms var(--ease);
}
.mute:hover { color: var(--sodium); border-color: var(--sodium-line); }
.mute:active { transform: translateY(1px); }
.mute.off #wave { opacity: 0.18; }
.mute.off { color: var(--muted); }
input[type='range'] {
flex: 1;
-webkit-appearance: none;
appearance: none;
height: 22px;
background: transparent;
cursor: pointer;
}
input[type='range']::-webkit-slider-runnable-track {
height: 2px;
background: var(--line);
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
width: 3px;
height: 15px;
margin-top: -6.5px;
background: var(--sodium);
border: 0;
border-radius: 0;
transition: height 160ms var(--ease), margin-top 160ms var(--ease);
}
input[type='range']:hover::-webkit-slider-thumb,
input[type='range']:focus-visible::-webkit-slider-thumb {
height: 21px;
margin-top: -9.5px;
}
.volval { width: 3ch; text-align: right; color: var(--muted); }
.audiohint {
margin-top: 10px;
color: var(--sodium);
animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: 0.45; } }
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* Once the world is ready the rail steps aside and reveals the live camera.
The page background has to go transparent too, otherwise the fade reveals
nothing but our own backdrop colour. */
body.handover, body.handover .rail { background: transparent !important; }
body.handover .scrim { transition: opacity 1400ms var(--ease); opacity: 0; }
body.handover #sky { transition: opacity 1400ms var(--ease); opacity: 0; }
body.handover .grain { transition: opacity 1400ms var(--ease); opacity: 0; }
body.handover .rail { transition: opacity 900ms var(--ease), transform 1200ms var(--ease); opacity: 0; transform: translateY(-12px); }