impeccable (#18)
first pass with impeccable Reviewed-on: #18 Reviewed-by: xtul <mpiasecki720@protonmail.com> Co-authored-by: vickydotbat <vickydotbat@tutamail.com> Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit was merged in pull request #18.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
---
|
||||
target: SoW NodeBB theme visual pass
|
||||
total_score: 27
|
||||
p0_count: 1
|
||||
p1_count: 3
|
||||
timestamp: 2026-07-11T12-28-14Z
|
||||
slug: sow-nodebb-theme
|
||||
---
|
||||
# Critique: nodebb-theme-westgate (visual pass)
|
||||
|
||||
Method: dual-agent (A: design-review sub-agent · B: detector-evidence sub-agent), live forum at http://localhost:4567 (NodeBB dev stack, theme rebuilt from working tree @ branch `impeccable`, commit 950f3ba).
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Key Issue |
|
||||
|---|-----------|-------|-----------|
|
||||
| 1 | Visibility of System Status | 3 | Unread counts, watching chips, draft badge all present; icon-only topbar gives weak "where am I" feedback |
|
||||
| 2 | Match System / Real World | 3 | Ledger metaphor fits; "World", "Crosspost Topic", "Manage Open Social Web Handles" are opaque jargon |
|
||||
| 3 | User Control and Freedom | 3 | Discard-with-confirm, Escape closes drawer, breadcrumbs, skip-link |
|
||||
| 4 | Consistency and Standards | 2 | Two primary-button styles; post tables unthemed while notification/flags tables are themed; pink default avatars |
|
||||
| 5 | Error Prevention | 3 | Password rules shown upfront on register; discard confirmation |
|
||||
| 6 | Recognition Rather Than Recall | 2 | 9+ icon-only topbar destinations, tooltip-only labels |
|
||||
| 7 | Flexibility and Efficiency | 3 | Good search filters and sorts; no keyboard-shortcut affordance anywhere |
|
||||
| 8 | Aesthetic and Minimalist Design | 3 | Strong restrained system; noise from zero-count stat boxes and 15 zero-count notification filters |
|
||||
| 9 | Error Recovery | 3 | Themed 404 with home link |
|
||||
| 10 | Help and Documentation | 2 | Wiki landing leaks admin setup copy to guests; no member onboarding |
|
||||
| **Total** | | **27/40** | **Acceptable — solid base, specific fixes needed** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
**Not AI slop, not a default skin.** The velvet gradients, stepped gold borders, Cinzel/Jost pairing, and custom footer are genuinely art-directed. No gradient text, no glassmorphism, no hero-metric template, no eyebrow scaffolding.
|
||||
|
||||
Two ban-level violations and a set of "default NodeBB" tells:
|
||||
|
||||
- **Side-stripe ban violated (and the theme's own Gilt Edge Rule):** topic rows carry a 2px gold `border-left` accent — `scss/westgate/_topics.scss:64` (with state variants at 76–182) and `_widgets.scss:108–148`. Detector confirmed (`side-tab` ×3 on /topic/2). The rule says gold lives in 1px edges.
|
||||
- **Display font on data:** profile email in Cinzel wraps mid-token ("ADMIN@WESTGATE.LOCA / L"); timestamps as 11.5px uppercase tracked text ("5 MINUTES AGO") — DESIGN.md's Engraving Rule says timestamps keep letter-spacing 0.
|
||||
- **Default-skin tells:** bright magenta auto-generated avatars, Harmony's grey-triangle profile cover, the near-white "Powered by NodeBB / TipTap" footer pills — the loudest pixels on every page are not the theme's.
|
||||
|
||||
**Deterministic scan:** CLI detector cannot read `.tpl` files, so the shipped templates got zero static coverage; 1786 CLI findings all come from `custom_pages/` mockup artifacts (mostly duplicates/dead files — treat as noise, or delete the stale mockups). The real evidence came from injecting the detector into the live pages: recurring cross-page hits were footer meta text at 4.4:1 contrast (6 of 7 pages), gold glow on footer pills and category cards (`dark-glow`), 1px-border + 16–18px-shadow on the brand mark and avatars, tight line-height 1.25 on card teasers, and ~107–155 characters per line in post/search/wiki prose (aim < 80). Detector's extreme claims (1.1:1 text on gold) are sampling artifacts — visually checked, false positives.
|
||||
|
||||
**Overlays:** the detector ran in a headless browser (Playwright MCP could not launch Chrome on NixOS), so there is no user-visible overlay tab; console findings above are the signal.
|
||||
|
||||
## Overall Impression
|
||||
|
||||
The Velvet Ledger system is real and consistently executed — screenshots are unmistakably this forum, and body text (9.2:1) proves atmosphere is not taxing reading. The biggest opportunity is finishing the last 5%: the places where inherited NodeBB/Bootstrap defaults poke through the velvet (invisible post tables, pink avatars, near-white footer pills, dim focus ring). Each one individually is small; together they are the difference between "themed forum" and "in-world artifact."
|
||||
|
||||
## What's Working
|
||||
|
||||
1. **A real material system, applied everywhere.** The 100° velvet gradient + 1px gold borders at stepped alphas + three panel tones appear identically on cards, composer, search, 404, empty states. Hierarchy comes from tone and border strength, not decoration — the Candlelight Rule holds.
|
||||
2. **Accessibility bones are genuinely built in.** Skip-link first in tab order, consistent outline token on all tabbed controls, aria states on the burger, Escape closes the drawer, wiki tables get focusable scroll wrappers with labels. Only the focus token's alpha is wrong (see P1).
|
||||
3. **Thematic parity ~95%.** Login, register, 404, search, composer, notification empty states all stay in-world; zero horizontal scroll on mobile across tested pages.
|
||||
|
||||
## Priority Issues
|
||||
|
||||
1. **[P0] Markdown tables in posts are invisible.** Computed 1.02:1 — near-black text on near-black panel (`rgb(9,8,11)` on `rgb(15,13,18)`). The merchant-price table that is the whole point of the seeded feedback thread cannot be read; any player table (loot splits, event schedules) will hit this. Root cause: `--bs-table-*` overrides exist only for notification tables (`_controls.scss:211`) and flags (`_surfaces.scss:361`); post-content tables fall through to Bootstrap's dark default. Fix: hoist the already-duplicated table-variable block into one shared rule that also covers `[component="post/content"] table`. Both assessments found this independently. → `/impeccable polish`
|
||||
2. **[P1] Focus ring fails non-text contrast.** `--wg-focus: rgba(194,163,90,0.28)` (`_tokens.scss:98`) ≈ 1.7:1 over crypt black (WCAG wants ≥3:1 for focus indicators). Confirmed visually: barely perceptible on the topbar icons. PRODUCT.md demands visible focus; DESIGN.md itself specifies the failing value, so the spec needs the fix too. Fix: solid `#c2a35a` (≈6:1) for outlines; keep the soft token for input glows. → `/impeccable polish`
|
||||
3. **[P1] Mobile drawer shows duplicate navigation.** Categories/Recent/Unread/Tags appear twice: `templates/partials/header/topbar.tpl:99–111` loops `{{{ each navigation }}}` then unconditionally appends four hardcoded links. Reads as a glitch on every mobile visit. Fix: delete or guard the hardcoded block. → `/impeccable polish`
|
||||
4. **[P1] The wiki surface is the weakest page, and it's the front door.** (a) Guests see "Set the Wiki Homepage… You do not have permission" — admin plumbing shown to the visitor most likely arriving from out-of-game links. (b) Console error on every load: `error loading forum/wiki — Cannot find module './wiki'` — the wiki's client-side module is missing from the build (plugin/build issue, worth confirming against production). (c) Wiki card prose runs ~155 chars/line. → `/impeccable harden` (empty/guest states) + a code fix in the wiki plugin or build
|
||||
5. **[P2] Inherited defaults break the palette on every page.** Bright magenta generated avatars, grey-triangle default profile cover, near-white "Powered by NodeBB / TipTap" pills (`templates/partials/footer/westgate-global.tpl:47–50`), footer meta at 4.4:1 (`#7f756f` on `#09080b` — fails AA by a hair). Fix: constrain generated avatar background colors to a plum/gold/dried-blood set, ship a themed default cover, restyle the powered-by pills to ghost-button treatment, bump footer meta one step toward ash. → `/impeccable polish`
|
||||
|
||||
## Persona Red Flags
|
||||
|
||||
**Alex (power user):** 9 icon-only topbar destinations with tooltip-only labels (flame=Popular, globe=World, inbox=Unread) demand memorization; no keyboard-shortcut affordance anywhere; /notifications makes him scan 15 zero-count filter rows. Positive: search dropdown autofocuses its input.
|
||||
|
||||
**Sam (keyboard/screen-reader):** the ~1.7:1 focus ring makes complete keyboard support practically invisible; the 1.02:1 post tables fail WCAG 1.4.3 catastrophically; 11.5px uppercase tracked metadata is the hardest-to-read treatment applied to the most-repeated text. Positives: skip-link, aria states, Escape handling, focusable table scroll wrappers.
|
||||
|
||||
**Returning SoW roleplayer on mobile:** duplicate drawer nav reads as a glitch every visit; the price table they came to check is invisible; "WATCHING" chips stamped on every topic row tax the list they skim most. Positives: no horizontal scroll at 390px; thumb-friendly bottom pager.
|
||||
|
||||
## Minor Observations
|
||||
|
||||
- Core i18n grammar leaks: "1 pages", "2 result(s)", "1 posts".
|
||||
- 404 keeps Harmony's playful otter — mildly off the "nothing playful" anti-reference; an in-world line was free. Same for the notification empty state ("wind" icon).
|
||||
- `public/client.js:140` adds mobile scroll wrappers only to `.westgate-wiki` tables; post-content tables get none.
|
||||
- Card teaser line-height 1.25 (detector `tight-leading` ×3 on home/recent) — bump to ≥1.3.
|
||||
- Two primary-button vocabularies: bright gold gradient (topbar Register, composer Submit) vs the spec'd dark gold plate (Login, "Mark all read"). Gilt Edge Rule says the dark plate is the only gold fill — pick one or amend DESIGN.md.
|
||||
- Post prose ~107 chars/line at desktop width; cap the content column nearer 80ch.
|
||||
- `custom_pages/` carries stale duplicate mockups (`Westgate v1…`, `uploads/Westgate.dc.html`) that dominate any static scan — consider pruning.
|
||||
- Sitewide brand text reads "NODEBB" — dev config value, but verify production siteTitle.
|
||||
|
||||
## Questions to Consider
|
||||
|
||||
1. If gold "marks what can be touched," why is the widest gold on the site a decorative 2px stripe on topic rows — while the most touchable thing, the focus ring, is the dimmest gold in the system?
|
||||
2. The ledger is the court's own book — so why does a guest opening /wiki read the clerk's setup notes, and why are the only near-white surfaces in the theme third-party credits?
|
||||
3. Should user data (emails, timestamps, usernames) ever wear the Cinzel engraving, or only the court's own inscriptions — and what enforces that when the next plugin ships a table?
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"generatedAt": "2026-07-11T00:00:00Z",
|
||||
"title": "Design System: Westgate",
|
||||
"extensions": {
|
||||
"colorMeta": {
|
||||
"tarnished-gold": { "role": "primary", "displayName": "Tarnished Gold", "canonical": "#c2a35a", "tonalRamp": ["#241d0e", "#3d3118", "#574624", "#725c31", "#8f7440", "#c2a35a", "#d9bf7f", "#efdcae"] },
|
||||
"bruised-plum": { "role": "secondary", "displayName": "Bruised Plum", "canonical": "#2a1222", "tonalRamp": ["#160811", "#2a1222", "#3a1830", "#54254a", "#6f3563", "#8b487e", "#b371a5", "#d9a8ce"] },
|
||||
"dried-blood": { "role": "tertiary", "displayName": "Dried-Blood Red", "canonical": "#8e3438", "tonalRamp": ["#20090a", "#3c1416", "#5a2124", "#8e3438", "#a84a4e", "#c26a6d", "#d99093", "#eebcbe"] },
|
||||
"crypt-black": { "role": "neutral", "displayName": "Crypt Black", "canonical": "#0f0d12", "tonalRamp": ["#09080b", "#0f0d12", "#110f15", "#18141d", "#201924", "#3a323c", "#625760", "#9a9086"] },
|
||||
"bone-text": { "role": "neutral", "displayName": "Bone Text", "canonical": "#e6e0d6", "tonalRamp": ["#2b2823", "#4a453d", "#6b645a", "#8d8579", "#9a9086", "#b9b2a6", "#e6e0d6", "#f3ede4"] },
|
||||
"ledger-ink": { "role": "neutral", "displayName": "Ledger Ink", "canonical": "#d8c28a", "tonalRamp": ["#2a2413", "#4a3f22", "#6b5b33", "#8d7846", "#b09659", "#d8c28a", "#e5d4a8", "#f2e7ca"] }
|
||||
},
|
||||
"typographyMeta": {
|
||||
"display": { "displayName": "Display", "purpose": "All headings and titles; engraved Cinzel with a faint etch shadow." },
|
||||
"body": { "displayName": "Body", "purpose": "Posts and long reading; calm geometric Jost." },
|
||||
"label": { "displayName": "Label", "purpose": "Small engraved labels: topbar, footer headings, table headers. The only place tracking and uppercase are allowed." },
|
||||
"code": { "displayName": "Code", "purpose": "Code blocks and inline code." }
|
||||
},
|
||||
"shadows": [
|
||||
{ "name": "velvet-rest", "value": "inset 0 1px 0 rgba(255,255,255,0.025), inset 0 0 34px rgba(96,32,68,0.11), 0 10px 26px rgba(0,0,0,0.24)", "purpose": "Default velvet surface: sheen, plum bloom, ambient depth." },
|
||||
{ "name": "velvet-hover", "value": "inset 0 1px 0 rgba(255,255,255,0.035), inset 0 0 38px rgba(120,36,84,0.14), 0 12px 30px rgba(0,0,0,0.3)", "purpose": "Hover state of velvet surfaces; warmer bloom, slightly deeper." },
|
||||
{ "name": "ledger-rest", "value": "inset 0 1px 0 rgba(255,255,255,0.024), inset 0 -1px 0 rgba(0,0,0,0.42), 0 8px 18px rgba(0,0,0,0.2)", "purpose": "Flatter page-like ledger surfaces." },
|
||||
{ "name": "control-depth", "value": "inset 0 1px 0 rgba(255,244,221,0.04), 0 2px 8px rgba(0,0,0,0.22)", "purpose": "Small ambient depth on buttons and controls." }
|
||||
],
|
||||
"motion": [],
|
||||
"breakpoints": []
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"name": "Primary Button",
|
||||
"kind": "button",
|
||||
"refersTo": "button-primary",
|
||||
"description": "Dark gold plate with gilt border; the main call to action.",
|
||||
"html": "<button class=\"ds-btn-primary\">Post Reply</button>",
|
||||
"css": ".ds-btn-primary { font-family: Jost, system-ui, sans-serif; font-size: 0.95rem; color: #f0e6d8; background: linear-gradient(to bottom, #5a4a1a 0%, #473914 100%); border: 1px solid #a8893f; border-radius: 6px; padding: 8px 20px; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 2px 8px rgba(0,0,0,0.24); transition: background 0.15s, border-color 0.15s; } .ds-btn-primary:hover { color: #fff4dd; background: linear-gradient(to bottom, #6a5720 0%, #53431a 100%); border-color: #c2a35a; } .ds-btn-primary:focus-visible { outline: 2px solid rgba(194,163,90,0.28); outline-offset: 2px; }"
|
||||
},
|
||||
{
|
||||
"name": "Ghost Button",
|
||||
"kind": "button",
|
||||
"refersTo": "button-ghost",
|
||||
"description": "Near-transparent dark button with a gilt edge; secondary actions.",
|
||||
"html": "<button class=\"ds-btn-ghost\">Cancel</button>",
|
||||
"css": ".ds-btn-ghost { font-family: Jost, system-ui, sans-serif; font-size: 0.95rem; color: #d8cec0; background: linear-gradient(to bottom, rgba(255,244,221,0.018), rgba(0,0,0,0.06)), rgba(8,7,10,0.12); border: 1px solid rgba(216,194,138,0.28); border-radius: 6px; padding: 8px 20px; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255,244,221,0.035); transition: background 0.15s, border-color 0.15s; } .ds-btn-ghost:hover { color: #e6e0d6; background: rgba(194,163,90,0.075); border-color: rgba(216,194,138,0.44); } .ds-btn-ghost:focus-visible { outline: 2px solid rgba(194,163,90,0.28); outline-offset: 2px; }"
|
||||
},
|
||||
{
|
||||
"name": "Text Input",
|
||||
"kind": "input",
|
||||
"refersTo": "input",
|
||||
"description": "Near-black well with gilt border; gold ring on focus.",
|
||||
"html": "<input class=\"ds-input\" type=\"text\" placeholder=\"Search Westgate...\" />",
|
||||
"css": ".ds-input { font-family: Jost, system-ui, sans-serif; font-size: 1rem; color: #e6e0d6; background-color: rgba(18,15,22,0.96); border: 1px solid rgba(194,163,90,0.18); border-radius: 6px; padding: 8px 12px; width: 260px; transition: border-color 0.15s, box-shadow 0.15s; } .ds-input::placeholder { color: rgba(185,178,166,0.62); } .ds-input:focus { outline: none; background-color: rgba(255,255,255,0.04); border-color: rgba(194,163,90,0.5); box-shadow: inset 0 1px 0 rgba(255,255,255,0.026), 0 0 0 1px rgba(194,163,90,0.28); }"
|
||||
},
|
||||
{
|
||||
"name": "Velvet Card",
|
||||
"kind": "card",
|
||||
"refersTo": "card",
|
||||
"description": "The signature velvet surface used for category rows, topics, and posts.",
|
||||
"html": "<div class=\"ds-velvet-card\"><h3 class=\"ds-velvet-title\">The Harbor District</h3><p class=\"ds-velvet-body\">Rumors travel faster than ships. Post what you have heard on the docks.</p></div>",
|
||||
"css": ".ds-velvet-card { background: linear-gradient(100deg, rgba(42,18,34,0.46) 0%, rgba(25,22,31,0.95) 23%, rgba(18,16,23,0.99) 62%, rgba(13,12,17,0.98) 100%), #0f0d12; border: 1px solid rgba(194,163,90,0.14); border-radius: 8px; padding: 16px 20px; max-width: 420px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.025), inset 0 0 34px rgba(96,32,68,0.11), 0 10px 26px rgba(0,0,0,0.24); transition: border-color 0.2s, box-shadow 0.2s; } .ds-velvet-card:hover { border-color: rgba(194,163,90,0.22); box-shadow: inset 0 1px 0 rgba(255,255,255,0.035), inset 0 0 38px rgba(120,36,84,0.14), 0 12px 30px rgba(0,0,0,0.3); } .ds-velvet-title { font-family: Cinzel, Georgia, serif; font-weight: 600; font-size: 1.1rem; color: #e6e0d6; margin: 0 0 6px; text-shadow: 0 1px 0 rgba(0,0,0,0.9), 0 0 10px rgba(104,32,76,0.16); } .ds-velvet-body { font-family: Jost, system-ui, sans-serif; font-size: 0.95rem; color: #b9b2a6; margin: 0; }"
|
||||
},
|
||||
{
|
||||
"name": "Ledger Panel",
|
||||
"kind": "custom",
|
||||
"refersTo": "card",
|
||||
"description": "Ruled page-like surface for tables and lists, written in ledger ink.",
|
||||
"html": "<div class=\"ds-ledger\"><div class=\"ds-ledger-head\">Notifications</div><div class=\"ds-ledger-row\">A letter arrives from the Night Court</div><div class=\"ds-ledger-row\">Your application was <span class=\"ds-ledger-stamp\">stamped</span></div></div>",
|
||||
"css": ".ds-ledger { background: linear-gradient(100deg, rgba(38,22,35,0.5) 0%, rgba(19,17,24,0.97) 34%, rgba(13,12,17,0.98) 100%), #0f0d12; border: 1px solid rgba(194,163,90,0.18); border-radius: 8px; max-width: 420px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.024), inset 0 -1px 0 rgba(0,0,0,0.42), 0 8px 18px rgba(0,0,0,0.2); } .ds-ledger-head { font-family: Cinzel, Georgia, serif; font-weight: 600; font-size: 0.86rem; letter-spacing: 0.02em; color: #a99d8f; padding: 10px 16px; border-bottom: 1px solid rgba(194,163,90,0.16); } .ds-ledger-row { font-family: Jost, system-ui, sans-serif; font-size: 0.95rem; color: #d8c28a; padding: 10px 16px; border-bottom: 1px solid rgba(194,163,90,0.055); } .ds-ledger-row:last-child { border-bottom: none; } .ds-ledger-stamp { color: #a84a4e; font-weight: 600; }"
|
||||
}
|
||||
],
|
||||
"narrative": {
|
||||
"northStar": "The Velvet Ledger",
|
||||
"overview": "The forum is the account book of a vampire court: pages of near-black velvet, ruled with faint gold lines, written in tarnished gilt ink, stamped here and there with a dried-blood seal. Every surface is dark and plush. Gold appears as thin edges, rulings, and small marks of importance — the way gilt survives on an old book long after the leather has gone dark. Nothing is bright. Nothing is flat. Depth comes from fabric and candlelight, not from floating cards. It is a working forum first: text stays readable, focus stays visible, and the atmosphere never taxes reading.",
|
||||
"keyCharacteristics": [
|
||||
"Near-black plum-tinted surfaces with velvet gradient sheen",
|
||||
"Tarnished gold carries all borders, links, and affordances",
|
||||
"Engraved serif headings (Cinzel) over calm geometric body text (Jost)",
|
||||
"Red reserved for states and small stamped details",
|
||||
"Ambient, candlelit shadows; hierarchy by panel tone, not lift"
|
||||
],
|
||||
"rules": [
|
||||
{ "name": "The Gilt Edge Rule", "body": "Gold is edging, not paint. It lives in 1px borders, thin rules, icons, and text. The only gold-filled surface in the theme is the primary button, and even that is a dark #5a4a1a, not bright gold.", "section": "colors" },
|
||||
{ "name": "The Sparing Red Rule", "body": "Red is a state and a detail — an error, a seal, a warning. It is never a background, never decoration, never loud.", "section": "colors" },
|
||||
{ "name": "The Engraving Rule", "body": "Wide letter-spacing and uppercase belong only to small Cinzel labels. Headings at reading size keep letter-spacing 0. Body text is never tracked, never uppercase.", "section": "typography" },
|
||||
{ "name": "The Candlelight Rule", "body": "Shadows are atmosphere. If you need to show one thing sits above another, change the panel tone and border alpha; do not invent a bigger shadow.", "section": "elevation" }
|
||||
],
|
||||
"dos": [
|
||||
"Do keep body text at or above 4.5:1 contrast; Ash Text (#b9b2a6) on Crypt Black is the floor. Drop to Dust Text only for genuinely secondary metadata.",
|
||||
"Do carry the theme to every surface — wiki, custom pages, applications, reports, admin-facing lists. One unthemed page breaks the spell.",
|
||||
"Do build hierarchy with the three panel tones and border alpha steps (8% / 14% / 18–22%), per the Candlelight Rule.",
|
||||
"Do keep a visible 2px gold focus outline on everything interactive.",
|
||||
"Do stay a small child theme: inherit from Harmony and override only what Westgate needs."
|
||||
],
|
||||
"donts": [
|
||||
"Don't make anything \"bright, playful, clinical, or flat SaaS-style\" (PRODUCT.md). No white surfaces, no saturated blues, no flat borderless cards.",
|
||||
"Don't add \"loud ornament\" (PRODUCT.md). No filigree backgrounds, no large gold fills, no glow effects beyond the existing plum bloom.",
|
||||
"Don't use red as a surface or decoration — states and small seals only (the Sparing Red Rule).",
|
||||
"Don't track or uppercase body text; that treatment belongs to small Cinzel labels only (the Engraving Rule).",
|
||||
"Don't ship anything that could pass as a generic default forum skin. If a screenshot could be any NodeBB forum, it is not done."
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user