oficina / estilo Public

Add approved Oficina app icons

Wire the shared white Lucide artwork into product branding. Source review: GPT-6 Astra (independent agent).
Co-Authored-By: Codex <noreply@openai.com>
Paul Campbell pushed by paul@keymail.dev 7317287c8b1c3d79fb96acbe9d198e425db4aba6 parent 38ed721
71 files changed, +480 −0
  • brand/LICENSE.lucide +43 −0
  • brand/README.md +66 −0
  • brand/brand.css +19 −0
  • brand/distribute.py +42 −0
  • brand/generate.mjs +58 −0
  • brand/icons.json +9 −0
  • brand/png/calendar-1024.png binary
  • brand/png/calendar-16.png binary
  • brand/png/calendar-180.png binary
  • brand/png/calendar-192.png binary
  • brand/png/calendar-32.png binary
  • brand/png/calendar-48.png binary
  • brand/png/calendar-512.png binary
  • brand/png/docs-1024.png binary
  • brand/png/docs-16.png binary
  • brand/png/docs-180.png binary
  • brand/png/docs-192.png binary
  • brand/png/docs-32.png binary
  • brand/png/docs-48.png binary
  • brand/png/docs-512.png binary
  • brand/png/meet-1024.png binary
  • brand/png/meet-16.png binary
  • brand/png/meet-180.png binary
  • brand/png/meet-192.png binary
  • brand/png/meet-32.png binary
  • brand/png/meet-48.png binary
  • brand/png/meet-512.png binary
  • brand/png/memoria-1024.png binary
  • brand/png/memoria-16.png binary
  • brand/png/memoria-180.png binary
  • brand/png/memoria-192.png binary
  • brand/png/memoria-32.png binary
  • brand/png/memoria-48.png binary
  • brand/png/memoria-512.png binary
  • brand/png/oficina-1024.png binary
  • brand/png/oficina-16.png binary
  • brand/png/oficina-180.png binary
  • brand/png/oficina-192.png binary
  • brand/png/oficina-32.png binary
  • brand/png/oficina-48.png binary
  • brand/png/oficina-512.png binary
  • brand/png/sheets-1024.png binary
  • brand/png/sheets-16.png binary
  • brand/png/sheets-180.png binary
  • brand/png/sheets-192.png binary
  • brand/png/sheets-32.png binary
  • brand/png/sheets-48.png binary
  • brand/png/sheets-512.png binary
  • brand/png/slides-1024.png binary
  • brand/png/slides-16.png binary
  • brand/png/slides-180.png binary
  • brand/png/slides-192.png binary
  • brand/png/slides-32.png binary
  • brand/png/slides-48.png binary
  • brand/png/slides-512.png binary
  • brand/preview.png binary
  • brand/preview.svg +97 −0
  • brand/svg/calendar.svg +10 −0
  • brand/svg/docs.svg +5 −0
  • brand/svg/meet.svg +2 −0
  • brand/svg/memoria.svg +4 −0
  • brand/svg/oficina.svg +3 −0
  • brand/svg/sheets.svg +4 −0
  • brand/svg/slides.svg +3 −0
  • brand/upstream/book-search.svg +16 −0
  • brand/upstream/calendar-days.svg +22 −0
  • brand/upstream/file-text.svg +17 −0
  • brand/upstream/panels-top-left.svg +15 −0
  • brand/upstream/presentation.svg +15 −0
  • brand/upstream/table.svg +16 −0
  • brand/upstream/video.svg +14 −0

CI passed — run details

