chore: init repo
77
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
CLAUDE.md
|
||||
.claude
|
||||
logs
|
||||
logs-archive
|
||||
utils/save*sh
|
||||
.fastembed_cache
|
||||
presentaciones
|
||||
COMMIT_MESSAGE.md
|
||||
.wrks
|
||||
nushell
|
||||
nushell-*
|
||||
*.tar.gz
|
||||
#*-nushell-plugins.tar.gz
|
||||
github-com
|
||||
.coder
|
||||
target
|
||||
artifacts/
|
||||
distribution
|
||||
.qodo
|
||||
# enviroment to load on bin/build
|
||||
.env
|
||||
# OSX trash
|
||||
.DS_Store
|
||||
|
||||
# Vscode files
|
||||
.vscode
|
||||
|
||||
# Emacs save files
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
|
||||
# Vim-related files
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
|
||||
# cscope-related files
|
||||
cscope.*
|
||||
|
||||
# User cluster configs
|
||||
.kubeconfig
|
||||
|
||||
.tags*
|
||||
|
||||
# direnv .envrc files
|
||||
.envrc
|
||||
|
||||
# make-related metadata
|
||||
/.make/
|
||||
|
||||
# Just in time generated data in the source, should never be committed
|
||||
/test/e2e/generated/bindata.go
|
||||
|
||||
# This file used by some vendor repos (e.g. github.com/go-openapi/...) to store secret variables and should not be ignored
|
||||
!\.drone\.sec
|
||||
|
||||
# Godeps workspace
|
||||
/Godeps/_workspace
|
||||
|
||||
/bazel-*
|
||||
*.pyc
|
||||
|
||||
# generated by verify-vendor.sh
|
||||
vendordiff.patch
|
||||
.claude/settings.local.json
|
||||
|
||||
# Generated SBOM files
|
||||
SBOM.*.json
|
||||
*.sbom.json
|
||||
|
||||
# UnoCSS build
|
||||
assets/css/node_modules/
|
||||
assets/css/pnpm-lock.yaml
|
||||
crates/ontoref-daemon/public/css/ontoref.css
|
||||
115
CHANGES.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Changes
|
||||
|
||||
Record of accepted architectural decisions and visible surface changes for
|
||||
`lian-build`. Format follows [Keep a Changelog]; project is pre-1.0 so the
|
||||
SemVer guarantee is **not** yet in force — schema and CLI shape may break
|
||||
between minor versions until `0.1.0` is tagged.
|
||||
|
||||
Dates reflect the ADR acceptance date or the date the on-disk artefact was
|
||||
recorded; this project does not yet publish git tags or release builds.
|
||||
|
||||
[Keep a Changelog]: https://keepachangelog.com/en/1.1.0/
|
||||
|
||||
---
|
||||
|
||||
## [Unreleased] — 0.1.0
|
||||
|
||||
### Architecture
|
||||
|
||||
- **adr-003 — Nickel coupled via subprocess, not library linkage** *(2026-05-03)*
|
||||
All NCL → Rust conversions flow through `directives::nickel_export(path,
|
||||
&[import_paths])`, which spawns `nickel export --format json` and feeds
|
||||
serde. No `nickel-lang*` crate is added to `Cargo.toml`. `nickel` is a
|
||||
runtime requirement on `$PATH`.
|
||||
|
||||
- **adr-002 — CLI surface is subcommand-only with stdout reserved for
|
||||
structured envelopes** *(2026-05-03)*
|
||||
`lian-build` now exposes `{build, integrate}` as clap subcommands. There is
|
||||
no default subcommand and no flat-arg compatibility shim. `tracing_subscriber`
|
||||
is pinned to `with_writer(std::io::stderr)` so stdout carries at most one
|
||||
envelope per invocation. **Breaking** for any caller invoking the legacy
|
||||
`buildkit-launcher` flat-arg form: migrate to `lian-build build <args>`.
|
||||
|
||||
- **adr-001 — lian-build is a standalone build substrate, not part of
|
||||
provisioning** *(2026-05-01)*
|
||||
`buildkit-launcher` and `buildkit_runner` extracted from
|
||||
`provisioning/platform/crates/buildkit-launcher` and
|
||||
`provisioning/extensions/components/buildkit_runner` into this standalone
|
||||
project. Binary renamed from `buildkit-launcher` to `lian-build`.
|
||||
|
||||
### Added
|
||||
|
||||
- `lian-build build` subcommand. Accepts flat args **or** `--directives
|
||||
<file.ncl>`; directives take precedence over flat-arg fields when both are
|
||||
present (`src/main.rs::resolve_build_plan`).
|
||||
- `lian-build integrate` subcommand. Federated probe that reads a
|
||||
`SecretDeliveryContext` JSON envelope from stdin and emits a single
|
||||
`ResultEnvelope` JSON line on stdout (`src/integration/`).
|
||||
- `BuildDirectives` NCL vocabulary (`schemas/build_directives.ncl`) and
|
||||
matching Rust types in `src/directives.rs` with serde round-trip tests.
|
||||
- `BuildSpec` schema (`schemas/build_spec.ncl`) with `bounded_cpu_` ≤ 256 and
|
||||
`bounded_time_budget_` ≤ 1440 min contract bounds; honoured by
|
||||
`sizing::resolve` at the explicit-spec tier.
|
||||
- `CachePolicy` schema (`schemas/cache_policy.ncl`) with `BuildMode` =
|
||||
`'ci | 'session`, `SessionActor` (`'human | 'agent | 'ci_aux`), and
|
||||
`SessionCacheDisposition` (`'export | 'discard | 'rollback`).
|
||||
- `src/cache.rs` — `build_cache_flags(workspace, mode, registry)` returning
|
||||
import/export `Vec<String>`; enforces `ci/<workspace>/*` (canonical,
|
||||
read-only to sessions) and `dev/<actor-id>-<workspace>/*` (per-session).
|
||||
Invariants guarded by unit tests: `ci_never_imports_dev`,
|
||||
`session_never_exports_ci`, `session_imports_both_layers`,
|
||||
`mode_switch_purity`.
|
||||
- `src/sizing.rs` — three-tier resolution: explicit `.build-spec.ncl` →
|
||||
P95 historical (×1.2, floored) → language defaults (rust=4/8/60,
|
||||
go=2/4/30, java|kotlin|scala=4/8/45, default=2/4/30).
|
||||
- `src/retry.rs` — `MAX_OOM_RETRIES = 1` (hard bound), `SIZE_TIERS` walk
|
||||
`cx22 → cx32 → cx42 → cx52`. OOM is detected via exit-code `137` or stderr
|
||||
matching `OOM` / `Killed`.
|
||||
- `src/orchestrator_client.rs` — HTTP client wrapping responses in
|
||||
`ApiResponse<T> { success, data, error }`. Methods: `spawn_runner`,
|
||||
`destroy_runner`, `get_p95`, `record_metrics`.
|
||||
- `src/buildctl_runner.rs` — `rsync_context` and `run_buildctl` over SSH.
|
||||
- `src/nats_events.rs` — `BuildEventPublisher` over
|
||||
`platform_nats::EventStream`. Publishes `started` / `completed` / `failed`
|
||||
to `<prefix>.<workspace>.build.<event>`. Connect failures warn and disable
|
||||
publishing; they never fail a build.
|
||||
- `defaults/build_directives.ncl` — `make_*` constructors and
|
||||
`ci_cache_policy` / `session_cache_policy` helpers.
|
||||
- `examples/sample.ncl` — example `BuildDirectives` instance.
|
||||
- Ontoref scaffolding: `.ontology/{core,state,gate,manifest}.ncl`,
|
||||
`.ontoref/project.ncl`, `card.ncl`, `reflection/{backlog,qa}.ncl`,
|
||||
`reflection/modes/`, `catalog/{domains,modes}/`.
|
||||
- Repository assets: `assets/` (logo and branding), `justfile` with imports
|
||||
from `justfiles/{build,test,ci}.just`, `.pre-commit-config.yaml`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Binary target renamed `buildkit-launcher` → `lian-build`. The
|
||||
`tracing` env-filter directive remains the legacy crate name
|
||||
(`buildkit_launcher=info`) — explicitly noted in `CLAUDE.md`.
|
||||
- `--platforms` is now a comma-separated list (multi-platform builds);
|
||||
legacy single-value invocations remain valid.
|
||||
|
||||
### Constraints (grep-checked, ADR-001)
|
||||
|
||||
- `no-provisioning-lib-import` — `Cargo.toml` and `src/` must not match
|
||||
`platform-config|provisioning|stratum-`. The `platform-nats` path-dep from
|
||||
`stratumiops` is explicitly allowed (it is shared infrastructure, not the
|
||||
provisioning workspace).
|
||||
- `build-directives-ncl-vocabulary` — `src/` must not match
|
||||
`provisioning_workspace|vapora_|woodpecker_`.
|
||||
|
||||
### Status (`.ontology/state.ncl`)
|
||||
|
||||
| Dimension | Current | Desired |
|
||||
|------------------------------------|--------------------------|---------------------------|
|
||||
| provider-pluggability-maturity | hcloud-zot-only | provider-trait-stable |
|
||||
| session-multi-actor-maturity | ci-single-actor | multi-actor-stable |
|
||||
| active-active-registry-maturity | single-zot-libre-wuji | active-active-verified |
|
||||
| caller-integration-maturity | provisioning-only | multi-caller-stable |
|
||||
| peer-publishing-status | packaged | production |
|
||||
|
||||
`ComputeProvider` / `RegistryProvider` Rust traits are **not yet declared**;
|
||||
the orchestrator client is currently hcloud-shaped. Compute is selected via
|
||||
NCL discriminant but dispatch in core is monolithic — see ADR-001 and the
|
||||
`provider-pluggability-maturity` dimension.
|
||||
244
README.md
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
<p align="">
|
||||
<img src="assets/lian-h-static.svg" alt="ontoref" width="300" />
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
# lian-build
|
||||
|
||||
> 炼 — *alchemical refinement.* Standalone build substrate for ephemeral BuildKit sessions.
|
||||
|
||||
`lian-build` is a single Rust binary that orchestrates ephemeral remote BuildKit
|
||||
runs against a pluggable orchestrator.
|
||||
|
||||
Callers (provisioning, vapora, workspace CI) supply intent as `BuildDirectives` in NCL; `lian-build` controls compute
|
||||
provisioning, OCI cache flow, and multi-actor session namespacing.
|
||||
|
||||
Compute (`hcloud` / `proxmox` / `docker-local`) and registry (`zot` / `harbor` / `ghcr`) are plug-in slots.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Crate | `lian-build` (binary), `0.1.0` |
|
||||
| Status | Beta · pre-1.0, schema and CLI surface still mobile |
|
||||
| Edition | 2021 |
|
||||
| ADRs | adr-001 lift-out · adr-002 CLI subcommand discipline · adr-003 Nickel via subprocess |
|
||||
|
||||
---
|
||||
|
||||
## What it is
|
||||
|
||||
- An **orchestrator client**, not a buildkitd. It spawns runners through an
|
||||
external HTTP orchestrator (default `http://localhost:9011`), `rsync`s the
|
||||
build context, then drives `buildctl` over SSH.
|
||||
- A **directives consumer**. Schemas live in `schemas/*.ncl`; the Rust types
|
||||
in `src/directives.rs` mirror them and round-trip via `serde_json`.
|
||||
- An **event publisher**. `started` / `completed` / `failed` lifecycle events
|
||||
go to NATS at `<prefix>.<workspace>.build.<event>` (best-effort — NATS
|
||||
failures never fail a build).
|
||||
|
||||
## What it is not
|
||||
|
||||
- Not a library. There is no `lib.rs`. Public surface is the CLI and the NCL
|
||||
schemas.
|
||||
- Not coupled to provisioning. ADR-001 forbids importing `provisioning`,
|
||||
`platform-config`, or any `stratum-`-prefixed crate (grep-checked).
|
||||
- Not a Nickel runtime. NCL is parsed by shelling out to the `nickel` CLI
|
||||
(ADR-003). `nickel` must be on `$PATH`.
|
||||
|
||||
---
|
||||
|
||||
## CLI
|
||||
|
||||
Two subcommands, no default, no flat-arg fallback (ADR-002). All logs go to
|
||||
**stderr**; **stdout** is reserved for one structured envelope per invocation.
|
||||
|
||||
### `lian-build build`
|
||||
|
||||
Run a build on an ephemeral runner. Flat args **or** a directives file:
|
||||
|
||||
```bash
|
||||
lian-build build \
|
||||
--workspace <name> \
|
||||
--context <dir> \
|
||||
--image <fully-qualified-ref> \
|
||||
--ssh-key <path> \
|
||||
[--directives <file.ncl>] \
|
||||
[--dockerfile Dockerfile] \
|
||||
[--cache-from <ref>] [--cache-to <ref>] \
|
||||
[--language rust|go|java|...] \
|
||||
[--platforms linux/amd64,linux/arm64] \
|
||||
[--runner-image <id>] \
|
||||
[--orchestrator-url <url>] \
|
||||
[--nats-url <url>] [--nats-nkey-seed <seed>] [--nats-subject-prefix <p>]
|
||||
```
|
||||
|
||||
When `--directives <file.ncl>` is supplied it takes precedence over flat-arg
|
||||
fields except `--ssh-key`, which is still required separately (directives
|
||||
don't yet carry an inline SSH reference).
|
||||
|
||||
Environment fallbacks: `BUILDKIT_WORKSPACE`, `BUILDKIT_SSH_KEY`,
|
||||
`BUILDKIT_RUNNER_IMAGE`, `ORCHESTRATOR_URL`, `NATS_URL`, `NATS_NKEY_SEED`,
|
||||
`NATS_SUBJECT_PREFIX`.
|
||||
|
||||
### `lian-build integrate`
|
||||
|
||||
Federated probe. Reads a `SecretDeliveryContext` JSON envelope from stdin,
|
||||
emits a `ResultEnvelope` JSON line on stdout, optionally publishes a
|
||||
completion event to NATS.
|
||||
|
||||
```bash
|
||||
echo '<context-json>' | lian-build integrate \
|
||||
[--nats-url <url>] [--nats-nkey-seed <seed>]
|
||||
```
|
||||
|
||||
Omit `--nats-url` to skip event emission with a warning (the envelope still
|
||||
goes to stdout).
|
||||
|
||||
---
|
||||
|
||||
## Three-tier runner sizing
|
||||
|
||||
`sizing::resolve` walks three sources, first match wins:
|
||||
|
||||
1. **Explicit** — `.build-spec.ncl` in the build context, validated against
|
||||
`schemas/build_spec.ncl` (`bounded_cpu_` ≤ 256, `bounded_time_budget_` ≤
|
||||
1440 min).
|
||||
2. **P95 historical** — `OrchestratorClient::get_p95(workspace)` returns
|
||||
measured CPU / memory P95 from prior runs; multiplied by 1.2, floored at
|
||||
`min(2 cpu, 4 GB)`.
|
||||
3. **Language defaults** — `RunnerSize::language_default(lang)`:
|
||||
|
||||
| Language | CPU | Memory | Time budget |
|
||||
|------------------------|-----|--------|-------------|
|
||||
| `rust` | 4 | 8 GB | 60 min |
|
||||
| `go` | 2 | 4 GB | 30 min |
|
||||
| `java` / `kotlin` / `scala` | 4 | 8 GB | 45 min |
|
||||
| *(default)* | 2 | 4 GB | 30 min |
|
||||
|
||||
On exit-code `137` or stderr containing `OOM` / `Killed`, the build retries
|
||||
**once** at the next tier (`cx22` → `cx32` → `cx42` → `cx52`). The retry cap
|
||||
is hard-bound (`retry::MAX_OOM_RETRIES = 1`).
|
||||
|
||||
---
|
||||
|
||||
## Cache namespacing
|
||||
|
||||
Two layers, defined in `schemas/cache_policy.ncl` and enforced in `src/cache.rs`:
|
||||
|
||||
- `ci/<workspace>/*` — canonical, written by CI, **read-only** to sessions.
|
||||
- `dev/<actor-id>-<workspace>/*` — ephemeral per-session actor.
|
||||
|
||||
Sessions read from both layers; CI never imports from `dev/*`. These
|
||||
invariants are guarded by tests under `src/cache.rs`.
|
||||
|
||||
---
|
||||
|
||||
## NCL contract surface
|
||||
|
||||
| Schema | Defines |
|
||||
|-------------------------------|-------------------------------------------------------------------------|
|
||||
| `schemas/build_directives.ncl`| `BuildDirectives`, `BuildArtifact`, `ComputeProviderRef`, `RegistryProviderRef`, `RunnerOverride`, `NatsEventConfig` |
|
||||
| `schemas/build_spec.ncl` | `BuildSpec` (per-repo `.build-spec.ncl`) |
|
||||
| `schemas/cache_policy.ncl` | `CachePolicy`, `BuildMode`, `SessionActor`, `SessionCacheDisposition` |
|
||||
| `schemas/build_result.ncl` | `BuildResult` envelope shape |
|
||||
| `schemas/vault_refs.ncl` | `VaultCredRef`, `VaultKeyRef` |
|
||||
| `defaults/build_directives.ncl` | `make_*` constructors, `ci_cache_policy` / `session_cache_policy` helpers |
|
||||
|
||||
Validate with the `nickel` CLI:
|
||||
|
||||
```bash
|
||||
nickel export \
|
||||
--import-path /Users/Akasha/Development/lian-build \
|
||||
--import-path /Users/Akasha/Development/lian-build/schemas \
|
||||
--import-path /Users/Akasha/Development/ontoref \
|
||||
--import-path /Users/Akasha/Development/ontoref/ontology \
|
||||
schemas/build_directives.ncl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hard constraints (ADR-001)
|
||||
|
||||
These two rules are grep-checked and define the lift-out boundary:
|
||||
|
||||
1. **`no-provisioning-lib-import`** — `Cargo.toml` and `src/` must not match
|
||||
`platform-config|provisioning|stratum-`. The `platform-nats` path-dep from
|
||||
`stratumiops` is explicitly allowed; the constraint targets the
|
||||
`provisioning` workspace and `stratum-`-prefixed crates.
|
||||
2. **`build-directives-ncl-vocabulary`** — `src/` must not match
|
||||
`provisioning_workspace|vapora_|woodpecker_`. Caller-specific logic stays
|
||||
in caller-supplied directives, not in core.
|
||||
|
||||
---
|
||||
|
||||
## Build / test / run
|
||||
|
||||
```bash
|
||||
cargo build # debug
|
||||
cargo build --release # release at target/release/lian-build
|
||||
cargo clippy -- -D warnings # mandatory before commit
|
||||
cargo fmt
|
||||
cargo test # full suite
|
||||
cargo test <name> # single test by substring
|
||||
cargo test -- --nocapture # show tracing in tests
|
||||
```
|
||||
|
||||
`just` recipes live in `justfiles/{build,test,ci}.just`. Run `just` (or
|
||||
`just help`) to list them.
|
||||
|
||||
### Stratumiops peer dependency
|
||||
|
||||
`platform-nats` is consumed as a local path dependency from
|
||||
`/Users/Akasha/Development/stratumiops/crates/platform-nats` (declared
|
||||
directly in `Cargo.toml`). That path must exist for `cargo build` to
|
||||
succeed — there is no feature flag to disable it.
|
||||
|
||||
---
|
||||
|
||||
## Module layout
|
||||
|
||||
```
|
||||
src/
|
||||
main.rs # CLI parsing, top-level orchestration, OOM-retry control flow
|
||||
buildctl_runner.rs # rsync_context + run_buildctl over SSH; OOM_EXIT_CODE = 137
|
||||
cache.rs # BuildMode, build_cache_flags, ci/* vs dev/<actor>/* invariants
|
||||
directives.rs # BuildDirectives ↔ JSON via `nickel export` subprocess
|
||||
integration/ # federated probe handler (stdin → ResultEnvelope on stdout)
|
||||
context.rs · event.rs · handler.rs · result.rs · mod.rs
|
||||
nats_events.rs # BuildEventPublisher over platform-nats::EventStream
|
||||
orchestrator_client.rs # HTTP client: spawn_runner, destroy_runner, get_p95, record_metrics
|
||||
retry.rs # MAX_OOM_RETRIES = 1; SIZE_TIERS walk (cx22→cx32→cx42→cx52)
|
||||
sizing.rs # three-tier resolution
|
||||
```
|
||||
|
||||
Operational surface:
|
||||
|
||||
```
|
||||
adrs/ # accepted ADRs (NCL)
|
||||
.ontology/ # core, state, gate, manifest — design intent
|
||||
reflection/ # modes, backlog, qa
|
||||
schemas/ # caller-facing NCL contracts
|
||||
defaults/ # constructor / helper NCL
|
||||
catalog/{domains,modes}/ # federated peer publishing layout
|
||||
examples/sample.ncl # example BuildDirectives instance
|
||||
tests/fixtures/ # integration test fixtures
|
||||
.coder/ # session interaction files (not product docs)
|
||||
.claude/ # operational config (symlinks into shared dev-system)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Further reading
|
||||
|
||||
- `adrs/adr-001-lian-build-as-standalone.ncl` — why this project exists,
|
||||
alternatives rejected, the two grep-checked invariants.
|
||||
- `adrs/adr-002-cli-subcommand-discipline.ncl` — subcommand-only surface,
|
||||
stderr/stdout discipline.
|
||||
- `adrs/adr-003-nickel-via-subprocess.ncl` — why `nickel` is on `$PATH`,
|
||||
not in `Cargo.toml`.
|
||||
- `.ontology/core.ncl` — axioms (ephemeral-builds, provider-pluggability,
|
||||
cache-content-addressed, caller-supplies-directives), tensions, practices.
|
||||
- `.ontology/state.ncl` — five maturity dimensions and their current
|
||||
transitions (provider-pluggability, session-multi-actor,
|
||||
active-active-registry, caller-integration, peer-publishing).
|
||||
- `CHANGES.md` — record of accepted decisions and visible surface changes.
|
||||
1062
assets/branding/index.html
Normal file
71
assets/branding/lian-dark-h.svg
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="ldh-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ldh-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.ldh-flame{fill:url(#ldh-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:ldh-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-core{fill:url(#ldh-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:ldh-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-cruc{fill:none;stroke:#c0c0b8;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:ldh-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-word{fill:#d4a870;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:ldh-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-sub{fill:#fbb555;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:ldh-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-fbord{fill:none;stroke:#f07830;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:ldh-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldh-code{opacity:0;animation:ldh-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes ldh-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldh-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes ldh-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldh-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldh-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@keyframes ldh-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldh-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.ldh-flame,.ldh-fbord,.ldh-core,.ldh-cruc,.ldh-word,.ldh-sub,.ldh-code{animation:none}
|
||||
.ldh-flame{opacity:1} .ldh-fbord{opacity:1} .ldh-code{opacity:1}
|
||||
.ldh-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.ldh-cruc{opacity:.8;stroke-dashoffset:0} .ldh-word{opacity:1} .ldh-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect width="163" height="90" fill="#0d1117"/>
|
||||
<path class="ldh-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="ldh-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="ldh-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="ldh-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="ldh-code" fill="none" stroke="#d4642a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="ldh-word" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="ldh-sub" x="83" y="64" text-anchor="start">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
71
assets/branding/lian-dark-v.svg
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="ldv-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ldv-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.ldv-flame{fill:url(#ldv-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:ldv-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-core{fill:url(#ldv-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:ldv-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-cruc{fill:none;stroke:#c0c0b8;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:36;stroke-dashoffset:36;animation:ldv-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-word{fill:#d4a870;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:ldv-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-sub{fill:#fbb555;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:ldv-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-fbord{fill:none;stroke:#f07830;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:ldv-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.ldv-code{opacity:0;animation:ldv-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes ldv-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldv-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes ldv-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldv-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldv-cruc{
|
||||
0%,18.3%{stroke-dashoffset:36;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:36;opacity:0}
|
||||
}
|
||||
@keyframes ldv-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes ldv-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.ldv-flame,.ldv-fbord,.ldv-core,.ldv-cruc,.ldv-word,.ldv-sub,.ldv-code{animation:none}
|
||||
.ldv-flame{opacity:1} .ldv-fbord{opacity:1} .ldv-code{opacity:1}
|
||||
.ldv-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.ldv-cruc{opacity:.8;stroke-dashoffset:0} .ldv-word{opacity:1} .ldv-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect x="55" width="110" height="160" fill="#0d1117"/>
|
||||
<path class="ldv-cruc" d="M 92 66 L 128 66"/>
|
||||
<path class="ldv-flame" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="ldv-fbord" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="ldv-core" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z"/>
|
||||
<g class="ldv-code" fill="none" stroke="#d4642a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="ldv-word" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="ldv-sub" x="112" y="110" text-anchor="middle">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
29
assets/branding/lian-h-static.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lhs-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lhs-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lhs-word,.lhs-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".8"/>
|
||||
<path fill="url(#lhs-fg)" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="url(#lhs-cg)" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z" opacity=".35" transform="translate(50,57) scale(.92) translate(-50,-57)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lhs-word" fill="#bd9156" font-size="22" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lhs-sub" fill="#f29a3d" font-size="14" x="83" y="64" text-anchor="start" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
70
assets/branding/lian-h.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lh-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lh-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lh-flame{fill:url(#lh-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-core{fill:url(#lh-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:lh-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lh-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lh-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lh-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-code{opacity:0;animation:lh-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lh-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lh-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@keyframes lh-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lh-flame,.lh-fbord,.lh-core,.lh-cruc,.lh-word,.lh-sub,.lh-code{animation:none}
|
||||
.lh-flame{opacity:1} .lh-fbord{opacity:1} .lh-code{opacity:1}
|
||||
.lh-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lh-cruc{opacity:.8;stroke-dashoffset:0} .lh-word{opacity:1} .lh-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lh-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="lh-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="lh-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lh-word" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lh-sub" x="83" y="64" text-anchor="start">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
23
assets/branding/lian-icon-static.svg
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="30 20 40 48" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build icon">
|
||||
<title>Lian Build icon</title>
|
||||
<defs>
|
||||
<linearGradient id="lis-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lis-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".8"/>
|
||||
<path fill="url(#lis-fg)" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="url(#lis-cg)" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z" opacity=".35" transform="translate(50,57) scale(.92) translate(-50,-57)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
59
assets/branding/lian-icon.svg
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="30 20 40 48" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build icon">
|
||||
<title>Lian Build icon</title>
|
||||
<defs>
|
||||
<linearGradient id="li-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="li-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
.li-flame{fill:url(#li-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:li-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-core{fill:url(#li-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:li-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:li-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:li-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-code{opacity:0;animation:li-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes li-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes li-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.li-flame,.li-fbord,.li-core,.li-cruc,.li-code{animation:none}
|
||||
.li-flame{opacity:1} .li-fbord{opacity:1} .li-code{opacity:1}
|
||||
.li-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.li-cruc{opacity:.8;stroke-dashoffset:0}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="li-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="li-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="li-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="li-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="li-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
21
assets/branding/lian-mono-black-h.svg
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lmbh-word,.lmbh-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#1a1a1a" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".75"/>
|
||||
<path fill="#1a1a1a" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#1a1a1a" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="#5a5a5a" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g fill="none" stroke="#d8d8d8" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lmbh-word" fill="#1a1a1a" font-size="22" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lmbh-sub" fill="#3d3d3d" font-size="14" x="83" y="64" text-anchor="start" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
21
assets/branding/lian-mono-black-v.svg
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lmbv-word,.lmbv-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#1a1a1a" stroke-width="1.5" stroke-linecap="round" d="M 92 66 L 128 66" opacity=".75"/>
|
||||
<path fill="#1a1a1a" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="none" stroke="#1a1a1a" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="#5a5a5a" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z"/>
|
||||
<g fill="none" stroke="#d8d8d8" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lmbv-word" fill="#1a1a1a" font-size="22" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lmbv-sub" fill="#3d3d3d" font-size="14" x="112" y="110" text-anchor="middle" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
22
assets/branding/lian-mono-white-h.svg
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lmwh-word,.lmwh-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect width="163" height="90" fill="#0d1117"/>
|
||||
<path fill="none" stroke="#d0d0c8" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".75"/>
|
||||
<path fill="#e8e8e8" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="#ffffff" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z" opacity=".45"/>
|
||||
<g fill="none" stroke="#c0c0c0" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lmwh-word" fill="#ffffff" font-size="22" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lmwh-sub" fill="#c0c0c0" font-size="14" x="83" y="64" text-anchor="start" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
22
assets/branding/lian-mono-white-v.svg
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lmwv-word,.lmwv-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect x="55" width="110" height="160" fill="#0d1117"/>
|
||||
<path fill="none" stroke="#d0d0c8" stroke-width="1.5" stroke-linecap="round" d="M 92 66 L 128 66" opacity=".75"/>
|
||||
<path fill="#e8e8e8" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="#ffffff" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z" opacity=".45"/>
|
||||
<g fill="none" stroke="#c0c0c0" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lmwv-word" fill="#ffffff" font-size="22" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lmwv-sub" fill="#c0c0c0" font-size="14" x="112" y="110" text-anchor="middle" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
18
assets/branding/lian-text.svg
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="65 0 110 80" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build wordmark">
|
||||
<title>Lian Build wordmark</title>
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
@keyframes lt-in{0%{opacity:0;transform:translateY(6px)}100%{opacity:1;transform:translateY(0)}}
|
||||
.lt-w{animation:lt-in .9s ease-out .3s both}
|
||||
.lt-s{animation:lt-in .9s ease-out .55s both}
|
||||
]]></style>
|
||||
</defs>
|
||||
<text class="lt-w" x="120" y="54" text-anchor="middle"
|
||||
font-family='"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif'
|
||||
font-weight="500" font-size="52" letter-spacing=".1em" fill="#bd9156">lian</text>
|
||||
<text class="lt-s" x="124" y="74" text-anchor="middle"
|
||||
font-family='"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif'
|
||||
font-weight="500" font-size="20" letter-spacing=".22em" fill="#f29a3d">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
29
assets/branding/lian-v-static.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lvs-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lvs-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lvs-word,.lvs-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 92 66 L 128 66" opacity=".8"/>
|
||||
<path fill="url(#lvs-fg)" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="url(#lvs-cg)" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z" opacity=".35" transform-origin="110 58" transform="scale(.92)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lvs-word" fill="#bd9156" font-size="22" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lvs-sub" fill="#f29a3d" font-size="14" x="112" y="110" text-anchor="middle" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
70
assets/branding/lian-v.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lv-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lv-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lv-flame{fill:url(#lv-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-core{fill:url(#lv-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:36;stroke-dashoffset:36;animation:lv-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lv-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lv-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lv-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-code{opacity:0;animation:lv-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lv-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lv-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-cruc{
|
||||
0%,18.3%{stroke-dashoffset:36;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:36;opacity:0}
|
||||
}
|
||||
@keyframes lv-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lv-flame,.lv-fbord,.lv-core,.lv-cruc,.lv-word,.lv-sub,.lv-code{animation:none}
|
||||
.lv-flame{opacity:1} .lv-fbord{opacity:1} .lv-code{opacity:1}
|
||||
.lv-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lv-cruc{opacity:.8;stroke-dashoffset:0} .lv-word{opacity:1} .lv-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lv-cruc" d="M 92 66 L 128 66"/>
|
||||
<path class="lv-flame" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-fbord" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-core" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z"/>
|
||||
<g class="lv-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lv-word" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lv-sub" x="112" y="110" text-anchor="middle">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
29
assets/lian-h-static.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lhs-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lhs-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lhs-word,.lhs-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".8"/>
|
||||
<path fill="url(#lhs-fg)" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="url(#lhs-cg)" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z" opacity=".35" transform="translate(50,57) scale(.92) translate(-50,-57)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lhs-word" fill="#bd9156" font-size="22" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lhs-sub" fill="#f29a3d" font-size="14" x="83" y="64" text-anchor="start" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
70
assets/lian-h.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lh-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lh-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lh-flame{fill:url(#lh-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-core{fill:url(#lh-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:lh-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lh-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lh-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lh-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-code{opacity:0;animation:lh-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lh-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lh-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@keyframes lh-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lh-flame,.lh-fbord,.lh-core,.lh-cruc,.lh-word,.lh-sub,.lh-code{animation:none}
|
||||
.lh-flame{opacity:1} .lh-fbord{opacity:1} .lh-code{opacity:1}
|
||||
.lh-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lh-cruc{opacity:.8;stroke-dashoffset:0} .lh-word{opacity:1} .lh-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lh-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="lh-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="lh-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lh-word" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lh-sub" x="83" y="64" text-anchor="start">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
29
assets/lian-v-static.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lvs-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lvs-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lvs-word,.lvs-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 92 66 L 128 66" opacity=".8"/>
|
||||
<path fill="url(#lvs-fg)" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path fill="url(#lvs-cg)" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z" opacity=".35" transform-origin="110 58" transform="scale(.92)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lvs-word" fill="#bd9156" font-size="22" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lvs-sub" fill="#f29a3d" font-size="14" x="112" y="110" text-anchor="middle" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
70
assets/lian-v.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lv-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lv-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lv-flame{fill:url(#lv-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-core{fill:url(#lv-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:36;stroke-dashoffset:36;animation:lv-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lv-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lv-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lv-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-code{opacity:0;animation:lv-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lv-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lv-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-cruc{
|
||||
0%,18.3%{stroke-dashoffset:36;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:36;opacity:0}
|
||||
}
|
||||
@keyframes lv-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lv-flame,.lv-fbord,.lv-core,.lv-cruc,.lv-word,.lv-sub,.lv-code{animation:none}
|
||||
.lv-flame{opacity:1} .lv-fbord{opacity:1} .lv-code{opacity:1}
|
||||
.lv-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lv-cruc{opacity:.8;stroke-dashoffset:0} .lv-word{opacity:1} .lv-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lv-cruc" d="M 92 66 L 128 66"/>
|
||||
<path class="lv-flame" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-fbord" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-core" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z"/>
|
||||
<g class="lv-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lv-word" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lv-sub" x="112" y="110" text-anchor="middle">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
13
assets/web/index.html
Normal file
29
assets/web/lian-h-static.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lhs-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lhs-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lhs-word,.lhs-sub{font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path fill="none" stroke="#a8a8a0" stroke-width="1.5" stroke-linecap="round" d="M 33 65 L 67 65" opacity=".8"/>
|
||||
<path fill="url(#lhs-fg)" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="none" stroke="#e86c2f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path fill="url(#lhs-cg)" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z" opacity=".35" transform="translate(50,57) scale(.92) translate(-50,-57)"/>
|
||||
<g fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lhs-word" fill="#bd9156" font-size="22" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lhs-sub" fill="#f29a3d" font-size="14" x="83" y="64" text-anchor="start" opacity=".85">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
70
assets/web/lian-h.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="28 19 110 50" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lh-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lh-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lh-flame{fill:url(#lh-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-core{fill:url(#lh-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lh-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:lh-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lh-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lh-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lh-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lh-code{opacity:0;animation:lh-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lh-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lh-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@keyframes lh-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lh-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lh-flame,.lh-fbord,.lh-core,.lh-cruc,.lh-word,.lh-sub,.lh-code{animation:none}
|
||||
.lh-flame{opacity:1} .lh-fbord{opacity:1} .lh-code{opacity:1}
|
||||
.lh-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lh-cruc{opacity:.8;stroke-dashoffset:0} .lh-word{opacity:1} .lh-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lh-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="lh-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="lh-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="lh-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
<text class="lh-word" x="80" y="46" text-anchor="start">lian</text>
|
||||
<text class="lh-sub" x="83" y="64" text-anchor="start">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
59
assets/web/lian-icon.svg
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="30 20 40 48" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build icon">
|
||||
<title>Lian Build icon</title>
|
||||
<defs>
|
||||
<linearGradient id="li-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="li-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
.li-flame{fill:url(#li-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:li-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-core{fill:url(#li-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:li-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:34;stroke-dashoffset:34;animation:li-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:li-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.li-code{opacity:0;animation:li-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes li-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes li-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes li-cruc{
|
||||
0%,18.3%{stroke-dashoffset:34;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:34;opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.li-flame,.li-fbord,.li-core,.li-cruc,.li-code{animation:none}
|
||||
.li-flame{opacity:1} .li-fbord{opacity:1} .li-code{opacity:1}
|
||||
.li-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.li-cruc{opacity:.8;stroke-dashoffset:0}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="li-cruc" d="M 33 65 L 67 65"/>
|
||||
<path class="li-flame" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="li-fbord" d="M 50 62 C 33 57 33 37 50 23 C 67 37 67 57 50 62 Z"/>
|
||||
<path class="li-core" d="M 50 57 C 41 52 41 40 50 32 C 59 40 59 52 50 57 Z"/>
|
||||
<g class="li-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="46,42 43,46 46,50"/>
|
||||
<line x1="51" y1="43" x2="48" y2="49"/>
|
||||
<polyline points="54,42 57,46 54,50"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
70
assets/web/lian-v.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="85 19 56 100" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Lian Build">
|
||||
<title>Lian Build</title>
|
||||
<defs>
|
||||
<linearGradient id="lv-fg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#e86c2f"/>
|
||||
<stop offset="1" stop-color="#f5a623"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lv-cg" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0" stop-color="#f5a623"/>
|
||||
<stop offset="1" stop-color="#fcd99e"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500&display=swap');
|
||||
.lv-flame{fill:url(#lv-fg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-flame 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-core{fill:url(#lv-cg);opacity:0;transform-origin:50% 100%;transform-box:fill-box;animation:lv-core 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-cruc{fill:none;stroke:#a8a8a0;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:36;stroke-dashoffset:36;animation:lv-cruc 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-word{fill:#bd9156;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:22px;opacity:0;animation:lv-word 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-sub{fill:#f29a3d;font-family:"Jost","Avenir Next",Futura,"Century Gothic","Helvetica Neue",system-ui,sans-serif;font-weight:500;letter-spacing:.22em;font-size:14px;opacity:0;animation:lv-sub 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-fbord{fill:none;stroke:#e86c2f;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation:lv-fbord 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
.lv-code{opacity:0;animation:lv-code 23s cubic-bezier(.4,0,.2,1) infinite}
|
||||
@keyframes lv-core{
|
||||
0%{opacity:0;transform:scale(.5)} 1.3%{opacity:.75;transform:scale(1.05)}
|
||||
3.5%{opacity:.55;transform:scale(.95)} 5.2%{opacity:.35;transform:scale(.92)}
|
||||
22%{opacity:.55;transform:scale(1)} 37%{opacity:.24;transform:scale(.88)}
|
||||
53%{opacity:.55;transform:scale(1)} 68%{opacity:.24;transform:scale(.88)}
|
||||
83%{opacity:.55;transform:scale(1)} 99%{opacity:.35;transform:scale(.92)} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-flame{
|
||||
0%,6.5%{opacity:0;transform:scale(.4)} 8.7%{opacity:.9;transform:scale(1)}
|
||||
10.4%{opacity:1;transform:scale(1.05)} 12%{opacity:1;transform:scale(1)}
|
||||
99%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.4)}
|
||||
}
|
||||
@keyframes lv-fbord{
|
||||
0%,11.7%{opacity:0} 14.3%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-code{
|
||||
0%,15.2%{opacity:0} 17.4%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-cruc{
|
||||
0%,18.3%{stroke-dashoffset:36;opacity:0} 18.8%{opacity:.8}
|
||||
22.6%{stroke-dashoffset:0;opacity:.8} 99%{stroke-dashoffset:0;opacity:.8}
|
||||
100%{stroke-dashoffset:36;opacity:0}
|
||||
}
|
||||
@keyframes lv-sub{
|
||||
0%,19.6%{opacity:0} 23.9%{opacity:.85} 99%{opacity:.85} 100%{opacity:0}
|
||||
}
|
||||
@keyframes lv-word{
|
||||
0%,20.4%{opacity:0} 25.2%{opacity:1} 99%{opacity:1} 100%{opacity:0}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.lv-flame,.lv-fbord,.lv-core,.lv-cruc,.lv-word,.lv-sub,.lv-code{animation:none}
|
||||
.lv-flame{opacity:1} .lv-fbord{opacity:1} .lv-code{opacity:1}
|
||||
.lv-core{opacity:.35;transform:scale(.95);transform-origin:50% 100%;transform-box:fill-box}
|
||||
.lv-cruc{opacity:.8;stroke-dashoffset:0} .lv-word{opacity:1} .lv-sub{opacity:.85}
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<path class="lv-cruc" d="M 92 66 L 128 66"/>
|
||||
<path class="lv-flame" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-fbord" d="M 110 64 C 92 57 92 36 110 22 C 128 36 128 57 110 64 Z"/>
|
||||
<path class="lv-core" d="M 110 58 C 100 52 100 40 110 32 C 120 40 120 52 110 58 Z"/>
|
||||
<g class="lv-code" fill="none" stroke="#c25a1a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="106,42 103,46 106,50"/>
|
||||
<line x1="112" y1="43" x2="109" y2="49"/>
|
||||
<polyline points="115,42 118,46 115,50"/>
|
||||
</g>
|
||||
<text class="lv-word" x="113" y="93" text-anchor="middle">lian</text>
|
||||
<text class="lv-sub" x="112" y="110" text-anchor="middle">build</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
110
assets/web/minify.sh
Executable file
|
|
@ -0,0 +1,110 @@
|
|||
#!/usr/bin/env bash
|
||||
# Minify HTML files from src/ to production versions in this directory.
|
||||
# Adapted from /Users/Akasha/Development/ontoref/assets/web/minify.sh.
|
||||
# Usage: ./minify.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
FILES=("index.html")
|
||||
|
||||
minify_file() {
|
||||
local filename="$1"
|
||||
local SRC_FILE="${BASE_DIR}/src/${filename}"
|
||||
local OUT_FILE="${BASE_DIR}/${filename}"
|
||||
local TEMP_FILE="${OUT_FILE}.tmp"
|
||||
|
||||
if [ ! -f "$SRC_FILE" ]; then
|
||||
echo " Source file not found: $SRC_FILE (skipping)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Minifying ${filename}..."
|
||||
echo " Input: $SRC_FILE"
|
||||
echo " Output: $OUT_FILE"
|
||||
|
||||
perl -e "
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
open(my \$fh, '<', '$SRC_FILE') or die \$!;
|
||||
my \$content = do { local \$/; <\$fh> };
|
||||
close(\$fh);
|
||||
|
||||
# Preserve <pre>, <code>, and <textarea> blocks verbatim.
|
||||
my @preserved;
|
||||
\$content =~ s{(<(pre|code|textarea)\b[^>]*>.*?</\2>)}{
|
||||
push @preserved, \$1;
|
||||
'__PRESERVED_' . (\$#preserved) . '__';
|
||||
}gies;
|
||||
|
||||
# Remove HTML comments
|
||||
\$content =~ s/<!--.*?-->//gs;
|
||||
|
||||
# Compress CSS (remove block comments and collapse whitespace)
|
||||
\$content =~ s/(<style[^>]*>)(.*?)(<\/style>)/
|
||||
my \$before = \$1;
|
||||
my \$style = \$2;
|
||||
my \$after = \$3;
|
||||
\$style =~ s{\/\*.*?\*\/}{}gs;
|
||||
\$style =~ s{\s+}{ }gs;
|
||||
\$style =~ s{\s*([{}:;,>+~])\s*}{\$1}gs;
|
||||
\$before . \$style . \$after;
|
||||
/gies;
|
||||
|
||||
# Compress JavaScript (strip line comments and collapse whitespace)
|
||||
\$content =~ s/(<script[^>]*>)(.*?)(<\/script>)/
|
||||
my \$before = \$1;
|
||||
my \$script = \$2;
|
||||
my \$after = \$3;
|
||||
\$script =~ s{\/\/[^\n\r]*}{}g;
|
||||
\$script =~ s{\s+}{ }gs;
|
||||
\$script =~ s{\s*([{}();,])\s*}{\$1}gs;
|
||||
\$before . \$script . \$after;
|
||||
/gies;
|
||||
|
||||
# Remove whitespace between tags
|
||||
\$content =~ s/>\s+</></gs;
|
||||
|
||||
# Compress general whitespace
|
||||
\$content =~ s/\s+/ /gs;
|
||||
|
||||
# Trim
|
||||
\$content =~ s/^\s+|\s+\$//g;
|
||||
|
||||
# Restore preserved blocks
|
||||
\$content =~ s/__PRESERVED_(\d+)__/\$preserved[\$1]/g;
|
||||
|
||||
open(my \$out, '>', '$TEMP_FILE') or die \$!;
|
||||
print \$out \$content;
|
||||
close(\$out);
|
||||
" || {
|
||||
echo "Minification failed"
|
||||
rm -f "$TEMP_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
mv "$TEMP_FILE" "$OUT_FILE"
|
||||
|
||||
original=$(wc -c <"$SRC_FILE")
|
||||
minified=$(wc -c <"$OUT_FILE")
|
||||
saved=$((original - minified))
|
||||
percent=$((saved * 100 / original))
|
||||
|
||||
echo ""
|
||||
echo " Compression statistics:"
|
||||
printf " Original: %6d bytes\n" "$original"
|
||||
printf " Minified: %6d bytes\n" "$minified"
|
||||
printf " Saved: %6d bytes (%d%%)\n" "$saved" "$percent"
|
||||
echo " ${filename} ready for production"
|
||||
}
|
||||
|
||||
echo "Starting HTML minification..."
|
||||
|
||||
for file in "${FILES[@]}"; do
|
||||
minify_file "$file"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "All files minified successfully."
|
||||