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."
|
||||||