diff --git a/brand/LICENSE.lucide b/brand/LICENSE.lucide
new file mode 100644
index 0000000..718bb3f
--- /dev/null
+++ b/brand/LICENSE.lucide
@@ -0,0 +1,43 @@
+ISC License
+
+Copyright (c) 2026 Lucide Icons and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+---
+
+The following Lucide icons are derived from the Feather project:
+
+airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
+
+The MIT License (MIT) (for the icons listed above)
+
+Copyright (c) 2013-present Cole Bemis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/brand/README.md b/brand/README.md
new file mode 100644
index 0000000..2cc01cd
--- /dev/null
+++ b/brand/README.md
@@ -0,0 +1,66 @@
+# Oficina icon set
+
+Approved 2026-09-12 by Paul for Oficina and its six apps. This directory is
+the source for the suite's brand artwork. The colours identify products;
+they do not change Estilo's day theme tokens.
+
+Apps vendor the generated files so each Go binary embeds everything it needs.
+This distributes artwork, not an alternative implementation of a component.
+The shared `brand.css` styles the `estilo-brand` and `estilo-brand-icon`
+attributes. Images beside a product name have empty alt text to avoid
+announcing the same name twice. No JavaScript is needed.
+
+Copy or verify a consumer from any directory:
+
+```sh
+python3 brand/distribute.py home /path/to/home
+python3 brand/distribute.py meet /path/to/meet --check
+```
+
+The app argument is one of home, docs, sheets, slides, calendar, meet or
+memoria. Home receives all six listing icons; Meet also receives native
+Apple assets. Docs keeps its assets outside the editor build directory.
+The distributor only writes the named artwork files; application wiring
+lives in each app. Run `--check` for every consumer after changing artwork.
+
+Open `preview.png` or `preview.svg` to compare the family and small sizes.
+
+| Product | Lucide symbol | Background |
+| --- | --- | --- |
+| Oficina | panels-top-left | `#303642` |
+| Docs | file-text | `#2563EB` |
+| Sheets | table | `#168049` |
+| Slides | presentation | `#C45B16` |
+| Calendar | calendar-days | `#CB3F50` |
+| Meet | video | `#087F8C` |
+| Memoria | book-search | `#7C3FC8` |
+
+`svg/` contains opaque square 1024-unit masters. `png/` contains 16, 32,
+48, 180, 192, 512 and 1024-pixel exports for each. Platform masks should
+provide rounded corners; the rounded tiles in the preview are illustrative.
+The Lucide canvas occupies 70% of each tile, with the original 2-unit white
+stroke. Use visible product names alongside launcher icons.
+
+Edit `icons.json`, then from the Estilo repository root run:
+
+```sh
+node brand/generate.mjs --render
+```
+
+Rendering uses Playwright and Chromium. Set `OFICINA_PLAYWRIGHT` to an installed
+Playwright module's absolute entry-point path when it is not installed here. Omit
+`--render` to regenerate SVGs without a browser. No network is needed.
+
+The original SVGs in `upstream/` come from Lucide commit
+`a79b2d131dab2bf20cb224bd0937b439a9c4fa99`:
+https://github.com/lucide-icons/lucide/tree/a79b2d131dab2bf20cb224bd0937b439a9c4fa99/icons
+
+The complete upstream license is included as `LICENSE.lucide`, copied from
+Estilo's existing notice for that same commit. Retain it when distributing
+the icons. Upstream paths are unchanged; the generated artwork adds white
+stroke colour, scaling and a background.
+
+Validation: preview rendered in Chromium and visually inspected at 48, 32
+and 16 px; all 49 PNG dimensions and full opacity checked. White/background
+contrast ranges from 4.33:1 (Slides) to 12.12:1 (Oficina). Fine details soften
+at 16 px; these are provisional marks, not bespoke favicon drawings.
diff --git a/brand/brand.css b/brand/brand.css
new file mode 100644
index 0000000..2e194c6
--- /dev/null
+++ b/brand/brand.css
@@ -0,0 +1,19 @@
+/* Oficina brand artwork. Source: oficina/estilo brand/. */
+[estilo-brand] {
+ display: inline-flex;
+ align-items: center;
+ gap: .5rem;
+ min-block-size: 24px;
+}
+[estilo-brand-icon] {
+ display: inline-block;
+ flex-shrink: 0;
+ inline-size: 1.75rem;
+ block-size: 1.75rem;
+ border-radius: 21%;
+ vertical-align: middle;
+}
+[estilo-brand-icon="listing"] {
+ inline-size: 2.5rem;
+ block-size: 2.5rem;
+}
diff --git a/brand/distribute.py b/brand/distribute.py
new file mode 100644
index 0000000..1836c2f
--- /dev/null
+++ b/brand/distribute.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+"""Copy approved artwork to an explicit application checkout; --check detects drift."""
+import argparse
+from pathlib import Path
+
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument("app", choices=["home", "docs", "sheets", "slides", "calendar", "meet", "memoria"])
+parser.add_argument("checkout", type=Path)
+parser.add_argument("--check", action="store_true")
+args = parser.parse_args()
+source = Path(__file__).resolve().parent
+app = args.app
+destination = args.checkout / "internal" / app
+if not destination.is_dir():
+ parser.error("checkout does not contain internal/" + app)
+destination /= "brand" if app == "docs" else "static/brand"
+product = "oficina" if app == "home" else app
+files = {
+ "icon.svg": source / "svg" / (product + ".svg"),
+ "icon-32.png": source / "png" / (product + "-32.png"),
+ "apple-touch-icon.png": source / "png" / (product + "-180.png"),
+ "brand.css": source / "brand.css",
+ "LICENSE.lucide": source / "LICENSE.lucide",
+}
+if app == "home":
+ for name in ["docs", "sheets", "slides", "calendar", "meet", "memoria"]:
+ files[name + ".svg"] = source / "svg" / (name + ".svg")
+copies = {destination / name: path for name, path in files.items()}
+if app == "meet":
+ for name in ["native/ios/Assets.xcassets/AppIcon.appiconset/AppIcon.png",
+ "native/apple/Sources/MeetMac/Resources/AppIcon.png"]:
+ copies[args.checkout / name] = source / "png/meet-1024.png"
+ copies[args.checkout / "native/LICENSE.lucide"] = source / "LICENSE.lucide"
+for target, original in copies.items():
+ data = original.read_bytes()
+ if args.check:
+ if not target.is_file() or target.read_bytes() != data:
+ raise SystemExit("Brand asset missing or stale: " + str(target))
+ else:
+ target.parent.mkdir(parents=True, exist_ok=True)
+ target.write_bytes(data)
+print(("Checked " if args.check else "Copied ") + str(len(copies)) + " assets for " + app)
diff --git a/brand/generate.mjs b/brand/generate.mjs
new file mode 100644
index 0000000..156e469
--- /dev/null
+++ b/brand/generate.mjs
@@ -0,0 +1,58 @@
+import { readFile, writeFile, mkdir } from 'node:fs/promises';
+import { fileURLToPath } from 'node:url';
+
+const root = new URL('./', import.meta.url);
+const icons = JSON.parse(await readFile(new URL('icons.json', root), 'utf8'));
+const bodies = new Map();
+for (const item of icons) {
+ const source = await readFile(new URL(`upstream/${item.icon}.svg`, root), 'utf8');
+ bodies.set(item.id, source.match(/<svg[^>]*>([\s\S]*?)<\/svg>/)[1].trim());
+}
+const glyph = (item, x, y, size) => `<g transform="translate(${x} ${y}) scale(${size / 24})" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${bodies.get(item.id)}</g>`;
+// All seven use the same 24-unit Lucide canvas, occupying 70% of the tile.
+const tile = (item, x, y, size, radius = 0) => `<rect x="${x}" y="${y}" width="${size}" height="${size}" rx="${radius}" fill="${item.colour}"/>${glyph(item, x + size * .15, y + size * .15, size * .7)}`;
+const svg = (width, height, content, title) => `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${title}"><title>${title}</title>${content}</svg>\n`;
+await mkdir(new URL('svg/', root), { recursive: true });
+for (const item of icons) {
+ await writeFile(new URL(`svg/${item.id}.svg`, root), svg(1024, 1024, tile(item, 0, 0, 1024), item.name));
+}
+let sheet = `<rect width="1400" height="790" fill="#F6F5F2"/><g font-family="Arial, sans-serif"><text x="64" y="77" font-size="32" fill="#252932" font-weight="700">Oficina</text><text x="64" y="111" font-size="16" fill="#606571">One family. Seven marks. White Lucide symbols on solid colour.</text>`;
+for (let i = 0; i < icons.length; i++) {
+ const item = icons[i];
+ const x = 64 + i * 184;
+ sheet += tile(item, x, 165, 144, 30);
+ sheet += `<text x="${x}" y="345" font-size="20" font-weight="700" fill="#252932">${item.name}</text><text x="${x}" y="372" font-size="13" fill="#606571">${item.icon}</text><text x="${x}" y="395" font-size="13" fill="#606571">${item.colour}</text>`;
+ for (const [size, y] of [[48, 450], [32, 525], [16, 588]]) sheet += tile(item, x, y, size, size * .21);
+}
+sheet += `<text x="64" y="666" font-size="14" fill="#606571">Actual sizes above: 48, 32 and 16 px. Rounded corners are a preview mask only.</text><text x="64" y="733" font-size="14" fill="#606571">Square, opaque masters · Shared padding · 2-unit strokes · No gradients or shadows</text></g>`;
+await writeFile(new URL('preview.svg', root), svg(1400, 790, sheet, 'Oficina app icon set'));
+console.log('Generated seven SVG masters and preview.svg');
+
+if (process.argv.includes('--render')) {
+ const { chromium } = await import(process.env.OFICINA_PLAYWRIGHT || 'playwright');
+ const browser = await chromium.launch({ headless: true });
+ try {
+ const page = await browser.newPage({ viewport: { width: 1400, height: 790 }, deviceScaleFactor: 1 });
+ await page.goto(new URL('preview.svg', root).href);
+ await page.screenshot({ path: fileURLToPath(new URL('preview.png', root)) });
+ await mkdir(new URL('png/', root), { recursive: true });
+ for (const item of icons) {
+ const source = await readFile(new URL(`svg/${item.id}.svg`, root), 'utf8');
+ for (const size of [16, 32, 48, 180, 192, 512, 1024]) {
+ const png = await page.evaluate(async ({ source, size }) => {
+ const img = new Image();
+ img.src = 'data:image/svg+xml;base64,' + btoa(source);
+ await img.decode();
+ const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
+ canvas.width = canvas.height = size;
+ canvas.getContext('2d', { alpha: false }).drawImage(img, 0, 0, size, size);
+ return canvas.toDataURL('image/png').split(',')[1];
+ }, { source, size });
+ await writeFile(new URL(`png/${item.id}-${size}.png`, root), Buffer.from(png, 'base64'));
+ }
+ }
+ console.log('Rendered preview.png and seven PNG sizes per app');
+ } finally {
+ await browser.close();
+ }
+}
diff --git a/brand/icons.json b/brand/icons.json
new file mode 100644
index 0000000..86a9219
--- /dev/null
+++ b/brand/icons.json
@@ -0,0 +1,9 @@
+[
+ {"id":"oficina","name":"Oficina","icon":"panels-top-left","colour":"#303642"},
+ {"id":"docs","name":"Docs","icon":"file-text","colour":"#2563EB"},
+ {"id":"sheets","name":"Sheets","icon":"table","colour":"#168049"},
+ {"id":"slides","name":"Slides","icon":"presentation","colour":"#C45B16"},
+ {"id":"calendar","name":"Calendar","icon":"calendar-days","colour":"#CB3F50"},
+ {"id":"meet","name":"Meet","icon":"video","colour":"#087F8C"},
+ {"id":"memoria","name":"Memoria","icon":"book-search","colour":"#7C3FC8"}
+]
diff --git a/brand/png/calendar-1024.png b/brand/png/calendar-1024.png
new file mode 100644
index 0000000..548da0a
Binary files /dev/null and b/brand/png/calendar-1024.png differ
diff --git a/brand/png/calendar-16.png b/brand/png/calendar-16.png
new file mode 100644
index 0000000..afc37bb
Binary files /dev/null and b/brand/png/calendar-16.png differ
diff --git a/brand/png/calendar-180.png b/brand/png/calendar-180.png
new file mode 100644
index 0000000..bc1747b
Binary files /dev/null and b/brand/png/calendar-180.png differ
diff --git a/brand/png/calendar-192.png b/brand/png/calendar-192.png
new file mode 100644
index 0000000..b969d12
Binary files /dev/null and b/brand/png/calendar-192.png differ
diff --git a/brand/png/calendar-32.png b/brand/png/calendar-32.png
new file mode 100644
index 0000000..68d97aa
Binary files /dev/null and b/brand/png/calendar-32.png differ
diff --git a/brand/png/calendar-48.png b/brand/png/calendar-48.png
new file mode 100644
index 0000000..3382e10
Binary files /dev/null and b/brand/png/calendar-48.png differ
diff --git a/brand/png/calendar-512.png b/brand/png/calendar-512.png
new file mode 100644
index 0000000..25b8c58
Binary files /dev/null and b/brand/png/calendar-512.png differ
diff --git a/brand/png/docs-1024.png b/brand/png/docs-1024.png
new file mode 100644
index 0000000..ad3778f
Binary files /dev/null and b/brand/png/docs-1024.png differ
diff --git a/brand/png/docs-16.png b/brand/png/docs-16.png
new file mode 100644
index 0000000..4d44d74
Binary files /dev/null and b/brand/png/docs-16.png differ
diff --git a/brand/png/docs-180.png b/brand/png/docs-180.png
new file mode 100644
index 0000000..d75bdba
Binary files /dev/null and b/brand/png/docs-180.png differ
diff --git a/brand/png/docs-192.png b/brand/png/docs-192.png
new file mode 100644
index 0000000..a4edecb
Binary files /dev/null and b/brand/png/docs-192.png differ
diff --git a/brand/png/docs-32.png b/brand/png/docs-32.png
new file mode 100644
index 0000000..b61cb5f
Binary files /dev/null and b/brand/png/docs-32.png differ
diff --git a/brand/png/docs-48.png b/brand/png/docs-48.png
new file mode 100644
index 0000000..37b3009
Binary files /dev/null and b/brand/png/docs-48.png differ
diff --git a/brand/png/docs-512.png b/brand/png/docs-512.png
new file mode 100644
index 0000000..66da51f
Binary files /dev/null and b/brand/png/docs-512.png differ
diff --git a/brand/png/meet-1024.png b/brand/png/meet-1024.png
new file mode 100644
index 0000000..29baeff
Binary files /dev/null and b/brand/png/meet-1024.png differ
diff --git a/brand/png/meet-16.png b/brand/png/meet-16.png
new file mode 100644
index 0000000..a8075c6
Binary files /dev/null and b/brand/png/meet-16.png differ
diff --git a/brand/png/meet-180.png b/brand/png/meet-180.png
new file mode 100644
index 0000000..1354068
Binary files /dev/null and b/brand/png/meet-180.png differ
diff --git a/brand/png/meet-192.png b/brand/png/meet-192.png
new file mode 100644
index 0000000..185e98f
Binary files /dev/null and b/brand/png/meet-192.png differ
diff --git a/brand/png/meet-32.png b/brand/png/meet-32.png
new file mode 100644
index 0000000..e5b0218
Binary files /dev/null and b/brand/png/meet-32.png differ
diff --git a/brand/png/meet-48.png b/brand/png/meet-48.png
new file mode 100644
index 0000000..d4ba0ad
Binary files /dev/null and b/brand/png/meet-48.png differ
diff --git a/brand/png/meet-512.png b/brand/png/meet-512.png
new file mode 100644
index 0000000..ba3c3c7
Binary files /dev/null and b/brand/png/meet-512.png differ
diff --git a/brand/png/memoria-1024.png b/brand/png/memoria-1024.png
new file mode 100644
index 0000000..d07f2d2
Binary files /dev/null and b/brand/png/memoria-1024.png differ
diff --git a/brand/png/memoria-16.png b/brand/png/memoria-16.png
new file mode 100644
index 0000000..ba1acc9
Binary files /dev/null and b/brand/png/memoria-16.png differ
diff --git a/brand/png/memoria-180.png b/brand/png/memoria-180.png
new file mode 100644
index 0000000..8e9e150
Binary files /dev/null and b/brand/png/memoria-180.png differ
diff --git a/brand/png/memoria-192.png b/brand/png/memoria-192.png
new file mode 100644
index 0000000..c49afe3
Binary files /dev/null and b/brand/png/memoria-192.png differ
diff --git a/brand/png/memoria-32.png b/brand/png/memoria-32.png
new file mode 100644
index 0000000..9c7742b
Binary files /dev/null and b/brand/png/memoria-32.png differ
diff --git a/brand/png/memoria-48.png b/brand/png/memoria-48.png
new file mode 100644
index 0000000..072c329
Binary files /dev/null and b/brand/png/memoria-48.png differ
diff --git a/brand/png/memoria-512.png b/brand/png/memoria-512.png
new file mode 100644
index 0000000..c982593
Binary files /dev/null and b/brand/png/memoria-512.png differ
diff --git a/brand/png/oficina-1024.png b/brand/png/oficina-1024.png
new file mode 100644
index 0000000..2d178fe
Binary files /dev/null and b/brand/png/oficina-1024.png differ
diff --git a/brand/png/oficina-16.png b/brand/png/oficina-16.png
new file mode 100644
index 0000000..b19f71e
Binary files /dev/null and b/brand/png/oficina-16.png differ
diff --git a/brand/png/oficina-180.png b/brand/png/oficina-180.png
new file mode 100644
index 0000000..97b522c
Binary files /dev/null and b/brand/png/oficina-180.png differ
diff --git a/brand/png/oficina-192.png b/brand/png/oficina-192.png
new file mode 100644
index 0000000..92669d4
Binary files /dev/null and b/brand/png/oficina-192.png differ
diff --git a/brand/png/oficina-32.png b/brand/png/oficina-32.png
new file mode 100644
index 0000000..bbfaca7
Binary files /dev/null and b/brand/png/oficina-32.png differ
diff --git a/brand/png/oficina-48.png b/brand/png/oficina-48.png
new file mode 100644
index 0000000..9d7f483
Binary files /dev/null and b/brand/png/oficina-48.png differ
diff --git a/brand/png/oficina-512.png b/brand/png/oficina-512.png
new file mode 100644
index 0000000..6712cb6
Binary files /dev/null and b/brand/png/oficina-512.png differ
diff --git a/brand/png/sheets-1024.png b/brand/png/sheets-1024.png
new file mode 100644
index 0000000..458ff0f
Binary files /dev/null and b/brand/png/sheets-1024.png differ
diff --git a/brand/png/sheets-16.png b/brand/png/sheets-16.png
new file mode 100644
index 0000000..17f9057
Binary files /dev/null and b/brand/png/sheets-16.png differ
diff --git a/brand/png/sheets-180.png b/brand/png/sheets-180.png
new file mode 100644
index 0000000..2487bbb
Binary files /dev/null and b/brand/png/sheets-180.png differ
diff --git a/brand/png/sheets-192.png b/brand/png/sheets-192.png
new file mode 100644
index 0000000..a5ce5fe
Binary files /dev/null and b/brand/png/sheets-192.png differ
diff --git a/brand/png/sheets-32.png b/brand/png/sheets-32.png
new file mode 100644
index 0000000..b99fcb4
Binary files /dev/null and b/brand/png/sheets-32.png differ
diff --git a/brand/png/sheets-48.png b/brand/png/sheets-48.png
new file mode 100644
index 0000000..c6624d8
Binary files /dev/null and b/brand/png/sheets-48.png differ
diff --git a/brand/png/sheets-512.png b/brand/png/sheets-512.png
new file mode 100644
index 0000000..73ed474
Binary files /dev/null and b/brand/png/sheets-512.png differ
diff --git a/brand/png/slides-1024.png b/brand/png/slides-1024.png
new file mode 100644
index 0000000..4967c67
Binary files /dev/null and b/brand/png/slides-1024.png differ
diff --git a/brand/png/slides-16.png b/brand/png/slides-16.png
new file mode 100644
index 0000000..73f878c
Binary files /dev/null and b/brand/png/slides-16.png differ
diff --git a/brand/png/slides-180.png b/brand/png/slides-180.png
new file mode 100644
index 0000000..eed92dd
Binary files /dev/null and b/brand/png/slides-180.png differ
diff --git a/brand/png/slides-192.png b/brand/png/slides-192.png
new file mode 100644
index 0000000..7a74373
Binary files /dev/null and b/brand/png/slides-192.png differ
diff --git a/brand/png/slides-32.png b/brand/png/slides-32.png
new file mode 100644
index 0000000..58f126e
Binary files /dev/null and b/brand/png/slides-32.png differ
diff --git a/brand/png/slides-48.png b/brand/png/slides-48.png
new file mode 100644
index 0000000..7a01c0c
Binary files /dev/null and b/brand/png/slides-48.png differ
diff --git a/brand/png/slides-512.png b/brand/png/slides-512.png
new file mode 100644
index 0000000..6908667
Binary files /dev/null and b/brand/png/slides-512.png differ
diff --git a/brand/preview.png b/brand/preview.png
new file mode 100644
index 0000000..6626cef
Binary files /dev/null and b/brand/preview.png differ
diff --git a/brand/preview.svg b/brand/preview.svg
new file mode 100644
index 0000000..5484fe7
--- /dev/null
+++ b/brand/preview.svg
@@ -0,0 +1,97 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="790" viewBox="0 0 1400 790" role="img" aria-label="Oficina app icon set"><title>Oficina app icon set</title><rect width="1400" height="790" fill="#F6F5F2"/><g font-family="Arial, sans-serif"><text x="64" y="77" font-size="32" fill="#252932" font-weight="700">Oficina</text><text x="64" y="111" font-size="16" fill="#606571">One family. Seven marks. White Lucide symbols on solid colour.</text><rect x="64" y="165" width="144" height="144" rx="30" fill="#303642"/><g transform="translate(85.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" /></g><text x="64" y="345" font-size="20" font-weight="700" fill="#252932">Oficina</text><text x="64" y="372" font-size="13" fill="#606571">panels-top-left</text><text x="64" y="395" font-size="13" fill="#606571">#303642</text><rect x="64" y="450" width="48" height="48" rx="10.08" fill="#303642"/><g transform="translate(71.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" /></g><rect x="64" y="525" width="32" height="32" rx="6.72" fill="#303642"/><g transform="translate(68.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" /></g><rect x="64" y="588" width="16" height="16" rx="3.36" fill="#303642"/><g transform="translate(66.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" /></g><rect x="248" y="165" width="144" height="144" rx="30" fill="#2563EB"/><g transform="translate(269.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" /></g><text x="248" y="345" font-size="20" font-weight="700" fill="#252932">Docs</text><text x="248" y="372" font-size="13" fill="#606571">file-text</text><text x="248" y="395" font-size="13" fill="#606571">#2563EB</text><rect x="248" y="450" width="48" height="48" rx="10.08" fill="#2563EB"/><g transform="translate(255.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" /></g><rect x="248" y="525" width="32" height="32" rx="6.72" fill="#2563EB"/><g transform="translate(252.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" /></g><rect x="248" y="588" width="16" height="16" rx="3.36" fill="#2563EB"/><g transform="translate(250.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" /></g><rect x="432" y="165" width="144" height="144" rx="30" fill="#168049"/><g transform="translate(453.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" /></g><text x="432" y="345" font-size="20" font-weight="700" fill="#252932">Sheets</text><text x="432" y="372" font-size="13" fill="#606571">table</text><text x="432" y="395" font-size="13" fill="#606571">#168049</text><rect x="432" y="450" width="48" height="48" rx="10.08" fill="#168049"/><g transform="translate(439.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" /></g><rect x="432" y="525" width="32" height="32" rx="6.72" fill="#168049"/><g transform="translate(436.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" /></g><rect x="432" y="588" width="16" height="16" rx="3.36" fill="#168049"/><g transform="translate(434.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" /></g><rect x="616" y="165" width="144" height="144" rx="30" fill="#C45B16"/><g transform="translate(637.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" /></g><text x="616" y="345" font-size="20" font-weight="700" fill="#252932">Slides</text><text x="616" y="372" font-size="13" fill="#606571">presentation</text><text x="616" y="395" font-size="13" fill="#606571">#C45B16</text><rect x="616" y="450" width="48" height="48" rx="10.08" fill="#C45B16"/><g transform="translate(623.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" /></g><rect x="616" y="525" width="32" height="32" rx="6.72" fill="#C45B16"/><g transform="translate(620.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" /></g><rect x="616" y="588" width="16" height="16" rx="3.36" fill="#C45B16"/><g transform="translate(618.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" /></g><rect x="800" y="165" width="144" height="144" rx="30" fill="#CB3F50"/><g transform="translate(821.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" /></g><text x="800" y="345" font-size="20" font-weight="700" fill="#252932">Calendar</text><text x="800" y="372" font-size="13" fill="#606571">calendar-days</text><text x="800" y="395" font-size="13" fill="#606571">#CB3F50</text><rect x="800" y="450" width="48" height="48" rx="10.08" fill="#CB3F50"/><g transform="translate(807.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" /></g><rect x="800" y="525" width="32" height="32" rx="6.72" fill="#CB3F50"/><g transform="translate(804.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" /></g><rect x="800" y="588" width="16" height="16" rx="3.36" fill="#CB3F50"/><g transform="translate(802.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" /></g><rect x="984" y="165" width="144" height="144" rx="30" fill="#087F8C"/><g transform="translate(1005.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" /></g><text x="984" y="345" font-size="20" font-weight="700" fill="#252932">Meet</text><text x="984" y="372" font-size="13" fill="#606571">video</text><text x="984" y="395" font-size="13" fill="#606571">#087F8C</text><rect x="984" y="450" width="48" height="48" rx="10.08" fill="#087F8C"/><g transform="translate(991.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" /></g><rect x="984" y="525" width="32" height="32" rx="6.72" fill="#087F8C"/><g transform="translate(988.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" /></g><rect x="984" y="588" width="16" height="16" rx="3.36" fill="#087F8C"/><g transform="translate(986.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" /></g><rect x="1168" y="165" width="144" height="144" rx="30" fill="#7C3FC8"/><g transform="translate(1189.6 186.6) scale(4.2)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" /></g><text x="1168" y="345" font-size="20" font-weight="700" fill="#252932">Memoria</text><text x="1168" y="372" font-size="13" fill="#606571">book-search</text><text x="1168" y="395" font-size="13" fill="#606571">#7C3FC8</text><rect x="1168" y="450" width="48" height="48" rx="10.08" fill="#7C3FC8"/><g transform="translate(1175.2 457.2) scale(1.3999999999999997)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" /></g><rect x="1168" y="525" width="32" height="32" rx="6.72" fill="#7C3FC8"/><g transform="translate(1172.8 529.8) scale(0.9333333333333332)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" /></g><rect x="1168" y="588" width="16" height="16" rx="3.36" fill="#7C3FC8"/><g transform="translate(1170.4 590.4) scale(0.4666666666666666)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" /></g><text x="64" y="666" font-size="14" fill="#606571">Actual sizes above: 48, 32 and 16 px. Rounded corners are a preview mask only.</text><text x="64" y="733" font-size="14" fill="#606571">Square, opaque masters · Shared padding · 2-unit strokes · No gradients or shadows</text></g></svg>
diff --git a/brand/svg/calendar.svg b/brand/svg/calendar.svg
new file mode 100644
index 0000000..97108f8
--- /dev/null
+++ b/brand/svg/calendar.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Calendar"><title>Calendar</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#CB3F50"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" /></g></svg>
diff --git a/brand/svg/docs.svg b/brand/svg/docs.svg
new file mode 100644
index 0000000..9b7f4c6
--- /dev/null
+++ b/brand/svg/docs.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Docs"><title>Docs</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#2563EB"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" /></g></svg>
diff --git a/brand/svg/meet.svg b/brand/svg/meet.svg
new file mode 100644
index 0000000..a027150
--- /dev/null
+++ b/brand/svg/meet.svg
@@ -0,0 +1,2 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Meet"><title>Meet</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#087F8C"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" /></g></svg>
diff --git a/brand/svg/memoria.svg b/brand/svg/memoria.svg
new file mode 100644
index 0000000..6d7c4c8
--- /dev/null
+++ b/brand/svg/memoria.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Memoria"><title>Memoria</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#7C3FC8"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" /></g></svg>
diff --git a/brand/svg/oficina.svg b/brand/svg/oficina.svg
new file mode 100644
index 0000000..f30e545
--- /dev/null
+++ b/brand/svg/oficina.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Oficina"><title>Oficina</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#303642"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" /></g></svg>
diff --git a/brand/svg/sheets.svg b/brand/svg/sheets.svg
new file mode 100644
index 0000000..3739ca0
--- /dev/null
+++ b/brand/svg/sheets.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Sheets"><title>Sheets</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#168049"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" /></g></svg>
diff --git a/brand/svg/slides.svg b/brand/svg/slides.svg
new file mode 100644
index 0000000..ea6ead1
--- /dev/null
+++ b/brand/svg/slides.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Slides"><title>Slides</title><rect x="0" y="0" width="1024" height="1024" rx="0" fill="#C45B16"/><g transform="translate(153.6 153.6) scale(29.866666666666664)" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" /></g></svg>
diff --git a/brand/upstream/book-search.svg b/brand/upstream/book-search.svg
new file mode 100644
index 0000000..3c1e1ad
--- /dev/null
+++ b/brand/upstream/book-search.svg
@@ -0,0 +1,16 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="M11 22H5.5a1 1 0 0 1 0-5h4.501" />
+ <path d="m21 22-1.879-1.878" />
+ <path d="M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8" />
+ <circle cx="17" cy="18" r="3" />
+</svg>
diff --git a/brand/upstream/calendar-days.svg b/brand/upstream/calendar-days.svg
new file mode 100644
index 0000000..78c5420
--- /dev/null
+++ b/brand/upstream/calendar-days.svg
@@ -0,0 +1,22 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="M8 2v3" />
+ <path d="M16 2v3" />
+ <rect x="3" y="3" width="18" height="18" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M8 13h.01" />
+ <path d="M12 13h.01" />
+ <path d="M16 13h.01" />
+ <path d="M8 17h.01" />
+ <path d="M12 17h.01" />
+ <path d="M16 17h.01" />
+</svg>
diff --git a/brand/upstream/file-text.svg b/brand/upstream/file-text.svg
new file mode 100644
index 0000000..67dc5b9
--- /dev/null
+++ b/brand/upstream/file-text.svg
@@ -0,0 +1,17 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
+ <path d="M10 9H8" />
+ <path d="M16 13H8" />
+ <path d="M16 17H8" />
+</svg>
diff --git a/brand/upstream/panels-top-left.svg b/brand/upstream/panels-top-left.svg
new file mode 100644
index 0000000..574e4de
--- /dev/null
+++ b/brand/upstream/panels-top-left.svg
@@ -0,0 +1,15 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M9 21V9" />
+</svg>
diff --git a/brand/upstream/presentation.svg b/brand/upstream/presentation.svg
new file mode 100644
index 0000000..7795726
--- /dev/null
+++ b/brand/upstream/presentation.svg
@@ -0,0 +1,15 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="M2 3h20" />
+ <path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" />
+ <path d="m7 21 5-5 5 5" />
+</svg>
diff --git a/brand/upstream/table.svg b/brand/upstream/table.svg
new file mode 100644
index 0000000..725f23c
--- /dev/null
+++ b/brand/upstream/table.svg
@@ -0,0 +1,16 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="M12 3v18" />
+ <rect width="18" height="18" x="3" y="3" rx="2" />
+ <path d="M3 9h18" />
+ <path d="M3 15h18" />
+</svg>
diff --git a/brand/upstream/video.svg b/brand/upstream/video.svg
new file mode 100644
index 0000000..c198f49
--- /dev/null
+++ b/brand/upstream/video.svg
@@ -0,0 +1,14 @@
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+>
+ <path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
+ <rect x="2" y="6" width="14" height="12" rx="2" />
+</svg>